|
|
@@ -0,0 +1,130 @@
|
|
|
+import string
|
|
|
+
|
|
|
+from pyparsing import (
|
|
|
+ Literal, White, Word, alphanums, CharsNotIn, Forward, Group, SkipTo,
|
|
|
+ Optional, OneOrMore, ZeroOrMore, pythonStyleComment)
|
|
|
+
|
|
|
+
|
|
|
+class NginxParser(object):
|
|
|
+ """
|
|
|
+ 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 + "_/")
|
|
|
+ value = CharsNotIn("{};")
|
|
|
+ value2 = CharsNotIn(";")
|
|
|
+ location = CharsNotIn("{};," + string.whitespace)
|
|
|
+ ifword = Literal("if")
|
|
|
+ setword = Literal("set")
|
|
|
+ # modifier for location uri [ = | ~ | ~* | ^~ ]
|
|
|
+ modifier = Literal("=") | Literal("~*") | Literal("~") | Literal("^~")
|
|
|
+
|
|
|
+ # rules
|
|
|
+ assignment = (key + Optional(space + value) + semicolon)
|
|
|
+ setblock = (setword + OneOrMore(space + value2) + semicolon)
|
|
|
+ block = Forward()
|
|
|
+ ifblock = Forward()
|
|
|
+ subblock = Forward()
|
|
|
+
|
|
|
+ ifblock << (
|
|
|
+ Group(ifword + Optional(space) + Optional(value) + SkipTo('{'))
|
|
|
+ + left_bracket
|
|
|
+ + Group(subblock)
|
|
|
+ + right_bracket)
|
|
|
+
|
|
|
+ subblock << ZeroOrMore(
|
|
|
+ Group(assignment) | block | Group(ifblock) | setblock
|
|
|
+ )
|
|
|
+
|
|
|
+ block << Group(
|
|
|
+ Group(key + Optional(space + modifier) + Optional(space + location))
|
|
|
+ + left_bracket
|
|
|
+ + Group(subblock)
|
|
|
+ + right_bracket
|
|
|
+ )
|
|
|
+
|
|
|
+ script = OneOrMore(Group(assignment) | block).ignore(pythonStyleComment)
|
|
|
+
|
|
|
+ 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 list of tree.
|
|
|
+ """
|
|
|
+ return self.parse().asList()
|
|
|
+
|
|
|
+
|
|
|
+class NginxDumper(object):
|
|
|
+ """
|
|
|
+ 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:
|
|
|
+ if current_indent:
|
|
|
+ yield spacer
|
|
|
+ indentation = spacer * current_indent
|
|
|
+ if isinstance(key, list):
|
|
|
+ yield indentation + spacer.join(key) + ' {'
|
|
|
+ for parameter in values:
|
|
|
+ if isinstance(parameter[0], list):
|
|
|
+ dumped = self.__iter__(
|
|
|
+ [parameter],
|
|
|
+ current_indent + self.indentation)
|
|
|
+ for line in dumped:
|
|
|
+ yield line
|
|
|
+ else:
|
|
|
+ dumped = spacer.join(parameter) + ';'
|
|
|
+ yield spacer * (
|
|
|
+ current_indent + self.indentation) + dumped
|
|
|
+
|
|
|
+ yield indentation + '}'
|
|
|
+ else:
|
|
|
+ yield spacer * current_indent + key + spacer + values + ';'
|
|
|
+
|
|
|
+ def as_string(self):
|
|
|
+ return '\n'.join(self)
|
|
|
+
|
|
|
+ def to_file(self, out):
|
|
|
+ for line in self:
|
|
|
+ out.write(line+"\n")
|
|
|
+ out.close()
|
|
|
+ return out
|
|
|
+
|
|
|
+
|
|
|
+# Shortcut functions to respect Python's serialization interface
|
|
|
+# (like pyyaml, picker or json)
|
|
|
+
|
|
|
+def loads(source):
|
|
|
+ return NginxParser(source).as_list()
|
|
|
+
|
|
|
+
|
|
|
+def load(_file):
|
|
|
+ return loads(_file.read())
|
|
|
+
|
|
|
+
|
|
|
+def dumps(blocks, indentation=4):
|
|
|
+ return NginxDumper(blocks, indentation).as_string()
|
|
|
+
|
|
|
+
|
|
|
+def dump(blocks, _file, indentation=4):
|
|
|
+ return NginxDumper(blocks, indentation).to_file(_file)
|