ezKit 1.11.14__tar.gz → 1.11.15__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. {ezkit-1.11.14/ezKit.egg-info → ezkit-1.11.15}/PKG-INFO +1 -1
  2. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/bottle.py +287 -178
  3. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/utils.py +8 -2
  4. {ezkit-1.11.14 → ezkit-1.11.15/ezKit.egg-info}/PKG-INFO +1 -1
  5. {ezkit-1.11.14 → ezkit-1.11.15}/setup.py +1 -1
  6. {ezkit-1.11.14 → ezkit-1.11.15}/LICENSE +0 -0
  7. {ezkit-1.11.14 → ezkit-1.11.15}/MANIFEST.in +0 -0
  8. {ezkit-1.11.14 → ezkit-1.11.15}/README.md +0 -0
  9. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/__init__.py +0 -0
  10. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/_file.py +0 -0
  11. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/bottle_extensions.py +0 -0
  12. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/cipher.py +0 -0
  13. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/database.py +0 -0
  14. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/http.py +0 -0
  15. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/markdown_to_html.template +0 -0
  16. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/mongo.py +0 -0
  17. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/qywx.py +0 -0
  18. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/redis.py +0 -0
  19. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/sendemail.py +0 -0
  20. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/token.py +0 -0
  21. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit/xftp.py +0 -0
  22. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit.egg-info/SOURCES.txt +0 -0
  23. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit.egg-info/dependency_links.txt +0 -0
  24. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit.egg-info/requires.txt +0 -0
  25. {ezkit-1.11.14 → ezkit-1.11.15}/ezKit.egg-info/top_level.txt +0 -0
  26. {ezkit-1.11.14 → ezkit-1.11.15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ezKit
3
- Version: 1.11.14
3
+ Version: 1.11.15
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -14,6 +14,7 @@ License: MIT (see LICENSE for details)
14
14
  """
15
15
 
16
16
  from __future__ import print_function
17
+
17
18
  import sys
18
19
 
19
20
  __author__ = 'Marcel Hellkamp'
@@ -62,48 +63,66 @@ def _cli_patch(cli_args): # pragma: no coverage
62
63
  eventlet.monkey_patch()
63
64
 
64
65
 
65
- if __name__ == '__main__':
66
+ if __name__ == "__main__":
66
67
  _cli_patch(sys.argv)
67
68
 
68
69
  ###############################################################################
69
70
  # Imports and Python 2/3 unification ##########################################
70
71
  ###############################################################################
71
72
 
72
- import base64, calendar, email.utils, functools, hmac, itertools,\
73
- mimetypes, os, re, tempfile, threading, time, warnings, weakref, hashlib
74
-
75
- from types import FunctionType
76
- from datetime import date as datedate, datetime, timedelta
73
+ import base64
74
+ import calendar
75
+ import email.utils
76
+ import functools
77
+ import hashlib
78
+ import hmac
79
+ import itertools
80
+ import mimetypes
81
+ import os
82
+ import re
83
+ import tempfile
84
+ import threading
85
+ import time
86
+ import warnings
87
+ import weakref
88
+ from datetime import date as datedate
89
+ from datetime import datetime, timedelta
77
90
  from tempfile import NamedTemporaryFile
78
91
  from traceback import format_exc, print_exc
92
+ from types import FunctionType
79
93
  from unicodedata import normalize
80
94
 
81
95
  try:
82
- from ujson import dumps as json_dumps, loads as json_lds
96
+ from ujson import dumps as json_dumps
97
+ from ujson import loads as json_lds
83
98
  except ImportError:
84
- from json import dumps as json_dumps, loads as json_lds
99
+ from json import dumps as json_dumps
100
+ from json import loads as json_lds
85
101
 
86
102
  py = sys.version_info
87
103
  py3k = py.major > 2
88
104
 
89
105
  # Lots of stdlib and builtin differences.
90
106
  if py3k:
91
- import http.client as httplib
92
107
  import _thread as thread
93
- from urllib.parse import urljoin, SplitResult as UrlSplitResult
94
- from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote
108
+ import http.client as httplib
109
+ from urllib.parse import SplitResult as UrlSplitResult
110
+ from urllib.parse import quote as urlquote
111
+ from urllib.parse import unquote as urlunquote
112
+ from urllib.parse import urlencode, urljoin
95
113
  urlunquote = functools.partial(urlunquote, encoding='latin1')
96
- from http.cookies import SimpleCookie, Morsel, CookieError
97
- from collections.abc import MutableMapping as DictMixin
98
- from types import ModuleType as new_module
99
- import pickle
100
- from io import BytesIO
101
114
  import configparser
115
+ import pickle
116
+ from collections.abc import MutableMapping as DictMixin
102
117
  from datetime import timezone
118
+ from http.cookies import CookieError, Morsel, SimpleCookie
119
+ from io import BytesIO
120
+ from types import ModuleType as new_module
103
121
  UTC = timezone.utc
104
122
  # getfullargspec was deprecated in 3.5 and un-deprecated in 3.6
105
123
  # getargspec was deprecated in 3.0 and removed in 3.11
106
124
  from inspect import getfullargspec
125
+
107
126
  def getargspec(func):
108
127
  spec = getfullargspec(func)
109
128
  kwargs = makelist(spec[0]) + makelist(spec.kwonlyargs)
@@ -111,27 +130,31 @@ if py3k:
111
130
 
112
131
  basestring = str
113
132
  unicode = str
114
- json_loads = lambda s: json_lds(touni(s))
115
- callable = lambda x: hasattr(x, '__call__')
133
+ def json_loads(s): return json_lds(touni(s))
134
+ def callable(x): return hasattr(x, '__call__')
116
135
  imap = map
117
136
 
118
137
  def _raise(*a):
119
138
  raise a[0](a[1]).with_traceback(a[2])
120
139
  else: # 2.x
121
140
  warnings.warn("Python 2 support will be dropped in Bottle 0.14", DeprecationWarning)
122
- import httplib
123
- import thread
124
- from urlparse import urljoin, SplitResult as UrlSplitResult
125
- from urllib import urlencode, quote as urlquote, unquote as urlunquote
126
- from Cookie import SimpleCookie, Morsel, CookieError
141
+ from collections import MutableMapping as DictMixin
142
+ from datetime import tzinfo
143
+ from imp import new_module
144
+ from inspect import getargspec
127
145
  from itertools import imap
146
+ from urllib import quote as urlquote
147
+ from urllib import unquote as urlunquote
148
+ from urllib import urlencode
149
+
150
+ import ConfigParser as configparser
128
151
  import cPickle as pickle
129
- from imp import new_module
152
+ import httplib
153
+ import thread
154
+ from Cookie import CookieError, Morsel, SimpleCookie
130
155
  from StringIO import StringIO as BytesIO
131
- import ConfigParser as configparser
132
- from collections import MutableMapping as DictMixin
133
- from inspect import getargspec
134
- from datetime import tzinfo
156
+ from urlparse import SplitResult as UrlSplitResult
157
+ from urlparse import urljoin
135
158
 
136
159
  class _UTC(tzinfo):
137
160
  def utcoffset(self, dt): return timedelta(0)
@@ -164,7 +187,7 @@ def _stderr(*args):
164
187
  try:
165
188
  print(*args, file=sys.stderr)
166
189
  except (IOError, AttributeError):
167
- pass # Some environments do not allow printing (mod_wsgi)
190
+ pass # Some environments do not allow printing (mod_wsgi)
168
191
 
169
192
 
170
193
  # A bug in functools causes it to break if the wrapper is an instance method
@@ -209,17 +232,21 @@ class DictProperty(object):
209
232
  return self
210
233
 
211
234
  def __get__(self, obj, cls):
212
- if obj is None: return self
235
+ if obj is None:
236
+ return self
213
237
  key, storage = self.key, getattr(obj, self.attr)
214
- if key not in storage: storage[key] = self.getter(obj)
238
+ if key not in storage:
239
+ storage[key] = self.getter(obj)
215
240
  return storage[key]
216
241
 
217
242
  def __set__(self, obj, value):
218
- if self.read_only: raise AttributeError("Read-Only property.")
243
+ if self.read_only:
244
+ raise AttributeError("Read-Only property.")
219
245
  getattr(obj, self.attr)[self.key] = value
220
246
 
221
247
  def __delete__(self, obj):
222
- if self.read_only: raise AttributeError("Read-Only property.")
248
+ if self.read_only:
249
+ raise AttributeError("Read-Only property.")
223
250
  del getattr(obj, self.attr)[self.key]
224
251
 
225
252
 
@@ -233,7 +260,8 @@ class cached_property(object):
233
260
  self.func = func
234
261
 
235
262
  def __get__(self, obj, cls):
236
- if obj is None: return self
263
+ if obj is None:
264
+ return self
237
265
  value = obj.__dict__[self.func.__name__] = self.func(obj)
238
266
  return value
239
267
 
@@ -339,9 +367,9 @@ class Router(object):
339
367
  self.filters[name] = func
340
368
 
341
369
  rule_syntax = re.compile('(\\\\*)'
342
- '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'
343
- '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'
344
- '(?::((?:\\\\.|[^\\\\>])+)?)?)?>))')
370
+ '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'
371
+ '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'
372
+ '(?::((?:\\\\.|[^\\\\>])+)?)?)?>))')
345
373
 
346
374
  def _itertokens(self, rule):
347
375
  offset, prefix = 0, ''
@@ -376,7 +404,8 @@ class Router(object):
376
404
  for key, mode, conf in self._itertokens(rule):
377
405
  if mode:
378
406
  is_static = False
379
- if mode == 'default': mode = self.default_filter
407
+ if mode == 'default':
408
+ mode = self.default_filter
380
409
  mask, in_filter, out_filter = self.filters[mode](conf)
381
410
  if not key:
382
411
  pattern += '(?:%s)' % mask
@@ -385,14 +414,16 @@ class Router(object):
385
414
  else:
386
415
  pattern += '(?P<%s>%s)' % (key, mask)
387
416
  keys.append(key)
388
- if in_filter: filters.append((key, in_filter))
417
+ if in_filter:
418
+ filters.append((key, in_filter))
389
419
  builder.append((key, out_filter or str))
390
420
  elif key:
391
421
  pattern += re.escape(key)
392
422
  builder.append((None, key))
393
423
 
394
424
  self.builder[rule] = builder
395
- if name: self.builder[name] = builder
425
+ if name:
426
+ self.builder[name] = builder
396
427
 
397
428
  if is_static and not self.strict_order:
398
429
  self.static.setdefault(method, {})
@@ -548,11 +579,15 @@ class Route(object):
548
579
  """ Yield all Plugins affecting this route. """
549
580
  unique = set()
550
581
  for p in reversed(self.app.plugins + self.plugins):
551
- if True in self.skiplist: break
582
+ if True in self.skiplist:
583
+ break
552
584
  name = getattr(p, 'name', False)
553
- if name and (name in self.skiplist or name in unique): continue
554
- if p in self.skiplist or type(p) in self.skiplist: continue
555
- if name: unique.add(name)
585
+ if name and (name in self.skiplist or name in unique):
586
+ continue
587
+ if p in self.skiplist or type(p) in self.skiplist:
588
+ continue
589
+ if name:
590
+ unique.add(name)
556
591
  yield p
557
592
 
558
593
  def _make_callback(self):
@@ -815,7 +850,8 @@ class Bottle(object):
815
850
  applied to all routes of this application. A plugin may be a simple
816
851
  decorator or an object that implements the :class:`Plugin` API.
817
852
  """
818
- if hasattr(plugin, 'setup'): plugin.setup(self)
853
+ if hasattr(plugin, 'setup'):
854
+ plugin.setup(self)
819
855
  if not callable(plugin) and not hasattr(plugin, 'apply'):
820
856
  raise TypeError("Plugins must be callable or implement .apply()")
821
857
  self.plugins.append(plugin)
@@ -830,20 +866,25 @@ class Bottle(object):
830
866
  removed, remove = [], plugin
831
867
  for i, plugin in list(enumerate(self.plugins))[::-1]:
832
868
  if remove is True or remove is plugin or remove is type(plugin) \
833
- or getattr(plugin, 'name', True) == remove:
869
+ or getattr(plugin, 'name', True) == remove:
834
870
  removed.append(plugin)
835
871
  del self.plugins[i]
836
- if hasattr(plugin, 'close'): plugin.close()
837
- if removed: self.reset()
872
+ if hasattr(plugin, 'close'):
873
+ plugin.close()
874
+ if removed:
875
+ self.reset()
838
876
  return removed
839
877
 
840
878
  def reset(self, route=None):
841
879
  """ Reset all routes (force plugins to be re-applied) and clear all
842
880
  caches. If an ID or route object is given, only that specific route
843
881
  is affected. """
844
- if route is None: routes = self.routes
845
- elif isinstance(route, Route): routes = [route]
846
- else: routes = [self.routes[route]]
882
+ if route is None:
883
+ routes = self.routes
884
+ elif isinstance(route, Route):
885
+ routes = [route]
886
+ else:
887
+ routes = [self.routes[route]]
847
888
  for route in routes:
848
889
  route.reset()
849
890
  if DEBUG:
@@ -854,7 +895,8 @@ class Bottle(object):
854
895
  def close(self):
855
896
  """ Close the application and all installed plugins. """
856
897
  for plugin in self.plugins:
857
- if hasattr(plugin, 'close'): plugin.close()
898
+ if hasattr(plugin, 'close'):
899
+ plugin.close()
858
900
 
859
901
  def run(self, **kwargs):
860
902
  """ Calls :func:`run` with the same parameters. """
@@ -877,7 +919,8 @@ class Bottle(object):
877
919
  attribute."""
878
920
  self.routes.append(route)
879
921
  self.router.add(route.rule, route.method, route, name=route.name)
880
- if DEBUG: route.prepare()
922
+ if DEBUG:
923
+ route.prepare()
881
924
 
882
925
  def route(self,
883
926
  path=None,
@@ -911,12 +954,14 @@ class Bottle(object):
911
954
  Any additional keyword arguments are stored as route-specific
912
955
  configuration and passed to plugins (see :meth:`Plugin.apply`).
913
956
  """
914
- if callable(path): path, callback = None, path
957
+ if callable(path):
958
+ path, callback = None, path
915
959
  plugins = makelist(apply)
916
960
  skiplist = makelist(skip)
917
961
 
918
962
  def decorator(callback):
919
- if isinstance(callback, basestring): callback = load(callback)
963
+ if isinstance(callback, basestring):
964
+ callback = load(callback)
920
965
  for rule in makelist(path) or yieldroutes(callback):
921
966
  for verb in makelist(method):
922
967
  verb = verb.upper()
@@ -965,7 +1010,8 @@ class Bottle(object):
965
1010
  """
966
1011
 
967
1012
  def decorator(callback):
968
- if isinstance(callback, basestring): callback = load(callback)
1013
+ if isinstance(callback, basestring):
1014
+ callback = load(callback)
969
1015
  self.error_handler[int(code)] = callback
970
1016
  return callback
971
1017
 
@@ -984,7 +1030,7 @@ class Bottle(object):
984
1030
  response.bind()
985
1031
 
986
1032
  try:
987
- while True: # Remove in 0.14 together with RouteReset
1033
+ while True: # Remove in 0.14 together with RouteReset
988
1034
  out = None
989
1035
  try:
990
1036
  self.trigger_hook('before_request')
@@ -1014,7 +1060,8 @@ class Bottle(object):
1014
1060
  except (KeyboardInterrupt, SystemExit, MemoryError):
1015
1061
  raise
1016
1062
  except Exception as E:
1017
- if not self.catchall: raise
1063
+ if not self.catchall:
1064
+ raise
1018
1065
  stacktrace = format_exc()
1019
1066
  environ['wsgi.errors'].write(stacktrace)
1020
1067
  environ['wsgi.errors'].flush()
@@ -1038,7 +1085,7 @@ class Bottle(object):
1038
1085
  return []
1039
1086
  # Join lists of byte or unicode strings. Mixed lists are NOT supported
1040
1087
  if isinstance(out, (tuple, list))\
1041
- and isinstance(out[0], (bytes, unicode)):
1088
+ and isinstance(out[0], (bytes, unicode)):
1042
1089
  out = out[0][0:0].join(out) # b'abc'[0:0] -> b''
1043
1090
  # Encode unicode strings
1044
1091
  if isinstance(out, unicode):
@@ -1079,7 +1126,8 @@ class Bottle(object):
1079
1126
  except (KeyboardInterrupt, SystemExit, MemoryError):
1080
1127
  raise
1081
1128
  except Exception as error:
1082
- if not self.catchall: raise
1129
+ if not self.catchall:
1130
+ raise
1083
1131
  first = HTTPError(500, 'Unhandled exception', error, format_exc())
1084
1132
 
1085
1133
  # These are the inner types allowed in iterator or generator objects.
@@ -1088,7 +1136,7 @@ class Bottle(object):
1088
1136
  elif isinstance(first, bytes):
1089
1137
  new_iter = itertools.chain([first], iout)
1090
1138
  elif isinstance(first, unicode):
1091
- encoder = lambda x: x.encode(response.charset)
1139
+ def encoder(x): return x.encode(response.charset)
1092
1140
  new_iter = imap(encoder, itertools.chain([first], iout))
1093
1141
  else:
1094
1142
  msg = 'Unsupported response type: %s' % type(first)
@@ -1103,8 +1151,9 @@ class Bottle(object):
1103
1151
  out = self._cast(self._handle(environ))
1104
1152
  # rfc2616 section 4.3
1105
1153
  if response._status_code in (100, 101, 204, 304)\
1106
- or environ['REQUEST_METHOD'] == 'HEAD':
1107
- if hasattr(out, 'close'): out.close()
1154
+ or environ['REQUEST_METHOD'] == 'HEAD':
1155
+ if hasattr(out, 'close'):
1156
+ out.close()
1108
1157
  out = []
1109
1158
  exc_info = environ.get('bottle.exc_info')
1110
1159
  if exc_info is not None:
@@ -1114,7 +1163,8 @@ class Bottle(object):
1114
1163
  except (KeyboardInterrupt, SystemExit, MemoryError):
1115
1164
  raise
1116
1165
  except Exception as E:
1117
- if not self.catchall: raise
1166
+ if not self.catchall:
1167
+ raise
1118
1168
  err = '<h1>Critical error while processing request: %s</h1>' \
1119
1169
  % html_escape(environ.get('PATH_INFO', '/'))
1120
1170
  if DEBUG:
@@ -1303,7 +1353,8 @@ class BaseRequest(object):
1303
1353
  maxread = max(0, self.content_length)
1304
1354
  while maxread:
1305
1355
  part = read(min(maxread, bufsize))
1306
- if not part: break
1356
+ if not part:
1357
+ break
1307
1358
  yield part
1308
1359
  maxread -= len(part)
1309
1360
 
@@ -1316,20 +1367,24 @@ class BaseRequest(object):
1316
1367
  while header[-2:] != rn:
1317
1368
  c = read(1)
1318
1369
  header += c
1319
- if not c: raise err
1320
- if len(header) > bufsize: raise err
1370
+ if not c:
1371
+ raise err
1372
+ if len(header) > bufsize:
1373
+ raise err
1321
1374
  size, _, _ = header.partition(sem)
1322
1375
  try:
1323
1376
  maxread = int(tonat(size.strip()), 16)
1324
1377
  except ValueError:
1325
1378
  raise err
1326
- if maxread == 0: break
1379
+ if maxread == 0:
1380
+ break
1327
1381
  buff = bs
1328
1382
  while maxread > 0:
1329
1383
  if not buff:
1330
1384
  buff = read(min(maxread, bufsize))
1331
1385
  part, buff = buff[:maxread], buff[maxread:]
1332
- if not part: raise err
1386
+ if not part:
1387
+ raise err
1333
1388
  yield part
1334
1389
  maxread -= len(part)
1335
1390
  if read(2) != rn:
@@ -1408,15 +1463,15 @@ class BaseRequest(object):
1408
1463
  if not boundary:
1409
1464
  raise MultipartError("Invalid content type header, missing boundary")
1410
1465
  parser = _MultipartParser(self.body, boundary, self.content_length,
1411
- mem_limit=self.MEMFILE_MAX, memfile_limit=self.MEMFILE_MAX,
1412
- charset=charset)
1466
+ mem_limit=self.MEMFILE_MAX, memfile_limit=self.MEMFILE_MAX,
1467
+ charset=charset)
1413
1468
 
1414
1469
  for part in parser.parse():
1415
1470
  if not part.filename and part.is_buffered():
1416
1471
  post[part.name] = tonat(part.value, 'utf8')
1417
1472
  else:
1418
1473
  post[part.name] = FileUpload(part.file, part.name,
1419
- part.filename, part.headerlist)
1474
+ part.filename, part.headerlist)
1420
1475
 
1421
1476
  return post
1422
1477
 
@@ -1436,7 +1491,7 @@ class BaseRequest(object):
1436
1491
  server. """
1437
1492
  env = self.environ
1438
1493
  http = env.get('HTTP_X_FORWARDED_PROTO') \
1439
- or env.get('wsgi.url_scheme', 'http')
1494
+ or env.get('wsgi.url_scheme', 'http')
1440
1495
  host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST')
1441
1496
  if not host:
1442
1497
  # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients.
@@ -1511,9 +1566,11 @@ class BaseRequest(object):
1511
1566
  the user field is looked up from the ``REMOTE_USER`` environ
1512
1567
  variable. On any errors, None is returned. """
1513
1568
  basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION', ''))
1514
- if basic: return basic
1569
+ if basic:
1570
+ return basic
1515
1571
  ruser = self.environ.get('REMOTE_USER')
1516
- if ruser: return (ruser, None)
1572
+ if ruser:
1573
+ return (ruser, None)
1517
1574
  return None
1518
1575
 
1519
1576
  @property
@@ -1523,7 +1580,8 @@ class BaseRequest(object):
1523
1580
  work if all proxies support the ```X-Forwarded-For`` header. Note
1524
1581
  that this information can be forged by malicious clients. """
1525
1582
  proxy = self.environ.get('HTTP_X_FORWARDED_FOR')
1526
- if proxy: return [ip.strip() for ip in proxy.split(',')]
1583
+ if proxy:
1584
+ return [ip.strip() for ip in proxy.split(',')]
1527
1585
  remote = self.environ.get('REMOTE_ADDR')
1528
1586
  return [remote] if remote else []
1529
1587
 
@@ -1589,7 +1647,8 @@ class BaseRequest(object):
1589
1647
 
1590
1648
  def __setattr__(self, name, value):
1591
1649
  """ Define new attributes that are local to the bound request environment. """
1592
- if name == 'environ': return object.__setattr__(self, name, value)
1650
+ if name == 'environ':
1651
+ return object.__setattr__(self, name, value)
1593
1652
  key = 'bottle.request.ext.%s' % name
1594
1653
  if hasattr(self, name):
1595
1654
  raise AttributeError("Attribute already defined: %s" % name)
@@ -1622,7 +1681,8 @@ class HeaderProperty(object):
1622
1681
  self.__doc__ = 'Current value of the %r header.' % name.title()
1623
1682
 
1624
1683
  def __get__(self, obj, _):
1625
- if obj is None: return self
1684
+ if obj is None:
1685
+ return self
1626
1686
  value = obj.get_header(self.name, self.default)
1627
1687
  return self.reader(value) if self.reader else value
1628
1688
 
@@ -1649,8 +1709,8 @@ class BaseResponse(object):
1649
1709
  bad_headers = {
1650
1710
  204: frozenset(('Content-Type', 'Content-Length')),
1651
1711
  304: frozenset(('Allow', 'Content-Encoding', 'Content-Language',
1652
- 'Content-Length', 'Content-Range', 'Content-Type',
1653
- 'Content-Md5', 'Last-Modified'))
1712
+ 'Content-Length', 'Content-Range', 'Content-Type',
1713
+ 'Content-Md5', 'Last-Modified'))
1654
1714
  }
