added 2 missing template files
[limo.git] / fcgi / fcgiutil.py
blob3096117afae5b72ef49d3bc2f8244c635c48bc16
1 from __future__ import with_statement
2 import datetime, thread, threading, struct, os, sys
3 import cgitb
5 def log(msg):
6 with open("fcgi.log","a") as f:
7 f.write(msg+"\n")
9 class Loggable():
10 def log(self, msg):
11 log("%s %s %s: %s" % (str(datetime.datetime.now()), pad(thread.get_ident(),5), self.__class__.__name__, msg))
13 def ellipsis(text,maxlen,endsize=7):
14 """ ellipsis is a helper function
15 It grabs a little off both ends and puts an ellipsis in the middle.
17 >>> ellipsis("1234567890_2_4_6_8_01_3_5_7_9_", 20)
18 1234567890..._5_7_9_
19 >>> ellipsis("1234567890", 8, 2)
20 123...90
21 >>> len(ellipsis("1234567890", 8, 2))
24 """
25 if type(text) not in (str,unicode):
26 text = str(text)
27 assert type(text) in (str, unicode)
28 if len(text) <= maxlen:
29 return text
30 if endsize == 0:
31 return text[0:maxlen-3]+"..."
32 return text[0:maxlen-(endsize+3)]+"..."+text[-endsize:]
34 def pad(s,min,char=' '):
35 s = str(s)
36 if len(s) < min:
37 return s + (char * (min - len(s)))
38 return s
39 def ms_elapsed(since):
40 d = (datetime.datetime.now() - since)
41 return (d.seconds *1000.0) + (d.microseconds / 1000.0)
43 class NiceThread(threading.Thread):
44 def __init__(self):
45 threading.Thread.__init__(self)
46 self.setDaemon(True)
47 self._run = True
48 def please_stop(self):
49 self._run = False
50 def run(self):
51 while self._run:
52 self.thread()
53 self.onexit()
54 def onexit(self):
55 pass # override this in a subclass
56 def thread(self):
57 pass # override this in a subclass
59 class FastCGIPairs(Loggable):
60 """ FastCGI uses a unique pair encoding:
61 <1-byte length><1-byte length><name><value>
62 """
63 def __init__(self, s):
64 self.pairs = []
65 self._i = -1 # this is the counter if this object is used as an iterator
66 while len(s):
67 # decode one pair
68 # dont forget to follow logic from the spec: Lengths of 127 bytes and less can be encoded in one byte, while longer lengths are always encoded in four bytes
69 name_len = struct.unpack("!B",s[0])[0]
70 if name_len >> 7 == 1:
71 name_len = struct.unpack("!L",s[0:4])[0]
72 name_len &= 0x7FFFFF
73 s = s[4:]
74 else:
75 s = s[1:]
77 value_len = struct.unpack("!B",s[0])[0]
78 if value_len >> 7 == 1: # if the 4-byte marker is set
79 value_len = struct.unpack("!L", s[0:4])[0] # then get its long value
80 value_len &= 0x7FFFFF # clear the marker bit
81 s = s[4:]
82 else:
83 s = s[1:]
84 name = s[0:name_len]
85 s = s[name_len:]
86 value = s[0:value_len]
87 s = s[value_len:]
88 self.pairs.append( (name,value) )
89 def add(self, pair):
90 self.pairs.append(pair)
91 def encode(self):
92 ret = ''
93 for pair in pairs:
94 a = len(pair[0])
95 b = len(pair[1])
96 if len(a) > 127:
97 pa = "L"
98 else:
99 pa = "B"
100 if len(b) > 127:
101 pb = "L"
102 else:
103 pb = "B"
104 ret += struct.pack("!"+pa+pb, a, b)
105 ret += pair[0]
106 ret += pair[1]
107 return ret
108 def __iter__(self):
109 return self
110 def next(self):
111 self._i += 1
112 if self._i >= len(self.pairs):
113 self._i = -1
114 raise StopIteration()
115 return self.pairs[self._i]
117 def flatten(gen):
118 """ Returns a generator that iterates depth-first through 'gen', a potentially nested generator.
120 All the generators throughout the flatten() process are allowed to yield lists, tuples, simple values, or other generators.
122 Simples values flatten() to str() of themselves.
124 The other value types (list,tuple,generator) will recurse and flatten their contents.
126 >>> def foo():
127 ... for c in ('f','o','o'):
128 ... yield c
130 >>> def bar():
131 ... for c in ('b','a','r'):
132 ... yield c
134 >>> def gen():
135 ... yield [foo(),bar()]
137 >>> list(flatten(gen))
138 ['f', 'o', 'o', 'b', 'a', 'r']
140 >>> def bad():
141 ... yield 1
142 ... yield 1/0
144 >>> def gen():
145 ... yield [foo(), bad()]
147 >>> list(flatten(gen()))
148 Traceback (most recent call last):
150 GeneratorException: Caused by:
151 <type 'exceptions.ZeroDivisionError'> integer division or modulo by zero
152 Stack:
153 ('generator', ['gen', '<doctest __main__.flatten[5]>', 1]):
154 ('list',1):
155 ('generator', ['bad', '<doctest __main__.flatten[4]>', 1]):
156 <BLANKLINE>
159 try:
160 stack = '(not-yet-defined)'
161 if type(gen) in (str, unicode):
162 yield gen
163 elif type(gen).__name__ == 'function':
164 stack = ('function',gen.__name__)
165 for i in flatten(gen()):
166 yield i
167 elif type(gen) in (list,tuple):
168 for i in range(len(gen)):
169 stack = ('list',i)
170 for j in flatten(gen[i]):
171 yield j
172 elif type(gen).__name__ == 'generator':
173 stack = ('generator', [gen.gi_frame.f_code.co_name, gen.gi_frame.f_code.co_filename.split(os.path.sep)[-1], gen.gi_frame.f_lineno])
174 for i in gen:
175 for j in flatten(i):
176 yield j
177 else:
178 if gen is not None:
179 yield str(gen)
180 except Exception:
181 (a,b,c) = sys.exc_info()
182 raise GeneratorException(b, c, stack)
184 class GeneratorException(Exception):
185 def __init__(self, error, tb=None, stack=[]):
186 t = type(error)
187 if hasattr(t, "__name__") and t.__name__ == 'GeneratorException':
188 self.error = error.error
189 self.stack = stack + (error.stack,)
190 self.tb = error.tb
191 del error
192 else:
193 self.error = error
194 self.stack = stack
195 self.tb = tb
196 def __del__(self):
197 try:
198 del self.error
199 except:
200 pass
201 try:
202 del self.tb
203 except:
204 pass
205 def __str__(self):
206 def unroll_stack(stack,n=0):
207 if len(stack) > 0:
208 return ("\t"*n) \
209 + str(stack[:2])+":\n" \
210 + (unroll_stack(stack[2],n+1) if len(stack) > 2 else '')
211 return "Caused by:\n%s %s\nStack:\n%s\nTB:%s\n" \
212 % (str(type(self.error)),
213 str(self.error),
214 unroll_stack(self.stack),
215 self.tb)
217 if __name__ == '__main__':
218 import doctest
219 doctest.testmod()