From c8f298cf101adaac9f0abb5724c7b68731f1f3ec67902e7bbb572b43eb23420a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mark=C3=A9ta=20Machov=C3=A1?= Date: Tue, 4 Feb 2025 10:23:44 +0000 Subject: [PATCH] - Update to 5.0.9 * Drop support for Python 2 and <3.7 * Fix CVE-2023-26112, ReDoS attack - Drop CVE-2023-26112.patch, merged upstream - Drop remove_six.patch, fixed upstream OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-configobj?expand=0&rev=51 --- .gitattributes | 23 +++ .gitignore | 1 + CVE-2023-26112.patch | 48 +++++ configobj-5.0.8-gh.tar.gz | 3 + configobj-5.0.9-gh.tar.gz | 3 + python-configobj.changes | 183 ++++++++++++++++++ python-configobj.spec | 77 ++++++++ remove_six.patch | 379 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 717 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CVE-2023-26112.patch create mode 100644 configobj-5.0.8-gh.tar.gz create mode 100644 configobj-5.0.9-gh.tar.gz create mode 100644 python-configobj.changes create mode 100644 python-configobj.spec create mode 100644 remove_six.patch diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/CVE-2023-26112.patch b/CVE-2023-26112.patch new file mode 100644 index 0000000..fa21013 --- /dev/null +++ b/CVE-2023-26112.patch @@ -0,0 +1,48 @@ +From a82ea8fb0338f2bd46cf627c4b763094448e6bd7 Mon Sep 17 00:00:00 2001 +From: cdcadman +Date: Wed, 17 May 2023 03:57:08 -0700 +Subject: [PATCH] Address CVE-2023-26112 ReDoS + +--- + src/configobj/validate.py | 2 +- + src/tests/test_validate_errors.py | 10 +++++++++- + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/src/configobj/validate.py b/src/configobj/validate.py +index 9267a3f..98d879f 100644 +--- a/src/configobj/validate.py ++++ b/src/configobj/validate.py +@@ -541,7 +541,7 @@ class Validator(object): + """ + + # 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) +diff --git a/src/tests/test_validate_errors.py b/src/tests/test_validate_errors.py +index 399daa8..f7d6c27 100644 +--- a/src/tests/test_validate_errors.py ++++ b/src/tests/test_validate_errors.py +@@ -3,7 +3,7 @@ + import pytest + + from configobj import ConfigObj, get_extra_values, ParseError, NestingError +-from configobj.validate import Validator ++from configobj.validate import Validator, VdtUnknownCheckError + + @pytest.fixture() + def thisdir(): +@@ -77,3 +77,11 @@ def test_no_parent(tmpdir, specpath): + ini.write('[[haha]]') + with pytest.raises(NestingError): + conf = ConfigObj(str(ini), configspec=specpath, file_error=True) ++ ++ ++def test_re_dos(val): ++ value = "aaa" ++ i = 165100 ++ attack = '\x00'*i + ')' + '('*i ++ with pytest.raises(VdtUnknownCheckError): ++ val.check(attack, value) diff --git a/configobj-5.0.8-gh.tar.gz b/configobj-5.0.8-gh.tar.gz new file mode 100644 index 0000000..0a7bef2 --- /dev/null +++ b/configobj-5.0.8-gh.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:547dc047e31c71d7a8732016336769ed450588f34a7c13077aa7acc7df245eda +size 99071 diff --git a/configobj-5.0.9-gh.tar.gz b/configobj-5.0.9-gh.tar.gz new file mode 100644 index 0000000..b8f68fd --- /dev/null +++ b/configobj-5.0.9-gh.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bd70f9ce7912679c4ba9c80da289877906db0ca6bd02c3ab545d660e9b60d4f +size 98246 diff --git a/python-configobj.changes b/python-configobj.changes new file mode 100644 index 0000000..7d09bd9 --- /dev/null +++ b/python-configobj.changes @@ -0,0 +1,183 @@ +------------------------------------------------------------------- +Tue Feb 4 08:55:17 UTC 2025 - John Paul Adrian Glaubitz + +- Update to 5.0.9 + * Drop support for Python 2 and <3.7 + * Fix CVE-2023-26112, ReDoS attack +- Drop CVE-2023-26112.patch, merged upstream +- Drop remove_six.patch, fixed upstream + +------------------------------------------------------------------- +Wed Sep 11 12:08:59 UTC 2024 - Matej Cepl + +- Refresh CVE-2023-26112.patch according to the last state of + gh#DiffSK/configobj!236. + +------------------------------------------------------------------- +Wed Jan 3 16:47:32 UTC 2024 - Ben Greiner + +- Remove six from the rpm requirements. Why would we have + remove_six.patch in the first place if we still require it? +- Enable unit tests + +------------------------------------------------------------------- +Wed Jan 3 09:46:30 UTC 2024 - Dirk Müller + +- require setuptools +- remove python2 logic, this makes no sense after + "remove_six.patch" + +------------------------------------------------------------------- +Wed Jul 5 07:29:38 UTC 2023 - Matej Cepl + +- Add remove_six.patch (gh#DiffSK/configobj#239) removing the + need for six. + +------------------------------------------------------------------- +Tue Jul 4 10:51:42 UTC 2023 - Markéta Machová + +- Add CVE-2023-26112.patch (bsc#1210070) + +------------------------------------------------------------------- +Thu May 4 18:56:05 UTC 2023 - Dirk Müller + +- update to 5.0.8: + * 5.0.7 originally did this work, but 5.0.8 fixes a regression + * update testing to validate against python version 2.7 and + 3.5-3.11 + * update broken links / non-existent services and references + +------------------------------------------------------------------- +Fri Apr 21 12:23:30 UTC 2023 - Dirk Müller + +- add sle15_python_module_pythons (jsc#PED-68) + +------------------------------------------------------------------- +Thu Apr 13 22:40:36 UTC 2023 - Matej Cepl + +- Make calling of %{sle15modernpython} optional. + +------------------------------------------------------------------- +Mon Jan 16 21:16:58 UTC 2023 - Dirk Müller + +- require setuptools + +------------------------------------------------------------------- +Tue Dec 4 12:46:48 UTC 2018 - Matej Cepl + +- Remove superfluous devel dependency for noarch package + +------------------------------------------------------------------- +Wed May 24 17:01:11 UTC 2017 - toddrme2178@gmail.com + +- Implement single-spec version. +- Fix source URL. + +------------------------------------------------------------------- +Thu Sep 11 10:37:46 UTC 2014 - toddrme2178@gmail.com + +- Update to version 5.0.6 + * Improves error messages in certain edge cases + +------------------------------------------------------------------- +Thu Jul 24 09:03:10 UTC 2014 - fcastelli@suse.com + +- Added runtime depedency: python-six + +------------------------------------------------------------------- +Wed Jul 23 13:04:31 UTC 2014 - fcastelli@suse.com + +- Update to version 5.0.5: + * BUGFIX: error in writing out config files to disk with non-ascii + characters + * BUGFIX: correcting that the code path fixed in 5.0.3 didn’t cover + reading in config files + * BUGFIX: not handling unicode encoding well, especially with respect to + writing out files + * Specific error message for installing version this version on Python + versions older than 2.5 + * Documentation corrections + * BUGFIX: Fixed regression on python 2.x where passing an encoding parameter + did not convert a bytestring config file (which is the most common) to + unicode. Added unit tests for this and related cases + * BUGFIX: A particular error message would fail to display with a type error + on python 2.6 only + * Python 3 single-source compatibility at the cost of a more restrictive set + of versions: 2.6, 2.7, 3.2, 3.3 (otherwise unchanged) + +------------------------------------------------------------------- +Tue May 29 08:26:59 UTC 2012 - cfarrell@suse.com + +- license update: BSD-3-Clause + Refers only to the license available at + http://www.voidspace.org.uk/python/license.shtml (which is BSD-3-Clause) + +------------------------------------------------------------------- +Fri May 25 08:21:00 UTC 2012 - toddrme2178@gmail.com + +- Spec file cleanups + +------------------------------------------------------------------- +Sun Feb 5 19:40:33 UTC 2012 - dimstar@opensuse.org + +- Really make the obsoletes sane: The package was called + python-configobj-docs before, so that's what we need to obsolete + (not -doc). +- The Obsoletes tag is for <= %{version}, as the package existed in + version 4.7.2, which is also when it was merged back. Obsoleting + only < 4.7.2 would cause conflicts when installing the newly + merged package. + +------------------------------------------------------------------- +Tue Jan 24 13:50:32 UTC 2012 - bwiedemann@suse.com + +- fix Obsoletes + +------------------------------------------------------------------- +Fri Dec 9 12:39:05 UTC 2011 - saschpe@suse.de + +- Spec file cleanup: + * Use upstream tarball + * Obsoleted empty doc package + * Fix SLE-11 build + * Use upstream description + +------------------------------------------------------------------- +Thu Dec 8 13:54:30 UTC 2011 - coolo@suse.com + +- fix license to be in spdx.org format + +------------------------------------------------------------------- +Mon Sep 13 22:07:25 CEST 2010 - dimstar@opensuse.org + +- Update to version 4.7.2: + + BUGFIX: Restore Python 2.3 compatibility + + BUGFIX: Members that were lists were being returned as copies + due to interpolation introduced in 4.7. Lists are now only + copies if interpolation changes a list member. + + BUGFIX: pop now does interpolation in list values as well. + + BUGFIX: where interpolation matches a section name rather + than a value it is ignored instead of raising an exception on + fetching the item. + + BUGFIX: values that use interpolation to reference members + that don't exist can now be repr'd. + + BUGFIX: Fix to avoid writing '\r\r\n' on Windows when given a + file opened in text write mode ('w'). + +------------------------------------------------------------------- +Tue Jul 20 20:24:28 UTC 2010 - pascal.bleser@opensuse.org + +- update to 4.7.1 +- split out -docs subpackage + +------------------------------------------------------------------- +Sun May 27 18:22:00 CEST 2007 - peter+rpmspam@suntel.com.tr + +- add "unzip" as a builddep so it builds on Factory - 4.4.0 + +------------------------------------------------------------------- + +Fri May 11 16:59:40 CEST 2007 - poeml@suse.de + +- package created (copy from packman package) - 4.4.0 + diff --git a/python-configobj.spec b/python-configobj.spec new file mode 100644 index 0000000..0d11e45 --- /dev/null +++ b/python-configobj.spec @@ -0,0 +1,77 @@ +# +# spec file for package python-configobj +# +# Copyright (c) 2025 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + + +%{?sle15_python_module_pythons} +Name: python-configobj +Version: 5.0.9 +Release: 0 +Summary: Config file reading, writing and validation +License: BSD-3-Clause +Group: Development/Languages/Python +URL: https://github.com/DiffSK/configobj +# No tests in PyPI sdist +Source: https://github.com/DiffSK/configobj/archive/refs/tags/v%{version}.tar.gz#/configobj-%{version}-gh.tar.gz +BuildRequires: %{python_module pip} +BuildRequires: %{python_module setuptools} +BuildRequires: %{python_module wheel} +BuildRequires: fdupes +BuildRequires: python-rpm-macros +BuildArch: noarch +# SECTION test +BuildRequires: %{python_module pytest} +# /SECTION +%python_subpackages + +%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 + +%prep +%autosetup -p1 -n configobj-%{version} + +%build +%pyproject_wheel + +%install +%pyproject_install +%python_expand %fdupes %{buildroot}%{$python_sitelib} + +%check +%pytest + +%files %{python_files} +%{python_sitelib}/configobj +%{python_sitelib}/validate +%{python_sitelib}/configobj-%{version}.dist-info + +%changelog diff --git a/remove_six.patch b/remove_six.patch new file mode 100644 index 0000000..4a64c53 --- /dev/null +++ b/remove_six.patch @@ -0,0 +1,379 @@ +https://github.com/DiffSK/configobj/issues/239 + +Index: configobj-5.0.8/setup.py +=================================================================== +--- configobj-5.0.8.orig/setup.py ++++ configobj-5.0.8/setup.py +@@ -41,7 +41,6 @@ DESCRIPTION = 'Config file reading, writ + URL = 'https://github.com/DiffSK/configobj' + + REQUIRES = """ +- six + """ + + VERSION = '' +Index: configobj-5.0.8/src/configobj/__init__.py +=================================================================== +--- configobj-5.0.8.orig/src/configobj/__init__.py ++++ configobj-5.0.8/src/configobj/__init__.py +@@ -19,7 +19,6 @@ import sys + + from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE + +-import six + from ._version import __version__ + + # imported lazily to avoid startup performance hit if it isn't used +@@ -553,11 +552,11 @@ class Section(dict): + """Fetch the item and do string interpolation.""" + val = dict.__getitem__(self, key) + if self.main.interpolation: +- if isinstance(val, six.string_types): ++ if isinstance(val, str): + return self._interpolate(key, val) + if isinstance(val, list): + def _check(entry): +- if isinstance(entry, six.string_types): ++ if isinstance(entry, str): + return self._interpolate(key, entry) + return entry + new = [_check(entry) for entry in val] +@@ -580,7 +579,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, six.string_types): ++ if not isinstance(key, str): + raise ValueError('The key "%s" is not a string.' % key) + + # add the comment +@@ -614,11 +613,11 @@ class Section(dict): + if key not in self: + self.scalars.append(key) + if not self.main.stringify: +- if isinstance(value, six.string_types): ++ if isinstance(value, str): + pass + elif isinstance(value, (list, tuple)): + for entry in value: +- if not isinstance(entry, six.string_types): ++ if not isinstance(entry, str): + raise TypeError('Value is not a string "%s".' % entry) + else: + raise TypeError('Value is not a string "%s".' % value) +@@ -959,7 +958,7 @@ class Section(dict): + return False + else: + try: +- if not isinstance(val, six.string_types): ++ if not isinstance(val, str): + # TODO: Why do we raise a KeyError here? + raise KeyError() + else: +@@ -1230,7 +1229,7 @@ class ConfigObj(Section): + + + def _load(self, infile, configspec): +- if isinstance(infile, six.string_types): ++ if isinstance(infile, str): + self.filename = infile + if os.path.isfile(infile): + with open(infile, 'rb') as h: +@@ -1298,7 +1297,7 @@ class ConfigObj(Section): + break + break + +- assert all(isinstance(line, six.string_types) for line in content), repr(content) ++ assert all(isinstance(line, str) for line in content), repr(content) + content = [line.rstrip('\r\n') for line in content] + + self._parse(content) +@@ -1403,7 +1402,7 @@ class ConfigObj(Section): + else: + line = infile + +- if isinstance(line, six.text_type): ++ 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 +@@ -1448,7 +1447,7 @@ 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 isinstance(line, six.binary_type) or 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: +@@ -1464,9 +1463,9 @@ class ConfigObj(Section): + else: + infile = newline + # UTF-8 +- if isinstance(infile, six.text_type): ++ if isinstance(infile, str): + return infile.splitlines(True) +- elif isinstance(infile, six.binary_type): ++ elif isinstance(infile, bytes): + return infile.decode('utf-8').splitlines(True) + else: + return self._decode(infile, 'utf-8') +@@ -1474,12 +1473,8 @@ class ConfigObj(Section): + return self._decode(infile, encoding) + + +- if six.PY2 and isinstance(line, str): +- # don't actually do any decoding, since we're on python 2 and +- # returning a bytestring is fine +- return self._decode(infile, None) + # No BOM discovered and no encoding specified, default to UTF-8 +- if isinstance(infile, six.binary_type): ++ if isinstance(infile, bytes): + return infile.decode('utf-8').splitlines(True) + else: + return self._decode(infile, 'utf-8') +@@ -1487,7 +1482,7 @@ class ConfigObj(Section): + + def _a_to_u(self, aString): + """Decode ASCII strings to unicode if a self.encoding is specified.""" +- if isinstance(aString, six.binary_type) and self.encoding: ++ if isinstance(aString, bytes) and self.encoding: + return aString.decode(self.encoding) + else: + return aString +@@ -1499,9 +1494,9 @@ class ConfigObj(Section): + + if is a string, it also needs converting to a list. + """ +- if isinstance(infile, six.string_types): ++ if isinstance(infile, str): + return infile.splitlines(True) +- if isinstance(infile, six.binary_type): ++ if isinstance(infile, bytes): + # NOTE: Could raise a ``UnicodeDecodeError`` + if encoding: + return infile.decode(encoding).splitlines(True) +@@ -1510,7 +1505,7 @@ class ConfigObj(Section): + + if encoding: + for i, line in enumerate(infile): +- if isinstance(line, six.binary_type): ++ 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`` +@@ -1520,7 +1515,7 @@ class ConfigObj(Section): + + def _decode_element(self, line): + """Decode element to unicode if necessary.""" +- if isinstance(line, six.binary_type) and self.default_encoding: ++ if isinstance(line, bytes) and self.default_encoding: + return line.decode(self.default_encoding) + else: + return line +@@ -1532,7 +1527,7 @@ class ConfigObj(Section): + Used by ``stringify`` within validate, to turn non-string values + into strings. + """ +- if not isinstance(value, six.string_types): ++ 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) +@@ -1786,7 +1781,7 @@ 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, six.string_types): ++ 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 +@@ -2111,7 +2106,7 @@ class ConfigObj(Section): + if not output.endswith(newline): + output += newline + +- if isinstance(output, six.binary_type): ++ if isinstance(output, bytes): + output_bytes = output + else: + output_bytes = output.encode(self.encoding or +@@ -2353,7 +2348,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, six.string_types): ++ if not isinstance(self.filename, str): + raise ReloadError() + + filename = self.filename +Index: configobj-5.0.8/src/tests/test_configobj.py +=================================================================== +--- configobj-5.0.8.orig/src/tests/test_configobj.py ++++ configobj-5.0.8/src/tests/test_configobj.py +@@ -1,5 +1,6 @@ + # coding=utf-8 + from __future__ import unicode_literals ++import io + import os + import re + +@@ -8,7 +9,6 @@ from warnings import catch_warnings + from tempfile import NamedTemporaryFile + + import pytest +-import six + + import configobj as co + from configobj import ConfigObj, flatten_errors, ReloadError, DuplicateError, MissingInterpolationOption, InterpolationLoopError, ConfigObjError +@@ -36,13 +36,13 @@ def cfg_lines(config_string_representati + '{!r}'.format(config_string_representation)) + + first_content = lines[line_no_with_content] +- if isinstance(first_content, six.binary_type): ++ if isinstance(first_content, bytes): + first_content = first_content.decode('utf-8') + ws_chars = len(re.search('^(\s*)', first_content).group(1)) + + def yield_stringified_line(): + for line in lines: +- if isinstance(line, six.binary_type): ++ if isinstance(line, bytes): + yield line.decode('utf-8') + else: + yield line +@@ -70,7 +70,7 @@ def cfg_contents(request): + + with NamedTemporaryFile(delete=False, mode='wb') as cfg_file: + for line in lines: +- if isinstance(line, six.binary_type): ++ if isinstance(line, bytes): + cfg_file.write(line + os.linesep.encode('utf-8')) + else: + cfg_file.write((line + os.linesep).encode('utf-8')) +@@ -186,11 +186,7 @@ class TestEncoding(object): + + c = ConfigObj(cfg, encoding='utf8') + +- if six.PY2: +- assert not isinstance(c['test'], str) +- assert isinstance(c['test'], unicode) +- else: +- assert isinstance(c['test'], str) ++ assert isinstance(c['test'], str) + + + #issue #18 +@@ -198,11 +194,7 @@ class TestEncoding(object): + cfg = cfg_contents(b"test = some string") + + c = ConfigObj(cfg) +- if six.PY2: +- assert isinstance(c['test'], str) +- assert not isinstance(c['test'], unicode) +- else: +- assert isinstance(c['test'], str) ++ assert isinstance(c['test'], str) + + #issue #44 + def test_that_encoding_using_list_of_strings(self): +@@ -210,11 +202,7 @@ class TestEncoding(object): + + c = ConfigObj(cfg, encoding='utf8') + +- if six.PY2: +- assert isinstance(c['test'], unicode) +- assert not isinstance(c['test'], str) +- else: +- assert isinstance(c['test'], str) ++ assert isinstance(c['test'], str) + + assert c['test'] == '\U0001f41c' + +@@ -223,7 +211,7 @@ class TestEncoding(object): + c = cfg_contents(ant_cfg) + cfg = ConfigObj(c, encoding='utf-8') + +- assert isinstance(cfg['tags']['bug']['translated'], six.text_type) ++ assert isinstance(cfg['tags']['bug']['translated'], str) + + #issue #44 and #55 + def test_encoding_in_config_files(self, request, ant_cfg): +@@ -233,7 +221,7 @@ class TestEncoding(object): + request.addfinalizer(lambda : os.unlink(cfg_file.name)) + + cfg = ConfigObj(cfg_file.name, encoding='utf-8') +- assert isinstance(cfg['tags']['bug']['translated'], six.text_type) ++ assert isinstance(cfg['tags']['bug']['translated'], str) + cfg.write() + + @pytest.fixture +@@ -500,7 +488,7 @@ def test_unicode_handling(): + 'section': {'test': 'test', 'test2': 'test2'}} + uc = ConfigObj(u, encoding='utf_8', default_encoding='latin-1') + assert uc.BOM +- assert isinstance(uc['test1'], six.text_type) ++ assert isinstance(uc['test1'], str) + assert uc.encoding == 'utf_8' + assert uc.newlines == '\n' + assert len(uc.write()) == 13 +@@ -508,14 +496,14 @@ def test_unicode_handling(): + a_list = uc.write() + assert 'latin1' in str(a_list) + assert len(a_list) == 14 +- assert isinstance(a_list[0], six.binary_type) ++ assert isinstance(a_list[0], bytes) + assert a_list[0].startswith(BOM_UTF8) + + u = u_base.replace('\n', '\r\n').encode('utf-8').splitlines(True) + uc = ConfigObj(u) + assert uc.newlines == '\r\n' + uc.newlines = '\r' +- file_like = six.BytesIO() ++ file_like = io.BytesIO() + uc.write(file_like) + file_like.seek(0) + uc2 = ConfigObj(file_like) +@@ -723,7 +711,7 @@ class TestSectionBehavior(object): + val = section[key] + newkey = key.replace('XXXX', 'CLIENT1') + section.rename(key, newkey) +- if isinstance(val, six.string_types): ++ if isinstance(val, str): + val = val.replace('XXXX', 'CLIENT1') + section[newkey] = val + +@@ -811,7 +799,7 @@ class TestReloading(object): + return content + + def test_handle_no_filename(self): +- for bad_args in ([six.BytesIO()], [], [[]]): ++ for bad_args in ([io.BytesIO()], [], [[]]): + cfg = ConfigObj(*bad_args) + with pytest.raises(ReloadError) as excinfo: + cfg.reload() +@@ -1264,21 +1252,21 @@ class TestEdgeCasesWhenWritingOut(object + def test_newline_terminated(self, empty_cfg): + empty_cfg.newlines = '\n' + empty_cfg['a'] = 'b' +- collector = six.BytesIO() ++ collector = io.BytesIO() + empty_cfg.write(collector) + assert collector.getvalue() == b'a = b\n' + + def test_hash_escaping(self, empty_cfg): + empty_cfg.newlines = '\n' + empty_cfg['#a'] = 'b # something' +- collector = six.BytesIO() ++ collector = io.BytesIO() + empty_cfg.write(collector) + assert collector.getvalue() == b'"#a" = "b # something"\n' + + empty_cfg = ConfigObj() + empty_cfg.newlines = '\n' + empty_cfg['a'] = 'b # something', 'c # something' +- collector = six.BytesIO() ++ collector = io.BytesIO() + empty_cfg.write(collector) + assert collector.getvalue() == b'a = "b # something", "c # something"\n' +