1""" 2 sphinx.util.tags 3 ~~~~~~~~~~~~~~~~ 4 5 :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 6 :license: BSD, see LICENSE for details. 7""" 8 9from typing import Iterator, List 10 11from jinja2 import nodes 12from jinja2.environment import Environment 13from jinja2.nodes import Node 14from jinja2.parser import Parser 15 16env = Environment() 17 18 19class BooleanParser(Parser): 20 """ 21 Only allow condition exprs and/or/not operations. 22 """ 23 24 def parse_compare(self) -> Node: 25 node = None # type: Node 26 token = self.stream.current 27 if token.type == 'name': 28 if token.value in ('true', 'false', 'True', 'False'): 29 node = nodes.Const(token.value in ('true', 'True'), 30 lineno=token.lineno) 31 elif token.value in ('none', 'None'): 32 node = nodes.Const(None, lineno=token.lineno) 33 else: 34 node = nodes.Name(token.value, 'load', lineno=token.lineno) 35 next(self.stream) 36 elif token.type == 'lparen': 37 next(self.stream) 38 node = self.parse_expression() 39 self.stream.expect('rparen') 40 else: 41 self.fail("unexpected token '%s'" % (token,), token.lineno) 42 return node 43 44 45class Tags: 46 def __init__(self, tags: List[str] = None) -> None: 47 self.tags = dict.fromkeys(tags or [], True) 48 49 def has(self, tag: str) -> bool: 50 return tag in self.tags 51 52 __contains__ = has 53 54 def __iter__(self) -> Iterator[str]: 55 return iter(self.tags) 56 57 def add(self, tag: str) -> None: 58 self.tags[tag] = True 59 60 def remove(self, tag: str) -> None: 61 self.tags.pop(tag, None) 62 63 def eval_condition(self, condition: str) -> bool: 64 # exceptions are handled by the caller 65 parser = BooleanParser(env, condition, state='variable') 66 expr = parser.parse_expression() 67 if not parser.stream.eos: 68 raise ValueError('chunk after expression') 69 70 def eval_node(node: Node) -> bool: 71 if isinstance(node, nodes.CondExpr): 72 if eval_node(node.test): # type: ignore 73 return eval_node(node.expr1) # type: ignore 74 else: 75 return eval_node(node.expr2) # type: ignore 76 elif isinstance(node, nodes.And): 77 return eval_node(node.left) and eval_node(node.right) # type: ignore 78 elif isinstance(node, nodes.Or): 79 return eval_node(node.left) or eval_node(node.right) # type: ignore 80 elif isinstance(node, nodes.Not): 81 return not eval_node(node.node) # type: ignore 82 elif isinstance(node, nodes.Name): 83 return self.tags.get(node.name, False) # type: ignore 84 else: 85 raise ValueError('invalid node, check parsing') 86 87 return eval_node(expr) 88