diff --git a/python-configobj.changes b/python-configobj.changes
index c5fe8eb..09be544 100644
--- a/python-configobj.changes
+++ b/python-configobj.changes
@@ -1,3 +1,9 @@
+-------------------------------------------------------------------
+Wed Jul  5 07:29:38 UTC 2023 - Matej Cepl <mcepl@suse.com>
+
+- Add remove_six.patch (gh#DiffSK/configobj#239) removing the
+  need for six.
+
 -------------------------------------------------------------------
 Thu May  4 18:56:05 UTC 2023 - Dirk Müller <dmueller@suse.com>
 
diff --git a/python-configobj.spec b/python-configobj.spec
index 00eb318..5427d43 100644
--- a/python-configobj.spec
+++ b/python-configobj.spec
@@ -16,7 +16,6 @@
 #
 
 
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define oldpython python
 %{?sle15_python_module_pythons}
 Name:           python-configobj
@@ -27,8 +26,11 @@ License:        BSD-3-Clause
 Group:          Development/Languages/Python
 URL:            https://github.com/DiffSK/configobj
 Source:         https://files.pythonhosted.org/packages/source/c/configobj/configobj-%{version}.tar.gz
-BuildRequires:  %{python_module setuptools}
-BuildRequires:  %{python_module six}
+# PATCH-FIX-UPSTREAM remove_six.patch gh#DiffSK/configobj#239 mcepl@suse.com
+# We don't need six anymore
+Patch0:         remove_six.patch
+BuildRequires:  %{python_module pip}
+BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-six
@@ -60,18 +62,18 @@ It has lots of other features though:
  * Powerful ``unrepr`` mode for storing/retrieving Python data-types
 
 %prep
-%setup -q -n configobj-%{version}
+%autosetup -p1 -n configobj-%{version}
 
 %build
-%python_build
+%pyproject_wheel
 
 %install
-%python_install
+%pyproject_install
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %files %{python_files}
 %{python_sitelib}/configobj
 %{python_sitelib}/validate
-%{python_sitelib}/configobj-%{version}-py*.egg-info
+%{python_sitelib}/configobj-%{version}*-info
 
 %changelog
diff --git a/remove_six.patch b/remove_six.patch
new file mode 100644
index 0000000..306baf7
--- /dev/null
+++ b/remove_six.patch
@@ -0,0 +1,213 @@
+---
+ setup.py                            |    1 
+ src/configobj.egg-info/requires.txt |    1 
+ src/configobj/__init__.py           |   49 ++++++++++++++++--------------------
+ 3 files changed, 22 insertions(+), 29 deletions(-)
+
+--- a/setup.py
++++ b/setup.py
+@@ -41,7 +41,6 @@ DESCRIPTION = 'Config file reading, writ
+ URL = 'https://github.com/DiffSK/configobj'
+ 
+ REQUIRES = """
+-    six
+ """
+ 
+ VERSION = ''
+--- a/src/configobj.egg-info/requires.txt
++++ b/src/configobj.egg-info/requires.txt
+@@ -1 +0,0 @@
+-six
+--- a/src/configobj/__init__.py
++++ b/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