1655
1715
 
1656
1716
  def __init__(self, body='', status=None, headers=None, **more_headers):
@@ -1686,9 +1746,9 @@ class BaseResponse(object):
1686
1746
  copy._headers = dict((k, v[:]) for (k, v) in self._headers.items())
1687
1747
  if self._cookies:
1688
1748
  cookies = copy._cookies = SimpleCookie()
1689
- for k,v in self._cookies.items():
1749
+ for k, v in self._cookies.items():
1690
1750
  cookies[k] = v.value
1691
- cookies[k].update(v) # also copy cookie attributes
1751
+ cookies[k].update(v) # also copy cookie attributes
1692
1752
  return copy
1693
1753
 
1694
1754
  def __iter__(self):
@@ -1879,13 +1939,13 @@ class BaseResponse(object):
1879
1939
  self._cookies[name] = value
1880
1940
 
1881
1941
  for key, value in options.items():
1882
- if key in ('max_age', 'maxage'): # 'maxage' variant added in 0.13
1942
+ if key in ('max_age', 'maxage'): # 'maxage' variant added in 0.13
1883
1943
  key = 'max-age'
1884
1944
  if isinstance(value, timedelta):
1885
1945
  value = value.seconds + value.days * 24 * 3600
1886
1946
  if key == 'expires':
