[{"data":1,"prerenderedAt":1595},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frefresh-excel-data-connections-and-recalculate-with-win32com":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frefresh-excel-data-connections-and-recalculate-with-win32com":1586},{"id":4,"title":5,"body":6,"dateModified":1559,"datePublished":1559,"description":1560,"extension":1561,"faq":1562,"meta":1571,"navigation":193,"path":1579,"seo":1580,"slug":1582,"stem":1583,"type":1584,"__hash__":1585},"docs\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frefresh-excel-data-connections-and-recalculate-with-win32com\u002Findex.md","Refresh Excel Data Connections and Recalculate with win32com",{"type":7,"value":8,"toc":1544},"minimark",[9,19,122,127,152,155,159,165,361,367,371,382,440,458,462,558,570,667,675,679,682,728,742,746,749,918,921,925,935,1175,1187,1191,1194,1202,1205,1209,1330,1334,1341,1439,1455,1459,1471,1475,1482,1488,1494,1500,1504,1540],[10,11,12,13,18],"p",{},"The value of driving Excel is often not the data but the calculation: a model whose formulas belong\nto somebody else, a Power Query connection pointing at a warehouse, a pivot cache that has to be\nrebuilt before the summary sheet means anything. All three are one method call away — and all three\nhave a timing problem that produces stale output without any error. This guide, part of\n",[14,15,17],"a",{"href":16},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002F","Automating Excel with COM and pywin32",",\ncovers the calls and the waiting.",[20,21,29,30,29,34,29,38,29,45,29,52,29,62,29,68,29,73,29,80,29,85,29,90,29,94,29,97,29,101,29,104,29,109,29,114,29,117],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"0 0 760 232","img",[25,26],"ref-async-t","ref-async-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:760px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"Why a refresh can finish after the save",[35,36,37],"desc",{"id":26},"RefreshAll starts every connection and returns immediately, so a save on the next line writes the old values unless the script waits for the queries to complete first.",[39,40],"rect",{"x":41,"y":41,"width":42,"height":43,"fill":44},"0","760","232","#ffffff",[46,47,51],"text",{"x":48,"y":49,"style":50},"380.0","32","font-size:13px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","the timing bug",[39,53],{"x":54,"y":55,"width":56,"height":57,"rx":58,"fill":59,"stroke":60,"style":61},"24.0","74","208.0","96","12","#fee8f2","var(--accent,#d81b73)","stroke-width:2px",[46,63,67],{"x":64,"y":65,"style":66},"128.0","114","font-size:14px;font-weight:700;fill:var(--accent,#d81b73);text-anchor:middle","RefreshAll()",[46,69,72],{"x":64,"y":70,"style":71},"136","font-size:11.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","starts, does not finish",[74,75],"line",{"x1":76,"y1":77,"x2":78,"y2":77,"stroke":79,"style":61},"237.0","122.0","269.0","var(--brand,#5b5cf0)",[81,82],"polygon",{"points":83,"fill":84},"269.0,122.0 260.0,117.0 260.0,127.0","#5b5cf0",[39,86],{"x":87,"y":55,"width":56,"height":57,"rx":58,"fill":88,"stroke":89,"style":61},"276.0","#fdefd8","var(--gold,#b4740a)",[46,91,93],{"x":48,"y":65,"style":92},"font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","wait for queries",[46,95,96],{"x":48,"y":70,"style":71},"the step people skip",[74,98],{"x1":99,"y1":77,"x2":100,"y2":77,"stroke":79,"style":61},"489.0","521.0",[81,102],{"points":103,"fill":84},"521.0,122.0 512.0,117.0 512.0,127.0",[39,105],{"x":106,"y":55,"width":56,"height":57,"rx":58,"fill":107,"stroke":108,"style":61},"528.0","#d9f4f1","var(--teal,#0f9488)",[46,110,113],{"x":111,"y":65,"style":112},"632.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Save()",[46,115,116],{"x":111,"y":70,"style":71},"now the numbers are new",[46,118,121],{"x":48,"y":119,"style":120},"210","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","without the middle step the file looks refreshed and is not",[123,124,126],"h2",{"id":125},"prerequisites","Prerequisites",[128,129,134],"pre",{"className":130,"code":131,"language":132,"meta":133,"style":133},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pywin32\n","bash","",[135,136,137],"code",{"__ignoreMap":133},[138,139,141,145,149],"span",{"class":74,"line":140},1,[138,142,144],{"class":143},"sMTad","pip",[138,146,148],{"class":147},"srMev"," install",[138,150,151],{"class":147}," pywin32\n",[10,153,154],{},"A workbook with something to refresh — a query, a pivot table, or formulas that depend on data your\nscript writes.",[123,156,158],{"id":157},"the-refresh-that-is-not-finished-when-it-returns","The refresh that is not finished when it returns",[10,160,161,164],{},[135,162,163],{},"RefreshAll"," triggers every connection and pivot cache in the workbook and returns straight away.\nIf the next line saves the file, the saved workbook contains whatever was there before.",[128,166,170],{"className":167,"code":168,"language":169,"meta":133,"style":133},"language-python shiki shiki-themes github-light github-dark-high-contrast","import win32com.client as win32\n\nexcel = win32.DispatchEx(\"Excel.Application\")\nexcel.Visible = False\nexcel.DisplayAlerts = False\ntry:\n    book = excel.Workbooks.Open(r\"C:\\finance\\model.xlsx\", UpdateLinks=0)\n    try:\n        book.RefreshAll()\n        excel.CalculateUntilAsyncQueriesDone()      # block until queries finish\n        book.Save()\n    finally:\n        book.Close(SaveChanges=False)\nfinally:\n    excel.Quit()\n","python",[135,171,172,188,195,213,225,235,244,293,301,307,317,323,331,347,355],{"__ignoreMap":133},[138,173,174,178,182,185],{"class":74,"line":140},[138,175,177],{"class":176},"s-kum","import",[138,179,181],{"class":180},"skGVy"," win32com.client ",[138,183,184],{"class":176},"as",[138,186,187],{"class":180}," win32\n",[138,189,191],{"class":74,"line":190},2,[138,192,194],{"emptyLinePlaceholder":193},true,"\n",[138,196,198,201,204,207,210],{"class":74,"line":197},3,[138,199,200],{"class":180},"excel ",[138,202,203],{"class":176},"=",[138,205,206],{"class":180}," win32.DispatchEx(",[138,208,209],{"class":147},"\"Excel.Application\"",[138,211,212],{"class":180},")\n",[138,214,216,219,221],{"class":74,"line":215},4,[138,217,218],{"class":180},"excel.Visible ",[138,220,203],{"class":176},[138,222,224],{"class":223},"sP0c6"," False\n",[138,226,228,231,233],{"class":74,"line":227},5,[138,229,230],{"class":180},"excel.DisplayAlerts ",[138,232,203],{"class":176},[138,234,224],{"class":223},[138,236,238,241],{"class":74,"line":237},6,[138,239,240],{"class":176},"try",[138,242,243],{"class":180},":\n",[138,245,247,250,252,255,258,261,265,268,271,274,277,280,283,287,289,291],{"class":74,"line":246},7,[138,248,249],{"class":180},"    book ",[138,251,203],{"class":176},[138,253,254],{"class":180}," excel.Workbooks.Open(",[138,256,257],{"class":176},"r",[138,259,260],{"class":147},"\"C:",[138,262,264],{"class":263},"s_b0D","\\f",[138,266,267],{"class":147},"inance",[138,269,270],{"class":263},"\\m",[138,272,273],{"class":147},"odel",[138,275,276],{"class":223},".",[138,278,279],{"class":147},"xlsx\"",[138,281,282],{"class":180},", ",[138,284,286],{"class":285},"sa561","UpdateLinks",[138,288,203],{"class":176},[138,290,41],{"class":223},[138,292,212],{"class":180},[138,294,296,299],{"class":74,"line":295},8,[138,297,298],{"class":176},"    try",[138,300,243],{"class":180},[138,302,304],{"class":74,"line":303},9,[138,305,306],{"class":180},"        book.RefreshAll()\n",[138,308,310,313],{"class":74,"line":309},10,[138,311,312],{"class":180},"        excel.CalculateUntilAsyncQueriesDone()      ",[138,314,316],{"class":315},"s-wDw","# block until queries finish\n",[138,318,320],{"class":74,"line":319},11,[138,321,322],{"class":180},"        book.Save()\n",[138,324,326,329],{"class":74,"line":325},12,[138,327,328],{"class":176},"    finally",[138,330,243],{"class":180},[138,332,334,337,340,342,345],{"class":74,"line":333},13,[138,335,336],{"class":180},"        book.Close(",[138,338,339],{"class":285},"SaveChanges",[138,341,203],{"class":176},[138,343,344],{"class":223},"False",[138,346,212],{"class":180},[138,348,350,353],{"class":74,"line":349},14,[138,351,352],{"class":176},"finally",[138,354,243],{"class":180},[138,356,358],{"class":74,"line":357},15,[138,359,360],{"class":180},"    excel.Quit()\n",[10,362,363,366],{},[135,364,365],{},"CalculateUntilAsyncQueriesDone"," is the simplest fix and covers most workbooks. It waits for\noutstanding queries and then recalculates, which is exactly the sequence a person performs by\npressing Refresh All and waiting for the status bar to settle.",[123,368,370],{"id":369},"making-refreshes-synchronous-instead","Making refreshes synchronous instead",[10,372,373,374,377,378,381],{},"The more dependable approach removes the asynchrony rather than waiting it out. Every connection has\na ",[135,375,376],{},"BackgroundQuery"," flag, and turning it off makes ",[135,379,380],{},"Refresh"," block until the data has arrived.",[128,383,385],{"className":167,"code":384,"language":169,"meta":133,"style":133},"for connection in book.Connections:\n    try:\n        connection.OLEDBConnection.BackgroundQuery = False\n    except Exception:\n        connection.ODBCConnection.BackgroundQuery = False\n    connection.Refresh()\n",[135,386,387,401,407,416,426,435],{"__ignoreMap":133},[138,388,389,392,395,398],{"class":74,"line":140},[138,390,391],{"class":176},"for",[138,393,394],{"class":180}," connection ",[138,396,397],{"class":176},"in",[138,399,400],{"class":180}," book.Connections:\n",[138,402,403,405],{"class":74,"line":190},[138,404,298],{"class":176},[138,406,243],{"class":180},[138,408,409,412,414],{"class":74,"line":197},[138,410,411],{"class":180},"        connection.OLEDBConnection.BackgroundQuery ",[138,413,203],{"class":176},[138,415,224],{"class":223},[138,417,418,421,424],{"class":74,"line":215},[138,419,420],{"class":176},"    except",[138,422,423],{"class":223}," Exception",[138,425,243],{"class":180},[138,427,428,431,433],{"class":74,"line":227},[138,429,430],{"class":180},"        connection.ODBCConnection.BackgroundQuery ",[138,432,203],{"class":176},[138,434,224],{"class":223},[138,436,437],{"class":74,"line":237},[138,438,439],{"class":180},"    connection.Refresh()\n",[10,441,442,443,445,446,449,450,453,454,457],{},"The ",[135,444,240],{},"\u002F",[135,447,448],{},"except"," is not laziness — a connection exposes either an ",[135,451,452],{},"OLEDBConnection"," or an\n",[135,455,456],{},"ODBCConnection"," depending on its type, and accessing the wrong one raises. Iterating this way also\ngives you per-connection control, so a slow warehouse query can be skipped when only a local table\nneeds updating.",[123,459,461],{"id":460},"recalculating-deliberately","Recalculating deliberately",[20,463,29,468,29,471,29,474,29,477,29,486,29,492,29,496,29,500,29,504,29,507,29,512,29,516,29,519,29,522,29,525,29,529,29,532,29,534,29,537,29,541,29,546,29,548,29,551,29,554],{"viewBox":464,"role":23,"ariaLabelledBy":465,"xmlns":27,"style":28},"0 0 760 216",[466,467],"ref-calc-t","ref-calc-d",[31,469,470],{"id":466},"Three levels of recalculation and when each applies",[35,472,473],{"id":467},"Calculate recomputes only what is marked dirty, CalculateFull recomputes every formula, and CalculateFullRebuild also rebuilds the dependency tree, which is required after a script writes formulas.",[39,475],{"x":41,"y":41,"width":42,"height":476,"fill":44},"216",[39,478],{"x":479,"y":480,"width":481,"height":482,"rx":483,"fill":484,"stroke":485},"18","30","724","34","8","#e7ebef","var(--line,#cdd5e6)",[46,487,491],{"x":488,"y":489,"style":490},"126.6","52","font-size:12px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","Call",[46,493,495],{"x":494,"y":489,"style":490},"319.65","Recomputes",[46,497,499],{"x":498,"y":489,"style":490},"488.55","Rebuilds graph",[46,501,503],{"x":502,"y":489,"style":490},"657.45","Use when",[39,505],{"x":479,"y":506,"width":481,"height":482,"rx":483,"fill":107,"stroke":108},"67",[46,508,511],{"x":488,"y":509,"style":510},"88.0","font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Calculate",[46,513,515],{"x":494,"y":509,"style":514},"font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","dirty cells",[46,517,518],{"x":498,"y":509,"style":514},"no",[46,520,521],{"x":502,"y":509,"style":514},"values changed",[39,523],{"x":479,"y":524,"width":481,"height":482,"rx":483,"fill":88,"stroke":89},"107",[46,526,528],{"x":488,"y":64,"style":527},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","CalculateFull",[46,530,531],{"x":494,"y":64,"style":514},"every formula",[46,533,518],{"x":498,"y":64,"style":514},[46,535,536],{"x":502,"y":64,"style":514},"results look stale",[39,538],{"x":479,"y":539,"width":481,"height":482,"rx":483,"fill":540,"stroke":79},"147","#f0f4ff",[46,542,545],{"x":488,"y":543,"style":544},"168.0","font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","CalculateFullRebuild",[46,547,531],{"x":494,"y":543,"style":514},[46,549,550],{"x":498,"y":543,"style":514},"yes",[46,552,553],{"x":502,"y":543,"style":514},"formulas were written",[46,555,557],{"x":48,"y":556,"style":120},"204","after a script writes formulas, only the third is safe",[10,559,560,561,563,564,566,567,569],{},"Excel has three levels of recalculation and they are not interchangeable. ",[135,562,511],{}," recomputes\nwhat the dependency tree marks as dirty. ",[135,565,528],{}," recomputes everything. ",[135,568,545],{},"\nrebuilds the dependency tree first, which matters after a script has written formulas — Excel's\ngraph can be stale in exactly that case.",[128,571,573],{"className":167,"code":572,"language":169,"meta":133,"style":133},"XL_MANUAL, XL_AUTOMATIC = -4135, -4105\n\nexcel.Calculation = XL_MANUAL          # stop recalculating after every write\nsheet = book.Sheets(\"Data\")\nsheet.Range(\"E2:E5001\").Formula = \"=C2*D2\"\nexcel.CalculateFullRebuild()           # rebuild the graph, then compute\nexcel.Calculation = XL_AUTOMATIC\n",[135,574,575,602,606,619,634,650,658],{"__ignoreMap":133},[138,576,577,580,582,585,588,591,594,596,599],{"class":74,"line":140},[138,578,579],{"class":223},"XL_MANUAL",[138,581,282],{"class":180},[138,583,584],{"class":223},"XL_AUTOMATIC",[138,586,587],{"class":176}," =",[138,589,590],{"class":176}," -",[138,592,593],{"class":223},"4135",[138,595,282],{"class":180},[138,597,598],{"class":176},"-",[138,600,601],{"class":223},"4105\n",[138,603,604],{"class":74,"line":190},[138,605,194],{"emptyLinePlaceholder":193},[138,607,608,611,613,616],{"class":74,"line":197},[138,609,610],{"class":180},"excel.Calculation ",[138,612,203],{"class":176},[138,614,615],{"class":223}," XL_MANUAL",[138,617,618],{"class":315},"          # stop recalculating after every write\n",[138,620,621,624,626,629,632],{"class":74,"line":215},[138,622,623],{"class":180},"sheet ",[138,625,203],{"class":176},[138,627,628],{"class":180}," book.Sheets(",[138,630,631],{"class":147},"\"Data\"",[138,633,212],{"class":180},[138,635,636,639,642,645,647],{"class":74,"line":227},[138,637,638],{"class":180},"sheet.Range(",[138,640,641],{"class":147},"\"E2:E5001\"",[138,643,644],{"class":180},").Formula ",[138,646,203],{"class":176},[138,648,649],{"class":147}," \"=C2*D2\"\n",[138,651,652,655],{"class":74,"line":237},[138,653,654],{"class":180},"excel.CalculateFullRebuild()           ",[138,656,657],{"class":315},"# rebuild the graph, then compute\n",[138,659,660,662,664],{"class":74,"line":246},[138,661,610],{"class":180},[138,663,203],{"class":176},[138,665,666],{"class":223}," XL_AUTOMATIC\n",[10,668,669,670,674],{},"Writing five thousand formulas with calculation on automatic makes Excel recompute the workbook five\nthousand times. Switching to manual first, then rebuilding once, is the difference between seconds\nand minutes — and it is the same pattern as the file-level advice in\n",[14,671,673],{"href":672},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Frecalculate-excel-formulas-without-excel-in-python\u002F","Recalculate Excel Formulas Without Excel in Python",",\napproached from the other side.",[123,676,678],{"id":677},"pivot-tables-and-their-caches","Pivot tables and their caches",[10,680,681],{},"A pivot table reads from a cache, and the cache reads from the source. Refreshing the wrong one\nproduces a table that looks updated and is not.",[128,683,685],{"className":167,"code":684,"language":169,"meta":133,"style":133},"for sheet in book.Sheets:\n    for pivot in sheet.PivotTables():\n        pivot.PivotCache().Refresh()      # pull new data into the cache\n        pivot.RefreshTable()              # rebuild the table from the cache\n",[135,686,687,699,712,720],{"__ignoreMap":133},[138,688,689,691,694,696],{"class":74,"line":140},[138,690,391],{"class":176},[138,692,693],{"class":180}," sheet ",[138,695,397],{"class":176},[138,697,698],{"class":180}," book.Sheets:\n",[138,700,701,704,707,709],{"class":74,"line":190},[138,702,703],{"class":176},"    for",[138,705,706],{"class":180}," pivot ",[138,708,397],{"class":176},[138,710,711],{"class":180}," sheet.PivotTables():\n",[138,713,714,717],{"class":74,"line":197},[138,715,716],{"class":180},"        pivot.PivotCache().Refresh()      ",[138,718,719],{"class":315},"# pull new data into the cache\n",[138,721,722,725],{"class":74,"line":215},[138,723,724],{"class":180},"        pivot.RefreshTable()              ",[138,726,727],{"class":315},"# rebuild the table from the cache\n",[10,729,730,733,734,737,738,276],{},[135,731,732],{},"RefreshTable"," alone rebuilds from a cache that may be hours old. ",[135,735,736],{},"PivotCache().Refresh()"," alone\nupdates the cache but can leave the visible table showing the previous layout. Doing both, in that\norder, is the reliable sequence. The pure-Python alternatives — where a pivot is generated rather\nthan refreshed — are in\n",[14,739,741],{"href":740},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002F","Creating Pivot Tables from Excel Data",[123,743,745],{"id":744},"verifying-the-refresh-actually-happened","Verifying the refresh actually happened",[10,747,748],{},"The failure this guide exists to prevent is silent, so the defence is a check rather than a\nconvention. Stamp the refresh time into the workbook and assert on a value you know should change.",[128,750,752],{"className":167,"code":751,"language":169,"meta":133,"style":133},"from datetime import datetime\n\nbefore = book.Sheets(\"Summary\").Range(\"C12\").Value\nbook.RefreshAll()\nexcel.CalculateUntilAsyncQueriesDone()\nafter = book.Sheets(\"Summary\").Range(\"C12\").Value\n\nbook.Sheets(\"Summary\").Range(\"A1\").Value = f\"Refreshed {datetime.now():%Y-%m-%d %H:%M}\"\nif before == after:\n    print(\"warning: the headline figure did not move — check the connection\")\nbook.Save()\n",[135,753,754,767,771,792,797,802,819,823,886,900,913],{"__ignoreMap":133},[138,755,756,759,762,764],{"class":74,"line":140},[138,757,758],{"class":176},"from",[138,760,761],{"class":180}," datetime ",[138,763,177],{"class":176},[138,765,766],{"class":180}," datetime\n",[138,768,769],{"class":74,"line":190},[138,770,194],{"emptyLinePlaceholder":193},[138,772,773,776,778,780,783,786,789],{"class":74,"line":197},[138,774,775],{"class":180},"before ",[138,777,203],{"class":176},[138,779,628],{"class":180},[138,781,782],{"class":147},"\"Summary\"",[138,784,785],{"class":180},").Range(",[138,787,788],{"class":147},"\"C12\"",[138,790,791],{"class":180},").Value\n",[138,793,794],{"class":74,"line":215},[138,795,796],{"class":180},"book.RefreshAll()\n",[138,798,799],{"class":74,"line":227},[138,800,801],{"class":180},"excel.CalculateUntilAsyncQueriesDone()\n",[138,803,804,807,809,811,813,815,817],{"class":74,"line":237},[138,805,806],{"class":180},"after ",[138,808,203],{"class":176},[138,810,628],{"class":180},[138,812,782],{"class":147},[138,814,785],{"class":180},[138,816,788],{"class":147},[138,818,791],{"class":180},[138,820,821],{"class":74,"line":246},[138,822,194],{"emptyLinePlaceholder":193},[138,824,825,828,830,832,835,838,840,843,846,850,853,856,859,862,865,867,870,872,875,877,880,883],{"class":74,"line":295},[138,826,827],{"class":180},"book.Sheets(",[138,829,782],{"class":147},[138,831,785],{"class":180},[138,833,834],{"class":147},"\"A1\"",[138,836,837],{"class":180},").Value ",[138,839,203],{"class":176},[138,841,842],{"class":176}," f",[138,844,845],{"class":147},"\"Refreshed ",[138,847,849],{"class":848},"sSjpA","{",[138,851,852],{"class":180},"datetime.now():",[138,854,855],{"class":176},"%",[138,857,858],{"class":180},"Y",[138,860,861],{"class":176},"-%",[138,863,864],{"class":180},"m",[138,866,861],{"class":176},[138,868,869],{"class":180},"d ",[138,871,855],{"class":176},[138,873,874],{"class":180},"H:",[138,876,855],{"class":176},[138,878,879],{"class":180},"M",[138,881,882],{"class":848},"}",[138,884,885],{"class":147},"\"\n",[138,887,888,891,894,897],{"class":74,"line":303},[138,889,890],{"class":176},"if",[138,892,893],{"class":180}," before ",[138,895,896],{"class":176},"==",[138,898,899],{"class":180}," after:\n",[138,901,902,905,908,911],{"class":74,"line":309},[138,903,904],{"class":223},"    print",[138,906,907],{"class":180},"(",[138,909,910],{"class":147},"\"warning: the headline figure did not move — check the connection\"",[138,912,212],{"class":180},[138,914,915],{"class":74,"line":319},[138,916,917],{"class":180},"book.Save()\n",[10,919,920],{},"A visible timestamp on the sheet costs one line and answers the question every recipient eventually\nasks. The comparison catches a connection that failed quietly, which is the more common of the two\nfailure modes.",[123,922,924],{"id":923},"polling-a-refresh-that-has-no-synchronous-mode","Polling a refresh that has no synchronous mode",[10,926,927,928,930,931,934],{},"Some connection types — notably certain Power Query and OLAP sources — ignore ",[135,929,376],{}," or\ndo not expose it at all. For those, the option left is to poll, and the property to watch is\n",[135,932,933],{},"Refreshing"," on the connection object.",[128,936,938],{"className":167,"code":937,"language":169,"meta":133,"style":133},"import time\n\ndef wait_for_refresh(book, timeout=600, interval=2):\n    deadline = time.monotonic() + timeout\n    while time.monotonic() \u003C deadline:\n        busy = [c.Name for c in book.Connections if _is_refreshing(c)]\n        if not busy:\n            return\n        time.sleep(interval)\n    raise TimeoutError(f\"connections still refreshing after {timeout}s: {busy}\")\n\n\ndef _is_refreshing(connection):\n    for attribute in (\"OLEDBConnection\", \"ODBCConnection\"):\n        try:\n            return getattr(connection, attribute).Refreshing\n        except Exception:\n            continue\n    return False\n",[135,939,940,947,951,979,995,1008,1033,1044,1049,1054,1092,1096,1100,1110,1132,1139,1151,1161,1167],{"__ignoreMap":133},[138,941,942,944],{"class":74,"line":140},[138,943,177],{"class":176},[138,945,946],{"class":180}," time\n",[138,948,949],{"class":74,"line":190},[138,950,194],{"emptyLinePlaceholder":193},[138,952,953,956,960,963,965,968,971,973,976],{"class":74,"line":197},[138,954,955],{"class":176},"def",[138,957,959],{"class":958},"s_Opv"," wait_for_refresh",[138,961,962],{"class":180},"(book, timeout",[138,964,203],{"class":176},[138,966,967],{"class":223},"600",[138,969,970],{"class":180},", interval",[138,972,203],{"class":176},[138,974,975],{"class":223},"2",[138,977,978],{"class":180},"):\n",[138,980,981,984,986,989,992],{"class":74,"line":215},[138,982,983],{"class":180},"    deadline ",[138,985,203],{"class":176},[138,987,988],{"class":180}," time.monotonic() ",[138,990,991],{"class":176},"+",[138,993,994],{"class":180}," timeout\n",[138,996,997,1000,1002,1005],{"class":74,"line":227},[138,998,999],{"class":176},"    while",[138,1001,988],{"class":180},[138,1003,1004],{"class":176},"\u003C",[138,1006,1007],{"class":180}," deadline:\n",[138,1009,1010,1013,1015,1018,1020,1023,1025,1028,1030],{"class":74,"line":237},[138,1011,1012],{"class":180},"        busy ",[138,1014,203],{"class":176},[138,1016,1017],{"class":180}," [c.Name ",[138,1019,391],{"class":176},[138,1021,1022],{"class":180}," c ",[138,1024,397],{"class":176},[138,1026,1027],{"class":180}," book.Connections ",[138,1029,890],{"class":176},[138,1031,1032],{"class":180}," _is_refreshing(c)]\n",[138,1034,1035,1038,1041],{"class":74,"line":246},[138,1036,1037],{"class":176},"        if",[138,1039,1040],{"class":176}," not",[138,1042,1043],{"class":180}," busy:\n",[138,1045,1046],{"class":74,"line":295},[138,1047,1048],{"class":176},"            return\n",[138,1050,1051],{"class":74,"line":303},[138,1052,1053],{"class":180},"        time.sleep(interval)\n",[138,1055,1056,1059,1062,1064,1067,1070,1072,1075,1077,1080,1082,1085,1087,1090],{"class":74,"line":309},[138,1057,1058],{"class":176},"    raise",[138,1060,1061],{"class":223}," TimeoutError",[138,1063,907],{"class":180},[138,1065,1066],{"class":176},"f",[138,1068,1069],{"class":147},"\"connections still refreshing after ",[138,1071,849],{"class":848},[138,1073,1074],{"class":180},"timeout",[138,1076,882],{"class":848},[138,1078,1079],{"class":147},"s: ",[138,1081,849],{"class":848},[138,1083,1084],{"class":180},"busy",[138,1086,882],{"class":848},[138,1088,1089],{"class":147},"\"",[138,1091,212],{"class":180},[138,1093,1094],{"class":74,"line":319},[138,1095,194],{"emptyLinePlaceholder":193},[138,1097,1098],{"class":74,"line":325},[138,1099,194],{"emptyLinePlaceholder":193},[138,1101,1102,1104,1107],{"class":74,"line":333},[138,1103,955],{"class":176},[138,1105,1106],{"class":958}," _is_refreshing",[138,1108,1109],{"class":180},"(connection):\n",[138,1111,1112,1114,1117,1119,1122,1125,1127,1130],{"class":74,"line":349},[138,1113,703],{"class":176},[138,1115,1116],{"class":180}," attribute ",[138,1118,397],{"class":176},[138,1120,1121],{"class":180}," (",[138,1123,1124],{"class":147},"\"OLEDBConnection\"",[138,1126,282],{"class":180},[138,1128,1129],{"class":147},"\"ODBCConnection\"",[138,1131,978],{"class":180},[138,1133,1134,1137],{"class":74,"line":357},[138,1135,1136],{"class":176},"        try",[138,1138,243],{"class":180},[138,1140,1142,1145,1148],{"class":74,"line":1141},16,[138,1143,1144],{"class":176},"            return",[138,1146,1147],{"class":223}," getattr",[138,1149,1150],{"class":180},"(connection, attribute).Refreshing\n",[138,1152,1154,1157,1159],{"class":74,"line":1153},17,[138,1155,1156],{"class":176},"        except",[138,1158,423],{"class":223},[138,1160,243],{"class":180},[138,1162,1164],{"class":74,"line":1163},18,[138,1165,1166],{"class":176},"            continue\n",[138,1168,1170,1173],{"class":74,"line":1169},19,[138,1171,1172],{"class":176},"    return",[138,1174,224],{"class":223},[10,1176,1177,1178,1181,1182,1186],{},"A timeout is not optional here. A query pointed at a warehouse that is down will otherwise leave the\nscript — and the ",[135,1179,1180],{},"EXCEL.EXE"," process behind it — waiting indefinitely, which is precisely the\nscenario that fills a machine with orphans overnight. Raising after ten minutes turns that into a\nfailed run that the scheduler can report, and the cleanup rules in\n",[14,1183,1185],{"href":1184},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fclose-excel-cleanly-and-avoid-orphan-com-processes\u002F","Close Excel Cleanly and Avoid Orphan COM Processes","\nmake sure the process still exits.",[123,1188,1190],{"id":1189},"credentials-prompts-and-unattended-runs","Credentials, prompts and unattended runs",[10,1192,1193],{},"The other reason a refresh hangs has nothing to do with timing: the connection wants a password.\nInteractively, Excel asks; unattended, it asks a dialog nobody can see. There are three workable\narrangements, and it is worth choosing one deliberately rather than discovering the problem at 06:00.",[10,1195,1196,1197,1201],{},"Store the credential in the connection string and let the workbook keep it — simple, and only\nacceptable where the file's own access controls are strong enough to hold a password. Use integrated\nauthentication so the connection runs as the Windows account executing the script, which is the\ncleanest option when the warehouse supports it. Or take the connection out of the workbook entirely\nand query the database from Python, writing the results into the sheet — which removes the whole\ncategory of problem and is usually the right long-term answer, as\n",[14,1198,1200],{"href":1199},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmoving-data-between-excel-and-databases\u002Fexport-sql-query-results-to-excel-with-python\u002F","Export SQL Query Results to Excel with Python","\ndescribes.",[10,1203,1204],{},"The third option also makes the job testable. A Python query can be run against a development\ndatabase from a laptop; a refresh embedded in a workbook can only be exercised by opening the\nworkbook on a machine with the right credentials, which means it is never covered by anything\nresembling a test.",[123,1206,1208],{"id":1207},"common-pitfalls","Common pitfalls",[1210,1211,1212,1228],"table",{},[1213,1214,1215],"thead",{},[1216,1217,1218,1222,1225],"tr",{},[1219,1220,1221],"th",{},"Symptom",[1219,1223,1224],{},"Cause",[1219,1226,1227],{},"Fix",[1229,1230,1231,1250,1267,1288,1304,1315],"tbody",{},[1216,1232,1233,1237,1242],{},[1234,1235,1236],"td",{},"Saved file has last run's numbers",[1234,1238,1239,1241],{},[135,1240,163],{}," returned before the data did",[1234,1243,1244,1246,1247],{},[135,1245,365],{},", or ",[135,1248,1249],{},"BackgroundQuery = False",[1216,1251,1252,1255,1258],{},[1234,1253,1254],{},"A prompt about updating links hangs the script",[1234,1256,1257],{},"External references, with alerts enabled",[1234,1259,1260,1263,1264],{},[135,1261,1262],{},"excel.AskToUpdateLinks = False"," and ",[135,1265,1266],{},"Open(..., UpdateLinks=0)",[1216,1268,1269,1272,1282],{},[1234,1270,1271],{},"Formulas show as text",[1234,1273,1274,1275,1278,1279],{},"They were written to ",[135,1276,1277],{},".Value"," rather than ",[135,1280,1281],{},".Formula",[1234,1283,1284,1285,1287],{},"Assign to ",[135,1286,1281],{},", then recalculate",[1216,1289,1290,1293,1296],{},[1234,1291,1292],{},"Pivot looks refreshed but is not",[1234,1294,1295],{},"The cache was not refreshed",[1234,1297,1298,1300,1301],{},[135,1299,736],{}," then ",[135,1302,1303],{},"RefreshTable()",[1216,1305,1306,1309,1312],{},[1234,1307,1308],{},"Refresh fails with a credentials error",[1234,1310,1311],{},"The connection needs a saved password that this account does not have",[1234,1313,1314],{},"Store the credential for the running account, or refresh from a query the account can run",[1216,1316,1317,1320,1323],{},[1234,1318,1319],{},"Recalculation takes minutes",[1234,1321,1322],{},"Calculation left on automatic during bulk writes",[1234,1324,1325,1326,1329],{},"Set ",[135,1327,1328],{},"Calculation = xlCalculationManual"," around the writes",[123,1331,1333],{"id":1332},"performance-and-scale","Performance and scale",[10,1335,1336,1337,1340],{},"The expensive part of a refresh is nearly always the query rather than Excel. That has a practical\nconsequence: if a workbook has six connections and the report only depends on two, iterating\n",[135,1338,1339],{},"book.Connections"," and refreshing the two you need can turn a fifteen-minute job into a two-minute\none, with no change to the output.",[20,1342,29,1347,29,1350,29,1353,29,1356,29,1363,29,1368,29,1374,29,1378,29,1382,29,1386,29,1389,29,1394,29,1398,29,1401,29,1404,29,1407,29,1415,29,1420,29,1425,29,1428,29,1432,29,1435],{"viewBox":1343,"role":23,"ariaLabelledBy":1344,"xmlns":27,"style":28},"0 0 760 201",[1345,1346],"ref-scope-t","ref-scope-d",[31,1348,1349],{"id":1345},"Refreshing everything versus refreshing what the report needs",[35,1351,1352],{"id":1346},"RefreshAll runs every connection in the workbook including ones the report does not depend on, while iterating the connections collection lets a script refresh only the two that matter.",[39,1354],{"x":41,"y":41,"width":42,"height":1355,"fill":44},"201",[39,1357],{"x":1358,"y":1359,"width":1360,"height":1361,"rx":1362,"fill":59,"stroke":60,"style":61},"20","28","270.0","139","14",[46,1364,163],{"x":1365,"y":1366,"style":1367},"155.0","54","font-size:13px;font-weight:700;fill:var(--accent,#d81b73);text-anchor:middle",[74,1369],{"x1":1370,"y1":1371,"x2":1372,"y2":1371,"stroke":60,"style":1373},"36","64","274.0","stroke-width:1px",[46,1375,1377],{"x":1365,"y":1376,"style":514},"86","six connections",[46,1379,1381],{"x":1365,"y":1380,"style":514},"109","two are needed",[46,1383,1385],{"x":1365,"y":1384,"style":514},"132","fifteen minutes",[39,1387],{"x":1388,"y":1359,"width":1360,"height":1361,"rx":1362,"fill":107,"stroke":108,"style":61},"470.0",[46,1390,1393],{"x":1391,"y":1366,"style":1392},"605.0","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","selective refresh",[74,1395],{"x1":1396,"y1":1371,"x2":1397,"y2":1371,"stroke":108,"style":1373},"486.0","724.0",[46,1399,1400],{"x":1391,"y":1376,"style":514},"two connections",[46,1402,1403],{"x":1391,"y":1380,"style":514},"same output",[46,1405,1406],{"x":1391,"y":1384,"style":514},"two minutes",[39,1408],{"x":1409,"y":1410,"width":1411,"height":1412,"rx":1413,"fill":1414,"stroke":79},"316.0","78.5","128","38","19","#ebebfd",[46,1416,1419],{"x":48,"y":1417,"style":1418},"102.5","font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","iterate",[74,1421],{"x1":1422,"y1":1423,"x2":1424,"y2":1423,"stroke":79,"style":61},"295.0","97.5","309.0",[81,1426],{"points":1427,"fill":84},"309.0,97.5 300.0,92.5 300.0,102.5",[74,1429],{"x1":1430,"y1":1423,"x2":1431,"y2":1423,"stroke":79,"style":61},"449.0","463.0",[81,1433],{"points":1434,"fill":84},"463.0,97.5 454.0,92.5 454.0,102.5",[46,1436,1438],{"x":48,"y":1437,"style":120},"187","the query is the cost, not Excel",[10,1440,1441,1442,282,1445,282,1448,282,1451,1454],{},"Recalculation cost scales with the dependency graph rather than with the sheet size, so a workbook\nwith a few thousand volatile functions — ",[135,1443,1444],{},"NOW",[135,1446,1447],{},"RAND",[135,1449,1450],{},"OFFSET",[135,1452,1453],{},"INDIRECT"," — can be slower than\none with a hundred thousand simple formulas. When a model is slow to calculate, the fix is usually\nin the model rather than in the automation, and the automation's job is to avoid triggering it\nrepeatedly.",[123,1456,1458],{"id":1457},"conclusion","Conclusion",[10,1460,1461,1462,1464,1465,1467,1468,1470],{},"Refreshing is easy; knowing when it is finished is the actual work. Prefer synchronous refreshes by\nturning ",[135,1463,376],{}," off, fall back to ",[135,1466,365],{}," when you cannot,\nrecalculate with ",[135,1469,545],{}," after writing formulas, and refresh pivot caches before the\ntables that read them. Then verify — a timestamp on the sheet and a comparison against a known\nfigure turn the silent failure mode into a visible one.",[123,1472,1474],{"id":1473},"frequently-asked-questions","Frequently asked questions",[10,1476,1477,1481],{},[1478,1479,1480],"strong",{},"Does RefreshAll wait for the refresh to finish?","\nNo. It starts every connection and returns immediately, which is why saving straight afterwards can write the previous run's numbers. Follow it with CalculateUntilAsyncQueriesDone, or poll the connection's Refreshing property.",[10,1483,1484,1487],{},[1478,1485,1486],{},"Why do my Power Query connections not refresh?","\nUsually because BackgroundQuery is left on. Set that flag to False on the connection's OLEDBConnection object before refreshing, and the call becomes synchronous — which removes the timing question entirely.",[10,1489,1490,1493],{},[1478,1491,1492],{},"What is the difference between Calculate and CalculateFullRebuild?","\nCalculate recalculates what Excel thinks is dirty. CalculateFull recalculates everything regardless. CalculateFullRebuild also rebuilds the dependency tree, which is the one to use when formulas were added by a script and Excel's dependency graph may be stale.",[10,1495,1496,1499],{},[1478,1497,1498],{},"Can I refresh a pivot table without refreshing its source query?","\nYes — call RefreshTable on the PivotTable, which rebuilds it from its cache. To update the cache from the underlying data, refresh the PivotCache or the connection instead.",[123,1501,1503],{"id":1502},"related","Related",[1505,1506,1507,1514,1521,1528,1533],"ul",{},[1508,1509,1510,1511,1513],"li",{},"Up one level: ",[14,1512,17],{"href":16}," — the session skeleton these calls run inside.",[1508,1515,1516,1520],{},[14,1517,1519],{"href":1518},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Frefresh-pivot-tables-and-queries-in-excel-with-python\u002F","Refresh Pivot Tables and Queries in Excel with Python"," — the same operations through xlwings.",[1508,1522,1523,1527],{},[14,1524,1526],{"href":1525},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmoving-data-between-excel-and-databases\u002Frefresh-an-excel-report-from-a-database-on-a-schedule\u002F","Refresh an Excel Report from a Database on a Schedule"," — replacing the workbook connection with a Python query.",[1508,1529,1530,1532],{},[14,1531,673],{"href":672}," — what is possible when no copy of Excel is available.",[1508,1534,1535,1539],{},[14,1536,1538],{"href":1537},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fvalidate-an-excel-report-before-sending-it\u002F","Validate an Excel Report Before Sending It"," — the checks that catch a stale refresh before a recipient does.",[1541,1542,1543],"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 .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_b0D, html code.shiki .s_b0D{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}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}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":133,"searchDepth":190,"depth":190,"links":1545},[1546,1547,1548,1549,1550,1551,1552,1553,1554,1555,1556,1557,1558],{"id":125,"depth":190,"text":126},{"id":157,"depth":190,"text":158},{"id":369,"depth":190,"text":370},{"id":460,"depth":190,"text":461},{"id":677,"depth":190,"text":678},{"id":744,"depth":190,"text":745},{"id":923,"depth":190,"text":924},{"id":1189,"depth":190,"text":1190},{"id":1207,"depth":190,"text":1208},{"id":1332,"depth":190,"text":1333},{"id":1457,"depth":190,"text":1458},{"id":1473,"depth":190,"text":1474},{"id":1502,"depth":190,"text":1503},"2026-09-04","RefreshAll returns before the data arrives. Make refreshes synchronous, recalculate with the right level, refresh pivot caches in order, and verify nothing went stale.","md",[1563,1565,1567,1569],{"q":1480,"a":1564},"No. It starts every connection and returns immediately, which is why saving straight afterwards can write the previous run's numbers. Follow it with CalculateUntilAsyncQueriesDone, or poll the connection's Refreshing property.",{"q":1486,"a":1566},"Usually because BackgroundQuery is left on. Set that flag to False on the connection's OLEDBConnection object before refreshing, and the call becomes synchronous — which removes the timing question entirely.",{"q":1492,"a":1568},"Calculate recalculates what Excel thinks is dirty. CalculateFull recalculates everything regardless. CalculateFullRebuild also rebuilds the dependency tree, which is the one to use when formulas were added by a script and Excel's dependency graph may be stale.",{"q":1498,"a":1570},"Yes — call RefreshTable on the PivotTable, which rebuilds it from its cache. To update the cache from the underlying data, refresh the PivotCache or the connection instead.",{"breadcrumb":1572},[1573,1575,1578],{"name":1574,"item":445},"Home",{"name":1576,"item":1577},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":17,"item":16},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frefresh-excel-data-connections-and-recalculate-with-win32com",{"title":5,"description":1581},"Refresh queries and pivot caches from Python without saving stale numbers: BackgroundQuery, CalculateUntilAsyncQueriesDone, CalculateFullRebuild and a verification step.","refresh-excel-data-connections-and-recalculate-with-win32com","getting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frefresh-excel-data-connections-and-recalculate-with-win32com\u002Findex","how-to","neCFOuMBTo7zj5nE9G3zDj_vaHHyVKm7wKhr9mrDl5k",[1587,1591],{"title":1588,"path":1589,"stem":1590,"children":-1},"Read and Write Cell Ranges with win32com","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fread-and-write-cell-ranges-with-win32com","getting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fread-and-write-cell-ranges-with-win32com\u002Findex",{"title":1592,"path":1593,"stem":1594,"children":-1},"Run an Excel Macro from Python with win32com","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frun-an-excel-macro-from-python-with-win32com","getting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Frun-an-excel-macro-from-python-with-win32com\u002Findex",1788710154301]