diff options
Diffstat (limited to 'venv/lib/python3.11/site-packages/editorconfig')
18 files changed, 720 insertions, 0 deletions
| diff --git a/venv/lib/python3.11/site-packages/editorconfig/__init__.py b/venv/lib/python3.11/site-packages/editorconfig/__init__.py new file mode 100644 index 0000000..2574ce4 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__init__.py @@ -0,0 +1,18 @@ +"""EditorConfig Python Core""" + +from editorconfig.versiontools import join_version +from editorconfig.version import VERSION + +__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 * diff --git a/venv/lib/python3.11/site-packages/editorconfig/__main__.py b/venv/lib/python3.11/site-packages/editorconfig/__main__.py new file mode 100644 index 0000000..fc98b6f --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__main__.py @@ -0,0 +1,82 @@ +"""EditorConfig command line interface + +Licensed under Simplified BSD License (see LICENSE.BSD file). + +""" + +import getopt +import sys + +from editorconfig import VERSION, __version__ +from editorconfig.compat import force_unicode +from editorconfig.exceptions import ParsingError, PathError, VersionError +from editorconfig.handler import EditorConfigHandler +from editorconfig.versiontools import split_version + + +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)) + + +if __name__ == "__main__": +    main() diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/__init__.cpython-311.pycBinary files differ new file mode 100644 index 0000000..35f562f --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/__init__.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/__main__.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/__main__.cpython-311.pycBinary files differ new file mode 100644 index 0000000..5343829 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/__main__.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/compat.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/compat.cpython-311.pycBinary files differ new file mode 100644 index 0000000..1686a75 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/compat.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/exceptions.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/exceptions.cpython-311.pycBinary files differ new file mode 100644 index 0000000..511078f --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/exceptions.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/fnmatch.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/fnmatch.cpython-311.pycBinary files differ new file mode 100644 index 0000000..5d4a548 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/fnmatch.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/handler.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/handler.cpython-311.pycBinary files differ new file mode 100644 index 0000000..8504183 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/handler.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/ini.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/ini.cpython-311.pycBinary files differ new file mode 100644 index 0000000..65d5307 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/ini.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/version.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/version.cpython-311.pycBinary files differ new file mode 100644 index 0000000..2f937fa --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/version.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/__pycache__/versiontools.cpython-311.pyc b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/versiontools.cpython-311.pycBinary files differ new file mode 100644 index 0000000..69373b5 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/__pycache__/versiontools.cpython-311.pyc diff --git a/venv/lib/python3.11/site-packages/editorconfig/compat.py b/venv/lib/python3.11/site-packages/editorconfig/compat.py new file mode 100644 index 0000000..4b9f8ca --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/compat.py @@ -0,0 +1,24 @@ +"""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 diff --git a/venv/lib/python3.11/site-packages/editorconfig/exceptions.py b/venv/lib/python3.11/site-packages/editorconfig/exceptions.py new file mode 100644 index 0000000..c25f681 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/exceptions.py @@ -0,0 +1,27 @@ +"""EditorConfig exception classes + +Licensed under Simplified BSD License (see LICENSE.BSD 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""" diff --git a/venv/lib/python3.11/site-packages/editorconfig/fnmatch.py b/venv/lib/python3.11/site-packages/editorconfig/fnmatch.py new file mode 100644 index 0000000..76692b8 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/fnmatch.py @@ -0,0 +1,223 @@ +"""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.PSF 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""" + +    (?<! \\ )           # Not preceded by "\" + +    \{                  # "{" + +    """, re.VERBOSE +) + +RIGHT_BRACE = re.compile( +    r""" + +    (?<! \\ )           # Not preceded by "\" + +    \}                  # "}" + +    """, 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 + 1 +                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 == ']': +            if in_brackets and pat[index-2] == '\\': +                result += '\\]' +            else: +                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 += r"([+-]?\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 = r'(?s)%s\Z' % result +    return result, numeric_groups diff --git a/venv/lib/python3.11/site-packages/editorconfig/handler.py b/venv/lib/python3.11/site-packages/editorconfig/handler.py new file mode 100644 index 0000000..1c33c02 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/handler.py @@ -0,0 +1,127 @@ +"""EditorConfig file handler + +Provides ``EditorConfigHandler`` class for locating and parsing +EditorConfig files relevant to a given filepath. + +Licensed under Simplified BSD License (see LICENSE.BSD file). + +""" + +import os + +from editorconfig import VERSION +from editorconfig.exceptions import PathError, VersionError +from editorconfig.ini import EditorConfigParser + + +__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"] diff --git a/venv/lib/python3.11/site-packages/editorconfig/ini.py b/venv/lib/python3.11/site-packages/editorconfig/ini.py new file mode 100644 index 0000000..c603d79 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/ini.py @@ -0,0 +1,183 @@ +"""EditorConfig file parser + +Based on code from ConfigParser.py file distributed with Python 2.6. + +Licensed under PSF License (see LICENSE.PSF 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 posixpath +import re +from codecs import open +from collections import OrderedDict +from os import sep +from os.path import dirname, normpath + +from editorconfig.compat import u +from editorconfig.exceptions import ParsingError +from editorconfig.fnmatch import fnmatch + + +__all__ = ["ParsingError", "EditorConfigParser"] + +MAX_SECTION_LENGTH = 4096 +MAX_PROPERTY_LENGTH= 50 +MAX_VALUE_LENGTH = 255 + + +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') +                    if len(sectname) > MAX_SECTION_LENGTH: +                        continue +                    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 (len(optname) > MAX_PROPERTY_LENGTH or +                            len(optval) > MAX_VALUE_LENGTH): +                            continue +                        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() diff --git a/venv/lib/python3.11/site-packages/editorconfig/version.py b/venv/lib/python3.11/site-packages/editorconfig/version.py new file mode 100644 index 0000000..1dc3e55 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/version.py @@ -0,0 +1 @@ +VERSION = (0, 12, 4, "final") diff --git a/venv/lib/python3.11/site-packages/editorconfig/versiontools.py b/venv/lib/python3.11/site-packages/editorconfig/versiontools.py new file mode 100644 index 0000000..01744f8 --- /dev/null +++ b/venv/lib/python3.11/site-packages/editorconfig/versiontools.py @@ -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) | 
