[{"data":1,"prerenderedAt":2324},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook":2288,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook":2297,"children:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook":2302,"related:\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook":2303},{"id":4,"title":5,"body":6,"dateModified":2253,"datePublished":2253,"description":2254,"extension":2255,"faq":2256,"keyword":2265,"kind":2266,"meta":2267,"navigation":247,"path":2276,"seo":2277,"slug":2280,"stem":2281,"topics":2282,"type":2286,"__hash__":2287},"docs\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook\u002Findex.md","What openpyxl Loses When You Save an Existing Workbook",{"type":7,"value":8,"toc":2241},"minimark",[9,28,182,187,214,217,939,943,951,1056,1063,1067,1070,1417,1584,1596,1600,1603,1954,1961,1965,2094,2102,2109,2115,2119,2156,2160,2166,2170,2176,2180,2186,2192,2198,2204,2208,2237],[10,11,12,13,17,18,21,22,27],"p",{},"openpyxl does not edit an .xlsx file in place. ",[14,15,16],"code",{},"load_workbook"," parses the parts of the package it understands into Python objects, and ",[14,19,20],{},"save"," writes a brand-new package from those objects. Anything the library does not model is not in the objects, so it is not in the new file. Usually nothing important is lost. Occasionally a designed dashboard comes back without its sparklines, its text boxes or its macros — and the only warning was a line on stderr nobody read. This guide, part of ",[23,24,26],"a",{"href":25},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002F","Using openpyxl for Excel File Manipulation",", lists what survives and what does not, based on an actual round trip, and shows how to scan a workbook for risk before you overwrite it.",[29,30,38,39,38,43,38,47,38,55,38,65,38,76,38,83,38,88,38,92,38,99,38,104,38,108,38,115,38,120,38,124,38,128,38,131,38,136,38,143,38,148,38,151,38,155,38,158,38,163,38,169,38,173,38,176],"svg",{"viewBox":31,"role":32,"ariaLabelledBy":33,"xmlns":36,"style":37},"4 0 752 168","img",[34,35],"lose-roundtrip-t","lose-roundtrip-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[40,41,42],"title",{"id":34},"Why an openpyxl save can drop features",[44,45,46],"desc",{"id":35},"The original xlsx package contains parts openpyxl understands and parts it does not. load_workbook builds Python objects only for the understood parts, and save writes a new package from those objects, so the unknown parts are not written back.",[48,49],"rect",{"x":50,"y":51,"width":52,"height":53,"fill":54},"4","0","752","168","#ffffff",[48,56],{"x":57,"y":58,"width":59,"height":60,"rx":61,"fill":62,"stroke":63,"style":64},"20.0","16.0","153.0","110.5","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[66,67,69],"g",{"transform":68},"translate(84.5,29.0) scale(1.000)",[70,71],"path",{"d":72,"fill":73,"stroke":74,"style":75},"M6 3h9l4 4v14H6z M15 3v4h4 M9.5 11.5l4.5 6 M14 11.5l-4.5 6","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[77,78,82],"text",{"x":79,"y":80,"style":81},"96.5","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","original.xlsx",[77,84,87],{"x":79,"y":85,"style":86},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","cells, styles, charts,",[77,89,91],{"x":79,"y":90,"style":86},"112.5","extensions, shapes",[93,94],"line",{"x1":95,"y1":96,"x2":97,"y2":96,"stroke":98,"style":64},"178.0","71.2","200.0","var(--brand,#5b5cf0)",[100,101],"polygon",{"points":102,"fill":103},"204.0,71.2 195.0,76.2 195.0,66.2","#5b5cf0",[48,105],{"x":106,"y":58,"width":59,"height":60,"rx":61,"fill":107,"stroke":98,"style":64},"209.0","#f0f4ff",[66,109,111],{"transform":110},"translate(273.5,37.8) scale(1.000)",[70,112],{"d":113,"fill":73,"stroke":114,"style":75},"M12 3c-4 0-4 1.6-4 3v2h4.2v1H6c-2 0-3 1.4-3 4s1 4 3 4h2v-2.6c0-1.6 1.3-2.9 2.9-2.9h4.2c1.3 0 2.4-1.1 2.4-2.4V6c0-1.6-1.8-3-5.5-3z M10 5.5h.01 M12 21c4 0 4-1.6 4-3v-2h-4.2v-1H18c2 0 3-1.4 3-4s-1-4-3-4h-2v2.6c0 1.6-1.3 2.9-2.9 2.9H8.9c-1.3 0-2.4 1.1-2.4 2.4V18c0 1.6 1.8 3 5.5 3z M14 18.5h.01","var(--brand-strong,#4338ca)",[77,116,16],{"x":117,"y":118,"style":119},"285.5","84.2","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[77,121,123],{"x":117,"y":122,"style":86},"103.8","keeps what it models",[93,125],{"x1":126,"y1":96,"x2":127,"y2":96,"stroke":98,"style":64},"367.0","389.0",[100,129],{"points":130,"fill":103},"393.0,71.2 384.0,76.2 384.0,66.2",[48,132],{"x":133,"y":58,"width":59,"height":60,"rx":61,"fill":134,"stroke":135,"style":64},"398.0","#fdefd8","var(--gold,#b4740a)",[66,137,139],{"transform":138},"translate(462.5,37.8) scale(1.000)",[70,140],{"d":141,"fill":73,"stroke":142,"style":75},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","var(--gold-ink,#7a4e06)",[77,144,147],{"x":145,"y":118,"style":146},"474.5","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Python objects",[77,149,150],{"x":145,"y":122,"style":86},"unknown parts gone",[93,152],{"x1":153,"y1":96,"x2":154,"y2":96,"stroke":98,"style":64},"556.0","578.0",[100,156],{"points":157,"fill":103},"582.0,71.2 573.0,76.2 573.0,66.2",[48,159],{"x":160,"y":58,"width":59,"height":60,"rx":61,"fill":161,"stroke":162,"style":64},"587.0","#fee8f2","var(--accent,#f43f8f)",[66,164,166],{"transform":165},"translate(651.5,37.8) scale(1.000)",[70,167],{"d":72,"fill":73,"stroke":168,"style":75},"var(--accent-ink,#be185d)",[77,170,20],{"x":171,"y":118,"style":172},"663.5","font-size:13.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[77,174,175],{"x":171,"y":122,"style":86},"a brand-new package",[77,177,181],{"x":178,"y":179,"style":180},"380.0","148.5","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","openpyxl never patches the file — it re-creates it from what it understood",[183,184,186],"h2",{"id":185},"prerequisites","Prerequisites",[188,189,194],"pre",{"className":190,"code":191,"language":192,"meta":193,"style":193},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl xlsxwriter\n","bash","",[14,195,196],{"__ignoreMap":193},[197,198,200,204,208,211],"span",{"class":93,"line":199},1,[197,201,203],{"class":202},"sMTad","pip",[197,205,207],{"class":206},"srMev"," install",[197,209,210],{"class":206}," openpyxl",[197,212,213],{"class":206}," xlsxwriter\n",[10,215,216],{},"To see the losses for yourself, build a workbook full of features with xlsxwriter — which writes them all — then round-trip it through openpyxl:",[188,218,222],{"className":219,"code":220,"language":221,"meta":193,"style":193},"language-python shiki shiki-themes github-light github-dark-high-contrast","import struct\nimport zlib\n\nimport xlsxwriter\n\ndef tiny_png(path: str) -> None:\n    raw = b\"\".join(b\"\\x00\" + b\"\\x43\\x38\\xca\" * 4 for _ in range(4))\n    def chunk(tag, data):\n        return struct.pack(\">I\", len(data)) + tag + data + struct.pack(\">I\", zlib.crc32(tag + data) & 0xFFFFFFFF)\n    header = struct.pack(\">IIBBBBB\", 4, 4, 8, 2, 0, 0, 0)\n    with open(path, \"wb\") as fh:\n        fh.write(b\"\\x89PNG\\r\\n\\x1a\\n\" + chunk(b\"IHDR\", header) + chunk(b\"IDAT\", zlib.compress(raw)) + chunk(b\"IEND\", b\"\"))\n\ntiny_png(\"logo.png\")\nbook = xlsxwriter.Workbook(\"dashboard.xlsx\")\nws = book.add_worksheet(\"Data\")\nws.write_row(0, 0, [\"name\", \"a\", \"b\"])\nfor i in range(1, 6):\n    ws.write_row(i, 0, [f\"r{i}\", i * 10, i * 5])\nchart = book.add_chart({\"type\": \"column\"})\nchart.add_series({\"values\": \"=Data!$B$2:$B$6\"})\nws.insert_chart(\"E2\", chart)\nws.insert_image(\"E18\", \"logo.png\")\nws.add_sparkline(\"D2\", {\"range\": \"Data!B2:C2\"})\nws.conditional_format(\"B2:B6\", {\"type\": \"data_bar\", \"bar_solid\": True})\nws.data_validation(\"A8\", {\"validate\": \"list\", \"source\": [\"x\", \"y\"]})\nws.write_comment(\"A1\", \"a note\")\nws.add_table(\"H10:I12\", {\"columns\": [{\"header\": \"k\"}, {\"header\": \"v\"}]})\nws.insert_textbox(\"K2\", \"Figures are provisional\")\nbook.close()\n","python",[14,223,224,234,242,249,256,261,287,355,367,425,470,494,557,562,573,589,605,636,662,707,730,746,758,773,795,825,863,879,917,933],{"__ignoreMap":193},[197,225,226,230],{"class":93,"line":199},[197,227,229],{"class":228},"s-kum","import",[197,231,233],{"class":232},"skGVy"," struct\n",[197,235,237,239],{"class":93,"line":236},2,[197,238,229],{"class":228},[197,240,241],{"class":232}," zlib\n",[197,243,245],{"class":93,"line":244},3,[197,246,248],{"emptyLinePlaceholder":247},true,"\n",[197,250,252,254],{"class":93,"line":251},4,[197,253,229],{"class":228},[197,255,213],{"class":232},[197,257,259],{"class":93,"line":258},5,[197,260,248],{"emptyLinePlaceholder":247},[197,262,264,267,271,274,278,281,284],{"class":93,"line":263},6,[197,265,266],{"class":228},"def",[197,268,270],{"class":269},"s_Opv"," tiny_png",[197,272,273],{"class":232},"(path: ",[197,275,277],{"class":276},"sP0c6","str",[197,279,280],{"class":232},") -> ",[197,282,283],{"class":276},"None",[197,285,286],{"class":232},":\n",[197,288,290,293,296,299,302,305,308,311,315,317,320,322,324,327,329,332,335,338,341,344,347,350,352],{"class":93,"line":289},7,[197,291,292],{"class":232},"    raw ",[197,294,295],{"class":228},"=",[197,297,298],{"class":228}," b",[197,300,301],{"class":206},"\"\"",[197,303,304],{"class":232},".join(",[197,306,307],{"class":228},"b",[197,309,310],{"class":206},"\"",[197,312,314],{"class":313},"sSjpA","\\x00",[197,316,310],{"class":206},[197,318,319],{"class":228}," +",[197,321,298],{"class":228},[197,323,310],{"class":206},[197,325,326],{"class":313},"\\x43\\x38\\xca",[197,328,310],{"class":206},[197,330,331],{"class":228}," *",[197,333,334],{"class":276}," 4",[197,336,337],{"class":228}," for",[197,339,340],{"class":232}," _ ",[197,342,343],{"class":228},"in",[197,345,346],{"class":276}," range",[197,348,349],{"class":232},"(",[197,351,50],{"class":276},[197,353,354],{"class":232},"))\n",[197,356,358,361,364],{"class":93,"line":357},8,[197,359,360],{"class":228},"    def",[197,362,363],{"class":269}," chunk",[197,365,366],{"class":232},"(tag, data):\n",[197,368,370,373,376,379,382,385,388,391,394,396,399,401,403,405,408,410,413,416,419,422],{"class":93,"line":369},9,[197,371,372],{"class":228},"        return",[197,374,375],{"class":232}," struct.pack(",[197,377,378],{"class":206},"\">I\"",[197,380,381],{"class":232},", ",[197,383,384],{"class":276},"len",[197,386,387],{"class":232},"(data)) ",[197,389,390],{"class":228},"+",[197,392,393],{"class":232}," tag ",[197,395,390],{"class":228},[197,397,398],{"class":232}," data ",[197,400,390],{"class":228},[197,402,375],{"class":232},[197,404,378],{"class":206},[197,406,407],{"class":232},", zlib.crc32(tag ",[197,409,390],{"class":228},[197,411,412],{"class":232}," data) ",[197,414,415],{"class":228},"&",[197,417,418],{"class":228}," 0x",[197,420,421],{"class":276},"FFFFFFFF",[197,423,424],{"class":232},")\n",[197,426,428,431,433,435,438,440,442,444,446,448,451,453,456,458,460,462,464,466,468],{"class":93,"line":427},10,[197,429,430],{"class":232},"    header ",[197,432,295],{"class":228},[197,434,375],{"class":232},[197,436,437],{"class":206},"\">IIBBBBB\"",[197,439,381],{"class":232},[197,441,50],{"class":276},[197,443,381],{"class":232},[197,445,50],{"class":276},[197,447,381],{"class":232},[197,449,450],{"class":276},"8",[197,452,381],{"class":232},[197,454,455],{"class":276},"2",[197,457,381],{"class":232},[197,459,51],{"class":276},[197,461,381],{"class":232},[197,463,51],{"class":276},[197,465,381],{"class":232},[197,467,51],{"class":276},[197,469,424],{"class":232},[197,471,473,476,479,482,485,488,491],{"class":93,"line":472},11,[197,474,475],{"class":228},"    with",[197,477,478],{"class":276}," open",[197,480,481],{"class":232},"(path, ",[197,483,484],{"class":206},"\"wb\"",[197,486,487],{"class":232},") ",[197,489,490],{"class":228},"as",[197,492,493],{"class":232}," fh:\n",[197,495,497,500,502,504,507,510,513,515,517,520,522,525,528,530,532,534,537,540,542,544,546,549,551,553,555],{"class":93,"line":496},12,[197,498,499],{"class":232},"        fh.write(",[197,501,307],{"class":228},[197,503,310],{"class":206},[197,505,506],{"class":313},"\\x89",[197,508,509],{"class":206},"PNG",[197,511,512],{"class":313},"\\r\\n\\x1a\\n",[197,514,310],{"class":206},[197,516,319],{"class":228},[197,518,519],{"class":232}," chunk(",[197,521,307],{"class":228},[197,523,524],{"class":206},"\"IHDR\"",[197,526,527],{"class":232},", header) ",[197,529,390],{"class":228},[197,531,519],{"class":232},[197,533,307],{"class":228},[197,535,536],{"class":206},"\"IDAT\"",[197,538,539],{"class":232},", zlib.compress(raw)) ",[197,541,390],{"class":228},[197,543,519],{"class":232},[197,545,307],{"class":228},[197,547,548],{"class":206},"\"IEND\"",[197,550,381],{"class":232},[197,552,307],{"class":228},[197,554,301],{"class":206},[197,556,354],{"class":232},[197,558,560],{"class":93,"line":559},13,[197,561,248],{"emptyLinePlaceholder":247},[197,563,565,568,571],{"class":93,"line":564},14,[197,566,567],{"class":232},"tiny_png(",[197,569,570],{"class":206},"\"logo.png\"",[197,572,424],{"class":232},[197,574,576,579,581,584,587],{"class":93,"line":575},15,[197,577,578],{"class":232},"book ",[197,580,295],{"class":228},[197,582,583],{"class":232}," xlsxwriter.Workbook(",[197,585,586],{"class":206},"\"dashboard.xlsx\"",[197,588,424],{"class":232},[197,590,592,595,597,600,603],{"class":93,"line":591},16,[197,593,594],{"class":232},"ws ",[197,596,295],{"class":228},[197,598,599],{"class":232}," book.add_worksheet(",[197,601,602],{"class":206},"\"Data\"",[197,604,424],{"class":232},[197,606,608,611,613,615,617,620,623,625,628,630,633],{"class":93,"line":607},17,[197,609,610],{"class":232},"ws.write_row(",[197,612,51],{"class":276},[197,614,381],{"class":232},[197,616,51],{"class":276},[197,618,619],{"class":232},", [",[197,621,622],{"class":206},"\"name\"",[197,624,381],{"class":232},[197,626,627],{"class":206},"\"a\"",[197,629,381],{"class":232},[197,631,632],{"class":206},"\"b\"",[197,634,635],{"class":232},"])\n",[197,637,639,642,645,647,649,651,654,656,659],{"class":93,"line":638},18,[197,640,641],{"class":228},"for",[197,643,644],{"class":232}," i ",[197,646,343],{"class":228},[197,648,346],{"class":276},[197,650,349],{"class":232},[197,652,653],{"class":276},"1",[197,655,381],{"class":232},[197,657,658],{"class":276},"6",[197,660,661],{"class":232},"):\n",[197,663,665,668,670,672,675,678,681,684,687,689,692,695,698,700,702,705],{"class":93,"line":664},19,[197,666,667],{"class":232},"    ws.write_row(i, ",[197,669,51],{"class":276},[197,671,619],{"class":232},[197,673,674],{"class":228},"f",[197,676,677],{"class":206},"\"r",[197,679,680],{"class":313},"{",[197,682,683],{"class":232},"i",[197,685,686],{"class":313},"}",[197,688,310],{"class":206},[197,690,691],{"class":232},", i ",[197,693,694],{"class":228},"*",[197,696,697],{"class":276}," 10",[197,699,691],{"class":232},[197,701,694],{"class":228},[197,703,704],{"class":276}," 5",[197,706,635],{"class":232},[197,708,710,713,715,718,721,724,727],{"class":93,"line":709},20,[197,711,712],{"class":232},"chart ",[197,714,295],{"class":228},[197,716,717],{"class":232}," book.add_chart({",[197,719,720],{"class":206},"\"type\"",[197,722,723],{"class":232},": ",[197,725,726],{"class":206},"\"column\"",[197,728,729],{"class":232},"})\n",[197,731,733,736,739,741,744],{"class":93,"line":732},21,[197,734,735],{"class":232},"chart.add_series({",[197,737,738],{"class":206},"\"values\"",[197,740,723],{"class":232},[197,742,743],{"class":206},"\"=Data!$B$2:$B$6\"",[197,745,729],{"class":232},[197,747,749,752,755],{"class":93,"line":748},22,[197,750,751],{"class":232},"ws.insert_chart(",[197,753,754],{"class":206},"\"E2\"",[197,756,757],{"class":232},", chart)\n",[197,759,761,764,767,769,771],{"class":93,"line":760},23,[197,762,763],{"class":232},"ws.insert_image(",[197,765,766],{"class":206},"\"E18\"",[197,768,381],{"class":232},[197,770,570],{"class":206},[197,772,424],{"class":232},[197,774,776,779,782,785,788,790,793],{"class":93,"line":775},24,[197,777,778],{"class":232},"ws.add_sparkline(",[197,780,781],{"class":206},"\"D2\"",[197,783,784],{"class":232},", {",[197,786,787],{"class":206},"\"range\"",[197,789,723],{"class":232},[197,791,792],{"class":206},"\"Data!B2:C2\"",[197,794,729],{"class":232},[197,796,798,801,804,806,808,810,813,815,818,820,823],{"class":93,"line":797},25,[197,799,800],{"class":232},"ws.conditional_format(",[197,802,803],{"class":206},"\"B2:B6\"",[197,805,784],{"class":232},[197,807,720],{"class":206},[197,809,723],{"class":232},[197,811,812],{"class":206},"\"data_bar\"",[197,814,381],{"class":232},[197,816,817],{"class":206},"\"bar_solid\"",[197,819,723],{"class":232},[197,821,822],{"class":276},"True",[197,824,729],{"class":232},[197,826,828,831,834,836,839,841,844,846,849,852,855,857,860],{"class":93,"line":827},26,[197,829,830],{"class":232},"ws.data_validation(",[197,832,833],{"class":206},"\"A8\"",[197,835,784],{"class":232},[197,837,838],{"class":206},"\"validate\"",[197,840,723],{"class":232},[197,842,843],{"class":206},"\"list\"",[197,845,381],{"class":232},[197,847,848],{"class":206},"\"source\"",[197,850,851],{"class":232},": [",[197,853,854],{"class":206},"\"x\"",[197,856,381],{"class":232},[197,858,859],{"class":206},"\"y\"",[197,861,862],{"class":232},"]})\n",[197,864,866,869,872,874,877],{"class":93,"line":865},27,[197,867,868],{"class":232},"ws.write_comment(",[197,870,871],{"class":206},"\"A1\"",[197,873,381],{"class":232},[197,875,876],{"class":206},"\"a note\"",[197,878,424],{"class":232},[197,880,882,885,888,890,893,896,899,901,904,907,909,911,914],{"class":93,"line":881},28,[197,883,884],{"class":232},"ws.add_table(",[197,886,887],{"class":206},"\"H10:I12\"",[197,889,784],{"class":232},[197,891,892],{"class":206},"\"columns\"",[197,894,895],{"class":232},": [{",[197,897,898],{"class":206},"\"header\"",[197,900,723],{"class":232},[197,902,903],{"class":206},"\"k\"",[197,905,906],{"class":232},"}, {",[197,908,898],{"class":206},[197,910,723],{"class":232},[197,912,913],{"class":206},"\"v\"",[197,915,916],{"class":232},"}]})\n",[197,918,920,923,926,928,931],{"class":93,"line":919},29,[197,921,922],{"class":232},"ws.insert_textbox(",[197,924,925],{"class":206},"\"K2\"",[197,927,381],{"class":232},[197,929,930],{"class":206},"\"Figures are provisional\"",[197,932,424],{"class":232},[197,934,936],{"class":93,"line":935},30,[197,937,938],{"class":232},"book.close()\n",[183,940,942],{"id":941},"step-1-round-trip-and-watch-the-warnings","Step 1 — Round-trip and watch the warnings",[10,944,945,946,950],{},"openpyxl's only notice that it is about to drop something is a Python warning at ",[947,948,949],"em",{},"load"," time. Capture them — in a scheduled job they would otherwise vanish into a log nobody reads:",[188,952,954],{"className":219,"code":953,"language":221,"meta":193,"style":193},"import warnings\nfrom openpyxl import load_workbook\n\nwith warnings.catch_warnings(record=True) as caught:\n    warnings.simplefilter(\"always\")\n    wb = load_workbook(\"dashboard.xlsx\")\nfor w in caught:\n    print(w.message)\nwb.save(\"dashboard_resaved.xlsx\")\n",[14,955,956,963,976,980,1003,1013,1027,1038,1046],{"__ignoreMap":193},[197,957,958,960],{"class":93,"line":199},[197,959,229],{"class":228},[197,961,962],{"class":232}," warnings\n",[197,964,965,968,971,973],{"class":93,"line":236},[197,966,967],{"class":228},"from",[197,969,970],{"class":232}," openpyxl ",[197,972,229],{"class":228},[197,974,975],{"class":232}," load_workbook\n",[197,977,978],{"class":93,"line":244},[197,979,248],{"emptyLinePlaceholder":247},[197,981,982,985,988,992,994,996,998,1000],{"class":93,"line":251},[197,983,984],{"class":228},"with",[197,986,987],{"class":232}," warnings.catch_warnings(",[197,989,991],{"class":990},"sa561","record",[197,993,295],{"class":228},[197,995,822],{"class":276},[197,997,487],{"class":232},[197,999,490],{"class":228},[197,1001,1002],{"class":232}," caught:\n",[197,1004,1005,1008,1011],{"class":93,"line":258},[197,1006,1007],{"class":232},"    warnings.simplefilter(",[197,1009,1010],{"class":206},"\"always\"",[197,1012,424],{"class":232},[197,1014,1015,1018,1020,1023,1025],{"class":93,"line":263},[197,1016,1017],{"class":232},"    wb ",[197,1019,295],{"class":228},[197,1021,1022],{"class":232}," load_workbook(",[197,1024,586],{"class":206},[197,1026,424],{"class":232},[197,1028,1029,1031,1034,1036],{"class":93,"line":289},[197,1030,641],{"class":228},[197,1032,1033],{"class":232}," w ",[197,1035,343],{"class":228},[197,1037,1002],{"class":232},[197,1039,1040,1043],{"class":93,"line":357},[197,1041,1042],{"class":276},"    print",[197,1044,1045],{"class":232},"(w.message)\n",[197,1047,1048,1051,1054],{"class":93,"line":369},[197,1049,1050],{"class":232},"wb.save(",[197,1052,1053],{"class":206},"\"dashboard_resaved.xlsx\"",[197,1055,424],{"class":232},[10,1057,1058,1059,1062],{},"For this workbook openpyxl reports three: an unknown extension, a conditional formatting extension (the solid-fill option on the data bar) and a sparkline group extension. Each of those is a feature that will be missing from ",[14,1060,1061],{},"dashboard_resaved.xlsx",".",[183,1064,1066],{"id":1065},"step-2-compare-what-went-in-with-what-came-out","Step 2 — Compare what went in with what came out",[10,1068,1069],{},"Warnings cover extensions, not everything. The objective check is to compare the two packages: an .xlsx file is a zip archive, so list its parts and look inside the worksheet and drawing XML for the features you care about.",[188,1071,1073],{"className":219,"code":1072,"language":221,"meta":193,"style":193},"import zipfile\n\ndef features(path: str) -> dict:\n    z = zipfile.ZipFile(path)\n    sheet = z.read(\"xl\u002Fworksheets\u002Fsheet1.xml\").decode()\n    drawings = \"\".join(z.read(n).decode() for n in z.namelist() if n.startswith(\"xl\u002Fdrawings\u002Fdrawing\"))\n    return {\n        \"chart\": any(n.startswith(\"xl\u002Fcharts\u002F\") for n in z.namelist()),\n        \"image\": any(n.startswith(\"xl\u002Fmedia\u002F\") for n in z.namelist()),\n        \"sparkline\": \"sparklineGroup\" in sheet,\n        \"data bar options\": \"x14:dataBar\" in sheet,\n        \"text box\": \"\u003Cxdr:sp\" in drawings,\n        \"table\": any(n.startswith(\"xl\u002Ftables\u002F\") for n in z.namelist()),\n        \"comment\": any(\"comment\" in n for n in z.namelist()),\n        \"validation\": \"dataValidation\" in sheet,\n    }\n\nbefore, after = features(\"dashboard.xlsx\"), features(\"dashboard_resaved.xlsx\")\nfor name in before:\n    print(f\"{name:18} {before[name]!s:6} -> {after[name]}\")\n",[14,1074,1075,1082,1086,1104,1114,1130,1164,1172,1199,1223,1239,1253,1268,1292,1318,1332,1337,1341,1360,1372],{"__ignoreMap":193},[197,1076,1077,1079],{"class":93,"line":199},[197,1078,229],{"class":228},[197,1080,1081],{"class":232}," zipfile\n",[197,1083,1084],{"class":93,"line":236},[197,1085,248],{"emptyLinePlaceholder":247},[197,1087,1088,1090,1093,1095,1097,1099,1102],{"class":93,"line":244},[197,1089,266],{"class":228},[197,1091,1092],{"class":269}," features",[197,1094,273],{"class":232},[197,1096,277],{"class":276},[197,1098,280],{"class":232},[197,1100,1101],{"class":276},"dict",[197,1103,286],{"class":232},[197,1105,1106,1109,1111],{"class":93,"line":251},[197,1107,1108],{"class":232},"    z ",[197,1110,295],{"class":228},[197,1112,1113],{"class":232}," zipfile.ZipFile(path)\n",[197,1115,1116,1119,1121,1124,1127],{"class":93,"line":258},[197,1117,1118],{"class":232},"    sheet ",[197,1120,295],{"class":228},[197,1122,1123],{"class":232}," z.read(",[197,1125,1126],{"class":206},"\"xl\u002Fworksheets\u002Fsheet1.xml\"",[197,1128,1129],{"class":232},").decode()\n",[197,1131,1132,1135,1137,1140,1143,1145,1148,1150,1153,1156,1159,1162],{"class":93,"line":263},[197,1133,1134],{"class":232},"    drawings ",[197,1136,295],{"class":228},[197,1138,1139],{"class":206}," \"\"",[197,1141,1142],{"class":232},".join(z.read(n).decode() ",[197,1144,641],{"class":228},[197,1146,1147],{"class":232}," n ",[197,1149,343],{"class":228},[197,1151,1152],{"class":232}," z.namelist() ",[197,1154,1155],{"class":228},"if",[197,1157,1158],{"class":232}," n.startswith(",[197,1160,1161],{"class":206},"\"xl\u002Fdrawings\u002Fdrawing\"",[197,1163,354],{"class":232},[197,1165,1166,1169],{"class":93,"line":289},[197,1167,1168],{"class":228},"    return",[197,1170,1171],{"class":232}," {\n",[197,1173,1174,1177,1179,1182,1185,1188,1190,1192,1194,1196],{"class":93,"line":357},[197,1175,1176],{"class":206},"        \"chart\"",[197,1178,723],{"class":232},[197,1180,1181],{"class":276},"any",[197,1183,1184],{"class":232},"(n.startswith(",[197,1186,1187],{"class":206},"\"xl\u002Fcharts\u002F\"",[197,1189,487],{"class":232},[197,1191,641],{"class":228},[197,1193,1147],{"class":232},[197,1195,343],{"class":228},[197,1197,1198],{"class":232}," z.namelist()),\n",[197,1200,1201,1204,1206,1208,1210,1213,1215,1217,1219,1221],{"class":93,"line":369},[197,1202,1203],{"class":206},"        \"image\"",[197,1205,723],{"class":232},[197,1207,1181],{"class":276},[197,1209,1184],{"class":232},[197,1211,1212],{"class":206},"\"xl\u002Fmedia\u002F\"",[197,1214,487],{"class":232},[197,1216,641],{"class":228},[197,1218,1147],{"class":232},[197,1220,343],{"class":228},[197,1222,1198],{"class":232},[197,1224,1225,1228,1230,1233,1236],{"class":93,"line":427},[197,1226,1227],{"class":206},"        \"sparkline\"",[197,1229,723],{"class":232},[197,1231,1232],{"class":206},"\"sparklineGroup\"",[197,1234,1235],{"class":228}," in",[197,1237,1238],{"class":232}," sheet,\n",[197,1240,1241,1244,1246,1249,1251],{"class":93,"line":472},[197,1242,1243],{"class":206},"        \"data bar options\"",[197,1245,723],{"class":232},[197,1247,1248],{"class":206},"\"x14:dataBar\"",[197,1250,1235],{"class":228},[197,1252,1238],{"class":232},[197,1254,1255,1258,1260,1263,1265],{"class":93,"line":496},[197,1256,1257],{"class":206},"        \"text box\"",[197,1259,723],{"class":232},[197,1261,1262],{"class":206},"\"\u003Cxdr:sp\"",[197,1264,1235],{"class":228},[197,1266,1267],{"class":232}," drawings,\n",[197,1269,1270,1273,1275,1277,1279,1282,1284,1286,1288,1290],{"class":93,"line":559},[197,1271,1272],{"class":206},"        \"table\"",[197,1274,723],{"class":232},[197,1276,1181],{"class":276},[197,1278,1184],{"class":232},[197,1280,1281],{"class":206},"\"xl\u002Ftables\u002F\"",[197,1283,487],{"class":232},[197,1285,641],{"class":228},[197,1287,1147],{"class":232},[197,1289,343],{"class":228},[197,1291,1198],{"class":232},[197,1293,1294,1297,1299,1301,1303,1306,1308,1310,1312,1314,1316],{"class":93,"line":564},[197,1295,1296],{"class":206},"        \"comment\"",[197,1298,723],{"class":232},[197,1300,1181],{"class":276},[197,1302,349],{"class":232},[197,1304,1305],{"class":206},"\"comment\"",[197,1307,1235],{"class":228},[197,1309,1147],{"class":232},[197,1311,641],{"class":228},[197,1313,1147],{"class":232},[197,1315,343],{"class":228},[197,1317,1198],{"class":232},[197,1319,1320,1323,1325,1328,1330],{"class":93,"line":575},[197,1321,1322],{"class":206},"        \"validation\"",[197,1324,723],{"class":232},[197,1326,1327],{"class":206},"\"dataValidation\"",[197,1329,1235],{"class":228},[197,1331,1238],{"class":232},[197,1333,1334],{"class":93,"line":591},[197,1335,1336],{"class":232},"    }\n",[197,1338,1339],{"class":93,"line":607},[197,1340,248],{"emptyLinePlaceholder":247},[197,1342,1343,1346,1348,1351,1353,1356,1358],{"class":93,"line":638},[197,1344,1345],{"class":232},"before, after ",[197,1347,295],{"class":228},[197,1349,1350],{"class":232}," features(",[197,1352,586],{"class":206},[197,1354,1355],{"class":232},"), features(",[197,1357,1053],{"class":206},[197,1359,424],{"class":232},[197,1361,1362,1364,1367,1369],{"class":93,"line":664},[197,1363,641],{"class":228},[197,1365,1366],{"class":232}," name ",[197,1368,343],{"class":228},[197,1370,1371],{"class":232}," before:\n",[197,1373,1374,1376,1378,1380,1382,1384,1387,1390,1392,1395,1398,1401,1403,1406,1408,1411,1413,1415],{"class":93,"line":709},[197,1375,1042],{"class":276},[197,1377,349],{"class":232},[197,1379,674],{"class":228},[197,1381,310],{"class":206},[197,1383,680],{"class":313},[197,1385,1386],{"class":232},"name",[197,1388,1389],{"class":228},":18",[197,1391,686],{"class":313},[197,1393,1394],{"class":313}," {",[197,1396,1397],{"class":232},"before[name]",[197,1399,1400],{"class":228},"!s:6",[197,1402,686],{"class":313},[197,1404,1405],{"class":206}," -> ",[197,1407,680],{"class":313},[197,1409,1410],{"class":232},"after[name]",[197,1412,686],{"class":313},[197,1414,310],{"class":206},[197,1416,424],{"class":232},[29,1418,38,1423,38,1426,38,1429,38,1432,38,1437,38,1442,38,1448,38,1452,38,1458,38,1460,38,1468,38,1471,38,1475,38,1477,38,1484,38,1487,38,1491,38,1493,38,1498,38,1501,38,1505,38,1507,38,1512,38,1515,38,1519,38,1521,38,1526,38,1529,38,1533,38,1535,38,1542,38,1545,38,1549,38,1551,38,1556,38,1559,38,1563,38,1565,38,1570,38,1573,38,1577,38,1579],{"viewBox":1419,"role":32,"ariaLabelledBy":1420,"xmlns":36,"style":37},"4 0 752 378",[1421,1422],"lose-results-t","lose-results-d",[40,1424,1425],{"id":1421},"What survived an openpyxl load and save in this test",[44,1427,1428],{"id":1422},"Charts, images, Excel tables, comments, data validation, hyperlinks, frozen panes and column widths survived. Sparklines, data bar extension options such as solid fills, and text box shapes were lost. Charts were rewritten, so advanced chart formatting can be simplified.",[48,1430],{"x":50,"y":51,"width":52,"height":1431,"fill":54},"378",[48,1433],{"x":57,"y":58,"width":1434,"height":1435,"rx":51,"fill":54,"stroke":63,"style":1436},"224.9","40.0","stroke-width:1px",[48,1438],{"x":1439,"y":58,"width":1440,"height":1435,"rx":51,"fill":107,"stroke":98,"style":1441},"244.9","495.1","stroke-width:1.2px",[77,1443,1447],{"x":1444,"y":1445,"style":1446},"492.4","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Survives the round trip",[48,1449],{"x":57,"y":1450,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"56.0","34.0",[77,1453,1457],{"x":1454,"y":1455,"style":1456},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Cell values and styles",[48,1459],{"x":1439,"y":1450,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1461,1463],{"transform":1462},"translate(483.4,64.0) scale(0.750)",[70,1464],{"d":1465,"fill":73,"stroke":1466,"style":1467},"M5 12.5l4.5 4.5L19 7","var(--teal,#0f9488)","stroke-width:2.40px;stroke-linecap:round;stroke-linejoin:round",[48,1469],{"x":57,"y":1470,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"90.0",[77,1472,1474],{"x":1454,"y":1473,"style":1456},"112.0","Charts (rewritten by openpyxl)",[48,1476],{"x":1439,"y":1470,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1478,1480],{"transform":1479},"translate(483.9,98.5) scale(0.708)",[70,1481],{"d":1482,"fill":73,"stroke":135,"style":1483},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","stroke-width:2.54px;stroke-linecap:round;stroke-linejoin:round",[48,1485],{"x":57,"y":1486,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"124.0",[77,1488,1490],{"x":1454,"y":1489,"style":1456},"146.0","Images",[48,1492],{"x":1439,"y":1486,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1494,1496],{"transform":1495},"translate(483.4,132.0) scale(0.750)",[70,1497],{"d":1465,"fill":73,"stroke":1466,"style":1467},[48,1499],{"x":57,"y":1500,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"158.0",[77,1502,1504],{"x":1454,"y":1503,"style":1456},"180.0","Excel tables",[48,1506],{"x":1439,"y":1500,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1508,1510],{"transform":1509},"translate(483.4,166.0) scale(0.750)",[70,1511],{"d":1465,"fill":73,"stroke":1466,"style":1467},[48,1513],{"x":57,"y":1514,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"192.0",[77,1516,1518],{"x":1454,"y":1517,"style":1456},"214.0","Comments and data validation",[48,1520],{"x":1439,"y":1514,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1522,1524],{"transform":1523},"translate(483.4,200.0) scale(0.750)",[70,1525],{"d":1465,"fill":73,"stroke":1466,"style":1467},[48,1527],{"x":57,"y":1528,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"226.0",[77,1530,1532],{"x":1454,"y":1531,"style":1456},"248.0","Sparklines",[48,1534],{"x":1439,"y":1528,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1536,1538],{"transform":1537},"translate(484.4,235.0) scale(0.667)",[70,1539],{"d":1540,"fill":73,"stroke":162,"style":1541},"M6 6l12 12 M18 6L6 18","stroke-width:2.70px;stroke-linecap:round;stroke-linejoin:round",[48,1543],{"x":57,"y":1544,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"260.0",[77,1546,1548],{"x":1454,"y":1547,"style":1456},"282.0","Data bar extension options",[48,1550],{"x":1439,"y":1544,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1552,1554],{"transform":1553},"translate(484.4,269.0) scale(0.667)",[70,1555],{"d":1540,"fill":73,"stroke":162,"style":1541},[48,1557],{"x":57,"y":1558,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"294.0",[77,1560,1562],{"x":1454,"y":1561,"style":1456},"316.0","Text boxes and shapes",[48,1564],{"x":1439,"y":1558,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1566,1568],{"transform":1567},"translate(484.4,303.0) scale(0.667)",[70,1569],{"d":1540,"fill":73,"stroke":162,"style":1541},[48,1571],{"x":57,"y":1572,"width":1434,"height":1451,"rx":51,"fill":62,"stroke":63,"style":1436},"328.0",[77,1574,1576],{"x":1454,"y":1575,"style":1456},"350.0","VBA without keep_vba",[48,1578],{"x":1439,"y":1572,"width":1440,"height":1451,"rx":51,"fill":54,"stroke":63,"style":1436},[66,1580,1582],{"transform":1581},"translate(484.4,337.0) scale(0.667)",[70,1583],{"d":1540,"fill":73,"stroke":162,"style":1541},[10,1585,1586,1587,1591,1592,1595],{},"The chart survives, but openpyxl rebuilt it from its own chart model — the chart part shrank from 1,624 to 1,372 bytes in this test — so styling it does not model, such as some custom colours or label layouts on a chart drawn in Excel, can be simplified. Treat \"partial\" as \"check it by eye\". Macros are covered separately in ",[23,1588,1590],{"href":1589},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fwork-with-macro-enabled-xlsm-files-in-openpyxl\u002F","Work with Macro-Enabled .xlsm Files in openpyxl",": without ",[14,1593,1594],{},"keep_vba=True"," the VBA project is discarded. Slicers, timelines, form controls and Power Pivot data models belong to the same category as shapes — features openpyxl does not model — and should be assumed lost.",[183,1597,1599],{"id":1598},"step-3-scan-a-workbook-before-you-overwrite-it","Step 3 — Scan a workbook before you overwrite it",[10,1601,1602],{},"Knowing the list is useful; enforcing it is better. Before a job saves over a workbook it did not create, scan the package for parts and markup openpyxl is known to drop, and refuse to overwrite if any are present:",[188,1604,1606],{"className":219,"code":1605,"language":221,"meta":193,"style":193},"RISKY_MARKERS = {\n    \"sparklines\": (\"xl\u002Fworksheets\u002F\", \"sparklineGroup\"),\n    \"extension formats\": (\"xl\u002Fworksheets\u002F\", \"\u003Cx14:\"),\n    \"shapes or text boxes\": (\"xl\u002Fdrawings\u002F\", \"\u003Cxdr:sp\"),\n}\nRISKY_PARTS = {\n    \"VBA project\": \"xl\u002FvbaProject.bin\",\n    \"slicers\": \"xl\u002Fslicers\u002F\",\n    \"timelines\": \"xl\u002Ftimelines\u002F\",\n    \"data model\": \"xl\u002Fmodel\u002F\",\n    \"form controls\": \"xl\u002FctrlProps\u002F\",\n}\n\ndef at_risk(path: str) -> list[str]:\n    found = []\n    with zipfile.ZipFile(path) as z:\n        names = z.namelist()\n        for label, prefix in RISKY_PARTS.items():\n            if any(n.startswith(prefix) for n in names):\n                found.append(label)\n        for label, (folder, marker) in RISKY_MARKERS.items():\n            if any(n.startswith(folder) and n.endswith(\".xml\") and marker in z.read(n).decode(\"utf-8\", \"ignore\")\n                   for n in names):\n                found.append(label)\n    return found\n\nprint(at_risk(\"dashboard.xlsx\"))            # ['sparklines', 'extension formats', 'shapes or text boxes']\nprint(at_risk(\"dashboard_resaved.xlsx\"))    # []\n",[14,1607,1608,1618,1636,1652,1668,1673,1682,1695,1707,1719,1731,1743,1747,1751,1770,1780,1792,1802,1818,1838,1843,1857,1897,1908,1912,1919,1923,1940],{"__ignoreMap":193},[197,1609,1610,1613,1616],{"class":93,"line":199},[197,1611,1612],{"class":276},"RISKY_MARKERS",[197,1614,1615],{"class":228}," =",[197,1617,1171],{"class":232},[197,1619,1620,1623,1626,1629,1631,1633],{"class":93,"line":236},[197,1621,1622],{"class":206},"    \"sparklines\"",[197,1624,1625],{"class":232},": (",[197,1627,1628],{"class":206},"\"xl\u002Fworksheets\u002F\"",[197,1630,381],{"class":232},[197,1632,1232],{"class":206},[197,1634,1635],{"class":232},"),\n",[197,1637,1638,1641,1643,1645,1647,1650],{"class":93,"line":244},[197,1639,1640],{"class":206},"    \"extension formats\"",[197,1642,1625],{"class":232},[197,1644,1628],{"class":206},[197,1646,381],{"class":232},[197,1648,1649],{"class":206},"\"\u003Cx14:\"",[197,1651,1635],{"class":232},[197,1653,1654,1657,1659,1662,1664,1666],{"class":93,"line":251},[197,1655,1656],{"class":206},"    \"shapes or text boxes\"",[197,1658,1625],{"class":232},[197,1660,1661],{"class":206},"\"xl\u002Fdrawings\u002F\"",[197,1663,381],{"class":232},[197,1665,1262],{"class":206},[197,1667,1635],{"class":232},[197,1669,1670],{"class":93,"line":258},[197,1671,1672],{"class":232},"}\n",[197,1674,1675,1678,1680],{"class":93,"line":263},[197,1676,1677],{"class":276},"RISKY_PARTS",[197,1679,1615],{"class":228},[197,1681,1171],{"class":232},[197,1683,1684,1687,1689,1692],{"class":93,"line":289},[197,1685,1686],{"class":206},"    \"VBA project\"",[197,1688,723],{"class":232},[197,1690,1691],{"class":206},"\"xl\u002FvbaProject.bin\"",[197,1693,1694],{"class":232},",\n",[197,1696,1697,1700,1702,1705],{"class":93,"line":357},[197,1698,1699],{"class":206},"    \"slicers\"",[197,1701,723],{"class":232},[197,1703,1704],{"class":206},"\"xl\u002Fslicers\u002F\"",[197,1706,1694],{"class":232},[197,1708,1709,1712,1714,1717],{"class":93,"line":369},[197,1710,1711],{"class":206},"    \"timelines\"",[197,1713,723],{"class":232},[197,1715,1716],{"class":206},"\"xl\u002Ftimelines\u002F\"",[197,1718,1694],{"class":232},[197,1720,1721,1724,1726,1729],{"class":93,"line":427},[197,1722,1723],{"class":206},"    \"data model\"",[197,1725,723],{"class":232},[197,1727,1728],{"class":206},"\"xl\u002Fmodel\u002F\"",[197,1730,1694],{"class":232},[197,1732,1733,1736,1738,1741],{"class":93,"line":472},[197,1734,1735],{"class":206},"    \"form controls\"",[197,1737,723],{"class":232},[197,1739,1740],{"class":206},"\"xl\u002FctrlProps\u002F\"",[197,1742,1694],{"class":232},[197,1744,1745],{"class":93,"line":496},[197,1746,1672],{"class":232},[197,1748,1749],{"class":93,"line":559},[197,1750,248],{"emptyLinePlaceholder":247},[197,1752,1753,1755,1758,1760,1762,1765,1767],{"class":93,"line":564},[197,1754,266],{"class":228},[197,1756,1757],{"class":269}," at_risk",[197,1759,273],{"class":232},[197,1761,277],{"class":276},[197,1763,1764],{"class":232},") -> list[",[197,1766,277],{"class":276},[197,1768,1769],{"class":232},"]:\n",[197,1771,1772,1775,1777],{"class":93,"line":575},[197,1773,1774],{"class":232},"    found ",[197,1776,295],{"class":228},[197,1778,1779],{"class":232}," []\n",[197,1781,1782,1784,1787,1789],{"class":93,"line":591},[197,1783,475],{"class":228},[197,1785,1786],{"class":232}," zipfile.ZipFile(path) ",[197,1788,490],{"class":228},[197,1790,1791],{"class":232}," z:\n",[197,1793,1794,1797,1799],{"class":93,"line":607},[197,1795,1796],{"class":232},"        names ",[197,1798,295],{"class":228},[197,1800,1801],{"class":232}," z.namelist()\n",[197,1803,1804,1807,1810,1812,1815],{"class":93,"line":638},[197,1805,1806],{"class":228},"        for",[197,1808,1809],{"class":232}," label, prefix ",[197,1811,343],{"class":228},[197,1813,1814],{"class":276}," RISKY_PARTS",[197,1816,1817],{"class":232},".items():\n",[197,1819,1820,1823,1826,1829,1831,1833,1835],{"class":93,"line":664},[197,1821,1822],{"class":228},"            if",[197,1824,1825],{"class":276}," any",[197,1827,1828],{"class":232},"(n.startswith(prefix) ",[197,1830,641],{"class":228},[197,1832,1147],{"class":232},[197,1834,343],{"class":228},[197,1836,1837],{"class":232}," names):\n",[197,1839,1840],{"class":93,"line":709},[197,1841,1842],{"class":232},"                found.append(label)\n",[197,1844,1845,1847,1850,1852,1855],{"class":93,"line":732},[197,1846,1806],{"class":228},[197,1848,1849],{"class":232}," label, (folder, marker) ",[197,1851,343],{"class":228},[197,1853,1854],{"class":276}," RISKY_MARKERS",[197,1856,1817],{"class":232},[197,1858,1859,1861,1863,1866,1869,1872,1875,1877,1879,1882,1884,1887,1890,1892,1895],{"class":93,"line":748},[197,1860,1822],{"class":228},[197,1862,1825],{"class":276},[197,1864,1865],{"class":232},"(n.startswith(folder) ",[197,1867,1868],{"class":228},"and",[197,1870,1871],{"class":232}," n.endswith(",[197,1873,1874],{"class":206},"\".xml\"",[197,1876,487],{"class":232},[197,1878,1868],{"class":228},[197,1880,1881],{"class":232}," marker ",[197,1883,343],{"class":228},[197,1885,1886],{"class":232}," z.read(n).decode(",[197,1888,1889],{"class":206},"\"utf-8\"",[197,1891,381],{"class":232},[197,1893,1894],{"class":206},"\"ignore\"",[197,1896,424],{"class":232},[197,1898,1899,1902,1904,1906],{"class":93,"line":760},[197,1900,1901],{"class":228},"                   for",[197,1903,1147],{"class":232},[197,1905,343],{"class":228},[197,1907,1837],{"class":232},[197,1909,1910],{"class":93,"line":775},[197,1911,1842],{"class":232},[197,1913,1914,1916],{"class":93,"line":797},[197,1915,1168],{"class":228},[197,1917,1918],{"class":232}," found\n",[197,1920,1921],{"class":93,"line":827},[197,1922,248],{"emptyLinePlaceholder":247},[197,1924,1925,1928,1931,1933,1936],{"class":93,"line":865},[197,1926,1927],{"class":276},"print",[197,1929,1930],{"class":232},"(at_risk(",[197,1932,586],{"class":206},[197,1934,1935],{"class":232},"))            ",[197,1937,1939],{"class":1938},"s-wDw","# ['sparklines', 'extension formats', 'shapes or text boxes']\n",[197,1941,1942,1944,1946,1948,1951],{"class":93,"line":881},[197,1943,1927],{"class":276},[197,1945,1930],{"class":232},[197,1947,1053],{"class":206},[197,1949,1950],{"class":232},"))    ",[197,1952,1953],{"class":1938},"# []\n",[10,1955,1956,1957,1960],{},"A job can call ",[14,1958,1959],{},"at_risk"," on its input and either stop with a clear message or switch to one of the safer strategies below. The empty result for the re-saved file is the loss made visible: the features are simply no longer there to detect.",[183,1962,1964],{"id":1963},"step-4-safer-ways-to-update-a-designed-workbook","Step 4 — Safer ways to update a designed workbook",[29,1966,38,1971,38,1974,38,1977,38,1980,38,1984,38,1989,38,1992,38,1996,38,2000,38,2003,38,2006,38,2013,38,2018,38,2024,38,2031,38,2036,38,2040,38,2042,38,2045,38,2049,38,2052,38,2055,38,2061,38,2064,38,2067,38,2069,38,2072,38,2076,38,2079,38,2082,38,2088,38,2091],{"viewBox":1967,"role":32,"ariaLabelledBy":1968,"xmlns":36,"style":37},"4 0 752 272",[1969,1970],"lose-alternatives-t","lose-alternatives-d",[40,1972,1973],{"id":1969},"How to update a workbook that contains features openpyxl would drop",[44,1975,1976],{"id":1970},"If only data changes, write it to a separate data workbook or sheet that the designed workbook references. If Excel is available, let Excel open and save the file through xlwings or COM. If neither is possible, save a copy with openpyxl and keep the original, reviewing what changed.",[48,1978],{"x":50,"y":51,"width":52,"height":1979,"fill":54},"272",[48,1981],{"x":1982,"y":58,"width":1983,"height":80,"rx":61,"fill":107,"stroke":98,"style":64},"141.5","476.9",[66,1985,1987],{"transform":1986},"translate(368.0,29.0) scale(1.000)",[70,1988],{"d":1482,"fill":73,"stroke":114,"style":75},[77,1990,1991],{"x":178,"y":80,"style":119},"The workbook has features openpyxl drops. What changes?",[93,1993],{"x1":178,"y1":1994,"x2":178,"y2":1995,"stroke":98,"style":64},"91.5","111.5",[93,1997],{"x1":1998,"y1":1995,"x2":1999,"y2":1995,"stroke":98,"style":64},"134.7","625.3",[93,2001],{"x1":1998,"y1":1995,"x2":1998,"y2":2002,"stroke":98,"style":64},"157.5",[100,2004],{"points":2005,"fill":103},"134.7,161.5 129.7,152.5 139.7,152.5",[48,2007],{"x":2008,"y":2009,"width":2010,"height":2011,"rx":2012,"fill":54,"stroke":63,"style":1441},"87.5","121.5","94.4","21.0","10",[77,2014,2017],{"x":1998,"y":2015,"style":2016},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","only the data",[48,2019],{"x":57,"y":2020,"width":2021,"height":2022,"rx":61,"fill":2023,"stroke":1466,"style":64},"163.5","229.3","93.0","#d9f4f1",[66,2025,2027],{"transform":2026},"translate(122.7,176.5) scale(1.000)",[70,2028],{"d":2029,"fill":73,"stroke":2030,"style":75},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1","var(--teal-ink,#0b6157)",[77,2032,2035],{"x":1998,"y":2033,"style":2034},"223.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","separate data file",[77,2037,2039],{"x":1998,"y":2038,"style":86},"242.5","designed file reads from it",[93,2041],{"x1":178,"y1":1995,"x2":178,"y2":2002,"stroke":98,"style":64},[100,2043],{"points":2044,"fill":103},"380.0,161.5 375.0,152.5 385.0,152.5",[48,2046],{"x":2047,"y":2009,"width":2048,"height":2011,"rx":2012,"fill":54,"stroke":63,"style":1441},"320.7","118.6",[77,2050,2051],{"x":178,"y":2015,"style":2016},"Excel is available",[48,2053],{"x":2054,"y":2020,"width":2021,"height":2022,"rx":61,"fill":107,"stroke":98,"style":64},"265.3",[66,2056,2058],{"transform":2057},"translate(368.0,176.5) scale(1.000)",[70,2059],{"d":2060,"fill":73,"stroke":114,"style":75},"M3 5.5l8-1.2v7.2H3z M13 4l8-1.2v8.7h-8z M3 13.5h8v7.2l-8-1.2z M13 13.5h8v8.7l-8-1.2z",[77,2062,2063],{"x":178,"y":2033,"style":119},"let Excel save it",[77,2065,2066],{"x":178,"y":2038,"style":86},"xlwings or COM",[93,2068],{"x1":1999,"y1":1995,"x2":1999,"y2":2002,"stroke":98,"style":64},[100,2070],{"points":2071,"fill":103},"625.3,161.5 620.3,152.5 630.3,152.5",[48,2073],{"x":2074,"y":2009,"width":2075,"height":2011,"rx":2012,"fill":54,"stroke":63,"style":1441},"595.3","60.0",[77,2077,2078],{"x":1999,"y":2015,"style":2016},"neither",[48,2080],{"x":2081,"y":2020,"width":2021,"height":2022,"rx":61,"fill":134,"stroke":135,"style":64},"510.7",[66,2083,2085],{"transform":2084},"translate(613.3,176.5) scale(1.000)",[70,2086],{"d":2087,"fill":73,"stroke":142,"style":75},"M6 3h9l4 4v14H6z M15 3v4h4",[77,2089,2090],{"x":1999,"y":2033,"style":146},"save a copy",[77,2092,2093],{"x":1999,"y":2038,"style":86},"never overwrite the original",[10,2095,2096,2097,2101],{},"The cleanest pattern separates data from design. The designed workbook — sparklines, shapes, slicers and all — is edited only in Excel, and it reads its numbers from a plain data sheet or a linked data workbook that your script regenerates in full every run. openpyxl or xlsxwriter never touches the designed file, so nothing can be lost. The template-filling workflow in ",[23,2098,2100],{"href":2099},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002F","Generating Excel Reports from Templates"," follows the same principle.",[10,2103,2104,2105,1062],{},"When the designed file itself must change and Excel is installed, let Excel do the saving: xlwings or pywin32 open the workbook in the real application, write values, and save through Excel, which preserves everything Excel understands. See ",[23,2106,2108],{"href":2107},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fread-and-write-a-live-excel-workbook-with-xlwings\u002F","Read and Write a Live Excel Workbook with xlwings",[10,2110,2111,2112,2114],{},"If neither is possible, never overwrite the original: save to a new file name, run ",[14,2113,1959],{}," on the input, and put its findings in the job's log so the loss is at least recorded.",[183,2116,2118],{"id":2117},"common-pitfalls","Common pitfalls",[2120,2121,2122,2130,2140,2146],"ul",{},[2123,2124,2125,2129],"li",{},[2126,2127,2128],"strong",{},"Treating a clean load as a safe save."," Shapes and some other features are dropped without any warning at all. Scan the package rather than relying on warnings.",[2123,2131,2132,2135,2136,2139],{},[2126,2133,2134],{},"Suppressing warnings globally."," ",[14,2137,2138],{},"warnings.filterwarnings(\"ignore\")"," at the top of a script hides exactly the messages this guide depends on.",[2123,2141,2142,2145],{},[2126,2143,2144],{},"Testing with your own files only."," Workbooks from colleagues accumulate features over time. A job that has saved a file safely for months can lose a sparkline the week someone adds one.",[2123,2147,2148,2151,2152,2155],{},[2126,2149,2150],{},"Assuming read-only is harmless because it cannot save."," It cannot, which is the point: for jobs that only read, ",[14,2153,2154],{},"read_only=True"," guarantees nothing is lost because nothing is written.",[183,2157,2159],{"id":2158},"performance-and-scale-notes","Performance and scale notes",[10,2161,2162,2163,2165],{},"The risk scan opens the zip and reads only the worksheet and drawing XML, which is fast even for large workbooks — far faster than ",[14,2164,16],{}," itself. Run it on every input, not just on the ones you expect to be complex. The round-trip cost is the other consideration: load and save parse and rewrite every cell, so an openpyxl edit of a large workbook takes seconds where writing a small separate data file takes milliseconds — another reason to keep bulk data and design apart.",[183,2167,2169],{"id":2168},"conclusion","Conclusion",[10,2171,2172,2173,2175],{},"An openpyxl save is a rewrite, not an edit. Cell values, styles, tables, comments, validation, images and charts come through; sparklines, extension formats, shapes and text boxes do not, charts can lose formatting, and macros need ",[14,2174,1594],{},". Capture the load-time warnings, scan the package for features openpyxl drops before overwriting anything, and keep designed workbooks away from the save path by writing data into a separate file or letting Excel itself do the saving.",[183,2177,2179],{"id":2178},"frequently-asked-questions","Frequently asked questions",[10,2181,2182,2185],{},[2126,2183,2184],{},"Does openpyxl keep charts and images when it saves an existing file?","\nIn current versions it reads existing charts and images and writes them back, so they usually survive. Charts are rebuilt from openpyxl's own model, though, so formatting it does not support can be simplified on the way through.",[10,2187,2188,2191],{},[2126,2189,2190],{},"What are the warnings about extensions not being supported?","\nExcel stores newer features — sparklines, data-bar options, some data validation and conditional formatting — in extension blocks. openpyxl warns that it does not support them and drops them when saving. The warning is the only notice you get.",[10,2193,2194,2197],{},[2126,2195,2196],{},"How do I keep VBA macros in an .xlsm file?","\nLoad with keep_vba=True and save with the .xlsm extension. Without keep_vba, the macro project is discarded.",[10,2199,2200,2203],{},[2126,2201,2202],{},"What should I use if the workbook contains features openpyxl drops?","\nWrite your data into a separate, plain workbook or sheet and let the designed workbook pull it in, or drive Excel itself with xlwings or COM so Excel does the saving. For values only, some tools patch the XML directly, but that is fragile.",[183,2205,2207],{"id":2206},"related","Related",[2120,2209,2210,2216,2223,2230],{},[2123,2211,2212,2213,2215],{},"Up one level: ",[23,2214,26],{"href":25}," — the openpyxl object model.",[2123,2217,2218,2222],{},[23,2219,2221],{"href":2220},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fkeep-charts-and-images-when-filling-an-excel-template\u002F","Keep Charts and Images When Filling an Excel Template"," — template edits that keep visuals.",[2123,2224,2225,2229],{},[23,2226,2228],{"href":2227},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-data-validation-extension-is-not-supported-warning\u002F","Fix the \"Data Validation extension is not supported\" Warning in openpyxl"," — the warning explained.",[2123,2231,2232,2236],{},[23,2233,2235],{"href":2234},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002F","Choosing a Python Excel Library"," — when openpyxl is the wrong tool.",[2238,2239,2240],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":193,"searchDepth":236,"depth":236,"links":2242},[2243,2244,2245,2246,2247,2248,2249,2250,2251,2252],{"id":185,"depth":236,"text":186},{"id":941,"depth":236,"text":942},{"id":1065,"depth":236,"text":1066},{"id":1598,"depth":236,"text":1599},{"id":1963,"depth":236,"text":1964},{"id":2117,"depth":236,"text":2118},{"id":2158,"depth":236,"text":2159},{"id":2168,"depth":236,"text":2169},{"id":2178,"depth":236,"text":2179},{"id":2206,"depth":236,"text":2207},"2026-09-23","openpyxl rewrites the whole file when it saves, and features it does not model disappear — sparklines, shapes and text boxes, data-bar and validation extensions, macros, slicers. Tested list, a pre-save risk scan, and safer alternatives.","md",[2257,2259,2261,2263],{"q":2184,"a":2258},"In current versions it reads existing charts and images and writes them back, so they usually survive. Charts are rebuilt from openpyxl's own model, though, so formatting it does not support can be simplified on the way through.",{"q":2190,"a":2260},"Excel stores newer features — sparklines, data-bar options, some data validation and conditional formatting — in extension blocks. openpyxl warns that it does not support them and drops them when saving. The warning is the only notice you get.",{"q":2196,"a":2262},"Load with keep_vba=True and save with the .xlsm extension. Without keep_vba, the macro project is discarded.",{"q":2202,"a":2264},"Write your data into a separate, plain workbook or sheet and let the designed workbook pull it in, or drive Excel itself with xlwings or COM so Excel does the saving. For values only, some tools patch the XML directly, but that is fragile.","what openpyxl loses when you save an existing workbook",null,{"breadcrumb":2268},[2269,2272,2275],{"name":2270,"item":2271},"Home","\u002F",{"name":2273,"item":2274},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":26,"item":25},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook",{"title":2278,"description":2279},"What openpyxl Drops When Saving an Existing Excel File","A tested list of what survives an openpyxl load-and-save — charts and images do, sparklines, text boxes, extension formats and VBA do not — with a Python scan that flags at-risk workbooks before you overwrite them.","what-openpyxl-loses-when-saving-an-existing-workbook","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fwhat-openpyxl-loses-when-saving-an-existing-workbook\u002Findex",[2283,2284,2285],"troubleshooting","writing-excel","openpyxl","how-to","J2KaBbW-CqimYcMEbgItK12LJOSZwxaAb4qi7_6D0Zw",[2289,2293],{"title":2290,"path":2291,"stem":2292,"children":-1},"Use Theme Colours and Tints in openpyxl","\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl","getting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation\u002Fuse-theme-colours-and-tints-in-openpyxl\u002Findex",{"title":2294,"path":2295,"stem":2296,"children":-1},"Using Python in Excel","\u002Fgetting-started-with-python-excel-automation\u002Fusing-python-in-excel","getting-started-with-python-excel-automation\u002Fusing-python-in-excel\u002Findex",[2298,2300],{"path":2299,"title":2273},"\u002Fgetting-started-with-python-excel-automation",{"path":2301,"title":26},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-openpyxl-for-excel-file-manipulation",[],[2304,2308,2312,2315,2318,2321],{"path":2305,"title":2306,"kind":2307},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fhandle-permission-denied-when-writing-excel-in-python","Handle Permission Denied When Writing Excel in Python","fix",{"path":2309,"title":2310,"kind":2311},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors","Troubleshooting Common Python Excel Errors","guide",{"path":2313,"title":2314,"kind":2307},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-at-least-one-sheet-must-be-visible-error","Fix \"At least one sheet must be visible\" in openpyxl and pandas",{"path":2316,"title":2317,"kind":2307},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-badzipfile-error-when-reading-excel-in-python","Fix BadZipFile When Reading an Excel File in Python",{"path":2319,"title":2320,"kind":2307},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-cannot-convert-to-excel-valueerror","Fix \"ValueError: Cannot convert to Excel\" in openpyxl",{"path":2322,"title":2323,"kind":2307},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-could-not-convert-string-to-float-in-excel-data","Fix \"Could Not Convert String to Float\" in Excel Data",1790972073758]