1887
1947
  value = http_date(value)
1888
- if key in ('same_site', 'samesite'): # 'samesite' variant added in 0.13
1948
+ if key in ('same_site', 'samesite'): # 'samesite' variant added in 0.13
1889
1949
  key, value = 'samesite', (value or "none").lower()
1890
1950
  if value not in ('lax', 'strict', 'none'):
1891
1951
  raise CookieError("Invalid value for SameSite")
@@ -2005,19 +2065,20 @@ class JSONPlugin(object):
2005
2065
 
2006
2066
  def setup(self, app):
2007
2067
  app.config._define('json.enable', default=True, validate=bool,
2008
- help="Enable or disable automatic dict->json filter.")
2068
+ help="Enable or disable automatic dict->json filter.")
2009
2069
  app.config._define('json.ascii', default=False, validate=bool,
2010
- help="Use only 7-bit ASCII characters in output.")
2070
+ help="Use only 7-bit ASCII characters in output.")
2011
2071
  app.config._define('json.indent', default=True, validate=bool,
2012
- help="Add whitespace to make json more readable.")
2072
+ help="Add whitespace to make json more readable.")
2013
2073
  app.config._define('json.dump_func', default=None,
2014
- help="If defined, use this function to transform"
2015
- " dict into json. The other options no longer"
2016
- " apply.")
2074
+ help="If defined, use this function to transform"
2075
+ " dict into json. The other options no longer"
2076
+ " apply.")
2017
2077
 
