Repository navigation
fix(querystring): preserve empty string values - #3838
sylvesterkaczmarek wants to merge 5 commits into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
6b4e277 to
11607aa
Compare
|
Following up after the Group 6 maintenance pass. This branch is current with main, mergeable, and has no failing checks or unresolved review threads. Could a maintainer review it when convenient? |
|
The scalar fix is correct, but I found one remaining branch-specific inconsistency that the current tests do not cover.
from openai._qs import Querystring
qs = Querystring()
assert qs.stringify({"filter": None}, array_format="comma") == ""
assert qs.stringify({"filter": [None]}, array_format="comma") == "filter=" # currently fails the invariantThis matters because the PR establishes the semantic distinction A regression test should cover both sides of the distinction: assert stringify({"filter": [None]}, array_format="comma") == ""
assert stringify({"filter": [""]}, array_format="comma") == "filter="
assert stringify({"filter": [None, "active"]}, array_format="comma") == "filter=active"The implementation can preserve explicit empty strings while omitting an all- |
|
Thanks, reproduced. Fixed in GitHub-Verified commit 2eff92f: comma-format arrays now drop None values and omit the parameter entirely when nothing remains, while explicit empty strings still serialize as filter=. Added regressions for [], [None], [""], and [None, "active"]. Focused test_qs suite passes 11/11; Ruff check/format and git diff --check are clean. A maintainer re-review of the refreshed head would be appreciated. |
|
Checked the refreshed head against both sides of the distinction: value is None now returns early, the comma branch filters None items and returns nothing when the filtered list is empty, and an empty string still serialises as an explicit empty value. The issue's scalar case and the nested-array case I raised are both covered. One residual test gap I would close before merge, because the invariant now lives in the shared helper rather than in the comma branch alone: all four array formats should agree on the nothing-left-after-filtering outcome. @pytest.mark.parametrize("array_format", ["comma", "repeat", "indices", "brackets"]) repeat, indices and brackets already produce the empty string for both inputs today, since each one recurses per element back into _stringify_item, but only the comma cases are asserted. A parameterised test would tie that to the shared implementation, so a later change to one branch cannot leave the four formats disagreeing. The non-empty cases stay deliberately format-specific — an empty string becomes filter= under comma but filter[]= or filter[0]= under the others — so a one-line comment in the test saying that only the empty outcome is shared would stop the next contributor from trying to unify them. Since the PR notes that no local suite run was possible, CI is the only verification of the refreshed head, and the focused test_qs additions look sufficient on their own. |
|
Added the cross-format regression in d42e99f. comma, repeat, indices and brackets now all explicitly assert that [] and [None] omit the parameter, with a note that non-empty serialization remains intentionally format-specific. Focused test_qs passes 15/15; Ruff check/format and git diff --check are clean. Commit is GitHub Verified. |
|
Great, this closes the remaining cross-format gap while preserving the intentional differences for non-empty values. The 15/15 focused test result, clean Ruff checks, and verified commit provide a solid verification record. The parameterized empty-array coverage now protects the shared invariant across all four formats, so I have no further changes to request from this review. |
|
Independent offline validation of this existing fix at head The failing baseline shapes were scalar empty string, nested brackets/dots, repeated/bracketed/indexed arrays containing an empty string, and a tuple containing an empty string. Controls retained None omission, false, numeric zero, whitespace, Unicode/escaped query text, and comma-array serialization. Python 3.14.7, fictional query values, network denied, no client/API calls. Exact source imports share the existing isolated dependency environment; this is not a full-suite or historical dependency-matrix approval. I have not created a competing PR. Diagnostic below: """Offline Querystring matrix; no clients or transport."""
import json
from urllib.parse import parse_qsl
import openai._qs as qs
cases=[('empty',{'filter':''},{},[('filter','')]),('none',{'filter':None},{},[]),('false',{'filter':False},{},[('filter','false')]),('zero',{'filter':0},{},[('filter','0')]),('space',{'filter':' '},{},[('filter',' ')]),('escaping',{'filter':'&=雪'},{},[('filter','&=雪')]),('nested_brackets',{'filter':{'name':''}},{},[('filter[name]','')]),('nested_dots',{'filter':{'name':''}},{'nested_format':'dots'},[('filter.name','')]),('repeat',{'filter':['','x',None]},{},[('filter',''),('filter','x')]),('brackets',{'filter':['','x',None]},{'array_format':'brackets'},[('filter[]',''),('filter[]','x')]),('indices',{'filter':['','x',None]},{'array_format':'indices'},[('filter[0]',''),('filter[1]','x')]),('comma',{'filter':['','x',None]},{'array_format':'comma'},[('filter',',x')]),('tuple',{'filter':('','x')},{},[('filter',''),('filter','x')])]
rows=[]
for name,value,options,expected in cases:
encoded=qs.stringify(value,**options);actual=parse_qsl(encoded,keep_blank_values=True)
rows.append({'case':name,'pass':actual==expected,'encoded':encoded,'actual':actual,'expected':expected})
print(json.dumps({'source_module':qs.__file__,'cases':rows,'passed':sum(r['pass'] for r in rows),'failed':sum(not r['pass'] for r in rows),'scope':'Querystring helper; parse_qsl with keep_blank_values=True is inspection oracle, not SDK parse() round-trip contract.'}))AI disclosure: Codex prepared and executed these automated diagnostic checks; all inputs and backend outcomes are fictional. |
|
Thanks for the careful follow-up. I re-checked the The scalar distinction established by this PR should also hold inside comma-separated arrays: stringify({"filter": None}, array_format="comma") == ""
stringify({"filter": [None]}, array_format="comma") == ""
stringify({"filter": [""]}, array_format="comma") == "filter="
stringify({"filter": [None, "active"]}, array_format="comma") == "filter=active"At the moment, the comma branch filters Once this is addressed, the fix looks focused and consistent with the semantics established by the PR. |
|
AI-assisted follow-up to the latest comment: I re-ran its four examples plus The current comma branch filters None into a list and returns no items when that list is empty, before joining. Its file is byte-identical to the pinned head in this check. The all-None concern therefore appears to describe an earlier revision. This is a local query-helper check with network denied, not a server-contract or full-suite approval. No additional code change was needed for those examples. |
Summary
Nonequery values omittedFixes #3837
Tests
Regression coverage added in
tests/test_qs_empty_values.py. The connected development host was unavailable for a local suite run.