fix compatibility with pyparsing 3 and update to it (#10560)

on main if you run tools/pinning/current/repin.sh and run our unit
tests, they will fail due to new deprecation warnings from pyparsing.
the cause of these warnings is described at
dc009668d8/docs/whats_new_in_3_0_0.rst (L613-L708)

this PR fixes these warnings and updates our minimum required pyparsing
version to 3.0 where the new naming convention is available. i ran our
full test suite on the first commit here and it passed

i don't think it's worth trying to keep compatibility with pyparsing<3
unless we get a request for us to do so which i really doubt we will
This commit is contained in:
Brad Warren 2026-02-03 11:51:26 -08:00 committed by GitHub
parent a9746336b7
commit 410ee87242
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 61 additions and 59 deletions

View file

@ -11,7 +11,7 @@ install_requires = [
# PyOpenSSL>=25.0.0 is just needed to satisfy mypy right now so this dependency can probably be
# relaxed to >=24.0.0 if needed.
'PyOpenSSL>=25.0.0',
'pyparsing>=2.4.7',
'pyparsing>=3.0.0',
]
setup(

View file

@ -33,14 +33,14 @@ class RawNginxParser:
"""A class that parses nginx configuration with pyparsing."""
# constants
space = Optional(White(ws=' \t\r\n\u00a0')).leaveWhitespace()
required_space = White(ws=' \t\r\n\u00a0').leaveWhitespace()
space = Optional(White(ws=' \t\r\n\u00a0')).leave_whitespace()
required_space = White(ws=' \t\r\n\u00a0').leave_whitespace()
left_bracket = Literal("{").suppress()
right_bracket = space + Literal("}").suppress()
semicolon = Literal(";").suppress()
dquoted = QuotedString('"', multiline=True, unquoteResults=False, escChar='\\')
squoted = QuotedString("'", multiline=True, unquoteResults=False, escChar='\\')
dquoted = QuotedString('"', multiline=True, unquote_results=False, esc_char='\\')
squoted = QuotedString("'", multiline=True, unquote_results=False, esc_char='\\')
quoted = dquoted | squoted
head_tokenchars = Regex(r"(\$\{)|[^{};\s'\"]") # if (last_space)
tail_tokenchars = Regex(r"(\$\{)|[^{;\s]") # else
@ -61,18 +61,18 @@ class RawNginxParser:
contents = Group(comment) | Group(block) | Group(assignment)
block_begin = Group(whitespace_token_group)
block_innards = Group(ZeroOrMore(contents) + space).leaveWhitespace()
block_innards = Group(ZeroOrMore(contents) + space).leave_whitespace()
block << block_begin + left_bracket + block_innards + right_bracket
script = ZeroOrMore(contents) + space + stringEnd
script.parseWithTabs().leaveWhitespace()
script.parse_with_tabs().leave_whitespace()
def __init__(self, source: str) -> None:
self.source = source
def parse(self) -> ParseResults:
"""Returns the parsed tree."""
return self.script.parseString(self.source)
return self.script.parse_string(self.source)
def as_list(self) -> list[Any]:
"""Returns the parsed tree as a list."""

View file

@ -23,21 +23,21 @@ class TestRawNginxParser(unittest.TestCase):
"""Test the raw low-level Nginx config parser."""
def test_assignments(self):
parsed = RawNginxParser.assignment.parseString('root /test;').asList()
parsed = RawNginxParser.assignment.parse_string('root /test;').asList()
assert parsed == ['root', ' ', '/test']
parsed = RawNginxParser.assignment.parseString('root /test;foo bar;').asList()
parsed = RawNginxParser.assignment.parse_string('root /test;foo bar;').asList()
assert parsed == ['root', ' ', '/test'], ['foo', ' ', 'bar']
def test_blocks(self):
parsed = RawNginxParser.block.parseString('foo {}').asList()
parsed = RawNginxParser.block.parse_string('foo {}').asList()
assert parsed == [['foo', ' '], []]
parsed = RawNginxParser.block.parseString('location /foo{}').asList()
parsed = RawNginxParser.block.parse_string('location /foo{}').asList()
assert parsed == [['location', ' ', '/foo'], []]
parsed = RawNginxParser.block.parseString('foo { bar foo ; }').asList()
parsed = RawNginxParser.block.parse_string('foo { bar foo ; }').asList()
assert parsed == [['foo', ' '], [[' ', 'bar', ' ', 'foo', ' '], ' ']]
def test_nested_blocks(self):
parsed = RawNginxParser.block.parseString('foo { bar {} }').asList()
parsed = RawNginxParser.block.parse_string('foo { bar {} }').asList()
block, content = parsed
assert FIRST(content) == [[' ', 'bar', ' '], []]
assert FIRST(block) == 'foo'

View file

@ -0,0 +1 @@
certbot-nginx now requires pyparsing>=3.0.0.

View file

@ -2,30 +2,30 @@
# that script.
apacheconfig==0.3.2 ; python_version == "3.10"
asn1crypto==0.24.0 ; python_version == "3.10"
astroid==4.0.1 ; python_version == "3.10"
astroid==4.0.3 ; python_version == "3.10"
attrs==25.4.0 ; python_version == "3.10"
beautifulsoup4==4.14.2 ; python_version == "3.10"
beautifulsoup4==4.14.3 ; python_version == "3.10"
boto3==1.20.34 ; python_version == "3.10"
botocore==1.23.34 ; python_version == "3.10"
cachetools==5.5.2 ; python_version == "3.10"
certifi==2025.10.5 ; python_version == "3.10"
certifi==2026.1.4 ; python_version == "3.10"
cffi==1.14.1 ; python_version == "3.10"
chardet==3.0.4 ; python_version == "3.10"
cloudflare==2.19.0 ; python_version == "3.10"
colorama==0.4.6 ; (sys_platform == "win32" or platform_system == "Windows") and python_version == "3.10"
configargparse==1.5.3 ; python_version == "3.10"
configobj==5.0.6 ; python_version == "3.10"
coverage==7.11.0 ; python_version == "3.10"
coverage==7.13.3 ; python_version == "3.10"
cryptography==43.0.0 ; python_version == "3.10"
cython==0.29.37 ; python_version == "3.10"
dill==0.4.0 ; python_version == "3.10"
dill==0.4.1 ; python_version == "3.10"
distlib==0.4.0 ; python_version == "3.10"
distro==1.0.1 ; python_version == "3.10"
dns-lexicon==3.15.1 ; python_version == "3.10"
dnspython==2.6.1 ; python_version == "3.10"
exceptiongroup==1.3.0 ; python_version == "3.10"
execnet==2.1.1 ; python_version == "3.10"
filelock==3.20.0 ; python_version == "3.10"
exceptiongroup==1.3.1 ; python_version == "3.10"
execnet==2.1.2 ; python_version == "3.10"
filelock==3.20.3 ; python_version == "3.10"
funcsigs==0.4 ; python_version == "3.10"
google-api-python-client==1.6.5 ; python_version == "3.10"
google-auth==2.16.0 ; python_version == "3.10"
@ -38,17 +38,18 @@ jmespath==0.10.0 ; python_version == "3.10"
josepy==2.2.0 ; python_version == "3.10"
jsonlines==4.0.0 ; python_version == "3.10"
jsonpickle==4.1.1 ; python_version == "3.10"
librt==0.7.8 ; python_version == "3.10" and platform_python_implementation != "PyPy"
mccabe==0.7.0 ; python_version == "3.10"
mypy-extensions==1.1.0 ; python_version == "3.10"
mypy==1.18.2 ; python_version == "3.10"
mypy==1.19.1 ; python_version == "3.10"
ndg-httpsclient==0.3.2 ; python_version == "3.10"
oauth2client==4.1.3 ; python_version == "3.10"
packaging==25.0 ; python_version == "3.10"
packaging==26.0 ; python_version == "3.10"
parsedatetime==2.6 ; python_version == "3.10"
pathspec==0.12.1 ; python_version == "3.10"
pathspec==1.0.4 ; python_version == "3.10"
pbr==1.8.0 ; python_version == "3.10"
pip==25.3 ; python_version == "3.10"
platformdirs==4.5.0 ; python_version == "3.10"
pip==26.0 ; python_version == "3.10"
platformdirs==4.5.1 ; python_version == "3.10"
pluggy==1.6.0 ; python_version == "3.10"
ply==3.4 ; python_version == "3.10"
py==1.11.0 ; python_version == "3.10"
@ -56,14 +57,14 @@ pyasn1-modules==0.4.1 ; python_version == "3.10"
pyasn1==0.4.8 ; python_version == "3.10"
pycparser==2.14 ; python_version == "3.10"
pygments==2.19.2 ; python_version == "3.10"
pylint==4.0.2 ; python_version == "3.10"
pylint==4.0.4 ; python_version == "3.10"
pyopenssl==25.0.0 ; python_version == "3.10"
pyotp==2.9.0 ; python_version == "3.10"
pyparsing==2.4.7 ; python_version == "3.10"
pyparsing==3.0.0 ; python_version == "3.10"
pyrfc3339==1.0 ; python_version == "3.10"
pytest-cov==7.0.0 ; python_version == "3.10"
pytest-xdist==3.8.0 ; python_version == "3.10"
pytest==8.4.2 ; python_version == "3.10"
pytest==9.0.2 ; python_version == "3.10"
python-augeas==0.5.0 ; python_version == "3.10"
python-dateutil==2.9.0.post0 ; python_version == "3.10"
python-digitalocean==1.15.0 ; python_version == "3.10"
@ -73,25 +74,25 @@ pyyaml==6.0.3 ; python_version == "3.10"
requests-file==3.0.1 ; python_version == "3.10"
requests==2.25.1 ; python_version == "3.10"
rsa==4.9.1 ; python_version == "3.10"
ruff==0.14.3 ; python_version == "3.10"
ruff==0.15.0 ; python_version == "3.10"
s3transfer==0.5.2 ; python_version == "3.10"
setuptools==80.9.0 ; python_version == "3.10"
setuptools==80.10.2 ; python_version == "3.10"
six==1.16.0 ; python_version == "3.10"
soupsieve==2.8 ; python_version == "3.10"
tldextract==5.3.0 ; python_version == "3.10"
tomli==2.3.0 ; python_version == "3.10"
tomlkit==0.13.3 ; python_version == "3.10"
soupsieve==2.8.3 ; python_version == "3.10"
tldextract==5.3.1 ; python_version == "3.10"
tomli==2.4.0 ; python_version == "3.10"
tomlkit==0.14.0 ; python_version == "3.10"
tox==3.28.0 ; python_version == "3.10"
types-httplib2==0.31.0.20250913 ; python_version == "3.10"
types-httplib2==0.31.2.20260125 ; python_version == "3.10"
types-pyrfc3339==2.0.1.20250825 ; python_version == "3.10"
types-python-dateutil==2.9.0.20251008 ; python_version == "3.10"
types-python-dateutil==2.9.0.20260124 ; python_version == "3.10"
types-pywin32==311.0.0.20251008 ; python_version == "3.10"
types-requests==2.31.0.6 ; python_version == "3.10"
types-setuptools==80.9.0.20250822 ; python_version == "3.10"
types-setuptools==80.10.0.20260124 ; python_version == "3.10"
types-urllib3==1.26.25.14 ; python_version == "3.10"
typing-extensions==4.15.0 ; python_version == "3.10"
uritemplate==3.0.1 ; python_version == "3.10"
urllib3==1.26.5 ; python_version == "3.10"
uv==0.9.7 ; python_version == "3.10"
virtualenv==20.35.4 ; python_version == "3.10"
wheel==0.45.1 ; python_version == "3.10"
uv==0.9.28 ; python_version == "3.10"
virtualenv==20.36.1 ; python_version == "3.10"
wheel==0.46.3 ; python_version == "3.10"

View file

@ -79,7 +79,7 @@ pyOpenSSL = "25.0.0"
pyRFC3339 = "1.0"
pyasn1 = "0.4.8"
pycparser = "2.14"
pyparsing = "2.4.7"
pyparsing = "3.0.0"
python-augeas = "0.5.0"
python-digitalocean = "1.15.0"
requests = "2.25.1"

View file

@ -13,15 +13,15 @@ asttokens==3.0.1 ; python_version >= "3.10" and python_version < "4.0"
attrs==25.4.0 ; python_version >= "3.10" and python_version < "4.0"
azure-core==1.38.0 ; python_version >= "3.10" and python_version < "4.0"
azure-devops==7.1.0b4 ; python_version >= "3.10" and python_version < "4.0"
babel==2.17.0 ; python_version >= "3.10" and python_version < "4.0"
babel==2.18.0 ; python_version >= "3.10" and python_version < "4.0"
backports-tarfile==1.2.0 ; python_version >= "3.10" and python_version < "3.12"
bcrypt==5.0.0 ; python_version >= "3.10" and python_version < "4.0"
beautifulsoup4==4.14.3 ; python_version >= "3.10" and python_version < "4.0"
boto3==1.42.38 ; python_version >= "3.10" and python_version < "4.0"
botocore==1.42.38 ; python_version >= "3.10" and python_version < "4.0"
boto3==1.42.40 ; python_version >= "3.10" and python_version < "4.0"
botocore==1.42.40 ; python_version >= "3.10" and python_version < "4.0"
build==1.4.0 ; python_version >= "3.10" and python_version < "4.0"
cachecontrol==0.14.4 ; python_version >= "3.10" and python_version < "4.0"
cachetools==6.2.6 ; python_version >= "3.10" and python_version < "4.0"
cachetools==7.0.0 ; python_version >= "3.10" and python_version < "4.0"
certifi==2026.1.4 ; python_version >= "3.10" and python_version < "4.0"
cffi==2.0.0 ; python_version >= "3.10" and python_version < "4.0"
chardet==5.2.0 ; python_version >= "3.10" and python_version < "4.0"
@ -32,7 +32,7 @@ cloudflare==2.19.4 ; python_version >= "3.10" and python_version < "4.0"
colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0"
configargparse==1.7.1 ; python_version >= "3.10" and python_version < "4.0"
configobj==5.0.9 ; python_version >= "3.10" and python_version < "4.0"
coverage==7.13.2 ; python_version >= "3.10" and python_version < "4.0"
coverage==7.13.3 ; python_version >= "3.10" and python_version < "4.0"
crashtest==0.4.1 ; python_version >= "3.10" and python_version < "4.0"
cryptography==46.0.4 ; python_version >= "3.10" and python_version < "4.0"
cython==0.29.37 ; python_version >= "3.10" and python_version <= "3.12"
@ -46,7 +46,7 @@ dns-lexicon==3.23.2 ; python_version >= "3.10" and python_version < "4.0"
dnspython==2.8.0 ; python_version >= "3.10" and python_version < "4.0"
docutils==0.21.2 ; python_version == "3.10"
docutils==0.22.4 ; python_version >= "3.11" and python_version < "4.0"
dulwich==0.25.2 ; python_version >= "3.10" and python_version < "4.0"
dulwich==1.0.0 ; python_version >= "3.10" and python_version < "4.0"
exceptiongroup==1.3.1 ; python_version == "3.10"
execnet==2.1.2 ; python_version >= "3.10" and python_version < "4.0"
executing==2.2.1 ; python_version >= "3.10" and python_version < "4.0"
@ -73,7 +73,7 @@ invoke==2.2.1 ; python_version >= "3.10" and python_version < "4.0"
ipdb==0.13.13 ; python_version >= "3.10" and python_version < "4.0"
ipython-pygments-lexers==1.1.1 ; python_version >= "3.11" and python_version < "4.0"
ipython==8.38.0 ; python_version == "3.10"
ipython==9.9.0 ; python_version >= "3.11" and python_version < "4.0"
ipython==9.10.0 ; python_version >= "3.11" and python_version < "4.0"
isodate==0.7.2 ; python_version >= "3.10" and python_version < "4.0"
isort==5.13.2 ; python_version >= "3.10" and python_version < "4.0"
jaraco-classes==3.4.0 ; python_version >= "3.10" and python_version < "4.0"
@ -105,16 +105,16 @@ parsedatetime==2.6 ; python_version >= "3.10" and python_version < "4.0"
parso==0.8.5 ; python_version >= "3.10" and python_version < "4.0"
pbs-installer==2026.1.27 ; python_version >= "3.10" and python_version < "4.0"
pexpect==4.9.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform != "win32" and sys_platform != "emscripten"
pip==25.3 ; python_version >= "3.10" and python_version < "4.0"
pip==26.0 ; python_version >= "3.10" and python_version < "4.0"
pkginfo==1.12.1.2 ; python_version >= "3.10" and python_version < "4.0"
platformdirs==4.5.1 ; python_version >= "3.10" and python_version < "4.0"
pluggy==1.6.0 ; python_version >= "3.10" and python_version < "4.0"
ply==3.11 ; python_version >= "3.10" and python_version < "4.0"
poetry-core==2.3.0 ; python_version >= "3.10" and python_version < "4.0"
poetry-core==2.3.1 ; python_version >= "3.10" and python_version < "4.0"
poetry-plugin-export==1.10.0 ; python_version >= "3.10" and python_version < "4.0"
poetry==2.3.1 ; python_version >= "3.10" and python_version < "4.0"
poetry==2.3.2 ; python_version >= "3.10" and python_version < "4.0"
prompt-toolkit==3.0.52 ; python_version >= "3.10" and python_version < "4.0"
proto-plus==1.27.0 ; python_version >= "3.10" and python_version < "4.0"
proto-plus==1.27.1 ; python_version >= "3.10" and python_version < "4.0"
protobuf==6.33.5 ; python_version >= "3.10" and python_version < "4.0"
ptyprocess==0.7.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform != "win32" and sys_platform != "emscripten"
pure-eval==0.2.3 ; python_version >= "3.10" and python_version < "4.0"
@ -126,7 +126,7 @@ pylint==3.3.3 ; python_version >= "3.10" and python_version < "4.0"
pynacl==1.6.2 ; python_version >= "3.10" and python_version < "4.0"
pyopenssl==25.3.0 ; python_version >= "3.10" and python_version < "4.0"
pyotp==2.9.0 ; python_version >= "3.10" and python_version < "4.0"
pyparsing==3.2.5 ; python_version >= "3.10" and python_version < "4.0"
pyparsing==3.3.2 ; python_version >= "3.10" and python_version < "4.0"
pyproject-api==1.10.0 ; python_version >= "3.10" and python_version < "4.0"
pyproject-hooks==1.2.0 ; python_version >= "3.10" and python_version < "4.0"
pyrfc3339==2.1.0 ; python_version >= "3.10" and python_version < "4.0"
@ -146,10 +146,10 @@ requests-oauthlib==2.0.0 ; python_version >= "3.10" and python_version < "4.0"
requests-toolbelt==1.0.0 ; python_version >= "3.10" and python_version < "4.0"
requests==2.32.5 ; python_version >= "3.10" and python_version < "4.0"
rfc3986==2.0.0 ; python_version >= "3.10" and python_version < "4.0"
rich==14.3.1 ; python_version >= "3.10" and python_version < "4.0"
rich==14.3.2 ; python_version >= "3.10" and python_version < "4.0"
roman-numerals==4.1.0 ; python_version >= "3.11" and python_version < "4.0"
rsa==4.9.1 ; python_version >= "3.10" and python_version < "4.0"
ruff==0.14.14 ; python_version >= "3.10" and python_version < "4.0"
ruff==0.15.0 ; python_version >= "3.10" and python_version < "4.0"
s3transfer==0.16.0 ; python_version >= "3.10" and python_version < "4.0"
secretstorage==3.5.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "linux"
semantic-version==2.10.0 ; python_version >= "3.10" and python_version < "4.0"
@ -190,9 +190,9 @@ uritemplate==4.2.0 ; python_version >= "3.10" and python_version < "4.0"
urllib3==2.6.3 ; python_version >= "3.10" and python_version < "4.0"
uv==0.9.28 ; python_version >= "3.10" and python_version < "4.0"
virtualenv==20.36.1 ; python_version >= "3.10" and python_version < "4.0"
wcwidth==0.5.2 ; python_version >= "3.10" and python_version < "4.0"
wcwidth==0.5.3 ; python_version >= "3.10" and python_version < "4.0"
wheel==0.46.3 ; python_version >= "3.10" and python_version < "4.0"
wrapt==2.0.1 ; python_version >= "3.10" and python_version < "4.0"
wrapt==2.1.1 ; python_version >= "3.10" and python_version < "4.0"
xattr==1.3.0 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "darwin"
zipp==3.23.0 ; python_version >= "3.10" and python_version < "3.12"
zstandard==0.25.0 ; python_version >= "3.10" and python_version < "4.0"