"""Own Python UTF-8 serializer rehearsal, NOT pgAdmin/PostgreSQL export results.""" from pathlib import Path import csv,json,platform,hashlib,unicodedata,xml.etree.ElementTree as ET from datetime import datetime,timezone R=Path(__file__).resolve().parent;D=R/'data';D.mkdir(exist_ok=True);F=R/'output/rehearsal-files';F.mkdir(parents=True,exist_ok=True) fixtures=[('E01','Accented text','Fictional Ọlá'),('E02','Combining characters','Fictional A\u0300da'),('E03','Naira symbol','₦1500'),('E04','Comma','Lagos, example'),('E05','Quotation marks','A "sample"'),('E06','Embedded newline','Line 1\nLine 2'),('E07','Leading zeros','001234'),('E08','Long text identifier','9007199254740993'),('E09','Empty string',''),('E10','Null proxy',None),('E11','Whitespace',' sample '),('E12','Ordinary ASCII','sample')] def csvout(path,rows): with path.open('w',encoding='utf-8',newline='') as f: w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows) rows=[{'case_id':i,'risk':risk,'value':value} for i,risk,value in fixtures] assert len(rows)==12 and len({r['case_id'] for r in rows})==12 # CSV contract: backslash-N represents null; all other values remain text. # A literal backslash-N string is not among this deliberately limited fixture pack. csvout(F/'local-roundtrip.csv',[{**r,'value':'\\N' if r['value'] is None else r['value']} for r in rows]) with (F/'local-roundtrip.csv').open(encoding='utf-8',newline='') as f: csv_read=[{**r,'value':None if r['value']=='\\N' else r['value']} for r in csv.DictReader(f)] (F/'local-roundtrip.json').write_text(json.dumps(rows,ensure_ascii=False,indent=2)+'\n',encoding='utf-8');json_read=json.loads((F/'local-roundtrip.json').read_text(encoding='utf-8')) root=ET.Element('fixtures') for r in rows: e=ET.SubElement(root,'row',{'case_id':r['case_id'],'risk':r['risk']});v=ET.SubElement(e,'value') if r['value'] is None:v.set('is_null','true') else:v.text=r['value'] ET.ElementTree(root).write(F/'local-roundtrip.xml',encoding='utf-8',xml_declaration=True) xml_read=[{'case_id':e.get('case_id'),'risk':e.get('risk'),'value':None if e.find('value').get('is_null')=='true' else (e.find('value').text or '')} for e in ET.parse(F/'local-roundtrip.xml').getroot()] results=[] for fmt,actual in [('CSV',csv_read),('JSON',json_read),('XML',xml_read)]: assert [r['case_id'] for r in actual]==[r['case_id'] for r in rows] for expected,got in zip(rows,actual): ok=expected==got and (got['value'] is None or type(got['value']) is str) results.append({'case_id':expected['case_id'],'risk':expected['risk'],'format':fmt,'value_equal':expected['value']==got['value'],'type_equal':type(expected['value']) is type(got['value']),'result':'exact_preservation' if ok else 'unexpected_transformation','environment':'Python3.9.6_standard_library_not_pgAdmin','encoding':'UTF-8','bom':False,'normalisation':'none'}) assert len(results)==36 and all(r['result']=='exact_preservation' for r in results) csvout(D/'encoding-fictional-fixtures.csv',[{'case_id':r['case_id'],'risk':r['risk'],'expected_value':'' if r['value'] is None else r['value'],'expected_is_null':r['value'] is None,'expected_type':'null_proxy' if r['value'] is None else 'text'} for r in rows]) csvout(D/'encoding-local-rehearsal-results.csv',results) config=[{'format':'CSV','encoding':'UTF-8','bom':False,'writer':'Python csv.DictWriter','reader':'Python csv.DictReader','null_policy':'\\N marker restored as None; literal marker not covered','normalisation':'none','pgadmin_tested':False,'postgresql_tested':False},{'format':'JSON','encoding':'UTF-8','bom':False,'writer':'Python json.dumps ensure_ascii=False','reader':'Python json.loads','null_policy':'JSON null restored as None','normalisation':'none','pgadmin_tested':False,'postgresql_tested':False},{'format':'XML','encoding':'UTF-8','bom':False,'writer':'Python ElementTree','reader':'Python ElementTree','null_policy':'Custom is_null attribute; empty element without flag restored as empty text','normalisation':'none','pgadmin_tested':False,'postgresql_tested':False}] csvout(D/'encoding-rehearsal-configurations.csv',config) csvout(D/'encoding-pgadmin-ledger-template.csv',[{'case_id':r['case_id'],'risk':r['risk'],'format':fmt,'pgadmin_version':'','postgresql_version':'','export_encoding':'','bom':'','null_policy':'','reader_version':'','normalisation_policy':'','expected_schema':'text_or_null','actual_value':'','classification':'','evidence_reference':''} for fmt in ['CSV','JSON','XML'] for r in rows]) run={'executed_at_utc':datetime.now(timezone.utc).isoformat(),'python':platform.python_version(),'platform':platform.system()+' '+platform.machine(),'fixture_rows':12,'formats':3,'row_format_observations':36,'exact_preservation':36,'pgadmin_runs':0,'postgresql_runs':0,'unicode_normalisation':'none; exact codepoint equality','bom':False,'network_calls':0,'script_sha256':hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),'files':{f.name:{'sha256':hashlib.sha256(f.read_bytes()).hexdigest(),'bytes':len(f.read_bytes())} for f in F.iterdir()},'scope':'Own constrained Python serializer/parser pairs; not a pgAdmin or spreadsheet compatibility benchmark'} (R/'output/run.json').write_text(json.dumps(run,indent=2)+'\n');print(json.dumps(run,indent=2))