~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# Copyright 2005 Canonical Ltd.  All rights reserved.

"""Handlers for warnings, to be installed when testing."""

__metaclass__ = type

import atexit
import warnings
import sys
import inspect
import StringIO

class WarningReport:

    def __init__(self, message, info):
        self.message = message
        self.info = info

    def __str__(self):
        info = str(self.info)
        if info:
            return info
        else:
            return self.message

class ImportantInfo:

    def __init__(self, expressiontext, viewclassname, templatefilename,
        requesturl, viewclassfilename, viewclasslineno, viewclassfunc,
        doctestname, doctestline):
        self.expressiontext = expressiontext
        self.viewclassname = viewclassname
        self.viewclassfilename = viewclassfilename
        self.viewclasslineno = viewclasslineno
        self.templatefilename = templatefilename
        self.requesturl = requesturl
        self.viewclassfunc = viewclassfunc
        self.doctestname = doctestname
        self.doctestline = doctestline

    def __str__(self):
        L = []
        if self.expressiontext:
            L.append('The expression: %s in %s' % (
                self.expressiontext, self.templatefilename))
        if self.viewclassname:
            L.append('The method %s.%s' % (
                self.viewclassname, self.viewclassfunc))
            #L.append('at line %s of file %s' % (
            #    self.viewclasslineno, self.viewclassfilename)
        if self.doctestname:
            L.append("The doctest %s, at the line:" % self.doctestname)
            L.append("    >>> %s" % self.doctestline)
        if self.requesturl:
            L.append('request url: %s' % self.requesturl)
        return '\n'.join(L)

# PageTemplateFile has .filename.
from zope.pagetemplate.pagetemplatefile import PageTemplateFile
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile

# PythonExpr has .text, the text of the expression.
from zope.tales.pythonexpr import PythonExpr

# TrustedZopeContext has self.contexts, a dict with template, view, context,
# request, etc.
from zope.app.pagetemplate.engine import TrustedZopeContext

# TALInterpreter has self.sourceFile, a filename of a page template.
from zope.tal.talinterpreter import TALInterpreter

from zope.app.pagetemplate.simpleviewclass import simple

def find_important_info():
    stack = inspect.stack()
    try:
        important_classes = set([
            PythonExpr,
            TrustedZopeContext,
            TALInterpreter,
            ViewPageTemplateFile,
            simple
            ])
        important_objects = {}
        metadata = {}  # cls -> (filename, lineno, funcname)

        for frame, filename, lineno, func_name, context, lineidx in stack:
            try:
                if (filename.startswith('<doctest ') and
                    "doctest" not in important_objects):
                    # Very fragile inspection of the state of the doctest
                    # runner.  So, enclosed in a try-except so it will at
                    # least fail gracefully if it fails.
                    try:
                        line = frame.f_back.f_locals['example'].source
                    except KeyboardInterrupt:
                        pass
                    except Exception:
                        line = "# cannot get line of code"
                    important_objects["doctest"] = (filename, line)
                    metadata["doctest"] = (filename, lineno, func_name)
                if 'self' in frame.f_locals:
                    fself = frame.f_locals['self']
                    ftype = type(fself)
                    for cls in list(important_classes):
                        if isinstance(fself, cls):
                            important_objects[cls] = fself
                            metadata[cls] = (filename, lineno, func_name)
                            important_classes.remove(cls)
            finally:
                del frame
    finally:
        del stack

    expressiontext = ''
    if PythonExpr in important_objects:
        expressiontext = important_objects[PythonExpr].text

    viewclassname = ''
    viewclassfilename = ''
    viewclasslineno = ''
    viewclassfunc = ''
    doctestname = ''
    doctestline = ''
    if simple in important_objects:
        cls = important_objects[simple].__class__
        if cls is not simple:
            viewclassname = cls.__mro__[1].__name__
            viewclassfilename, viewclasslineno, viewclassfunc = (
                metadata[simple])

    templatefilename = ''
    if ViewPageTemplateFile in important_objects:
        templatefilename = important_objects[ViewPageTemplateFile].filename
        templatefilename = templatefilename.split('/')[-1]

    requesturl = ''
    if TrustedZopeContext in important_objects:
        ptcontexts = important_objects[TrustedZopeContext].contexts
        requesturl = ptcontexts['request'].getURL()

    if "doctest" in important_objects:
        doctestname, doctestline = important_objects["doctest"]
    return ImportantInfo(expressiontext, viewclassname, templatefilename,
        requesturl, viewclassfilename, viewclasslineno, viewclassfunc,
        doctestname, doctestline)

need_page_titles = []
no_order_by = []
other_warnings = []

old_show_warning = warnings.showwarning
def launchpad_showwarning(message, category, filename, lineno, file=None):
    if file is None:
        file = sys.stderr
    stream = StringIO.StringIO()
    old_show_warning(message, category, filename, lineno, stream)
    warning_message = stream.getvalue()
    important_info = find_important_info()

    if isinstance(message, UserWarning):
        args = message.args
        if args:
            arg = args[0]
            if arg.startswith('No page title in '):
                global need_page_titles
                need_page_titles.append(arg)
                return
            if arg == 'Getting a slice of an unordered set is unpredictable.':
                # find the page template and view class, if any
                # show these, plus the request.
                global no_order_by
                no_order_by.append(
                    WarningReport(warning_message, important_info)
                    )
                return
    other_warnings.append(WarningReport(warning_message, important_info))

def report_need_page_titles():
    global need_page_titles
    if need_page_titles:
        print
        print "The following pages need titles."
        for message in need_page_titles:
            print "   ", message

def report_no_order_by():
    global no_order_by
    if no_order_by:
        print
        print ("The following code has issues with"
               " ambiguous select results ordering.")
        for report in no_order_by:
            print
            print report

def report_other_warnings():
    global other_warnings
    if other_warnings:
        print
        print "General warnings."
        for warninginfo in other_warnings:
            print
            print warninginfo.message,
            print warninginfo

def report_warnings():
    report_need_page_titles()
    report_no_order_by()
    report_other_warnings()

def install_warning_handler():
    warnings.showwarning = launchpad_showwarning
    atexit.register(report_warnings)