~launchpad-pqm/launchpad/devel

10637.3.1 by Guilherme Salgado
Use the default python version instead of a hard-coded version
1
#! /usr/bin/python -S
8687.15.4 by Karl Fogel
Add the copyright header block to more files; tweak format in a few files.
2
#
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
3
# Copyright 2010 Canonical Ltd.  This software is licensed under the
8687.15.4 by Karl Fogel
Add the copyright header block to more files; tweak format in a few files.
4
# GNU Affero General Public License version 3 (see the file LICENSE).
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
5
6
"""Create a static WADL file describing the current webservice.
11369.3.1 by Benji York
checkpoint
7
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
8
Example:
11369.3.1 by Benji York
checkpoint
9
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
10
    % LPCONFIG=development bin/py utilities/create-lp-wadl-and-apidoc.py \\
11
      "lib/canonical/launchpad/apidoc/wadl-development-%(version)s.xml"
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
12
"""
13289.2.3 by Gary Poster
lint
13
import _pythonpath  # Not lint, actually needed.
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
14
11929.14.1 by Benji York
parallelize the WADL generation
15
from multiprocessing import Process
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
16
import optparse
10466.8.1 by Leonard Richardson
Initial implementation.
17
import os
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
18
import sys
10420.4.1 by Leonard Richardson
Initial implementation.
19
13289.2.2 by Gary Poster
set mtime of generated wadl files to correspond to last checkin of branch. This can let Apache serve ETags consistently across machines.
20
import bzrlib
21
from bzrlib.branch import Branch
10420.4.1 by Leonard Richardson
Initial implementation.
22
from zope.component import getUtility
10466.8.1 by Leonard Richardson
Initial implementation.
23
from zope.pagetemplate.pagetemplatefile import PageTemplateFile
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
24
13289.2.1 by Gary Poster
make changes so that the wadl can be served directly from Apache.
25
from canonical.launchpad.rest.wadl import (
26
    generate_html,
27
    generate_json,
28
    generate_wadl,
13289.2.3 by Gary Poster
lint
29
    )
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
30
from canonical.launchpad.scripts import execute_zcml_for_scripts
7182.3.2 by Gary Poster
change code to keep launchpad code in launchpad, not lazr; add tests. Once (or if) lint is happy, this should be ready for review.
31
from canonical.launchpad.systemhomes import WebServiceApplication
10420.4.1 by Leonard Richardson
Initial implementation.
32
from lazr.restful.interfaces import IWebServiceConfiguration
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
33
10736.1.1 by Jonathan Lange
Use non-testing login in script required to build Launchpad
34
13289.2.2 by Gary Poster
set mtime of generated wadl files to correspond to last checkin of branch. This can let Apache serve ETags consistently across machines.
35
def write(filename, content, timestamp):
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
36
    """Replace the named file with the given string."""
37
    f = open(filename, 'w')
38
    f.write(content)
39
    f.close()
13289.2.3 by Gary Poster
lint
40
    os.utime(filename, (timestamp, timestamp))  # (atime, mtime)
41
13289.2.2 by Gary Poster
set mtime of generated wadl files to correspond to last checkin of branch. This can let Apache serve ETags consistently across machines.
42
43
def make_files(directory, version, timestamp, force):
13289.2.1 by Gary Poster
make changes so that the wadl can be served directly from Apache.
44
    version_directory = os.path.join(directory, version)
45
    base_filename = os.path.join(version_directory, os.environ['LPCONFIG'])
46
    wadl_filename = base_filename + '.wadl'
47
    json_filename = base_filename + '.json'
48
    html_filename = os.path.join(directory, version + ".html")
49
    wadl_index = os.path.join(version_directory, 'index.wadl')
50
    json_index = os.path.join(version_directory, 'index.json')
51
    html_index = os.path.join(version_directory, 'index.html')
52
    brokenwadl_index = os.path.join(version_directory, 'index.brokenwadl')
53
54
    # Make sure we have our dir.
55
    if not os.path.exists(version_directory):
56
        # We expect the main directory to exist.
57
        os.mkdir(version_directory)
58
59
    # Make wadl and json files.
60
    for src, dest, gen, name in (
61
        (wadl_filename, wadl_index, generate_wadl, 'WADL'),
62
        (json_filename, json_index, generate_json, 'JSON')):
63
        # If the src doesn't exist or we are forced to regenerate it...
64
        if (not os.path.exists(src) or force):
65
            print "Writing %s for version %s to %s." % (
66
                name, version, src)
13289.2.2 by Gary Poster
set mtime of generated wadl files to correspond to last checkin of branch. This can let Apache serve ETags consistently across machines.
67
            write(src, gen(version), timestamp)
13289.2.1 by Gary Poster
make changes so that the wadl can be served directly from Apache.
68
        else:
69
            print "Skipping already present %s file: %s" % (
70
                name, src)
71
        # Make "index" symlinks, removing any preexisting ones.
72
        if os.path.exists(dest):
73
            os.remove(dest)
74
        os.symlink(os.path.basename(src), dest)
75
76
    # Make the brokenwadl symlink.  This is because we need to support a
77
    # misspelled wadl mimetype that some legacy launchpadlib versions used.
78
    # Multiple attempts have been made to make this unnecessary, and removing
79
    # it is welcome.  In particular, these two approaches were attempted in
80
    # Apache.
81
    #
82
    # Approach 1 (a variant of example 4
83
    # from http://httpd.apache.org/docs/2.0/mod/mod_headers.html)
84
    # SetEnvIf Accept \Qapplication/vd.sun.wadl+xml\E X_WADL_MIME
