You can hack ast.literal_eval
to make it return a frozenset when it sees a set. Here is how to do:
- search where the library for your Python installation is
- it contains the file
ast.py
that contains the function literal_eval
- copy that function (using a different name) in you own module and change it to import all the relevant names from the
ast
module
- in the line processing a
Set
, replace the generation of a set
with a frozenset
You can then use it to safely parse literal sets containing sets. For my Python 3.5, I used:
def frozen_literal_eval(node_or_string):
"""
Safely evaluate an expression node or a string containing a Python
expression. The string or node provided may only consist of the following
Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
sets, booleans, and None.
SPECIAL: This version uses frozensets instead of sets
"""
# SPECIAL: import names from ast module
from ast import parse, Expression, Str, Bytes, Num, Tuple, List, Set, Dict
from ast import NameConstant, UnaryOp, UAdd, USub, BinOp, Add, Sub
# END SPECIAL
if isinstance(node_or_string, str):
node_or_string = parse(node_or_string, mode='eval')
if isinstance(node_or_string, Expression):
node_or_string = node_or_string.body
def _convert(node):
if isinstance(node, (Str, Bytes)):
return node.s
elif isinstance(node, Num):
return node.n
elif isinstance(node, Tuple):
return tuple(map(_convert, node.elts))
elif isinstance(node, List):
return list(map(_convert, node.elts))
elif isinstance(node, Set):
#SPECIAL: returns a frozenset
return frozenset(map(_convert, node.elts))
# END SPECIAL
elif isinstance(node, Dict):
return dict((_convert(k), _convert(v)) for k, v
in zip(node.keys, node.values))
elif isinstance(node, NameConstant):
return node.value
elif isinstance(node, UnaryOp) and \
isinstance(node.op, (UAdd, USub)) and \
isinstance(node.operand, (Num, UnaryOp, BinOp)):
operand = _convert(node.operand)
if isinstance(node.op, UAdd):
return + operand
else:
return - operand
elif isinstance(node, BinOp) and \
isinstance(node.op, (Add, Sub)) and \
isinstance(node.right, (Num, UnaryOp, BinOp)) and \
isinstance(node.left, (Num, UnaryOp, BinOp)):
left = _convert(node.left)
right = _convert(node.right)
if isinstance(node.op, Add):
return left + right
else:
return left - right
raise ValueError('malformed node or string: ' + repr(node))
return _convert(node_or_string)
I could the use:
>>> s = '{ 1, 2, {3, 4}}'
>>> frozen_literal_eval(s)
frozenset({1, 2, frozenset({3, 4})})