2018
2078
  def apply(self, callback, route):
2019
2079
  dumps = self.json_dumps
2020
- if not self.json_dumps: return callback
2080
+ if not self.json_dumps:
2081
+ return callback
2021
2082
 
2022
2083
  @functools.wraps(callback)
2023
2084
  def wrapper(*a, **ka):
@@ -2077,24 +2138,29 @@ class _ImportRedirect(object):
2077
2138
  sys.meta_path.append(self)
2078
2139
 
2079
2140
  def find_spec(self, fullname, path, target=None):
2080
- if '.' not in fullname: return
2081
- if fullname.rsplit('.', 1)[0] != self.name: return
2141
+ if '.' not in fullname:
2142
+ return
2143
+ if fullname.rsplit('.', 1)[0] != self.name:
2144
+ return
2082
2145
  from importlib.util import spec_from_loader
2083
2146
  return spec_from_loader(fullname, self)
2084
2147
 
2085
2148
  def find_module(self, fullname, path=None):
2086
- if '.' not in fullname: return
2087
- if fullname.rsplit('.', 1)[0] != self.name: return
2149
+ if '.' not in fullname:
2150
+ return
2151
+ if fullname.rsplit('.', 1)[0] != self.name:
2152
+ return
2088
2153
  return self
2089
2154
 
2090
2155
  def create_module(self, spec):
2091
2156
  return self.load_module(spec.name)
2092
2157
 
2093
2158
  def exec_module(self, module):
2094
- pass # This probably breaks importlib.reload() :/
2159
+ pass # This probably breaks importlib.reload() :/
2095
2160
 
2096
2161
  def load_module(self, fullname):
2097
- if fullname in sys.modules: return sys.modules[fullname]
2162
+ if fullname in sys.modules:
2163
+ return sys.modules[fullname]
2098
2164
  modname = fullname.rsplit('.', 1)[1]
2099
2165
  realname = self.impmask % modname
2100
2166
  __import__(realname)
@@ -2263,7 +2329,8 @@ class HeaderDict(MultiDict):
2263
2329
 
2264
2330
  def __init__(self, *a, **ka):
2265
2331
  self.dict = {}
2266
- if a or ka: self.update(*a, **ka)
2332
+ if a or ka:
2333
+ self.update(*a, **ka)
2267
2334
 
2268
2335
  def __contains__(self, key):
2269
2336
  return _hkey(key) in self.dict
@@ -2354,6 +2421,7 @@ class WSGIHeaderDict(DictMixin):
2354
2421
  def __contains__(self, key):
2355
2422
  return self._ekey(key) in self.environ
2356
2423
 
2424
+
2357
2425
  _UNSET = object()
2358
2426
 
2359
2427
  class ConfigDict(dict):
@@ -2535,7 +2603,7 @@ class ConfigDict(dict):
2535
2603
 
2536
2604
  def meta_set(self, key, metafield, value):
2537
2605
  """ Set the meta field for a key to a new value.
2538
-
2606
+
2539
2607
  Meta-fields are shared between all members of an overlay tree.
2540
2608
  """
2541
2609
  self._meta.setdefault(key, {})[metafield] = value
@@ -2595,8 +2663,6 @@ class ConfigDict(dict):
2595
2663
  return overlay
2596
2664
 
2597
2665
 
2598
-
2599
-
2600
2666
  class AppStack(list):
2601
2667
  """ A stack-like list. Calling it returns the head of the stack. """
2602
2668
 
@@ -2624,7 +2690,8 @@ class WSGIFileWrapper(object):
2624
2690
  def __init__(self, fp, buffer_size=1024 * 64):
2625
2691
  self.fp, self.buffer_size = fp, buffer_size