85
    # RequestHeader set Accept "application/vnd.sun.wadl+xml" env=X_WADL_MIME
86
    # This, at least in combination with Apache's MultiViews, doesn't work
87
    # in developer tests.
88
    #
89
    # Approach 2:
90
    # In mime.conf,
91
    # AddType application/vnd.sun.wadl+xml .wadl
92
    # AddType application/vd.sun.wadl+xml .wadl
93
    # In developer tests, it seems Apache only allows a single mime type
94
    # for a given extension.
95
    #
96
    # Therefore, the approach we use is
97
    # AddType application/vnd.sun.wadl+xml .wadl
98
    # AddType application/vd.sun.wadl+xml .brokenwadl
99
    # We support that here.
100
    if not os.path.exists(brokenwadl_index):
101
        os.symlink(os.path.basename(wadl_index), brokenwadl_index)
13289.2.3 by Gary Poster
lint
102
11929.14.1 by Benji York
parallelize the WADL generation
103
    # Now, convert the WADL into an human-readable description and
104
    # put the HTML in the same directory as the WADL.
105
    # If the HTML file doesn't exist or we're being forced to regenerate
106
    # it...
107
    if (not os.path.exists(html_filename) or force):
108
        print "Writing apidoc for version %s to %s" % (
109
            version, html_filename)
110
        write(html_filename, generate_html(wadl_filename,
13289.2.2 by Gary Poster
set mtime of generated wadl files to correspond to last checkin of branch. This can let Apache serve ETags consistently across machines.
111
            suppress_stderr=False), timestamp)
11929.14.1 by Benji York
parallelize the WADL generation
112
    else:
113
        print "Skipping already present HTML file:", html_filename
114
13289.2.1 by Gary Poster
make changes so that the wadl can be served directly from Apache.
115
    # Symlink the top-level version html in the version directory for
116
    # completeness.
117
    if not os.path.exists(html_index):
118
        os.symlink(
119
            os.path.join(os.path.pardir, os.path.basename(html_filename)),
120
            html_index)
121
122
123
def main(directory, force=False):
13289.2.3 by Gary Poster
lint
124
    WebServiceApplication.cached_wadl = None  # do not use cached file version
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
125
    execute_zcml_for_scripts()
10420.4.1 by Leonard Richardson
Initial implementation.
126
    config = getUtility(IWebServiceConfiguration)
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
127
10524.1.2 by Leonard Richardson
Style the apidoc index and simplify its generation.
128
    # First, create an index.html with links to all the HTML
129
    # documentation files we're about to generate.
130
    template_file = 'apidoc-index.pt'
131
    template = PageTemplateFile(template_file)
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
132
    index_filename = os.path.join(directory, "index.html")
133
    print "Writing index:", index_filename
134
    f = open(index_filename, 'w')
10524.1.3 by Leonard Richardson
Simplified template code even more thanks to gary's code.
135
    f.write(template(config=config))
10524.1.2 by Leonard Richardson
Style the apidoc index and simplify its generation.
136
13289.2.2 by Gary Poster
set mtime of generated wadl files to correspond to last checkin of branch. This can let Apache serve ETags consistently across machines.
137
    # Get the time of the last commit.  We will use this as the mtime for the
138
    # generated files so that we can safely use it as part of Apache's etag
139
    # generation in the face of multiple servers/filesystems.
140
    with bzrlib.initialize():
141
        branch = Branch.open(os.path.dirname(os.path.dirname(__file__)))
142
        timestamp = branch.repository.get_revision(
143
            branch.last_revision()).timestamp
144
11929.14.1 by Benji York
parallelize the WADL generation
145
    # Start a process to build each set of WADL and HTML files.
146
    processes = []
10466.8.2 by Leonard Richardson
Minor cleanup.
147
    for version in config.active_versions:
11929.14.1 by Benji York
parallelize the WADL generation
148
        p = Process(target=make_files,
13289.2.2 by Gary Poster
set mtime of generated wadl files to correspond to last checkin of branch. This can let Apache serve ETags consistently across machines.
149
            args=(directory, version, timestamp, force))
11929.14.1 by Benji York
parallelize the WADL generation
150
        p.start()
151
        processes.append(p)
10466.8.1 by Leonard Richardson
Initial implementation.
152
11929.14.1 by Benji York
parallelize the WADL generation
153
    # Wait for all the subprocesses to finish.
154
    for p in processes:
155
        p.join()
10466.8.1 by Leonard Richardson
Initial implementation.
156
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
157
    return 0
158
11604.1.3 by Benji York
fix lint
159
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
160
def parse_args(args):
13289.2.1 by Gary Poster
make changes so that the wadl can be served directly from Apache.
161
    usage = "usage: %prog [options] DIR"
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
162
    parser = optparse.OptionParser(usage=usage)
163
    parser.add_option(
164
        "--force", action="store_true",
165
        help="Replace any already-existing files.")
166
    parser.set_defaults(force=False)
167
    options, args = parser.parse_args(args)
168
    if len(args) != 2:
13289.2.1 by Gary Poster
make changes so that the wadl can be served directly from Apache.
169
        parser.error("A directory is required.")
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
170
171
    return options, args
172
11604.1.3 by Benji York
fix lint
173
6770.2.1 by Francis J. Lacoste
Add create-lp-wadl.py
174
if __name__ == '__main__':
11604.1.1 by Benji York
extract just the refactorings from my (now abandoned) check-in-wadl branch and
175
    options, args = parse_args(sys.argv)
176
    sys.exit(main(args[1], options.force))