1
0
mirror of https://github.com/certbot/certbot.git synced 2025-08-09 15:02:48 +03:00
Files
certbot/certbot-nginx/certbot_nginx/nginxparser.py
2016-04-13 16:45:54 -07:00

138 lines
4.1 KiB
Python

"""Very low-level nginx config parser based on pyparsing."""
import string
from pyparsing import (
Literal, White, Word, alphanums, CharsNotIn, Forward, Group,
Optional, OneOrMore, Regex, ZeroOrMore)
from pyparsing import stringEnd
from pyparsing import restOfLine
class RawNginxParser(object):
# pylint: disable=expression-not-assigned
"""A class that parses nginx configuration with pyparsing."""
# constants
left_bracket = Literal("{").suppress()
right_bracket = Literal("}").suppress()
semicolon = Literal(";").suppress()
space = White().suppress()
key = Word(alphanums + "_/")
# Matches anything that is not a special character AND any chars in single
# or double quotes
value = Regex(r"((\".*\")?(\'.*\')?[^\{\};,]?)+")
location = CharsNotIn("{};," + string.whitespace)
# modifier for location uri [ = | ~ | ~* | ^~ ]
modifier = Literal("=") | Literal("~*") | Literal("~") | Literal("^~")
# rules
comment = Literal('#') + restOfLine()
assignment = (key + Optional(space + value, default=None) + semicolon)
location_statement = Optional(space + modifier) + Optional(space + location)
if_statement = Literal("if") + space + Regex(r"\(.+\)") + space
block = Forward()
block << Group(
(Group(key + location_statement) ^ Group(if_statement)) +
left_bracket +
Group(ZeroOrMore(Group(comment | assignment) | block)) +
right_bracket)
script = OneOrMore(Group(comment | assignment) ^ block) + stringEnd
def __init__(self, source):
self.source = source
def parse(self):
"""Returns the parsed tree."""
return self.script.parseString(self.source)
def as_list(self):
"""Returns the parsed tree as a list."""
return self.parse().asList()
class RawNginxDumper(object):
# pylint: disable=too-few-public-methods
"""A class that dumps nginx configuration from the provided tree."""
def __init__(self, blocks, indentation=4):
self.blocks = blocks
self.indentation = indentation
def __iter__(self, blocks=None, current_indent=0, spacer=' '):
"""Iterates the dumped nginx content."""
blocks = blocks or self.blocks
for key, values in blocks:
indentation = spacer * current_indent
if isinstance(key, list):
if current_indent:
yield ''
yield indentation + spacer.join(key) + ' {'
for parameter in values:
dumped = self.__iter__([parameter], current_indent + self.indentation)
for line in dumped:
yield line
yield indentation + '}'
else:
if key == '#':
yield spacer * current_indent + key + values
else:
if values is None:
yield spacer * current_indent + key + ';'
else:
yield spacer * current_indent + key + spacer + values + ';'
def __str__(self):
"""Return the parsed block as a string."""
return '\n'.join(self) + '\n'
# Shortcut functions to respect Python's serialization interface
# (like pyyaml, picker or json)
def loads(source):
"""Parses from a string.
:param str souce: The string to parse
:returns: The parsed tree
:rtype: list
"""
return RawNginxParser(source).as_list()
def load(_file):
"""Parses from a file.
:param file _file: The file to parse
:returns: The parsed tree
:rtype: list
"""
return loads(_file.read())
def dumps(blocks, indentation=4):
"""Dump to a string.
:param list block: The parsed tree
:param int indentation: The number of spaces to indent
:rtype: str
"""
return str(RawNginxDumper(blocks, indentation))
def dump(blocks, _file, indentation=4):
"""Dump to a file.
:param list block: The parsed tree
:param file _file: The file to dump to
:param int indentation: The number of spaces to indent
:rtype: NoneType
"""
return _file.write(dumps(blocks, indentation))