Issue #2974 - Part 41: Update configobj to version 5.
This version is compatible with Python 3 already.
This commit is contained in:
1 parent
1545a33858
commit
74762d6567
4 files changed
+357
-277
No files matched your search
+78
-37
@@ -1,47 +1,88 @@
|
||||
Metadata-Version: 1.0
|
||||
Metadata-Version: 2.1
|
||||
Name: configobj
|
||||
Version: 4.7.2
|
||||
Version: 5.0.9
|
||||
Summary: Config file reading, writing and validation.
|
||||
Home-page: http://www.voidspace.org.uk/python/configobj.html
|
||||
Author: Michael Foord & Nicola Larosa
|
||||
Author-email: fuzzyman@voidspace.org.uk
|
||||
License: UNKNOWN
|
||||
Download-URL: http://www.voidspace.org.uk/downloads/configobj-4.7.2.zip
|
||||
Description: **ConfigObj** is a simple but powerful config file reader and writer: an *ini
|
||||
file round tripper*. Its main feature is that it is very easy to use, with a
|
||||
straightforward programmer's interface and a simple syntax for config files.
|
||||
It has lots of other features though :
|
||||
|
||||
* Nested sections (subsections), to any level
|
||||
* List values
|
||||
* Multiple line values
|
||||
* Full Unicode support
|
||||
* String interpolation (substitution)
|
||||
* Integrated with a powerful validation system
|
||||
|
||||
- including automatic type checking/conversion
|
||||
- and allowing default values
|
||||
- repeated sections
|
||||
|
||||
* All comments in the file are preserved
|
||||
* The order of keys/sections is preserved
|
||||
* Powerful ``unrepr`` mode for storing/retrieving Python data-types
|
||||
|
||||
| Release 4.7.2 fixes several bugs in 4.7.1
|
||||
| Release 4.7.1 fixes a bug with the deprecated options keyword in
|
||||
| 4.7.0.
|
||||
| Release 4.7.0 improves performance adds features for validation and
|
||||
| fixes some bugs.
|
||||
Home-page: https://github.com/DiffSK/configobj
|
||||
Author: Rob Dennis, Eli Courtwright (Michael Foord & Nicola Larosa original maintainers)
|
||||
Author-email: rdennis+configobj@gmail.com, eli@courtwright.org, michael@python.org, nico@tekNico.net
|
||||
License: BSD-3-Clause
|
||||
Keywords: config,ini,dictionary,application,admin,sysadmin,configuration,validation
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 6 - Mature
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2.3
|
||||
Classifier: Programming Language :: Python :: 2.4
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Topic :: Software Development :: Libraries
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Requires-Python: >=3.7
|
||||
License-File: LICENSE
|
||||
|
||||
**ConfigObj** is a simple but powerful config file reader and writer: an *ini
|
||||
file round tripper*. Its main feature is that it is very easy to use, with a
|
||||
straightforward programmer's interface and a simple syntax for config files.
|
||||
|
||||
List of Features
|
||||
----------------
|
||||
|
||||
* Nested sections (subsections), to any level
|
||||
* List values
|
||||
* Multiple line values
|
||||
* Full Unicode support
|
||||
* String interpolation (substitution)
|
||||
* Integrated with a powerful validation system
|
||||
|
||||
- including automatic type checking/conversion
|
||||
- and allowing default values
|
||||
- repeated sections
|
||||
|
||||
* All comments in the file are preserved
|
||||
* The order of keys/sections is preserved
|
||||
* Powerful ``unrepr`` mode for storing/retrieving Python data-types
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
Release 5.0.9
|
||||
"""""""""""""
|
||||
|
||||
* drop support for Python 2 and <3.7
|
||||
* fix CVE-2023-26112, ReDoS attack
|
||||
|
||||
Release 5.0.8
|
||||
"""""""""""""
|
||||
|
||||
* fixing/test for a regression introduced in 5.0.7 that prevented ``import validate`` from working
|
||||
|
||||
|
||||
Release 5.0.7
|
||||
"""""""""""""
|
||||
|
||||
* update testing to validate against python version 2.7 and 3.5-3.11
|
||||
* update broken links / non-existent services and references
|
||||
|
||||
Older Releases
|
||||
""""""""""""""
|
||||
|
||||
* Release 5.0.6 improves error messages in certain edge cases
|
||||
* Release 5.0.5 corrects a unicode-bug that still existed in writing files
|
||||
* Release 5.0.4 corrects a unicode-bug that still existed in reading files after
|
||||
fixing lists of string in 5.0.3
|
||||
* Release 5.0.3 corrects errors related to the incorrectly handling unicode
|
||||
encoding and writing out files
|
||||
* Release 5.0.2 adds a specific error message when trying to install on
|
||||
Python versions older than 2.5
|
||||
* Release 5.0.1 fixes a regression with unicode conversion not happening
|
||||
in certain cases PY2
|
||||
* Release 5.0.0 updates the supported Python versions to 2.6, 2.7, 3.2, 3.3
|
||||
and is otherwise unchanged
|
||||
* Release 4.7.2 fixes several bugs in 4.7.1
|
||||
* Release 4.7.1 fixes a bug with the deprecated options keyword in 4.7.0.
|
||||
* Release 4.7.0 improves performance adds features for validation and
|
||||
fixes some bugs.
|
||||
+124
-116
@@ -1,22 +1,17 @@
|
||||
# configobj.py
|
||||
# A config file reader/writer that supports nested sections in config files.
|
||||
# Copyright (C) 2005-2010 Michael Foord, Nicola Larosa
|
||||
# E-mail: fuzzyman AT voidspace DOT org DOT uk
|
||||
# nico AT tekNico DOT net
|
||||
# Copyright (C) 2005-2014:
|
||||
# (name) : (email)
|
||||
# Michael Foord: fuzzyman AT voidspace DOT org DOT uk
|
||||
# Nicola Larosa: nico AT tekNico DOT net
|
||||
# Rob Dennis: rdennis AT gmail DOT com
|
||||
# Eli Courtwright: eli AT courtwright DOT org
|
||||
|
||||
# ConfigObj 4
|
||||
# http://www.voidspace.org.uk/python/configobj.html
|
||||
# This software is licensed under the terms of the BSD license.
|
||||
# http://opensource.org/licenses/BSD-3-Clause
|
||||
|
||||
# Released subject to the BSD License
|
||||
# Please see http://www.voidspace.org.uk/python/license.shtml
|
||||
|
||||
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
|
||||
# For information about bugfixes, updates and support, please join the
|
||||
# ConfigObj mailing list:
|
||||
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
|
||||
# Comments, suggestions and bug reports welcome.
|
||||
|
||||
from __future__ import generators
|
||||
# ConfigObj 5 - main repository for documentation and issue tracking:
|
||||
# https://github.com/DiffSK/configobj
|
||||
|
||||
import os
|
||||
import re
|
||||
@@ -83,17 +78,7 @@ tdquot = "'''%s'''"
|
||||
# Sentinel for use in getattr calls to replace hasattr
|
||||
MISSING = object()
|
||||
|
||||
__version__ = '4.7.2'
|
||||
|
||||
try:
|
||||
any
|
||||
except NameError:
|
||||
def any(iterable):
|
||||
for entry in iterable:
|
||||
if entry:
|
||||
return True
|
||||
return False
|
||||
|
||||
__version__ = '5.0.9'
|
||||
|
||||
__all__ = (
|
||||
'__version__',
|
||||
@@ -137,8 +122,6 @@ OPTION_DEFAULTS = {
|
||||
'write_empty_values': False,
|
||||
}
|
||||
|
||||
|
||||
|
||||
def getObj(s):
|
||||
global compiler
|
||||
if compiler is None:
|
||||
@@ -155,7 +138,6 @@ class UnknownType(Exception):
|
||||
class Builder(object):
|
||||
|
||||
def build(self, o):
|
||||
m = getattr(self, 'build_' + o.__class__.__name__, None)
|
||||
if m is None:
|
||||
raise UnknownType(o.__class__.__name__)
|
||||
return m(o)
|
||||
@@ -214,8 +196,10 @@ _builder = Builder()
|
||||
def unrepr(s):
|
||||
if not s:
|
||||
return s
|
||||
return _builder.build(getObj(s))
|
||||
|
||||
|
||||
# this is supposed to be safe
|
||||
import ast
|
||||
return ast.literal_eval(s)
|
||||
|
||||
|
||||
class ConfigObjError(SyntaxError):
|
||||
@@ -566,11 +550,11 @@ class Section(dict):
|
||||
"""Fetch the item and do string interpolation."""
|
||||
val = dict.__getitem__(self, key)
|
||||
if self.main.interpolation:
|
||||
if isinstance(val, basestring):
|
||||
if isinstance(val, str):
|
||||
return self._interpolate(key, val)
|
||||
if isinstance(val, list):
|
||||
def _check(entry):
|
||||
if isinstance(entry, basestring):
|
||||
if isinstance(entry, str):
|
||||
return self._interpolate(key, entry)
|
||||
return entry
|
||||
new = [_check(entry) for entry in val]
|
||||
@@ -593,7 +577,7 @@ class Section(dict):
|
||||
``unrepr`` must be set when setting a value to a dictionary, without
|
||||
creating a new sub-section.
|
||||
"""
|
||||
if not isinstance(key, basestring):
|
||||
if not isinstance(key, str):
|
||||
raise ValueError('The key "%s" is not a string.' % key)
|
||||
|
||||
# add the comment
|
||||
@@ -627,11 +611,11 @@ class Section(dict):
|
||||
if key not in self:
|
||||
self.scalars.append(key)
|
||||
if not self.main.stringify:
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, str):
|
||||
pass
|
||||
elif isinstance(value, (list, tuple)):
|
||||
for entry in value:
|
||||
if not isinstance(entry, basestring):
|
||||
if not isinstance(entry, str):
|
||||
raise TypeError('Value is not a string "%s".' % entry)
|
||||
else:
|
||||
raise TypeError('Value is not a string "%s".' % value)
|
||||
@@ -972,7 +956,7 @@ class Section(dict):
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
if not isinstance(val, basestring):
|
||||
if not isinstance(val, str):
|
||||
# TODO: Why do we raise a KeyError here?
|
||||
raise KeyError()
|
||||
else:
|
||||
@@ -1013,15 +997,15 @@ class Section(dict):
|
||||
|
||||
>>> a = ConfigObj()
|
||||
>>> a['a'] = 'fish'
|
||||
>>> a.as_float('a')
|
||||
>>> a.as_float('a') #doctest: +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
ValueError: invalid literal for float(): fish
|
||||
>>> a['b'] = '1'
|
||||
>>> a.as_float('b')
|
||||
1.0
|
||||
>>> a['b'] = '3.2'
|
||||
>>> a.as_float('b')
|
||||
3.2000000000000002
|
||||
>>> a.as_float('b') #doctest: +ELLIPSIS
|
||||
3.2...
|
||||
"""
|
||||
return float(self[key])
|
||||
|
||||
@@ -1243,12 +1227,11 @@ class ConfigObj(Section):
|
||||
|
||||
|
||||
def _load(self, infile, configspec):
|
||||
if isinstance(infile, basestring):
|
||||
if isinstance(infile, str):
|
||||
self.filename = infile
|
||||
if os.path.isfile(infile):
|
||||
h = open(infile, 'rb')
|
||||
infile = h.read() or []
|
||||
h.close()
|
||||
with open(infile, 'rb') as h:
|
||||
content = h.readlines() or []
|
||||
elif self.file_error:
|
||||
# raise an error if the file doesn't exist
|
||||
raise IOError('Config file not found: "%s".' % self.filename)
|
||||
@@ -1257,13 +1240,12 @@ class ConfigObj(Section):
|
||||
if self.create_empty:
|
||||
# this is a good test that the filename specified
|
||||
# isn't impossible - like on a non-existent device
|
||||
h = open(infile, 'w')
|
||||
h.write('')
|
||||
h.close()
|
||||
infile = []
|
||||
with open(infile, 'w') as h:
|
||||
h.write('')
|
||||
content = []
|
||||
|
||||
elif isinstance(infile, (list, tuple)):
|
||||
infile = list(infile)
|
||||
content = list(infile)
|
||||
|
||||
elif isinstance(infile, dict):
|
||||
# initialise self
|
||||
@@ -1291,21 +1273,21 @@ class ConfigObj(Section):
|
||||
|
||||
elif getattr(infile, 'read', MISSING) is not MISSING:
|
||||
# This supports file like objects
|
||||
infile = infile.read() or []
|
||||
content = infile.read() or []
|
||||
# needs splitting into lines - but needs doing *after* decoding
|
||||
# in case it's not an 8 bit encoding
|
||||
else:
|
||||
raise TypeError('infile must be a filename, file like object, or list of lines.')
|
||||
|
||||
if infile:
|
||||
|
||||
if content:
|
||||
# don't do it for the empty ConfigObj
|
||||
infile = self._handle_bom(infile)
|
||||
content = self._handle_bom(content)
|
||||
# infile is now *always* a list
|
||||
#
|
||||
# Set the newlines attribute (first line ending it finds)
|
||||
# and strip trailing '\n' or '\r' from lines
|
||||
for line in infile:
|
||||
if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
|
||||
for line in content:
|
||||
if (not line) or (line[-1] not in ('\r', '\n')):
|
||||
continue
|
||||
for end in ('\r\n', '\n', '\r'):
|
||||
if line.endswith(end):
|
||||
@@ -1313,9 +1295,10 @@ class ConfigObj(Section):
|
||||
break
|
||||
break
|
||||
|
||||
infile = [line.rstrip('\r\n') for line in infile]
|
||||
assert all(isinstance(line, str) for line in content), repr(content)
|
||||
content = [line.rstrip('\r\n') for line in content]
|
||||
|
||||
self._parse(infile)
|
||||
self._parse(content)
|
||||
# if we had any errors, now is the time to raise them
|
||||
if self._errors:
|
||||
info = "at line %s." % self._errors[0].line_number
|
||||
@@ -1404,6 +1387,7 @@ class ConfigObj(Section):
|
||||
``infile`` must always be returned as a list of lines, but may be
|
||||
passed in as a single string.
|
||||
"""
|
||||
|
||||
if ((self.encoding is not None) and
|
||||
(self.encoding.lower() not in BOM_LIST)):
|
||||
# No need to check for a BOM
|
||||
@@ -1415,6 +1399,13 @@ class ConfigObj(Section):
|
||||
line = infile[0]
|
||||
else:
|
||||
line = infile
|
||||
|
||||
if isinstance(line, str):
|
||||
# it's already decoded and there's no need to do anything
|
||||
# else, just use the _decode utility method to handle
|
||||
# listifying appropriately
|
||||
return self._decode(infile, self.encoding)
|
||||
|
||||
if self.encoding is not None:
|
||||
# encoding explicitly supplied
|
||||
# And it could have an associated BOM
|
||||
@@ -1454,7 +1445,8 @@ class ConfigObj(Section):
|
||||
|
||||
# No encoding specified - so we need to check for UTF8/UTF16
|
||||
for BOM, (encoding, final_encoding) in list(BOMS.items()):
|
||||
if not line.startswith(BOM):
|
||||
if not isinstance(line, bytes) or not line.startswith(BOM):
|
||||
# didn't specify a BOM, or it's not a bytestring
|
||||
continue
|
||||
else:
|
||||
# BOM discovered
|
||||
@@ -1468,25 +1460,28 @@ class ConfigObj(Section):
|
||||
infile[0] = newline
|
||||
else:
|
||||
infile = newline
|
||||
# UTF8 - don't decode
|
||||
if isinstance(infile, basestring):
|
||||
# UTF-8
|
||||
if isinstance(infile, str):
|
||||
return infile.splitlines(True)
|
||||
elif isinstance(infile, bytes):
|
||||
return infile.decode('utf-8').splitlines(True)
|
||||
else:
|
||||
return infile
|
||||
return self._decode(infile, 'utf-8')
|
||||
# UTF16 - have to decode
|
||||
return self._decode(infile, encoding)
|
||||
|
||||
# No BOM discovered and no encoding specified, just return
|
||||
if isinstance(infile, basestring):
|
||||
# infile read from a file will be a single string
|
||||
return infile.splitlines(True)
|
||||
return infile
|
||||
|
||||
# No BOM discovered and no encoding specified, default to UTF-8
|
||||
if isinstance(infile, bytes):
|
||||
return infile.decode('utf-8').splitlines(True)
|
||||
else:
|
||||
return self._decode(infile, 'utf-8')
|
||||
|
||||
|
||||
def _a_to_u(self, aString):
|
||||
"""Decode ASCII strings to unicode if a self.encoding is specified."""
|
||||
if self.encoding:
|
||||
return aString.decode('ascii')
|
||||
if isinstance(aString, bytes) and self.encoding:
|
||||
return aString.decode(self.encoding)
|
||||
else:
|
||||
return aString
|
||||
|
||||
@@ -1497,34 +1492,42 @@ class ConfigObj(Section):
|
||||
|
||||
if is a string, it also needs converting to a list.
|
||||
"""
|
||||
if isinstance(infile, basestring):
|
||||
# can't be unicode
|
||||
if isinstance(infile, str):
|
||||
return infile.splitlines(True)
|
||||
if isinstance(infile, bytes):
|
||||
# NOTE: Could raise a ``UnicodeDecodeError``
|
||||
return infile.decode(encoding).splitlines(True)
|
||||
for i, line in enumerate(infile):
|
||||
if not isinstance(line, str):
|
||||
# NOTE: The isinstance test here handles mixed lists of unicode/string
|
||||
# NOTE: But the decode will break on any non-string values
|
||||
# NOTE: Or could raise a ``UnicodeDecodeError``
|
||||
infile[i] = line.decode(encoding)
|
||||
if encoding:
|
||||
return infile.decode(encoding).splitlines(True)
|
||||
else:
|
||||
return infile.splitlines(True)
|
||||
|
||||
if encoding:
|
||||
for i, line in enumerate(infile):
|
||||
if isinstance(line, bytes):
|
||||
# NOTE: The isinstance test here handles mixed lists of unicode/string
|
||||
# NOTE: But the decode will break on any non-string values
|
||||
# NOTE: Or could raise a ``UnicodeDecodeError``
|
||||
infile[i] = line.decode(encoding)
|
||||
return infile
|
||||
|
||||
|
||||
def _decode_element(self, line):
|
||||
"""Decode element to unicode if necessary."""
|
||||
if not self.encoding:
|
||||
return line
|
||||
if isinstance(line, str) and self.default_encoding:
|
||||
if isinstance(line, bytes) and self.default_encoding:
|
||||
return line.decode(self.default_encoding)
|
||||
return line
|
||||
else:
|
||||
return line
|
||||
|
||||
|
||||
# TODO: this may need to be modified
|
||||
def _str(self, value):
|
||||
"""
|
||||
Used by ``stringify`` within validate, to turn non-string values
|
||||
into strings.
|
||||
"""
|
||||
if not isinstance(value, basestring):
|
||||
if not isinstance(value, str):
|
||||
# intentially 'str' because it's just whatever the "normal"
|
||||
# string type is for the python version we're dealing with
|
||||
return str(value)
|
||||
else:
|
||||
return value
|
||||
@@ -1571,7 +1574,7 @@ class ConfigObj(Section):
|
||||
self.indent_type = indent
|
||||
cur_depth = sect_open.count('[')
|
||||
if cur_depth != sect_close.count(']'):
|
||||
self._handle_error("Cannot compute the section depth at line %s.",
|
||||
self._handle_error("Cannot compute the section depth",
|
||||
NestingError, infile, cur_index)
|
||||
continue
|
||||
|
||||
@@ -1581,7 +1584,7 @@ class ConfigObj(Section):
|
||||
parent = self._match_depth(this_section,
|
||||
cur_depth).parent
|
||||
except SyntaxError:
|
||||
self._handle_error("Cannot compute nesting level at line %s.",
|
||||
self._handle_error("Cannot compute nesting level",
|
||||
NestingError, infile, cur_index)
|
||||
continue
|
||||
elif cur_depth == this_section.depth:
|
||||
@@ -1591,12 +1594,13 @@ class ConfigObj(Section):
|
||||
# the new section is a child the current section
|
||||
parent = this_section
|
||||
else:
|
||||
self._handle_error("Section too nested at line %s.",
|
||||
self._handle_error("Section too nested",
|
||||
NestingError, infile, cur_index)
|
||||
continue
|
||||
|
||||
sect_name = self._unquote(sect_name)
|
||||
if sect_name in parent:
|
||||
self._handle_error('Duplicate section name at line %s.',
|
||||
self._handle_error('Duplicate section name',
|
||||
DuplicateError, infile, cur_index)
|
||||
continue
|
||||
|
||||
@@ -1615,10 +1619,8 @@ class ConfigObj(Section):
|
||||
# so it should be a valid ``key = value`` line
|
||||
mat = self._keyword.match(line)
|
||||
if mat is None:
|
||||
# it neither matched as a keyword
|
||||
# or a section marker
|
||||
self._handle_error(
|
||||
'Invalid line at line "%s".',
|
||||
'Invalid line ({0!r}) (matched as neither section nor keyword)'.format(line),
|
||||
ParseError, infile, cur_index)
|
||||
else:
|
||||
# is a keyword value
|
||||
@@ -1633,7 +1635,7 @@ class ConfigObj(Section):
|
||||
value, infile, cur_index, maxline)
|
||||
except SyntaxError:
|
||||
self._handle_error(
|
||||
'Parse error in value at line %s.',
|
||||
'Parse error in multiline value',
|
||||
ParseError, infile, cur_index)
|
||||
continue
|
||||
else:
|
||||
@@ -1643,9 +1645,9 @@ class ConfigObj(Section):
|
||||
value = unrepr(value)
|
||||
except Exception as e:
|
||||
if type(e) == UnknownType:
|
||||
msg = 'Unknown name or type in value at line %s.'
|
||||
msg = 'Unknown name or type in value'
|
||||
else:
|
||||
msg = 'Parse error in value at line %s.'
|
||||
msg = 'Parse error from unrepr-ing multiline value'
|
||||
self._handle_error(msg, UnreprError, infile,
|
||||
cur_index)
|
||||
continue
|
||||
@@ -1656,9 +1658,9 @@ class ConfigObj(Section):
|
||||
value = unrepr(value)
|
||||
except Exception as e:
|
||||
if isinstance(e, UnknownType):
|
||||
msg = 'Unknown name or type in value at line %s.'
|
||||
msg = 'Unknown name or type in value'
|
||||
else:
|
||||
msg = 'Parse error in value at line %s.'
|
||||
msg = 'Parse error from unrepr-ing value'
|
||||
self._handle_error(msg, UnreprError, infile,
|
||||
cur_index)
|
||||
continue
|
||||
@@ -1668,14 +1670,14 @@ class ConfigObj(Section):
|
||||
(value, comment) = self._handle_value(value)
|
||||
except SyntaxError:
|
||||
self._handle_error(
|
||||
'Parse error in value at line %s.',
|
||||
'Parse error in value',
|
||||
ParseError, infile, cur_index)
|
||||
continue
|
||||
#
|
||||
key = self._unquote(key)
|
||||
if key in this_section:
|
||||
self._handle_error(
|
||||
'Duplicate keyword name at line %s.',
|
||||
'Duplicate keyword name',
|
||||
DuplicateError, infile, cur_index)
|
||||
continue
|
||||
# add the key.
|
||||
@@ -1726,7 +1728,7 @@ class ConfigObj(Section):
|
||||
"""
|
||||
line = infile[cur_index]
|
||||
cur_index += 1
|
||||
message = text % cur_index
|
||||
message = '{0} at line {1}.'.format(text, cur_index)
|
||||
error = ErrorClass(message, cur_index, line)
|
||||
if self.raise_errors:
|
||||
# raise the error - parsing stops here
|
||||
@@ -1777,8 +1779,10 @@ class ConfigObj(Section):
|
||||
return self._quote(value[0], multiline=False) + ','
|
||||
return ', '.join([self._quote(val, multiline=False)
|
||||
for val in value])
|
||||
if not isinstance(value, basestring):
|
||||
if not isinstance(value, str):
|
||||
if self.stringify:
|
||||
# intentially 'str' because it's just whatever the "normal"
|
||||
# string type is for the python version we're dealing with
|
||||
value = str(value)
|
||||
else:
|
||||
raise TypeError('Value "%s" is not a string.' % value)
|
||||
@@ -2049,7 +2053,7 @@ class ConfigObj(Section):
|
||||
this_entry = section[entry]
|
||||
comment = self._handle_comment(section.inline_comments[entry])
|
||||
|
||||
if isinstance(this_entry, dict):
|
||||
if isinstance(this_entry, Section):
|
||||
# a section
|
||||
out.append(self._write_marker(
|
||||
indent_string,
|
||||
@@ -2097,21 +2101,25 @@ class ConfigObj(Section):
|
||||
# Windows specific hack to avoid writing '\r\r\n'
|
||||
newline = '\n'
|
||||
output = self._a_to_u(newline).join(out)
|
||||
if self.encoding:
|
||||
output = output.encode(self.encoding)
|
||||
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
|
||||
# Add the UTF8 BOM
|
||||
output = BOM_UTF8 + output
|
||||
|
||||
if not output.endswith(newline):
|
||||
output += newline
|
||||
if outfile is not None:
|
||||
outfile.write(output)
|
||||
else:
|
||||
h = open(self.filename, 'wb')
|
||||
h.write(output)
|
||||
h.close()
|
||||
|
||||
if isinstance(output, bytes):
|
||||
output_bytes = output
|
||||
else:
|
||||
output_bytes = output.encode(self.encoding or
|
||||
self.default_encoding or
|
||||
'ascii')
|
||||
|
||||
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
|
||||
# Add the UTF8 BOM
|
||||
output_bytes = BOM_UTF8 + output_bytes
|
||||
|
||||
if outfile is not None:
|
||||
outfile.write(output_bytes)
|
||||
else:
|
||||
with open(self.filename, 'wb') as h:
|
||||
h.write(output_bytes)
|
||||
|
||||
def validate(self, validator, preserve_errors=False, copy=False,
|
||||
section=None):
|
||||
@@ -2155,7 +2163,7 @@ class ConfigObj(Section):
|
||||
if preserve_errors:
|
||||
# We do this once to remove a top level dependency on the validate module
|
||||
# Which makes importing configobj faster
|
||||
from validate import VdtMissingValue
|
||||
from configobj.validate import VdtMissingValue
|
||||
self._vdtMissingValue = VdtMissingValue
|
||||
|
||||
section = self
|
||||
@@ -2338,7 +2346,7 @@ class ConfigObj(Section):
|
||||
This method raises a ``ReloadError`` if the ConfigObj doesn't have
|
||||
a filename attribute pointing to a file.
|
||||
"""
|
||||
if not isinstance(self.filename, basestring):
|
||||
if not isinstance(self.filename, str):
|
||||
raise ReloadError()
|
||||
|
||||
filename = self.filename
|
||||
@@ -2416,12 +2424,12 @@ def flatten_errors(cfg, res, levels=None, results=None):
|
||||
levels = []
|
||||
results = []
|
||||
if res == True:
|
||||
return results
|
||||
return sorted(results)
|
||||
if res == False or isinstance(res, Exception):
|
||||
results.append((levels[:], None, res))
|
||||
if levels:
|
||||
levels.pop()
|
||||
return results
|
||||
return sorted(results)
|
||||
for (key, val) in list(res.items()):
|
||||
if val == True:
|
||||
continue
|
||||
@@ -2436,7 +2444,7 @@ def flatten_errors(cfg, res, levels=None, results=None):
|
||||
if levels:
|
||||
levels.pop()
|
||||
#
|
||||
return results
|
||||
return sorted(results)
|
||||
|
||||
|
||||
def get_extra_values(conf, _prepend=()):
|
||||
|
||||
+75
-39
@@ -1,31 +1,54 @@
|
||||
#!/usr/bin/env python
|
||||
# setup.py
|
||||
# Install script for ConfigObj
|
||||
# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa
|
||||
# E-mail: fuzzyman AT voidspace DOT org DOT uk
|
||||
# mark AT la-la DOT com
|
||||
# nico AT tekNico DOT net
|
||||
# -*- coding: utf-8 -*-
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
"""Install script for ConfigObj"""
|
||||
|
||||
# Copyright (C) 2005-2014:
|
||||
# (name) : (email)
|
||||
# Michael Foord: fuzzyman AT voidspace DOT org DOT uk
|
||||
# Mark Andrews: mark AT la-la DOT com
|
||||
# Nicola Larosa: nico AT tekNico DOT net
|
||||
# Rob Dennis: rdennis AT gmail DOT com
|
||||
# Eli Courtwright: eli AT courtwright DOT org
|
||||
|
||||
# This software is licensed under the terms of the BSD license.
|
||||
# http://www.voidspace.org.uk/python/license.shtml
|
||||
|
||||
# http://opensource.org/licenses/BSD-3-Clause
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from distutils.core import setup
|
||||
from configobj import __version__ as VERSION
|
||||
from contextlib import closing
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
if sys.version_info[0] < 2:
|
||||
print('for Python versions < 3 use configobj '
|
||||
'version 5.0.8')
|
||||
sys.exit(1)
|
||||
|
||||
__here__ = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
NAME = 'configobj'
|
||||
|
||||
MODULES = 'configobj', 'validate'
|
||||
|
||||
MODULES = []
|
||||
PACKAGES = ['configobj', 'validate']
|
||||
DESCRIPTION = 'Config file reading, writing and validation.'
|
||||
URL = 'https://github.com/DiffSK/configobj'
|
||||
|
||||
URL = 'http://www.voidspace.org.uk/python/configobj.html'
|
||||
|
||||
DOWNLOAD_URL = "http://www.voidspace.org.uk/downloads/configobj-%s.zip" % VERSION
|
||||
VERSION = ''
|
||||
with closing(open(os.path.join(__here__, 'src', PACKAGES[0], '_version.py'), 'r')) as handle:
|
||||
for line in handle.readlines():
|
||||
if line.startswith('__version__'):
|
||||
VERSION = re.split('''['"]''', line)[1]
|
||||
assert re.match(r"[0-9](\.[0-9]+)", VERSION), "No semantic version found in 'configobj._version'"
|
||||
|
||||
LONG_DESCRIPTION = """**ConfigObj** is a simple but powerful config file reader and writer: an *ini
|
||||
file round tripper*. Its main feature is that it is very easy to use, with a
|
||||
straightforward programmer's interface and a simple syntax for config files.
|
||||
It has lots of other features though :
|
||||
|
||||
List of Features
|
||||
----------------
|
||||
|
||||
* Nested sections (subsections), to any level
|
||||
* List values
|
||||
@@ -42,42 +65,55 @@ It has lots of other features though :
|
||||
* The order of keys/sections is preserved
|
||||
* Powerful ``unrepr`` mode for storing/retrieving Python data-types
|
||||
|
||||
| Release 4.7.2 fixes several bugs in 4.7.1
|
||||
| Release 4.7.1 fixes a bug with the deprecated options keyword in
|
||||
| 4.7.0.
|
||||
| Release 4.7.0 improves performance adds features for validation and
|
||||
| fixes some bugs."""
|
||||
"""
|
||||
|
||||
try:
|
||||
with io.open('CHANGES.rst', encoding='utf-8') as handle:
|
||||
LONG_DESCRIPTION += handle.read()
|
||||
except EnvironmentError as exc:
|
||||
# Build / install anyway
|
||||
print("WARNING: Cannot open/read CHANGES.rst due to {}".format(exc))
|
||||
|
||||
CLASSIFIERS = [
|
||||
# Details at http://pypi.python.org/pypi?:action=list_classifiers
|
||||
'Development Status :: 6 - Mature',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2.3',
|
||||
'Programming Language :: Python :: 2.4',
|
||||
'Programming Language :: Python :: 2.5',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Operating System :: OS Independent',
|
||||
'Topic :: Software Development :: Libraries',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
]
|
||||
|
||||
AUTHOR = 'Michael Foord & Nicola Larosa'
|
||||
AUTHOR = 'Rob Dennis, Eli Courtwright (Michael Foord & Nicola Larosa original maintainers)'
|
||||
|
||||
AUTHOR_EMAIL = 'fuzzyman@voidspace.org.uk'
|
||||
AUTHOR_EMAIL = 'rdennis+configobj@gmail.com, eli@courtwright.org, michael@python.org, nico@tekNico.net'
|
||||
|
||||
KEYWORDS = "config, ini, dictionary, application, admin, sysadmin, configuration, validation".split(', ')
|
||||
|
||||
project = dict(
|
||||
name=NAME,
|
||||
version=VERSION,
|
||||
description=DESCRIPTION,
|
||||
long_description=LONG_DESCRIPTION,
|
||||
author=AUTHOR,
|
||||
author_email=AUTHOR_EMAIL,
|
||||
url=URL,
|
||||
py_modules=MODULES,
|
||||
package_dir={'': 'src'},
|
||||
packages=PACKAGES,
|
||||
python_requires='>=3.7',
|
||||
classifiers=CLASSIFIERS,
|
||||
keywords=KEYWORDS,
|
||||
license='BSD-3-Clause',
|
||||
)
|
||||
|
||||
setup(name=NAME,
|
||||
version=VERSION,
|
||||
description=DESCRIPTION,
|
||||
long_description=LONG_DESCRIPTION,
|
||||
download_url=DOWNLOAD_URL,
|
||||
author=AUTHOR,
|
||||
author_email=AUTHOR_EMAIL,
|
||||
url=URL,
|
||||
py_modules=MODULES,
|
||||
classifiers=CLASSIFIERS,
|
||||
keywords=KEYWORDS
|
||||
)
|
||||
if __name__ == '__main__':
|
||||
setup(**project)
|
||||
@@ -1,20 +1,18 @@
|
||||
# validate.py
|
||||
# A Validator object
|
||||
# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa
|
||||
# E-mail: fuzzyman AT voidspace DOT org DOT uk
|
||||
# mark AT la-la DOT com
|
||||
# nico AT tekNico DOT net
|
||||
# Copyright (C) 2005-2014:
|
||||
# (name) : (email)
|
||||
# Michael Foord: fuzzyman AT voidspace DOT org DOT uk
|
||||
# Mark Andrews: mark AT la-la DOT com
|
||||
# Nicola Larosa: nico AT tekNico DOT net
|
||||
# Rob Dennis: rdennis AT gmail DOT com
|
||||
# Eli Courtwright: eli AT courtwright DOT org
|
||||
|
||||
# This software is licensed under the terms of the BSD license.
|
||||
# http://www.voidspace.org.uk/python/license.shtml
|
||||
# Basically you're free to copy, modify, distribute and relicense it,
|
||||
# So long as you keep a copy of the license with it.
|
||||
# http://opensource.org/licenses/BSD-3-Clause
|
||||
|
||||
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
|
||||
# For information about bugfixes, updates and support, please join the
|
||||
# ConfigObj mailing list:
|
||||
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
|
||||
# Comments, suggestions and bug reports welcome.
|
||||
# ConfigObj 5 - main repository for documentation and issue tracking:
|
||||
# https://github.com/DiffSK/configobj
|
||||
|
||||
"""
|
||||
The Validator object is used to check that supplied values
|
||||
@@ -160,11 +158,12 @@ __all__ = (
|
||||
'is_ip_addr_list',
|
||||
'is_mixed_list',
|
||||
'is_option',
|
||||
'__docformat__',
|
||||
)
|
||||
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
_list_arg = re.compile(r'''
|
||||
@@ -244,17 +243,6 @@ _paramstring = r'''
|
||||
|
||||
_matchstring = '^%s*' % _paramstring
|
||||
|
||||
# Python pre 2.2.1 doesn't have bool
|
||||
try:
|
||||
bool
|
||||
except NameError:
|
||||
def bool(val):
|
||||
"""Simple boolean equivalent function. """
|
||||
if val:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def dottedQuadToNum(ip):
|
||||
"""
|
||||
@@ -269,7 +257,7 @@ def dottedQuadToNum(ip):
|
||||
>>> int(dottedQuadToNum('1.2.3.4'))
|
||||
16909060
|
||||
>>> dottedQuadToNum('255.255.255.255')
|
||||
4294967295L
|
||||
4294967295
|
||||
>>> dottedQuadToNum('255.255.255.256')
|
||||
Traceback (most recent call last):
|
||||
ValueError: Not a good dotted-quad IP: 255.255.255.256
|
||||
@@ -282,41 +270,54 @@ def dottedQuadToNum(ip):
|
||||
return struct.unpack('!L',
|
||||
socket.inet_aton(ip.strip()))[0]
|
||||
except socket.error:
|
||||
# bug in inet_aton, corrected in Python 2.4
|
||||
if ip.strip() == '255.255.255.255':
|
||||
return 0xFFFFFFFF
|
||||
else:
|
||||
raise ValueError('Not a good dotted-quad IP: %s' % ip)
|
||||
raise ValueError('Not a good dotted-quad IP: %s' % ip)
|
||||
return
|
||||
|
||||
|
||||
def numToDottedQuad(num):
|
||||
"""
|
||||
Convert long int to dotted quad string
|
||||
Convert int or long int to dotted quad string
|
||||
|
||||
>>> numToDottedQuad(-1L)
|
||||
>>> numToDottedQuad(int(-1))
|
||||
Traceback (most recent call last):
|
||||
ValueError: Not a good numeric IP: -1
|
||||
>>> numToDottedQuad(1L)
|
||||
>>> numToDottedQuad(int(1))
|
||||
'0.0.0.1'
|
||||
>>> numToDottedQuad(16777218L)
|
||||
>>> numToDottedQuad(int(16777218))
|
||||
'1.0.0.2'
|
||||
>>> numToDottedQuad(16908291L)
|
||||
>>> numToDottedQuad(int(16908291))
|
||||
'1.2.0.3'
|
||||
>>> numToDottedQuad(16909060L)
|
||||
>>> numToDottedQuad(int(16909060))
|
||||
'1.2.3.4'
|
||||
>>> numToDottedQuad(4294967295L)
|
||||
>>> numToDottedQuad(int(4294967295))
|
||||
'255.255.255.255'
|
||||
>>> numToDottedQuad(4294967296L)
|
||||
>>> numToDottedQuad(int(4294967296))
|
||||
Traceback (most recent call last):
|
||||
ValueError: Not a good numeric IP: 4294967296
|
||||
>>> numToDottedQuad(-1)
|
||||
Traceback (most recent call last):
|
||||
ValueError: Not a good numeric IP: -1
|
||||
>>> numToDottedQuad(1)
|
||||
'0.0.0.1'
|
||||
>>> numToDottedQuad(16777218)
|
||||
'1.0.0.2'
|
||||
>>> numToDottedQuad(16908291)
|
||||
'1.2.0.3'
|
||||
>>> numToDottedQuad(16909060)
|
||||
'1.2.3.4'
|
||||
>>> numToDottedQuad(4294967295)
|
||||
'255.255.255.255'
|
||||
>>> numToDottedQuad(4294967296)
|
||||
Traceback (most recent call last):
|
||||
ValueError: Not a good numeric IP: 4294967296
|
||||
|
||||
"""
|
||||
|
||||
# import here to avoid it when ip_addr values are not used
|
||||
import socket, struct
|
||||
|
||||
# no need to intercept here, 4294967295L is fine
|
||||
if num > 4294967295 or num < 0:
|
||||
if num > int(4294967295) or num < 0:
|
||||
raise ValueError('Not a good numeric IP: %s' % num)
|
||||
try:
|
||||
return socket.inet_ntoa(
|
||||
@@ -464,9 +465,9 @@ class Validator(object):
|
||||
... # check that value is of the correct type.
|
||||
... # possible valid inputs are integers or strings
|
||||
... # that represent integers
|
||||
... if not isinstance(value, (int, long, basestring)):
|
||||
... if not isinstance(value, (int, str)):
|
||||
... raise VdtTypeError(value)
|
||||
... elif isinstance(value, basestring):
|
||||
... elif isinstance(value, str):
|
||||
... # if we are given a string
|
||||
... # attempt to convert to an integer
|
||||
... try:
|
||||
@@ -510,11 +511,11 @@ class Validator(object):
|
||||
ConfigObj, an alternative to ConfigParser which supports lists and
|
||||
can validate a config file using a config schema.
|
||||
For more details on using Validator with ConfigObj see:
|
||||
http://www.voidspace.org.uk/python/configobj.html
|
||||
https://configobj.readthedocs.org/en/latest/configobj.html
|
||||
"""
|
||||
|
||||
# this regex does the initial parsing of the checks
|
||||
_func_re = re.compile(r'(.+?)\((.*)\)', re.DOTALL)
|
||||
_func_re = re.compile(r'([^\(\)]+?)\((.*)\)', re.DOTALL)
|
||||
|
||||
# this regex takes apart keyword arguments
|
||||
_key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL)
|
||||
@@ -736,7 +737,7 @@ def _is_num_param(names, values, to_float=False):
|
||||
for (name, val) in zip(names, values):
|
||||
if val is None:
|
||||
out_params.append(val)
|
||||
elif isinstance(val, (int, int, float, basestring)):
|
||||
elif isinstance(val, (int, float, str)):
|
||||
try:
|
||||
out_params.append(fun(val))
|
||||
except ValueError as e:
|
||||
@@ -754,7 +755,7 @@ def _is_num_param(names, values, to_float=False):
|
||||
|
||||
def is_integer(value, min=None, max=None):
|
||||
"""
|
||||
A check that tests that a given value is an integer (int, or long)
|
||||
A check that tests that a given value is an integer (int)
|
||||
and optionally, between bounds. A negative value is accepted, while
|
||||
a float will fail.
|
||||
|
||||
@@ -793,9 +794,9 @@ def is_integer(value, min=None, max=None):
|
||||
0
|
||||
"""
|
||||
(min_val, max_val) = _is_num_param(('min', 'max'), (min, max))
|
||||
if not isinstance(value, (int, int, basestring)):
|
||||
if not isinstance(value, (int, str)):
|
||||
raise VdtTypeError(value)
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, str):
|
||||
# if it's a string - does it represent an integer ?
|
||||
try:
|
||||
value = int(value)
|
||||
@@ -845,7 +846,7 @@ def is_float(value, min=None, max=None):
|
||||
"""
|
||||
(min_val, max_val) = _is_num_param(
|
||||
('min', 'max'), (min, max), to_float=True)
|
||||
if not isinstance(value, (int, int, float, basestring)):
|
||||
if not isinstance(value, (int, float, str)):
|
||||
raise VdtTypeError(value)
|
||||
if not isinstance(value, float):
|
||||
# if it's a string - does it represent a float ?
|
||||
@@ -910,7 +911,7 @@ def is_boolean(value):
|
||||
VdtTypeError: the value "up" is of the wrong type.
|
||||
|
||||
"""
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return bool_dict[value.lower()]
|
||||
except KeyError:
|
||||
@@ -953,7 +954,7 @@ def is_ip_addr(value):
|
||||
Traceback (most recent call last):
|
||||
VdtTypeError: the value "0" is of the wrong type.
|
||||
"""
|
||||
if not isinstance(value, basestring):
|
||||
if not isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
value = value.strip()
|
||||
try:
|
||||
@@ -995,7 +996,7 @@ def is_list(value, min=None, max=None):
|
||||
VdtTypeError: the value "12" is of the wrong type.
|
||||
"""
|
||||
(min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
try:
|
||||
num_members = len(value)
|
||||
@@ -1064,7 +1065,7 @@ def is_string(value, min=None, max=None):
|
||||
Traceback (most recent call last):
|
||||
VdtValueTooLongError: the value "1234" is too long.
|
||||
"""
|
||||
if not isinstance(value, basestring):
|
||||
if not isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
(min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
|
||||
try:
|
||||
@@ -1170,7 +1171,7 @@ def is_string_list(value, min=None, max=None):
|
||||
Traceback (most recent call last):
|
||||
VdtTypeError: the value "hello" is of the wrong type.
|
||||
"""
|
||||
if isinstance(value, basestring):
|
||||
if isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
return [is_string(mem) for mem in is_list(value, min, max)]
|
||||
|
||||
@@ -1266,21 +1267,10 @@ def is_mixed_list(value, *args):
|
||||
>>> vtor.check(mix_str, 0)
|
||||
Traceback (most recent call last):
|
||||
VdtTypeError: the value "0" is of the wrong type.
|
||||
|
||||
This test requires an elaborate setup, because of a change in error string
|
||||
output from the interpreter between Python 2.2 and 2.3 .
|
||||
|
||||
>>> res_seq = (
|
||||
... 'passed an incorrect value "',
|
||||
... 'yoda',
|
||||
... '" for parameter "mixed_list".',
|
||||
... )
|
||||
>>> res_str = "'".join(res_seq)
|
||||
>>> try:
|
||||
... vtor.check('mixed_list("yoda")', ('a'))
|
||||
... except VdtParamError, err:
|
||||
... str(err) == res_str
|
||||
1
|
||||
|
||||
>>> vtor.check('mixed_list("yoda")', ('a'))
|
||||
Traceback (most recent call last):
|
||||
VdtParamError: passed an incorrect value "KeyError('yoda',)" for parameter "'mixed_list'"
|
||||
"""
|
||||
try:
|
||||
length = len(value)
|
||||
@@ -1309,7 +1299,7 @@ def is_option(value, *options):
|
||||
Traceback (most recent call last):
|
||||
VdtTypeError: the value "0" is of the wrong type.
|
||||
"""
|
||||
if not isinstance(value, basestring):
|
||||
if not isinstance(value, str):
|
||||
raise VdtTypeError(value)
|
||||
if not value in options:
|
||||
raise VdtValueError(value)
|
||||
@@ -1338,20 +1328,20 @@ def _test(value, *args, **keywargs):
|
||||
... ]
|
||||
>>> v = Validator({'test': _test})
|
||||
>>> for entry in checks:
|
||||
... print v.check(('test(%s)' % entry), 3)
|
||||
(3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'})
|
||||
... pprint(v.check(('test(%s)' % entry), 3))
|
||||
(3, ('3', '6'), {'max': '3', 'min': '1', 'test': ['a', 'b', 'c']})
|
||||
(3, ('3',), {})
|
||||
(3, ('3', '6'), {})
|
||||
(3, ('3',), {})
|
||||
(3, (), {'test': 'a b c', 'min': '1'})
|
||||
(3, (), {'test': 'a, b, c', 'min': '5'})
|
||||
(3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'})
|
||||
(3, (), {'test': '-99', 'min': '-100'})
|
||||
(3, (), {'min': '1', 'test': 'a b c'})
|
||||
(3, (), {'min': '5', 'test': 'a, b, c'})
|
||||
(3, (), {'max': '3', 'min': '1', 'test': 'a, b, c'})
|
||||
(3, (), {'min': '-100', 'test': '-99'})
|
||||
(3, (), {'max': '3', 'min': '1'})
|
||||
(3, ('3', '6'), {'test': '36'})
|
||||
(3, ('3', '6'), {'test': 'a, b, c'})
|
||||
(3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'})
|
||||
(3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'})
|
||||
(3, ('3',), {'max': '3', 'test': ['a', 'b', 'c']})
|
||||
(3, ('3',), {'max': '3', 'test': ["'a'", 'b', 'x=(c)']})
|
||||
(3, (), {'test': 'x=fish(3)'})
|
||||
|
||||
>>> v = Validator()
|
||||
@@ -1383,14 +1373,14 @@ def _test(value, *args, **keywargs):
|
||||
|
||||
Bug test for unicode arguments
|
||||
>>> v = Validator()
|
||||
>>> v.check(u'string(min=4)', u'test')
|
||||
u'test'
|
||||
>>> v.check('string(min=4)', 'test') == 'test'
|
||||
True
|
||||
|
||||
>>> v = Validator()
|
||||
>>> v.get_default_value(u'string(min=4, default="1234")')
|
||||
u'1234'
|
||||
>>> v.check(u'string(min=4, default="1234")', u'test')
|
||||
u'test'
|
||||
>>> v.get_default_value('string(min=4, default="1234")') == '1234'
|
||||
True
|
||||
>>> v.check('string(min=4, default="1234")', 'test') == 'test'
|
||||
True
|
||||
|
||||
>>> v = Validator()
|
||||
>>> default = v.get_default_value('string(default=None)')
|
||||
@@ -1416,7 +1406,8 @@ def _test3():
|
||||
''
|
||||
>>> vtor.check('string(default="\n")', '', missing=True)
|
||||
'\n'
|
||||
>>> print vtor.check('string(default="\n")', '', missing=True),
|
||||
>>> print(vtor.check('string(default="\n")', '', missing=True))
|
||||
<BLANKLINE>
|
||||
<BLANKLINE>
|
||||
>>> vtor.check('string()', '\n')
|
||||
'\n'
|
||||
@@ -1447,4 +1438,8 @@ if __name__ == '__main__':
|
||||
globs.update({
|
||||
'vtor': Validator(),
|
||||
})
|
||||
doctest.testmod(m, globs=globs)
|
||||
|
||||
failures, tests = doctest.testmod(
|
||||
m, globs=globs,
|
||||
optionflags=doctest.IGNORE_EXCEPTION_DETAIL | doctest.ELLIPSIS)
|
||||
assert not failures, '{} failures out of {} tests'.format(failures, tests)
|
||||
Reference in new issue
Block a user