2626
2692
  for attr in 'fileno', 'close', 'read', 'readlines', 'tell', 'seek':
2627
- if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr))
2693
+ if hasattr(fp, attr):
2694
+ setattr(self, attr, getattr(fp, attr))
2628
2695
 
2629
2696
  def __iter__(self):
2630
2697
  buff, read = self.buffer_size, self.read
@@ -2706,11 +2773,14 @@ class ResourceManager(object):
2706
2773
  search = self.path[:]
2707
2774
  while search:
2708
2775
  path = search.pop()
2709
- if not os.path.isdir(path): continue
2776
+ if not os.path.isdir(path):
2777
+ continue
2710
2778
  for name in os.listdir(path):
2711
2779
  full = os.path.join(path, name)
2712
- if os.path.isdir(full): search.append(full)
2713
- else: yield full
2780
+ if os.path.isdir(full):
2781
+ search.append(full)
2782
+ else:
2783
+ yield full
2714
2784
 
2715
2785
  def lookup(self, name):
2716
2786
  """ Search for a resource and return an absolute file path, or `None`.
@@ -2732,7 +2802,8 @@ class ResourceManager(object):
2732
2802
  def open(self, name, mode='r', *args, **kwargs):
2733
2803
  """ Find a resource and return a file object, or raise IOError. """
2734
2804
  fname = self.lookup(name)
2735
- if not fname: raise IOError("Resource %r not found." % name)
2805
+ if not fname:
2806
+ raise IOError("Resource %r not found." % name)
2736
2807
  return self.opener(fname, mode=mode, *args, **kwargs)
2737
2808
 
2738
2809
 
@@ -2779,7 +2850,8 @@ class FileUpload(object):
2779
2850
  read, write, offset = self.file.read, fp.write, self.file.tell()
2780
2851
  while 1:
2781
2852
  buf = read(chunk_size)
2782
- if not buf: break
2853
+ if not buf:
2854
+ break
2783
2855
  write(buf)
2784
2856
  self.file.seek(offset)
2785
2857
 
@@ -2889,11 +2961,11 @@ def static_file(filename, root,
2889
2961
  mimetype, encoding = mimetypes.guess_type(name)
2890
2962
  if encoding == 'gzip':
2891
2963
  mimetype = 'application/gzip'
2892
- elif encoding: # e.g. bzip2 -> application/x-bzip2
2964
+ elif encoding: # e.g. bzip2 -> application/x-bzip2
2893
2965
  mimetype = 'application/x-' + encoding
2894
2966
 
2895
2967
  if charset and mimetype and 'charset=' not in mimetype \
2896
- and (mimetype[:5] == 'text/' or mimetype == 'application/javascript'):
2968
+ and (mimetype[:5] == 'text/' or mimetype == 'application/javascript'):
2897
2969
  mimetype += '; charset=%s' % charset
2898
2970
 
2899
2971
  if mimetype:
@@ -2903,7 +2975,7 @@ def static_file(filename, root,
2903
2975
  download = os.path.basename(filename)
2904
2976
 
2905
2977
  if download:
2906
- download = download.replace('"','')
2978
+ download = download.replace('"', '')
2907
2979
  headers['Content-Disposition'] = 'attachment; filename="%s"' % download
2908
2980
 
2909
2981
  stats = os.stat(filename)
@@ -2940,7 +3012,8 @@ def static_file(filename, root,
2940
3012
  rlen = end - offset
2941
3013
  headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, clen)
2942
3014
  headers["Content-Length"] = str(rlen)
2943
- if body: body = _closeiter(_rangeiter(body, offset, rlen), body.close)
3015
+ if body:
3016
+ body = _closeiter(_rangeiter(body, offset, rlen), body.close)
2944
3017
  return HTTPResponse(body, status=206, **headers)
2945
3018
  return HTTPResponse(body, **headers)
2946
3019
 
@@ -2953,7 +3026,8 @@ def debug(mode=True):
2953
3026
  """ Change the debug level.
2954
3027
  There is only one debug level supported at the moment."""
2955
3028
  global DEBUG
2956
- if mode: warnings.simplefilter('default')
3029
+ if mode:
3030
+ warnings.simplefilter('default')
2957
3031
  DEBUG = bool(mode)
2958
3032
 
2959
3033
 
@@ -2996,7 +3070,8 @@ def parse_auth(header):
2996
3070
  def parse_range_header(header, maxlen=0):
2997
3071
  """ Yield (start, end) ranges parsed from a HTTP Range header. Skip
2998
3072
  unsatisfiable ranges. The end index is non-inclusive."""
2999
- if not header or header[:6] != 'bytes=': return
3073
+ if not header or header[:6] != 'bytes=':
3074
+ return
3000
3075
  ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r]
3001
3076
  for start, end in ranges:
3002
3077
  try:
@@ -3051,9 +3126,11 @@ def _parse_http_header(h):
3051
3126
  def _parse_qsl(qs):
3052
3127
  r = []
3053
3128
  for pair in qs.split('&'):
3054
- if not pair: continue
3129
+ if not pair:
3130
+ continue
3055
3131
  nv = pair.split('=', 1)
3056
- if len(nv) != 2: nv.append('')
3132
+ if len(nv) != 2:
3133
+ nv.append('')
3057
3134
  key = urlunquote(nv[0].replace('+', ' '))
3058
3135
  value = urlunquote(nv[1].replace('+', ' '))
3059
3136
  r.append((key, value))
@@ -3107,7 +3184,7 @@ def html_escape(string):
3107
3184
  def html_quote(string):
3108
3185
  """ Escape and quote a string to be used as an HTTP attribute."""
3109
3186
  return '"%s"' % html_escape(string).replace('\n', '&#10;')\
3110
- .replace('\r', '&#13;').replace('\t', '&#9;')
3187
+ .replace('\r', '&#13;').replace('\t', '&#9;')
3111
3188
 
3112
3189
 
3113
3190
  def yieldroutes(func):
@@ -3139,11 +3216,14 @@ def path_shift(script_name, path_info, shift=1):
3139
3216
  :param shift: The number of path fragments to shift. May be negative to
3140
3217
  change the shift direction. (default: 1)
3141
3218
  """
3142
- if shift == 0: return script_name, path_info
3219
+ if shift == 0:
3220
+ return script_name, path_info
3143
3221
  pathlist = path_info.strip('/').split('/')
3144
3222
  scriptlist = script_name.strip('/').split('/')
3145
- if pathlist and pathlist[0] == '': pathlist = []
3146
- if scriptlist and scriptlist[0] == '': scriptlist = []
3223
+ if pathlist and pathlist[0] == '':
3224
+ pathlist = []
3225
+ if scriptlist and scriptlist[0] == '':
3226
+ scriptlist = []
3147
3227
  if 0 < shift <= len(pathlist):
3148
3228
  moved = pathlist[:shift]
3149
3229
  scriptlist = scriptlist + moved
@@ -3157,7 +3237,8 @@ def path_shift(script_name, path_info, shift=1):
3157
3237
  raise AssertionError("Cannot shift. Nothing left from %s" % empty)
3158
3238
  new_script_name = '/' + '/'.join(scriptlist)
3159
3239
  new_path_info = '/' + '/'.join(pathlist)
3160
- if path_info.endswith('/') and pathlist: new_path_info += '/'
3240
+ if path_info.endswith('/') and pathlist:
3241
+ new_path_info += '/'
3161
3242
  return new_script_name, new_path_info
3162
3243
 
3163
3244
 
@@ -3194,18 +3275,18 @@ def make_default_app_wrapper(name):
3194
3275
  return wrapper
3195
3276
 
3196
3277
 
3197
- route = make_default_app_wrapper('route')
3198
- get = make_default_app_wrapper('get')
3199
- post = make_default_app_wrapper('post')
3200
- put = make_default_app_wrapper('put')
3201
- delete = make_default_app_wrapper('delete')
3202
- patch = make_default_app_wrapper('patch')
3203
- error = make_default_app_wrapper('error')
3204
- mount = make_default_app_wrapper('mount')
3205
- hook = make_default_app_wrapper('hook')
3206
- install = make_default_app_wrapper('install')
3278
+ route = make_default_app_wrapper('route')
3279
+ get = make_default_app_wrapper('get')
3280
+ post = make_default_app_wrapper('post')
3281
+ put = make_default_app_wrapper('put')
3282
+ delete = make_default_app_wrapper('delete')
3283
+ patch = make_default_app_wrapper('patch')
3284
+ error = make_default_app_wrapper('error')
3285
+ mount = make_default_app_wrapper('mount')
3286
+ hook = make_default_app_wrapper('hook')
3287
+ install = make_default_app_wrapper('install')
3207
3288
  uninstall = make_default_app_wrapper('uninstall')
3208
- url = make_default_app_wrapper('get_url')
3289
+ url = make_default_app_wrapper('get_url')
3209
3290
 
3210
3291
 
3211
3292
  ###############################################################################
@@ -3276,7 +3357,7 @@ class _MultipartParser(object):
3276
3357
  if i >= 0:
3277
3358
  yield chunk[scanpos:i], b'\r\n'
3278
3359
  scanpos = i + 2
3279
- else: # CRLF not found
3360
+ else: # CRLF not found
3280
3361
  partial = chunk[scanpos:] if scanpos else chunk
3281
3362
  break
3282
3363
 
@@ -3418,7 +3499,7 @@ class _MultipartPart(object):
3418
3499
  if "filename" in self.options:
3419
3500
  self.filename = self.options.get("filename")
3420
3501
  if self.filename[1:3] == ":\\" or self.filename[:2] == "\\\\":
3421
- self.filename = self.filename.split("\\")[-1] # ie6 bug
3502
+ self.filename = self.filename.split("\\")[-1] # ie6 bug
3422
3503
 
3423
3504
  self.content_type, options = _parse_http_header(content_type)[0] if content_type else (None, {})
3424
3505
  self.charset = options.get("charset") or self.charset
@@ -3477,7 +3558,7 @@ class ServerAdapter(object):
3477
3558
 
3478
3559
  def __repr__(self):
3479
3560
  args = ', '.join('%s=%s' % (k, repr(v))
3480
- for k, v in self.options.items())
3561
+ for k, v in self.options.items())
3481
3562
  return "%s(%s)" % (self.__class__.__name__, args)
3482
3563
 
3483
3564
 
@@ -3503,9 +3584,8 @@ class FlupFCGIServer(ServerAdapter):
3503
3584
 
3504
3585
  class WSGIRefServer(ServerAdapter):
3505
3586
  def run(self, app): # pragma: no cover
3506
- from wsgiref.simple_server import make_server
3507
- from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
3508
3587
  import socket
3588
+ from wsgiref.simple_server import WSGIRequestHandler, WSGIServer, make_server
3509
3589
 
3510
3590
  class FixedHandler(WSGIRequestHandler):
3511
3591
  def address_string(self): # Prevent reverse DNS lookups please.
@@ -3539,7 +3619,7 @@ class CherryPyServer(ServerAdapter):
3539
3619
  depr(0, 13, "The wsgi server part of cherrypy was split into a new "
3540
3620
  "project called 'cheroot'.", "Use the 'cheroot' server "
3541
3621
  "adapter instead of cherrypy.")
3542
- from cherrypy import wsgiserver # This will fail for CherryPy >= 9
3622
+ from cherrypy import wsgiserver # This will fail for CherryPy >= 9
3543
3623
 
3544
3624
  self.options['bind_addr'] = (self.host, self.port)
3545
3625
  self.options['wsgi_app'] = handler
@@ -3564,7 +3644,7 @@ class CherryPyServer(ServerAdapter):
3564
3644
 
3565
3645
 
3566
3646
  class CherootServer(ServerAdapter):
3567
- def run(self, handler): # pragma: no cover
3647
+ def run(self, handler): # pragma: no cover
3568
3648
  from cheroot import wsgi
3569
3649
  from cheroot.ssl import builtin
3570
3650
  self.options['bind_addr'] = (self.host, self.port)
@@ -3575,7 +3655,7 @@ class CherootServer(ServerAdapter):
3575
3655
  server = wsgi.Server(**self.options)
3576
3656
  if certfile and keyfile:
3577
3657
  server.ssl_adapter = builtin.BuiltinSSLAdapter(
3578
- certfile, keyfile, chainfile)
3658
+ certfile, keyfile, chainfile)
3579
3659
  try:
3580
3660
  server.start()
3581
3661
  finally:
@@ -3635,7 +3715,9 @@ class TornadoServer(ServerAdapter):
3635
3715
  """ The super hyped asynchronous server by facebook. Untested. """
3636
3716
 
3637
3717
  def run(self, handler): # pragma: no cover
3638
- import tornado.wsgi, tornado.httpserver, tornado.ioloop
3718
+ import tornado.httpserver
3719
+ import tornado.ioloop
3720
+ import tornado.wsgi
3639
3721
  container = tornado.wsgi.WSGIContainer(handler)
3640
3722
  server = tornado.httpserver.HTTPServer(container)
3641
3723
  server.listen(port=self.port, address=self.host)
@@ -3650,6 +3732,7 @@ class AppEngineServer(ServerAdapter):
3650
3732
  depr(0, 13, "AppEngineServer no longer required",
3651
3733
  "Configure your application directly in your app.yaml")
3652
3734
  from google.appengine.ext.webapp import util
3735
+
3653
3736
  # A main() function in the handler script enables 'App Caching'.
3654
3737
  # Lets makes sure it is there. This _really_ improves performance.
3655
3738
  module = sys.modules.get('__main__')
@@ -3662,9 +3745,9 @@ class TwistedServer(ServerAdapter):
3662
3745
  """ Untested. """
3663
3746
 
3664
3747
  def run(self, handler):
3665
- from twisted.web import server, wsgi
3666
- from twisted.python.threadpool import ThreadPool
3667
3748
  from twisted.internet import reactor
3749
+ from twisted.python.threadpool import ThreadPool
3750
+ from twisted.web import server, wsgi
3668
3751
  thread_pool = ThreadPool()
3669
3752
  thread_pool.start()
3670
3753
  reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
@@ -3691,7 +3774,7 @@ class GeventServer(ServerAdapter):
3691
3774
  """
3692
3775
 
3693
3776
  def run(self, handler):
3694
- from gevent import pywsgi, local
3777
+ from gevent import local, pywsgi
3695
3778
  if not isinstance(threading.local(), local.local):
3696
3779
  msg = "Bottle requires gevent.monkey.patch_all() (before import)"
3697
3780
  raise RuntimeError(msg)
@@ -3740,7 +3823,7 @@ class EventletServer(ServerAdapter):
3740
3823
  """
3741
3824
 
3742
3825
  def run(self, handler):
3743
- from eventlet import wsgi, listen, patcher
3826
+ from eventlet import listen, patcher, wsgi
3744
3827
  if not patcher.is_monkey_patched(os):
3745
3828
  msg = "Bottle requires eventlet.monkey_patch() (before import)"
3746
3829
  raise RuntimeError(msg)
@@ -3768,6 +3851,7 @@ class BjoernServer(ServerAdapter):
3768
3851
 
3769
3852
  class AsyncioServerAdapter(ServerAdapter):
3770
3853
  """ Extend ServerAdapter for adding custom event loop """
