summaryrefslogtreecommitdiff
path: root/kconfiglib.py
diff options
context:
space:
mode:
authorBenjamin Cabé <benjamin@zephyrproject.org>2025-10-23 17:25:08 +0200
committerTorsten Tejlmand Rasmussen <torsten.rasmussen@nordicsemi.no>2026-02-02 08:22:15 +0100
commit24aef157aead07f813f874f43ee471b057e622cb (patch)
tree57cbb867898e31f9efb3fcce0997ecd3267ea1cc /kconfiglib.py
parentffb54593b899c42fe70e55d26e02d4cd4a9ca53d (diff)
drop support for Python 2.x as it is very much EOL
A follow-up to b96a5ad562deffa697d966c29546650aae645f48 where we stopped having CI run tests on Python 2.x. This actually drops the few remaining Python 2.x compatibility bits as Python 2.x has EOL'd a long time ago. Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org> Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
Diffstat (limited to 'kconfiglib.py')
-rw-r--r--kconfiglib.py132
1 files changed, 31 insertions, 101 deletions
diff --git a/kconfiglib.py b/kconfiglib.py
index ce40eb7..2bbc517 100644
--- a/kconfiglib.py
+++ b/kconfiglib.py
@@ -5,7 +5,7 @@
Overview
========
-Kconfiglib is a Python 2/3 library for scripting and extracting information
+Kconfiglib is a Python 3 library for scripting and extracting information
from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt)
configuration systems.
@@ -52,17 +52,14 @@ sections.
make kmenuconfig
----------------
-This target runs the curses menuconfig interface with Python 3. As of
-Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, only
-Python 3 was supported, so this was a backport).
+This target runs the curses menuconfig interface with Python 3.
make guiconfig
--------------
-This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3
-are supported. To change the Python interpreter used, pass
-PYTHONCMD=<executable> to 'make'. The default is 'python'.
+This target runs the Tkinter menuconfig interface. To change the Python
+interpreter used, pass PYTHONCMD=<executable> to 'make'. The default is 'python'.
make [ARCH=<arch>] iscriptconfig
@@ -878,11 +875,8 @@ class Kconfig(object):
Raises KconfigError on syntax/semantic errors, and OSError or (possibly
a subclass of) IOError on IO errors ('errno', 'strerror', and
- 'filename' are available). Note that IOError is an alias for OSError on
- Python 3, so it's enough to catch OSError there. If you need Python 2/3
- compatibility, it's easiest to catch EnvironmentError, which is a
- common base class of OSError/IOError on Python 2 and an alias for
- OSError on Python 3.
+ 'filename' are available). Note that IOError is an alias for OSError in
+ Python 3, so it's enough to catch OSError.
filename (default: "Kconfig"):
The Kconfig file to load. For the Linux kernel, you'll want "Kconfig"
@@ -925,11 +919,6 @@ class Kconfig(object):
The "utf-8" default avoids exceptions on systems that are configured
to use the C locale, which implies an ASCII encoding.
- This parameter has no effect on Python 2, due to implementation
- issues (regular strings turning into Unicode strings, which are
- distinct in Python 2). Python 2 doesn't decode regular strings
- anyway.
-
Related PEP: https://www.python.org/dev/peps/pep-0538/
suppress_traceback (default: False):
@@ -2118,18 +2107,12 @@ class Kconfig(object):
try:
return self._open(join(self.srctree, filename), "r")
except EnvironmentError as e2:
- # This is needed for Python 3, because e2 is deleted after
- # the try block:
- #
- # https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
- e = e2
-
- raise _KconfigIOError(
- e, "Could not open '{}' ({}: {}). Check that the $srctree "
- "environment variable ({}) is set correctly."
- .format(filename, errno.errorcode[e.errno], e.strerror,
- "set to '{}'".format(self.srctree) if self.srctree
- else "unset or blank"))
+ raise _KconfigIOError(
+ e2, "Could not open '{}' ({}: {}). Check that the $srctree "
+ "environment variable ({}) is set correctly."
+ .format(filename, errno.errorcode[e.errno], e.strerror,
+ "set to '{}'".format(self.srctree) if self.srctree
+ else "unset or blank"))
def _enter_file(self, filename):
# Jumps to the beginning of a sourced Kconfig file, saving the previous
@@ -3895,41 +3878,8 @@ class Kconfig(object):
self._parse_error("extra tokens at end of line")
def _open(self, filename, mode):
- # open() wrapper:
- #
- # - Enable universal newlines mode on Python 2 to ease
- # interoperability between Linux and Windows. It's already the
- # default on Python 3.
- #
- # The "U" flag would currently work for both Python 2 and 3, but it's
- # deprecated on Python 3, so play it future-safe.
- #
- # io.open() defaults to universal newlines on Python 2 (and is an
- # alias for open() on Python 3), but it returns 'unicode' strings and
- # slows things down:
- #
- # Parsing x86 Kconfigs on Python 2
- #
- # with open(..., "rU"):
- #
- # real 0m0.930s
- # user 0m0.905s
- # sys 0m0.025s
- #
- # with io.open():
- #
- # real 0m1.069s
- # user 0m1.040s
- # sys 0m0.029s
- #
- # There's no appreciable performance difference between "r" and
- # "rU" for parsing performance on Python 2.
- #
- # - For Python 3, force the encoding. Forcing the encoding on Python 2
- # turns strings into Unicode strings, which gets messy. Python 2
- # doesn't decode regular strings anyway.
- return open(filename, "rU" if mode == "r" else mode) if _IS_PY2 else \
- open(filename, mode, encoding=self._encoding)
+ # open() wrapper that forces the encoding
+ return open(filename, mode, encoding=self._encoding)
def _check_undef_syms(self):
# Prints warnings for all references to undefined symbols within the
@@ -3958,7 +3908,7 @@ class Kconfig(object):
return True
- for sym in (self.syms.viewvalues if _IS_PY2 else self.syms.values)():
+ for sym in self.syms.values():
# - sym.nodes empty means the symbol is undefined (has no
# definition locations)
#
@@ -6513,16 +6463,9 @@ def _save_old(path):
if islink(path):
# Preserve symlinks
copy_fn = copy
- elif hasattr(os, "replace"):
- # Python 3 (3.3+) only. Best choice when available, because it
- # removes <filename>.old on both *nix and Windows.
- copy_fn = os.replace
- elif os.name == "posix":
- # Removes <filename>.old on POSIX systems
- copy_fn = os.rename
else:
- # Fall back on copying
- copy_fn = copy
+ # atomic replace of <filename>.old
+ copy_fn = os.replace
try:
copy_fn(path, path + ".old")
@@ -6894,30 +6837,21 @@ def _error_if_fn(kconf, _, cond, msg):
def _shell_fn(kconf, _, command):
import subprocess # Only import as needed, to save some startup time
- stdout, stderr = subprocess.Popen(
- command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
- ).communicate()
-
- if not _IS_PY2:
- try:
- stdout = stdout.decode(kconf._encoding)
- stderr = stderr.decode(kconf._encoding)
- except UnicodeDecodeError as e:
- _decoding_error(e, kconf.filename, kconf.linenr)
+ result = subprocess.run(
+ command,
+ shell=True,
+ capture_output=True,
+ text=True,
+ encoding=kconf._encoding
+ )
- if stderr:
+ if result.stderr:
kconf._warn("'{}' wrote to stderr: {}".format(
- command, "\n".join(stderr.splitlines())),
+ command, "\n".join(result.stderr.splitlines())),
kconf.loc)
- # Universal newlines with splitlines() (to prevent e.g. stray \r's in
- # command output on Windows), trailing newline removal, and
- # newline-to-space conversion.
- #
- # On Python 3 versions before 3.6, it's not possible to specify the
- # encoding when passing universal_newlines=True to Popen() (the 'encoding'
- # parameter was added in 3.6), so we do this manual version instead.
- return "\n".join(stdout.splitlines()).rstrip("\n").replace("\n", " ")
+ # Trailing newline removal, and newline-to-space conversion.
+ return result.stdout.rstrip("\n").replace("\n", " ")
#
# Global constants
@@ -6940,9 +6874,6 @@ STR_TO_TRI = {
# Symbol will do. We test this with 'is'.
_NO_CACHED_SELECTION = 0
-# Are we running on Python 2?
-_IS_PY2 = sys.version_info[0] < 3
-
try:
_UNAME_RELEASE = os.uname()[2]
except AttributeError:
@@ -7231,15 +7162,14 @@ KIND_TO_STR = {
# Helper functions for getting compiled regular expressions, with the needed
# matching function returned directly as a small optimization.
#
-# Use ASCII regex matching on Python 3. It's already the default on Python 2.
-
+# Use ASCII regex matching on Python 3.
def _re_match(regex):
- return re.compile(regex, 0 if _IS_PY2 else re.ASCII).match
+ return re.compile(regex, re.ASCII).match
def _re_search(regex):
- return re.compile(regex, 0 if _IS_PY2 else re.ASCII).search
+ return re.compile(regex, re.ASCII).search
# Various regular expressions used during parsing