From 324f7c0ff95146bd0ae044fa7d843375eaaabd90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Mon, 22 Dec 2025 18:15:12 +0100 Subject: [PATCH 1/6] Avoid pyparsing deprecations Apparently, some camel case methods have been deprecated... --- pym/bob/cmds/archive.py | 12 ++++++------ pym/bob/pathspec.py | 16 ++++++++-------- pym/bob/stringparser.py | 12 ++++++------ setup.py | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pym/bob/cmds/archive.py b/pym/bob/cmds/archive.py index 195859c3..c1ee2cb8 100644 --- a/pym/bob/cmds/archive.py +++ b/pym/bob/cmds/archive.py @@ -17,7 +17,7 @@ import tarfile # need to enable this for nested expression parsing performance -pyparsing.ParserElement.enablePackrat() +pyparsing.ParserElement.enable_packrat() class ArchiveScanner: CUR_VERSION = 3 @@ -344,12 +344,12 @@ def getRetained(self): # meta.package == "root" && build.date > "2017-06-19" LIMIT 5 ORDER BY build.date ASC def query(scanner, expressions): varReference = pyparsing.Word(pyparsing.alphanums+'._-') - varReference.setParseAction(lambda s, loc, toks: VarReference(s, loc, toks)) + varReference.set_parse_action(lambda s, loc, toks: VarReference(s, loc, toks)) stringLiteral = pyparsing.QuotedString('"', '\\') - stringLiteral.setParseAction(lambda s, loc, toks: StringLiteral(s, loc, toks)) + stringLiteral.set_parse_action(lambda s, loc, toks: StringLiteral(s, loc, toks)) - selectExpr = pyparsing.infixNotation( + selectExpr = pyparsing.infix_notation( stringLiteral | varReference, [ ('!', 1, pyparsing.opAssoc.RIGHT, lambda s, loc, toks: NotPredicate(s, loc, toks)), @@ -371,10 +371,10 @@ def query(scanner, expressions): varReference + pyparsing.Optional(pyparsing.CaselessKeyword("ASC") | pyparsing.CaselessKeyword("DESC")))) - expr.setParseAction(lambda s, loc, toks: RetainExpression(s, loc, toks)) + expr.set_parse_action(lambda s, loc, toks: RetainExpression(s, loc, toks)) try: - retainExpressions = [ expr.parseString(e, True)[0] for e in expressions ] + retainExpressions = [ expr.parse_string(e, True)[0] for e in expressions ] except pyparsing.ParseBaseException as e: raise BobError("Invalid retention expression: " + str(e)) diff --git a/pym/bob/pathspec.py b/pym/bob/pathspec.py index 8970d720..02bb2bbd 100644 --- a/pym/bob/pathspec.py +++ b/pym/bob/pathspec.py @@ -15,7 +15,7 @@ import sqlite3 # need to enable this for nested expression parsing performance -pyparsing.ParserElement.enablePackrat() +pyparsing.ParserElement.enable_packrat() # See "Efficient algorithms for processing XPath queries" [1] for the core # algorithms that are applied here. @@ -766,12 +766,12 @@ def __init__(self, cacheKey, aliases, stringFunctions, packageGenerator, emptyMo abbreviatedStep = pyparsing.Keyword('.') sQStringLiteral = pyparsing.QuotedString("'") - sQStringLiteral.setParseAction( + sQStringLiteral.set_parse_action( lambda s, loc, toks: StringLiteral(s, loc, toks, False, self.__stringFunctions, self.__getGraphIter)) dQStringLiteral = pyparsing.QuotedString('"', '\\') - dQStringLiteral.setParseAction( + dQStringLiteral.set_parse_action( lambda s, loc, toks: StringLiteral(s, loc, toks, True, self.__stringFunctions, self.__getGraphIter)) @@ -784,11 +784,11 @@ def __init__(self, cacheKey, aliases, stringFunctions, packageGenerator, emptyMo pyparsing.Optional(functionArg + pyparsing.ZeroOrMore(pyparsing.Suppress(',') + functionArg)) + \ pyparsing.Suppress(')') - functionCall.setParseAction( + functionCall.set_parse_action( lambda s, loc, toks: FunctionCall(s, loc, toks, self.__stringFunctions, self.__getGraphIter)) - predExpr = pyparsing.infixNotation( + predExpr = pyparsing.infix_notation( locationPath ^ stringLiteral ^ functionCall, [ ('!', 1, pyparsing.opAssoc.RIGHT, lambda s, loc, toks: NotOperator(s, loc, toks, self.__getGraphRoot, 9)), @@ -804,7 +804,7 @@ def __init__(self, cacheKey, aliases, stringFunctions, packageGenerator, emptyMo predicate = '[' + predExpr + ']' step = abbreviatedStep | (pyparsing.Optional(axisSpecifier) + nodeTest + pyparsing.Optional(predicate)) - step.setParseAction(lambda s, loc, toks: LocationStep(s, loc, toks)) + step.set_parse_action(lambda s, loc, toks: LocationStep(s, loc, toks)) abbreviatedRelativeLocationPath = step + '//' + relativeLocationPath relativeLocationPath << ( abbreviatedRelativeLocationPath | @@ -814,7 +814,7 @@ def __init__(self, cacheKey, aliases, stringFunctions, packageGenerator, emptyMo absoluteLocationPath = abbreviatedAbsoluteLocationPath | \ ('/' + relativeLocationPath) locationPath << (absoluteLocationPath | relativeLocationPath) - locationPath.setParseAction( + locationPath.set_parse_action( lambda s, loc, toks: LocationPath(s, loc, toks, self.__getGraphRoot)) self.__pathGrammer = locationPath @@ -876,7 +876,7 @@ def __query(self, path): while path.endswith('/'): path = path[:-1] if path: try: - path = self.__pathGrammer.parseString(path, True) + path = self.__pathGrammer.parse_string(path, True) except pyparsing.ParseBaseException as e: raise BobError("Invalid syntax: " + str(e), help=markLocation(e.line, e.col)) diff --git a/pym/bob/stringparser.py b/pym/bob/stringparser.py index 7e322667..1d7a3089 100644 --- a/pym/bob/stringparser.py +++ b/pym/bob/stringparser.py @@ -13,7 +13,7 @@ import re # need to enable this for nested expression parsing performance -pyparsing.ParserElement.enablePackrat() +pyparsing.ParserElement.enable_packrat() NAME_START = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz' NAME_CHARS = NAME_START + '0123456789' @@ -378,11 +378,11 @@ class IfExpressionParser: def __init__(self): # create parsing grammer sQStringLiteral = pyparsing.QuotedString("'") - sQStringLiteral.setParseAction( + sQStringLiteral.set_parse_action( lambda s, loc, toks: StringLiteral(s, loc, toks, False)) dQStringLiteral = pyparsing.QuotedString('"', '\\') - dQStringLiteral.setParseAction( + dQStringLiteral.set_parse_action( lambda s, loc, toks: StringLiteral(s, loc, toks, True)) stringLiteral = sQStringLiteral | dQStringLiteral @@ -394,10 +394,10 @@ def __init__(self): pyparsing.Optional(functionArg + pyparsing.ZeroOrMore(pyparsing.Suppress(',') + functionArg)) + \ pyparsing.Suppress(')') - functionCall.setParseAction( + functionCall.set_parse_action( lambda s, loc, toks: FunctionCall(s, loc, toks)) - predExpr = pyparsing.infixNotation( + predExpr = pyparsing.infix_notation( stringLiteral ^ functionCall , [ ('!', 1, pyparsing.opAssoc.RIGHT, lambda s, loc, toks: NotOperator(s, loc, toks)), @@ -415,7 +415,7 @@ def __init__(self): def parseExpression(self, expression): try: - ret = self.__ifgrammer.parseString(expression, True) + ret = self.__ifgrammer.parse_string(expression, True) except pyparsing.ParseBaseException as e: raise ParseError("Invalid syntax: " + str(e)) return ret[0] diff --git a/setup.py b/setup.py index 83ba0956..12a36881 100644 --- a/setup.py +++ b/setup.py @@ -136,7 +136,7 @@ class build(build_orig): 'PyYAML', 'schema', 'python-magic', - 'pyparsing', + 'pyparsing>=3', ], # Optional dependencies that are not needed by default From 2fcb495da1d2a7967a1c10a5a1365d44e6c69519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Mon, 22 Dec 2025 21:42:45 +0100 Subject: [PATCH 2/6] Require at least Python 3.9 At least pyparsing has stopped supporting Python 3.8 in it recent releases. --- .github/workflows/workflow.yaml | 2 +- pym/bob/develop/version.py | 4 ++-- setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index a3ee1c83..82bf8b26 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -8,7 +8,7 @@ jobs: timeout-minutes: 15 strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] fail-fast: false steps: diff --git a/pym/bob/develop/version.py b/pym/bob/develop/version.py index 334153db..244eed8d 100644 --- a/pym/bob/develop/version.py +++ b/pym/bob/develop/version.py @@ -9,8 +9,8 @@ if sys.version_info.major != 3: print("Bob requires Python 3") sys.exit(1) -elif sys.version_info.minor < 8: - print("Bob requires at least Python 3.8") +elif sys.version_info.minor < 9: + print("Bob requires at least Python 3.9") sys.exit(1) def getVersion(): diff --git a/setup.py b/setup.py index 12a36881..4333f8ef 100644 --- a/setup.py +++ b/setup.py @@ -131,7 +131,7 @@ class build(build_orig): cmdclass = cmdclass, # Our runtime dependencies - python_requires = '>=3.8', + python_requires = '>=3.9', install_requires = [ 'PyYAML', 'schema', From 686e844f09390c1dfc0a9da4ece6442eb7983f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Mon, 22 Dec 2025 21:46:44 +0100 Subject: [PATCH 3/6] utils: remove pre-3.9 compatibility --- pym/bob/archive.py | 4 ++-- pym/bob/utils.py | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/pym/bob/archive.py b/pym/bob/archive.py index 297b18b6..94a24e11 100644 --- a/pym/bob/archive.py +++ b/pym/bob/archive.py @@ -23,7 +23,7 @@ from .errors import BuildError, BobError from .tty import stepAction, stepMessage, \ SKIPPED, EXECUTED, WARNING, INFO, TRACE, ERROR, IMPORTANT -from .utils import asHexStr, removePath, isWindows, getBashPath, tarfileOpen, binStat, removePrefix +from .utils import asHexStr, removePath, isWindows, getBashPath, tarfileOpen, binStat from .webdav import WebDav, WebdavError, WebdavNotFoundError, WebdavAlreadyExistsError from tempfile import mkstemp, NamedTemporaryFile, TemporaryFile, gettempdir import asyncio @@ -936,7 +936,7 @@ def _listDir(self, path): entries = [] for info in path_info: if info["path"]: - entries.append(removePrefix(info["path"], path + "/")) + entries.append(info["path"].removeprefix(path + "/")) return entries def _delete(self, filename): diff --git a/pym/bob/utils.py b/pym/bob/utils.py index 9d3d0340..ba7c913e 100644 --- a/pym/bob/utils.py +++ b/pym/bob/utils.py @@ -142,14 +142,6 @@ def onerror(func, path, exc): except OSError as e: raise BuildError("Error removing '"+path+"': " + str(e)) -def removePrefix(string, prefix): - if sys.version_info >= (3, 9): - return string.removeprefix(prefix) - else: - if string.startswith(prefix): - string = string[len(prefix):] - return string - def emptyDirectory(path): try: if os.path.exists(path): From ca7b6914c9350128139c38cfa39e298d71067f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Mon, 22 Dec 2025 21:54:08 +0100 Subject: [PATCH 4/6] doc: document Python 3.9 requirement --- doc/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/installation.rst b/doc/installation.rst index 9fa917e1..875f7bae 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -4,7 +4,7 @@ Installation Dependencies ============ -Bob is built with Python3 (>=3.8). Some additional Python packages are +Bob is built with Python3 (>=3.9). Some additional Python packages are required. They are installed automatically as dependencies. Apart from the Python dependencies additional run time dependencies could arise, From 6d43d25ab25ddfd314861517dc40c26d695499e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Mon, 22 Dec 2025 21:57:02 +0100 Subject: [PATCH 5/6] github: update to Ubuntu 24.04 --- .github/workflows/workflow.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index 82bf8b26..9e470a44 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: build-linux: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 15 strategy: matrix: @@ -39,12 +39,13 @@ jobs: python -m pip install --upgrade setuptools pip install PyYAML coverage schema python-magic pyparsing sphinx wheel sudo apt-get update - sudo apt-get install cvs + sudo apt-get install cvs subversion - name: Run unit tests run: | git config --global init.defaultBranch master # keep the old name git config --global protocol.file.allow always # roll back CVE-2022-39253 + sudo sh -c "echo 0 > /proc/sys/kernel/apparmor_restrict_unprivileged_userns" eatmydata ./test/run-tests.sh -c xml -v - name: Build Python package From b1f9f48a26ae0592832d47c5725e33bc115ad961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= Date: Mon, 22 Dec 2025 21:57:20 +0100 Subject: [PATCH 6/6] github: build Python 3.14 too --- .github/workflows/workflow.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yaml b/.github/workflows/workflow.yaml index 9e470a44..8918dde7 100644 --- a/.github/workflows/workflow.yaml +++ b/.github/workflows/workflow.yaml @@ -8,7 +8,7 @@ jobs: timeout-minutes: 15 strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] fail-fast: false steps: