Coding questions · Bloomberg
Implement classes within an abstract Python framework
This question evaluates a candidate's skills in object-oriented design, implementing concrete classes from abstract interfaces, file I/O and streaming data transformations, error handling, and integration with a registry and CLI within a Python codebase.
You are given an existing Python codebase (~200 lines shown) that defines an abstract base class DataProcessor with abstract methods load(self), transform(self, record), and save(self, records); a registry/factory that instantiates processors by name; and a CLI that wires them together. Without editing the abstractions, implement a concrete CsvToJsonProcessor that: (1) reads CSV files in chunks; (2) transforms each row to a normalized JSON object; (3) writes line-delimited JSON to an output path; (4) handles bad rows with a retry/logging policy; and (5) integrates with the registry so the CLI can invoke it by the name 'csv_to_json'. Provide the class implementation outline and explain the control flow through the existing template methods.
Implement a Chunked CsvToJsonProcessor
You are working inside an existing Python framework that already defines an abstract base class DataProcessor with template methods load, transform, and save, plus a registry/factory and CLI wiring. Your task is to implement the core behavior of a concrete processor named csv_to_json.
To keep the problem self-contained and testable, you will write a function that simulates what the concrete CsvToJsonProcessor would do:
load: read CSV records in contiguous chunks of sizechunk_sizetransform: normalize one row at a time into a JSON-ready objectsave: emit line-delimited JSON records in the same order as successful rows- bad rows: retry transformation up to
max_retriestimes after the first failure; if the row still fails, log it by recording its 0-based row index and skip it
Normalization rules:
- Normalize each header by trimming whitespace, lowercasing, and replacing runs of spaces with a single underscore.
- The normalized header is guaranteed to contain
id,name, andage, and normalized headers are unique. - A row is bad if its column count does not match the header length.
- Required fields:
id,name,age.id: convert to integername: trim and collapse internal whitespace to a single space; it must not be emptyage: convert to integer and it must be non-negative
- Optional fields:
- empty string becomes
None email, if present and non-empty, is lowercased- all other optional fields are trimmed but otherwise unchanged
- empty string becomes
- Output each successful record as a compact JSON string (one JSON object per line conceptually).
Return a tuple (output_lines, failed_rows) where:
output_linesis a list of JSON strings for successfully processed rowsfailed_rowsis a list of 0-based row indices that still failed after all retries
This captures the control flow of the framework's template methods without requiring actual file I/O.
Constraints
- 0 <= len(rows) <= 100000
- 1 <= len(header) <= 50
- 1 <= chunk_size <= 10000
- 0 <= max_retries <= 5
- The normalized header contains the keys
id,name, andage - Normalized header names are unique
Examples
Example 1
Input: ([' ID ', ' Name ', 'Age', ' Email '], [['1', ' Alice ', '30', 'ALICE@EXAMPLE.COM'], ['x2', 'Bob', '22', 'bob@example.com'], ['3', ' Carol Danvers ', '41', ''], ['4', ' ', '28', 'dave@example.com'], ['5', 'Eve', '0', 'EVE@EXAMPLE.COM']], 2, 1)
Output: (['{"id":1,"name":"Alice","age":30,"email":"alice@example.com"}', '{"id":3,"name":"Carol Danvers","age":41,"email":null}', '{"id":5,"name":"Eve","age":0,"email":"eve@example.com"}'], [1, 3])
Explanation: Rows 1 and 3 fail because id is not an integer and name becomes empty after trimming. The other rows are normalized and emitted as compact JSON.
Example 2
Input: (['id', 'name', 'age'], [], 3, 2)
Output: ([], [])
Explanation: No data rows means no output lines and no failures.
Hints
- Precompute the normalized column names once, then use a helper to validate and transform a single row.
- Process rows chunk by chunk, but keep the original row index so failed rows can be logged correctly.