diff --git a/.custodia.metadata b/.custodia.metadata new file mode 100644 index 0000000..db52fee --- /dev/null +++ b/.custodia.metadata @@ -0,0 +1 @@ +7bfa722c3afe0151157e0d73d90a38ee97d8d5c8 SOURCES/custodia-0.3.1.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f89fec4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/custodia-0.3.1.tar.gz diff --git a/SOURCES/0001-Vendor-configparser-3.5.0.patch b/SOURCES/0001-Vendor-configparser-3.5.0.patch new file mode 100644 index 0000000..ce43770 --- /dev/null +++ b/SOURCES/0001-Vendor-configparser-3.5.0.patch @@ -0,0 +1,1668 @@ +From 85001e92986b3e3f6b7dcbbe5d71cde99962fd58 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Tue, 28 Mar 2017 17:49:39 +0200 +Subject: [PATCH 1/4] Vendor configparser-3.5.0 + +Signed-off-by: Christian Heimes +--- + custodia/vendor/backports/__init__.py | 11 + + custodia/vendor/backports/configparser/__init__.py | 1390 ++++++++++++++++++++ + custodia/vendor/backports/configparser/helpers.py | 171 +++ + custodia/vendor/configparser.py | 52 + + 4 files changed, 1624 insertions(+) + create mode 100644 custodia/vendor/backports/__init__.py + create mode 100644 custodia/vendor/backports/configparser/__init__.py + create mode 100644 custodia/vendor/backports/configparser/helpers.py + create mode 100644 custodia/vendor/configparser.py + +diff --git a/custodia/vendor/backports/__init__.py b/custodia/vendor/backports/__init__.py +new file mode 100644 +index 0000000..f84d25c +--- /dev/null ++++ b/custodia/vendor/backports/__init__.py +@@ -0,0 +1,11 @@ ++# A Python "namespace package" http://www.python.org/dev/peps/pep-0382/ ++# This always goes inside of a namespace package's __init__.py ++ ++from pkgutil import extend_path ++__path__ = extend_path(__path__, __name__) ++ ++try: ++ import pkg_resources ++ pkg_resources.declare_namespace(__name__) ++except ImportError: ++ pass +diff --git a/custodia/vendor/backports/configparser/__init__.py b/custodia/vendor/backports/configparser/__init__.py +new file mode 100644 +index 0000000..06d7a08 +--- /dev/null ++++ b/custodia/vendor/backports/configparser/__init__.py +@@ -0,0 +1,1390 @@ ++#!/usr/bin/env python ++# -*- coding: utf-8 -*- ++ ++"""Configuration file parser. ++ ++A configuration file consists of sections, lead by a "[section]" header, ++and followed by "name: value" entries, with continuations and such in ++the style of RFC 822. ++ ++Intrinsic defaults can be specified by passing them into the ++ConfigParser constructor as a dictionary. ++ ++class: ++ ++ConfigParser -- responsible for parsing a list of ++ configuration files, and managing the parsed database. ++ ++ methods: ++ ++ __init__(defaults=None, dict_type=_default_dict, allow_no_value=False, ++ delimiters=('=', ':'), comment_prefixes=('#', ';'), ++ inline_comment_prefixes=None, strict=True, ++ empty_lines_in_values=True, default_section='DEFAULT', ++ interpolation=, converters=): ++ Create the parser. When `defaults' is given, it is initialized into the ++ dictionary or intrinsic defaults. The keys must be strings, the values ++ must be appropriate for %()s string interpolation. ++ ++ When `dict_type' is given, it will be used to create the dictionary ++ objects for the list of sections, for the options within a section, and ++ for the default values. ++ ++ When `delimiters' is given, it will be used as the set of substrings ++ that divide keys from values. ++ ++ When `comment_prefixes' is given, it will be used as the set of ++ substrings that prefix comments in empty lines. Comments can be ++ indented. ++ ++ When `inline_comment_prefixes' is given, it will be used as the set of ++ substrings that prefix comments in non-empty lines. ++ ++ When `strict` is True, the parser won't allow for any section or option ++ duplicates while reading from a single source (file, string or ++ dictionary). Default is True. ++ ++ When `empty_lines_in_values' is False (default: True), each empty line ++ marks the end of an option. Otherwise, internal empty lines of ++ a multiline option are kept as part of the value. ++ ++ When `allow_no_value' is True (default: False), options without ++ values are accepted; the value presented for these is None. ++ ++ sections() ++ Return all the configuration section names, sans DEFAULT. ++ ++ has_section(section) ++ Return whether the given section exists. ++ ++ has_option(section, option) ++ Return whether the given option exists in the given section. ++ ++ options(section) ++ Return list of configuration options for the named section. ++ ++ read(filenames, encoding=None) ++ Read and parse the list of named configuration files, given by ++ name. A single filename is also allowed. Non-existing files ++ are ignored. Return list of successfully read files. ++ ++ read_file(f, filename=None) ++ Read and parse one configuration file, given as a file object. ++ The filename defaults to f.name; it is only used in error ++ messages (if f has no `name' attribute, the string `' is used). ++ ++ read_string(string) ++ Read configuration from a given string. ++ ++ read_dict(dictionary) ++ Read configuration from a dictionary. Keys are section names, ++ values are dictionaries with keys and values that should be present ++ in the section. If the used dictionary type preserves order, sections ++ and their keys will be added in order. Values are automatically ++ converted to strings. ++ ++ get(section, option, raw=False, vars=None, fallback=_UNSET) ++ Return a string value for the named option. All % interpolations are ++ expanded in the return values, based on the defaults passed into the ++ constructor and the DEFAULT section. Additional substitutions may be ++ provided using the `vars' argument, which must be a dictionary whose ++ contents override any pre-existing defaults. If `option' is a key in ++ `vars', the value from `vars' is used. ++ ++ getint(section, options, raw=False, vars=None, fallback=_UNSET) ++ Like get(), but convert value to an integer. ++ ++ getfloat(section, options, raw=False, vars=None, fallback=_UNSET) ++ Like get(), but convert value to a float. ++ ++ getboolean(section, options, raw=False, vars=None, fallback=_UNSET) ++ Like get(), but convert value to a boolean (currently case ++ insensitively defined as 0, false, no, off for False, and 1, true, ++ yes, on for True). Returns False or True. ++ ++ items(section=_UNSET, raw=False, vars=None) ++ If section is given, return a list of tuples with (name, value) for ++ each option in the section. Otherwise, return a list of tuples with ++ (section_name, section_proxy) for each section, including DEFAULTSECT. ++ ++ remove_section(section) ++ Remove the given file section and all its options. ++ ++ remove_option(section, option) ++ Remove the given option from the given section. ++ ++ set(section, option, value) ++ Set the given option. ++ ++ write(fp, space_around_delimiters=True) ++ Write the configuration state in .ini format. If ++ `space_around_delimiters' is True (the default), delimiters ++ between keys and values are surrounded by spaces. ++""" ++ ++from __future__ import absolute_import ++from __future__ import division ++from __future__ import print_function ++from __future__ import unicode_literals ++ ++from collections import MutableMapping ++import functools ++import io ++import itertools ++import re ++import sys ++import warnings ++ ++from backports.configparser.helpers import OrderedDict as _default_dict ++from backports.configparser.helpers import ChainMap as _ChainMap ++from backports.configparser.helpers import from_none, open, str, PY2 ++ ++__all__ = ["NoSectionError", "DuplicateOptionError", "DuplicateSectionError", ++ "NoOptionError", "InterpolationError", "InterpolationDepthError", ++ "InterpolationMissingOptionError", "InterpolationSyntaxError", ++ "ParsingError", "MissingSectionHeaderError", ++ "ConfigParser", "SafeConfigParser", "RawConfigParser", ++ "Interpolation", "BasicInterpolation", "ExtendedInterpolation", ++ "LegacyInterpolation", "SectionProxy", "ConverterMapping", ++ "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] ++ ++DEFAULTSECT = "DEFAULT" ++ ++MAX_INTERPOLATION_DEPTH = 10 ++ ++ ++# exception classes ++class Error(Exception): ++ """Base class for ConfigParser exceptions.""" ++ ++ def __init__(self, msg=''): ++ self.message = msg ++ Exception.__init__(self, msg) ++ ++ def __repr__(self): ++ return self.message ++ ++ __str__ = __repr__ ++ ++ ++class NoSectionError(Error): ++ """Raised when no section matches a requested option.""" ++ ++ def __init__(self, section): ++ Error.__init__(self, 'No section: %r' % (section,)) ++ self.section = section ++ self.args = (section, ) ++ ++ ++class DuplicateSectionError(Error): ++ """Raised when a section is repeated in an input source. ++ ++ Possible repetitions that raise this exception are: multiple creation ++ using the API or in strict parsers when a section is found more than once ++ in a single input file, string or dictionary. ++ """ ++ ++ def __init__(self, section, source=None, lineno=None): ++ msg = [repr(section), " already exists"] ++ if source is not None: ++ message = ["While reading from ", repr(source)] ++ if lineno is not None: ++ message.append(" [line {0:2d}]".format(lineno)) ++ message.append(": section ") ++ message.extend(msg) ++ msg = message ++ else: ++ msg.insert(0, "Section ") ++ Error.__init__(self, "".join(msg)) ++ self.section = section ++ self.source = source ++ self.lineno = lineno ++ self.args = (section, source, lineno) ++ ++ ++class DuplicateOptionError(Error): ++ """Raised by strict parsers when an option is repeated in an input source. ++ ++ Current implementation raises this exception only when an option is found ++ more than once in a single file, string or dictionary. ++ """ ++ ++ def __init__(self, section, option, source=None, lineno=None): ++ msg = [repr(option), " in section ", repr(section), ++ " already exists"] ++ if source is not None: ++ message = ["While reading from ", repr(source)] ++ if lineno is not None: ++ message.append(" [line {0:2d}]".format(lineno)) ++ message.append(": option ") ++ message.extend(msg) ++ msg = message ++ else: ++ msg.insert(0, "Option ") ++ Error.__init__(self, "".join(msg)) ++ self.section = section ++ self.option = option ++ self.source = source ++ self.lineno = lineno ++ self.args = (section, option, source, lineno) ++ ++ ++class NoOptionError(Error): ++ """A requested option was not found.""" ++ ++ def __init__(self, option, section): ++ Error.__init__(self, "No option %r in section: %r" % ++ (option, section)) ++ self.option = option ++ self.section = section ++ self.args = (option, section) ++ ++ ++class InterpolationError(Error): ++ """Base class for interpolation-related exceptions.""" ++ ++ def __init__(self, option, section, msg): ++ Error.__init__(self, msg) ++ self.option = option ++ self.section = section ++ self.args = (option, section, msg) ++ ++ ++class InterpolationMissingOptionError(InterpolationError): ++ """A string substitution required a setting which was not available.""" ++ ++ def __init__(self, option, section, rawval, reference): ++ msg = ("Bad value substitution: option {0!r} in section {1!r} contains " ++ "an interpolation key {2!r} which is not a valid option name. " ++ "Raw value: {3!r}".format(option, section, reference, rawval)) ++ InterpolationError.__init__(self, option, section, msg) ++ self.reference = reference ++ self.args = (option, section, rawval, reference) ++ ++ ++class InterpolationSyntaxError(InterpolationError): ++ """Raised when the source text contains invalid syntax. ++ ++ Current implementation raises this exception when the source text into ++ which substitutions are made does not conform to the required syntax. ++ """ ++ ++ ++class InterpolationDepthError(InterpolationError): ++ """Raised when substitutions are nested too deeply.""" ++ ++ def __init__(self, option, section, rawval): ++ msg = ("Recursion limit exceeded in value substitution: option {0!r} " ++ "in section {1!r} contains an interpolation key which " ++ "cannot be substituted in {2} steps. Raw value: {3!r}" ++ "".format(option, section, MAX_INTERPOLATION_DEPTH, ++ rawval)) ++ InterpolationError.__init__(self, option, section, msg) ++ self.args = (option, section, rawval) ++ ++ ++class ParsingError(Error): ++ """Raised when a configuration file does not follow legal syntax.""" ++ ++ def __init__(self, source=None, filename=None): ++ # Exactly one of `source'/`filename' arguments has to be given. ++ # `filename' kept for compatibility. ++ if filename and source: ++ raise ValueError("Cannot specify both `filename' and `source'. " ++ "Use `source'.") ++ elif not filename and not source: ++ raise ValueError("Required argument `source' not given.") ++ elif filename: ++ source = filename ++ Error.__init__(self, 'Source contains parsing errors: %r' % source) ++ self.source = source ++ self.errors = [] ++ self.args = (source, ) ++ ++ @property ++ def filename(self): ++ """Deprecated, use `source'.""" ++ warnings.warn( ++ "The 'filename' attribute will be removed in future versions. " ++ "Use 'source' instead.", ++ DeprecationWarning, stacklevel=2 ++ ) ++ return self.source ++ ++ @filename.setter ++ def filename(self, value): ++ """Deprecated, user `source'.""" ++ warnings.warn( ++ "The 'filename' attribute will be removed in future versions. " ++ "Use 'source' instead.", ++ DeprecationWarning, stacklevel=2 ++ ) ++ self.source = value ++ ++ def append(self, lineno, line): ++ self.errors.append((lineno, line)) ++ self.message += '\n\t[line %2d]: %s' % (lineno, line) ++ ++ ++class MissingSectionHeaderError(ParsingError): ++ """Raised when a key-value pair is found before any section header.""" ++ ++ def __init__(self, filename, lineno, line): ++ Error.__init__( ++ self, ++ 'File contains no section headers.\nfile: %r, line: %d\n%r' % ++ (filename, lineno, line)) ++ self.source = filename ++ self.lineno = lineno ++ self.line = line ++ self.args = (filename, lineno, line) ++ ++ ++# Used in parser getters to indicate the default behaviour when a specific ++# option is not found it to raise an exception. Created to enable `None' as ++# a valid fallback value. ++_UNSET = object() ++ ++ ++class Interpolation(object): ++ """Dummy interpolation that passes the value through with no changes.""" ++ ++ def before_get(self, parser, section, option, value, defaults): ++ return value ++ ++ def before_set(self, parser, section, option, value): ++ return value ++ ++ def before_read(self, parser, section, option, value): ++ return value ++ ++ def before_write(self, parser, section, option, value): ++ return value ++ ++ ++class BasicInterpolation(Interpolation): ++ """Interpolation as implemented in the classic ConfigParser. ++ ++ The option values can contain format strings which refer to other values in ++ the same section, or values in the special default section. ++ ++ For example: ++ ++ something: %(dir)s/whatever ++ ++ would resolve the "%(dir)s" to the value of dir. All reference ++ expansions are done late, on demand. If a user needs to use a bare % in ++ a configuration file, she can escape it by writing %%. Other % usage ++ is considered a user error and raises `InterpolationSyntaxError'.""" ++ ++ _KEYCRE = re.compile(r"%\(([^)]+)\)s") ++ ++ def before_get(self, parser, section, option, value, defaults): ++ L = [] ++ self._interpolate_some(parser, option, L, value, section, defaults, 1) ++ return ''.join(L) ++ ++ def before_set(self, parser, section, option, value): ++ tmp_value = value.replace('%%', '') # escaped percent signs ++ tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax ++ if '%' in tmp_value: ++ raise ValueError("invalid interpolation syntax in %r at " ++ "position %d" % (value, tmp_value.find('%'))) ++ return value ++ ++ def _interpolate_some(self, parser, option, accum, rest, section, map, ++ depth): ++ rawval = parser.get(section, option, raw=True, fallback=rest) ++ if depth > MAX_INTERPOLATION_DEPTH: ++ raise InterpolationDepthError(option, section, rawval) ++ while rest: ++ p = rest.find("%") ++ if p < 0: ++ accum.append(rest) ++ return ++ if p > 0: ++ accum.append(rest[:p]) ++ rest = rest[p:] ++ # p is no longer used ++ c = rest[1:2] ++ if c == "%": ++ accum.append("%") ++ rest = rest[2:] ++ elif c == "(": ++ m = self._KEYCRE.match(rest) ++ if m is None: ++ raise InterpolationSyntaxError(option, section, ++ "bad interpolation variable reference %r" % rest) ++ var = parser.optionxform(m.group(1)) ++ rest = rest[m.end():] ++ try: ++ v = map[var] ++ except KeyError: ++ raise from_none(InterpolationMissingOptionError( ++ option, section, rawval, var)) ++ if "%" in v: ++ self._interpolate_some(parser, option, accum, v, ++ section, map, depth + 1) ++ else: ++ accum.append(v) ++ else: ++ raise InterpolationSyntaxError( ++ option, section, ++ "'%%' must be followed by '%%' or '(', " ++ "found: %r" % (rest,)) ++ ++ ++class ExtendedInterpolation(Interpolation): ++ """Advanced variant of interpolation, supports the syntax used by ++ `zc.buildout'. Enables interpolation between sections.""" ++ ++ _KEYCRE = re.compile(r"\$\{([^}]+)\}") ++ ++ def before_get(self, parser, section, option, value, defaults): ++ L = [] ++ self._interpolate_some(parser, option, L, value, section, defaults, 1) ++ return ''.join(L) ++ ++ def before_set(self, parser, section, option, value): ++ tmp_value = value.replace('$$', '') # escaped dollar signs ++ tmp_value = self._KEYCRE.sub('', tmp_value) # valid syntax ++ if '$' in tmp_value: ++ raise ValueError("invalid interpolation syntax in %r at " ++ "position %d" % (value, tmp_value.find('$'))) ++ return value ++ ++ def _interpolate_some(self, parser, option, accum, rest, section, map, ++ depth): ++ rawval = parser.get(section, option, raw=True, fallback=rest) ++ if depth > MAX_INTERPOLATION_DEPTH: ++ raise InterpolationDepthError(option, section, rawval) ++ while rest: ++ p = rest.find("$") ++ if p < 0: ++ accum.append(rest) ++ return ++ if p > 0: ++ accum.append(rest[:p]) ++ rest = rest[p:] ++ # p is no longer used ++ c = rest[1:2] ++ if c == "$": ++ accum.append("$") ++ rest = rest[2:] ++ elif c == "{": ++ m = self._KEYCRE.match(rest) ++ if m is None: ++ raise InterpolationSyntaxError(option, section, ++ "bad interpolation variable reference %r" % rest) ++ path = m.group(1).split(':') ++ rest = rest[m.end():] ++ sect = section ++ opt = option ++ try: ++ if len(path) == 1: ++ opt = parser.optionxform(path[0]) ++ v = map[opt] ++ elif len(path) == 2: ++ sect = path[0] ++ opt = parser.optionxform(path[1]) ++ v = parser.get(sect, opt, raw=True) ++ else: ++ raise InterpolationSyntaxError( ++ option, section, ++ "More than one ':' found: %r" % (rest,)) ++ except (KeyError, NoSectionError, NoOptionError): ++ raise from_none(InterpolationMissingOptionError( ++ option, section, rawval, ":".join(path))) ++ if "$" in v: ++ self._interpolate_some(parser, opt, accum, v, sect, ++ dict(parser.items(sect, raw=True)), ++ depth + 1) ++ else: ++ accum.append(v) ++ else: ++ raise InterpolationSyntaxError( ++ option, section, ++ "'$' must be followed by '$' or '{', " ++ "found: %r" % (rest,)) ++ ++ ++class LegacyInterpolation(Interpolation): ++ """Deprecated interpolation used in old versions of ConfigParser. ++ Use BasicInterpolation or ExtendedInterpolation instead.""" ++ ++ _KEYCRE = re.compile(r"%\(([^)]*)\)s|.") ++ ++ def before_get(self, parser, section, option, value, vars): ++ rawval = value ++ depth = MAX_INTERPOLATION_DEPTH ++ while depth: # Loop through this until it's done ++ depth -= 1 ++ if value and "%(" in value: ++ replace = functools.partial(self._interpolation_replace, ++ parser=parser) ++ value = self._KEYCRE.sub(replace, value) ++ try: ++ value = value % vars ++ except KeyError as e: ++ raise from_none(InterpolationMissingOptionError( ++ option, section, rawval, e.args[0])) ++ else: ++ break ++ if value and "%(" in value: ++ raise InterpolationDepthError(option, section, rawval) ++ return value ++ ++ def before_set(self, parser, section, option, value): ++ return value ++ ++ @staticmethod ++ def _interpolation_replace(match, parser): ++ s = match.group(1) ++ if s is None: ++ return match.group() ++ else: ++ return "%%(%s)s" % parser.optionxform(s) ++ ++ ++class RawConfigParser(MutableMapping): ++ """ConfigParser that does not do interpolation.""" ++ ++ # Regular expressions for parsing section headers and options ++ _SECT_TMPL = r""" ++ \[ # [ ++ (?P
[^]]+) # very permissive! ++ \] # ] ++ """ ++ _OPT_TMPL = r""" ++ (?P