3854
+
3771
3855
  def get_event_loop(self):
3772
3856
  pass
3773
3857
 
@@ -3783,6 +3867,7 @@ class AiohttpServer(AsyncioServerAdapter):
3783
3867
 
3784
3868
  def run(self, handler):
3785
3869
  import asyncio
3870
+
3786
3871
  from aiohttp_wsgi.wsgi import serve
3787
3872
  self.loop = self.get_event_loop()
3788
3873
  asyncio.set_event_loop(self.loop)
@@ -3798,6 +3883,7 @@ class AiohttpUVLoopServer(AiohttpServer):
3798
3883
  """uvloop
3799
3884
  https://github.com/MagicStack/uvloop
3800
3885
  """
3886
+
3801
3887
  def get_event_loop(self):
3802
3888
  import uvloop
3803
3889
  return uvloop.new_event_loop()
@@ -3855,9 +3941,12 @@ def load(target, **namespace):
3855
3941
  local variables. Example: ``import_string('re:compile(x)', x='[a-z]')``
3856
3942
  """
3857
3943
  module, target = target.split(":", 1) if ':' in target else (target, None)
3858
- if module not in sys.modules: __import__(module)
3859
- if not target: return sys.modules[module]
3860
- if target.isalnum(): return getattr(sys.modules[module], target)
3944
+ if module not in sys.modules:
3945
+ __import__(module)
3946
+ if not target:
3947
+ return sys.modules[module]
3948
+ if target.isalnum():
3949
+ return getattr(sys.modules[module], target)
3861
3950
  package_name = module.split('.')[0]
3862
3951
  namespace[package_name] = sys.modules[package_name]
3863
3952
  return eval('%s.%s' % (module, target), namespace)
@@ -3907,7 +3996,8 @@ def run(app=None,
3907
3996
  :param quiet: Suppress output to stdout and stderr? (default: False)
3908
3997
  :param options: Options passed to the server adapter.
3909
3998
  """
3910
- if NORUN: return
3999
+ if NORUN:
4000
+ return
3911
4001
  if reloader and not os.environ.get('BOTTLE_CHILD'):
3912
4002
  import subprocess
3913
4003
  fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')
