root/configobj.py

Revision 3b4d2eec2ee3f6a4438826949cdbf8e1194260c7, 87.2 kB (checked in by pj <pj@03e2507f-5f03-0410-b1bc-a06d56394c08>, 3 years ago)

bugfix - must open the file for writing

git-svn-id: file:///home/pj/.svnroot/proj/mhi@17 03e2507f-5f03-0410-b1bc-a06d56394c08

  • Property mode set to 100644
Line 
1 # configobj.py
2 # A config file reader/writer that supports nested sections in config files.
3 # Copyright (C) 2005 Michael Foord, Nicola Larosa
4 # E-mail: fuzzyman AT voidspace DOT org DOT uk
5 #         nico AT tekNico DOT net
6
7 # ConfigObj 4
8
9 # Released subject to the BSD License
10 # Please see http://www.voidspace.org.uk/documents/BSD-LICENSE.txt
11
12 # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
13 # For information about bugfixes, updates and support, please join the
14 # ConfigObj mailing list:
15 # http://lists.sourceforge.net/lists/listinfo/configobj-develop
16 # Comments, suggestions and bug reports welcome.
17
18 """
19     >>> z = ConfigObj()
20     >>> z['a'] = 'a'
21     >>> z['sect'] = {
22     ...    'subsect': {
23     ...         'a': 'fish',
24     ...         'b': 'wobble',
25     ...     },
26     ...     'member': 'value',
27     ... }
28     >>> x = ConfigObj(z.write())
29     >>> z == x
30     1
31 """
32
33 import sys
34 INTP_VER = sys.version_info[:2]
35 if INTP_VER < (2, 2):
36     raise RuntimeError("Python v.2.2 or later needed")
37
38 import os, re
39 from types import StringTypes
40
41 # the UTF8 BOM - from codecs module
42 BOM_UTF8 = '\xef\xbb\xbf'
43
44 __version__ = '4.0.0beta5'
45
46 __revision__ = '$Id: configobj.py 126 2005-09-12 08:09:40Z fuzzyman $'
47
48 __docformat__ = "restructuredtext en"
49
50 __all__ = (
51     '__version__',
52     'BOM_UTF8',
53     'DEFAULT_INDENT_TYPE',
54     'NUM_INDENT_SPACES',
55     'MAX_INTERPOL_DEPTH',
56     'ConfigObjError',
57     'NestingError',
58     'ParseError',
59     'DuplicateError',
60     'ConfigspecError',
61     'ConfigObj',
62     'SimpleVal',
63     'InterpolationError',
64     'InterpolationDepthError',
65     'MissingInterpolationOption',
66     'RepeatSectionError',
67     '__docformat__',
68 )
69
70 DEFAULT_INDENT_TYPE = ' '
71 NUM_INDENT_SPACES = 4
72 MAX_INTERPOL_DEPTH = 10
73
74 OPTION_DEFAULTS = {
75     'interpolation': True,
76     'raise_errors': False,
77     'list_values': True,
78     'create_empty': False,
79     'file_error': False,
80     'configspec': None,
81     'stringify': True,
82     # option may be set to one of ('', ' ', '\t')
83     'indent_type': None,
84 }
85
86 class ConfigObjError(SyntaxError):
87     """
88     This is the base class for all errors that ConfigObj raises.
89     It is a subclass of SyntaxError.
90     
91     >>> raise ConfigObjError
92     Traceback (most recent call last):
93     ConfigObjError
94     """
95     def __init__(self, message='', line_number=None, line=''):
96         self.line = line
97         self.line_number = line_number
98         self.message = message
99         SyntaxError.__init__(self, message)
100
101 class NestingError(ConfigObjError):
102     """
103     This error indicates a level of nesting that doesn't match.
104     
105     >>> raise NestingError
106     Traceback (most recent call last):
107     NestingError
108     """
109
110 class ParseError(ConfigObjError):
111     """
112     This error indicates that a line is badly written.
113     It is neither a valid ``key = value`` line,
114     nor a valid section marker line.
115     
116     >>> raise ParseError
117     Traceback (most recent call last):
118     ParseError
119     """
120
121 class DuplicateError(ConfigObjError):
122     """
123     The keyword or section specified already exists.
124     
125     >>> raise DuplicateError
126     Traceback (most recent call last):
127     DuplicateError
128     """
129
130 class ConfigspecError(ConfigObjError):
131     """
132     An error occured whilst parsing a configspec.
133     
134     >>> raise ConfigspecError
135     Traceback (most recent call last):
136     ConfigspecError
137     """
138
139 class InterpolationError(ConfigObjError):
140     """Base class for the two interpolation errors."""
141
142 class InterpolationDepthError(InterpolationError):
143     """Maximum interpolation depth exceeded in string interpolation."""
144
145     def __init__(self, option):
146         """
147         >>> raise InterpolationDepthError('yoda')
148         Traceback (most recent call last):
149         InterpolationDepthError: max interpolation depth exceeded in value "yoda".
150         """
151         InterpolationError.__init__(
152             self,
153             'max interpolation depth exceeded in value "%s".' % option)
154
155 class RepeatSectionError(ConfigObjError):
156     """
157     This error indicates additional sections in a section with a
158     ``__many__`` (repeated) section.
159     
160     >>> raise RepeatSectionError
161     Traceback (most recent call last):
162     RepeatSectionError
163     """
164
165 class MissingInterpolationOption(InterpolationError):
166     """A value specified for interpolation was missing."""
167
168     def __init__(self, option):
169         """
170         >>> raise MissingInterpolationOption('yoda')
171         Traceback (most recent call last):
172         MissingInterpolationOption: missing option "yoda" in interpolation.
173         """
174         InterpolationError.__init__(
175             self,
176             'missing option "%s" in interpolation.' % option)
177
178 class Section(dict):
179     """
180     A dictionary-like object that represents a section in a config file.
181     
182     It does string interpolation if the 'interpolate' attribute
183     of the 'main' object is set to True.
184     
185     Interpolation is tried first from the 'DEFAULT' section of this object,
186     next from the 'DEFAULT' section of the parent, lastly the main object.
187     
188     A Section will behave like an ordered dictionary - following the
189     order of the ``scalars`` and ``sections`` attributes.
190     You can use this to change the order of members.
191     
192     Iteration follows the order: scalars, then sections.
193     """
194
195     _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
196
197     def __init__(self, parent, depth, main, indict=None, name=None):
198         """
199         parent is the section above
200         depth is the depth level of this section
201         main is the main ConfigObj
202         indict is a dictionary to initialise the section with
203         """
204         if indict is None:
205             indict = {}
206         dict.__init__(self)
207         # used for nesting level *and* interpolation
208         self.parent = parent
209         # used for the interpolation attribute
210         self.main = main
211         # level of nesting depth of this Section
212         self.depth = depth
213         # the sequence of scalar values in this Section
214         self.scalars = []
215         # the sequence of sections in this Section
216         self.sections = []
217         # purely for information
218         self.name = name
219         # for comments :-)
220         self.comments = {}
221         self.inline_comments = {}
222         # for the configspec
223         self.configspec = {}
224         # for defaults
225         self.defaults = []
226         #
227         # we do this explicitly so that __setitem__ is used properly
228         # (rather than just passing to ``dict.__init__``)
229         for entry in indict:
230             self[entry] = indict[entry]
231
232     def _interpolate(self, value):
233         """Nicked from ConfigParser."""
234         depth = MAX_INTERPOL_DEPTH
235         # loop through this until it's done
236         while depth:
237             depth -= 1
238             if value.find("%(") != -1:
239                 value = self._KEYCRE.sub(self._interpolation_replace, value)
240             else:
241                 break
242         else:
243             raise InterpolationDepthError(value)
244         return value
245
246     def _interpolation_replace(self, match):
247         """ """
248         s = match.group(1)
249         if s is None:
250             return match.group()
251         else:
252             # switch off interpolation before we try and fetch anything !
253             self.main.interpolation = False
254             # try the 'DEFAULT' member of *this section* first
255             val = self.get('DEFAULT', {}).get(s)
256             # try the 'DEFAULT' member of the *parent section* next
257             if val is None:
258                 val = self.parent.get('DEFAULT', {}).get(s)
259             # last, try the 'DEFAULT' member of the *main section*
260             if val is None:
261                 val = self.main.get('DEFAULT', {}).get(s)
262             self.main.interpolation = True
263             if val is None:
264                 raise MissingInterpolationOption(s)
265             return val
266
267     def __getitem__(self, key):
268         """Fetch the item and do string interpolation."""
269         val = dict.__getitem__(self, key)
270         if self.main.interpolation and isinstance(val, StringTypes):
271             return self._interpolate(val)
272         return val
273
274     def __setitem__(self, key, value):
275         """
276         Correctly set a value.
277         
278         Making dictionary values Section instances.
279         (We have to special case 'Section' instances - which are also dicts)
280         
281         Keys must be strings.
282         Values need only be strings (or lists of strings) if
283         ``main.stringify`` is set.
284         """
285         if not isinstance(key, StringTypes):
286             raise ValueError, 'The key "%s" is not a string.' % key
287 ##        if self.depth is None:
288 ##            self.depth = 0
289         # add the comment
290         if not self.comments.has_key(key):
291             self.comments[key] = []
292             self.inline_comments[key] = ''
293         # remove the entry from defaults
294         if key in self.defaults:
295             self.defaults.remove(key)
296         #
297         if isinstance(value, Section):
298             if not self.has_key(key):
299                 self.sections.append(key)
300             dict.__setitem__(self, key, value)
301         elif isinstance(value, dict):
302             # First create the new depth level,
303             # then create the section
304             if not self.has_key(key):
305                 self.sections.append(key)
306             new_depth = self.depth + 1
307             dict.__setitem__(
308                 self,
309                 key,
310                 Section(
311                     self,
312                     new_depth,
313                     self.main,
314                     indict=value,
315                     name=key))
316         else:
317             if not self.has_key(key):
318                 self.scalars.append(key)
319             if not self.main.stringify:
320                 if isinstance(value, StringTypes):
321                     pass
322                 elif isinstance(value, (list, tuple)):
323                     for entry in value:
324                         if not isinstance(entry, StringTypes):
325                             raise TypeError, (
326                                 'Value is not a string "%s".' % entry)
327                 else:
328                     raise TypeError, 'Value is not a string "%s".' % value
329             dict.__setitem__(self, key, value)
330
331     def __delitem__(self, key):
332         """Remove items from the sequence when deleting."""
333         dict. __delitem__(self, key)
334         if key in self.scalars:
335             self.scalars.remove(key)
336         else:
337             self.sections.remove(key)
338         del self.comments[key]
339         del self.inline_comments[key]
340
341     def get(self, key, default=None):
342         """A version of ``get`` that doesn't bypass string interpolation."""
343         try:
344             return self[key]
345         except KeyError:
346             return default
347
348     def update(self, indict):
349         """A version of update that uses our ``__setitem__``."""
350         for entry in indict:
351             self[entry] = indict[entry]
352
353     def pop(self, key, *args):
354         """ """
355         val = dict.pop(self, key, *args)
356         if key in self.scalars:
357             del self.comments[key]
358             del self.inline_comments[key]
359             self.scalars.remove(key)
360         elif key in self.sections:
361             del self.comments[key]
362             del self.inline_comments[key]
363             self.sections.remove(key)
364         if self.main.interpolation and isinstance(val, StringTypes):
365             return self._interpolate(val)
366         return val
367
368     def popitem(self):
369         """Pops the first (key,val)"""
370         sequence = (self.scalars + self.sections)
371         if not sequence:
372             raise KeyError, ": 'popitem(): dictionary is empty'"
373         key = sequence[0]
374         val =  self[key]
375         del self[key]
376         return key, val
377
378     def clear(self):
379         """
380         A version of clear that also affects scalars/sections
381         Also clears comments and configspec.
382         
383         Leaves other attributes alone :
384             depth/main/parent are not affected
385         """
386         dict.clear(self)
387         self.scalars = []
388         self.sections = []
389         self.comments = {}
390         self.inline_comments = {}
391         self.configspec = {}
392
393     def setdefault(self, key, default=None):
394         """A version of setdefault that sets sequence if appropriate."""
395         try:
396             return self[key]
397         except KeyError:
398             self[key] = default
399             return default
400
401     def items(self):
402         """ """
403         return zip((self.scalars + self.sections), self.values())
404
405     def keys(self):
406         """ """
407         return (self.scalars + self.sections)
408
409     def values(self):
410         """ """
411         return [self[key] for key in (self.scalars + self.sections)]
412
413     def iteritems(self):
414         """ """
415         return iter(self.items())
416
417     def iterkeys(self):
418         """ """
419         return iter((self.scalars + self.sections))
420
421     __iter__ = iterkeys
422
423     def itervalues(self):
424         """ """
425         return iter(self.values())
426
427     def __repr__(self):
428         return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
429             for key in (self.scalars + self.sections)])
430
431     __str__ = __repr__
432
433     # Extra methods - not in a normal dictionary
434
435     def dict(self):
436         """
437         Return a deepcopy of self as a dictionary.
438         
439         All members that are ``Section`` instances are recursively turned to
440         ordinary dictionaries - by calling their ``dict`` method.
441         
442         >>> n = a.dict()
443         >>> n == a
444         1
445         >>> n is a
446         0
447         """
448         newdict = {}
449         for entry in self:
450             this_entry = self[entry]
451             if isinstance(this_entry, Section):
452                 this_entry = this_entry.dict()
453             elif isinstance(this_entry, (list, tuple)):
454                 # create a copy rather than a reference
455                 this_entry = list(this_entry)
456             newdict[entry] = this_entry
457         return newdict
458
459     def rename(self, oldkey, newkey):
460         """
461         Change a keyname to another, without changing position in sequence.
462         
463         Implemented so that transformations can be made on keys,
464         as well as on values. (used by encode and decode)
465         
466         Also renames comments.
467         """
468         if oldkey in self.scalars:
469             the_list = self.scalars
470         elif oldkey in self.sections:
471             the_list = self.sections
472         else:
473             raise KeyError, 'Key "%s" not found.' % oldkey
474         pos = the_list.index(oldkey)
475         #
476         val = self[oldkey]
477         dict.__delitem__(self, oldkey)
478         dict.__setitem__(self, newkey, val)
479         the_list.remove(oldkey)
480         the_list.insert(pos, newkey)
481         comm = self.comments[oldkey]
482         inline_comment = self.inline_comments[oldkey]
483         del self.comments[oldkey]
484         del self.inline_comments[oldkey]
485         self.comments[newkey] = comm
486         self.inline_comments[newkey] = inline_comment
487
488     def walk(self, function, raise_errors=True,
489             call_on_sections=False, **keywargs):
490         """
491         Walk every member and call a function on the keyword and value.
492         
493         Return a dictionary of the return values
494         
495         If the function raises an exception, raise the errror
496         unless ``raise_errors=False``, in which case set the return value to
497         ``False``.
498         
499         Any unrecognised keyword arguments you pass to walk, will be pased on
500         to the function you pass in.
501         
502         Note: if ``call_on_sections`` is ``True`` then - on encountering a
503         subsection, *first* the function is called for the *whole* subsection,
504         and then recurses into it's members. This means your function must be
505         able to handle strings, dictionaries and lists. This allows you
506         to change the key of subsections as well as for ordinary members. The
507         return value when called on the whole subsection has to be discarded.
508         
509         See  the encode and decode methods for examples, including functions.
510         """
511         out = {}
512         # scalars first
513         for entry in self.scalars[:]:
514             try:
515                 out[entry] = function(self, entry, **keywargs)
516             except Exception:
517                 if raise_errors:
518                     raise
519                 else:
520                     out[entry] = False
521         # then sections
522         for entry in self.sections[:]:
523             if call_on_sections:
524                 try:
525                     function(self, entry, **keywargs)
526                 except Exception:
527                     if raise_errors:
528                         raise
529                     else:
530                         out[entry] = False
531             # previous result is discarded
532             out[entry] = self[entry].walk(
533                 function,
534                 raise_errors=raise_errors,
535                 call_on_sections=call_on_sections,
536                 **keywargs)
537         return out
538
539     def decode(self, encoding):
540         """
541         Decode all strings and values to unicode, using the specified encoding.
542         
543         Works with subsections and list values.
544         
545         Uses the ``walk`` method.
546         
547         Testing ``encode`` and ``decode``.
548         >>> m = ConfigObj(a)
549         >>> m.decode('ascii')
550         >>> def testuni(val):
551         ...     for entry in val:
552         ...         if not isinstance(entry, unicode):
553         ...             print >> sys.stderr, type(entry)
554         ...             raise AssertionError, 'decode failed.'
555         ...         if isinstance(val[entry], dict):
556         ...             testuni(val[entry])
557         ...         elif not isinstance(val[entry], unicode):
558         ...             raise AssertionError, 'decode failed.'
559         >>> testuni(m)
560         >>> m.encode('ascii')
561         >>> a == m
562         1
563         """
564         def decode(section, key, encoding=encoding):
565             """ """
566             val = section[key]
567             if isinstance(val, (list, tuple)):
568                 newval = []
569                 for entry in val:
570                     newval.append(entry.decode(encoding))
571             elif isinstance(val, dict):
572                 newval = val
573             else:
574                 newval = val.decode(encoding)
575             newkey = key.decode(encoding)
576             section.rename(key, newkey)
577             section[newkey] = newval
578         # using ``call_on_sections`` allows us to modify section names
579         self.walk(decode, call_on_sections=True)
580
581     def encode(self, encoding):
582         """
583         Encode all strings and values from unicode,
584         using the specified encoding.
585         
586         Works with subsections and list values.
587         Uses the ``walk`` method.
588         """
589         def encode(section, key, encoding=encoding):
590             """ """
591             val = section[key]
592             if isinstance(val, (list, tuple)):
593                 newval = []
594                 for entry in val:
595                     newval.append(entry.encode(encoding))
596             elif isinstance(val, dict):
597                 newval = val
598             else:
599                 newval = val.encode(encoding)
600             newkey = key.encode(encoding)
601             section.rename(key, newkey)
602             section[newkey] = newval
603         self.walk(encode, call_on_sections=True)
604
605 class ConfigObj(Section):
606     """
607     An object to read, create, and write config files.
608     
609     Testing with duplicate keys and sections.
610     
611     >>> c = '''
612     ... [hello]
613     ... member = value
614     ... [hello again]
615     ... member = value
616     ... [ "hello" ]
617     ... member = value
618     ... '''
619     >>> ConfigObj(c.split('\\n'), raise_errors = True)
620     Traceback (most recent call last):
621     DuplicateError: Duplicate section name at line 5.
622     
623     >>> d = '''
624     ... [hello]
625     ... member = value
626     ... [hello again]
627     ... member1 = value
628     ... member2 = value
629     ... 'member1' = value
630     ... [ "and again" ]
631     ... member = value
632     ... '''
633     >>> ConfigObj(d.split('\\n'), raise_errors = True)
634     Traceback (most recent call last):
635     DuplicateError: Duplicate keyword name at line 6.
636     """
637
638     _keyword = re.compile(r'''^ # line start
639         (\s*)                   # indentation
640         (                       # keyword
641             (?:".*?")|          # double quotes
642             (?:'.*?')|          # single quotes
643             (?:[^'"=].*?)       # no quotes
644         )
645         \s*=\s*                 # divider
646         (.*)                    # value (including list values and comments)
647         $   # line end
648         ''',
649         re.VERBOSE)
650
651     _sectionmarker = re.compile(r'''^
652         (\s*)                     # 1: indentation
653         ((?:\[\s*)+)              # 2: section marker open
654         (                         # 3: section name open
655             (?:"\s*\S.*?\s*")|    # at least one non-space with double quotes
656             (?:'\s*\S.*?\s*')|    # at least one non-space with single quotes
657             (?:[^'"\s].*?)        # at least one non-space unquoted
658         )                         # section name close
659         ((?:\s*\])+)              # 4: section marker close
660         \s*(\#.*)?                # 5: optional comment
661         $''',
662         re.VERBOSE)
663
664     # this regexp pulls list values out as a single string
665     # or single values and comments
666     _valueexp = re.compile(r'''^
667         (?:
668             (?:
669                 (
670                     (?:
671                         (?:
672                             (?:".*?")|              # double quotes
673                             (?:'.*?')|              # single quotes
674                             (?:[^'",\#][^,\#]*?)       # unquoted
675                         )
676                         \s*,\s*                     # comma
677                     )*      # match all list items ending in a comma (if any)
678                 )
679                 (
680                     (?:".*?")|                      # double quotes
681                     (?:'.*?')|                      # single quotes
682                     (?:[^'",\#\s][^,]*?)             # unquoted
683                 )?          # last item in a list - or string value
684             )|
685             (,)             # alternatively a single comma - empty list
686         )
687         \s*(\#.*)?          # optional comment
688         $''',
689         re.VERBOSE)
690
691     # use findall to get the members of a list value
692     _listvalueexp = re.compile(r'''
693         (
694             (?:".*?")|          # double quotes
695             (?:'.*?')|          # single quotes
696             (?:[^'",\#].*?)       # unquoted
697         )
698         \s*,\s*                 # comma
699         ''',
700         re.VERBOSE)
701
702     # this regexp is used for the value
703     # when lists are switched off
704     _nolistvalue = re.compile(r'''^
705         (
706             (?:".*?")|          # double quotes
707             (?:'.*?')|          # single quotes
708             (?:[^'"\#].*?)      # unquoted
709         )
710         \s*(\#.*)?              # optional comment
711         $''',
712         re.VERBOSE)
713
714     # regexes for finding triple quoted values on one line
715     _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
716     _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
717     _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
718     _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
719
720     _triple_quote = {
721         "'''": (_single_line_single, _multi_line_single),
722         '"""': (_single_line_double, _multi_line_double),
723     }
724
725     def __init__(self, infile=None, options=None, **kwargs):
726         """
727         Parse or create a config file object.
728         
729         ``ConfigObj(infile=None, options=None, **kwargs)``
730         """
731         if infile is None:
732             infile = []
733         if options is None:
734             options = {}
735         # keyword arguments take precedence over an options dictionary
736         options.update(kwargs)
737         # init the superclass
738         Section.__init__(self, self, 0, self)
739         #
740         defaults = OPTION_DEFAULTS.copy()
741         for entry in options.keys():
742             if entry not in defaults.keys():
743                 raise TypeError, 'Unrecognised option "%s".' % entry
744         # TODO: check the values too
745         # add the explicit options to the defaults
746         defaults.update(options)
747         #
748         # initialise a few variables
749         self._errors = []
750         self.raise_errors = defaults['raise_errors']
751         self.interpolation = defaults['interpolation']
752         self.list_values = defaults['list_values']
753         self.create_empty = defaults['create_empty']
754         self.file_error = defaults['file_error']
755         self.stringify = defaults['stringify']
756         self.indent_type = defaults['indent_type']
757         # used by the write method
758         self.BOM = None
759         #
760         self.initial_comment = []
761         self.final_comment = []
762         #
763         if isinstance(infile, StringTypes):
764             self.filename = os.path.abspath(infile)
765             if os.path.isfile(self.filename):
766                 infile = open(self.filename).readlines()
767             elif self.file_error:
768                 # raise an error if the file doesn't exist
769                 raise IOError, 'Config file not found: "%s".' % self.filename
770             else:
771                 # file doesn't already exist
772                 if self.create_empty:
773                     # this is a good test that the filename specified
774                     # isn't impossible - like on a non existent device
775                     h = open(self.filename,'w')
776                     h.write('')
777                     h.close()
778                 infile = []
779         elif isinstance(infile, (list, tuple)):
780             self.filename = None
781         elif isinstance(infile, dict):
782             # initialise self
783             # the Section class handles creating subsections
784             if isinstance(infile, ConfigObj):
785                 # get a copy of our ConfigObj
786                 infile = infile.dict()
787             for entry in infile:
788                 self[entry] = infile[entry]
789             self.filename = None
790             del self._errors
791             if defaults['configspec'] is not None:
792                 self._handle_configspec(defaults['configspec'])
793             else:
794                 self.configspec = None
795             return
796         elif hasattr(infile, 'seek'):
797             # this supports StringIO instances and even file objects
798             self.filename = infile
799             infile.seek(0)
800             infile = infile.readlines()
801             self.filename.seek(0)
802         else:
803             raise TypeError, ('infile must be a filename,'
804                 ' StringIO instance, or a file as a list.')
805         #
806         # strip trailing '\n' from lines
807         infile = [line.rstrip(