2 from . import req, dispatch, session, form, resp, proto
4 def wsgiwrap(callable):
5 def wrapper(env, startreq):
6 return dispatch.handleenv(env, startreq, callable)
7 wrapper.__wrapped__ = callable
10 def formparams(callable):
11 sig = inspect.signature(callable)
12 haskw = inspect.Parameter.VAR_KEYWORD in (par.kind for par in sig.parameters.values())
15 data = dict(form.formdata(req).items())
17 raise resp.httperror(400, "Invalid request", "Form data was incomplete")
24 for par in sig.parameters.values():
26 args[par.name] = data[par.name]
27 for par in sig.parameters.values():
28 if par.default is inspect.Parameter.empty and par.name not in args:
29 raise resp.httperror(400, "Missing parameter", ("The query parameter `", resp.h.code(par.name), "' is required but not supplied."))
30 return callable(**args)
31 wrapper.__wrapped__ = callable
34 class funplex(object):
35 def __init__(self, *funs, **nfuns):
37 self.dir.update(((self.unwrap(fun).__name__, fun) for fun in funs))
38 self.dir.update(nfuns)
42 while hasattr(fun, "__wrapped__"):
46 def __call__(self, req):
47 if req.pathinfo == "":
48 if "__root__" in self.dir:
49 return self.dir["__root__"](req)
50 raise resp.redirect(req.uriname + "/")
51 if req.pathinfo[:1] != "/":
58 p = p.partition("/")[0]
62 sreq.selfpath = req.pathinfo[1:]
63 return self.dir[p](sreq)
67 self.dir[self.unwrap(fun).__name__] = fun
76 def persession(data=None):
79 sess = session.get(req)
80 if callable not in sess:
82 sess[callable] = callable()
86 sess[callable] = callable(data)
87 return sess[callable].handle(req)
88 wrapper.__wrapped__ = callable
92 class preiter(object):
93 __slots__ = ["bk", "bki", "_next"]
95 def __init__(self, real):
105 if self._next is self.end:
106 raise StopIteration()
109 self._next = next(self.bki)
110 except StopIteration:
111 self._next = self.end
115 if hasattr(self.bk, "close"):
118 def pregen(callable):
119 def wrapper(*args, **kwargs):
120 return preiter(callable(*args, **kwargs))
121 wrapper.__wrapped__ = callable
124 def stringwrap(charset):
127 def wrapper(*args, **kwargs):
128 for string in callable(*args, **kwargs):
129 yield string.encode(charset)
130 wrapper.__wrapped__ = callable
134 class sessiondata(object):
136 def get(cls, req, create=True):
137 sess = cls.sessdb().get(req)
150 return session.default.val
152 class autodirty(sessiondata):
155 ret = super().get(req)
156 if "_is_dirty" not in ret.__dict__:
157 ret.__dict__["_is_dirty"] = False
160 def sessfrozen(self):
161 self.__dict__["_is_dirty"] = False
164 return self._is_dirty
166 def __setattr__(self, name, value):
167 super().__setattr__(name, value)
168 if "_is_dirty" in self.__dict__:
169 self.__dict__["_is_dirty"] = True
171 def __delattr__(self, name):
172 super().__delattr__(name, value)
173 if "_is_dirty" in self.__dict__:
174 self.__dict__["_is_dirty"] = True
176 class manudirty(object):
177 def __init__(self, *args, **kwargs):
178 super().__init__(*args, **kwargs)
181 def sessfrozen(self):
190 class specslot(object):
191 __slots__ = ["nm", "idx", "dirty"]
194 def __init__(self, nm, idx, dirty):
201 # Avoid calling __getattribute__
202 return specdirty.__sslots__.__get__(ins, type(ins))
204 def __get__(self, ins, cls):
205 val = self.slist(ins)[self.idx]
206 if val is specslot.unbound:
207 raise AttributeError("specslot %r is unbound" % self.nm)
210 def __set__(self, ins, val):
211 self.slist(ins)[self.idx] = val
215 def __delete__(self, ins):
216 self.slist(ins)[self.idx] = specslot.unbound
219 class specclass(type):
220 def __init__(self, name, bases, tdict):
221 super().__init__(name, bases, tdict)
224 for cls in self.__mro__:
225 css = cls.__dict__.get("__saveslots__", ())
227 dslots.update(cls.__dict__.get("__dirtyslots__", css))
228 self.__sslots_l__ = list(sslots)
229 self.__sslots_a__ = list(sslots | dslots)
230 for i, slot in enumerate(self.__sslots_a__):
231 setattr(self, slot, specslot(slot, i, slot in dslots))
233 class specdirty(sessiondata, metaclass=specclass):
234 __slots__ = ["session", "__sslots__", "_is_dirty"]
236 def __specinit__(self):
240 def __new__(cls, req, sess):
241 self = super().__new__(cls)
243 self.__sslots__ = [specslot.unbound] * len(cls.__sslots_a__)
245 self._is_dirty = False
248 def __getnewargs__(self):
249 return (None, self.session)
252 self._is_dirty = True
254 def sessfrozen(self):
255 self._is_dirty = False
258 return self._is_dirty
260 def __getstate__(self):
262 for nm, val in zip(type(self).__sslots_a__, specslot.slist(self)):
263 if val is specslot.unbound:
264 ret[nm] = False, None
269 def __setstate__(self, st):
270 ss = specslot.slist(self)
271 for i, nm in enumerate(type(self).__sslots_a__):
272 bound, val = st.pop(nm, (False, None))
274 ss[i] = specslot.unbound
278 def datecheck(req, mtime):
279 if "If-Modified-Since" in req.ihead:
280 rtime = proto.phttpdate(req.ihead["If-Modified-Since"])
281 if rtime is not None and rtime >= math.floor(mtime):
282 raise resp.unmodified()
283 req.ohead["Last-Modified"] = proto.httpdate(mtime)