188 lines
4.5 KiB
Python
188 lines
4.5 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
"""
|
||
|
parsedatetime/context.py
|
||
|
|
||
|
Context related classes
|
||
|
|
||
|
"""
|
||
|
|
||
|
from threading import local
|
||
|
|
||
|
|
||
|
class pdtContextStack(object):
|
||
|
"""
|
||
|
A thread-safe stack to store context(s)
|
||
|
|
||
|
Internally used by L{Calendar} object
|
||
|
"""
|
||
|
|
||
|
def __init__(self):
|
||
|
self.__local = local()
|
||
|
|
||
|
@property
|
||
|
def __stack(self):
|
||
|
if not hasattr(self.__local, 'stack'):
|
||
|
self.__local.stack = []
|
||
|
return self.__local.stack
|
||
|
|
||
|
def push(self, ctx):
|
||
|
self.__stack.append(ctx)
|
||
|
|
||
|
def pop(self):
|
||
|
try:
|
||
|
return self.__stack.pop()
|
||
|
except IndexError:
|
||
|
return None
|
||
|
|
||
|
def last(self):
|
||
|
try:
|
||
|
return self.__stack[-1]
|
||
|
except IndexError:
|
||
|
raise RuntimeError('context stack is empty')
|
||
|
|
||
|
def isEmpty(self):
|
||
|
return not self.__stack
|
||
|
|
||
|
|
||
|
class pdtContext(object):
|
||
|
"""
|
||
|
Context contains accuracy flag detected by L{Calendar.parse()}
|
||
|
|
||
|
Accuracy flag uses bitwise-OR operation and is combined by:
|
||
|
|
||
|
ACU_YEAR - "next year", "2014"
|
||
|
ACU_MONTH - "March", "July 2014"
|
||
|
ACU_WEEK - "last week", "next 3 weeks"
|
||
|
ACU_DAY - "tomorrow", "July 4th 2014"
|
||
|
ACU_HALFDAY - "morning", "tonight"
|
||
|
ACU_HOUR - "18:00", "next hour"
|
||
|
ACU_MIN - "18:32", "next 10 minutes"
|
||
|
ACU_SEC - "18:32:55"
|
||
|
ACU_NOW - "now"
|
||
|
|
||
|
"""
|
||
|
|
||
|
__slots__ = ('accuracy',)
|
||
|
|
||
|
ACU_YEAR = 2 ** 0
|
||
|
ACU_MONTH = 2 ** 1
|
||
|
ACU_WEEK = 2 ** 2
|
||
|
ACU_DAY = 2 ** 3
|
||
|
ACU_HALFDAY = 2 ** 4
|
||
|
ACU_HOUR = 2 ** 5
|
||
|
ACU_MIN = 2 ** 6
|
||
|
ACU_SEC = 2 ** 7
|
||
|
ACU_NOW = 2 ** 8
|
||
|
|
||
|
ACU_DATE = ACU_YEAR | ACU_MONTH | ACU_WEEK | ACU_DAY
|
||
|
ACU_TIME = ACU_HALFDAY | ACU_HOUR | ACU_MIN | ACU_SEC | ACU_NOW
|
||
|
|
||
|
_ACCURACY_MAPPING = [
|
||
|
(ACU_YEAR, 'year'),
|
||
|
(ACU_MONTH, 'month'),
|
||
|
(ACU_WEEK, 'week'),
|
||
|
(ACU_DAY, 'day'),
|
||
|
(ACU_HALFDAY, 'halfday'),
|
||
|
(ACU_HOUR, 'hour'),
|
||
|
(ACU_MIN, 'min'),
|
||
|
(ACU_SEC, 'sec'),
|
||
|
(ACU_NOW, 'now')]
|
||
|
|
||
|
_ACCURACY_REVERSE_MAPPING = {
|
||
|
'year': ACU_YEAR,
|
||
|
'years': ACU_YEAR,
|
||
|
'month': ACU_MONTH,
|
||
|
'months': ACU_MONTH,
|
||
|
'week': ACU_WEEK,
|
||
|
'weeks': ACU_WEEK,
|
||
|
'day': ACU_DAY,
|
||
|
'days': ACU_DAY,
|
||
|
'halfday': ACU_HALFDAY,
|
||
|
'morning': ACU_HALFDAY,
|
||
|
'afternoon': ACU_HALFDAY,
|
||
|
'evening': ACU_HALFDAY,
|
||
|
'night': ACU_HALFDAY,
|
||
|
'tonight': ACU_HALFDAY,
|
||
|
'midnight': ACU_HALFDAY,
|
||
|
'hour': ACU_HOUR,
|
||
|
'hours': ACU_HOUR,
|
||
|
'min': ACU_MIN,
|
||
|
'minute': ACU_MIN,
|
||
|
'mins': ACU_MIN,
|
||
|
'minutes': ACU_MIN,
|
||
|
'sec': ACU_SEC,
|
||
|
'second': ACU_SEC,
|
||
|
'secs': ACU_SEC,
|
||
|
'seconds': ACU_SEC,
|
||
|
'now': ACU_NOW}
|
||
|
|
||
|
def __init__(self, accuracy=0):
|
||
|
"""
|
||
|
Default constructor of L{pdtContext} class.
|
||
|
|
||
|
@type accuracy: integer
|
||
|
@param accuracy: Accuracy flag
|
||
|
|
||
|
@rtype: object
|
||
|
@return: L{pdtContext} instance
|
||
|
"""
|
||
|
self.accuracy = accuracy
|
||
|
|
||
|
def updateAccuracy(self, *accuracy):
|
||
|
"""
|
||
|
Updates current accuracy flag
|
||
|
"""
|
||
|
for acc in accuracy:
|
||
|
if not isinstance(acc, int):
|
||
|
acc = self._ACCURACY_REVERSE_MAPPING[acc]
|
||
|
self.accuracy |= acc
|
||
|
|
||
|
def update(self, context):
|
||
|
"""
|
||
|
Uses another L{pdtContext} instance to update current one
|
||
|
"""
|
||
|
self.updateAccuracy(context.accuracy)
|
||
|
|
||
|
@property
|
||
|
def hasDate(self):
|
||
|
"""
|
||
|
Returns True if current context is accurate to date
|
||
|
"""
|
||
|
return bool(self.accuracy & self.ACU_DATE)
|
||
|
|
||
|
@property
|
||
|
def hasTime(self):
|
||
|
"""
|
||
|
Returns True if current context is accurate to time
|
||
|
"""
|
||
|
return bool(self.accuracy & self.ACU_TIME)
|
||
|
|
||
|
@property
|
||
|
def dateTimeFlag(self):
|
||
|
"""
|
||
|
Returns the old date/time flag code
|
||
|
"""
|
||
|
return int(self.hasDate and 1) | int(self.hasTime and 2)
|
||
|
|
||
|
@property
|
||
|
def hasDateOrTime(self):
|
||
|
"""
|
||
|
Returns True if current context is accurate to date/time
|
||
|
"""
|
||
|
return bool(self.accuracy)
|
||
|
|
||
|
def __repr__(self):
|
||
|
accuracy_repr = []
|
||
|
for acc, name in self._ACCURACY_MAPPING:
|
||
|
if acc & self.accuracy:
|
||
|
accuracy_repr.append('pdtContext.ACU_%s' % name.upper())
|
||
|
if accuracy_repr:
|
||
|
accuracy_repr = 'accuracy=' + ' | '.join(accuracy_repr)
|
||
|
else:
|
||
|
accuracy_repr = ''
|
||
|
|
||
|
return 'pdtContext(%s)' % accuracy_repr
|
||
|
|
||
|
def __eq__(self, ctx):
|
||
|
return self.accuracy == ctx.accuracy
|