python ast.literal_eval и datetime
У меня есть строка "{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}"
и я хочу преобразовать это в объект, который он представляет. С помощью ast.literal_eval()
дает
ValueError: malformed string;
потому что это не позволяет строить объекты (т.е. datetime
вызов). Есть ли в любом случае, чтобы получить ast
обрабатывать это правильно, или для обеспечения eval
предотвратить внедрение кода?
5 ответов
В продолжение идеи Игнасио Васкеса-Абрамса:
import ast
import datetime
def parse_datetime_dict(astr,debug=False):
try: tree=ast.parse(astr)
except SyntaxError: raise ValueError(astr)
for node in ast.walk(tree):
if isinstance(node,(ast.Module,ast.Expr,ast.Dict,ast.Str,
ast.Attribute,ast.Num,ast.Name,ast.Load, ast.Tuple)): continue
if (isinstance(node,ast.Call)
and isinstance(node.func, ast.Attribute)
and node.func.attr == 'datetime'): continue
if debug:
attrs=[attr for attr in dir(node) if not attr.startswith('__')]
print(node)
for attrname in attrs:
print(' {k} ==> {v}'.format(k=attrname,v=getattr(node,attrname)))
raise ValueError(astr)
return eval(astr)
good_strings=["{'the_datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}"]
bad_strings=["__import__('os'); os.unlink",
"import os; os.unlink",
"import(os)", # SyntaxError
]
for astr in good_strings:
result=parse_datetime_dict(astr)
print('{s} ... [PASSED]'.format(s=astr))
for astr in bad_strings:
try:
result=parse_datetime_dict(astr)
except ValueError:
print('{s} ... [REJECTED]'.format(s=astr))
else:
sys.exit('ERROR: failed to catch {s!r}'.format(s=astr))
доходность
{'the_datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)} ... [PASSED]
__import__('os'); os.unlink ... [REJECTED]
import os; os.unlink ... [REJECTED]
import(os) ... [REJECTED]
Вы могли бы извлечь (2010, 11, 21, 0, 56, 58)
символы из строки, используя regex
передать это ast.literal_eval()
чтобы получить кортеж, а затем передать этот кортеж datetime.datetime(*a_tuple)
чтобы получить объект. Звучит как много, но каждый из шагов очень прост (и безопасен). Вот о чем я говорю:
import ast
import datetime
import re
s = "{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}"
m = re.search(r"""datetime(\((\d+)(,\s*\d+)*\))""", s)
if m: # any matches?
args = ast.literal_eval(m.group(1))
print datetime.datetime(*args)
# 2010-11-21 00:56:58
Это ищет шаблон "datetime(<comma separated list of integers>)"
, в строке и передает только список буквенных целочисленных значений ast.literal_eval()
для преобразования в кортеж - который всегда должен быть успешным и устойчивым к внедрению кода. Я считаю, что это называется "контекстно-зависимая оценка строк" или CSSE.
Вместо того, чтобы писать много кода, не используйте ast, когда вам нужно разобрать datetime objs. Вы можете запустить eval(). Кстати, обратите внимание, что у вас могут возникнуть проблемы с безопасностью при использовании этой функции, если строка может содержать хитрые команды python.
Вот как это работает:
>>> x="{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}"
>>> b=eval(x)
>>> b
{'datetime': datetime.datetime(2010, 11, 21, 0, 56, 58)}
>>> b["datetime"].year
2010
Наслаждайтесь!:D
Используйте языковые сервисы, чтобы скомпилировать его в AST, пройдитесь по AST, убедившись, что он содержит только наборы узлов из белого списка, затем выполните его.
Я столкнулся с этой проблемой и решил ее, заменив объект datetime такой строкой:
if mystrobj.__contains__('datetime.datetime('):
mystrobj = re.sub(r"datetime.datetime([(),0-9 ]*),", r"'\1',", mystrobj)
Используйте repr(datetime.datetime.utcnow()) для сохранения в вашем файле или файле. Посмотрите,
>>> import datetime
>>> oarg = datetime.datetime.utcnow()
>>> oarg
datetime.datetime(2013, 2, 6, 12, 39, 51, 709024)
>>> butiwantthis = repr(oarg)
>>> butiwantthis
'datetime.datetime(2013, 2, 6, 12, 39, 51, 709024)'
>>> eval(oarg)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: eval() arg 1 must be a string, bytes or code object
>>> eval(butiwantthis)
datetime.datetime(2013, 2, 6, 12, 39, 51, 709024)
Здорово! eval() работает!
Береги себя с импортом
>>> from datetime import datetime, date, time
>>> oarg = datetime.datetime.utcnow()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'
>>> oarg = datetime.utcnow()
>>> oarg
datetime.datetime(2013, 2, 6, 12, 41, 51, 870458)
>>> butiwantthis = repr(oarg)
>>> butiwantthis
'datetime.datetime(2013, 2, 6, 12, 41, 51, 870458)'
>>> eval(butiwantthis)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
AttributeError: type object 'datetime.datetime' has no attribute 'datetime'
>>> # wrong import
Отлично!
Ps: Python3.3