2013-10-07 00:15:47 -07:00
|
|
|
""" Helper methods for various modules. """
|
2014-01-26 18:44:36 -08:00
|
|
|
import threading
|
|
|
|
import Queue
|
2014-01-19 19:10:40 -08:00
|
|
|
import datetime
|
2013-10-07 00:15:47 -07:00
|
|
|
import re
|
|
|
|
|
2013-12-07 12:54:19 -08:00
|
|
|
RE_SANITIZE_FILENAME = re.compile(r"(~|(\.\.)|/|\+)")
|
|
|
|
RE_SLUGIFY = re.compile(r'[^A-Za-z0-9_]+')
|
|
|
|
|
2014-03-11 22:35:51 -07:00
|
|
|
DATE_STR_FORMAT = u"%H:%M:%S %d-%m-%Y"
|
2014-01-19 19:10:40 -08:00
|
|
|
|
2013-11-10 16:46:48 -08:00
|
|
|
|
2013-10-07 00:15:47 -07:00
|
|
|
def sanitize_filename(filename):
|
|
|
|
""" Sanitizes a filename by removing .. / and \\. """
|
2013-12-07 12:54:19 -08:00
|
|
|
return RE_SANITIZE_FILENAME.sub("", filename)
|
|
|
|
|
|
|
|
|
|
|
|
def slugify(text):
|
|
|
|
""" Slugifies a given text. """
|
|
|
|
text = text.strip().replace(" ", "_")
|
|
|
|
|
|
|
|
return RE_SLUGIFY.sub("", text)
|
2014-01-19 19:10:40 -08:00
|
|
|
|
|
|
|
|
|
|
|
def datetime_to_str(dattim):
|
|
|
|
""" Converts datetime to a string format.
|
|
|
|
|
|
|
|
@rtype : str
|
|
|
|
"""
|
|
|
|
return dattim.strftime(DATE_STR_FORMAT)
|
|
|
|
|
|
|
|
|
|
|
|
def str_to_datetime(dt_str):
|
|
|
|
""" Converts a string to a datetime object.
|
|
|
|
|
|
|
|
@rtype: datetime
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
return datetime.datetime.strptime(dt_str, DATE_STR_FORMAT)
|
|
|
|
except ValueError: # If dt_str did not match our format
|
|
|
|
return None
|
2014-01-23 22:03:13 -08:00
|
|
|
|
|
|
|
|
|
|
|
def split_entity_id(entity_id):
|
|
|
|
""" Splits a state entity_id into domain, object_id. """
|
|
|
|
return entity_id.split(".", 1)
|
|
|
|
|
|
|
|
|
|
|
|
def filter_entity_ids(entity_ids, domain_filter=None, strip_domain=False):
|
|
|
|
""" Filter a list of entities based on domain. Setting strip_domain
|
|
|
|
will only return the object_ids. """
|
|
|
|
return [
|
|
|
|
split_entity_id(entity_id)[1] if strip_domain else entity_id
|
|
|
|
for entity_id in entity_ids if
|
|
|
|
not domain_filter or entity_id.startswith(domain_filter)
|
|
|
|
]
|
2014-01-26 18:44:36 -08:00
|
|
|
|
|
|
|
|
|
|
|
def repr_helper(inp):
|
|
|
|
""" Helps creating a more readable string representation of objects. """
|
|
|
|
if isinstance(inp, dict):
|
2014-03-11 22:35:51 -07:00
|
|
|
return u", ".join(
|
|
|
|
repr_helper(key)+u"="+repr_helper(item) for key, item in inp.items()
|
2014-01-26 18:44:36 -08:00
|
|
|
)
|
|
|
|
elif isinstance(inp, list):
|
2014-03-11 22:35:51 -07:00
|
|
|
return u'[' + u', '.join(inp) + u']'
|
2014-01-26 18:44:36 -08:00
|
|
|
elif isinstance(inp, datetime.datetime):
|
|
|
|
return datetime_to_str(inp)
|
|
|
|
else:
|
2014-03-11 22:35:51 -07:00
|
|
|
return unicode(inp)
|
2014-01-26 18:44:36 -08:00
|
|
|
|
|
|
|
|
|
|
|
# Reason why I decided to roll my own ThreadPool instead of using
|
|
|
|
# multiprocessing.dummy.pool or even better, use multiprocessing.pool and
|
|
|
|
# not be hurt by the GIL in the cpython interpreter:
|
|
|
|
# 1. The built in threadpool does not allow me to create custom workers and so
|
|
|
|
# I would have to wrap every listener that I passed into it with code to log
|
|
|
|
# the exceptions. Saving a reference to the logger in the worker seemed
|
|
|
|
# like a more sane thing to do.
|
|
|
|
# 2. Most event listeners are simple checks if attributes match. If the method
|
|
|
|
# that they will call takes a long time to complete it might be better to
|
|
|
|
# put that request in a seperate thread. This is for every component to
|
|
|
|
# decide on its own instead of enforcing it for everyone.
|
|
|
|
class ThreadPool(object):
|
|
|
|
""" A simple queue-based thread pool.
|
|
|
|
|
|
|
|
Will initiate it's workers using worker(queue).start() """
|
|
|
|
|
|
|
|
# pylint: disable=too-few-public-methods
|
|
|
|
def __init__(self, worker_count, job_handler):
|
2014-02-02 21:42:57 -08:00
|
|
|
queue = self.queue = Queue.PriorityQueue()
|
2014-01-29 22:48:35 -08:00
|
|
|
current_jobs = self.current_jobs = []
|
2014-01-26 18:44:36 -08:00
|
|
|
|
|
|
|
for _ in xrange(worker_count):
|
|
|
|
worker = threading.Thread(target=_threadpool_worker,
|
2014-01-29 22:48:35 -08:00
|
|
|
args=(queue, current_jobs, job_handler))
|
2014-01-26 18:44:36 -08:00
|
|
|
worker.daemon = True
|
|
|
|
worker.start()
|
|
|
|
|
2014-02-02 21:42:57 -08:00
|
|
|
def add_job(self, priority, job):
|
2014-01-26 18:44:36 -08:00
|
|
|
""" Add a job to be sent to the workers. """
|
2014-02-02 21:42:57 -08:00
|
|
|
self.queue.put((priority, job))
|
2014-01-26 18:44:36 -08:00
|
|
|
|
|
|
|
|
2014-01-29 22:48:35 -08:00
|
|
|
def _threadpool_worker(queue, current_jobs, job_handler):
|
2014-01-26 18:44:36 -08:00
|
|
|
""" Provides the base functionality of a worker for the thread pool. """
|
|
|
|
while True:
|
2014-01-29 22:48:35 -08:00
|
|
|
# Get new item from queue
|
2014-02-02 21:42:57 -08:00
|
|
|
job = queue.get()[1]
|
2014-01-29 22:48:35 -08:00
|
|
|
|
|
|
|
# Add to current running jobs
|
|
|
|
job_log = (datetime.datetime.now(), job)
|
|
|
|
current_jobs.append(job_log)
|
|
|
|
|
|
|
|
# Do the job
|
2014-01-26 18:44:36 -08:00
|
|
|
job_handler(job)
|
2014-01-29 22:48:35 -08:00
|
|
|
|
|
|
|
# Remove from current running job
|
|
|
|
current_jobs.remove(job_log)
|
|
|
|
|
|
|
|
# Tell queue a task is done
|
2014-01-26 18:44:36 -08:00
|
|
|
queue.task_done()
|