_internal.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. # -*- coding: utf-8 -*-
  2. """
  3. werkzeug._internal
  4. ~~~~~~~~~~~~~~~~~~
  5. This module provides internally used helpers and constants.
  6. :copyright: 2007 Pallets
  7. :license: BSD-3-Clause
  8. """
  9. import inspect
  10. import logging
  11. import re
  12. import string
  13. from datetime import date
  14. from datetime import datetime
  15. from itertools import chain
  16. from weakref import WeakKeyDictionary
  17. from ._compat import int_to_byte
  18. from ._compat import integer_types
  19. from ._compat import iter_bytes
  20. from ._compat import range_type
  21. from ._compat import text_type
  22. _logger = None
  23. _signature_cache = WeakKeyDictionary()
  24. _epoch_ord = date(1970, 1, 1).toordinal()
  25. _cookie_params = {
  26. b"expires",
  27. b"path",
  28. b"comment",
  29. b"max-age",
  30. b"secure",
  31. b"httponly",
  32. b"version",
  33. }
  34. _legal_cookie_chars = (
  35. string.ascii_letters + string.digits + u"/=!#$%&'*+-.^_`|~:"
  36. ).encode("ascii")
  37. _cookie_quoting_map = {b",": b"\\054", b";": b"\\073", b'"': b'\\"', b"\\": b"\\\\"}
  38. for _i in chain(range_type(32), range_type(127, 256)):
  39. _cookie_quoting_map[int_to_byte(_i)] = ("\\%03o" % _i).encode("latin1")
  40. _octal_re = re.compile(br"\\[0-3][0-7][0-7]")
  41. _quote_re = re.compile(br"[\\].")
  42. _legal_cookie_chars_re = br"[\w\d!#%&\'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]"
  43. _cookie_re = re.compile(
  44. br"""
  45. (?P<key>[^=;]+)
  46. (?:\s*=\s*
  47. (?P<val>
  48. "(?:[^\\"]|\\.)*" |
  49. (?:.*?)
  50. )
  51. )?
  52. \s*;
  53. """,
  54. flags=re.VERBOSE,
  55. )
  56. class _Missing(object):
  57. def __repr__(self):
  58. return "no value"
  59. def __reduce__(self):
  60. return "_missing"
  61. _missing = _Missing()
  62. def _get_environ(obj):
  63. env = getattr(obj, "environ", obj)
  64. assert isinstance(env, dict), (
  65. "%r is not a WSGI environment (has to be a dict)" % type(obj).__name__
  66. )
  67. return env
  68. def _has_level_handler(logger):
  69. """Check if there is a handler in the logging chain that will handle
  70. the given logger's effective level.
  71. """
  72. level = logger.getEffectiveLevel()
  73. current = logger
  74. while current:
  75. if any(handler.level <= level for handler in current.handlers):
  76. return True
  77. if not current.propagate:
  78. break
  79. current = current.parent
  80. return False
  81. def _log(type, message, *args, **kwargs):
  82. """Log a message to the 'werkzeug' logger.
  83. The logger is created the first time it is needed. If there is no
  84. level set, it is set to :data:`logging.INFO`. If there is no handler
  85. for the logger's effective level, a :class:`logging.StreamHandler`
  86. is added.
  87. """
  88. global _logger
  89. if _logger is None:
  90. _logger = logging.getLogger("werkzeug")
  91. if _logger.level == logging.NOTSET:
  92. _logger.setLevel(logging.INFO)
  93. if not _has_level_handler(_logger):
  94. _logger.addHandler(logging.StreamHandler())
  95. getattr(_logger, type)(message.rstrip(), *args, **kwargs)
  96. def _parse_signature(func):
  97. """Return a signature object for the function."""
  98. if hasattr(func, "im_func"):
  99. func = func.im_func
  100. # if we have a cached validator for this function, return it
  101. parse = _signature_cache.get(func)
  102. if parse is not None:
  103. return parse
  104. # inspect the function signature and collect all the information
  105. if hasattr(inspect, "getfullargspec"):
  106. tup = inspect.getfullargspec(func)
  107. else:
  108. tup = inspect.getargspec(func)
  109. positional, vararg_var, kwarg_var, defaults = tup[:4]
  110. defaults = defaults or ()
  111. arg_count = len(positional)
  112. arguments = []
  113. for idx, name in enumerate(positional):
  114. if isinstance(name, list):
  115. raise TypeError(
  116. "cannot parse functions that unpack tuples in the function signature"
  117. )
  118. try:
  119. default = defaults[idx - arg_count]
  120. except IndexError:
  121. param = (name, False, None)
  122. else:
  123. param = (name, True, default)
  124. arguments.append(param)
  125. arguments = tuple(arguments)
  126. def parse(args, kwargs):
  127. new_args = []
  128. missing = []
  129. extra = {}
  130. # consume as many arguments as positional as possible
  131. for idx, (name, has_default, default) in enumerate(arguments):
  132. try:
  133. new_args.append(args[idx])
  134. except IndexError:
  135. try:
  136. new_args.append(kwargs.pop(name))
  137. except KeyError:
  138. if has_default:
  139. new_args.append(default)
  140. else:
  141. missing.append(name)
  142. else:
  143. if name in kwargs:
  144. extra[name] = kwargs.pop(name)
  145. # handle extra arguments
  146. extra_positional = args[arg_count:]
  147. if vararg_var is not None:
  148. new_args.extend(extra_positional)
  149. extra_positional = ()
  150. if kwargs and kwarg_var is None:
  151. extra.update(kwargs)
  152. kwargs = {}
  153. return (
  154. new_args,
  155. kwargs,
  156. missing,
  157. extra,
  158. extra_positional,
  159. arguments,
  160. vararg_var,
  161. kwarg_var,
  162. )
  163. _signature_cache[func] = parse
  164. return parse
  165. def _date_to_unix(arg):
  166. """Converts a timetuple, integer or datetime object into the seconds from
  167. epoch in utc.
  168. """
  169. if isinstance(arg, datetime):
  170. arg = arg.utctimetuple()
  171. elif isinstance(arg, integer_types + (float,)):
  172. return int(arg)
  173. year, month, day, hour, minute, second = arg[:6]
  174. days = date(year, month, 1).toordinal() - _epoch_ord + day - 1
  175. hours = days * 24 + hour
  176. minutes = hours * 60 + minute
  177. seconds = minutes * 60 + second
  178. return seconds
  179. class _DictAccessorProperty(object):
  180. """Baseclass for `environ_property` and `header_property`."""
  181. read_only = False
  182. def __init__(
  183. self,
  184. name,
  185. default=None,
  186. load_func=None,
  187. dump_func=None,
  188. read_only=None,
  189. doc=None,
  190. ):
  191. self.name = name
  192. self.default = default
  193. self.load_func = load_func
  194. self.dump_func = dump_func
  195. if read_only is not None:
  196. self.read_only = read_only
  197. self.__doc__ = doc
  198. def __get__(self, obj, type=None):
  199. if obj is None:
  200. return self
  201. storage = self.lookup(obj)
  202. if self.name not in storage:
  203. return self.default
  204. rv = storage[self.name]
  205. if self.load_func is not None:
  206. try:
  207. rv = self.load_func(rv)
  208. except (ValueError, TypeError):
  209. rv = self.default
  210. return rv
  211. def __set__(self, obj, value):
  212. if self.read_only:
  213. raise AttributeError("read only property")
  214. if self.dump_func is not None:
  215. value = self.dump_func(value)
  216. self.lookup(obj)[self.name] = value
  217. def __delete__(self, obj):
  218. if self.read_only:
  219. raise AttributeError("read only property")
  220. self.lookup(obj).pop(self.name, None)
  221. def __repr__(self):
  222. return "<%s %s>" % (self.__class__.__name__, self.name)
  223. def _cookie_quote(b):
  224. buf = bytearray()
  225. all_legal = True
  226. _lookup = _cookie_quoting_map.get
  227. _push = buf.extend
  228. for char in iter_bytes(b):
  229. if char not in _legal_cookie_chars:
  230. all_legal = False
  231. char = _lookup(char, char)
  232. _push(char)
  233. if all_legal:
  234. return bytes(buf)
  235. return bytes(b'"' + buf + b'"')
  236. def _cookie_unquote(b):
  237. if len(b) < 2:
  238. return b
  239. if b[:1] != b'"' or b[-1:] != b'"':
  240. return b
  241. b = b[1:-1]
  242. i = 0
  243. n = len(b)
  244. rv = bytearray()
  245. _push = rv.extend
  246. while 0 <= i < n:
  247. o_match = _octal_re.search(b, i)
  248. q_match = _quote_re.search(b, i)
  249. if not o_match and not q_match:
  250. rv.extend(b[i:])
  251. break
  252. j = k = -1
  253. if o_match:
  254. j = o_match.start(0)
  255. if q_match:
  256. k = q_match.start(0)
  257. if q_match and (not o_match or k < j):
  258. _push(b[i:k])
  259. _push(b[k + 1 : k + 2])
  260. i = k + 2
  261. else:
  262. _push(b[i:j])
  263. rv.append(int(b[j + 1 : j + 4], 8))
  264. i = j + 4
  265. return bytes(rv)
  266. def _cookie_parse_impl(b):
  267. """Lowlevel cookie parsing facility that operates on bytes."""
  268. i = 0
  269. n = len(b)
  270. while i < n:
  271. match = _cookie_re.search(b + b";", i)
  272. if not match:
  273. break
  274. key = match.group("key").strip()
  275. value = match.group("val") or b""
  276. i = match.end(0)
  277. # Ignore parameters. We have no interest in them.
  278. if key.lower() not in _cookie_params:
  279. yield _cookie_unquote(key), _cookie_unquote(value)
  280. def _encode_idna(domain):
  281. # If we're given bytes, make sure they fit into ASCII
  282. if not isinstance(domain, text_type):
  283. domain.decode("ascii")
  284. return domain
  285. # Otherwise check if it's already ascii, then return
  286. try:
  287. return domain.encode("ascii")
  288. except UnicodeError:
  289. pass
  290. # Otherwise encode each part separately
  291. parts = domain.split(".")
  292. for idx, part in enumerate(parts):
  293. parts[idx] = part.encode("idna")
  294. return b".".join(parts)
  295. def _decode_idna(domain):
  296. # If the input is a string try to encode it to ascii to
  297. # do the idna decoding. if that fails because of an
  298. # unicode error, then we already have a decoded idna domain
  299. if isinstance(domain, text_type):
  300. try:
  301. domain = domain.encode("ascii")
  302. except UnicodeError:
  303. return domain
  304. # Decode each part separately. If a part fails, try to
  305. # decode it with ascii and silently ignore errors. This makes
  306. # most sense because the idna codec does not have error handling
  307. parts = domain.split(b".")
  308. for idx, part in enumerate(parts):
  309. try:
  310. parts[idx] = part.decode("idna")
  311. except UnicodeError:
  312. parts[idx] = part.decode("ascii", "ignore")
  313. return ".".join(parts)
  314. def _make_cookie_domain(domain):
  315. if domain is None:
  316. return None
  317. domain = _encode_idna(domain)
  318. if b":" in domain:
  319. domain = domain.split(b":", 1)[0]
  320. if b"." in domain:
  321. return domain
  322. raise ValueError(
  323. "Setting 'domain' for a cookie on a server running locally (ex: "
  324. "localhost) is not supported by complying browsers. You should "
  325. "have something like: '127.0.0.1 localhost dev.localhost' on "
  326. "your hosts file and then point your server to run on "
  327. "'dev.localhost' and also set 'domain' for 'dev.localhost'"
  328. )
  329. def _easteregg(app=None):
  330. """Like the name says. But who knows how it works?"""
  331. def bzzzzzzz(gyver):
  332. import base64
  333. import zlib
  334. return zlib.decompress(base64.b64decode(gyver)).decode("ascii")
  335. gyver = u"\n".join(
  336. [
  337. x + (77 - len(x)) * u" "
  338. for x in bzzzzzzz(
  339. b"""
  340. eJyFlzuOJDkMRP06xRjymKgDJCDQStBYT8BCgK4gTwfQ2fcFs2a2FzvZk+hvlcRvRJD148efHt9m
  341. 9Xz94dRY5hGt1nrYcXx7us9qlcP9HHNh28rz8dZj+q4rynVFFPdlY4zH873NKCexrDM6zxxRymzz
  342. 4QIxzK4bth1PV7+uHn6WXZ5C4ka/+prFzx3zWLMHAVZb8RRUxtFXI5DTQ2n3Hi2sNI+HK43AOWSY
  343. jmEzE4naFp58PdzhPMdslLVWHTGUVpSxImw+pS/D+JhzLfdS1j7PzUMxij+mc2U0I9zcbZ/HcZxc
  344. q1QjvvcThMYFnp93agEx392ZdLJWXbi/Ca4Oivl4h/Y1ErEqP+lrg7Xa4qnUKu5UE9UUA4xeqLJ5
  345. jWlPKJvR2yhRI7xFPdzPuc6adXu6ovwXwRPXXnZHxlPtkSkqWHilsOrGrvcVWXgGP3daXomCj317
  346. 8P2UOw/NnA0OOikZyFf3zZ76eN9QXNwYdD8f8/LdBRFg0BO3bB+Pe/+G8er8tDJv83XTkj7WeMBJ
  347. v/rnAfdO51d6sFglfi8U7zbnr0u9tyJHhFZNXYfH8Iafv2Oa+DT6l8u9UYlajV/hcEgk1x8E8L/r
  348. XJXl2SK+GJCxtnyhVKv6GFCEB1OO3f9YWAIEbwcRWv/6RPpsEzOkXURMN37J0PoCSYeBnJQd9Giu
  349. LxYQJNlYPSo/iTQwgaihbART7Fcyem2tTSCcwNCs85MOOpJtXhXDe0E7zgZJkcxWTar/zEjdIVCk
  350. iXy87FW6j5aGZhttDBoAZ3vnmlkx4q4mMmCdLtnHkBXFMCReqthSGkQ+MDXLLCpXwBs0t+sIhsDI
  351. tjBB8MwqYQpLygZ56rRHHpw+OAVyGgaGRHWy2QfXez+ZQQTTBkmRXdV/A9LwH6XGZpEAZU8rs4pE
  352. 1R4FQ3Uwt8RKEtRc0/CrANUoes3EzM6WYcFyskGZ6UTHJWenBDS7h163Eo2bpzqxNE9aVgEM2CqI
  353. GAJe9Yra4P5qKmta27VjzYdR04Vc7KHeY4vs61C0nbywFmcSXYjzBHdiEjraS7PGG2jHHTpJUMxN
  354. Jlxr3pUuFvlBWLJGE3GcA1/1xxLcHmlO+LAXbhrXah1tD6Ze+uqFGdZa5FM+3eHcKNaEarutAQ0A
  355. QMAZHV+ve6LxAwWnXbbSXEG2DmCX5ijeLCKj5lhVFBrMm+ryOttCAeFpUdZyQLAQkA06RLs56rzG
  356. 8MID55vqr/g64Qr/wqwlE0TVxgoiZhHrbY2h1iuuyUVg1nlkpDrQ7Vm1xIkI5XRKLedN9EjzVchu
  357. jQhXcVkjVdgP2O99QShpdvXWoSwkp5uMwyjt3jiWCqWGSiaaPAzohjPanXVLbM3x0dNskJsaCEyz
  358. DTKIs+7WKJD4ZcJGfMhLFBf6hlbnNkLEePF8Cx2o2kwmYF4+MzAxa6i+6xIQkswOqGO+3x9NaZX8
  359. MrZRaFZpLeVTYI9F/djY6DDVVs340nZGmwrDqTCiiqD5luj3OzwpmQCiQhdRYowUYEA3i1WWGwL4
  360. GCtSoO4XbIPFeKGU13XPkDf5IdimLpAvi2kVDVQbzOOa4KAXMFlpi/hV8F6IDe0Y2reg3PuNKT3i
  361. RYhZqtkQZqSB2Qm0SGtjAw7RDwaM1roESC8HWiPxkoOy0lLTRFG39kvbLZbU9gFKFRvixDZBJmpi
  362. Xyq3RE5lW00EJjaqwp/v3EByMSpVZYsEIJ4APaHmVtpGSieV5CALOtNUAzTBiw81GLgC0quyzf6c
  363. NlWknzJeCsJ5fup2R4d8CYGN77mu5vnO1UqbfElZ9E6cR6zbHjgsr9ly18fXjZoPeDjPuzlWbFwS
  364. pdvPkhntFvkc13qb9094LL5NrA3NIq3r9eNnop9DizWOqCEbyRBFJTHn6Tt3CG1o8a4HevYh0XiJ
  365. sR0AVVHuGuMOIfbuQ/OKBkGRC6NJ4u7sbPX8bG/n5sNIOQ6/Y/BX3IwRlTSabtZpYLB85lYtkkgm
  366. p1qXK3Du2mnr5INXmT/78KI12n11EFBkJHHp0wJyLe9MvPNUGYsf+170maayRoy2lURGHAIapSpQ
  367. krEDuNoJCHNlZYhKpvw4mspVWxqo415n8cD62N9+EfHrAvqQnINStetek7RY2Urv8nxsnGaZfRr/
  368. nhXbJ6m/yl1LzYqscDZA9QHLNbdaSTTr+kFg3bC0iYbX/eQy0Bv3h4B50/SGYzKAXkCeOLI3bcAt
  369. mj2Z/FM1vQWgDynsRwNvrWnJHlespkrp8+vO1jNaibm+PhqXPPv30YwDZ6jApe3wUjFQobghvW9p
  370. 7f2zLkGNv8b191cD/3vs9Q833z8t"""
  371. ).splitlines()
  372. ]
  373. )
  374. def easteregged(environ, start_response):
  375. def injecting_start_response(status, headers, exc_info=None):
  376. headers.append(("X-Powered-By", "Werkzeug"))
  377. return start_response(status, headers, exc_info)
  378. if app is not None and environ.get("QUERY_STRING") != "macgybarchakku":
  379. return app(environ, injecting_start_response)
  380. injecting_start_response("200 OK", [("Content-Type", "text/html")])
  381. return [
  382. (
  383. u"""
  384. <!DOCTYPE html>
  385. <html>
  386. <head>
  387. <title>About Werkzeug</title>
  388. <style type="text/css">
  389. body { font: 15px Georgia, serif; text-align: center; }
  390. a { color: #333; text-decoration: none; }
  391. h1 { font-size: 30px; margin: 20px 0 10px 0; }
  392. p { margin: 0 0 30px 0; }
  393. pre { font: 11px 'Consolas', 'Monaco', monospace; line-height: 0.95; }
  394. </style>
  395. </head>
  396. <body>
  397. <h1><a href="http://werkzeug.pocoo.org/">Werkzeug</a></h1>
  398. <p>the Swiss Army knife of Python web development.</p>
  399. <pre>%s\n\n\n</pre>
  400. </body>
  401. </html>"""
  402. % gyver
  403. ).encode("latin1")
  404. ]
  405. return easteregged