##// END OF EJS Templates
Merge pull request #5340 from minrk/btcss...
Paul Ivanov -
r15817:531268a9 merge
parent child Browse files
Show More
@@ -1,175 +1,176 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Tour of IPython Notebok UI (with Bootstrap Tour)
9 // Tour of IPython Notebok UI (with Bootstrap Tour)
10 //============================================================================
10 //============================================================================
11
11
12 var tour_steps = [
12 var tour_steps = [
13 {
13 {
14 element: $("#ipython_notebook"),
14 element: $("#ipython_notebook"),
15 title: "Welcome to the Notebook Tour",
15 title: "Welcome to the Notebook Tour",
16 placement: 'bottom',
16 placement: 'bottom',
17 content: "This tour will take 2 minutes.",
17 content: "This tour will take 2 minutes.",
18 backdrop: true,
18 backdrop: true,
19 }, {
19 }, {
20 element: "#notebook_name",
20 element: "#notebook_name",
21 title: "Filename",
21 title: "Filename",
22 placement: 'bottom',
22 placement: 'bottom',
23 content: "Click here to change the filename for this notebook."
23 content: "Click here to change the filename for this notebook."
24 }, {
24 }, {
25 element: "#checkpoint_status",
25 element: "#checkpoint_status",
26 title: "Checkpoint Status",
26 title: "Checkpoint Status",
27 placement: 'bottom',
27 placement: 'bottom',
28 content: "Information about the last time this notebook was saved."
28 content: "Information about the last time this notebook was saved."
29 }, {
29 }, {
30 element: $("#menus").parent(),
30 element: $("#menus").parent(),
31 placement: 'bottom',
31 placement: 'bottom',
32 backdrop: true,
32 backdrop: true,
33 title: "Notebook Menubar",
33 title: "Notebook Menubar",
34 content: "The menubar has menus for actions on the notebook, its cells, and the kernel it communicates with."
34 content: "The menubar has menus for actions on the notebook, its cells, and the kernel it communicates with."
35 }, {
35 }, {
36 element: "#maintoolbar",
36 element: "#maintoolbar",
37 placement: 'bottom',
37 placement: 'bottom',
38 backdrop: true,
38 backdrop: true,
39 title: "Notebook Toolbar",
39 title: "Notebook Toolbar",
40 content: "The toolbar has buttons for the most common actions. Hover your mouse over each button for more information."
40 content: "The toolbar has buttons for the most common actions. Hover your mouse over each button for more information."
41 }, {
41 }, {
42 element: "#modal_indicator",
42 element: "#modal_indicator",
43 title: "Mode Indicator",
43 title: "Mode Indicator",
44 placement: 'bottom',
44 placement: 'bottom',
45 content: "The Notebook has two modes: Edit Mode and Command Mode. In this area, an indicator can appear to tell you which mode you are in.",
45 content: "The Notebook has two modes: Edit Mode and Command Mode. In this area, an indicator can appear to tell you which mode you are in.",
46 onShow: function(tour) { command_icon_hack(); }
46 onShow: function(tour) { command_icon_hack(); }
47 }, {
47 }, {
48 element: "#modal_indicator",
48 element: "#modal_indicator",
49 title: "Command Mode",
49 title: "Command Mode",
50 placement: 'bottom',
50 placement: 'bottom',
51 onShow: function(tour) { IPython.notebook.command_mode(); command_icon_hack(); },
51 onShow: function(tour) { IPython.notebook.command_mode(); command_icon_hack(); },
52 onNext: function(tour) { edit_mode(); },
52 onNext: function(tour) { edit_mode(); },
53 content: "Right now you are in Command Mode, and many keyboard shortcuts are available. In this mode, no icon is displayed in the indicator area."
53 content: "Right now you are in Command Mode, and many keyboard shortcuts are available. In this mode, no icon is displayed in the indicator area."
54 }, {
54 }, {
55 element: "#modal_indicator",
55 element: "#modal_indicator",
56 title: "Edit Mode",
56 title: "Edit Mode",
57 placement: 'bottom',
57 placement: 'bottom',
58 onShow: function(tour) { edit_mode(); },
58 onShow: function(tour) { edit_mode(); },
59 content: "Pressing <code>enter</code> or clicking in the input text area of the cell switches to Edit Mode."
59 content: "Pressing <code>enter</code> or clicking in the input text area of the cell switches to Edit Mode."
60 }, {
60 }, {
61 element: '.selected',
61 element: '.selected',
62 title: "Edit Mode",
62 title: "Edit Mode",
63 placement: 'bottom',
63 placement: 'bottom',
64 onShow: function(tour) { edit_mode(); },
64 onShow: function(tour) { edit_mode(); },
65 content: "Notice that the border around the currently active cell changed color. Typing will insert text into the currently active cell."
65 content: "Notice that the border around the currently active cell changed color. Typing will insert text into the currently active cell."
66 }, {
66 }, {
67 element: '.selected',
67 element: '.selected',
68 title: "Back to Command Mode",
68 title: "Back to Command Mode",
69 placement: 'bottom',
69 placement: 'bottom',
70 onShow: function(tour) { IPython.notebook.command_mode(); },
70 onShow: function(tour) { IPython.notebook.command_mode(); },
71 content: "Pressing <code>esc</code> or clicking outside of the input text area takes you back to Command Mode."
71 content: "Pressing <code>esc</code> or clicking outside of the input text area takes you back to Command Mode."
72 }, {
72 }, {
73 element: '#keyboard_shortcuts',
73 element: '#keyboard_shortcuts',
74 title: "Keyboard Shortcuts",
74 title: "Keyboard Shortcuts",
75 placement: 'bottom',
75 placement: 'bottom',
76 onShow: function(tour) { $('#help_menu').parent().addClass('open'); },
76 onShow: function(tour) { $('#help_menu').parent().addClass('open'); },
77 onHide: function(tour) { $('#help_menu').parent().removeClass('open'); },
77 onHide: function(tour) { $('#help_menu').parent().removeClass('open'); },
78 content: "You can click here to get a list of all of the keyboard shortcuts."
78 content: "You can click here to get a list of all of the keyboard shortcuts."
79 }, {
79 }, {
80 element: "#kernel_indicator",
80 element: "#kernel_indicator",
81 title: "Kernel Indicator",
81 title: "Kernel Indicator",
82 placement: 'bottom',
82 placement: 'bottom',
83 onShow: function(tour) { $([IPython.events]).trigger('status_idle.Kernel');},
83 onShow: function(tour) { $([IPython.events]).trigger('status_idle.Kernel');},
84 content: "This is the Kernel indicator. It looks like this when the Kernel is idle.",
84 content: "This is the Kernel indicator. It looks like this when the Kernel is idle.",
85 }, {
85 }, {
86 element: "#kernel_indicator",
86 element: "#kernel_indicator",
87 title: "Kernel Indicator",
87 title: "Kernel Indicator",
88 placement: 'bottom',
88 placement: 'bottom',
89 onShow: function(tour) { $([IPython.events]).trigger('status_busy.Kernel'); },
89 onShow: function(tour) { $([IPython.events]).trigger('status_busy.Kernel'); },
90 content: "The Kernel indicator looks like this when the Kernel is busy.",
90 content: "The Kernel indicator looks like this when the Kernel is busy.",
91 }, {
91 }, {
92 element: ".icon-stop",
92 element: ".icon-stop",
93 placement: 'bottom',
93 placement: 'bottom',
94 title: "Interrupting the Kernel",
94 title: "Interrupting the Kernel",
95 onHide: function(tour) { $([IPython.events]).trigger('status_idle.Kernel'); },
95 onHide: function(tour) { $([IPython.events]).trigger('status_idle.Kernel'); },
96 content: "To cancel a computation in progress, you can click here."
96 content: "To cancel a computation in progress, you can click here."
97 }, {
97 }, {
98 element: "#notification_kernel",
98 element: "#notification_kernel",
99 placement: 'bottom',
99 placement: 'bottom',
100 onShow: function(tour) { $('.icon-stop').click(); },
100 onShow: function(tour) { $('.icon-stop').click(); },
101 title: "Notification Area",
101 title: "Notification Area",
102 content: "Messages in response to user actions (Save, Interrupt, etc) appear here."
102 content: "Messages in response to user actions (Save, Interrupt, etc) appear here."
103 }, {
103 }, {
104 element: "#ipython_notebook",
104 element: "#ipython_notebook",
105 title: "Fin.",
105 title: "Fin.",
106 placement: 'bottom',
106 placement: 'bottom',
107 backdrop: true,
107 backdrop: true,
108 content: "This concludes the IPython Notebook User Interface Tour. Happy hacking!",
108 content: "This concludes the IPython Notebook User Interface Tour. Happy hacking!",
109 }
109 }
110 ];
110 ];
111
111
112 var tour_style = "<div class='popover tour' style='position:relative'>\
112 var tour_style = "<div class='popover tour' style='position:relative'>\
113 <div class='arrow'></div>\
113 <div class='arrow'></div>\
114 <div style='position:absolute; top:7px; right:7px'>\
114 <div style='position:absolute; top:7px; right:7px'>\
115 <button class='btn btn-sm icon-remove' data-role='end'></button></div>\
115 <button class='btn btn-sm icon-remove' data-role='end'></button></div>\
116 <h3 class='popover-title'></h3>\
116 <h3 class='popover-title'></h3>\
117 <div class='popover-content'></div>\
117 <div class='popover-content'></div>\
118 <div class='popover-navigation'>\
118 <div class='popover-navigation'>\
119 <button class='btn btn-default icon-step-backward' data-role='prev'></button>\
119 <button class='btn btn-default icon-step-backward' data-role='prev'></button>\
120 <button class='btn btn-default icon-step-forward pull-right' data-role='next'></button>\
120 <button class='btn btn-default icon-step-forward pull-right' data-role='next'></button>\
121 <button id='tour-pause' class='btn btn-sm btn-default icon-pause' data-resume-text='' data-pause-text='' data-role='pause-resume'></button>\
121 <button id='tour-pause' class='btn btn-sm btn-default icon-pause' data-resume-text='' data-pause-text='' data-role='pause-resume'></button>\
122 </div>\
122 </div>\
123 </div>";
123 </div>";
124
124
125 var command_icon_hack = function() {$('#modal_indicator').css('min-height', 20);}
125 var command_icon_hack = function() {$('#modal_indicator').css('min-height', 20);}
126 var toggle_pause_play = function () { $('#tour-pause').toggleClass('icon-pause icon-play'); };
126 var toggle_pause_play = function () { $('#tour-pause').toggleClass('icon-pause icon-play'); };
127 var edit_mode = function() {
127 var edit_mode = function() {
128 IPython.notebook.focus_cell();
128 IPython.notebook.focus_cell();
129 IPython.notebook.edit_mode();
129 IPython.notebook.edit_mode();
130 ;}
130 ;}
131
131
132 IPython = (function (IPython) {
132 IPython = (function (IPython) {
133 "use strict";
133 "use strict";
134
134
135
135
136 var NotebookTour = function () {
136 var NotebookTour = function () {
137 this.step_duration = 0;
137 this.step_duration = 0;
138 this.tour_steps = tour_steps ;
138 this.tour_steps = tour_steps ;
139 this.tour_steps[0].content = "You can use the left and right arrow keys to go backwards and forwards.";
139 this.tour_steps[0].content = "You can use the left and right arrow keys to go backwards and forwards.";
140 this.tour = new Tour({
140 this.tour = new Tour({
141 //orphan: true,
141 //orphan: true,
142 storage: false, // start tour from beginning every time
142 storage: false, // start tour from beginning every time
143 //element: $("#ipython_notebook"),
143 //element: $("#ipython_notebook"),
144 debug: true,
144 debug: true,
145 reflex: true, // click on element to continue tour
145 reflex: true, // click on element to continue tour
146 //backdrop: true, // show dark behind popover
146 //backdrop: true, // show dark behind popover
147 animation: false,
147 animation: false,
148 duration: this.step_duration,
148 duration: this.step_duration,
149 onStart: function() { console.log('tour started'); },
149 onStart: function() { console.log('tour started'); },
150 // TODO: remove the onPause/onResume logic once pi's patch has been
150 // TODO: remove the onPause/onResume logic once pi's patch has been
151 // merged upstream to make this work via data-resume-class and
151 // merged upstream to make this work via data-resume-class and
152 // data-resume-text attributes.
152 // data-resume-text attributes.
153 onPause: toggle_pause_play,
153 onPause: toggle_pause_play,
154 onResume: toggle_pause_play,
154 onResume: toggle_pause_play,
155 steps: this.tour_steps,
155 steps: this.tour_steps,
156 template: tour_style
156 template: tour_style
157 });
157 });
158 this.tour.init();
158
159 };
159 };
160
160
161 NotebookTour.prototype.start = function () {
161 NotebookTour.prototype.start = function () {
162 console.log("let's start the tour");
162 console.log("let's start the tour");
163 this.tour.init();
163 this.tour.start();
164 this.tour.start();
164 if (this.tour.ended())
165 if (this.tour.ended())
165 {
166 {
166 this.tour.restart();
167 this.tour.restart();
167 }
168 }
168 };
169 };
169
170
170 // Set module variables
171 // Set module variables
171 IPython.NotebookTour = NotebookTour;
172 IPython.NotebookTour = NotebookTour;
172
173
173 return IPython;
174 return IPython;
174
175
175 }(IPython));
176 }(IPython));
@@ -1,698 +1,699 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 from __future__ import print_function
11 from __future__ import print_function
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 #
15 #
16 # 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
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 import errno
23 import errno
24 import os
24 import os
25 import sys
25 import sys
26
26
27 from distutils.command.build_py import build_py
27 from distutils.command.build_py import build_py
28 from distutils.command.build_scripts import build_scripts
28 from distutils.command.build_scripts import build_scripts
29 from distutils.command.install import install
29 from distutils.command.install import install
30 from distutils.command.install_scripts import install_scripts
30 from distutils.command.install_scripts import install_scripts
31 from distutils.cmd import Command
31 from distutils.cmd import Command
32 from glob import glob
32 from glob import glob
33 from subprocess import call
33 from subprocess import call
34
34
35 from setupext import install_data_ext
35 from setupext import install_data_ext
36
36
37 #-------------------------------------------------------------------------------
37 #-------------------------------------------------------------------------------
38 # Useful globals and utility functions
38 # Useful globals and utility functions
39 #-------------------------------------------------------------------------------
39 #-------------------------------------------------------------------------------
40
40
41 # A few handy globals
41 # A few handy globals
42 isfile = os.path.isfile
42 isfile = os.path.isfile
43 pjoin = os.path.join
43 pjoin = os.path.join
44 repo_root = os.path.dirname(os.path.abspath(__file__))
44 repo_root = os.path.dirname(os.path.abspath(__file__))
45
45
46 def oscmd(s):
46 def oscmd(s):
47 print(">", s)
47 print(">", s)
48 os.system(s)
48 os.system(s)
49
49
50 # Py3 compatibility hacks, without assuming IPython itself is installed with
50 # Py3 compatibility hacks, without assuming IPython itself is installed with
51 # the full py3compat machinery.
51 # the full py3compat machinery.
52
52
53 try:
53 try:
54 execfile
54 execfile
55 except NameError:
55 except NameError:
56 def execfile(fname, globs, locs=None):
56 def execfile(fname, globs, locs=None):
57 locs = locs or globs
57 locs = locs or globs
58 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
58 exec(compile(open(fname).read(), fname, "exec"), globs, locs)
59
59
60 # A little utility we'll need below, since glob() does NOT allow you to do
60 # A little utility we'll need below, since glob() does NOT allow you to do
61 # exclusion on multiple endings!
61 # exclusion on multiple endings!
62 def file_doesnt_endwith(test,endings):
62 def file_doesnt_endwith(test,endings):
63 """Return true if test is a file and its name does NOT end with any
63 """Return true if test is a file and its name does NOT end with any
64 of the strings listed in endings."""
64 of the strings listed in endings."""
65 if not isfile(test):
65 if not isfile(test):
66 return False
66 return False
67 for e in endings:
67 for e in endings:
68 if test.endswith(e):
68 if test.endswith(e):
69 return False
69 return False
70 return True
70 return True
71
71
72 #---------------------------------------------------------------------------
72 #---------------------------------------------------------------------------
73 # Basic project information
73 # Basic project information
74 #---------------------------------------------------------------------------
74 #---------------------------------------------------------------------------
75
75
76 # release.py contains version, authors, license, url, keywords, etc.
76 # release.py contains version, authors, license, url, keywords, etc.
77 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
77 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
78
78
79 # Create a dict with the basic information
79 # Create a dict with the basic information
80 # This dict is eventually passed to setup after additional keys are added.
80 # This dict is eventually passed to setup after additional keys are added.
81 setup_args = dict(
81 setup_args = dict(
82 name = name,
82 name = name,
83 version = version,
83 version = version,
84 description = description,
84 description = description,
85 long_description = long_description,
85 long_description = long_description,
86 author = author,
86 author = author,
87 author_email = author_email,
87 author_email = author_email,
88 url = url,
88 url = url,
89 download_url = download_url,
89 download_url = download_url,
90 license = license,
90 license = license,
91 platforms = platforms,
91 platforms = platforms,
92 keywords = keywords,
92 keywords = keywords,
93 classifiers = classifiers,
93 classifiers = classifiers,
94 cmdclass = {'install_data': install_data_ext},
94 cmdclass = {'install_data': install_data_ext},
95 )
95 )
96
96
97
97
98 #---------------------------------------------------------------------------
98 #---------------------------------------------------------------------------
99 # Find packages
99 # Find packages
100 #---------------------------------------------------------------------------
100 #---------------------------------------------------------------------------
101
101
102 def find_packages():
102 def find_packages():
103 """
103 """
104 Find all of IPython's packages.
104 Find all of IPython's packages.
105 """
105 """
106 excludes = ['deathrow', 'quarantine']
106 excludes = ['deathrow', 'quarantine']
107 packages = []
107 packages = []
108 for dir,subdirs,files in os.walk('IPython'):
108 for dir,subdirs,files in os.walk('IPython'):
109 package = dir.replace(os.path.sep, '.')
109 package = dir.replace(os.path.sep, '.')
110 if any(package.startswith('IPython.'+exc) for exc in excludes):
110 if any(package.startswith('IPython.'+exc) for exc in excludes):
111 # package is to be excluded (e.g. deathrow)
111 # package is to be excluded (e.g. deathrow)
112 continue
112 continue
113 if '__init__.py' not in files:
113 if '__init__.py' not in files:
114 # not a package
114 # not a package
115 continue
115 continue
116 packages.append(package)
116 packages.append(package)
117 return packages
117 return packages
118
118
119 #---------------------------------------------------------------------------
119 #---------------------------------------------------------------------------
120 # Find package data
120 # Find package data
121 #---------------------------------------------------------------------------
121 #---------------------------------------------------------------------------
122
122
123 def find_package_data():
123 def find_package_data():
124 """
124 """
125 Find IPython's package_data.
125 Find IPython's package_data.
126 """
126 """
127 # This is not enough for these things to appear in an sdist.
127 # This is not enough for these things to appear in an sdist.
128 # We need to muck with the MANIFEST to get this to work
128 # We need to muck with the MANIFEST to get this to work
129
129
130 # exclude components from the walk,
130 # exclude components from the walk,
131 # we will build the components separately
131 # we will build the components separately
132 excludes = ['components']
132 excludes = ['components']
133
133
134 # add 'static/' prefix to exclusions, and tuplify for use in startswith
134 # add 'static/' prefix to exclusions, and tuplify for use in startswith
135 excludes = tuple([pjoin('static', ex) for ex in excludes])
135 excludes = tuple([pjoin('static', ex) for ex in excludes])
136
136
137 # walk notebook resources:
137 # walk notebook resources:
138 cwd = os.getcwd()
138 cwd = os.getcwd()
139 os.chdir(os.path.join('IPython', 'html'))
139 os.chdir(os.path.join('IPython', 'html'))
140 static_data = []
140 static_data = []
141 for parent, dirs, files in os.walk('static'):
141 for parent, dirs, files in os.walk('static'):
142 if parent.startswith(excludes):
142 if parent.startswith(excludes):
143 continue
143 continue
144 for f in files:
144 for f in files:
145 static_data.append(pjoin(parent, f))
145 static_data.append(pjoin(parent, f))
146 components = pjoin("static", "components")
146 components = pjoin("static", "components")
147 # select the components we actually need to install
147 # select the components we actually need to install
148 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
148 # (there are lots of resources we bundle for sdist-reasons that we don't actually use)
149 static_data.extend([
149 static_data.extend([
150 pjoin(components, "backbone", "backbone-min.js"),
150 pjoin(components, "backbone", "backbone-min.js"),
151 pjoin(components, "bootstrap", "bootstrap", "js", "bootstrap.min.js"),
151 pjoin(components, "bootstrap", "bootstrap", "js", "bootstrap.min.js"),
152 pjoin(components, "bootstrap-tour", "build", "css", "bootstrap-tour.min.css"),
152 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
153 pjoin(components, "bootstrap-tour", "build", "js", "bootstrap-tour.min.js"),
153 pjoin(components, "font-awesome", "font", "*.*"),
154 pjoin(components, "font-awesome", "font", "*.*"),
154 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
155 pjoin(components, "google-caja", "html-css-sanitizer-minified.js"),
155 pjoin(components, "highlight.js", "build", "highlight.pack.js"),
156 pjoin(components, "highlight.js", "build", "highlight.pack.js"),
156 pjoin(components, "jquery", "jquery.min.js"),
157 pjoin(components, "jquery", "jquery.min.js"),
157 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
158 pjoin(components, "jquery-ui", "ui", "minified", "jquery-ui.min.js"),
158 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
159 pjoin(components, "jquery-ui", "themes", "smoothness", "jquery-ui.min.css"),
159 pjoin(components, "marked", "lib", "marked.js"),
160 pjoin(components, "marked", "lib", "marked.js"),
160 pjoin(components, "requirejs", "require.js"),
161 pjoin(components, "requirejs", "require.js"),
161 pjoin(components, "underscore", "underscore-min.js"),
162 pjoin(components, "underscore", "underscore-min.js"),
162 ])
163 ])
163
164
164 # Ship all of Codemirror's CSS and JS
165 # Ship all of Codemirror's CSS and JS
165 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
166 for parent, dirs, files in os.walk(pjoin(components, 'codemirror')):
166 for f in files:
167 for f in files:
167 if f.endswith(('.js', '.css')):
168 if f.endswith(('.js', '.css')):
168 static_data.append(pjoin(parent, f))
169 static_data.append(pjoin(parent, f))
169
170
170 os.chdir(os.path.join('tests',))
171 os.chdir(os.path.join('tests',))
171 js_tests = glob('*.js') + glob('*/*.js')
172 js_tests = glob('*.js') + glob('*/*.js')
172
173
173 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
174 os.chdir(os.path.join(cwd, 'IPython', 'nbconvert'))
174 nbconvert_templates = [os.path.join(dirpath, '*.*')
175 nbconvert_templates = [os.path.join(dirpath, '*.*')
175 for dirpath, _, _ in os.walk('templates')]
176 for dirpath, _, _ in os.walk('templates')]
176
177
177 os.chdir(cwd)
178 os.chdir(cwd)
178
179
179 package_data = {
180 package_data = {
180 'IPython.config.profile' : ['README*', '*/*.py'],
181 'IPython.config.profile' : ['README*', '*/*.py'],
181 'IPython.core.tests' : ['*.png', '*.jpg'],
182 'IPython.core.tests' : ['*.png', '*.jpg'],
182 'IPython.lib.tests' : ['*.wav'],
183 'IPython.lib.tests' : ['*.wav'],
183 'IPython.testing.plugin' : ['*.txt'],
184 'IPython.testing.plugin' : ['*.txt'],
184 'IPython.html' : ['templates/*'] + static_data,
185 'IPython.html' : ['templates/*'] + static_data,
185 'IPython.html.tests' : js_tests,
186 'IPython.html.tests' : js_tests,
186 'IPython.qt.console' : ['resources/icon/*.svg'],
187 'IPython.qt.console' : ['resources/icon/*.svg'],
187 'IPython.nbconvert' : nbconvert_templates +
188 'IPython.nbconvert' : nbconvert_templates +
188 ['tests/files/*.*', 'exporters/tests/files/*.*'],
189 ['tests/files/*.*', 'exporters/tests/files/*.*'],
189 'IPython.nbconvert.filters' : ['marked.js'],
190 'IPython.nbconvert.filters' : ['marked.js'],
190 'IPython.nbformat' : ['tests/*.ipynb']
191 'IPython.nbformat' : ['tests/*.ipynb']
191 }
192 }
192
193
193 return package_data
194 return package_data
194
195
195
196
196 def check_package_data(package_data):
197 def check_package_data(package_data):
197 """verify that package_data globs make sense"""
198 """verify that package_data globs make sense"""
198 print("checking package data")
199 print("checking package data")
199 for pkg, data in package_data.items():
200 for pkg, data in package_data.items():
200 pkg_root = pjoin(*pkg.split('.'))
201 pkg_root = pjoin(*pkg.split('.'))
201 for d in data:
202 for d in data:
202 path = pjoin(pkg_root, d)
203 path = pjoin(pkg_root, d)
203 if '*' in path:
204 if '*' in path:
204 assert len(glob(path)) > 0, "No files match pattern %s" % path
205 assert len(glob(path)) > 0, "No files match pattern %s" % path
205 else:
206 else:
206 assert os.path.exists(path), "Missing package data: %s" % path
207 assert os.path.exists(path), "Missing package data: %s" % path
207
208
208
209
209 def check_package_data_first(command):
210 def check_package_data_first(command):
210 """decorator for checking package_data before running a given command
211 """decorator for checking package_data before running a given command
211
212
212 Probably only needs to wrap build_py
213 Probably only needs to wrap build_py
213 """
214 """
214 class DecoratedCommand(command):
215 class DecoratedCommand(command):
215 def run(self):
216 def run(self):
216 check_package_data(self.package_data)
217 check_package_data(self.package_data)
217 command.run(self)
218 command.run(self)
218 return DecoratedCommand
219 return DecoratedCommand
219
220
220
221
221 #---------------------------------------------------------------------------
222 #---------------------------------------------------------------------------
222 # Find data files
223 # Find data files
223 #---------------------------------------------------------------------------
224 #---------------------------------------------------------------------------
224
225
225 def make_dir_struct(tag,base,out_base):
226 def make_dir_struct(tag,base,out_base):
226 """Make the directory structure of all files below a starting dir.
227 """Make the directory structure of all files below a starting dir.
227
228
228 This is just a convenience routine to help build a nested directory
229 This is just a convenience routine to help build a nested directory
229 hierarchy because distutils is too stupid to do this by itself.
230 hierarchy because distutils is too stupid to do this by itself.
230
231
231 XXX - this needs a proper docstring!
232 XXX - this needs a proper docstring!
232 """
233 """
233
234
234 # we'll use these a lot below
235 # we'll use these a lot below
235 lbase = len(base)
236 lbase = len(base)
236 pathsep = os.path.sep
237 pathsep = os.path.sep
237 lpathsep = len(pathsep)
238 lpathsep = len(pathsep)
238
239
239 out = []
240 out = []
240 for (dirpath,dirnames,filenames) in os.walk(base):
241 for (dirpath,dirnames,filenames) in os.walk(base):
241 # we need to strip out the dirpath from the base to map it to the
242 # we need to strip out the dirpath from the base to map it to the
242 # output (installation) path. This requires possibly stripping the
243 # output (installation) path. This requires possibly stripping the
243 # path separator, because otherwise pjoin will not work correctly
244 # path separator, because otherwise pjoin will not work correctly
244 # (pjoin('foo/','/bar') returns '/bar').
245 # (pjoin('foo/','/bar') returns '/bar').
245
246
246 dp_eff = dirpath[lbase:]
247 dp_eff = dirpath[lbase:]
247 if dp_eff.startswith(pathsep):
248 if dp_eff.startswith(pathsep):
248 dp_eff = dp_eff[lpathsep:]
249 dp_eff = dp_eff[lpathsep:]
249 # The output path must be anchored at the out_base marker
250 # The output path must be anchored at the out_base marker
250 out_path = pjoin(out_base,dp_eff)
251 out_path = pjoin(out_base,dp_eff)
251 # Now we can generate the final filenames. Since os.walk only produces
252 # Now we can generate the final filenames. Since os.walk only produces
252 # filenames, we must join back with the dirpath to get full valid file
253 # filenames, we must join back with the dirpath to get full valid file
253 # paths:
254 # paths:
254 pfiles = [pjoin(dirpath,f) for f in filenames]
255 pfiles = [pjoin(dirpath,f) for f in filenames]
255 # Finally, generate the entry we need, which is a pari of (output
256 # Finally, generate the entry we need, which is a pari of (output
256 # path, files) for use as a data_files parameter in install_data.
257 # path, files) for use as a data_files parameter in install_data.
257 out.append((out_path, pfiles))
258 out.append((out_path, pfiles))
258
259
259 return out
260 return out
260
261
261
262
262 def find_data_files():
263 def find_data_files():
263 """
264 """
264 Find IPython's data_files.
265 Find IPython's data_files.
265
266
266 Just man pages at this point.
267 Just man pages at this point.
267 """
268 """
268
269
269 manpagebase = pjoin('share', 'man', 'man1')
270 manpagebase = pjoin('share', 'man', 'man1')
270
271
271 # Simple file lists can be made by hand
272 # Simple file lists can be made by hand
272 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
273 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
273 if not manpages:
274 if not manpages:
274 # When running from a source tree, the manpages aren't gzipped
275 # When running from a source tree, the manpages aren't gzipped
275 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
276 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
276
277
277 # And assemble the entire output list
278 # And assemble the entire output list
278 data_files = [ (manpagebase, manpages) ]
279 data_files = [ (manpagebase, manpages) ]
279
280
280 return data_files
281 return data_files
281
282
282
283
283 def make_man_update_target(manpage):
284 def make_man_update_target(manpage):
284 """Return a target_update-compliant tuple for the given manpage.
285 """Return a target_update-compliant tuple for the given manpage.
285
286
286 Parameters
287 Parameters
287 ----------
288 ----------
288 manpage : string
289 manpage : string
289 Name of the manpage, must include the section number (trailing number).
290 Name of the manpage, must include the section number (trailing number).
290
291
291 Example
292 Example
292 -------
293 -------
293
294
294 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
295 >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE
295 ('docs/man/ipython.1.gz',
296 ('docs/man/ipython.1.gz',
296 ['docs/man/ipython.1'],
297 ['docs/man/ipython.1'],
297 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
298 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz')
298 """
299 """
299 man_dir = pjoin('docs', 'man')
300 man_dir = pjoin('docs', 'man')
300 manpage_gz = manpage + '.gz'
301 manpage_gz = manpage + '.gz'
301 manpath = pjoin(man_dir, manpage)
302 manpath = pjoin(man_dir, manpage)
302 manpath_gz = pjoin(man_dir, manpage_gz)
303 manpath_gz = pjoin(man_dir, manpage_gz)
303 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
304 gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" %
304 locals() )
305 locals() )
305 return (manpath_gz, [manpath], gz_cmd)
306 return (manpath_gz, [manpath], gz_cmd)
306
307
307 # The two functions below are copied from IPython.utils.path, so we don't need
308 # The two functions below are copied from IPython.utils.path, so we don't need
308 # to import IPython during setup, which fails on Python 3.
309 # to import IPython during setup, which fails on Python 3.
309
310
310 def target_outdated(target,deps):
311 def target_outdated(target,deps):
311 """Determine whether a target is out of date.
312 """Determine whether a target is out of date.
312
313
313 target_outdated(target,deps) -> 1/0
314 target_outdated(target,deps) -> 1/0
314
315
315 deps: list of filenames which MUST exist.
316 deps: list of filenames which MUST exist.
316 target: single filename which may or may not exist.
317 target: single filename which may or may not exist.
317
318
318 If target doesn't exist or is older than any file listed in deps, return
319 If target doesn't exist or is older than any file listed in deps, return
319 true, otherwise return false.
320 true, otherwise return false.
320 """
321 """
321 try:
322 try:
322 target_time = os.path.getmtime(target)
323 target_time = os.path.getmtime(target)
323 except os.error:
324 except os.error:
324 return 1
325 return 1
325 for dep in deps:
326 for dep in deps:
326 dep_time = os.path.getmtime(dep)
327 dep_time = os.path.getmtime(dep)
327 if dep_time > target_time:
328 if dep_time > target_time:
328 #print "For target",target,"Dep failed:",dep # dbg
329 #print "For target",target,"Dep failed:",dep # dbg
329 #print "times (dep,tar):",dep_time,target_time # dbg
330 #print "times (dep,tar):",dep_time,target_time # dbg
330 return 1
331 return 1
331 return 0
332 return 0
332
333
333
334
334 def target_update(target,deps,cmd):
335 def target_update(target,deps,cmd):
335 """Update a target with a given command given a list of dependencies.
336 """Update a target with a given command given a list of dependencies.
336
337
337 target_update(target,deps,cmd) -> runs cmd if target is outdated.
338 target_update(target,deps,cmd) -> runs cmd if target is outdated.
338
339
339 This is just a wrapper around target_outdated() which calls the given
340 This is just a wrapper around target_outdated() which calls the given
340 command if target is outdated."""
341 command if target is outdated."""
341
342
342 if target_outdated(target,deps):
343 if target_outdated(target,deps):
343 os.system(cmd)
344 os.system(cmd)
344
345
345 #---------------------------------------------------------------------------
346 #---------------------------------------------------------------------------
346 # Find scripts
347 # Find scripts
347 #---------------------------------------------------------------------------
348 #---------------------------------------------------------------------------
348
349
349 def find_entry_points():
350 def find_entry_points():
350 """Find IPython's scripts.
351 """Find IPython's scripts.
351
352
352 if entry_points is True:
353 if entry_points is True:
353 return setuptools entry_point-style definitions
354 return setuptools entry_point-style definitions
354 else:
355 else:
355 return file paths of plain scripts [default]
356 return file paths of plain scripts [default]
356
357
357 suffix is appended to script names if entry_points is True, so that the
358 suffix is appended to script names if entry_points is True, so that the
358 Python 3 scripts get named "ipython3" etc.
359 Python 3 scripts get named "ipython3" etc.
359 """
360 """
360 ep = [
361 ep = [
361 'ipython%s = IPython:start_ipython',
362 'ipython%s = IPython:start_ipython',
362 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
363 'ipcontroller%s = IPython.parallel.apps.ipcontrollerapp:launch_new_instance',
363 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
364 'ipengine%s = IPython.parallel.apps.ipengineapp:launch_new_instance',
364 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
365 'ipcluster%s = IPython.parallel.apps.ipclusterapp:launch_new_instance',
365 'iptest%s = IPython.testing.iptestcontroller:main',
366 'iptest%s = IPython.testing.iptestcontroller:main',
366 ]
367 ]
367 suffix = str(sys.version_info[0])
368 suffix = str(sys.version_info[0])
368 return [e % '' for e in ep] + [e % suffix for e in ep]
369 return [e % '' for e in ep] + [e % suffix for e in ep]
369
370
370 script_src = """#!{executable}
371 script_src = """#!{executable}
371 # This script was automatically generated by setup.py
372 # This script was automatically generated by setup.py
372 if __name__ == '__main__':
373 if __name__ == '__main__':
373 from {mod} import {func}
374 from {mod} import {func}
374 {func}()
375 {func}()
375 """
376 """
376
377
377 class build_scripts_entrypt(build_scripts):
378 class build_scripts_entrypt(build_scripts):
378 def run(self):
379 def run(self):
379 self.mkpath(self.build_dir)
380 self.mkpath(self.build_dir)
380 outfiles = []
381 outfiles = []
381 for script in find_entry_points():
382 for script in find_entry_points():
382 name, entrypt = script.split('=')
383 name, entrypt = script.split('=')
383 name = name.strip()
384 name = name.strip()
384 entrypt = entrypt.strip()
385 entrypt = entrypt.strip()
385 outfile = os.path.join(self.build_dir, name)
386 outfile = os.path.join(self.build_dir, name)
386 outfiles.append(outfile)
387 outfiles.append(outfile)
387 print('Writing script to', outfile)
388 print('Writing script to', outfile)
388
389
389 mod, func = entrypt.split(':')
390 mod, func = entrypt.split(':')
390 with open(outfile, 'w') as f:
391 with open(outfile, 'w') as f:
391 f.write(script_src.format(executable=sys.executable,
392 f.write(script_src.format(executable=sys.executable,
392 mod=mod, func=func))
393 mod=mod, func=func))
393
394
394 return outfiles, outfiles
395 return outfiles, outfiles
395
396
396 class install_lib_symlink(Command):
397 class install_lib_symlink(Command):
397 user_options = [
398 user_options = [
398 ('install-dir=', 'd', "directory to install to"),
399 ('install-dir=', 'd', "directory to install to"),
399 ]
400 ]
400
401
401 def initialize_options(self):
402 def initialize_options(self):
402 self.install_dir = None
403 self.install_dir = None
403
404
404 def finalize_options(self):
405 def finalize_options(self):
405 self.set_undefined_options('symlink',
406 self.set_undefined_options('symlink',
406 ('install_lib', 'install_dir'),
407 ('install_lib', 'install_dir'),
407 )
408 )
408
409
409 def run(self):
410 def run(self):
410 if sys.platform == 'win32':
411 if sys.platform == 'win32':
411 raise Exception("This doesn't work on Windows.")
412 raise Exception("This doesn't work on Windows.")
412 pkg = os.path.join(os.getcwd(), 'IPython')
413 pkg = os.path.join(os.getcwd(), 'IPython')
413 dest = os.path.join(self.install_dir, 'IPython')
414 dest = os.path.join(self.install_dir, 'IPython')
414 if os.path.islink(dest):
415 if os.path.islink(dest):
415 print('removing existing symlink at %s' % dest)
416 print('removing existing symlink at %s' % dest)
416 os.unlink(dest)
417 os.unlink(dest)
417 print('symlinking %s -> %s' % (pkg, dest))
418 print('symlinking %s -> %s' % (pkg, dest))
418 os.symlink(pkg, dest)
419 os.symlink(pkg, dest)
419
420
420 class unsymlink(install):
421 class unsymlink(install):
421 def run(self):
422 def run(self):
422 dest = os.path.join(self.install_lib, 'IPython')
423 dest = os.path.join(self.install_lib, 'IPython')
423 if os.path.islink(dest):
424 if os.path.islink(dest):
424 print('removing symlink at %s' % dest)
425 print('removing symlink at %s' % dest)
425 os.unlink(dest)
426 os.unlink(dest)
426 else:
427 else:
427 print('No symlink exists at %s' % dest)
428 print('No symlink exists at %s' % dest)
428
429
429 class install_symlinked(install):
430 class install_symlinked(install):
430 def run(self):
431 def run(self):
431 if sys.platform == 'win32':
432 if sys.platform == 'win32':
432 raise Exception("This doesn't work on Windows.")
433 raise Exception("This doesn't work on Windows.")
433
434
434 # Run all sub-commands (at least those that need to be run)
435 # Run all sub-commands (at least those that need to be run)
435 for cmd_name in self.get_sub_commands():
436 for cmd_name in self.get_sub_commands():
436 self.run_command(cmd_name)
437 self.run_command(cmd_name)
437
438
438 # 'sub_commands': a list of commands this command might have to run to
439 # 'sub_commands': a list of commands this command might have to run to
439 # get its work done. See cmd.py for more info.
440 # get its work done. See cmd.py for more info.
440 sub_commands = [('install_lib_symlink', lambda self:True),
441 sub_commands = [('install_lib_symlink', lambda self:True),
441 ('install_scripts_sym', lambda self:True),
442 ('install_scripts_sym', lambda self:True),
442 ]
443 ]
443
444
444 class install_scripts_for_symlink(install_scripts):
445 class install_scripts_for_symlink(install_scripts):
445 """Redefined to get options from 'symlink' instead of 'install'.
446 """Redefined to get options from 'symlink' instead of 'install'.
446
447
447 I love distutils almost as much as I love setuptools.
448 I love distutils almost as much as I love setuptools.
448 """
449 """
449 def finalize_options(self):
450 def finalize_options(self):
450 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
451 self.set_undefined_options('build', ('build_scripts', 'build_dir'))
451 self.set_undefined_options('symlink',
452 self.set_undefined_options('symlink',
452 ('install_scripts', 'install_dir'),
453 ('install_scripts', 'install_dir'),
453 ('force', 'force'),
454 ('force', 'force'),
454 ('skip_build', 'skip_build'),
455 ('skip_build', 'skip_build'),
455 )
456 )
456
457
457 #---------------------------------------------------------------------------
458 #---------------------------------------------------------------------------
458 # Verify all dependencies
459 # Verify all dependencies
459 #---------------------------------------------------------------------------
460 #---------------------------------------------------------------------------
460
461
461 def check_for_dependencies():
462 def check_for_dependencies():
462 """Check for IPython's dependencies.
463 """Check for IPython's dependencies.
463
464
464 This function should NOT be called if running under setuptools!
465 This function should NOT be called if running under setuptools!
465 """
466 """
466 from setupext.setupext import (
467 from setupext.setupext import (
467 print_line, print_raw, print_status,
468 print_line, print_raw, print_status,
468 check_for_sphinx, check_for_pygments,
469 check_for_sphinx, check_for_pygments,
469 check_for_nose, check_for_pexpect,
470 check_for_nose, check_for_pexpect,
470 check_for_pyzmq, check_for_readline,
471 check_for_pyzmq, check_for_readline,
471 check_for_jinja2, check_for_tornado
472 check_for_jinja2, check_for_tornado
472 )
473 )
473 print_line()
474 print_line()
474 print_raw("BUILDING IPYTHON")
475 print_raw("BUILDING IPYTHON")
475 print_status('python', sys.version)
476 print_status('python', sys.version)
476 print_status('platform', sys.platform)
477 print_status('platform', sys.platform)
477 if sys.platform == 'win32':
478 if sys.platform == 'win32':
478 print_status('Windows version', sys.getwindowsversion())
479 print_status('Windows version', sys.getwindowsversion())
479
480
480 print_raw("")
481 print_raw("")
481 print_raw("OPTIONAL DEPENDENCIES")
482 print_raw("OPTIONAL DEPENDENCIES")
482
483
483 check_for_sphinx()
484 check_for_sphinx()
484 check_for_pygments()
485 check_for_pygments()
485 check_for_nose()
486 check_for_nose()
486 if os.name == 'posix':
487 if os.name == 'posix':
487 check_for_pexpect()
488 check_for_pexpect()
488 check_for_pyzmq()
489 check_for_pyzmq()
489 check_for_tornado()
490 check_for_tornado()
490 check_for_readline()
491 check_for_readline()
491 check_for_jinja2()
492 check_for_jinja2()
492
493
493 #---------------------------------------------------------------------------
494 #---------------------------------------------------------------------------
494 # VCS related
495 # VCS related
495 #---------------------------------------------------------------------------
496 #---------------------------------------------------------------------------
496
497
497 # utils.submodule has checks for submodule status
498 # utils.submodule has checks for submodule status
498 execfile(pjoin('IPython','utils','submodule.py'), globals())
499 execfile(pjoin('IPython','utils','submodule.py'), globals())
499
500
500 class UpdateSubmodules(Command):
501 class UpdateSubmodules(Command):
501 """Update git submodules
502 """Update git submodules
502
503
503 IPython's external javascript dependencies live in a separate repo.
504 IPython's external javascript dependencies live in a separate repo.
504 """
505 """
505 description = "Update git submodules"
506 description = "Update git submodules"
506 user_options = []
507 user_options = []
507
508
508 def initialize_options(self):
509 def initialize_options(self):
509 pass
510 pass
510
511
511 def finalize_options(self):
512 def finalize_options(self):
512 pass
513 pass
513
514
514 def run(self):
515 def run(self):
515 failure = False
516 failure = False
516 try:
517 try:
517 self.spawn('git submodule init'.split())
518 self.spawn('git submodule init'.split())
518 self.spawn('git submodule update --recursive'.split())
519 self.spawn('git submodule update --recursive'.split())
519 except Exception as e:
520 except Exception as e:
520 failure = e
521 failure = e
521 print(e)
522 print(e)
522
523
523 if not check_submodule_status(repo_root) == 'clean':
524 if not check_submodule_status(repo_root) == 'clean':
524 print("submodules could not be checked out")
525 print("submodules could not be checked out")
525 sys.exit(1)
526 sys.exit(1)
526
527
527
528
528 def git_prebuild(pkg_dir, build_cmd=build_py):
529 def git_prebuild(pkg_dir, build_cmd=build_py):
529 """Return extended build or sdist command class for recording commit
530 """Return extended build or sdist command class for recording commit
530
531
531 records git commit in IPython.utils._sysinfo.commit
532 records git commit in IPython.utils._sysinfo.commit
532
533
533 for use in IPython.utils.sysinfo.sys_info() calls after installation.
534 for use in IPython.utils.sysinfo.sys_info() calls after installation.
534
535
535 Also ensures that submodules exist prior to running
536 Also ensures that submodules exist prior to running
536 """
537 """
537
538
538 class MyBuildPy(build_cmd):
539 class MyBuildPy(build_cmd):
539 ''' Subclass to write commit data into installation tree '''
540 ''' Subclass to write commit data into installation tree '''
540 def run(self):
541 def run(self):
541 build_cmd.run(self)
542 build_cmd.run(self)
542 # this one will only fire for build commands
543 # this one will only fire for build commands
543 if hasattr(self, 'build_lib'):
544 if hasattr(self, 'build_lib'):
544 self._record_commit(self.build_lib)
545 self._record_commit(self.build_lib)
545
546
546 def make_release_tree(self, base_dir, files):
547 def make_release_tree(self, base_dir, files):
547 # this one will fire for sdist
548 # this one will fire for sdist
548 build_cmd.make_release_tree(self, base_dir, files)
549 build_cmd.make_release_tree(self, base_dir, files)
549 self._record_commit(base_dir)
550 self._record_commit(base_dir)
550
551
551 def _record_commit(self, base_dir):
552 def _record_commit(self, base_dir):
552 import subprocess
553 import subprocess
553 proc = subprocess.Popen('git rev-parse --short HEAD',
554 proc = subprocess.Popen('git rev-parse --short HEAD',
554 stdout=subprocess.PIPE,
555 stdout=subprocess.PIPE,
555 stderr=subprocess.PIPE,
556 stderr=subprocess.PIPE,
556 shell=True)
557 shell=True)
557 repo_commit, _ = proc.communicate()
558 repo_commit, _ = proc.communicate()
558 repo_commit = repo_commit.strip().decode("ascii")
559 repo_commit = repo_commit.strip().decode("ascii")
559
560
560 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
561 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
561 if os.path.isfile(out_pth) and not repo_commit:
562 if os.path.isfile(out_pth) and not repo_commit:
562 # nothing to write, don't clobber
563 # nothing to write, don't clobber
563 return
564 return
564
565
565 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
566 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
566
567
567 # remove to avoid overwriting original via hard link
568 # remove to avoid overwriting original via hard link
568 try:
569 try:
569 os.remove(out_pth)
570 os.remove(out_pth)
570 except (IOError, OSError):
571 except (IOError, OSError):
571 pass
572 pass
572 with open(out_pth, 'w') as out_file:
573 with open(out_pth, 'w') as out_file:
573 out_file.writelines([
574 out_file.writelines([
574 '# GENERATED BY setup.py\n',
575 '# GENERATED BY setup.py\n',
575 'commit = "%s"\n' % repo_commit,
576 'commit = "%s"\n' % repo_commit,
576 ])
577 ])
577 return require_submodules(MyBuildPy)
578 return require_submodules(MyBuildPy)
578
579
579
580
580 def require_submodules(command):
581 def require_submodules(command):
581 """decorator for instructing a command to check for submodules before running"""
582 """decorator for instructing a command to check for submodules before running"""
582 class DecoratedCommand(command):
583 class DecoratedCommand(command):
583 def run(self):
584 def run(self):
584 if not check_submodule_status(repo_root) == 'clean':
585 if not check_submodule_status(repo_root) == 'clean':
585 print("submodules missing! Run `setup.py submodule` and try again")
586 print("submodules missing! Run `setup.py submodule` and try again")
586 sys.exit(1)
587 sys.exit(1)
587 command.run(self)
588 command.run(self)
588 return DecoratedCommand
589 return DecoratedCommand
589
590
590 #---------------------------------------------------------------------------
591 #---------------------------------------------------------------------------
591 # bdist related
592 # bdist related
592 #---------------------------------------------------------------------------
593 #---------------------------------------------------------------------------
593
594
594 def get_bdist_wheel():
595 def get_bdist_wheel():
595 """Construct bdist_wheel command for building wheels
596 """Construct bdist_wheel command for building wheels
596
597
597 Constructs py2-none-any tag, instead of py2.7-none-any
598 Constructs py2-none-any tag, instead of py2.7-none-any
598 """
599 """
599 class RequiresWheel(Command):
600 class RequiresWheel(Command):
600 description = "Dummy command for missing bdist_wheel"
601 description = "Dummy command for missing bdist_wheel"
601 user_options = []
602 user_options = []
602
603
603 def initialize_options(self):
604 def initialize_options(self):
604 pass
605 pass
605
606
606 def finalize_options(self):
607 def finalize_options(self):
607 pass
608 pass
608
609
609 def run(self):
610 def run(self):
610 print("bdist_wheel requires the wheel package")
611 print("bdist_wheel requires the wheel package")
611 sys.exit(1)
612 sys.exit(1)
612
613
613 if 'setuptools' not in sys.modules:
614 if 'setuptools' not in sys.modules:
614 return RequiresWheel
615 return RequiresWheel
615 else:
616 else:
616 try:
617 try:
617 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
618 from wheel.bdist_wheel import bdist_wheel, read_pkg_info, write_pkg_info
618 except ImportError:
619 except ImportError:
619 return RequiresWheel
620 return RequiresWheel
620
621
621 class bdist_wheel_tag(bdist_wheel):
622 class bdist_wheel_tag(bdist_wheel):
622
623
623 def get_tag(self):
624 def get_tag(self):
624 return ('py%i' % sys.version_info[0], 'none', 'any')
625 return ('py%i' % sys.version_info[0], 'none', 'any')
625
626
626 def add_requirements(self, metadata_path):
627 def add_requirements(self, metadata_path):
627 """transform platform-dependent requirements"""
628 """transform platform-dependent requirements"""
628 pkg_info = read_pkg_info(metadata_path)
629 pkg_info = read_pkg_info(metadata_path)
629 # pkg_info is an email.Message object (?!)
630 # pkg_info is an email.Message object (?!)
630 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
631 # we have to remove the unconditional 'readline' and/or 'pyreadline' entries
631 # and transform them to conditionals
632 # and transform them to conditionals
632 requires = pkg_info.get_all('Requires-Dist')
633 requires = pkg_info.get_all('Requires-Dist')
633 del pkg_info['Requires-Dist']
634 del pkg_info['Requires-Dist']
634 def _remove_startswith(lis, prefix):
635 def _remove_startswith(lis, prefix):
635 """like list.remove, but with startswith instead of =="""
636 """like list.remove, but with startswith instead of =="""
636 found = False
637 found = False
637 for idx, item in enumerate(lis):
638 for idx, item in enumerate(lis):
638 if item.startswith(prefix):
639 if item.startswith(prefix):
639 found = True
640 found = True
640 break
641 break
641 if found:
642 if found:
642 lis.pop(idx)
643 lis.pop(idx)
643
644
644 for pkg in ("gnureadline", "pyreadline", "mock"):
645 for pkg in ("gnureadline", "pyreadline", "mock"):
645 _remove_startswith(requires, pkg)
646 _remove_startswith(requires, pkg)
646 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
647 requires.append("gnureadline; sys.platform == 'darwin' and platform.python_implementation == 'CPython'")
647 requires.append("pyreadline (>=2.0); sys.platform == 'win32' and platform.python_implementation == 'CPython'")
648 requires.append("pyreadline (>=2.0); sys.platform == 'win32' and platform.python_implementation == 'CPython'")
648 requires.append("mock; extra == 'test' and python_version < '3.3'")
649 requires.append("mock; extra == 'test' and python_version < '3.3'")
649 for r in requires:
650 for r in requires:
650 pkg_info['Requires-Dist'] = r
651 pkg_info['Requires-Dist'] = r
651 write_pkg_info(metadata_path, pkg_info)
652 write_pkg_info(metadata_path, pkg_info)
652
653
653 return bdist_wheel_tag
654 return bdist_wheel_tag
654
655
655 #---------------------------------------------------------------------------
656 #---------------------------------------------------------------------------
656 # Notebook related
657 # Notebook related
657 #---------------------------------------------------------------------------
658 #---------------------------------------------------------------------------
658
659
659 class CompileCSS(Command):
660 class CompileCSS(Command):
660 """Recompile Notebook CSS
661 """Recompile Notebook CSS
661
662
662 Regenerate the compiled CSS from LESS sources.
663 Regenerate the compiled CSS from LESS sources.
663
664
664 Requires various dev dependencies, such as fabric and lessc.
665 Requires various dev dependencies, such as fabric and lessc.
665 """
666 """
666 description = "Recompile Notebook CSS"
667 description = "Recompile Notebook CSS"
667 user_options = []
668 user_options = []
668
669
669 def initialize_options(self):
670 def initialize_options(self):
670 pass
671 pass
671
672
672 def finalize_options(self):
673 def finalize_options(self):
673 pass
674 pass
674
675
675 def run(self):
676 def run(self):
676 call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html"))
677 call("fab css", shell=True, cwd=pjoin(repo_root, "IPython", "html"))
677
678
678 class JavascriptVersion(Command):
679 class JavascriptVersion(Command):
679 """write the javascript version to notebook javascript"""
680 """write the javascript version to notebook javascript"""
680 description = "Write IPython version to javascript"
681 description = "Write IPython version to javascript"
681 user_options = []
682 user_options = []
682
683
683 def initialize_options(self):
684 def initialize_options(self):
684 pass
685 pass
685
686
686 def finalize_options(self):
687 def finalize_options(self):
687 pass
688 pass
688
689
689 def run(self):
690 def run(self):
690 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
691 nsfile = pjoin(repo_root, "IPython", "html", "static", "base", "js", "namespace.js")
691 with open(nsfile) as f:
692 with open(nsfile) as f:
692 lines = f.readlines()
693 lines = f.readlines()
693 with open(nsfile, 'w') as f:
694 with open(nsfile, 'w') as f:
694 for line in lines:
695 for line in lines:
695 if line.startswith("IPython.version"):
696 if line.startswith("IPython.version"):
696 line = 'IPython.version = "{0}";\n'.format(version)
697 line = 'IPython.version = "{0}";\n'.format(version)
697 f.write(line)
698 f.write(line)
698
699
General Comments 0
You need to be logged in to leave comments. Login now