parse_client_csv from yesterday works for clean CSVs. But a real time-tracker export has client names like "Smith, Jones" — a comma inside a quoted field. split(",") splits it into two broken fields. How do you handle that?
I would need a smarter parser that knows about quote characters. The manual split approach does not know context.
Python's csv.DictReader knows exactly that. Wrap the CSV string in io.StringIO to make it behave like a file, pass it to DictReader, and iterate rows as dicts automatically — header keys, quoted fields, and all:
import csv
import io
csv_text = 'name,rate,hours\n"Smith, Jones",200.0,3.0'
reader = csv.DictReader(io.StringIO(csv_text))
for row in reader:
print(row) # {"name": "Smith, Jones", "rate": "200.0", ...}Why io.StringIO? I thought csv.DictReader takes a file. This looks like a fake file.
io.StringIO wraps a string in a file-like interface. csv.DictReader expects anything it can iterate line by line — it does not care whether it is a real file or a StringIO wrapper. Same result, zero disk access.
So load_clients_from_csv replaces parse_client_csv with the same output shape but handles edge cases automatically.
Same output, better parser. The rest of the pipeline never changes:
import csv, io
def load_clients_from_csv(csv_text: str) -> list:
reader = csv.DictReader(io.StringIO(csv_text))
clients = []
for row in reader:
clients.append({"name": row["name"], "rate": float(row["rate"]), "hours": float(row["hours"])})
print(f"Loaded {len(clients)} clients")
return clientsMy time-tracker exports all have inconsistent quoting. This handles it without special cases.
And the downstream functions — filter_active_clients, status_summary, make_invoice_line — all accept the same list of dicts. Swap the parser; keep the pipeline.
csv.DictReader with io.StringIOcsv.DictReader parses each CSV row into a dict, using the header row as keys. To parse a string (not a file), wrap it in io.StringIO first:
import csv, io
reader = csv.DictReader(io.StringIO(csv_text))
for row in reader:
print(row) # {'name': 'Acme', 'rate': '120', 'hours': '4.5'}Type conversion: DictReader always returns strings. Convert numeric fields explicitly:
client = {'name': row['name'], 'rate': float(row['rate']), 'hours': float(row['hours'])}Why prefer DictReader? It handles quoted fields with commas, escaped characters, and varying whitespace — edge cases that break line.split(',') approaches on real export files.
parse_client_csv from yesterday works for clean CSVs. But a real time-tracker export has client names like "Smith, Jones" — a comma inside a quoted field. split(",") splits it into two broken fields. How do you handle that?
I would need a smarter parser that knows about quote characters. The manual split approach does not know context.
Python's csv.DictReader knows exactly that. Wrap the CSV string in io.StringIO to make it behave like a file, pass it to DictReader, and iterate rows as dicts automatically — header keys, quoted fields, and all:
import csv
import io
csv_text = 'name,rate,hours\n"Smith, Jones",200.0,3.0'
reader = csv.DictReader(io.StringIO(csv_text))
for row in reader:
print(row) # {"name": "Smith, Jones", "rate": "200.0", ...}Why io.StringIO? I thought csv.DictReader takes a file. This looks like a fake file.
io.StringIO wraps a string in a file-like interface. csv.DictReader expects anything it can iterate line by line — it does not care whether it is a real file or a StringIO wrapper. Same result, zero disk access.
So load_clients_from_csv replaces parse_client_csv with the same output shape but handles edge cases automatically.
Same output, better parser. The rest of the pipeline never changes:
import csv, io
def load_clients_from_csv(csv_text: str) -> list:
reader = csv.DictReader(io.StringIO(csv_text))
clients = []
for row in reader:
clients.append({"name": row["name"], "rate": float(row["rate"]), "hours": float(row["hours"])})
print(f"Loaded {len(clients)} clients")
return clientsMy time-tracker exports all have inconsistent quoting. This handles it without special cases.
And the downstream functions — filter_active_clients, status_summary, make_invoice_line — all accept the same list of dicts. Swap the parser; keep the pipeline.
csv.DictReader with io.StringIOcsv.DictReader parses each CSV row into a dict, using the header row as keys. To parse a string (not a file), wrap it in io.StringIO first:
import csv, io
reader = csv.DictReader(io.StringIO(csv_text))
for row in reader:
print(row) # {'name': 'Acme', 'rate': '120', 'hours': '4.5'}Type conversion: DictReader always returns strings. Convert numeric fields explicitly:
client = {'name': row['name'], 'rate': float(row['rate']), 'hours': float(row['hours'])}Why prefer DictReader? It handles quoted fields with commas, escaped characters, and varying whitespace — edge cases that break line.split(',') approaches on real export files.
Theo's time-tracker exports CSVs where some client names contain commas — like `"Smith, Jones"`. His manual split parser breaks on these. Write `load_clients_from_csv(csv_text)` using `csv.DictReader` and `io.StringIO` that returns a list of dicts with `name` as string and `rate`, `hours` as floats. The CSV always has a header row with `name`, `rate`, `hours` columns.
Tap each step for scaffolded hints.
No blank-editor panic.