Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 397d5a1

Browse files
author
anonymous
committed
fix(bsrch): restore Excel grid responses
Route BSRCH search parameters through ExcelGetGrid overrides and parse the actual GridResponse shape instead of the BEQS result schema. Constraint: Avoid Bloomberg-proprietary data in tests and issue updates Rejected: Auto-paginate ReachMax responses | live SequenceNumber probing returned empty continuation pages, so the fix validates reported row counts instead Confidence: high Scope-risk: moderate Not-tested: Full Python typecheck; existing repo-wide diagnostics remain unrelated to BSRCH
1 parent 4c718e3 commit 397d5a1

9 files changed

Lines changed: 716 additions & 146 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- **`bsrch` / `absrch` Excel grid requests restored (#333, #334)**: Python now sends BSRCH search parameters as `ExcelGetGridRequest` `Overrides[]` entries while keeping `Domain` as the top-level request element, so Excel-style weather searches no longer fail with Bloomberg element validation errors. The Rust extractor now parses `GridResponse.ColumnTitles[]` and `DataRecords[].DataFields[]` directly instead of the BEQS schema, preserves Bloomberg column names, propagates grid errors, and validates reported row/field counts rather than returning an empty placeholder ticker column.
13+
- **`bsrch` / `absrch` request plumbing hardened (#333, #334)**: Low-level `arequest()` now preserves `//blp/exrsvc` overrides as overrides instead of rewriting them into root elements, and the Rust engine normalizes `ExcelGetGridRequest` `Domain`/override pairs consistently across generated endpoints, raw requests, and direct request kwargs. `RawRequest` calls whose effective operation is `ExcelGetGridRequest` now pick the BSRCH extractor by default, with regression coverage for malformed Python override pairs and live weather-grid requests.
14+
1015
## [1.2.2] - 2026-05-06
1116

1217
### Fixed

crates/xbbg-async/src/engine/mod.rs

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,10 +879,17 @@ impl RequestParams {
879879
}
880880
}
881881

882+
pub(crate) fn is_excel_get_grid_request(&self) -> bool {
883+
matches!(
884+
parse_operation_lossless(self.effective_operation()),
885+
Operation::ExcelGetGrid
886+
)
887+
}
888+
882889
/// Apply default values derived from operation semantics.
883890
pub fn with_defaults(mut self) -> Self {
884891
if !self.extractor_set && self.extractor == ExtractorType::default() {
885-
let operation = parse_operation_lossless(&self.operation);
892+
let operation = parse_operation_lossless(self.effective_operation());
886893
self.extractor = operation.default_extractor();
887894
}
888895
self
@@ -1110,6 +1117,43 @@ impl RequestParams {
11101117
}
11111118
}
11121119

1120+
fn normalize_excel_grid_params(params: &mut RequestParams, kwargs: HashMap<String, String>) {
1121+
let mut domain: Option<String> = None;
1122+
let mut grid_overrides: Vec<(String, String)> = Vec::new();
1123+
1124+
fn route_pair(
1125+
domain: &mut Option<String>,
1126+
grid_overrides: &mut Vec<(String, String)>,
1127+
key: String,
1128+
value: String,
1129+
) {
1130+
if key.eq_ignore_ascii_case("Domain") {
1131+
*domain = Some(value);
1132+
} else if !key.is_empty() && !key.eq_ignore_ascii_case("Overrides") {
1133+
grid_overrides.push((key, value));
1134+
}
1135+
}
1136+
1137+
for (key, value) in params.elements.take().unwrap_or_default() {
1138+
route_pair(&mut domain, &mut grid_overrides, key, value);
1139+
}
1140+
1141+
for (key, value) in params.overrides.take().unwrap_or_default() {
1142+
route_pair(&mut domain, &mut grid_overrides, key, value);
1143+
}
1144+
1145+
let mut keys: Vec<String> = kwargs.keys().cloned().collect();
1146+
keys.sort();
1147+
for key in keys {
1148+
if let Some(value) = kwargs.get(&key) {
1149+
route_pair(&mut domain, &mut grid_overrides, key, value.clone());
1150+
}
1151+
}
1152+
1153+
params.elements = domain.map(|value| vec![("Domain".to_string(), value)]);
1154+
params.overrides = (!grid_overrides.is_empty()).then_some(grid_overrides);
1155+
}
1156+
11131157
fn merge_raw_kwargs_into_elements(params: &mut RequestParams, kwargs: HashMap<String, String>) {
11141158
if kwargs.is_empty() {
11151159
return;
@@ -1415,6 +1459,11 @@ impl Engine {
14151459
params.validate()?;
14161460

14171461
let kwargs = params.kwargs.take().unwrap_or_default();
1462+
if params.is_excel_get_grid_request() {
1463+
normalize_excel_grid_params(&mut params, kwargs);
1464+
return Ok(params);
1465+
}
1466+
14181467
if params.is_raw_request() {
14191468
merge_raw_kwargs_into_elements(&mut params, kwargs);
14201469
return Ok(params);
@@ -2365,6 +2414,63 @@ mod tests {
23652414
);
23662415
}
23672416

2417+
#[test]
2418+
fn normalize_excel_grid_params_routes_domain_and_grid_overrides() {
2419+
let mut params = RequestParams {
2420+
operation: Operation::ExcelGetGrid.to_string(),
2421+
elements: Some(vec![
2422+
("Domain".to_string(), "FI:OLD".to_string()),
2423+
("provider".to_string(), "wsi".to_string()),
2424+
]),
2425+
overrides: Some(vec![
2426+
("location".to_string(), "nwe".to_string()),
2427+
("Domain".to_string(), "COMDTY:WEATHER".to_string()),
2428+
]),
2429+
..Default::default()
2430+
};
2431+
2432+
normalize_excel_grid_params(
2433+
&mut params,
2434+
HashMap::from([("model".to_string(), "ecmwf".to_string())]),
2435+
);
2436+
2437+
assert_eq!(
2438+
params.elements,
2439+
Some(vec![("Domain".to_string(), "COMDTY:WEATHER".to_string())])
2440+
);
2441+
assert_eq!(
2442+
params.overrides,
2443+
Some(vec![
2444+
("provider".to_string(), "wsi".to_string()),
2445+
("location".to_string(), "nwe".to_string()),
2446+
("model".to_string(), "ecmwf".to_string()),
2447+
])
2448+
);
2449+
}
2450+
2451+
#[test]
2452+
fn excel_grid_detection_uses_raw_request_operation() {
2453+
let params = RequestParams {
2454+
operation: Operation::RawRequest.to_string(),
2455+
request_operation: Some(Operation::ExcelGetGrid.to_string()),
2456+
..Default::default()
2457+
};
2458+
2459+
assert!(params.is_excel_get_grid_request());
2460+
}
2461+
2462+
#[test]
2463+
fn raw_excel_grid_defaults_to_bsrch_extractor() {
2464+
let params = RequestParams {
2465+
operation: Operation::RawRequest.to_string(),
2466+
request_operation: Some(Operation::ExcelGetGrid.to_string()),
2467+
..Default::default()
2468+
}
2469+
.with_defaults();
2470+
2471+
assert_eq!(params.extractor, ExtractorType::Bsrch);
2472+
}
2473+
23682474
#[test]
23692475
fn subscription_status_records_failure_and_removes_active_topic() {
23702476
let metric = Arc::new(SubscriptionMetrics {

0 commit comments

Comments
 (0)