%PDF- %PDF-
Direktori : /lib/python3/dist-packages/xkit/ |
Current File : //lib/python3/dist-packages/xkit/xorgparser.py |
# xorgparser.py -- Core class of X-Kit's parser # # Copyright 2008 Alberto Milone <albertomilone@alice.it> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. from __future__ import print_function from __future__ import unicode_literals import sys from sys import stdout, stderr import copy class IdentifierException(Exception): '''Raise if no identifier can be found''' pass class OptionException(Exception): '''Raise when an option is not available.''' pass class SectionException(Exception): '''Raise when a section is not available.''' pass class ParseException(Exception): '''Raise when a postion is not available.''' pass class Parser(object): '''Only low-level methods here. See the xutils.XUtils subclass for higher-level methods.''' def __init__(self, source=None): '''source = can be an object or a file. If set to None (default) Parser will start from scratch with an empty configuration. Public: comments = name of the section which stores the commented lines located outside of the sections in the xorg.conf. globaldict = a global dictionary containing all the sections and options. For further information on globaldict, have a look at __check_sanity() and at get_value(). globaldict['Comments'] = stores the commented lines located inside of the sections in the xorg.conf. require_id = a list of the sections which require to have an "Identifier" set in the xorg.conf (e.g. Device sections). identifiers = a dictionary of the sections which require identifiers. sections = a tuple containing the names of all the sections in globaldict. Their names are not guaranteed to be all legal in xorg.conf (see "valid_sections"). valid_sections = a tuple containing the names of all the sections which __check_sanity() will look for in the xorg.conf. Sections with other names will be ignored by self._check_sanity(). references = a list containing the names of all the possible references.''' self.subsection = 'SubSection' self.commentsection = 'Comments' self.source = source self.sections = ('InputDevice', 'Device', 'Module', 'Monitor', 'Screen', 'ServerLayout', 'ServerFlags', 'Extensions', 'Files', 'InputClass', 'DRI', 'VideoAdaptor', 'Vendor', 'Modes', self.subsection, self.commentsection) # "Comments" is not a valid section self.valid_sections = self.sections[:-1] self.require_id = [ 'InputClass', 'InputDevice', 'Device', 'Monitor', 'Screen', 'ServerLayout' ] self.references = [ 'Device', 'InputDevice', 'Monitor', 'Screen' ] self.identifiers = {}.fromkeys(self.require_id) self.comments = [] self._gdict = {}.fromkeys(self.sections, 0) for elem in self._gdict: self._gdict[elem] = {} self._check_sanity() def _get_global(self): return self._gdict def _set_global(self, global_dict): self._gdict = global_dict # Property to expose _gdict as globaldict globaldict = property(_get_global, _set_global) def _check_sanity(self): '''Perform a sanity check of the file and fill self.globaldict with all the sections and subsections in the xorg.conf empty = is the file empty? If yes, then don't check if: * the last section is not complete * there are duplicates has_section: * == True: a section is already open * == False: a section is closed and/or a new section can be opened has_subsection: * == True: a subsection is already open * == False: a section is closed and/or a new section can be opened section_flag: * == '':a section is closed and/or a new section can be opened * == the name the current section section_tags = counter of the number of Section and EndSection strings subsection_tags = counter of the number of SubSection and EndSubSection strings lines_list = the list of the lines in the source object. global_iters = counts how many times each kind of section (section_flag) is found in the xorg.conf''' #See if the source is a file or a file object #and act accordingly file = self.source if file == None: lines_list = [] else: if not hasattr(file, 'write'):#it is a file myfile = open(file, 'r') lines_list = myfile.readlines() myfile.close() else:#it is a file object lines_list = file.readlines() # Create a dictionary such as the following: # {'Device': {}, 'InputDevice': {}} global_iters = {}.fromkeys(self.sections, 0) empty = True has_section = False has_subsection = False section_flag = '' section_tags = 0 subsection_tags = 0 it = 0 for line in lines_list: if line.strip().startswith('#'): if has_section == False: self.comments.append(line) else:#has_section == True section_pos = global_iters[section_flag] if has_subsection == False: self._gdict[self.commentsection].setdefault(section_flag, {}) temp_dict = self._gdict[self.commentsection][section_flag] temp_dict.setdefault(section_pos, {}) temp_dict[section_pos].setdefault('identifier', None) temp_dict[section_pos].setdefault('position', section_pos) temp_dict[section_pos].setdefault('section', None) temp_dict[section_pos].setdefault('options', []) temp_dict[section_pos]['options'].append(line.strip()) else:#has_subsection == True curlength = global_iters[self.subsection] self._gdict[self.commentsection].setdefault(self.subsection, {}) temp_dict = self._gdict[self.commentsection][self.subsection] temp_dict.setdefault(curlength, {}) temp_dict[curlength].setdefault('identifier', subsection_id) temp_dict[curlength].setdefault('position', section_pos) temp_dict[curlength].setdefault('section', section_flag) temp_dict[curlength].setdefault('options', []) temp_dict[curlength]['options'].append(line.strip()) del temp_dict # See if the name of the section is acceptable # i.e. included in self.valid_sections elif line.lower().strip().startswith('section'):#Begin Section test_line_found = False for sect in self.valid_sections: if line.lower().find('"' + sect.lower() + '"') != -1: test_line_found = True section = sect break if not test_line_found: # e.g. in case the name of the section is not # recognised: # Section "whatever" error = ('The name in the following line is invalid for a ' 'section:\n%s' % (line)) raise ParseException(error) else: if has_section == False: section_tags += 1 section_flag = section empty = False has_section = True else: error = 'Sections cannot be nested in other sections.' raise ParseException(error) elif line.lower().strip().startswith('endsection') == True: #End Section section_tags += 1 if has_section == True and has_subsection == False: global_iters[section_flag] += 1 section_flag = '' has_section = False else: error = 'An EndSection is in the wrong place.' raise ParseException(error) elif line.lower().strip().startswith('subsection') == True: #Begin SubSection subsection_tags += 1 if has_section == True and has_subsection == False: has_subsection = True subsection_id = line[line.find('"') + 1: line.rfind('"')].strip() self._gdict.setdefault(self.subsection, {}) curlength = global_iters[self.subsection] self._gdict[self.subsection][curlength] = {} # self._gdict - keys: # # section = the section in which the subsection is # located (e.g. "Screen") # position = e.g. in key 0 of the # self._gdict['Screen'] # identifier = e.g. 'Display' (in SubSection "Display") # options = a list of lines with the options temp_dict = self._gdict[self.subsection][curlength] temp_dict['section'] = section_flag try: temp_dict['position'] = global_iters[section_flag] except KeyError: del temp_dict error = ('SubSections can be nested only in well ' 'formed sections.') raise ParseException(error) temp_dict['identifier'] = subsection_id temp_dict['options'] = [] del temp_dict else: error = ('SubSections can be nested only in well formed ' 'sections.') raise ParseException(error) elif line.lower().strip().startswith('endsubsection') == True: #End SubSection subsection_tags += 1 if has_subsection == True: has_subsection = False global_iters[self.subsection] += 1 else: error = ('SubSections can be closed only after being ' 'previously opened.') raise ParseException(error) else: if section_flag != '': #any other line if line.strip() != '': #options if has_subsection == True: # section = the section in which the subsection # is located (e.g. "Screen") # position = e.g. in key 0 of the # self._gdict['Screen'] # identifier = e.g. 'Display' (in SubSection # "Display") # options = a list of lines with the options self._gdict[self.subsection][curlength][ 'options'].append('\t' + line.strip() + '\n') else: self._gdict.setdefault(section_flag, {}) curlength = global_iters[section_flag] self._gdict[section_flag].setdefault(curlength, []).append('\t' + line.strip() + '\n') it += 1 if not empty: # If the last section is not complete if section_tags % 2 != 0 or subsection_tags % 2 != 0: error = 'The last section is incomplete.' raise ParseException(error) # Fill self.identifiers self._fill_identifiers() # Make sure that the configuration file is compliant with # the rules of xorg self._check_syntax() else: self._fill_identifiers() def _check_syntax(self): '''This method contains the several checks which can guarantee compliance with the syntax rules of the xorg.conf''' # ''' # Raise an exception if there are duplicate options i.e. # options (not references) of the same kind with the same # or with a different value. # # e.g. Driver "nvidia" and Driver "intel" cannot coexist in the # same Device section. # ''' # if len(self.check_duplicate_options()) > 0: # error = ('There cannot be Duplicate Options:\n%s' % # (str(self.check_duplicate_options()))) # raise ParseException(error) # Raise an exception if there are duplicate sections i.e. # sections of the same kind (e.g. "Device") with the same # identifier. # # e.g. The following configuration is not allowed: # # Section "Device" # Identifier "My Device" # EndSection # # Section "Device" # Identifier "My Device" # EndSection if len(self.get_duplicate_sections()) > 0: error = ('There cannot be Duplicate Sections:\n%s' % (str(self.get_duplicate_sections()))) raise ParseException(error) # One word entries are not acceptable as either options or references. # If one is found, ParseException will be raised. self._validate_options() # Raise an exception if there are broken references i.e. references # to sections which don't exist. # # For example, if the xorg.conf were the following: # # Section "Device" # Identifier "Another Device" # EndSection # # Section "Screen" # Identifier "My Screen" # Device "My Device" # EndSection # # There would be no Device section which has "My Device" as an # identifier broken = self.get_broken_references() it = 0 for section in broken: it += len(broken[section]) if it > 0: error = 'There cannot be Broken References:\n%s' % (str(broken)) raise ParseException(error) # If there are sections which don't have an identifier # but they should (i.e. they are in self.require_id) # # NOTE: if there are empty sections without an identifier # e.g. Section "Device" # EndSection # # they won't trigger the ParseException but won't # cause any problem since they will be completely # ignored and won't appear in the target file. for section in self.require_id: if len(self._gdict[section]) != len(self.identifiers[section]): error = ('Not all the sections which require an identifier ' 'have an identifier.') raise ParseException(error) # The ServerLayout section must have at least 1 reference to a # "Screen" section if len(self._gdict['ServerLayout']) > 0: for section in self._gdict['ServerLayout']: screen_references = self.get_references('ServerLayout', section, reflist=['Screen']) if len(screen_references['Screen']) == 0: error = ('The ServerLayout section must have at ' 'least 1 reference to a "Screen" section.') raise ParseException(error) # No more than one default ServerLayout can be specified in the # ServerFlags section default_layout = self.get_default_serverlayout() if len(default_layout) > 0: if len(default_layout) > 1: error = ('No more than one default ServerLayout can be ' 'specified in the ServerFlags section.') raise ParseException(error) if not self.is_section('ServerLayout', position=default_layout[0]): error = 'The default ServerLayout does not exist.' raise ParseException(error) def _fill_identifiers(self): '''Fill self.identifiers self.identifiers has the section types as keys and a list of tuples as values. The tuples contain the identifier and the position of each section. Here's a basic scheme: self.identifiers = {section_type1: [ (identifier1, position1), (identifier2, position2) ], etc. } Concrete example: self.identifiers = {'Device': [ ('Configured Video Device', 0), ('Another Video Device', 1) ], 'Screen': [ ('Configured Screen Device', 0), ('Another Screen Device', 1) ], } ''' for sect in self.require_id:#identifiers.keys(): self.identifiers[sect] = [] it = 0 for elem in self._gdict[sect]: try: identifier = self.get_value(sect, 'Identifier', it) except (OptionException, SectionException): #if no identifier can be found error = ('No Identifier for section %s, position %d, ' 'can be found.' % (sect, elem)) raise ParseException(error) try: identifier.append('') identifier = identifier[0] except AttributeError: pass self.identifiers[sect].append((identifier, it)) it += 1 def _validate_options(self): '''One word entries are not acceptable as either options or references If a one word entry is found, ParseException will be raised.''' # Sections in sections_whitelist won't be validated sections_whitelist = ['Files', 'Comments'] options_whitelist = ['endmode'] for section in self.sections: if section not in sections_whitelist: for position in self._gdict[section]: if section == self.subsection:#'SubSection': options = self._gdict[section][position]['options'] else: options = self._gdict[section][position] for option in options: option = option.strip() if option.find('#') != -1:#remove comments option = option[0: option.find('#')] error = ('The following option is invalid: %s' % (option.strip())) optbits = self._clean_duplicates(option, include_null=True) if (len(optbits) == 1 and optbits[0].strip().lower() not in options_whitelist): raise ParseException(error) if not optbits[0][0].isalpha(): raise ParseException(error) def get_duplicate_options(self, section, position): '''See if there are duplicate options in a section It is ok to have duplicated references e.g. several Load options, or Screen, etc. though''' blacklist = ['driver', 'busid', 'identifier'] total = [] duplicates = [] if section == 'SubSection': options = self._gdict[section][position]['options'] else: options = self._gdict[section][position] for option in options: option = option.strip() if option.find('#') != -1:#remove comments option = option[0: option.find('#')] optbits = self._clean_duplicates(option) # optbits may look like this: # # ['Option', 'TestOption1', '0'] # # or # ['Screen', 'My screen 1'] try: if optbits[0].lower() in blacklist: total.append(optbits[0]) elif optbits[0].lower() == 'option': if len(optbits) > 1 and optbits[1] != None: ''' make sure it's not a broken option e.g. Option ''' total.append(optbits[1]) except (AttributeError, IndexError): pass final = {} for option in total: if final.get(option) != None: duplicates.append(option) else: final[option] = option return duplicates def check_duplicate_options(self): '''Look for and return duplicate options in all sections''' duplicates = {} for section in self._gdict: for elem in self._gdict[section]: duplopt = self.get_duplicate_options(section, elem) if len(duplopt) > 0: duplicates.setdefault(section, {}).setdefault(elem, duplopt) return duplicates def _clean_duplicates(self, option, include_null=None): '''Clean the option and return all its components in a list include_null - is used only by _validate_options() and makes sure that options with a null value assigned in quotation marks are not considered as one-word options''' #print '\nCLEAN', repr(option) optbits = [] optbit = '' it = 0 quotation = 0 optcount = option.count('"') if optcount > 0:#dealing with a section option for i in option: #print 'i', repr(i), 'optbit', optbit if not i.isspace(): if i == '"': quotation += 1 else: optbit += i else: if quotation % 2 != 0: optbit += i else: if len(optbit) > 0: optbits.append(optbit) #print 'i=', i, 'optbit=', optbit optbit = '' if it == len(option) - 1: if optbit != '': optbits.append(optbit) #print 'i=END', 'optbit=', optbit it += 1 else:#dealing with a subsection option for i in option: #print 'i', repr(i), 'optbit', optbit if not i.isspace(): optbit += i else: if len(optbit) > 0: optbits.append(optbit) #print 'i=', i, 'optbit=', optbit optbit = '' if it == len(option) - 1: if optbit != '': optbits.append(optbit) #print 'i=END', 'optbit=', optbit else: if include_null: optbit = '' optbits.append(optbit) it += 1 if include_null and len(optbits) != optcount/2 +1: # e.g. if the option looks like the following: # # Modelname "" # # add a '' which wouldn't be caught by this method otherwise. optbit = '' optbits.append(optbit) return optbits def get_duplicate_sections(self): '''Return a dictionary with the duplicate sections i.e. sections of the same kind, with the same identifier''' duplicates = {} for section in self.identifiers: temp = [] for sect in self.identifiers[section]: temp.append(sect[0]) for elem in temp: if temp.count(elem) > 1: duplicates.setdefault(section, {}).setdefault(elem, temp.count(elem)) return duplicates def add_option(self, section, option, value, option_type=None, position=None, reference=None, prefix='"'): '''Add an option to a section section= the section which will have the option added option= the option to add value= the value which will be assigned to the option position= e.g. 0 (i.e. the first element in the list of Screen sections) option_type= if set to "Option" it will cause the option to look like the following: Option "NameOfTheOption" "Value" Otherwise it will look like the following: NameOfTheOption "Value" position= e.g. 0 (i.e. the first element in the list of Screen sections) reference= used only in a particular case of reference (see add_reference) prefix= usually quotation marks are used for the values (e.g. "True") however sometimes they don't have to be used (e.g. DefaultDepth 24) and prefix should be set to '' instead of '"' ''' refSections = ['device'] #prefix = '"'#values are always in quotation marks if position != None: if self._gdict[section].get(position) == None: raise SectionException if reference: # Remove an option if it has a certain assigned value. We want # to do this when removing a reference. self.remove_option(section, option, value=value, position=position) #print 'Remove', option, 'from', section, 'position', position else: # value has to be set to None, however there is no way to do # so other than this since add_option() cannot be called with # value=None. Hence the need for this ugly nested if-block. self.remove_option(section, option, position=position) else: #print 'Remove', option, 'from all', section self.remove_option(section, option) if option_type == None: if reference == None: toadd = ('\t' + option + '\t' + prefix + str(value) + prefix + '\n') else: if section.strip().lower() not in refSections: # e.g. Screen "New Screen" toadd = ('\t' + option + '\t' + prefix + str(value) + prefix + '\n') else: # e.g. Screen 0 # which is used for Xinerama setups in the Device section toadd = '\t' + option + '\t' + str(value) + '\n' else: toadd = ('\t' + option_type + '\t' + '"' + option + '"' + '\t' + prefix + str(value) + prefix + '\n') if len(self._gdict[section]) == 0: self._gdict[section] = {} self._gdict[section][0] = [] if section in self.require_id: identifier = '\tIdentifier\t"Default ' + section + '"\n' self._gdict[section][0].append(identifier) if position == None: for elem in self._gdict[section]: self._gdict[section][elem].append(toadd) else: self._gdict[section][position].append(toadd) def _get_options_to_blacklist(self, section, option, value=None, position=None, reference=None): '''Private method shared by remove_option and comment_out_option''' to_remove = {} if len(self._gdict[section]) != 0:#if the section exists if position == None: #print 'Removing', option, 'from all', section, 'sections' for elem in self._gdict[section]: it = 0 for line in self._gdict[section][elem]: if value != None: #print 'line =', line, 'option=', option, 'value', # value if (line.lower().find(option.lower()) != -1 and line.lower().find(value.lower()) != -1): to_remove.setdefault(elem, []).append(it) else: if line.lower().find(option.lower()) != -1: to_remove.setdefault(elem, []).append(it) it += 1 else: if self._gdict[section].get(position) == None: return else: #print 'Removing', option, 'from', section, 'position', # position it = 0 for line in self._gdict[section][position]: if value != None: # Remove the option only if it has a certain value # assigned. This is useful in case we want to # remove a reference to a certain Section from # another section: # e.g. Screen "Generic Screen". if (line.lower().find(option.lower()) != -1 and line.lower().find(value.lower()) != -1): to_remove.setdefault(position, []).append(it) else: # Remove the option without caring about the # assigned value if line.lower().find(option.lower()) != -1: to_remove.setdefault(position, []).append(it) it += 1 return to_remove def remove_option(self, section, option, value=None, position=None, reference=None): '''Remove an option from a section. section= the section which will have the option removed option= the option to remove value= if you want to remove an option only if it has a certain value position= e.g. 0 (i.e. the first element in the list of Screen sections)''' to_remove = self._get_options_to_blacklist(section, option, value, position, reference) for part in to_remove: modded = 0 for line in to_remove[part]: realpos = line - modded del self._gdict[section][part][realpos] modded += 1 def make_section(self, section, identifier=None): '''Create a new section and return the position of the section The position is relative to the list of sections of the same type (e.g. "Screen") so as to make it available in case the user wants to add some options to it. The identifier and the position of the new section is added to self.identifiers[section] section= the section to create identifier= the identifier of a section (if the section requires an identifier)''' position = len(self._gdict[section]) if section in self.require_id: if identifier != None: option = 'Identifier' # Don't create a new section if one of the same kind and # with the same 'Identifier' is found create = True for sub in self._gdict[section]: if self.get_value(section, option, sub): try: if (self.get_value(section, option, sub).strip().lower() == identifier.strip().lower()): create = False break except AttributeError: for elem in self.get_value(section, option, sub): #print 'elem=', elem, 'id=', identifier if (elem.strip().lower() == identifier.strip().lower()): create = False break if create: self._gdict[section][position] = [] self.add_option(section, option, value=identifier, position=position) # Add to identifiers self.identifiers[section].append((identifier, position)) #print 'Created section', section, 'id =', identifier, # 'position =', position #else: #print section, 'Section labelled as', identifier, #'already exists. None will be created.' else: raise IdentifierException(('%s Section requires an identifier' %(section))) else: self._gdict[section][position] = [] return position def remove_section(self, section, identifier=None, position=None): '''Remove Sections by identifier, position or type''' # Remove any section of "section" type with the same identifier # currently sections of the same type cannot have the same id # for obvious reasons to_remove = {} if identifier: try: pos = self.get_position(section, identifier) to_remove.setdefault(pos, None) except IdentifierException: pass # Comment the section of "section" type at position "position" elif position != None: if self.is_section(section, position=position): to_remove.setdefault(position, None) # Comment any section of "section" type else: allkeys = list(self._gdict[section].keys()) to_remove = {}.fromkeys(allkeys) # If the section has an identifier i.e. if the section # is in self.require_id if section in self.require_id: # Get the references to remove from self.identifiers it = 0 for reference in self.identifiers[section]: try: ref = list(to_remove.keys()).index(reference[1]) to_remove[list(to_remove.keys())[ref]] = it except ValueError: pass it += 1 sorted_remove = list(to_remove.keys()) sorted_remove.sort() modded = 0 for sect in sorted_remove: subsections = self.get_subsections(section, sect) # Remove all its SubSections from SubSection for sub in subsections: try:#remove subsection del self._gdict[self.subsection][sub] except KeyError: pass # Remember to remove any related entry from the "Comments" # section self._remove_comment_entries(section, sect) # Remove the section from _gdict del self._gdict[section][sect] # Remove the reference from identifiers # if such reference exists identref = to_remove[sect] if identref != None: realpos = identref - modded del self.identifiers[section][realpos] modded += 1 def add_reference(self, section, reference, identifier, position=None): '''Add a reference to a section from another section. For example: to put a reference to the Screen section named "Default Screen" in the ServerLayout section you should do: section='ServerLayout' reference='Screen' identifier='Default Screen' position=0 #the first ServerLayout section NOTE: if position is set to None it will add such reference to any instance of the section (e.g. to any ServerLayout section)''' self.add_option(section, reference, value=identifier, position=position, reference=True) def remove_reference(self, section, reference, identifier, position=None): '''Remove a reference to a section from another section. For example: to remove a reference to Screen "Default Screen" from the ServerLayout section you should do: section='ServerLayout' reference='Screen' identifier='Default Screen' position=0 #the first ServerLayout section NOTE: if position is set to None it will remove such reference from any instance of the section (e.g. from any ServerLayout section)''' self.remove_option(section, reference, value=identifier, position=position, reference=True) def get_references(self, section, position, reflist=None): '''Get references to other sections which are located in a section. section= the section (e.g. "Screen") position= e.g. 0 stands for the 1st Screen section reflist= a list of references which this function should look for. The default list of references is self.require_id but this list can be overridden by the reflist argument so that, for example, if reflist is set to ['Device'], this function will look for references to other devices only (references to, say, screens, will be ignored).''' if reflist == None: options = self.require_id else: # if the following operation fails # an AttributeError will be raised # since reflist must be a list reflist.append('') del reflist[-1] options = reflist references = {}.fromkeys(options) for option in options: references[option] = [] reference_dict = {} try: ref = self.get_value(section, option, position, reference=True) except OptionException: ref = [] if ref: try:#if ref is already a list ref.append('') del ref[-1] for elem in ref: try: elem.append('') del elem[-1] for extref in elem: if elem: reference_dict.setdefault(extref) except AttributeError:# if ref is a string if elem: reference_dict.setdefault(elem) except AttributeError:# if ref is a string if ref: reference_dict.setdefault(ref) for reference in list(reference_dict.keys()): references[option].append(reference) return references def make_subsection(self, section, identifier, position=None): '''Create a new subsection inside of a section. section= the section to which the subsection will belong identifier= the name of the subsection position= the position of the section in the dictionary with the sections (e.g. the 1st "Screen" section would be 0). If set to None, it will create a new subsection in all the instances of the said section (e.g. in all the "Screen" sections)''' curlength = len(self._gdict[self.subsection]) if position == None: for elem in self._gdict[section]: # don't create a new subsection if one with the same # 'section', 'identifier' and 'position' is found create = True for sub in self._gdict[self.subsection]: if (self._gdict[self.subsection][sub].get('section') == section and self._gdict[self.subsection][sub].get('identifier') == identifier and self._gdict[self.subsection][sub].get('position') == elem): create = False if create: temp_dict = self._gdict[self.subsection][curlength] = {} temp_dict['section'] = section temp_dict['identifier'] = identifier temp_dict['options'] = [] temp_dict['position'] = elem del temp_dict curlength += 1 else: # don't create a new subsection if one with the same # 'section', 'identifier' and 'position' is found create = True for sub in self._gdict[self.subsection]: if (self._gdict[self.subsection][sub].get('section') == section and self._gdict[self.subsection][sub].get('identifier') == identifier and self._gdict[self.subsection][sub].get('position') == position): create = False if create: temp_dict = self._gdict[self.subsection][curlength] = {} temp_dict['section'] = section temp_dict['identifier'] = identifier temp_dict['options'] = [] temp_dict['position'] = position del temp_dict def remove_subsection(self, section, identifier, position=None): '''Remove a subsection from one or more sections. section= the section to which the subsection belongs identifier= the name of the subsection position= the position of the section in the dictionary with the sections (e.g. the 1st "Screen" section would be 0). If set to None it will remove a subsection from all the instances of the said section (e.g. in all the "Screen" sections)''' curlength = len(self._gdict[self.subsection]) to_remove = [] if position == None: for elem in self._gdict[self.subsection]: if (self._gdict[self.subsection][elem].get('section') == section and self._gdict[self.subsection][elem].get('identifier') == identifier): to_remove.append(elem) else: for elem in self._gdict[self.subsection]: if (self._gdict[self.subsection][elem].get('section') == section and self._gdict[self.subsection][elem].get('identifier') == identifier and self._gdict[self.subsection][elem].get('position') == position): to_remove.append(elem) for item in to_remove: del self._gdict[self.subsection][item] def add_suboption(self, section, identifier, option, value, option_type=None, position=None): '''Add an option to one or more subsections. section= the section which contains the subsection identifier= the identifier of the SubSection (e.g. Display) option= the option to add value= the value which will be assigned to the option option_type= if set to "Option" it will cause the option to look like the following: Option "NameOfTheOption" "Value" Otherwise it will look like the following: NameOfTheOption "Value" position= e.g. 0 (i.e. the option will be added to a subsection which is located in the first element in the list of Screen sections)''' prefix = '"' not_to_create = [] to_modify = [] if position == None: self.remove_suboption(section, identifier, option) else: self.remove_suboption(section, identifier, option, position=position) if option_type == None: toadd = '\t' + option + '\t' + str(value) + '\n' else: toadd = ('\t' + option_type + '\t' + prefix + option + prefix + '\t' + prefix + str(value) + prefix + '\n') curlength = len(self._gdict[self.subsection]) if curlength == 0: self._gdict[self.subsection][0] = {'section': section, 'identifier': identifier, 'options': []} if position == None: # if there is not a subsection for each selected section then # create it cursect_length = len(self._gdict[section]) it = 0 while it < cursect_length: for elem in self._gdict[self.subsection]: if (self._gdict[self.subsection][elem].get("position") == it and self._gdict[self.subsection][elem].get("section") == section and self._gdict[self.subsection][elem].get("identifier") == identifier): not_to_create.append(it) it += 1 for i in range(cursect_length + 1): if i not in not_to_create: self.make_subsection(section, identifier, position=i) for elem in self._gdict[self.subsection]: if (self._gdict[self.subsection][elem].get("identifier") == identifier and self._gdict[self.subsection][elem].get("section") == section): to_modify.append(elem) else: for elem in self._gdict[self.subsection]: if (self._gdict[self.subsection][elem].get("position") == position and self._gdict[self.subsection][elem].get("identifier") == identifier): to_modify.append(elem) if len(to_modify) == 0: curlength = len(self._gdict[self.subsection]) self._gdict[self.subsection][ len(self._gdict[self.subsection])] = \ {'section': section, 'identifier': identifier, 'options': [], 'position': position} to_modify.append(curlength) for elem in to_modify: self._gdict[self.subsection][elem]['options'].append(toadd) def _get_suboptions_to_blacklist(self, section, identifier, option, position=None): '''Get a dictionay of the suboptions to blacklist. See add_suboption() for an explanation on the arguments. Used in both remove_option() and remove_suboption()''' to_remove = {} if len(self._gdict[section]) != 0:#if the section exists if len(self._gdict[self.subsection]) != 0: for elem in self._gdict[self.subsection]: temp_elem = self._gdict[self.subsection][elem] if position == None: if (temp_elem.get('section') == section and temp_elem.get('identifier') == identifier): it = 0 for opt in temp_elem['options']: if (opt.strip().lower() .find(option.strip().lower()) != -1): to_remove.setdefault(elem, []).append(it) it += 1 else: if (temp_elem.get('section') == section and temp_elem.get('identifier') == identifier and temp_elem.get('position') == position): it = 0 for opt in temp_elem['options']: if (opt.strip().lower() .find(option.strip().lower()) != -1): to_remove.setdefault(elem, []).append(it) it += 1 del temp_elem return to_remove def remove_suboption(self, section, identifier, option, position=None): '''Remove an option from a subsection.''' to_remove = self._get_suboptions_to_blacklist(section, identifier, option, position) for elem in to_remove: modded = 0 for part in to_remove[elem]: real_pos = part - modded del self._gdict[self.subsection][elem]['options'][real_pos] modded += 1 def get_identifier(self, section, position): '''Get the identifier of a specific section from its position.''' error_msg = ('No identifier can be found for section "%s" No %d' % (section, position)) try: for sect in self.identifiers[section]: if sect[1] == position: return sect[0] except KeyError: raise SectionException raise IdentifierException(error_msg) def _clean_option(self, option, optname, reference=None, section=None): '''Clean the option and return the value This returns the last item of the list which this method generates. If no value can be found, return False.''' if reference: # If it's a reference to another section then options such as # Option "Device" "/dev/psaux" should not be taken into # account. if 'option' in option.strip().lower(): return False # Do not confuse Device "Configure device" with InputDevice # "device" if not option.strip().lower().startswith(optname.strip().lower()): return False optbits = [] optbit = '' it = 0 quotation = 0 optcount = option.count('"') if optcount > 0:#dealing with a section option for i in option: if optcount in [2, 4] and section == 'ServerLayout': if not i.isspace(): if i == '"': if quotation != 0 and quotation % 2 != 0: if len(optbit) > 0: optbits.append(optbit) optbit = '' quotation += 1 else: if quotation % 2 != 0: optbit += i else: if quotation % 2 != 0: optbit += i else: #print 'i', repr(i), 'optbit', optbit if not i.isspace(): if i == '"': quotation += 1 else: optbit += i else: if quotation % 2 != 0: optbit += i else: if len(optbit) > 0: optbits.append(optbit) #print 'i=', i, 'optbit=', optbit optbit = '' if it == len(option) - 1: if optbit != '': optbits.append(optbit) #print 'i=END', 'optbit=', optbit it += 1 else:#dealing with a subsection option for i in option: #print 'i', repr(i), 'optbit', optbit if not i.isspace(): optbit += i else: if len(optbit) > 0: optbits.append(optbit) #print 'i=', i, 'optbit=', optbit optbit = '' if it == len(option) - 1: if optbit != '': optbits.append(optbit) #print 'i=END', 'optbit=', optbit it += 1 optlen = len(optbits) if optlen > 1: # Let's make sure that the option is the one we're looking for # e.g. if we're looking for a reference to Device we are not # interested in getting references to InputDevice references_list = [x.lower().strip() for x in self.references] if (section != 'ServerLayout' and quotation == 0 and optlen == 2 and optbits[0].lower().strip() in references_list): # e.g. Screen 1 -> 1 stands for the position, therefore the # identifier of the section at position 1 should be returned # instead of the number (if possible). # # return [Screen, identifier] try: sect = '' value = int(optbits[1].strip()) for item in self.require_id: if optbits[0].lower().strip() == item.lower().strip(): sect = item break try: identifier = self.get_identifier(sect, value) return [identifier] except (IdentifierException): return False except ValueError: pass if optcount != 4 and section != 'ServerLayout': status = False for elem in optbits: if elem.lower() == optname.lower(): status = True if status == False: return False if optlen == 2 and optbits[0].lower().strip() == 'option': # e.g. Option "AddARGBGLXVisuals" # (The value was omitted but it will be interpreted as True by # Xorg) return 'True' sections = [sect.strip().lower() for sect in self.sections] # if optlen == 2 and optbits[0].lower().strip() in sections: # # Do not confuse Device "Configure device" with InputDevice # # "device" # if optbits[0].lower().strip() != optname.strip().lower(): # return False if optcount == 4 and section == 'ServerLayout': #If it's something like InputDevice "stylus" "SendCoreEvents" if (optname.lower().strip() == 'inputdevice' and len(optbits) == 2): del optbits[1] server_dict = {} for elem in optbits: server_dict.setdefault(elem) return list(server_dict.keys()) elif optcount > 0 and optcount <= 4: #dealing with a section option return optbits[optlen -1] elif optcount > 4: del optbits[0] return optbits elif optcount == 0: del optbits[0] return ' '.join(optbits) else: if optcount in [2, 4] and section == 'ServerLayout': return optbits return False def get_value(self, section, option, position, identifier=None, sect=None, reference=None): '''Get the value which is assigned to an option. Return types: * string (if only one value is available) - usually in options * list (if more than one option is found) - having multiple references of the same type is allowed. for example it is not unusual to have 2 references to Screen sections in the ServerLayout section (in case of Xinerama) - if the options are actually options and not references then there are duplicate options, which should be detected in advance with get_duplicate_options() * None (if no value can be found) - Not always true -> See below. Use-case for returning None * When dealing with incomplete references. For example: Screen "Configured Screen" is different from: Screen (which is an incomplete reference) * When dealing with incomplete options. For example: Depth 24 is different from: Depth (which is an incomplete option) * Exception: Some options (with the "Option" prefix) (not references) can be used with no value (explicitly) assigned and are considered as True by the Xserver. In such case get_value() will return "True". For example: Option "AddARGBGLXVisuals" is the same as: Option "AddARGBGLXVisuals" "True" Meaning of keys in Sections and SubSections: * When dealing with a Section: section= e.g. 'Screen', 'Device', etc. option= the option position= e.g. 0 (i.e. the first element in the list of Screen sections) reference= used only by get_references() * When dealing with a SubSection: section= 'SubSection' (this is mandatory) option= the option position= e.g. 0 would mean that the subsection belongs to the 1st item of the list of, say, "Screen" sections. (i.e. the first element in the list of Screen sections) ["position" is a key of an item of the list of subsections see below] identifier= the name of the subsection e.g. 'Display' sect = the 'section' key of an item of the list of subsections e.g. the "Display" subsection can be found in the "Screen" section ('sect' is the latter) Anatomy of Sections and SubSections: * Anatomy of subsections: self.globaldict['SubSection'] = {0: {'section': 'Screen', 'identifier': 'Display', 'position': 0, 'options': [option1, option2, etc.], etc.} In this case we refer to the 'Display' subsection which is located in the first 'Screen' section. * Anatomy of a section: self.globaldict['Screen'] = {0: [option1, option2, etc.], 1: [...], ...} 0, 1, etc. is the position ''' values = [] if self._gdict[section].get(position) == None: raise SectionException #if len(values) == 0: #raise OptionException #return values else: try: # see if it's a dictionary (e.g. in case of a subsection) # or a list (in case of a normal section) and act # accordingly self._gdict[section][position].index('foo') except AttributeError:#dict if identifier == None: raise Exception('An identifier is required for ' 'subsections') else: for elem in self._gdict[section]: if (self._gdict[section][elem].get('identifier') == identifier and self._gdict[section][elem].get('position') == position and self._gdict[section][elem].get('section') == sect): for opt in self._gdict[section][elem]['options']: if (option.strip().lower() in opt.strip().lower()): if opt.strip().find('#') != -1: stropt = opt.strip()[0: opt .strip().find('#')] else: stropt = opt.strip() # clean the option and return the value values.append(self._clean_option(stropt, option, reference=reference)) if len(values) == 0: raise OptionException if len(values) > 1: return values else: try: return values[0] except IndexError: return None except ValueError:#list for elem in self._gdict[section][position]: if option.strip().lower() in elem.strip().lower(): # clean the option and return the value if elem.strip().find('#') != -1: stropt = elem.strip()[0: elem.strip().find('#')] else: stropt = elem.strip() values.append(self._clean_option(stropt, option, reference=reference, section=section)) if len(values) == 0: raise OptionException if len(values) > 1: return values else: try: return values[0] except IndexError: return None except KeyError:#not found raise OptionException def is_section(self, section, identifier=None, position=None): '''See if a section with a certain identifier exists. NOTE: either identifier or position must be provided.''' if identifier != None: try: self.get_position(section, identifier) return True except IdentifierException: return False elif position != None: return self._gdict[section].get(position) != None else: error_msg = 'Either identifier or position must be provided' raise Exception(error_msg) def get_position(self, section, identifier): '''Get the position of a specific section from its identifier.''' error_msg = ('No %s section named "%s" can be found' % (section, identifier)) for sect in self.identifiers[section]: try: if sect[0].strip().lower() == identifier.strip().lower(): return sect[1] except AttributeError: pass raise IdentifierException(error_msg) def get_broken_references(self): '''Look for references to sections which don't exist This returns a dictionary having the items of self.require_id as keys and a dictionary with the identifiers of the sections which are being referred to by the broken references. For example: broken_references = { 'InputDevice': {'InputDevice 1': None, 'Another input device': None}, 'Device': {...}, 'Monitor' {...}, 'Screen' {...}, 'ServerLayout' {...} }''' broken_references = {}.fromkeys(self.require_id) references_tree = {} for section in self.require_id:#['Screen', 'ServerLayout'] references_tree[section] = {} broken_references[section] = {} for sect in self._gdict[section]: references_tree[section][sect] = self.get_references(section, sect) #print >> stderr, 'REFERENCES = %s' % (str(references_tree)) for section in references_tree: for elem in references_tree[section]: for refsect in references_tree[section][elem]: if len(references_tree[section][elem][refsect]) > 0: #references_tree[section][elem][refsect] for ref in references_tree[section][elem][refsect]: for sect in self.sections: if sect.lower() == refsect.strip().lower(): refsect = sect if not self.is_section(refsect, ref): #print '*****WARNING:', refsect, 'Section', # ref, 'does not exist!*****' broken_references[refsect].setdefault(ref) #print 'FIX: Creating', refsect, 'Section', # ref self.make_section(refsect, # identifier=ref) return broken_references def get_default_serverlayout(self): '''Return a list with the position of default ServerLayout sections NOTE: If the section set as the default ServerLayout doesn't exist it will raise a ParseException.''' default = [] serverflags = self._gdict['ServerFlags'] it = 0 for flag in serverflags: try: default_layout = self.get_value('ServerFlags', 'DefaultServerLayout', it) if default_layout: def_it = 0 for identifier in self.identifiers['ServerLayout']: if (identifier[0].lower().strip() == default_layout.lower().strip()): default.append(identifier[1])#LayoutPosition def_it += 1 if def_it == 0: # If the section set as the default ServerLayout # doesn't exist raise a ParseException error = 'The default ServerLayout does not exist.' raise ParseException(error) except OptionException:#no default_layout pass it += 1 return default def _merge_subsections(self, temp_dict): '''Put SubSections back into the sections to which they belong.''' for sect in temp_dict['SubSection']: section = temp_dict['SubSection'][sect]['section'] identifier = temp_dict['SubSection'][sect]['identifier'] position = temp_dict['SubSection'][sect].get('position') options = temp_dict['SubSection'][sect]['options'] temp_dict[section].setdefault(position, []).append( '\tSubSection ' + '"' + identifier + '"' + '\n') if len(options) > 0: temp_dict[section][position].append('\t' + '\t'.join(options) + '\tEndSubSection\n') else: temp_dict[section][position].append('\t'.join(options) + '\tEndSubSection\n') try: #remove subsection since it was merged del temp_dict['SubSection'] except KeyError: pass return temp_dict def write(self, destination, test=None): '''Write the changes to the destination The destination can be either a file (e.g. /etc/X11/xorg.conf) or a file object (e.g. sys.stdout). destination = the destination file or file object (mandatory) test = if set to True write will append the result to the destination file instead of overwriting it. It has no effect on file objects. Useful for testing.''' temp_dict = copy.deepcopy(self._gdict) # Commented options must be dealt with first temp_dict = self._merge_commented_options(temp_dict) # Merge all the non-commented subsections temp_dict = self._merge_subsections(temp_dict) lines = [] comments = ''.join(self.comments) + '\n' lines.append(comments) for section in temp_dict: if section != self.commentsection: if len(temp_dict[section]) > 0: for elem in temp_dict[section]: lines.append('Section ' + '"' + section + '"' + '\n') lines.append(''.join(temp_dict[section][elem]) + 'EndSection\n\n') del temp_dict if not hasattr(destination, 'write'):#it is a file if test: destination = open(destination, 'a') else: destination = open(destination, 'w') destination.write(''.join(lines)) destination.close() else:#it is a file object try: destination.write(str(bytes(''.join(lines), 'UTF-8'))) except TypeError: destination.write(b''.join(lines)) def get_subsections(self, section, position): '''Get all the subsections contained in a section''' # loop through subsections and see what subsections match # the section subsections = [] for sub in self._gdict[self.subsection]: if (self._gdict[self.subsection][sub]['section'] == section and self._gdict[self.subsection][sub]['position'] == position): subsections.append(sub) return subsections def _permanent_merge_subsections(self, subsections): '''Put SubSections back into their sections and comment them out This alters globaldict and should be used only in comment_out_section() i.e. when the whole section is being commented out. subsections = the list of the indices subsections to merge and remove''' for sect in subsections: section = self._gdict[self.subsection][sect]['section'] identifier = self._gdict[self.subsection][sect]['identifier'] position = self._gdict[self.subsection][sect].get('position') options = self._gdict[self.subsection][sect]['options'] self.comments.append('#\tSubSection ' + '"' + identifier + '"' + '\n') for option in options: opt = '#\t\t%s\n' % (option.strip()) self.comments.append(opt) self.comments.append('#\tEndSubSection\n') try:#remove subsection since it was merged del self._gdict[self.subsection][sect] except KeyError: pass def _get_comments(self, section, position): '''Return the index of the entry in the Comments section of a section''' comments = [] if self._gdict[self.commentsection].get(section): for sect in self._gdict[self.commentsection][section]: if (self._gdict[self.commentsection][section][sect] .get('position') == position): comments.append(sect) return comments def _merge_subsections_with_comments(self, subsections): '''Put SubSections back into their sections and comment them out This alters globaldict and should be used only to comment out subsections (i.e. in comment_out_subsection()) when the whole section is not being commented out. subsections = the list of the indices subsections to merge and remove''' end_subsection = '#\tEndSubSection\n' for sect in subsections: section = self._gdict[self.subsection][sect]['section'] identifier = self._gdict[self.subsection][sect]['identifier'] position = self._gdict[self.subsection][sect].get('position') options = self._gdict[self.subsection][sect]['options'] start_subsection = '#\tSubSection "%s"\n' % (identifier) comments = self._get_comments(section, position) if not comments: self._gdict[self.commentsection][section] = {} self._gdict[self.commentsection][section][position] = {} temp_dict = self._gdict[self.commentsection][section][position] temp_dict['identifier'] = None temp_dict['position'] = position temp_dict['section'] = None temp_dict['options'] = [] del temp_dict comments_options = self._gdict[self.commentsection][section ][position]['options'] comments_options.append(start_subsection) for option in options: opt = '#\t\t%s\n' % (option.strip()) comments_options.append(opt) comments_options.append(end_subsection) #remove subsection since it was merged del self._gdict[self.subsection][sect] def _comment_out_subsections(self, section, position): '''Comment out all the subsections of a section.''' subsections = self.get_subsections(section, position) self._permanent_merge_subsections(subsections) def _remove_comment_entries(self, section, position): '''Remove comment sections of a "section" from the "Comments" section''' comments = self._get_comments(section, position) for comment_section in comments: del self._gdict[self.commentsection][section][comment_section] def comment_out_section(self, section, identifier=None, position=None): '''Comment out a section and all its subsections.''' start_section = '\n#Section "%s"\n' % (section) end_section = '#EndSection\n' # Comment any section of "section" type with the same identifier # currently sections of the same type cannot have the same id # for obvious reasons to_remove = {} if identifier: try: pos = self.get_position(section, identifier) to_remove.setdefault(pos, None) except IdentifierException: pass # Comment the section of "section" type at position "position" elif position != None: if self.is_section(section, position=position): to_remove.setdefault(position, None) # Comment any section of "section" type else: all_keys = list(self._gdict[section].keys()) to_remove = {}.fromkeys(all_keys) # If the section has an identifier i.e. if the section # is in self.require_id if section in self.require_id: # Get the references to remove from self.identifiers it = 0 for reference in self.identifiers[section]: try: ref = list(to_remove.keys()).index(reference[1]) to_remove[list(to_remove.keys())[ref]] = it except ValueError: pass it += 1 sorted_remove = list(to_remove.keys()) sorted_remove.sort() modded = 0 for sect in sorted_remove: self.comments.append(start_section) for option in self._gdict[section][sect]: commented_option = '#\t%s\n' % (option.strip()) self.comments.append(commented_option) # Append all its SubSections (automatically commented # out) and remove them from SubSection self._comment_out_subsections(section, sect) self.comments.append(end_section) # Remember to remove any related entry from the "Comments" # section self._remove_comment_entries(section, sect) # Remove the section from _gdict del self._gdict[section][sect] # Remove the reference from identifiers # if such reference exists ident_ref = to_remove[sect] if ident_ref != None: realpos = ident_ref - modded del self.identifiers[section][realpos] modded += 1 def comment_out_subsection(self, section, identifier, position): '''Comment out a subsection. section= the type of the section which contains the subsection identifier= the identifier of the subsection position= the position of the section''' subsections = [] for subsection in self._gdict[self.subsection]: temp_dict = self._gdict[self.subsection][subsection] if (temp_dict['section'] == section and temp_dict['identifier'] == identifier and temp_dict['position'] == position): subsections.append(subsection) break del temp_dict # Add the subsection to the Comments section self._merge_subsections_with_comments(subsections) def comment_out_option(self, section, option, value=None, position=None, reference=None): '''Comment out an option in a section. section= the section which will have the option commented out option= the option to comment out value= if you want to comment out an option only if it has a certain value position= e.g. 0 (i.e. the first element in the list of Screen sections)''' to_remove = self._get_options_to_blacklist(section, option, value, position, reference) for part in to_remove: modded = 0 for line in to_remove[part]: realpos = line - modded self._gdict[section][part][realpos] = ('#%s' % (self._gdict[section][part][realpos].strip())) self._gdict[self.commentsection].setdefault(section, {}) curlength = len(self._gdict[self.commentsection][section]) temp_dict = self._gdict[self.commentsection][section] temp_dict.setdefault(part, {}) temp_dict[part].setdefault('identifier', None) temp_dict[part].setdefault('position', part) temp_dict[part].setdefault('section', None) temp_dict[part].setdefault('options', []) # Copy the option to the Comments section temp_dict[part]['options'].append( self._gdict[section][part][realpos]) del temp_dict #Remove it from its section in _gdict del self._gdict[section][part][realpos] modded += 1 def comment_out_suboption(self, section, identifier, option, position=None): '''Comment out an option in a subsection. section= the section which contains the subsection identifier= the identifier of the subsection option= the option to comment out position= the position of the section which contains the subsection e.g. 0 (i.e. the first element in the list of Screen sections)''' to_remove = self._get_suboptions_to_blacklist(section, identifier, option, position) for elem in to_remove: modded = 0 for part in to_remove[elem]: realpos = part - modded self._gdict[self.subsection][part]['options'][realpos] = ('#%s' % (self._gdict[self.subsection][part]['options'][realpos] .strip())) self._gdict[self.commentsection].setdefault(self.subsection, {}) temp_dict = self._gdict[self.commentsection][self.subsection] temp_dict.setdefault(part, {}) temp_dict[part].setdefault('identifier', identifier) temp_dict[part].setdefault('position', part) temp_dict[part].setdefault('section', section) temp_dict[part].setdefault('options', []) # Copy the option to the Comments section comments_options = temp_dict[part]['options'] commented_option = self._gdict[self.subsection][part][ 'options'][realpos] comments_options.append(commented_option) del temp_dict #Remove the option from its section in _gdict del self._gdict[self.subsection][elem]['options'][realpos] modded += 1 def _merge_commented_options(self, temp_dict): '''Put commented out options back into their sections or subsections''' for sect in temp_dict[self.commentsection]: section_options = None for section_instance in temp_dict[self.commentsection][sect]: section = temp_dict[self.commentsection][sect][ section_instance].get('section') identifier = temp_dict[self.commentsection][sect][ section_instance].get('identifier') position = temp_dict[self.commentsection][sect][ section_instance].get('position') options = temp_dict[self.commentsection][sect][ section_instance]['options'] if section == self.subsection: for sub in temp_dict[sect]: subsection = temp_dict[sect][sub] if (subsection['identifier'] == identifier and subsection['position'] == position and subsection['section'] == section): section_options = temp_dict[sect][sub]['options'] break else: section_options = temp_dict[sect].get(position) if section_options: for option in options: option = '\t%s\n' % (option.strip()) if sect == self.subsection: section_options.setdefault('options', []).append(option) else: section_options.append(option) return temp_dict