@@ -3938,7 +4028,8 @@ def run(app=None,
3938
4028
  return
3939
4029
 
3940
4030
  try:
3941
- if debug is not None: _debug(debug)
4031
+ if debug is not None:
4032
+ _debug(debug)
3942
4033
  app = app or default_app()
3943
4034
  if isinstance(app, basestring):
3944
4035
  app = load_app(app)
@@ -3987,7 +4078,8 @@ def run(app=None,
3987
4078
  except (SystemExit, MemoryError):
3988
4079
  raise
3989
4080
  except:
3990
- if not reloader: raise
4081
+ if not reloader:
4082
+ raise
3991
4083
  if not getattr(server, 'quiet', quiet):
3992
4084
  print_exc()
3993
4085
  time.sleep(interval)
@@ -4007,17 +4099,19 @@ class FileCheckerThread(threading.Thread):
4007
4099
 
4008
4100
  def run(self):
4009
4101
  exists = os.path.exists
4010
- mtime = lambda p: os.stat(p).st_mtime
4102
+ def mtime(p): return os.stat(p).st_mtime
4011
4103
  files = dict()
4012
4104
 
4013
4105
  for module in list(sys.modules.values()):
4014
4106
  path = getattr(module, '__file__', '') or ''
4015
- if path[-4:] in ('.pyo', '.pyc'): path = path[:-1]
4016
- if path and exists(path): files[path] = mtime(path)
4107
+ if path[-4:] in ('.pyo', '.pyc'):
4108
+ path = path[:-1]
4109
+ if path and exists(path):
4110
+ files[path] = mtime(path)
4017
4111
 
4018
4112
  while not self.status:
4019
4113
  if not exists(self.lockfile)\
4020
- or mtime(self.lockfile) < time.time() - self.interval - 5:
4114
+ or mtime(self.lockfile) < time.time() - self.interval - 5:
4021
4115
  self.status = 'error'
4022
4116
  thread.interrupt_main()
4023
4117
  for path, lmtime in list(files.items()):
@@ -4031,7 +4125,8 @@ class FileCheckerThread(threading.Thread):
4031
4125
  self.start()
4032
4126
 
4033
4127
  def __exit__(self, exc_type, *_):
4034
- if not self.status: self.status = 'exit' # silent exit
4128
+ if not self.status:
4129
+ self.status = 'exit' # silent exit
4035
4130
  self.join()
4036
4131
  return exc_type is not None and issubclass(exc_type, KeyboardInterrupt)
4037
4132
 
@@ -4047,8 +4142,8 @@ class TemplateError(BottleException):
4047
4142
  class BaseTemplate(object):
4048
4143
  """ Base class and minimal API for template adapters """
4049
4144
  extensions = ['tpl', 'html', 'thtml', 'stpl']
4050
- settings = {} #used in prepare()
4051
- defaults = {} #used in render()
4145
+ settings = {} # used in prepare()
4146
+ defaults = {} # used in render()
4052
4147
 
4053
4148
  def __init__(self,
4054
4149
  source=None,
@@ -4094,8 +4189,10 @@ class BaseTemplate(object):
4094
4189
  for spath in lookup:
4095
4190
  spath = os.path.abspath(spath) + os.sep
4096
4191
  fname = os.path.abspath(os.path.join(spath, name))
4097
- if not fname.startswith(spath): continue
4098
- if os.path.isfile(fname): return fname
4192
+ if not fname.startswith(spath):
4193
+ continue
4194
+ if os.path.isfile(fname):
4195
+ return fname
4099
4196
  for ext in cls.extensions:
4100
4197
  if os.path.isfile('%s.%s' % (fname, ext)):
4101
4198
  return '%s.%s' % (fname, ext)
@@ -4128,8 +4225,8 @@ class BaseTemplate(object):
4128
4225
 
4129
4226
  class MakoTemplate(BaseTemplate):
4130
4227
  def prepare(self, **options):
4131
- from mako.template import Template
4132
4228
  from mako.lookup import TemplateLookup
4229
+ from mako.template import Template
4133
4230
  options.update({'input_encoding': self.encoding})
4134
4231
  options.setdefault('format_exceptions', bool(DEBUG))
4135
4232
  lookup = TemplateLookup(directories=self.lookup, **options)
@@ -4173,9 +4270,12 @@ class Jinja2Template(BaseTemplate):
4173
4270
  def prepare(self, filters=None, tests=None, globals={}, **kwargs):
4174
4271
  from jinja2 import Environment, FunctionLoader
4175
4272
  self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
4176
- if filters: self.env.filters.update(filters)
4177
- if tests: self.env.tests.update(tests)
4178
- if globals: self.env.globals.update(globals)
4273
+ if filters:
4274
+ self.env.filters.update(filters)
4275
+ if tests:
4276
+ self.env.tests.update(tests)
4277
+ if globals:
4278
+ self.env.globals.update(globals)
4179
4279
  if self.source:
4180
4280
  self.tpl = self.env.from_string(self.source)
4181
4281
  else:
@@ -4193,7 +4293,8 @@ class Jinja2Template(BaseTemplate):
4193
4293
  fname = name
4194
4294
  else:
4195
4295
  fname = self.search(name, self.lookup)
4196
- if not fname: return
4296
+ if not fname:
4297
+ return
4197
4298
  with open(fname, "rb") as f:
4198
4299
  return (f.read().decode(self.encoding), fname, lambda: False)
4199
4300
 
@@ -4258,7 +4359,7 @@ class SimpleTemplate(BaseTemplate):
4258
4359
  exec(self.co, env)
4259
4360
  if env.get('_rebase'):
4260
4361
  subtpl, rargs = env.pop('_rebase')
4261
- rargs['base'] = ''.join(_stdout) #copy stdout
4362
+ rargs['base'] = ''.join(_stdout) # copy stdout
4262
4363
  del _stdout[:] # clear stdout
4263
4364
  return self._include(env, subtpl, **rargs)
4264
4365
  return env
@@ -4332,7 +4433,6 @@ class StplParser(object):
4332
4433
  _re_tok = '(?mx)' + _re_tok
4333
4434
  _re_inl = '(?mx)' + _re_inl
4334
4435
 
4335
-
4336
4436
  default_syntax = '<% %> % {{ }}'
4337
4437
 
4338
4438
  def __init__(self, source, syntax=None, encoding='utf8'):
@@ -4362,7 +4462,8 @@ class StplParser(object):
4362
4462
  syntax = property(get_syntax, set_syntax)
4363
4463
 
4364
4464
  def translate(self):
4365
- if self.offset: raise RuntimeError('Parser is a one time instance.')
4465
+ if self.offset:
4466
+ raise RuntimeError('Parser is a one time instance.')
4366
4467
  while True:
4367
4468
  m = self.re_split.search(self.source, pos=self.offset)
4368
4469
  if m:
@@ -4423,8 +4524,10 @@ class StplParser(object):
4423
4524
  code_line = _blk2
4424
4525
  self.indent_mod -= 1
4425
4526
  elif _cend: # The end-code-block template token (usually '%>')
4426
- if multiline: multiline = False
4427
- else: code_line += _cend
4527
+ if multiline:
4528
+ multiline = False
4529
+ else:
4530
+ code_line += _cend
4428
4531
  elif _end:
4429
4532
  self.indent -= 1
4430
4533
  self.indent_mod += 1
@@ -4440,19 +4543,23 @@ class StplParser(object):
4440
4543
  def flush_text(self):
4441
4544
  text = ''.join(self.text_buffer)
4442
4545
  del self.text_buffer[:]
4443
- if not text: return
4546
+ if not text:
4547
+ return
4444
4548
  parts, pos, nl = [], 0, '\\\n' + ' ' * self.indent
4445
4549
  for m in self.re_inl.finditer(text):
4446
4550
  prefix, pos = text[pos:m.start()], m.end()
4447
4551
  if prefix:
4448
4552
  parts.append(nl.join(map(repr, prefix.splitlines(True))))
4449
- if prefix.endswith('\n'): parts[-1] += nl
4553
+ if prefix.endswith('\n'):
4554
+ parts[-1] += nl
4450
4555
  parts.append(self.process_inline(m.group(1).strip()))
4451
4556
  if pos < len(text):
4452
4557
  prefix = text[pos:]
4453
4558
  lines = prefix.splitlines(True)
4454
- if lines[-1].endswith('\\\\\n'): lines[-1] = lines[-1][:-3]
4455
- elif lines[-1].endswith('\\\\\r\n'): lines[-1] = lines[-1][:-4]
4559
+ if lines[-1].endswith('\\\\\n'):
4560
+ lines[-1] = lines[-1][:-3]
4561
+ elif lines[-1].endswith('\\\\\r\n'):
4562
+ lines[-1] = lines[-1][:-4]
4456
4563
  parts.append(nl.join(map(repr, lines)))
4457
4564
  code = '_printlist((%s,))' % ', '.join(parts)
4458
4565
  self.lineno += code.count('\n') + 1
@@ -4460,7 +4567,8 @@ class StplParser(object):
4460
4567
 
4461
4568
  @staticmethod
4462
4569
  def process_inline(chunk):
4463
- if chunk[0] == '!': return '_str(%s)' % chunk[1:]
4570
+ if chunk[0] == '!':
4571
+ return '_str(%s)' % chunk[1:]
4464
4572
  return '_escape(%s)' % chunk
4465
4573
 
4466
4574
  def write_code(self, line, comment=''):
@@ -4486,7 +4594,8 @@ def template(*args, **kwargs):
4486
4594
  settings = kwargs.pop('template_settings', {})
4487
4595
  if isinstance(tpl, adapter):
4488
4596
  TEMPLATES[tplid] = tpl
4489
- if settings: TEMPLATES[tplid].prepare(**settings)
4597
+ if settings:
4598
+ TEMPLATES[tplid].prepare(**settings)
4490
4599
  elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
4491
4600
  TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings)
4492
4601
  else:
@@ -4550,7 +4659,7 @@ HTTP_CODES[418] = "I'm a teapot" # RFC 2324
4550
4659
  HTTP_CODES[428] = "Precondition Required"
4551
4660
  HTTP_CODES[429] = "Too Many Requests"
4552
4661
  HTTP_CODES[431] = "Request Header Fields Too Large"
4553
- HTTP_CODES[451] = "Unavailable For Legal Reasons" # RFC 7725
4662
+ HTTP_CODES[451] = "Unavailable For Legal Reasons" # RFC 7725
4554
4663
  HTTP_CODES[511] = "Network Authentication Required"
4555
4664
  _HTTP_STATUS_LINES = dict((k, '%d %s' % (k, v))
4556
4665
  for (k, v) in HTTP_CODES.items())
@@ -4614,7 +4723,7 @@ apps = app = default_app = AppStack()
4614
4723
 
4615
4724
  #: A virtual package that redirects import statements.
4616
4725
  #: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`.
4617
- ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else
4726
+ ext = _ImportRedirect('bottle.ext' if __name__ == "__main__" else
4618
4727
  __name__ + ".ext", 'bottle_%s').module
4619
4728
 
4620
4729
 
@@ -4676,5 +4785,5 @@ def main():
4676
4785
  _main(sys.argv)
4677
4786
 
4678
4787
 
4679
- if __name__ == '__main__': # pragma: no coverage
4788
+ if __name__ == "__main__": # pragma: no coverage
4680
4789
  main()
@@ -1,6 +1,8 @@
1
1
  """Utils"""
2
2
  import csv
3
3
  import hashlib
4
+ import importlib
5
+ import importlib.util
4
6
  import json
5
7
  import os
6
8
  import re
@@ -17,9 +19,11 @@ from typing import Any, Callable
17
19
  from urllib.parse import ParseResult, urlparse
18
20
  from uuid import uuid4
19
21
 
20
- import markdown
21
22
  from loguru import logger
22
23
 
24
+ if importlib.util.find_spec("markdown"):
25
+ import markdown # type: ignore
26
+
23
27
  # --------------------------------------------------------------------------------------------------
24
28
 
25
29
 
@@ -1478,7 +1482,9 @@ def markdown_to_html(markdown_file: str, html_file: str, title: str) -> bool:
1478
1482
 
1479
1483
  # 将 Markdown 转换为 HTML
1480
1484
  logger.info(f"{info} [将 Markdown 转换为 HTML]")
1481
- html_body = markdown.markdown(markdown_content, extensions=['tables'])
1485
+ # pylint: disable=E0606
1486
+ html_body = markdown.markdown(markdown_content, extensions=['tables']) # type: ignore
1487
+ # pylint: enable=E0606
1482
1488
 
1483
1489
  # 构造完整的 HTML
1484
1490
  logger.info(f"{info} [构造完整的 HTML]")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ezKit
3
- Version: 1.11.14
3
+ Version: 1.11.15
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -3,7 +3,7 @@ from setuptools import find_packages, setup
3
3
 
4
4
  setup(
5
5
  name='ezKit',
6
- version='1.11.14',
6
+ version='1.11.15',
7
7
  author='septvean',
8
8
  author_email='septvean@gmail.com',
9
9
  description='Easy Kit',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes