Thu, 01 Feb 2018 19:26:11 +0100
Started implementing support for EditorConfig.
--- a/QScintilla/Editor.py Thu Feb 01 19:25:47 2018 +0100 +++ b/QScintilla/Editor.py Thu Feb 01 19:26:11 2018 +0100 @@ -39,6 +39,8 @@ import UI.PixmapCache +from ThirdParty.EditorConfig import editorconfig + EditorAutoCompletionListID = 1 TemplateCompletionListID = 2 @@ -192,11 +194,13 @@ self.notcoveredMarkers = [] # just a list of marker handles self.showingNotcoveredMarkers = False + self.__loadEditorConfig() + self.condHistory = [] self.lexer_ = None self.__lexerReset = False self.completer = None - self.encoding = Preferences.getEditor("DefaultEncoding") + self.encoding = self.__getEditorConfig("DefaultEncoding") self.apiLanguage = '' self.lastModified = 0 self.line = -1 @@ -2960,6 +2964,9 @@ if createIt and not os.path.exists(fn): f = open(fn, "w") f.close() + if encoding == "": + encoding = self.__getEditorConfig("DefaultEncoding", + nodefault=True) if encoding: txt, self.encoding = Utilities.readEncodedFileWithEncoding( fn, encoding) @@ -2978,6 +2985,7 @@ fileEol = self.detectEolString(txt) modified = False + # TODO: editorconfig: indent_style if (not Preferences.getEditor("TabForIndentation")) and \ Preferences.getEditor("ConvertTabsOnLoad") and \ not (self.lexer_ and @@ -2994,7 +3002,9 @@ self.__processFlags() # perform automatic eol conversion - if Preferences.getEditor("AutomaticEOLConversion"): + # TODO: editorconfig: end_of_line + if self.__getEditorConfig("EOLMode", nodefault=True) or \ + Preferences.getEditor("AutomaticEOLConversion"): self.convertEols(self.eolMode()) else: self.setEolModeByEolString(fileEol) @@ -3027,12 +3037,14 @@ @param backup flag indicating to save a backup (boolean) @return flag indicating success (boolean) """ + # TODO: editorconfig: trim_trailing_whitespace if Preferences.getEditor("StripTrailingWhitespace"): self.__removeTrailingWhitespace() txt = self.text() # work around glitch in scintilla: always make sure, # that the last line is terminated properly + # TODO: editorconfig: insert_final_newline eol = self.getLineSeparator() if eol: if len(txt) >= len(eol): @@ -3066,7 +3078,10 @@ # now write text to the file fn try: - self.encoding = Utilities.writeEncodedFile(fn, txt, self.encoding) + editorConfigEncoding = self.__getEditorConfig( + "DefaultEncoding", nodefault=True) + self.encoding = Utilities.writeEncodedFile( + fn, txt, self.encoding, forcedEncoding=editorConfigEncoding) if createBackup and perms_valid: os.chmod(fn, permissions) return True @@ -3186,11 +3201,13 @@ # save to project, if a project is loaded if self.project.isOpen() and \ self.project.startswithProjectPath(fn): + # TODO: check against editorconfig self.setEolModeByEolString(self.project.getEolString()) self.convertEols(self.eolMode()) else: fn = self.fileName + self.__loadEditorConfig(fn) self.editorAboutToBeSaved.emit(self.fileName) if self.writeFile(fn): if saveas: @@ -3253,6 +3270,8 @@ self.fileName = fn self.setWindowTitle(self.fileName) + self.__loadEditorConfig() + if self.lexer_ is None: self.setLanguage(self.fileName) @@ -4317,8 +4336,11 @@ """ Private method to configure the text display. """ + # TODO: editorconfig: tab_width self.setTabWidth(Preferences.getEditor("TabWidth")) + # TODO: editorconfig: indent_size self.setIndentationWidth(Preferences.getEditor("IndentWidth")) + # TODO: editorconfig: indent_style if self.lexer_ and self.lexer_.alwaysKeepTabs(): self.setIndentationsUseTabs(True) else: @@ -4440,9 +4462,13 @@ if self.fileName and \ self.project.isOpen() and \ self.project.isProjectFile(self.fileName): - self.setEolModeByEolString(self.project.getEolString()) - else: - eolMode = Preferences.getEditor("EOLMode") + # TODO: editorconfig: end_of_line + eolMode = self.__getEditorConfig("EOLMode") + if eolMode is None: + eolMode = self.project.getEolString() + self.setEolModeByEolString(eolMode) + else: + eolMode = self.__getEditorConfig("EOLMode") eolMode = QsciScintilla.EolMode(eolMode) self.setEolMode(eolMode) self.__eolChanged() @@ -8016,3 +8042,78 @@ """ txt = self.selectedText() e5App().getObject("Shell").executeLines(txt) + + ####################################################################### + ## Methods implementing the interface to EditorConfig + ####################################################################### + + def __loadEditorConfig(self, fileName=""): + """ + Private method to load the EditorConfig properties. + + @param fileName name of the file + @type str + """ + if not fileName: + fileName = self.fileName + + if fileName: + try: + self.__editorConfig = \ + editorconfig.get_properties(fileName) + except editorconfig.EditorConfigError: + E5MessageBox.warning( + self, + self.tr("EditorConfig Properties"), + self.tr("""<p>The EditorConfig properties for file""" + """ <b>{0}</b> could not be loaded.</p>""") + .format(self.fileName)) + self.__editorConfig = {} + else: + self.__editorConfig = {} + + def __getEditorConfig(self, option, nodefault=False): + """ + Private method to get the requested option via EditorConfig. + + If there is no EditorConfig defined, the equivalent built-in option + will be used (Preferences.getEditor(). The option must be given as the + Preferences option key. The mapping to the EditorConfig option name + will be done within this method. + + @param option Preferences option key + @type str + @param nodefault flag indicating to not get the default value from + Preferences but return None instead + @type bool + @return value of requested setting or None if nothing was found and + nodefault parameter was True + @rtype any + """ + if not self.__editorConfig: + if nodefault: + return None + else: + return Preferences.getEditor(option) + + try: + if option == "EOLMode": + value = self.__editorConfig["end_of_line"] + if value == "lf": + value = QsciScintilla.EolUnix + elif value == "crlf": + value = QsciScintilla.EolWindows + elif value == "cr": + value = QsciScintilla.EolMac + else: + value = None + elif option == "DefaultEncoding": + value = self.__editorConfig["charset"] + except KeyError: + value = None + + if value is None and not nodefault: + # use Preferences in case of error + value = Preferences.getEditor(option) + + return value
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/LICENSE.txt Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,192 @@ +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +Python Software Foundation; All Rights Reserved" are retained in Python alone or +in any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the Internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the Internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/README.rst Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,90 @@ +======================== +EditorConfig Python Core +======================== +.. image:: https://img.shields.io/pypi/v/EditorConfig.svg + :target: https://pypi.python.org/pypi/EditorConfig + +.. image:: https://img.shields.io/pypi/wheel/EditorConfig.svg + :target: https://pypi.python.org/pypi/EditorConfig + +.. image:: https://img.shields.io/pypi/pyversions/EditorConfig.svg + :target: https://pypi.python.org/pypi/EditorConfig + +.. image:: https://secure.travis-ci.org/editorconfig/editorconfig-core-py.svg?branch=master + :target: http://travis-ci.org/editorconfig/editorconfig-core-py + +EditorConfig Python Core provides the same functionality as the +`EditorConfig C Core <https://github.com/editorconfig/editorconfig-core>`_. +EditorConfig Python core can be used as a command line program or as an +importable library. + +EditorConfig Project +==================== + +EditorConfig makes it easy to maintain the correct coding style when switching +between different text editors and between different projects. The +EditorConfig project maintains a file format and plugins for various text +editors which allow this file format to be read and used by those editors. For +information on the file format and supported text editors, see the +`EditorConfig website <http://editorconfig.org>`_. + +Installation +============ + +With setuptools:: + + sudo python setup.py install + +Getting Help +============ +For help with the EditorConfig core code, please write to our `mailing list +<http://groups.google.com/group/editorconfig>`_. Bugs and feature requests +should be submitted to our `issue tracker +<https://github.com/editorconfig/editorconfig/issues>`_. + +If you are writing a plugin a language that can import Python libraries, you +may want to import and use the EditorConfig Python Core directly. + +Using as a Library +================== + +Basic example use of EditorConfig Python Core as a library: + +.. code-block:: python + + from editorconfig import get_properties, EditorConfigError + + filename = "/home/zoidberg/humans/anatomy.md" + + try: + options = get_properties(filename) + except EditorConfigError: + print "Error occurred while getting EditorConfig properties" + else: + for key, value in options.items(): + print "%s=%s" % (key, value) + +For details, please take a look at the `online documentation +<http://pydocs.editorconfig.org>`_. + +Running Test Cases +================== + +`Cmake <http://www.cmake.org>`_ has to be installed first. Run the test cases +using the following commands:: + + cmake . + ctest . + +Use ``-DPYTHON_EXECUTABLE`` to run the tests using an alternative versions of +Python (e.g. Python 3):: + + cmake -DPYTHON_EXECUTABLE=/usr/bin/python3 . + ctest . + +License +======= + +Unless otherwise stated, all files are distributed under the PSF license. The +odict library (editorconfig/odict.py) is distributed under the New BSD license. +See LICENSE.txt file for details on PSF license.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/__init__.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2018 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package containing the editorconfig package. +"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/__init__.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,19 @@ +"""EditorConfig Python Core""" + +from editorconfig.versiontools import join_version + +VERSION = (0, 12, 0, "final") + +__all__ = ['get_properties', 'EditorConfigError', 'exceptions'] + +__version__ = join_version(VERSION) + + +def get_properties(filename): + """Locate and parse EditorConfig files for the given filename""" + handler = EditorConfigHandler(filename) + return handler.get_configurations() + + +from editorconfig.handler import EditorConfigHandler +from editorconfig.exceptions import *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/compat.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,23 @@ +"""EditorConfig Python2/Python3 compatibility utilities""" +import sys + +__all__ = ['force_unicode', 'u'] + + +if sys.version_info[0] == 2: + text_type = unicode +else: + text_type = str + + +def force_unicode(string): + if not isinstance(string, text_type): + string = text_type(string, encoding='utf-8') + return string + + +if sys.version_info[0] == 2: + import codecs + u = lambda s: codecs.unicode_escape_decode(s)[0] +else: + u = lambda s: s
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/exceptions.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,27 @@ +"""EditorConfig exception classes + +Licensed under PSF License (see LICENSE.txt file). + +""" + + +class EditorConfigError(Exception): + """Parent class of all exceptions raised by EditorConfig""" + + +try: + from ConfigParser import ParsingError as _ParsingError +except: + from configparser import ParsingError as _ParsingError + + +class ParsingError(_ParsingError, EditorConfigError): + """Error raised if an EditorConfig file could not be parsed""" + + +class PathError(ValueError, EditorConfigError): + """Error raised if invalid filepath is specified""" + + +class VersionError(ValueError, EditorConfigError): + """Error raised if invalid version number is specified"""
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/fnmatch.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,219 @@ +"""Filename matching with shell patterns. + +fnmatch(FILENAME, PATTERN) matches according to the local convention. +fnmatchcase(FILENAME, PATTERN) always takes case in account. + +The functions operate by translating the pattern into a regular +expression. They cache the compiled regular expressions for speed. + +The function translate(PATTERN) returns a regular expression +corresponding to PATTERN. (It does not compile it.) + +Based on code from fnmatch.py file distributed with Python 2.6. + +Licensed under PSF License (see LICENSE.txt file). + +Changes to original fnmatch module: +- translate function supports ``*`` and ``**`` similarly to fnmatch C library +""" + +import os +import re + +__all__ = ["fnmatch", "fnmatchcase", "translate"] + +_cache = {} + +LEFT_BRACE = re.compile( + r""" + + (?: ^ | [^\\] ) # Beginning of string or a character besides "\" + + \{ # "{" + + """, re.VERBOSE +) + +RIGHT_BRACE = re.compile( + r""" + + (?: ^ | [^\\] ) # Beginning of string or a character besides "\" + + \} # "}" + + """, re.VERBOSE +) + +NUMERIC_RANGE = re.compile( + r""" + ( # Capture a number + [+-] ? # Zero or one "+" or "-" characters + \d + # One or more digits + ) + + \.\. # ".." + + ( # Capture a number + [+-] ? # Zero or one "+" or "-" characters + \d + # One or more digits + ) + """, re.VERBOSE +) + + +def fnmatch(name, pat): + """Test whether FILENAME matches PATTERN. + + Patterns are Unix shell style: + + - ``*`` matches everything except path separator + - ``**`` matches everything + - ``?`` matches any single character + - ``[seq]`` matches any character in seq + - ``[!seq]`` matches any char not in seq + - ``{s1,s2,s3}`` matches any of the strings given (separated by commas) + + An initial period in FILENAME is not special. + Both FILENAME and PATTERN are first case-normalized + if the operating system requires it. + If you don't want this, use fnmatchcase(FILENAME, PATTERN). + """ + + name = os.path.normpath(name).replace(os.sep, "/") + return fnmatchcase(name, pat) + + +def cached_translate(pat): + if not pat in _cache: + res, num_groups = translate(pat) + regex = re.compile(res) + _cache[pat] = regex, num_groups + return _cache[pat] + + +def fnmatchcase(name, pat): + """Test whether FILENAME matches PATTERN, including case. + + This is a version of fnmatch() which doesn't case-normalize + its arguments. + """ + + regex, num_groups = cached_translate(pat) + match = regex.match(name) + if not match: + return False + pattern_matched = True + for (num, (min_num, max_num)) in zip(match.groups(), num_groups): + if num[0] == '0' or not (min_num <= int(num) <= max_num): + pattern_matched = False + break + return pattern_matched + + +def translate(pat, nested=False): + """Translate a shell PATTERN to a regular expression. + + There is no way to quote meta-characters. + """ + + index, length = 0, len(pat) # Current index and length of pattern + brace_level = 0 + in_brackets = False + result = '' + is_escaped = False + matching_braces = (len(LEFT_BRACE.findall(pat)) == + len(RIGHT_BRACE.findall(pat))) + numeric_groups = [] + while index < length: + current_char = pat[index] + index += 1 + if current_char == '*': + pos = index + if pos < length and pat[pos] == '*': + result += '.*' + else: + result += '[^/]*' + elif current_char == '?': + result += '.' + elif current_char == '[': + if in_brackets: + result += '\\[' + else: + pos = index + has_slash = False + while pos < length and pat[pos] != ']': + if pat[pos] == '/' and pat[pos-1] != '\\': + has_slash = True + break + pos += 1 + if has_slash: + result += '\\[' + pat[index:(pos + 1)] + '\\]' + index = pos + 2 + else: + if index < length and pat[index] in '!^': + index += 1 + result += '[^' + else: + result += '[' + in_brackets = True + elif current_char == '-': + if in_brackets: + result += current_char + else: + result += '\\' + current_char + elif current_char == ']': + result += current_char + in_brackets = False + elif current_char == '{': + pos = index + has_comma = False + while pos < length and (pat[pos] != '}' or is_escaped): + if pat[pos] == ',' and not is_escaped: + has_comma = True + break + is_escaped = pat[pos] == '\\' and not is_escaped + pos += 1 + if not has_comma and pos < length: + num_range = NUMERIC_RANGE.match(pat[index:pos]) + if num_range: + numeric_groups.append(map(int, num_range.groups())) + result += "([+-]?\d+)" + else: + inner_result, inner_groups = translate(pat[index:pos], + nested=True) + result += '\\{%s\\}' % (inner_result,) + numeric_groups += inner_groups + index = pos + 1 + elif matching_braces: + result += '(?:' + brace_level += 1 + else: + result += '\\{' + elif current_char == ',': + if brace_level > 0 and not is_escaped: + result += '|' + else: + result += '\\,' + elif current_char == '}': + if brace_level > 0 and not is_escaped: + result += ')' + brace_level -= 1 + else: + result += '\\}' + elif current_char == '/': + if pat[index:(index + 3)] == "**/": + result += "(?:/|/.*/)" + index += 3 + else: + result += '/' + elif current_char != '\\': + result += re.escape(current_char) + if current_char == '\\': + if is_escaped: + result += re.escape(current_char) + is_escaped = not is_escaped + else: + is_escaped = False + if not nested: + result += '\Z(?ms)' + return result, numeric_groups
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/handler.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,127 @@ +"""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=VERSION): + """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 >= (0, 10, 0)): + 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"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/ini.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,174 @@ +"""EditorConfig file parser + +Based on code from ConfigParser.py file distributed with Python 2.6. + +Licensed under PSF License (see LICENSE.txt file). + +Changes to original ConfigParser: + +- Special characters can be used in section names +- Octothorpe can be used for comments (not just at beginning of line) +- Only track INI options in sections that match target filename +- Stop parsing files with when ``root = true`` is found + +""" + +import re +from codecs import open +import posixpath +from os import sep +from os.path import normpath, dirname + +from editorconfig.exceptions import ParsingError +from editorconfig.fnmatch import fnmatch +from editorconfig.odict import OrderedDict +from editorconfig.compat import u + + +__all__ = ["ParsingError", "EditorConfigParser"] + + +class EditorConfigParser(object): + + """Parser for EditorConfig-style configuration files + + Based on RawConfigParser from ConfigParser.py in Python 2.6. + """ + + # Regular expressions for parsing section headers and options. + # Allow ``]`` and escaped ``;`` and ``#`` characters in section headers + SECTCRE = re.compile( + r""" + + \s * # Optional whitespace + \[ # Opening square brace + + (?P<header> # One or more characters excluding + ( [^\#;] | \\\# | \\; ) + # unescaped # and ; characters + ) + + \] # Closing square brace + + """, re.VERBOSE + ) + # Regular expression for parsing option name/values. + # Allow any amount of whitespaces, followed by separator + # (either ``:`` or ``=``), followed by any amount of whitespace and then + # any characters to eol + OPTCRE = re.compile( + r""" + + \s * # Optional whitespace + (?P<option> # One or more characters excluding + [^:=\s] # : a = characters (and first + [^:=] * # must not be whitespace) + ) + \s * # Optional whitespace + (?P<vi> + [:=] # Single = or : character + ) + \s * # Optional whitespace + (?P<value> + . * # One or more characters + ) + $ + + """, re.VERBOSE + ) + + def __init__(self, filename): + self.filename = filename + self.options = OrderedDict() + self.root_file = False + + def matches_filename(self, config_filename, glob): + """Return True if section glob matches filename""" + config_dirname = normpath(dirname(config_filename)).replace(sep, '/') + glob = glob.replace("\\#", "#") + glob = glob.replace("\\;", ";") + if '/' in glob: + if glob.find('/') == 0: + glob = glob[1:] + glob = posixpath.join(config_dirname, glob) + else: + glob = posixpath.join('**/', glob) + return fnmatch(self.filename, glob) + + def read(self, filename): + """Read and parse single EditorConfig file""" + try: + fp = open(filename, encoding='utf-8') + except IOError: + return + self._read(fp, filename) + fp.close() + + def _read(self, fp, fpname): + """Parse a sectioned setup file. + + The sections in setup file contains a title line at the top, + indicated by a name in square brackets (`[]'), plus key/value + options lines, indicated by `name: value' format lines. + Continuations are represented by an embedded newline then + leading whitespace. Blank lines, lines beginning with a '#', + and just about everything else are ignored. + """ + in_section = False + matching_section = False + optname = None + lineno = 0 + e = None # None, or an exception + while True: + line = fp.readline() + if not line: + break + if lineno == 0 and line.startswith(u('\ufeff')): + line = line[1:] # Strip UTF-8 BOM + lineno = lineno + 1 + # comment or blank line? + if line.strip() == '' or line[0] in '#;': + continue + # a section header or option header? + else: + # is it a section header? + mo = self.SECTCRE.match(line) + if mo: + sectname = mo.group('header') + in_section = True + matching_section = self.matches_filename(fpname, sectname) + # So sections can't start with a continuation line + optname = None + # an option line? + else: + mo = self.OPTCRE.match(line) + if mo: + optname, vi, optval = mo.group('option', 'vi', 'value') + if ';' in optval or '#' in optval: + # ';' and '#' are comment delimiters only if + # preceeded by a spacing character + m = re.search('(.*?) [;#]', optval) + if m: + optval = m.group(1) + optval = optval.strip() + # allow empty values + if optval == '""': + optval = '' + optname = self.optionxform(optname.rstrip()) + if not in_section and optname == 'root': + self.root_file = (optval.lower() == 'true') + if matching_section: + self.options[optname] = optval + else: + # a non-fatal parsing error occurred. set up the + # exception but keep going. the exception will be + # raised at the end of the file and will contain a + # list of all bogus lines + if not e: + e = ParsingError(fpname) + e.append(lineno, repr(line)) + # if any parsing errors occurred, raise an exception + if e: + raise e + + def optionxform(self, optionstr): + return optionstr.lower()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/main.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,78 @@ +"""EditorConfig command line interface + +Licensed under PSF License (see LICENSE.txt file). + +""" + +import getopt +import sys + +from editorconfig import __version__, VERSION +from editorconfig.compat import force_unicode +from editorconfig.versiontools import split_version +from editorconfig.handler import EditorConfigHandler +from editorconfig.exceptions import ParsingError, PathError, VersionError + + +def version(): + print("EditorConfig Python Core Version %s" % __version__) + + +def usage(command, error=False): + if error: + out = sys.stderr + else: + out = sys.stdout + out.write("%s [OPTIONS] FILENAME\n" % command) + out.write('-f ' + 'Specify conf filename other than ".editorconfig".\n') + out.write("-b " + "Specify version (used by devs to test compatibility).\n") + out.write("-h OR --help Print this help message.\n") + out.write("-v OR --version Display version information.\n") + + +def main(): + command_name = sys.argv[0] + try: + opts, args = getopt.getopt(list(map(force_unicode, sys.argv[1:])), + "vhb:f:", ["version", "help"]) + except getopt.GetoptError as e: + print(str(e)) + usage(command_name, error=True) + sys.exit(2) + + version_tuple = VERSION + conf_filename = '.editorconfig' + + for option, arg in opts: + if option in ('-h', '--help'): + usage(command_name) + sys.exit() + if option in ('-v', '--version'): + version() + sys.exit() + if option == '-f': + conf_filename = arg + if option == '-b': + version_tuple = split_version(arg) + if version_tuple is None: + sys.exit("Invalid version number: %s" % arg) + + if len(args) < 1: + usage(command_name, error=True) + sys.exit(2) + filenames = args + multiple_files = len(args) > 1 + + for filename in filenames: + handler = EditorConfigHandler(filename, conf_filename, version_tuple) + try: + options = handler.get_configurations() + except (ParsingError, PathError, VersionError) as e: + print(str(e)) + sys.exit(2) + if multiple_files: + print("[%s]" % filename) + for key, value in options.items(): + print("%s=%s" % (key, value))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/odict.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,897 @@ +"""odict.py: An Ordered Dictionary object""" +# Copyright (C) 2005 Nicola Larosa, Michael Foord +# E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk +# Copyright (c) 2003-2010, Michael Foord +# E-mail : fuzzyman AT voidspace DOT org DOT uk +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# * Neither the name of Michael Foord nor the name of Voidspace +# may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +from __future__ import generators +import sys +import warnings + + +__docformat__ = "restructuredtext en" +__all__ = ['OrderedDict'] + + +INTP_VER = sys.version_info[:2] +if INTP_VER < (2, 2): + raise RuntimeError("Python v.2.2 or later required") + + +class OrderedDict(dict): + """ + A class of dictionary that keeps the insertion order of keys. + + All appropriate methods return keys, items, or values in an ordered way. + + All normal dictionary methods are available. Update and comparison is + restricted to other OrderedDict objects. + + Various sequence methods are available, including the ability to explicitly + mutate the key ordering. + + __contains__ tests: + + >>> d = OrderedDict(((1, 3),)) + >>> 1 in d + 1 + >>> 4 in d + 0 + + __getitem__ tests: + + >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2] + 1 + >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4] + Traceback (most recent call last): + KeyError: 4 + + __len__ tests: + + >>> len(OrderedDict()) + 0 + >>> len(OrderedDict(((1, 3), (3, 2), (2, 1)))) + 3 + + get tests: + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.get(1) + 3 + >>> d.get(4) is None + 1 + >>> d.get(4, 5) + 5 + >>> d + OrderedDict([(1, 3), (3, 2), (2, 1)]) + + has_key tests: + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.has_key(1) + 1 + >>> d.has_key(4) + 0 + """ + + def __init__(self, init_val=(), strict=False): + """ + Create a new ordered dictionary. Cannot init from a normal dict, + nor from kwargs, since items order is undefined in those cases. + + If the ``strict`` keyword argument is ``True`` (``False`` is the + default) then when doing slice assignment - the ``OrderedDict`` you are + assigning from *must not* contain any keys in the remaining dict. + + >>> OrderedDict() + OrderedDict([]) + >>> OrderedDict({1: 1}) + Traceback (most recent call last): + TypeError: undefined order, cannot get items from dict + >>> OrderedDict({1: 1}.items()) + OrderedDict([(1, 1)]) + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d + OrderedDict([(1, 3), (3, 2), (2, 1)]) + >>> OrderedDict(d) + OrderedDict([(1, 3), (3, 2), (2, 1)]) + """ + self.strict = strict + dict.__init__(self) + if isinstance(init_val, OrderedDict): + self._sequence = init_val.keys() + dict.update(self, init_val) + elif isinstance(init_val, dict): + # we lose compatibility with other ordered dict types this way + raise TypeError('undefined order, cannot get items from dict') + else: + self._sequence = [] + self.update(init_val) + +### Special methods ### + + def __delitem__(self, key): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> del d[3] + >>> d + OrderedDict([(1, 3), (2, 1)]) + >>> del d[3] + Traceback (most recent call last): + KeyError: 3 + >>> d[3] = 2 + >>> d + OrderedDict([(1, 3), (2, 1), (3, 2)]) + >>> del d[0:1] + >>> d + OrderedDict([(2, 1), (3, 2)]) + """ + if isinstance(key, slice): + # FIXME: efficiency? + keys = self._sequence[key] + for entry in keys: + dict.__delitem__(self, entry) + del self._sequence[key] + else: + # do the dict.__delitem__ *first* as it raises + # the more appropriate error + dict.__delitem__(self, key) + self._sequence.remove(key) + + def __eq__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d == OrderedDict(d) + True + >>> d == OrderedDict(((1, 3), (2, 1), (3, 2))) + False + >>> d == OrderedDict(((1, 0), (3, 2), (2, 1))) + False + >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) + False + >>> d == dict(d) + False + >>> d == False + False + """ + if isinstance(other, OrderedDict): + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() == other.items()) + else: + return False + + def __lt__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) + >>> c < d + True + >>> d < c + False + >>> d < dict(c) + Traceback (most recent call last): + TypeError: Can only compare with other OrderedDicts + """ + if not isinstance(other, OrderedDict): + raise TypeError('Can only compare with other OrderedDicts') + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() < other.items()) + + def __le__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) + >>> e = OrderedDict(d) + >>> c <= d + True + >>> d <= c + False + >>> d <= dict(c) + Traceback (most recent call last): + TypeError: Can only compare with other OrderedDicts + >>> d <= e + True + """ + if not isinstance(other, OrderedDict): + raise TypeError('Can only compare with other OrderedDicts') + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() <= other.items()) + + def __ne__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d != OrderedDict(d) + False + >>> d != OrderedDict(((1, 3), (2, 1), (3, 2))) + True + >>> d != OrderedDict(((1, 0), (3, 2), (2, 1))) + True + >>> d == OrderedDict(((0, 3), (3, 2), (2, 1))) + False + >>> d != dict(d) + True + >>> d != False + True + """ + if isinstance(other, OrderedDict): + # FIXME: efficiency? + # Generate both item lists for each compare + return not (self.items() == other.items()) + else: + return True + + def __gt__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) + >>> d > c + True + >>> c > d + False + >>> d > dict(c) + Traceback (most recent call last): + TypeError: Can only compare with other OrderedDicts + """ + if not isinstance(other, OrderedDict): + raise TypeError('Can only compare with other OrderedDicts') + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() > other.items()) + + def __ge__(self, other): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> c = OrderedDict(((0, 3), (3, 2), (2, 1))) + >>> e = OrderedDict(d) + >>> c >= d + False + >>> d >= c + True + >>> d >= dict(c) + Traceback (most recent call last): + TypeError: Can only compare with other OrderedDicts + >>> e >= d + True + """ + if not isinstance(other, OrderedDict): + raise TypeError('Can only compare with other OrderedDicts') + # FIXME: efficiency? + # Generate both item lists for each compare + return (self.items() >= other.items()) + + def __repr__(self): + """ + Used for __repr__ and __str__ + + >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) + >>> r1 + "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])" + >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) + >>> r2 + "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])" + >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f')))) + True + >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd')))) + True + """ + return '%s([%s])' % (self.__class__.__name__, ', '.join( + ['(%r, %r)' % (key, self[key]) for key in self._sequence])) + + def __setitem__(self, key, val): + """ + Allows slice assignment, so long as the slice is an OrderedDict + >>> d = OrderedDict() + >>> d['a'] = 'b' + >>> d['b'] = 'a' + >>> d[3] = 12 + >>> d + OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)]) + >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4))) + >>> d + OrderedDict([(1, 2), (2, 3), (3, 4)]) + >>> d[::2] = OrderedDict(((7, 8), (9, 10))) + >>> d + OrderedDict([(7, 8), (2, 3), (9, 10)]) + >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4))) + >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) + >>> d + OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) + >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True) + >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8))) + >>> d + OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)]) + + >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True) + >>> a[3] = 4 + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]) + Traceback (most recent call last): + ValueError: slice assignment must be from unique keys + >>> a = OrderedDict(((0, 1), (1, 2), (2, 3))) + >>> a[3] = 4 + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a + OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> a + OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)]) + + >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> d[:1] = 3 + Traceback (most recent call last): + TypeError: slice assignment requires an OrderedDict + + >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)]) + >>> d[:1] = OrderedDict([(9, 8)]) + >>> d + OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)]) + """ + if isinstance(key, slice): + if not isinstance(val, OrderedDict): + # FIXME: allow a list of tuples? + raise TypeError('slice assignment requires an OrderedDict') + keys = self._sequence[key] + # NOTE: Could use ``range(*key.indices(len(self._sequence)))`` + indexes = range(len(self._sequence))[key] + if key.step is None: + # NOTE: new slice may not be the same size as the one being + # overwritten ! + # NOTE: What is the algorithm for an impossible slice? + # e.g. d[5:3] + pos = key.start or 0 + del self[key] + newkeys = val.keys() + for k in newkeys: + if k in self: + if self.strict: + raise ValueError('slice assignment must be from ' + 'unique keys') + else: + # NOTE: This removes duplicate keys *first* + # so start position might have changed? + del self[k] + self._sequence = (self._sequence[:pos] + newkeys + + self._sequence[pos:]) + dict.update(self, val) + else: + # extended slice - length of new slice must be the same + # as the one being replaced + if len(keys) != len(val): + raise ValueError('attempt to assign sequence of size %s ' + 'to extended slice of size %s' % (len(val), len(keys))) + # FIXME: efficiency? + del self[key] + item_list = zip(indexes, val.items()) + # smallest indexes first - higher indexes not guaranteed to + # exist + item_list.sort() + for pos, (newkey, newval) in item_list: + if self.strict and newkey in self: + raise ValueError('slice assignment must be from unique' + ' keys') + self.insert(pos, newkey, newval) + else: + if key not in self: + self._sequence.append(key) + dict.__setitem__(self, key, val) + + def __getitem__(self, key): + """ + Allows slicing. Returns an OrderedDict if you slice. + >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)]) + >>> b[::-1] + OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)]) + >>> b[2:5] + OrderedDict([(5, 2), (4, 3), (3, 4)]) + >>> type(b[2:4]) + <class '__main__.OrderedDict'> + """ + if isinstance(key, slice): + # FIXME: does this raise the error we want? + keys = self._sequence[key] + # FIXME: efficiency? + return OrderedDict([(entry, self[entry]) for entry in keys]) + else: + return dict.__getitem__(self, key) + + __str__ = __repr__ + + def __setattr__(self, name, value): + """ + Implemented so that accesses to ``sequence`` raise a warning and are + diverted to the new ``setkeys`` method. + """ + if name == 'sequence': + warnings.warn('Use of the sequence attribute is deprecated.' + ' Use the keys method instead.', DeprecationWarning) + # NOTE: doesn't return anything + self.setkeys(value) + else: + # FIXME: do we want to allow arbitrary setting of attributes? + # Or do we want to manage it? + object.__setattr__(self, name, value) + + def __getattr__(self, name): + """ + Implemented so that access to ``sequence`` raises a warning. + + >>> d = OrderedDict() + >>> d.sequence + [] + """ + if name == 'sequence': + warnings.warn('Use of the sequence attribute is deprecated.' + ' Use the keys method instead.', DeprecationWarning) + # NOTE: Still (currently) returns a direct reference. Need to + # because code that uses sequence will expect to be able to + # mutate it in place. + return self._sequence + else: + # raise the appropriate error + raise AttributeError("OrderedDict has no '%s' attribute" % name) + + def __deepcopy__(self, memo): + """ + To allow deepcopy to work with OrderedDict. + + >>> from copy import deepcopy + >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)]) + >>> a['test'] = {} + >>> b = deepcopy(a) + >>> b == a + True + >>> b is a + False + >>> a['test'] is b['test'] + False + """ + from copy import deepcopy + return self.__class__(deepcopy(self.items(), memo), self.strict) + +### Read-only methods ### + + def copy(self): + """ + >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy() + OrderedDict([(1, 3), (3, 2), (2, 1)]) + """ + return OrderedDict(self) + + def items(self): + """ + ``items`` returns a list of tuples representing all the + ``(key, value)`` pairs in the dictionary. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.items() + [(1, 3), (3, 2), (2, 1)] + >>> d.clear() + >>> d.items() + [] + """ + return zip(self._sequence, self.values()) + + def keys(self): + """ + Return a list of keys in the ``OrderedDict``. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.keys() + [1, 3, 2] + """ + return self._sequence[:] + + def values(self, values=None): + """ + Return a list of all the values in the OrderedDict. + + Optionally you can pass in a list of values, which will replace the + current list. The value list must be the same len as the OrderedDict. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.values() + [3, 2, 1] + """ + return [self[key] for key in self._sequence] + + def iteritems(self): + """ + >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems() + >>> ii.next() + (1, 3) + >>> ii.next() + (3, 2) + >>> ii.next() + (2, 1) + >>> ii.next() + Traceback (most recent call last): + StopIteration + """ + def make_iter(self=self): + keys = self.iterkeys() + while True: + key = keys.next() + yield (key, self[key]) + return make_iter() + + def iterkeys(self): + """ + >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys() + >>> ii.next() + 1 + >>> ii.next() + 3 + >>> ii.next() + 2 + >>> ii.next() + Traceback (most recent call last): + StopIteration + """ + return iter(self._sequence) + + __iter__ = iterkeys + + def itervalues(self): + """ + >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues() + >>> iv.next() + 3 + >>> iv.next() + 2 + >>> iv.next() + 1 + >>> iv.next() + Traceback (most recent call last): + StopIteration + """ + def make_iter(self=self): + keys = self.iterkeys() + while True: + yield self[keys.next()] + return make_iter() + +### Read-write methods ### + + def clear(self): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.clear() + >>> d + OrderedDict([]) + """ + dict.clear(self) + self._sequence = [] + + def pop(self, key, *args): + """ + No dict.pop in Python 2.2, gotta reimplement it + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.pop(3) + 2 + >>> d + OrderedDict([(1, 3), (2, 1)]) + >>> d.pop(4) + Traceback (most recent call last): + KeyError: 4 + >>> d.pop(4, 0) + 0 + >>> d.pop(4, 0, 1) + Traceback (most recent call last): + TypeError: pop expected at most 2 arguments, got 3 + """ + if len(args) > 1: + raise TypeError('pop expected at most 2 arguments, got %s' % + (len(args) + 1)) + if key in self: + val = self[key] + del self[key] + else: + try: + val = args[0] + except IndexError: + raise KeyError(key) + return val + + def popitem(self, i=-1): + """ + Delete and return an item specified by index, not a random one as in + dict. The index is -1 by default (the last item). + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.popitem() + (2, 1) + >>> d + OrderedDict([(1, 3), (3, 2)]) + >>> d.popitem(0) + (1, 3) + >>> OrderedDict().popitem() + Traceback (most recent call last): + KeyError: 'popitem(): dictionary is empty' + >>> d.popitem(2) + Traceback (most recent call last): + IndexError: popitem(): index 2 not valid + """ + if not self._sequence: + raise KeyError('popitem(): dictionary is empty') + try: + key = self._sequence[i] + except IndexError: + raise IndexError('popitem(): index %s not valid' % i) + return (key, self.pop(key)) + + def setdefault(self, key, defval=None): + """ + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.setdefault(1) + 3 + >>> d.setdefault(4) is None + True + >>> d + OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)]) + >>> d.setdefault(5, 0) + 0 + >>> d + OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)]) + """ + if key in self: + return self[key] + else: + self[key] = defval + return defval + + def update(self, from_od): + """ + Update from another OrderedDict or sequence of (key, value) pairs + + >>> d = OrderedDict(((1, 0), (0, 1))) + >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1)))) + >>> d + OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)]) + >>> d.update({4: 4}) + Traceback (most recent call last): + TypeError: undefined order, cannot get items from dict + >>> d.update((4, 4)) + Traceback (most recent call last): + TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence + """ + if isinstance(from_od, OrderedDict): + for key, val in from_od.items(): + self[key] = val + elif isinstance(from_od, dict): + # we lose compatibility with other ordered dict types this way + raise TypeError('undefined order, cannot get items from dict') + else: + # FIXME: efficiency? + # sequence of 2-item sequences, or error + for item in from_od: + try: + key, val = item + except TypeError: + raise TypeError('cannot convert dictionary update' + ' sequence element "%s" to a 2-item sequence' % item) + self[key] = val + + def rename(self, old_key, new_key): + """ + Rename the key for a given value, without modifying sequence order. + + For the case where new_key already exists this raise an exception, + since if new_key exists, it is ambiguous as to what happens to the + associated values, and the position of new_key in the sequence. + + >>> od = OrderedDict() + >>> od['a'] = 1 + >>> od['b'] = 2 + >>> od.items() + [('a', 1), ('b', 2)] + >>> od.rename('b', 'c') + >>> od.items() + [('a', 1), ('c', 2)] + >>> od.rename('c', 'a') + Traceback (most recent call last): + ValueError: New key already exists: 'a' + >>> od.rename('d', 'b') + Traceback (most recent call last): + KeyError: 'd' + """ + if new_key == old_key: + # no-op + return + if new_key in self: + raise ValueError("New key already exists: %r" % new_key) + # rename sequence entry + value = self[old_key] + old_idx = self._sequence.index(old_key) + self._sequence[old_idx] = new_key + # rename internal dict entry + dict.__delitem__(self, old_key) + dict.__setitem__(self, new_key, value) + + def setitems(self, items): + """ + This method allows you to set the items in the dict. + + It takes a list of tuples - of the same sort returned by the ``items`` + method. + + >>> d = OrderedDict() + >>> d.setitems(((3, 1), (2, 3), (1, 2))) + >>> d + OrderedDict([(3, 1), (2, 3), (1, 2)]) + """ + self.clear() + # FIXME: this allows you to pass in an OrderedDict as well :-) + self.update(items) + + def setkeys(self, keys): + """ + ``setkeys`` all ows you to pass in a new list of keys which will + replace the current set. This must contain the same set of keys, but + need not be in the same order. + + If you pass in new keys that don't match, a ``KeyError`` will be + raised. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.keys() + [1, 3, 2] + >>> d.setkeys((1, 2, 3)) + >>> d + OrderedDict([(1, 3), (2, 1), (3, 2)]) + >>> d.setkeys(['a', 'b', 'c']) + Traceback (most recent call last): + KeyError: 'Keylist is not the same as current keylist.' + """ + # FIXME: Efficiency? (use set for Python 2.4 :-) + # NOTE: list(keys) rather than keys[:] because keys[:] returns + # a tuple, if keys is a tuple. + kcopy = list(keys) + kcopy.sort() + self._sequence.sort() + if kcopy != self._sequence: + raise KeyError('Keylist is not the same as current keylist.') + # NOTE: This makes the _sequence attribute a new object, instead + # of changing it in place. + # FIXME: efficiency? + self._sequence = list(keys) + + def setvalues(self, values): + """ + You can pass in a list of values, which will replace the + current list. The value list must be the same len as the OrderedDict. + + (Or a ``ValueError`` is raised.) + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.setvalues((1, 2, 3)) + >>> d + OrderedDict([(1, 1), (3, 2), (2, 3)]) + >>> d.setvalues([6]) + Traceback (most recent call last): + ValueError: Value list is not the same length as the OrderedDict. + """ + if len(values) != len(self): + # FIXME: correct error to raise? + raise ValueError('Value list is not the same length as the ' + 'OrderedDict.') + self.update(zip(self, values)) + +### Sequence Methods ### + + def index(self, key): + """ + Return the position of the specified key in the OrderedDict. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.index(3) + 1 + >>> d.index(4) + Traceback (most recent call last): + ValueError: 4 is not in list + """ + return self._sequence.index(key) + + def insert(self, index, key, value): + """ + Takes ``index``, ``key``, and ``value`` as arguments. + + Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in + the OrderedDict. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.insert(0, 4, 0) + >>> d + OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)]) + >>> d.insert(0, 2, 1) + >>> d + OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)]) + >>> d.insert(8, 8, 1) + >>> d + OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)]) + """ + if key in self: + # FIXME: efficiency? + del self[key] + self._sequence.insert(index, key) + dict.__setitem__(self, key, value) + + def reverse(self): + """ + Reverse the order of the OrderedDict. + + >>> d = OrderedDict(((1, 3), (3, 2), (2, 1))) + >>> d.reverse() + >>> d + OrderedDict([(2, 1), (3, 2), (1, 3)]) + """ + self._sequence.reverse() + + def sort(self, *args, **kwargs): + """ + Sort the key order in the OrderedDict. + + This method takes the same arguments as the ``list.sort`` method on + your version of Python. + + >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4))) + >>> d.sort() + >>> d + OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)]) + """ + self._sequence.sort(*args, **kwargs) + + +if __name__ == '__main__': + # turn off warnings for tests + warnings.filterwarnings('ignore') + # run the code tests in doctest format + import doctest + m = sys.modules.get('__main__') + globs = m.__dict__.copy() + globs.update({ + 'INTP_VER': INTP_VER, + }) + doctest.testmod(m, globs=globs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThirdParty/EditorConfig/editorconfig/versiontools.py Thu Feb 01 19:26:11 2018 +0100 @@ -0,0 +1,35 @@ +"""EditorConfig version tools + +Provides ``join_version`` and ``split_version`` classes for converting +__version__ strings to VERSION tuples and vice versa. + +""" + +import re + + +__all__ = ['join_version', 'split_version'] + + +_version_re = re.compile(r'^(\d+)\.(\d+)\.(\d+)(\..*)?$', re.VERBOSE) + + +def join_version(version_tuple): + """Return a string representation of version from given VERSION tuple""" + version = "%s.%s.%s" % version_tuple[:3] + if version_tuple[3] != "final": + version += "-%s" % version_tuple[3] + return version + + +def split_version(version): + """Return VERSION tuple for given string representation of version""" + match = _version_re.search(version) + if not match: + return None + else: + split_version = list(match.groups()) + if split_version[3] is None: + split_version[3] = "final" + split_version = list(map(int, split_version[:3])) + split_version[3:] + return tuple(split_version)
--- a/Utilities/__init__.py Thu Feb 01 19:25:47 2018 +0100 +++ b/Utilities/__init__.py Thu Feb 01 19:26:11 2018 +0100 @@ -299,16 +299,23 @@ return decode(text) -def writeEncodedFile(filename, text, orig_coding): +def writeEncodedFile(filename, text, origEncoding, forcedEncoding=""): """ Function to write a file with properly encoded text. - @param filename name of the file to read (string) - @param text text to be written (string) - @param orig_coding type of the original encoding (string) - @return encoding used for writing the file (string) + @param filename name of the file to read + @type str + @param text text to be written + @type str + @param origEncoding type of the original encoding + @type str + @param forcedEncoding encoding to be used for writing, if no coding + line is present + @type str + @return encoding used for writing the file + @rtype str """ - etext, encoding = encode(text, orig_coding) + etext, encoding = encode(text, origEncoding, forcedEncoding=forcedEncoding) f = open(filename, "wb") f.write(etext) @@ -317,17 +324,23 @@ return encoding -def encode(text, orig_coding): +def encode(text, origEncoding, forcedEncoding=""): """ Function to encode text into a byte text. - @param text text to be encoded (string) - @param orig_coding type of the original encoding (string) - @return tuple of encoded text and encoding used (bytes, string) + @param text text to be encoded + @type str + @param origEncoding type of the original encoding + @type str + @param forcedEncoding encoding to be used for writing, if no coding line + is present + @type str + @return tuple of encoded text and encoding used + @rtype tuple of (bytes, str) @exception CodingError raised to indicate an invalid encoding """ encoding = None - if orig_coding == 'utf-8-bom': + if origEncoding == 'utf-8-bom': etext, encoding = BOM_UTF8 + text.encode("utf-8"), 'utf-8-bom' else: # Try declared coding spec @@ -339,36 +352,46 @@ # Error: Declared encoding is incorrect raise CodingError(coding) else: - if orig_coding and orig_coding.endswith( - ('-selected', '-default', '-guessed', '-ignore')): - coding = orig_coding\ - .replace("-selected", "")\ - .replace("-default", "")\ - .replace("-guessed", "")\ - .replace("-ignore", "") + if forcedEncoding: try: - etext, encoding = text.encode(coding), coding + etext, encoding = ( + text.encode(forcedEncoding), forcedEncoding) except (UnicodeError, LookupError): + # Error: Forced encoding is incorrect, ignore it pass if encoding is None: - # Try configured default - try: - codec = Preferences.getEditor("DefaultEncoding") - etext, encoding = text.encode(codec), codec - except (UnicodeError, LookupError): - pass + # Try the original encoding + if origEncoding and origEncoding.endswith( + ('-selected', '-default', '-guessed', '-ignore')): + coding = origEncoding\ + .replace("-selected", "")\ + .replace("-default", "")\ + .replace("-guessed", "")\ + .replace("-ignore", "") + try: + etext, encoding = text.encode(coding), coding + except (UnicodeError, LookupError): + pass if encoding is None: - # Try saving as ASCII + # Try configured default try: - etext, encoding = text.encode('ascii'), 'ascii' - except UnicodeError: + codec = Preferences.getEditor("DefaultEncoding") + etext, encoding = text.encode(codec), codec + except (UnicodeError, LookupError): pass if encoding is None: - # Save as UTF-8 without BOM - etext, encoding = text.encode('utf-8'), 'utf-8' + # Try saving as ASCII + try: + etext, encoding = text.encode('ascii'), 'ascii' + except UnicodeError: + pass + + if encoding is None: + # Save as UTF-8 without BOM + etext, encoding = text.encode('utf-8'), 'utf-8' return etext, encoding
--- a/eric6.e4p Thu Feb 01 19:25:47 2018 +0100 +++ b/eric6.e4p Thu Feb 01 19:26:11 2018 +0100 @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE Project SYSTEM "Project-5.1.dtd"> <!-- eric project file for project eric6 --> -<!-- Copyright (C) 2017 Detlev Offenbach, detlev@die-offenbachs.de --> +<!-- Copyright (C) 2018 Detlev Offenbach, detlev@die-offenbachs.de --> <Project version="5.1"> <Language>en_US</Language> <ProjectWordList>Dictionaries/words.dic</ProjectWordList> @@ -1083,6 +1083,16 @@ <Source>ThirdParty/CharDet/chardet/universaldetector.py</Source> <Source>ThirdParty/CharDet/chardet/utf8prober.py</Source> <Source>ThirdParty/CharDet/chardet/version.py</Source> + <Source>ThirdParty/EditorConfig/__init__.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/__init__.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/compat.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/exceptions.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/fnmatch.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/handler.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/ini.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/main.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/odict.py</Source> + <Source>ThirdParty/EditorConfig/editorconfig/versiontools.py</Source> <Source>ThirdParty/Jasy/__init__.py</Source> <Source>ThirdParty/Jasy/jasy/__init__.py</Source> <Source>ThirdParty/Jasy/jasy/core/Console.py</Source> @@ -2189,14 +2199,14 @@ </Resources> <Others> <Other>.hgignore</Other> - <Other>APIs/Python/zope-2.10.7.api</Other> - <Other>APIs/Python/zope-2.11.2.api</Other> - <Other>APIs/Python/zope-3.3.1.api</Other> <Other>APIs/Python3/PyQt4.bas</Other> <Other>APIs/Python3/PyQt5.bas</Other> <Other>APIs/Python3/QScintilla2.bas</Other> <Other>APIs/Python3/eric6.api</Other> <Other>APIs/Python3/eric6.bas</Other> + <Other>APIs/Python/zope-2.10.7.api</Other> + <Other>APIs/Python/zope-2.11.2.api</Other> + <Other>APIs/Python/zope-3.3.1.api</Other> <Other>APIs/QSS/qss.api</Other> <Other>APIs/Ruby/Ruby-1.8.7.api</Other> <Other>APIs/Ruby/Ruby-1.8.7.bas</Other> @@ -2343,6 +2353,8 @@ <Other>THANKS</Other> <Other>ThirdParty/CharDet/LICENSE</Other> <Other>ThirdParty/CharDet/README.rst</Other> + <Other>ThirdParty/EditorConfig/LICENSE.txt</Other> + <Other>ThirdParty/EditorConfig/README.rst</Other> <Other>ThirdParty/Jasy/jasy/license.md</Other> <Other>ThirdParty/Pygments/pygments/AUTHORS</Other> <Other>ThirdParty/Pygments/pygments/CHANGES</Other>
--- a/eric6.py Thu Feb 01 19:25:47 2018 +0100 +++ b/eric6.py Thu Feb 01 19:26:11 2018 +0100 @@ -94,6 +94,8 @@ sys.path.insert(2, os.path.join(os.path.dirname(__file__), "ThirdParty", "Jasy")) sys.path.insert(2, os.path.join(os.path.dirname(__file__), + "ThirdParty", "EditorConfig")) +sys.path.insert(2, os.path.join(os.path.dirname(__file__), "DebugClients", "Python")) from E5Gui.E5Application import E5Application