Merge lp:~cjwatson/launchpad/simplify-buildout-bin-python-easy into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18314
Proposed branch: lp:~cjwatson/launchpad/simplify-buildout-bin-python-easy
Merge into: lp:launchpad
Diff against target: 453 lines (+149/-140)
9 files modified
Makefile (+1/-1)
lib/lp/app/templates/base-layout-macros.pt (+1/-1)
lib/lp/scripts/utilities/bzr.py (+10/-12)
lib/lp/scripts/utilities/js/combinecss.py (+32/-35)
lib/lp/scripts/utilities/js/watchjsbuild.py (+10/-2)
lib/lp/scripts/utilities/killtestservices.py (+19/-26)
lib/lp/scripts/utilities/spriteutil.py (+53/-47)
lib/lp/testing/utilities/retest.py (+13/-10)
setup.py (+10/-6)
To merge this branch: bzr merge lp:~cjwatson/launchpad/simplify-buildout-bin-python-easy
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+314976@code.launchpad.net

Commit message

Turn most Python scripts in buildout-templates/bin/ into entry points to utility modules.

Description of the change

buildout-templates/bin/test.in still remains after this, but that's by far the most complicated and my conversion to a utility module still seems to have a subtle bug in it somewhere, so I'll do that in a follow-up branch once I work out the details.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2016-09-21 02:51:58 +0000
+++ Makefile 2017-01-18 08:50:25 +0000
@@ -47,7 +47,7 @@
47# NB: It's important BUILDOUT_BIN only mentions things genuinely produced by47# NB: It's important BUILDOUT_BIN only mentions things genuinely produced by
48# buildout.48# buildout.
49BUILDOUT_BIN = \49BUILDOUT_BIN = \
50 $(PY) bin/apiindex bin/combine-css bin/fl-build-report \50 $(PY) bin/apiindex bin/bzr bin/combine-css bin/fl-build-report \
51 bin/fl-credential-ctl bin/fl-install-demo bin/fl-monitor-ctl \51 bin/fl-credential-ctl bin/fl-install-demo bin/fl-monitor-ctl \
52 bin/fl-record bin/fl-run-bench bin/fl-run-test bin/googletestservice \52 bin/fl-record bin/fl-run-bench bin/fl-run-test bin/googletestservice \
53 bin/i18ncompile bin/i18nextract bin/i18nmergeall bin/i18nstats \53 bin/i18ncompile bin/i18nextract bin/i18nmergeall bin/i18nstats \
5454
=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
--- lib/lp/app/templates/base-layout-macros.pt 2016-09-14 11:13:06 +0000
+++ lib/lp/app/templates/base-layout-macros.pt 2017-01-18 08:50:25 +0000
@@ -198,7 +198,7 @@
198 <tal:comment replace="nothing">198 <tal:comment replace="nothing">
199 This macro loads a single css file containing all our stylesheets.199 This macro loads a single css file containing all our stylesheets.
200 If you need to include a new css file here, add it to200 If you need to include a new css file here, add it to
201 buildout-templates/bin/combine-css.in instead.201 lib/lp/scripts/utilities/js/combinecss.py instead.
202202
203 We load the CSS from the same host that served the HTML in order to optimize203 We load the CSS from the same host that served the HTML in order to optimize
204 IE caching over SSL. This is inefficient when you cross subdomains (from204 IE caching over SSL. This is inefficient when you cross subdomains (from
205205
=== renamed file 'buildout-templates/bin/bzr.in' => 'lib/lp/scripts/utilities/bzr.py'
--- buildout-templates/bin/bzr.in 2010-11-24 17:27:46 +0000
+++ lib/lp/scripts/utilities/bzr.py 2017-01-18 08:50:25 +0000
@@ -1,15 +1,13 @@
1#!${buildout:executable} -S1# Copyright 2010-2017 Canonical Ltd. This software is licensed under the
22# GNU Affero General Public License version 3 (see the file LICENSE).
3# Initialize our paths.3
4${python-relative-path-setup}
5import sys
6sys.path.insert(0, ${scripts:parts-directory|path-repr})
7import site
8
9# Run the script.
10import pkg_resources4import pkg_resources
115
12bzr_distribution = pkg_resources.get_distribution(
13 pkg_resources.Requirement.parse('bzr'))
146
15bzr_distribution.run_script('bzr', globals().copy())7def main():
8 # Run the script.
9 bzr_distribution = pkg_resources.get_distribution(
10 pkg_resources.Requirement.parse('bzr'))
11 namespace = globals().copy()
12 namespace['__name__'] = '__main__'
13 bzr_distribution.run_script('bzr', namespace)
1614
=== renamed file 'buildout-templates/bin/combine-css.in' => 'lib/lp/scripts/utilities/js/combinecss.py'
--- buildout-templates/bin/combine-css.in 2013-07-04 01:04:50 +0000
+++ lib/lp/scripts/utilities/js/combinecss.py 2017-01-18 08:50:25 +0000
@@ -1,20 +1,13 @@
1#!${buildout:executable} -S1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
22# GNU Affero General Public License version 3 (see the file LICENSE).
3# Initialize our paths.
4${python-relative-path-setup}
5import sys
6sys.path.insert(0, ${scripts:parts-directory|path-repr})
7import site
83
9import os4import os
105
11from lp.scripts.utilities.js.jsbuild import ComboFile6from lp.scripts.utilities.js.jsbuild import ComboFile
12from lp.scripts.utilities.js.combo import combine_files7from lp.scripts.utilities.js.combo import combine_files
138from lp.services.config import config
14root = os.path.abspath('.')9
15root = os.path.normpath(${buildout:directory|path-repr})10
16icing = os.path.join(root, 'lib/canonical/launchpad/icing')
17target = os.path.join(icing, 'combo.css')
18# It'd probably be nice to have this script find all the CSS files we might11# It'd probably be nice to have this script find all the CSS files we might
19# need and combine them together, but if we do that we'd certainly end up12# need and combine them together, but if we do that we'd certainly end up
20# including lots of styles that we don't need/want, so keeping this hard-coded13# including lots of styles that we don't need/want, so keeping this hard-coded
@@ -58,26 +51,30 @@
58 'css/layout.css',51 'css/layout.css',
59 'css/modifiers.css']52 'css/modifiers.css']
6053
61# Get all the component css files so we don't have to edit this file every54
62# time a new component is added55def main():
63component_dir = 'css/components'56 icing = os.path.join(config.root, 'lib/canonical/launchpad/icing')
64component_path = os.path.abspath(os.path.join(icing, component_dir))57 target = os.path.join(icing, 'combo.css')
65for root, dirs, files in os.walk(component_path):58
66 for file in files:59 # Get all the component css files so we don't have to edit this file
67 if file.endswith('.css'):60 # every time a new component is added.
68 names.append('%s/%s' % (component_dir, file))61 component_dir = 'css/components'
6962 component_path = os.path.abspath(os.path.join(icing, component_dir))
70absolute_names = []63 for root, dirs, files in os.walk(component_path):
71for name in names:64 for file in files:
72 full_path_name = os.path.abspath(os.path.join(icing, name))65 if file.endswith('.css'):
73 absolute_names.append(full_path_name)66 names.append('%s/%s' % (component_dir, file))
7467
75combo = ComboFile(absolute_names, target)68 absolute_names = []
76if combo.needs_update():69 for name in names:
77 result = ''70 full_path_name = os.path.abspath(os.path.join(icing, name))
78 for content in combine_files(names, icing):71 absolute_names.append(full_path_name)
79 result += content72
8073 combo = ComboFile(absolute_names, target)
81 f = open(target, 'w')74 if combo.needs_update():
82 f.write(result)75 result = ''
83 f.close()76 for content in combine_files(names, icing):
77 result += content
78
79 with open(target, 'w') as f:
80 f.write(result)
8481
=== renamed file 'buildout-templates/bin/watch_jsbuild.in' => 'lib/lp/scripts/utilities/js/watchjsbuild.py'
--- buildout-templates/bin/watch_jsbuild.in 2012-02-28 19:40:50 +0000
+++ lib/lp/scripts/utilities/js/watchjsbuild.py 2017-01-18 08:50:25 +0000
@@ -1,8 +1,16 @@
1#!/usr/bin/env python1# Copyright 2016 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Automatically rebuild the JavaScript bundle when source files change."""
5
6from __future__ import absolute_import, print_function
7
2import os8import os
3import re9import re
10
4from jsautobuild import YUIBuilder11from jsautobuild import YUIBuilder
512
13
6# Using ionotify we watch our sources of JavaScript in order to know we should14# Using ionotify we watch our sources of JavaScript in order to know we should
7# build when the files change.15# build when the files change.
816
@@ -18,7 +26,7 @@
18 return os.path.join(JSDIR, RENAME.sub(js_dir, changed_path))26 return os.path.join(JSDIR, RENAME.sub(js_dir, changed_path))
1927
2028
21if __name__ == "__main__":29def main():
22 build_dir = 'build/js/lp'30 build_dir = 'build/js/lp'
23 meta_name = 'LP_MODULES'31 meta_name = 'LP_MODULES'
24 watch_dir = 'lib'32 watch_dir = 'lib'
2533
=== renamed file 'buildout-templates/bin/kill-test-services.in' => 'lib/lp/scripts/utilities/killtestservices.py'
--- buildout-templates/bin/kill-test-services.in 2011-12-30 01:48:17 +0000
+++ lib/lp/scripts/utilities/killtestservices.py 2017-01-18 08:50:25 +0000
@@ -1,40 +1,33 @@
1#!${buildout:executable} -S1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2#
3# Copyright 2009 Canonical Ltd. This software is licensed under the
4# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
3
5"""Kill all the test services that may persist between test runs."""4"""Kill all the test services that may persist between test runs."""
65
7# Initialize our paths.6from __future__ import print_function
8${python-relative-path-setup}7
9import sys8import sys
10sys.path.insert(0, ${scripts:parts-directory|path-repr})
11import site
129
13# Tell lp.services.config to use the testrunner config instance, so that
14# we don't kill the real services.
15from lp.services.config import config10from lp.services.config import config
16config.setInstance('testrunner')11from lp.services.librarianserver.testing.server import LibrarianServerFixture
17config.generate_overrides()
18
19import sys
20
21from lp.services.librarianserver.testing.server import LibrarianTestSetup
22from lp.services.osutils import kill_by_pidfile12from lp.services.osutils import kill_by_pidfile
23from lp.testing.layers import MemcachedLayer13from lp.testing.layers import MemcachedLayer
2414
2515
26def main(args):16def main():
17 args = sys.argv[1:]
27 if '-h' in args or '--help' in args:18 if '-h' in args or '--help' in args:
28 print __doc__19 print(__doc__)
29 return 020 return 0
30 print "Killing Memcached....",21 # Tell lp.services.config to use the testrunner config instance, so that
22 # we don't kill the real services.
23 config.setInstance('testrunner')
24 config.generate_overrides()
25 print("Killing Memcached....", end="")
31 kill_by_pidfile(MemcachedLayer.getPidFile())26 kill_by_pidfile(MemcachedLayer.getPidFile())
32 print "done."27 print("done.")
33 print "Killing Librarian....",28 print("Killing Librarian....", end="")
34 LibrarianTestSetup().tearDownRoot()29 librarian_fixture = LibrarianServerFixture(None)
35 print "done."30 kill_by_pidfile(librarian_fixture.pidfile)
31 librarian_fixture.tearDownRoot()
32 print("done.")
36 return 033 return 0
37
38
39if __name__ == '__main__':
40 sys.exit(main(sys.argv[1:]))
4134
=== renamed file 'buildout-templates/bin/sprite-util.in' => 'lib/lp/scripts/utilities/spriteutil.py'
--- buildout-templates/bin/sprite-util.in 2012-06-02 12:08:17 +0000
+++ lib/lp/scripts/utilities/spriteutil.py 2017-01-18 08:50:25 +0000
@@ -1,59 +1,65 @@
1#!${buildout:executable} -S1# Copyright 2010-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Create sprites."""
5
6from __future__ import absolute_import, print_function
27
3import os8import os
4import sys9import sys
510
6# Initialize our paths.11from lp.services.config import config
7${python-relative-path-setup}
8sys.path.insert(0, ${scripts:parts-directory|path-repr})
9import site
10
11from lp.services.spriteutils import SpriteUtil12from lp.services.spriteutils import SpriteUtil
1213
14
13command_options = ('create-image', 'create-css')15command_options = ('create-image', 'create-css')
1416
17
15def usage():18def usage():
16 return " Usage: %s %s" % (sys.argv[0], '|'.join(command_options))19 return " Usage: %s %s" % (sys.argv[0], '|'.join(command_options))
1720
18if len(sys.argv) != 2:21
19 print >> sys.stderr, "Expected a single argument."22def main():
20 print >> sys.stderr, usage()23 if len(sys.argv) != 2:
21 sys.exit(1)24 print("Expected a single argument.", file=sys.stderr)
22else:25 print(usage(), file=sys.stderr)
23 command = sys.argv[1]26 sys.exit(1)
24 if command not in command_options:
25 print >> sys.stderr, "Unknown argument: %s" % command
26 print >> sys.stderr, usage()
27 sys.exit(2)
28
29icing = ${buildout:directory/lib/canonical/launchpad/icing|path-repr}
30sprite_groups = [
31 file_name.replace('.css.in', '')
32 for file_name in os.listdir(icing) if file_name.endswith('.css.in')]
33
34for group_name in sprite_groups:
35 css_template_file = os.path.join(icing, '%s.css.in' % group_name)
36 combined_image_file = os.path.join(icing, '%s.png' % group_name)
37 positioning_file = os.path.join(icing, '%s.positioning' % group_name)
38 css_file = os.path.join(icing, 'build/%s.css' % group_name)
39 if group_name.startswith('block-'):
40 # 3 times the size of inline.
41 margin = 300
42 else:27 else:
43 # Inline is 2 lines to h1 text + %50 for zooming. 40px + 40px + 20px,28 command = sys.argv[1]
44 margin = 10029 if command not in command_options:
4530 print("Unknown argument: %s" % command, file=sys.stderr)
46 sprite_util = SpriteUtil(31 print(usage(), file=sys.stderr)
47 css_template_file, 'icon-sprites',32 sys.exit(2)
48 url_prefix_substitutions={'/@@/': '../images/'},33
49 margin=margin)34 icing = os.path.join(config.root, 'lib/canonical/launchpad/icing')
5035 sprite_groups = [
51 if command == 'create-image':36 file_name.replace('.css.in', '')
52 sprite_util.combineImages(icing)37 for file_name in os.listdir(icing) if file_name.endswith('.css.in')]
53 sprite_util.savePNG(combined_image_file)38
54 sprite_util.savePositioning(positioning_file)39 for group_name in sprite_groups:
55 elif command == 'create-css':40 css_template_file = os.path.join(icing, '%s.css.in' % group_name)
56 sprite_util.loadPositioning(positioning_file)41 combined_image_file = os.path.join(icing, '%s.png' % group_name)
57 # The icing/icon-sprites.png file is relative to the css file42 positioning_file = os.path.join(icing, '%s.positioning' % group_name)
58 # in the icing/build/ directory.43 css_file = os.path.join(icing, 'build/%s.css' % group_name)
59 sprite_util.saveConvertedCSS(css_file, '../%s.png' % group_name)44 if group_name.startswith('block-'):
45 # 3 times the size of inline.
46 margin = 300
47 else:
48 # Inline is 2 lines to h1 text + %50 for zooming.
49 # 40px + 40px + 20px
50 margin = 100
51
52 sprite_util = SpriteUtil(
53 css_template_file, 'icon-sprites',
54 url_prefix_substitutions={'/@@/': '../images/'},
55 margin=margin)
56
57 if command == 'create-image':
58 sprite_util.combineImages(icing)
59 sprite_util.savePNG(combined_image_file)
60 sprite_util.savePositioning(positioning_file)
61 elif command == 'create-css':
62 sprite_util.loadPositioning(positioning_file)
63 # The icing/icon-sprites.png file is relative to the css file
64 # in the icing/build/ directory.
65 sprite_util.saveConvertedCSS(css_file, '../%s.png' % group_name)
6066
=== added directory 'lib/lp/testing/utilities'
=== added file 'lib/lp/testing/utilities/__init__.py'
=== renamed file 'buildout-templates/bin/retest.in' => 'lib/lp/testing/utilities/retest.py'
--- buildout-templates/bin/retest.in 2012-01-05 16:22:45 +0000
+++ lib/lp/testing/utilities/retest.py 2017-01-18 08:50:25 +0000
@@ -1,6 +1,4 @@
1#!${buildout:executable}1# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
2#
3# Copyright 2009 Canonical Ltd. This software is licensed under the
4# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
53
6"""4"""
@@ -24,16 +22,19 @@
2422
25"""23"""
2624
25from __future__ import print_function
26
27import fileinput27import fileinput
28from itertools import takewhile, imap
28import os29import os
29import re30import re
30import sys31import sys
31from itertools import takewhile, imap32
3233from lp.services.config import config
33${python-relative-path-setup}34
3435
35# The test script for this branch.36# The test script for this branch.
36TEST = "${buildout:directory/bin/test}"37TEST = os.path.join(config.root, "bin/test")
3738
38# Regular expression to match numbered stories.39# Regular expression to match numbered stories.
39STORY_RE = re.compile("(.*)/\d{2}-.*")40STORY_RE = re.compile("(.*)/\d{2}-.*")
@@ -66,10 +67,12 @@
66 return (67 return (
67 line.startswith('Tests with failures:') or68 line.startswith('Tests with failures:') or
68 line.startswith('Tests with errors:'))69 line.startswith('Tests with errors:'))
70
69 def p_take(line):71 def p_take(line):
70 return not (72 return not (
71 line.isspace() or73 line.isspace() or
72 line.startswith('Total:'))74 line.startswith('Total:'))
75
73 lines = iter(lines)76 lines = iter(lines)
74 for line in lines:77 for line in lines:
75 if p_start(line):78 if p_start(line):
@@ -88,9 +91,9 @@
8891
89def run_tests(tests):92def run_tests(tests):
90 """Given a set of tests, run them as one group."""93 """Given a set of tests, run them as one group."""
91 print "Running tests:"94 print("Running tests:")
92 for test in tests:95 for test in tests:
93 print " %s" % test96 print(" %s" % test)
94 args = ['-vvc'] if sys.stdout.isatty() else ['-vv']97 args = ['-vvc'] if sys.stdout.isatty() else ['-vv']
95 for test in tests:98 for test in tests:
96 args.append('-t')99 args.append('-t')
@@ -98,7 +101,7 @@
98 os.execl(TEST, TEST, *args)101 os.execl(TEST, TEST, *args)
99102
100103
101if __name__ == '__main__':104def main():
102 lines = imap(decolorize, fileinput.input())105 lines = imap(decolorize, fileinput.input())
103 tests = extract_tests(lines)106 tests = extract_tests(lines)
104 if len(tests) >= 1:107 if len(tests) >= 1:
105108
=== modified file 'setup.py'
--- setup.py 2016-11-03 15:19:01 +0000
+++ setup.py 2017-01-18 08:50:25 +0000
@@ -164,15 +164,19 @@
164 entry_points=dict(164 entry_points=dict(
165 console_scripts=[ # `console_scripts` is a magic name to setuptools165 console_scripts=[ # `console_scripts` is a magic name to setuptools
166 'apiindex = lp.scripts.utilities.apiindex:main',166 'apiindex = lp.scripts.utilities.apiindex:main',
167 'bzr = lp.scripts.utilities.bzr:main',
168 'combine-css = lp.scripts.utilities.js.combinecss:main',
169 'harness = lp.scripts.harness:python',
170 'jsbuild = lp.scripts.utilities.js.jsbuild:main',
171 'kill-test-services = lp.scripts.utilities.killtestservices:main',
167 'killservice = lp.scripts.utilities.killservice:main',172 'killservice = lp.scripts.utilities.killservice:main',
168 'jsbuild = lp.scripts.utilities.js.jsbuild:main',173 'retest = lp.testing.utilities.retest:main',
169 'run = lp.scripts.runlaunchpad:start_launchpad',174 'run = lp.scripts.runlaunchpad:start_launchpad',
170 'run-testapp = '175 'run-testapp = lp.scripts.runlaunchpad:start_testapp',
171 'lp.scripts.runlaunchpad:start_testapp',176 'sprite-util = lp.scripts.utilities.spriteutil:main',
172 'harness = lp.scripts.harness:python',177 'start_librarian = lp.scripts.runlaunchpad:start_librarian',
173 'twistd = twisted.scripts.twistd:run',178 'twistd = twisted.scripts.twistd:run',
174 'start_librarian = '179 'watch_jsbuild = lp.scripts.utilities.js.watchjsbuild:main',
175 'lp.scripts.runlaunchpad:start_librarian',
176 ]180 ]
177 ),181 ),
178)182)