Event fixes

This commit is contained in:
Ruud
2012-02-11 02:49:35 +01:00
parent e4588a5e7e
commit cfadd851bf
14 changed files with 115 additions and 98 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ import os
log = CPLog(__name__)
app = Flask(__name__)
app = Flask(__name__, static_folder = 'nope')
web = Blueprint('web', __name__)
+4 -5
View File
@@ -70,7 +70,7 @@ class Core(Plugin):
def crappyShutdown(self):
try:
self.urlopen('%sapp.shutdown' % self.createApiUrl(), show_error = False)
self.urlopen('%s/app.shutdown' % self.createApiUrl(), show_error = False)
return True
except:
self.initShutdown()
@@ -78,7 +78,7 @@ class Core(Plugin):
def crappyRestart(self):
try:
self.urlopen('%sapp.restart' % self.createApiUrl(), show_error = False)
self.urlopen('%s/app.restart' % self.createApiUrl(), show_error = False)
return True
except:
self.initShutdown(restart = True)
@@ -163,8 +163,7 @@ class Core(Plugin):
host = 'localhost'
port = Env.setting('port')
return '%s:%d' % (cleanHost(host).rstrip('/'), int(port))
return '%s:%d%s' % (cleanHost(host).rstrip('/'), int(port), '/' + Env.setting('url_base').lstrip('/') if Env.setting('url_base') else '')
def createApiUrl(self):
return '%s/%s/' % (self.createBaseUrl(), Env.setting('api_key'))
return '%s/%s' % (self.createBaseUrl(), Env.setting('api_key'))
+1 -1
View File
@@ -17,7 +17,7 @@ def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = getattr(request, 'authorization')
if Env.setting('username') and (not auth or not check_auth(auth.username, md5(auth.password))):
if Env.setting('username') and Env.setting('password') and (not auth or not check_auth(auth.username, md5(auth.password))):
return authenticate()
return f(*args, **kwargs)
+10 -4
View File
@@ -2,6 +2,7 @@ from axl.axel import Event
from couchpotato.core.helpers.variable import mergeDicts
from couchpotato.core.logger import CPLog
import threading
import time
import traceback
log = CPLog(__name__)
@@ -25,15 +26,17 @@ def addEvent(name, handler, priority = 100):
try:
parent = handler.im_self
parent.beforeCall(handler)
bc = hasattr(parent, 'beforeCall')
if bc: parent.beforeCall(handler)
h = runHandler(name, handler, *args, **kwargs)
parent.afterCall(handler)
ac = hasattr(parent, 'afterCall')
if ac: parent.afterCall(handler)
except:
h = runHandler(name, handler, *args, **kwargs)
return h
e.handle(createHandle, priority = priority)
e.handle(handler, priority = priority)
def removeEvent(name, handler):
e = events[name]
@@ -64,8 +67,10 @@ def fireEvent(name, *args, **kwargs):
except: pass
e = events[name]
e.lock.acquire()
e.asynchronous = False
result = e(*args, **kwargs)
e.lock.release()
if single and not merge:
results = None
@@ -123,10 +128,11 @@ def fireEventAsync(name, *args, **kwargs):
#log.debug('Async "%s": %s, %s' % (name, args, kwargs))
try:
e = events[name]
e.lock.acquire()
e.asynchronous = True
e.error_handler = errorHandler
e(*args, **kwargs)
e.lock.release()
return True
except Exception, e:
log.error('%s: %s' % (name, e))
+17 -7
View File
@@ -1,7 +1,11 @@
from couchpotato.core.event import fireEvent
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
from gntp import notifier
import logging
import thread
import time
import traceback
log = CPLog(__name__)
@@ -15,13 +19,19 @@ class Growl(Notification):
logger.disabled = True
try:
self.growl = notifier.GrowlNotifier(
applicationName = 'CouchPotato',
notifications = ["Updates"],
defaultNotifications = ["Updates"],
applicationIcon = 'http://couchpota.to/media/images/couch.png',
)
self.growl.register()
def startGrowl():
time.sleep(2)
try:
self.growl = notifier.GrowlNotifier(
applicationName = 'CouchPotato',
notifications = ["Updates"],
defaultNotifications = ["Updates"],
applicationIcon = '%s/static/images/couch.png' % fireEvent('app.api_url', single = True),
)
self.growl.register()
except:
log.error('Failed register of growl: %s' % traceback.format_exc())
thread.start_new_thread(startGrowl, ())
except:
pass
+3 -5
View File
@@ -54,7 +54,7 @@ class Plugin(object):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', self.__class__.__name__)
class_name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
path = 'static/' + class_name + '/'
path = '%s/static/%s/' % (Env.setting('api_key'), class_name)
addView(path + '<path:filename>', self.showStatic, static = True)
if add_to_head:
@@ -94,8 +94,6 @@ class Plugin(object):
# http request
def urlopen(self, url, timeout = 10, params = {}, headers = {}, multipart = False, show_error = True):
socket.setdefaulttimeout(timeout)
# Fill in some headers
if not headers.get('Referer'):
headers['Referer'] = urlparse(url).hostname
@@ -114,13 +112,13 @@ class Plugin(object):
cookies = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies), MultipartPostHandler)
data = opener.open(request).read()
data = opener.open(request, timeout = timeout).read()
else:
log.info('Opening url: %s, params: %s' % (url, [x for x in params.iterkeys()]))
data = urllib.urlencode(params) if len(params) > 0 else None
request = urllib2.Request(url, data, headers)
data = urllib2.urlopen(request).read()
data = urllib2.urlopen(request, timeout = timeout).read()
except IOError:
if show_error:
log.error('Failed opening url in %s: %s %s' % (self.getName(), url, traceback.format_exc(1)))
+2 -2
View File
@@ -290,11 +290,11 @@ class Renamer(Plugin):
# Remove files
for src in remove_files:
log.info('Removing "%s"' % src)
log.info('(fake) Removing "%s"' % src)
# Remove matching releases
for release in remove_releases:
log.info('Removing release %s' % release)
log.info('(fake) Removing release %s' % release)
# Search for trailers etc
fireEventAsync('renamer.after', group)
+3 -1
View File
@@ -528,7 +528,9 @@ class Scanner(Plugin):
return True
def isSampleFile(self, filename):
return re.search('(^|[\W_])sample\d*[\W_]', filename.lower())
is_sample = re.search('(^|[\W_])sample\d*[\W_]', filename.lower())
if is_sample: log.debug('Is sample file: %s' % filename)
return is_sample
def filesizeBetween(self, file, min = 0, max = 100000):
try:
+4 -4
View File
@@ -16,18 +16,18 @@ log = CPLog(__name__)
class Searcher(Plugin):
def __init__(self):
addEvent('searcher.all', self.all)
addEvent('searcher.all', self.all_movies)
addEvent('searcher.single', self.single)
addEvent('searcher.correct_movie', self.correctMovie)
addEvent('searcher.download', self.download)
# Schedule cronjob
fireEvent('schedule.cron', 'searcher.all', self.all, day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute'))
fireEvent('schedule.cron', 'searcher.all', self.all_movies, day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute'))
if not Env.setting('development'):
addEvent('app.load', self.all)
addEvent('app.load', self.all_movies)
def all(self):
def all_movies(self):
db = get_session()
+1 -1
View File
@@ -9,7 +9,7 @@ import os.path
import time
class Settings():
class Settings(object):
options = {}
types = {}
+3 -2
View File
@@ -10,8 +10,8 @@ import locale
import logging
import os.path
import sys
import traceback
import time
import traceback
def getOptions(base_path, args):
@@ -180,7 +180,8 @@ def runCouchPotato(options, base_path, args, desktop = None):
}
# Static path
web.add_url_rule('static/<path:filename>',
app.static_folder = os.path.join(base_path, 'couchpotato', 'static')
web.add_url_rule('%s/static/<path:filename>' % api_key,
endpoint = 'static',
view_func = app.send_static_file)
Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

@@ -13,6 +13,7 @@ Page.Wanted = new Class({
// Wanted movies
self.wanted = new MovieList({
'status': 'active',
'navigation': true,
'actions': MovieActions
});
$(self.wanted).inject(self.el);
+65 -65
View File
@@ -6,59 +6,61 @@
# http://www.valuedlessons.com/2008/04/events-in-python.html
#
# This module is part of Axel and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
# the MIT License: http://www.opensource.org/licenses/mit-license.php
#
# Source: http://pypi.python.org/pypi/axel
# Docs: http://packages.python.org/axel
from couchpotato.core.helpers.variable import natcmp
import Queue
import hashlib
import sys
import threading
import time
class Event(object):
"""
"""
Event object inspired by C# events. Handlers can be registered and
unregistered using += and -= operators. Execution and result are
unregistered using += and -= operators. Execution and result are
influenced by the arguments passed to the constructor and += method.
from axel import Event
event = Event()
def on_event(*args, **kwargs):
return (args, kwargs)
event += on_event # handler registration
print(event(10, 20, y=30))
print(event(10, 20, y=30))
>> ((True, ((10, 20), {'y': 30}), <function on_event at 0x00BAA270>),)
event -= on_event # handler is unregistered
print(event(10, 20, y=30))
print(event(10, 20, y=30))
>> None
class Mouse(object):
def __init__(self):
self.click = Event(self)
self.click += self.on_click # handler registration
def on_click(self, sender, *args, **kwargs):
assert isinstance(sender, Mouse), 'Wrong sender'
return (args, kwargs)
mouse = Mouse()
print(mouse.click(10, 20))
>> ((True, ((10, 20), {}),
print(mouse.click(10, 20))
>> ((True, ((10, 20), {}),
>> <bound method Mouse.on_click of <__main__.Mouse object at 0x00B6F470>>),)
mouse.click -= mouse.on_click # handler is unregistered
print(mouse.click(10, 20))
print(mouse.click(10, 20))
>> None
"""
def __init__(self, sender = None, asynch = False, exc_info = False,
lock = None, threads = 3, traceback = False):
""" Creates an event
""" Creates an event
asynch
if True handler's are executes asynchronous
exc_info
@@ -66,31 +68,31 @@ class Event(object):
lock
threading.RLock used to synchronize execution
sender
event's sender. The sender is passed as the first argument to the
event's sender. The sender is passed as the first argument to the
handler, only if is not None. For this case the handler must have
a placeholder in the arguments to receive the sender
threads
maximum number of threads that will be started
traceback
if True, the execution result will contain sys.exc_info()
on error. exc_info must be also True to get the traceback
hash = hash(handler)
Handlers are stored in a dictionary that has as keys the handler's hash
handlers = {
if True, the execution result will contain sys.exc_info()
on error. exc_info must be also True to get the traceback
hash = self.hash(handler)
Handlers are stored in a dictionary that has as keys the handler's hash
handlers = {
hash : (handler, memoize, timeout),
hash : (handler, memoize, timeout), ...
hash : (handler, memoize, timeout), ...
}
The execution result is cached using the following structure
memoize = {
hash : ((args, kwargs, result), (args, kwargs, result), ...),
hash : ((args, kwargs, result), ...), ...
}
The execution result is cached using the following structure
memoize = {
hash : ((args, kwargs, result), (args, kwargs, result), ...),
hash : ((args, kwargs, result), ...), ...
}
The execution result is returned as a tuple having this structure
exec_result = (
(True, result, handler), # on success
(False, error_info, handler), # on error
(False, error_info, handler), # on error
(None, None, handler), ... # asynchronous execution
)
"""
@@ -103,31 +105,34 @@ class Event(object):
self.handlers = {}
self.memoize = {}
def hash(self, handler):
return hashlib.md5(str(handler)).hexdigest()
def handle(self, handler, priority = 0):
""" Registers a handler. The handler can be transmitted together
""" Registers a handler. The handler can be transmitted together
with two arguments as a list or dictionary. The arguments are:
memoize
memoize
if True, the execution result will be cached in self.memoize
timeout
timeout
will allocate a predefined time interval for the execution
If arguments are provided as a list, they are considered to have
this sequence: (handler, memoize, timeout)
If arguments are provided as a list, they are considered to have
this sequence: (handler, memoize, timeout)
Examples:
event += handler
event += handler
event += (handler, True, 1.5)
event += {'handler':handler, 'memoize':True, 'timeout':1.5}
event += {'handler':handler, 'memoize':True, 'timeout':1.5}
"""
handler_, memoize, timeout = self._extract(handler)
self.handlers['%s.%s' % (priority, hash(handler_))] = (handler_, memoize, timeout)
self.handlers['%s.%s' % (priority, self.hash(handler_))] = (handler_, memoize, timeout)
return self
def unhandle(self, handler):
""" Unregisters a handler """
handler_, memoize, timeout = self._extract(handler)
key = hash(handler_)
key = self.hash(handler_)
if not key in self.handlers:
raise ValueError('Handler "%s" was not found' % str(handler_))
del self.handlers[key]
@@ -139,6 +144,7 @@ class Event(object):
self.result = []
if self.handlers:
max_threads = self._threads()
for i in range(max_threads):
@@ -172,10 +178,8 @@ class Event(object):
""" Executes all handlers stored in the queue """
while True:
try:
handler, memoize, timeout = self.handlers[self.queue.get()]
if isinstance(self.lock, threading._RLock):
self.lock.acquire() #synchronization
h_ = self.queue.get()
handler, memoize, timeout = self.handlers[h_]
try:
r = self._memoize(memoize, timeout, handler, *args, **kwargs)
@@ -189,14 +193,9 @@ class Event(object):
else:
self.error_handler(sys.exc_info())
finally:
if isinstance(self.lock, threading._RLock):
self.lock.release()
if not self.asynchronous:
try:
self.queue.task_done()
except ValueError:
pass
self.queue.task_done()
if self.queue.empty():
raise Queue.Empty
@@ -205,9 +204,9 @@ class Event(object):
break
def _extract(self, queue_item):
""" Extracts a handler and handler's arguments that can be provided
as list or dictionary. If arguments are provided as list, they are
considered to have this sequence: (handler, memoize, timeout)
""" Extracts a handler and handler's arguments that can be provided
as list or dictionary. If arguments are provided as list, they are
considered to have this sequence: (handler, memoize, timeout)
Examples:
event += handler
event += (handler, True, 1.5)
@@ -234,11 +233,11 @@ class Event(object):
return (handler, bool(memoize), float(timeout))
def _memoize(self, memoize, timeout, handler, *args, **kwargs):
""" Caches the execution result of successful executions
hash = hash(handler)
memoize = {
hash : ((args, kwargs, result), (args, kwargs, result), ...),
hash : ((args, kwargs, result), ...), ...
""" Caches the execution result of successful executions
hash = self.hash(handler)
memoize = {
hash : ((args, kwargs, result), (args, kwargs, result), ...),
hash : ((args, kwargs, result), ...), ...
}
"""
if not isinstance(handler, Event) and self.sender is not None:
@@ -247,7 +246,8 @@ class Event(object):
if not memoize:
if timeout <= 0: #no time restriction
return [True, handler(*args, **kwargs), handler]
result = [True, handler(*args, **kwargs), handler]
return result
result = self._timeout(timeout, handler, *args, **kwargs)
if isinstance(result, tuple) and len(result) == 3:
@@ -255,7 +255,7 @@ class Event(object):
return [False, self._error(result), handler]
return [True, result, handler]
else:
hash_ = hash(handler)
hash_ = self.hash(handler)
if hash_ in self.memoize:
for args_, kwargs_, result in self.memoize[hash_]:
if args_ == args and kwargs_ == kwargs: