126 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """EditorConfig file handler
 | |
| 
 | |
| Provides ``EditorConfigHandler`` class for locating and parsing
 | |
| EditorConfig files relevant to a given filepath.
 | |
| 
 | |
| Licensed under PSF License (see LICENSE.txt file).
 | |
| 
 | |
| """
 | |
| 
 | |
| import os
 | |
| 
 | |
| from editorconfig import VERSION
 | |
| from editorconfig.ini import EditorConfigParser
 | |
| from editorconfig.exceptions import PathError, VersionError
 | |
| 
 | |
| 
 | |
| __all__ = ['EditorConfigHandler']
 | |
| 
 | |
| 
 | |
| def get_filenames(path, filename):
 | |
|     """Yield full filepath for filename in each directory in and above path"""
 | |
|     path_list = []
 | |
|     while True:
 | |
|         path_list.append(os.path.join(path, filename))
 | |
|         newpath = os.path.dirname(path)
 | |
|         if path == newpath:
 | |
|             break
 | |
|         path = newpath
 | |
|     return path_list
 | |
| 
 | |
| 
 | |
| class EditorConfigHandler(object):
 | |
| 
 | |
|     """
 | |
|     Allows locating and parsing of EditorConfig files for given filename
 | |
| 
 | |
|     In addition to the constructor a single public method is provided,
 | |
|     ``get_configurations`` which returns the EditorConfig options for
 | |
|     the ``filepath`` specified to the constructor.
 | |
| 
 | |
|     """
 | |
| 
 | |
|     def __init__(self, filepath, conf_filename='.editorconfig', version=None):
 | |
|         """Create EditorConfigHandler for matching given filepath"""
 | |
|         self.filepath = filepath
 | |
|         self.conf_filename = conf_filename
 | |
|         self.version = version
 | |
|         self.options = None
 | |
| 
 | |
|     def get_configurations(self):
 | |
| 
 | |
|         """
 | |
|         Find EditorConfig files and return all options matching filepath
 | |
| 
 | |
|         Special exceptions that may be raised by this function include:
 | |
| 
 | |
|         - ``VersionError``: self.version is invalid EditorConfig version
 | |
|         - ``PathError``: self.filepath is not a valid absolute filepath
 | |
|         - ``ParsingError``: improperly formatted EditorConfig file found
 | |
| 
 | |
|         """
 | |
| 
 | |
|         self.check_assertions()
 | |
|         path, filename = os.path.split(self.filepath)
 | |
|         conf_files = get_filenames(path, self.conf_filename)
 | |
| 
 | |
|         # Attempt to find and parse every EditorConfig file in filetree
 | |
|         for filename in conf_files:
 | |
|             parser = EditorConfigParser(self.filepath)
 | |
|             parser.read(filename)
 | |
| 
 | |
|             # Merge new EditorConfig file's options into current options
 | |
|             old_options = self.options
 | |
|             self.options = parser.options
 | |
|             if old_options:
 | |
|                 self.options.update(old_options)
 | |
| 
 | |
|             # Stop parsing if parsed file has a ``root = true`` option
 | |
|             if parser.root_file:
 | |
|                 break
 | |
| 
 | |
|         self.preprocess_values()
 | |
|         return self.options
 | |
| 
 | |
|     def check_assertions(self):
 | |
| 
 | |
|         """Raise error if filepath or version have invalid values"""
 | |
| 
 | |
|         # Raise ``PathError`` if filepath isn't an absolute path
 | |
|         if not os.path.isabs(self.filepath):
 | |
|             raise PathError("Input file must be a full path name.")
 | |
| 
 | |
|         # Raise ``VersionError`` if version specified is greater than current
 | |
|         if self.version is not None and self.version[:3] > VERSION[:3]:
 | |
|             raise VersionError(
 | |
|                     "Required version is greater than the current version.")
 | |
| 
 | |
|     def preprocess_values(self):
 | |
| 
 | |
|         """Preprocess option values for consumption by plugins"""
 | |
| 
 | |
|         opts = self.options
 | |
| 
 | |
|         # Lowercase option value for certain options
 | |
|         for name in ["end_of_line", "indent_style", "indent_size",
 | |
|             "insert_final_newline", "trim_trailing_whitespace", "charset"]:
 | |
|             if name in opts:
 | |
|                 opts[name] = opts[name].lower()
 | |
| 
 | |
|         # Set indent_size to "tab" if indent_size is unspecified and
 | |
|         # indent_style is set to "tab".
 | |
|         if (opts.get("indent_style") == "tab" and
 | |
|             not "indent_size" in opts and self.version >= VERSION[:3]):
 | |
|             opts["indent_size"] = "tab"
 | |
| 
 | |
|         # Set tab_width to indent_size if indent_size is specified and
 | |
|         # tab_width is unspecified
 | |
|         if ("indent_size" in opts and "tab_width" not in opts and
 | |
|             opts["indent_size"] != "tab"):
 | |
|             opts["tab_width"] = opts["indent_size"]
 | |
| 
 | |
|         # Set indent_size to tab_width if indent_size is "tab"
 | |
|         if ("indent_size" in opts and "tab_width" in opts and
 | |
|             opts["indent_size"] == "tab"):
 | |
|             opts["indent_size"] = opts["tab_width"]
 |