From 89294fabcee377da647170b59f30c79eb7ba5fae Mon Sep 17 00:00:00 2001 From: Dan LaManna Date: Wed, 30 Sep 2026 12:39:51 -0400 Subject: [PATCH 1/2] Upgrade pyparsing to v3.3.3 --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 83424092..27ef51a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,7 +45,7 @@ dependencies = [ "pycountry==26.2.16", "pydantic==2.13.4", "pyexiv2==2.15.5", - "pyparsing==3.3.2", + "pyparsing==3.3.3", "python-magic==0.4.27", "pyyaml==6.0.3", "requests==2.34.2", diff --git a/uv.lock b/uv.lock index 88f56e1a..3866dfe6 100644 --- a/uv.lock +++ b/uv.lock @@ -1876,7 +1876,7 @@ requires-dist = [ { name = "pydantic", specifier = "==2.13.4" }, { name = "pydantic-to-pyarrow", git = "https://github.com/ImageMarkup/pydantic-to-pyarrow?branch=support-computed-fields" }, { name = "pyexiv2", specifier = "==2.15.5" }, - { name = "pyparsing", specifier = "==3.3.2" }, + { name = "pyparsing", specifier = "==3.3.3" }, { name = "python-magic", specifier = "==0.4.27" }, { name = "pyyaml", specifier = "==6.0.3" }, { name = "requests", specifier = "==2.34.2" }, @@ -3204,11 +3204,11 @@ crypto = [ [[package]] name = "pyparsing" -version = "3.3.2" +version = "3.3.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/11/b213bebff182584360cb8d17c72c1677fec5c5c228de439e63bcf8ab1c8f/pyparsing-3.3.3.tar.gz", hash = "sha256:928ae7e20211f3b6f3915a72f06a0cfd29ab9d24279dd6346b6b1a7146397d36", size = 1050487, upload-time = "2026-09-20T20:59:05.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, + { url = "https://files.pythonhosted.org/packages/38/bb/d215ee7c73b61497b28a5503f9f53523f294fcc936762b7caf90e0c1c2b5/pyparsing-3.3.3-py3-none-any.whl", hash = "sha256:ece8c00a69cf01b45d0b1dedabb469c90d8caf996d4fda40f147627a122849a4", size = 126420, upload-time = "2026-09-20T20:59:04.025Z" }, ] [[package]] From 434f8be297f67267a0b352fdc6a043c73e99558d Mon Sep 17 00:00:00 2001 From: Dan LaManna Date: Wed, 30 Sep 2026 12:39:59 -0400 Subject: [PATCH 2/2] Return the OR result from the DSL parse actions The OR parse actions replaced toks[0] in place and returned None. With pyparsing 3.3.3, an OR in parentheses inside an AND reached the AND action still wrapped in a ParseResults. The ES parser then put a list inside a bool filter, and the Django parser raised a TypeError. --- isic/core/dsl.py | 4 ++-- isic/core/tests/test_dsl.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/isic/core/dsl.py b/isic/core/dsl.py index f6170388..1e8add9c 100644 --- a/isic/core/dsl.py +++ b/isic/core/dsl.py @@ -211,7 +211,7 @@ def es_query_or(s, loc, toks): ret: dict[str, Any] = {"bool": {"should": []}} for tok in toks[0]: ret["bool"]["should"].append(tok) - toks[0] = ret + return ret def q(s, loc, toks): @@ -245,7 +245,7 @@ def q_or(s, loc, toks): ret = Q() for tok in toks[0]: ret |= tok - toks[0] = ret + return ret # Lucene DSL only supports uppercase AND/OR/TO diff --git a/isic/core/tests/test_dsl.py b/isic/core/tests/test_dsl.py index 09f2e2ef..277d58a2 100644 --- a/isic/core/tests/test_dsl.py +++ b/isic/core/tests/test_dsl.py @@ -80,6 +80,14 @@ "public:true image_type:dermoscopic", Q(public=True) & Q(accession__image_type="dermoscopic"), ), + ( + "diagnosis_1:foobar OR (diagnosis_1:foobaz AND (diagnosis_1:foo* OR age_approx:50))", + Q(accession__diagnosis_1="foobar") + | ( + Q(accession__diagnosis_1="foobaz") + & (Q(accession__diagnosis_1__startswith="foo") | Q(accession__age__approx=50)) + ), + ), ], ) def test_dsl_django_parser(query, filter_or_exception):