Merge github.com:web2py/web2py
This commit is contained in:
+82
-112
@@ -11,7 +11,7 @@ Basic caching classes and methods
|
||||
|
||||
- Cache - The generic caching object interfacing with the others
|
||||
- CacheInRam - providing caching in ram
|
||||
- CacheInDisk - provides caches on disk
|
||||
- CacheOnDisk - provides caches on disk
|
||||
|
||||
Memcache is also available via a different module (see gluon.contrib.memcache)
|
||||
|
||||
@@ -130,22 +130,27 @@ class CacheInRam(CacheAbstract):
|
||||
meta_storage = {}
|
||||
|
||||
def __init__(self, request=None):
|
||||
self.locker.acquire()
|
||||
self.initialized = False
|
||||
self.request = request
|
||||
|
||||
def initialize(self):
|
||||
if self.initialized: return
|
||||
else: self.initialized = True
|
||||
self.locker.acquire()
|
||||
request = self.request
|
||||
if request:
|
||||
app = request.application
|
||||
else:
|
||||
app = ''
|
||||
if not app in self.meta_storage:
|
||||
self.storage = self.meta_storage[app] = {CacheAbstract.cache_stats_name: {
|
||||
'hit_total': 0,
|
||||
'misses': 0,
|
||||
}}
|
||||
self.storage = self.meta_storage[app] = {
|
||||
CacheAbstract.cache_stats_name: {'hit_total': 0, 'misses': 0}}
|
||||
else:
|
||||
self.storage = self.meta_storage[app]
|
||||
self.locker.release()
|
||||
|
||||
def clear(self, regex=None):
|
||||
self.initialize()
|
||||
self.locker.acquire()
|
||||
storage = self.storage
|
||||
if regex is None:
|
||||
@@ -154,10 +159,7 @@ class CacheInRam(CacheAbstract):
|
||||
self._clear(storage, regex)
|
||||
|
||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
||||
storage[CacheAbstract.cache_stats_name] = {
|
||||
'hit_total': 0,
|
||||
'misses': 0,
|
||||
}
|
||||
storage[CacheAbstract.cache_stats_name] = {'hit_total': 0,'misses': 0}
|
||||
|
||||
self.locker.release()
|
||||
|
||||
@@ -172,6 +174,7 @@ class CacheInRam(CacheAbstract):
|
||||
3) would work unless we deepcopy no storage and retrival which would make things slow.
|
||||
Anyway. You can deepcopy explicitly in the function generating the value to be cached.
|
||||
"""
|
||||
self.initialize()
|
||||
|
||||
dt = time_expire
|
||||
now = time.time()
|
||||
@@ -200,6 +203,7 @@ class CacheInRam(CacheAbstract):
|
||||
return value
|
||||
|
||||
def increment(self, key, value=1):
|
||||
self.initialize()
|
||||
self.locker.acquire()
|
||||
try:
|
||||
if key in self.storage:
|
||||
@@ -226,58 +230,65 @@ class CacheOnDisk(CacheAbstract):
|
||||
Values stored in disk cache must be pickable.
|
||||
"""
|
||||
|
||||
speedup_checks = set()
|
||||
def _close_shelve_and_unlock(self):
|
||||
try:
|
||||
if self.storage:
|
||||
self.storage.close()
|
||||
finally:
|
||||
if self.locker and self.locked:
|
||||
portalocker.unlock(self.locker)
|
||||
self.locker.close()
|
||||
self.locked = False
|
||||
|
||||
def _open_shelf_with_lock(self):
|
||||
def _open_shelve_and_lock(self):
|
||||
"""Open and return a shelf object, obtaining an exclusive lock
|
||||
on self.locker first. Replaces the close method of the
|
||||
returned shelf instance with one that releases the lock upon
|
||||
closing."""
|
||||
def _close(self):
|
||||
try:
|
||||
shelve.Shelf.close(self)
|
||||
finally:
|
||||
portalocker.unlock(self.locker)
|
||||
self.locker.close()
|
||||
|
||||
storage, locker, locker_locked = None, None, False
|
||||
|
||||
storage = None
|
||||
locker = None
|
||||
locked = False
|
||||
try:
|
||||
locker = open(self.locker_name, 'a')
|
||||
locker = locker = open(self.locker_name, 'a')
|
||||
portalocker.lock(locker, portalocker.LOCK_EX)
|
||||
locker_locked = True
|
||||
storage = shelve.open(self.shelve_name)
|
||||
storage.close = _close.__get__(storage, shelve.Shelf)
|
||||
storage.locker = locker
|
||||
except Exception:
|
||||
logger.error('corrupted cache file %s, will try to delete and recreate it!' % (self.shelve_name))
|
||||
locked = True
|
||||
try:
|
||||
storage = shelve.open(self.shelve_name)
|
||||
except:
|
||||
logger.error('corrupted cache file %s, will try rebuild it' \
|
||||
% (self.shelve_name))
|
||||
storage = None
|
||||
if not storage and os.path.exists(self.shelve_name):
|
||||
os.unlink(self.shelve_name)
|
||||
storage = shelve.open(self.shelve_name)
|
||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
||||
storage[CacheAbstract.cache_stats_name] = {'hit_total':0, 'misses': 0}
|
||||
storage.sync()
|
||||
except Exception, e:
|
||||
if storage:
|
||||
storage.close()
|
||||
storage = None
|
||||
|
||||
try:
|
||||
os.unlink(self.shelve_name)
|
||||
storage = shelve.open(self.shelve_name)
|
||||
storage.close = _close.__get__(storage, shelve.Shelf)
|
||||
storage.locker = locker
|
||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
||||
storage[CacheAbstract.cache_stats_name] = {
|
||||
'hit_total': 0,
|
||||
'misses': 0,
|
||||
}
|
||||
storage.sync()
|
||||
except (IOError, OSError):
|
||||
logger.warn('unable to delete and recreate cache file %s' % self.shelve_name)
|
||||
if storage:
|
||||
storage.close()
|
||||
storage = None
|
||||
if locker_locked:
|
||||
portalocker.unlock(locker)
|
||||
if locker:
|
||||
locker.close()
|
||||
if locked:
|
||||
portalocker.unlock(locker)
|
||||
locker.close()
|
||||
locked = False
|
||||
raise RuntimeError, 'unable to create/re-create cache file %s' % self.shelve_name
|
||||
self.locker = locker
|
||||
self.locked = locked
|
||||
self.storage = storage
|
||||
return storage
|
||||
|
||||
def __init__(self, request, folder=None):
|
||||
def __init__(self, request=None, folder=None):
|
||||
self.initialized = False
|
||||
self.request = request
|
||||
self.folder = folder
|
||||
|
||||
def initialize(self):
|
||||
if self.initialized: return
|
||||
else: self.initialized = True
|
||||
folder = self.folder
|
||||
request = self.request
|
||||
|
||||
# Lets test if the cache folder exists, if not
|
||||
# we are going to create it
|
||||
@@ -291,93 +302,54 @@ class CacheOnDisk(CacheAbstract):
|
||||
self.locker_name = os.path.join(folder,'cache.lock')
|
||||
self.shelve_name = os.path.join(folder,'cache.shelve')
|
||||
|
||||
speedup_key = (folder,CacheAbstract.cache_stats_name)
|
||||
if not speedup_key in self.speedup_checks or \
|
||||
not os.path.exists(self.shelve_name):
|
||||
try:
|
||||
storage = self._open_shelf_with_lock()
|
||||
try:
|
||||
if not CacheAbstract.cache_stats_name in storage:
|
||||
storage[CacheAbstract.cache_stats_name] = {
|
||||
'hit_total': 0,
|
||||
'misses': 0,
|
||||
}
|
||||
storage.sync()
|
||||
finally:
|
||||
storage.close()
|
||||
self.speedup_checks.add(speedup_key)
|
||||
except ImportError:
|
||||
pass # no module _bsddb, ignoring exception now so it makes a ticket only if used
|
||||
|
||||
def clear(self, regex=None):
|
||||
storage = self._open_shelf_with_lock()
|
||||
self.initialize()
|
||||
storage = self._open_shelve_and_lock()
|
||||
try:
|
||||
if regex is None:
|
||||
storage.clear()
|
||||
else:
|
||||
self._clear(storage, regex)
|
||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
||||
storage[CacheAbstract.cache_stats_name] = {
|
||||
'hit_total': 0,
|
||||
'misses': 0,
|
||||
}
|
||||
storage.sync()
|
||||
finally:
|
||||
storage.close()
|
||||
self._close_shelve_and_unlock()
|
||||
|
||||
def __call__(self, key, f,
|
||||
time_expire = DEFAULT_TIME_EXPIRE):
|
||||
self.initialize()
|
||||
dt = time_expire
|
||||
|
||||
storage = self._open_shelf_with_lock()
|
||||
storage = self._open_shelve_and_lock()
|
||||
try:
|
||||
item = storage.get(key, None)
|
||||
storage[CacheAbstract.cache_stats_name]['hit_total'] += 1
|
||||
if item and f is None:
|
||||
del storage[key]
|
||||
|
||||
storage[CacheAbstract.cache_stats_name] = {
|
||||
'hit_total': storage[CacheAbstract.cache_stats_name]['hit_total'] + 1,
|
||||
'misses': storage[CacheAbstract.cache_stats_name]['misses']
|
||||
}
|
||||
|
||||
storage.sync()
|
||||
storage.sync()
|
||||
now = time.time()
|
||||
if f is None:
|
||||
value = None
|
||||
elif item and (dt is None or item[0] > now - dt):
|
||||
value = item[1]
|
||||
else:
|
||||
value = f()
|
||||
storage[key] = (now, value)
|
||||
storage[CacheAbstract.cache_stats_name]['misses']+=1
|
||||
storage.sync()
|
||||
finally:
|
||||
if storage:
|
||||
storage.close()
|
||||
|
||||
now = time.time()
|
||||
if f is None:
|
||||
return None
|
||||
if item and (dt is None or item[0] > now - dt):
|
||||
return item[1]
|
||||
value = f()
|
||||
|
||||
storage = self._open_shelf_with_lock()
|
||||
try:
|
||||
storage[key] = (now, value)
|
||||
|
||||
storage[CacheAbstract.cache_stats_name] = {
|
||||
'hit_total': storage[CacheAbstract.cache_stats_name]['hit_total'],
|
||||
'misses': storage[CacheAbstract.cache_stats_name]['misses'] + 1
|
||||
}
|
||||
|
||||
storage.sync()
|
||||
finally:
|
||||
if storage:
|
||||
storage.close()
|
||||
self._close_shelve_and_unlock()
|
||||
|
||||
return value
|
||||
|
||||
def increment(self, key, value=1):
|
||||
storage = self._open_shelf_with_lock()
|
||||
self.initialize()
|
||||
storage = self._open_shelve_and_lock()
|
||||
try:
|
||||
if key in storage:
|
||||
value = storage[key][1] + value
|
||||
storage[key] = (time.time(), value)
|
||||
storage.sync()
|
||||
finally:
|
||||
if storage:
|
||||
storage.close()
|
||||
self._close_shelve_and_unlock()
|
||||
return value
|
||||
|
||||
class CacheAction(object):
|
||||
@@ -423,10 +395,9 @@ class Cache(object):
|
||||
the global request object
|
||||
"""
|
||||
# GAE will have a special caching
|
||||
|
||||
if have_settings and settings.global_settings.web2py_runtime_gae:
|
||||
from contrib.gae_memcache import MemcacheClient
|
||||
self.ram=self.disk=MemcacheClient(request)
|
||||
self.ram = self.disk = MemcacheClient(request)
|
||||
else:
|
||||
# Otherwise use ram (and try also disk)
|
||||
self.ram = CacheInRam(request)
|
||||
@@ -489,7 +460,6 @@ class Cache(object):
|
||||
cache_model(prefix + key, f, time_expire)
|
||||
|
||||
|
||||
|
||||
def lazy_cache(key=None,time_expire=None,cache_model='ram'):
|
||||
"""
|
||||
can be used to cache any function including in modules,
|
||||
|
||||
@@ -387,6 +387,7 @@ def build_environment(request, response, session, store_current=True):
|
||||
|
||||
t = environment['T'] = translator(request)
|
||||
c = environment['cache'] = Cache(request)
|
||||
|
||||
if store_current:
|
||||
current.globalenv = environment
|
||||
current.request = request
|
||||
|
||||
@@ -44,7 +44,7 @@ class MemcacheClient(Client):
|
||||
obj = self.get(key)
|
||||
if obj:
|
||||
value = obj[1] + value
|
||||
self.set((time.time(), value))
|
||||
self.set(key, (time.time(), value))
|
||||
return value
|
||||
|
||||
def clear(self, key = None):
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
"""
|
||||
Developed by niphlod@gmail.com
|
||||
"""
|
||||
|
||||
import redis
|
||||
from redis.exceptions import ConnectionError
|
||||
from gluon import current
|
||||
from gluon.storage import Storage
|
||||
import cPickle as pickle
|
||||
import time
|
||||
import re
|
||||
import logging
|
||||
import thread
|
||||
|
||||
logger = logging.getLogger("web2py.session.redis")
|
||||
|
||||
locker = thread.allocate_lock()
|
||||
|
||||
def RedisSession(*args, **vars):
|
||||
"""
|
||||
Usage example: put in models
|
||||
|
||||
sessiondb = RedisSession('localhost:6379',db=0, debug=True)
|
||||
session.connect(request, response, db = sessiondb)
|
||||
|
||||
Simple slip-in storage for session
|
||||
"""
|
||||
|
||||
locker.acquire()
|
||||
try:
|
||||
if not hasattr(RedisSession, 'redis_instance'):
|
||||
RedisSession.redis_instance = RedisClient(*args, **vars)
|
||||
finally:
|
||||
locker.release()
|
||||
return RedisSession.redis_instance
|
||||
|
||||
|
||||
class RedisClient(object):
|
||||
|
||||
meta_storage = {}
|
||||
MAX_RETRIES = 5
|
||||
RETRIES = 0
|
||||
|
||||
def __init__(self, server='localhost:6379', db=None, debug=False, session_expiry=False):
|
||||
"""session_expiry can be an integer, in seconds, to set the default expiration
|
||||
of sessions. The corresponding record will be deleted from the redis instance,
|
||||
and there's virtually no need to run sessions2trash.py
|
||||
"""
|
||||
self.server = server
|
||||
self.db = db or 0
|
||||
host,port = (self.server.split(':')+['6379'])[:2]
|
||||
port = int(port)
|
||||
self.debug = debug
|
||||
if current and current.request:
|
||||
self.app = current.request.application
|
||||
else:
|
||||
self.app = ''
|
||||
self.r_server = redis.Redis(host=host, port=port, db=self.db)
|
||||
self.tablename = None
|
||||
self.session_expiry = session_expiry
|
||||
|
||||
def get(self, what, default):
|
||||
return self.tablename
|
||||
|
||||
def Field(self, fieldname, type='string', length=None, default=None,
|
||||
required=False,requires=None):
|
||||
return None
|
||||
|
||||
def define_table(self,tablename,*fields,**args):
|
||||
if not self.tablename:
|
||||
self.tablename = MockTable(self, self.r_server, tablename, self.session_expiry)
|
||||
return self.tablename
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.tablename
|
||||
|
||||
def __call__(self, where=''):
|
||||
q = self.tablename.query
|
||||
return q
|
||||
|
||||
def commit(self):
|
||||
#this is only called by session2trash.py
|
||||
pass
|
||||
|
||||
class MockTable(object):
|
||||
|
||||
def __init__(self, db, r_server, tablename, session_expiry):
|
||||
self.db = db
|
||||
self.r_server = r_server
|
||||
self.tablename = tablename
|
||||
#set the namespace for sessions of this app
|
||||
self.keyprefix = 'w2p:sess:%s' % tablename.replace('web2py_session_', '')
|
||||
#fast auto-increment id (needed for session handling)
|
||||
self.serial = "%s:serial" % self.keyprefix
|
||||
#index of all the session keys of this app
|
||||
self.id_idx = "%s:id_idx" % self.keyprefix
|
||||
#remember the session_expiry setting
|
||||
self.session_expiry = session_expiry
|
||||
|
||||
def getserial(self):
|
||||
#return an auto-increment id
|
||||
return "%s" % self.r_server.incr(self.serial, 1)
|
||||
|
||||
def __getattr__(self, key):
|
||||
if key == 'id':
|
||||
#return a fake query. We need to query it just by id for normal operations
|
||||
self.query = MockQuery(field='id', db=self.r_server, prefix=self.keyprefix, session_expiry=self.session_expiry)
|
||||
return self.query
|
||||
elif key == '_db':
|
||||
#needed because of the calls in sessions2trash.py and globals.py
|
||||
return self.db
|
||||
|
||||
def insert(self, **kwargs):
|
||||
#usually kwargs would be a Storage with several keys:
|
||||
#'locked', 'client_ip','created_datetime','modified_datetime'
|
||||
#'unique_key', 'session_data'
|
||||
#retrieve a new key
|
||||
newid = self.getserial()
|
||||
key = "%s:%s" % (self.keyprefix, newid)
|
||||
#add it to the index
|
||||
self.r_server.sadd(self.id_idx, key)
|
||||
#set a hash key with the Storage
|
||||
self.r_server.hmset(key, kwargs)
|
||||
if self.session_expiry:
|
||||
self.r_server.expire(key, self.session_expiry)
|
||||
return newid
|
||||
|
||||
class MockQuery(object):
|
||||
"""a fake Query object that supports querying by id
|
||||
and listing all keys. No other operation is supported
|
||||
"""
|
||||
def __init__(self, field=None, db=None, prefix=None, session_expiry=False):
|
||||
self.field = field
|
||||
self.value = None
|
||||
self.db = db
|
||||
self.keyprefix = prefix
|
||||
self.op = None
|
||||
self.session_expiry = session_expiry
|
||||
|
||||
def __eq__(self, value, op='eq'):
|
||||
self.value = value
|
||||
self.op = op
|
||||
|
||||
def __gt__(self, value, op='ge'):
|
||||
self.value = value
|
||||
self.op = op
|
||||
|
||||
def select(self):
|
||||
if self.op == 'eq' and self.field == 'id' and self.value:
|
||||
#means that someone wants to retrieve the key self.value
|
||||
rtn = self.db.hgetall("%s:%s" % (self.keyprefix, self.value))
|
||||
if rtn == dict():
|
||||
#return an empty resultset for non existing key
|
||||
return []
|
||||
else:
|
||||
return [Storage(rtn)]
|
||||
elif self.op == 'ge' and self.field == 'id' and self.value == 0:
|
||||
#means that someone wants the complete list
|
||||
rtn = []
|
||||
id_idx = "%s:id_idx" % self.keyprefix
|
||||
#find all session keys of this app
|
||||
allkeys = self.db.smembers(id_idx)
|
||||
for sess in allkeys:
|
||||
val = self.db.hgetall(sess)
|
||||
if val == dict():
|
||||
if self.session_expiry:
|
||||
#clean up the idx, because the key expired
|
||||
self.db.srem(id_idx, sess)
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
val = Storage(val)
|
||||
#add a delete_record method (necessary for sessions2trash.py)
|
||||
val.delete_record = RecordDeleter(self.db, sess, self.keyprefix)
|
||||
rtn.append(val)
|
||||
return rtn
|
||||
else:
|
||||
raise Exception("Operation not supported")
|
||||
|
||||
def update(self, **kwargs):
|
||||
#means that the session has been found and needs an update
|
||||
if self.op == 'eq' and self.field == 'id' and self.value:
|
||||
return self.db.hmset("%s:%s" % (self.keyprefix, self.value), kwargs)
|
||||
|
||||
class RecordDeleter(object):
|
||||
"""Dumb record deleter to support sessions2trash.py"""
|
||||
|
||||
def __init__(self, db, key, keyprefix):
|
||||
self.db, self.key, self.keyprefix = db, key, keyprefix
|
||||
|
||||
def __call__(self):
|
||||
id_idx = "%s:id_idx" % self.keyprefix
|
||||
#remove from the index
|
||||
self.db.srem(id_idx, self.key)
|
||||
#remove the key itself
|
||||
self.db.delete(self.key)
|
||||
|
||||
|
||||
+3
-3
@@ -3981,8 +3981,8 @@ class DatabaseStoredFile:
|
||||
self.data += data
|
||||
|
||||
def close_connection(self):
|
||||
self.db.executesql("DELETE FROM web2py_filesystem WHERE path='%s'" \
|
||||
% self.filename)
|
||||
self.db.executesql(
|
||||
"DELETE FROM web2py_filesystem WHERE path='%s'" % self.filename)
|
||||
query = "INSERT INTO web2py_filesystem(path,content) VALUES ('%s','%s')"\
|
||||
% (self.filename, self.data.replace("'","''"))
|
||||
self.db.executesql(query)
|
||||
@@ -4007,7 +4007,7 @@ class UseDatabaseStoredFile:
|
||||
return DatabaseStoredFile(self.db,filename,mode)
|
||||
|
||||
def file_close(self, fileobj):
|
||||
fileobj.close()
|
||||
fileobj.close_connection()
|
||||
|
||||
def file_delete(self,filename):
|
||||
query = "DELETE FROM web2py_filesystem WHERE path='%s'" % filename
|
||||
|
||||
+10
-8
@@ -417,7 +417,7 @@ class Response(Storage):
|
||||
return handler(request, self, methods)
|
||||
|
||||
def toolbar(self):
|
||||
from html import DIV, SCRIPT, BEAUTIFY, TAG, URL
|
||||
from html import DIV, SCRIPT, BEAUTIFY, TAG, URL, A
|
||||
BUTTON = TAG.button
|
||||
admin = URL("admin","default","design",
|
||||
args=current.request.application)
|
||||
@@ -438,19 +438,21 @@ class Response(Storage):
|
||||
dbstats = [] # if no db or on GAE
|
||||
dbtables = {}
|
||||
u = web2py_uuid()
|
||||
backtotop = A('Back to top', _href="#totop-%s" % u)
|
||||
return DIV(
|
||||
BUTTON('design',_onclick="document.location='%s'" % admin),
|
||||
BUTTON('request',_onclick="jQuery('#request-%s').slideToggle()"%u),
|
||||
DIV(BEAUTIFY(current.request),_class="hidden",_id="request-%s"%u),
|
||||
BUTTON('session',_onclick="jQuery('#session-%s').slideToggle()"%u),
|
||||
DIV(BEAUTIFY(current.session),_class="hidden",_id="session-%s"%u),
|
||||
BUTTON('response',_onclick="jQuery('#response-%s').slideToggle()"%u),
|
||||
DIV(BEAUTIFY(current.response),_class="hidden",_id="response-%s"%u),
|
||||
BUTTON('session',_onclick="jQuery('#session-%s').slideToggle()"%u),
|
||||
BUTTON('db tables',_onclick="jQuery('#db-tables-%s').slideToggle()"%u),
|
||||
DIV(BEAUTIFY(dbtables),_class="hidden",_id="db-tables-%s"%u),
|
||||
BUTTON('db stats',_onclick="jQuery('#db-stats-%s').slideToggle()"%u),
|
||||
DIV(BEAUTIFY(dbstats),_class="hidden",_id="db-stats-%s"%u),
|
||||
DIV(BEAUTIFY(current.request), backtotop,_class="hidden",_id="request-%s"%u),
|
||||
DIV(BEAUTIFY(current.session), backtotop, _class="hidden",_id="session-%s"%u),
|
||||
DIV(BEAUTIFY(current.response), backtotop, _class="hidden",_id="response-%s"%u),
|
||||
DIV(BEAUTIFY(dbtables), backtotop, _class="hidden",_id="db-tables-%s"%u),
|
||||
DIV(BEAUTIFY(dbstats), backtotop, _class="hidden",_id="db-stats-%s"%u),
|
||||
SCRIPT("jQuery('.hidden').hide()")
|
||||
,_id="totop-%s" % u
|
||||
)
|
||||
|
||||
class Session(Storage):
|
||||
@@ -482,7 +484,7 @@ class Session(Storage):
|
||||
if not masterapp:
|
||||
masterapp = request.application
|
||||
response.session_id_name = 'session_id_%s' % masterapp.lower()
|
||||
|
||||
|
||||
# Load session data from cookie
|
||||
cookies = request.cookies
|
||||
|
||||
|
||||
+26
-22
@@ -88,7 +88,7 @@ from contenttype import contenttype
|
||||
from dal import BaseAdapter
|
||||
from settings import global_settings
|
||||
from validators import CRYPT
|
||||
from cache import Cache
|
||||
from cache import CacheInRam
|
||||
from html import URL, xmlescape
|
||||
from utils import is_valid_ip_address
|
||||
from rewrite import load, url_in, thread as rwthread, \
|
||||
@@ -148,7 +148,7 @@ def get_client(env):
|
||||
def copystream_progress(request, chunk_size= 10**5):
|
||||
"""
|
||||
copies request.env.wsgi_input into request.body
|
||||
and stores progress upload status in cache.ram
|
||||
and stores progress upload status in cache_ram
|
||||
X-Progress-ID:length and X-Progress-ID:uploaded
|
||||
"""
|
||||
env = request.env
|
||||
@@ -164,16 +164,16 @@ def copystream_progress(request, chunk_size= 10**5):
|
||||
copystream(source, dest, size, chunk_size)
|
||||
return dest
|
||||
cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID']
|
||||
cache = Cache(request)
|
||||
cache.ram(cache_key+':length', lambda: size, 0)
|
||||
cache.ram(cache_key+':uploaded', lambda: 0, 0)
|
||||
cache_ram = CacheInRam(request) # same as cache.ram because meta_storage
|
||||
cache_ram(cache_key+':length', lambda: size, 0)
|
||||
cache_ram(cache_key+':uploaded', lambda: 0, 0)
|
||||
while size > 0:
|
||||
if size < chunk_size:
|
||||
data = source.read(size)
|
||||
cache.ram.increment(cache_key+':uploaded', size)
|
||||
cache_ram.increment(cache_key+':uploaded', size)
|
||||
else:
|
||||
data = source.read(chunk_size)
|
||||
cache.ram.increment(cache_key+':uploaded', chunk_size)
|
||||
cache_ram.increment(cache_key+':uploaded', chunk_size)
|
||||
length = len(data)
|
||||
if length > size:
|
||||
(data, length) = (data[:size], size)
|
||||
@@ -184,8 +184,8 @@ def copystream_progress(request, chunk_size= 10**5):
|
||||
if length < chunk_size:
|
||||
break
|
||||
dest.seek(0)
|
||||
cache.ram(cache_key+':length', None)
|
||||
cache.ram(cache_key+':uploaded', None)
|
||||
cache_ram(cache_key+':length', None)
|
||||
cache_ram(cache_key+':uploaded', None)
|
||||
return dest
|
||||
|
||||
|
||||
@@ -425,19 +425,23 @@ def wsgibase(environ, responder):
|
||||
# ##################################################
|
||||
app = request.application ## must go after url_in!
|
||||
|
||||
http_host = env.http_host.split(':',1)[0]
|
||||
local_hosts = [http_host,'::1','127.0.0.1',
|
||||
'::ffff:127.0.0.1']
|
||||
if not global_settings.web2py_runtime_gae:
|
||||
try:
|
||||
local_hosts.append(socket.gethostname())
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
local_hosts.append(
|
||||
socket.gethostbyname(http_host))
|
||||
except (socket.gaierror,TypeError):
|
||||
pass
|
||||
if not global_settings.local_hosts:
|
||||
local_hosts = ['127.0.0.1','::ffff:127.0.0.1']
|
||||
if not global_settings.web2py_runtime_gae:
|
||||
try:
|
||||
local_hosts.append(socket.gethostname())
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
if env.server_name:
|
||||
local_hosts += [
|
||||
env.server_name,
|
||||
socket.gethostbyname(env.server_name)]
|
||||
except (socket.gaierror,TypeError):
|
||||
pass
|
||||
global_settings.local_hosts = local_hosts
|
||||
else:
|
||||
local_hosts = global_settings.local_hosts
|
||||
client = get_client(env)
|
||||
x_req_with = str(env.http_x_requested_with).lower()
|
||||
|
||||
|
||||
+1
-1
@@ -1466,7 +1466,7 @@ class Worker(Thread):
|
||||
except UnicodeDecodeError:
|
||||
self.err_log.warning('Invalid request header: '+repr(l))
|
||||
|
||||
if l.strip() == '':
|
||||
if l.strip().replace('\0','') == '':
|
||||
break
|
||||
elif l[0] in ' \t' and lname:
|
||||
# Some headers take more than one line
|
||||
|
||||
+1
-1
@@ -6,6 +6,7 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import platform
|
||||
from storage import Storage
|
||||
|
||||
@@ -37,4 +38,3 @@ global_settings.is_jython = \
|
||||
str(sys.copyright).find('Jython') > 0
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1059,7 +1059,7 @@ class SQLFORM(FORM):
|
||||
|
||||
xfields.append((row_id,label,inp,comment))
|
||||
self.custom.dspval[fieldname] = dspval or nbsp
|
||||
self.custom.inpval[fieldname] = inpval or ''
|
||||
self.custom.inpval[fieldname] = inpval if not inpval is None else ''
|
||||
self.custom.widget[fieldname] = inp
|
||||
|
||||
# if a record is provided and found, as is linkto
|
||||
|
||||
+29
-1
@@ -4576,6 +4576,7 @@ class Wiki(object):
|
||||
self.env['component'] = Wiki.component
|
||||
if render == 'markmin': render=self.markmin_render
|
||||
elif render == 'html': render=self.html_render
|
||||
self.render = render
|
||||
self.auth = auth
|
||||
if self.auth.user:
|
||||
self.force_prefix = force_prefix % self.auth.user
|
||||
@@ -4702,6 +4703,8 @@ class Wiki(object):
|
||||
)
|
||||
elif zero=='_cloud':
|
||||
return self.cloud()
|
||||
elif zero == '_preview':
|
||||
return self.preview(self.render)
|
||||
|
||||
def first_paragraph(self,page):
|
||||
if not self.can_read(page):
|
||||
@@ -4782,7 +4785,29 @@ class Wiki(object):
|
||||
elif form.accepted:
|
||||
current.session.flash = 'page created'
|
||||
redirect(URL(args=slug))
|
||||
return dict(content=form)
|
||||
script = """
|
||||
$(function() {
|
||||
if (!$('#wiki_page_body').length) return;
|
||||
var pagecontent = $('#wiki_page_body');
|
||||
var prevbutton = $('<button class="btn nopreview">Preview</button>');
|
||||
var preview = $('<div id="preview"></div>').hide();
|
||||
var table = $('form');
|
||||
prevbutton.insertBefore(table);
|
||||
prevbutton.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (prevbutton.hasClass('nopreview')) {
|
||||
prevbutton.addClass('preview').removeClass('nopreview').html('Edit Source');
|
||||
preview.insertBefore(table);
|
||||
web2py_ajax_page('post', '%(url)s', {body : $('#wiki_page_body').val()}, 'preview');
|
||||
table.fadeOut('medium', function() {preview.fadeIn()});
|
||||
} else {
|
||||
prevbutton.addClass('nopreview').removeClass('preview').html('Preview');
|
||||
preview.fadeOut('medium', function() {table.fadeIn()});
|
||||
}
|
||||
})
|
||||
})
|
||||
""" % dict(url=URL(args=('_preview')))
|
||||
return dict(content=TAG[''](form, SCRIPT(script)))
|
||||
|
||||
def editmedia(self,slug):
|
||||
auth = self.auth
|
||||
@@ -4977,6 +5002,9 @@ class Wiki(object):
|
||||
vars=dict(q=item.wiki_tag.name))))
|
||||
items.append(' ')
|
||||
return dict(content = DIV(_class='w2p_cloud',*items))
|
||||
def preview(self, render):
|
||||
request = current.request
|
||||
return render(request.post_vars)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
|
||||
Reference in New Issue
Block a user