1111INDENT = ' '
1212
1313
14+ def bump_child_depth (obj , depth ):
15+ children = getattr (obj , 'children' , [])
16+ for child in children :
17+ child ._depth = depth + 1
18+ bump_child_depth (child , child ._depth )
19+
20+
1421class Conf (object ):
1522 """
1623 Represents an nginx configuration.
@@ -97,41 +104,48 @@ def as_strings(self):
97104 else :
98105 for y in x .as_strings :
99106 ret .append (y )
107+ ret [- 1 ] = re .sub ('}\n +$' , '}\n ' , ret [- 1 ])
100108 return ret
101109
102110
103- class Server (object ):
111+ class Container (object ):
104112 """
105- Represents an nginx server block.
113+ Represents a type of child block found in an nginx config .
106114
107- A `Server` contains a list of key-values used to set up the web server
108- for a particular site. Can also contain other objects like Location blocks.
115+ Intended to be subclassed by various types of child blocks, like
116+ Locations or Geo blocks.
109117 """
110118
111- def __init__ (self , * args ):
119+ def __init__ (self , value , * args ):
112120 """
113121 Initialize object.
114122
115- :param *args: Any objects to include in this Server block.
123+ :param str value: Value to be used in name (e.g. regex for Location)
124+ :param *args: Any objects to include in this Conf.
116125 """
126+ self .name = ''
127+ self .value = value
128+ self ._depth = 0
117129 self .children = list (args )
130+ bump_child_depth (self , self ._depth )
118131
119132 def add (self , * args ):
120133 """
121- Add object(s) to the Server block .
134+ Add object(s) to the Container .
122135
123- :param *args: Any objects to add to the Server block .
124- :returns: full list of Server block 's child objects
136+ :param *args: Any objects to add to the Container .
137+ :returns: full list of Container 's child objects
125138 """
126139 self .children .extend (args )
140+ bump_child_depth (self , self ._depth )
127141 return self .children
128142
129143 def remove (self , * args ):
130144 """
131- Remove object(s) from the Server block .
145+ Remove object(s) from the Container .
132146
133- :param *args: Any objects to remove from the Server block .
134- :returns: full list of Server block 's child objects
147+ :param *args: Any objects to remove from the Container .
148+ :returns: full list of Container 's child objects
135149 """
136150 for x in args :
137151 self .children .remove (x )
@@ -171,88 +185,6 @@ def keys(self):
171185 """Return a list of child Key objects."""
172186 return [x for x in self .children if isinstance (x , Key )]
173187
174- @property
175- def as_list (self ):
176- """Return all child objects in nested lists of strings."""
177- return ['server' , '' , [x .as_list for x in self .children ]]
178-
179- @property
180- def as_dict (self ):
181- """Return all child objects in nested dict."""
182- return {'server' : [x .as_dict for x in self .children ]}
183-
184- @property
185- def as_strings (self ):
186- """Return the entire Server block as nginx config strings."""
187- ret = []
188- ret .append ('\n server {\n ' )
189- for x in self .children :
190- if isinstance (x , Key ):
191- ret .append (INDENT + x .as_strings )
192- elif isinstance (x , Comment ):
193- if x .inline and len (ret ) >= 1 :
194- ret [- 1 ] = ret [- 1 ].rstrip ('\n ' ) + ' ' + x .as_strings
195- else :
196- ret .append (INDENT + x .as_strings )
197- elif isinstance (x , Container ):
198- y = x .as_strings
199- ret .append ('\n ' + INDENT + y [0 ])
200- for z in y [1 :]:
201- ret .append (INDENT + z )
202- ret .append ('}\n ' )
203- return ret
204-
205-
206- class Container (object ):
207- """
208- Represents a type of child block found in an nginx config.
209-
210- Intended to be subclassed by various types of child blocks, like
211- Locations or Geo blocks.
212- """
213-
214- def __init__ (self , value , * args ):
215- """
216- Initialize object.
217-
218- :param str value: Value to be used in name (e.g. regex for Location)
219- :param *args: Any objects to include in this Conf.
220- """
221- self .name = ''
222- self .value = value
223- self .children = list (args )
224-
225- def add (self , * args ):
226- """
227- Add object(s) to the Container.
228-
229- :param *args: Any objects to add to the Container.
230- :returns: full list of Container's child objects
231- """
232- self .children .extend (args )
233- return self .children
234-
235- def remove (self , * args ):
236- """
237- Remove object(s) from the Container.
238-
239- :param *args: Any objects to remove from the Container.
240- :returns: full list of Container's child objects
241- """
242- for x in args :
243- self .children .remove (x )
244- return self .children
245-
246- @property
247- def comments (self ):
248- """Return a list of child Comment objects."""
249- return [x for x in self .children if isinstance (x , Comment )]
250-
251- @property
252- def keys (self ):
253- """Return a list of child Key objects."""
254- return [x for x in self .children if isinstance (x , Key )]
255-
256188 @property
257189 def as_list (self ):
258190 """Return all child objects in nested lists of strings."""
@@ -268,7 +200,11 @@ def as_dict(self):
268200 def as_strings (self ):
269201 """Return the entire Container as nginx config strings."""
270202 ret = []
271- ret .append ('{0} {1} {{\n ' .format (self .name , self .value ))
203+ container_title = (INDENT * self ._depth )
204+ container_title += '{0}{1} {{\n ' .format (
205+ self .name , (' {0}' .format (self .value ) if self .value else '' )
206+ )
207+ ret .append (container_title )
272208 for x in self .children :
273209 if isinstance (x , Key ):
274210 ret .append (INDENT + x .as_strings )
@@ -279,13 +215,14 @@ def as_strings(self):
279215 ret .append (INDENT + x .as_strings )
280216 elif isinstance (x , Container ):
281217 y = x .as_strings
282- ret .append ('\n ' + INDENT + INDENT + y [0 ])
218+ ret .append ('\n ' + y [0 ])
283219 for z in y [1 :]:
284220 ret .append (INDENT + z )
285221 else :
286222 y = x .as_strings
287223 ret .append (INDENT + y )
288- ret .append ('}\n ' )
224+ ret [- 1 ] = re .sub ('}\n +$' , '}\n ' , ret [- 1 ])
225+ ret .append ('}\n \n ' )
289226 return ret
290227
291228
@@ -318,6 +255,29 @@ def as_strings(self):
318255 return '# {0}\n ' .format (self .comment )
319256
320257
258+ class Http (Container ):
259+ """Container for HTTP sections in the main NGINX conf file."""
260+
261+ def __init__ (self , * args ):
262+ """Initialize."""
263+ super (Http , self ).__init__ ('' , * args )
264+ self .name = 'http'
265+
266+
267+ class Server (Container ):
268+ """Container for server block configurations."""
269+
270+ def __init__ (self , * args ):
271+ """Initialize."""
272+ super (Server , self ).__init__ ('' , * args )
273+ self .name = 'server'
274+
275+ @property
276+ def as_dict (self ):
277+ """Return all child objects in nested dict."""
278+ return {'server' : [x .as_dict for x in self .children ]}
279+
280+
321281class Location (Container ):
322282 """Container for Location-based options."""
323283
@@ -327,6 +287,15 @@ def __init__(self, value, *args):
327287 self .name = 'location'
328288
329289
290+ class Events (Container ):
291+ """Container for Event-based options."""
292+
293+ def __init__ (self , * args ):
294+ """Initialize."""
295+ super (Events , self ).__init__ ('' , * args )
296+ self .name = 'events'
297+
298+
330299class LimitExcept (Container ):
331300 """Container for specifying HTTP method restrictions."""
332301
@@ -420,6 +389,20 @@ def loads(data, conf=True):
420389 index = 0
421390
422391 while True :
392+ m = re .compile (r'^\s*events\s*{' , re .S ).search (data [index :])
393+ if m :
394+ e = Events ()
395+ lopen .insert (0 , e )
396+ index += m .end ()
397+ continue
398+
399+ m = re .compile (r'^\s*http\s*{' , re .S ).search (data [index :])
400+ if m :
401+ h = Http ()
402+ lopen .insert (0 , h )
403+ index += m .end ()
404+ continue
405+
423406 m = re .compile (r'^\s*server\s*{' , re .S ).search (data [index :])
424407 if m :
425408 s = Server ()
@@ -458,7 +441,7 @@ def loads(data, conf=True):
458441 m = re .compile (r'^(\s*)#\s*(.*?)\n' , re .S ).search (data [index :])
459442 if m :
460443 c = Comment (m .group (2 ), inline = '\n ' not in m .group (1 ))
461- if lopen and isinstance (lopen [0 ], ( Container , Server ) ):
444+ if lopen and isinstance (lopen [0 ], Container ):
462445 lopen [0 ].add (c )
463446 else :
464447 f .add (c ) if conf else f .append (c )
@@ -467,13 +450,10 @@ def loads(data, conf=True):
467450
468451 m = re .compile (r'^\s*}' , re .S ).search (data [index :])
469452 if m :
470- if isinstance (lopen [0 ], Server ):
471- f .add (lopen [0 ]) if conf else f .append (lopen [0 ])
472- lopen .pop (0 )
473- elif isinstance (lopen [0 ], Container ):
453+ if isinstance (lopen [0 ], Container ):
474454 c = lopen [0 ]
475455 lopen .pop (0 )
476- if lopen and isinstance (lopen [0 ], ( Container , Server ) ):
456+ if lopen and isinstance (lopen [0 ], Container ):
477457 lopen [0 ].add (c )
478458 else :
479459 f .add (c ) if conf else f .append (c )
@@ -484,7 +464,13 @@ def loads(data, conf=True):
484464 key_wo_quoted = r'^\s*([a-zA-Z0-9-_]+?)\s+(.+?);'
485465 m1 = re .compile (key_with_quoted , re .S ).search (data [index :])
486466 m2 = re .compile (key_wo_quoted , re .S ).search (data [index :])
487- m = m1 or m2
467+ if m1 and m2 :
468+ if m1 .start () <= m2 .start ():
469+ m = m1
470+ else :
471+ m = m2
472+ else :
473+ m = m1 or m2
488474 if m :
489475 k = Key (m .group (1 ), m .group (2 ))
490476 if lopen and isinstance (lopen [0 ], (Container , Server )):
0 commit comments