# 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