diff options
author | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:10:44 -0400 |
---|---|---|
committer | cyfraeviolae <cyfraeviolae> | 2024-04-03 03:10:44 -0400 |
commit | 6d7ba58f880be618ade07f8ea080fe8c4bf8a896 (patch) | |
tree | b1c931051ffcebd2bd9d61d98d6233ffa289bbce /venv/lib/python3.11/site-packages/editorconfig | |
parent | 4f884c9abc32990b4061a1bb6997b4b37e58ea0b (diff) |
venv
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.pyc Binary files differnew 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.pyc Binary files differnew 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.pyc Binary files differnew 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.pyc Binary files differnew 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.pyc Binary files differnew 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.pyc Binary files differnew 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.pyc Binary files differnew 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.pyc Binary files differnew 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.pyc Binary files differnew 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) |