From ee5c978b00d36fa8abd03ad6c95db419e7132cc6 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Mon, 8 Jun 2020 16:00:28 +0200 Subject: [PATCH] Fix sync error (pull) when basefile was missing for some reason The error was reported here https://github.com/lutraconsulting/qgis-mergin-plugin/issues/132 Also added better exception reporting for mergin CLI --- cli.py | 19 +++++++--- mergin/client_pull.py | 3 ++ mergin/test/test_client.py | 33 ++++++++++++++++++ .../test/test_missing_basefile_pull/base.gpkg | Bin 0 -> 98304 bytes .../test/test_missing_basefile_pull/test.qgs | 0 5 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 mergin/test/test_missing_basefile_pull/base.gpkg create mode 100644 mergin/test/test_missing_basefile_pull/test.qgs diff --git a/cli.py b/cli.py index 1905f4d1..25a2eec6 100755 --- a/cli.py +++ b/cli.py @@ -9,6 +9,8 @@ """ import os +import sys +import traceback import click from mergin import ( @@ -63,6 +65,13 @@ def _init_client(): return MerginClient(url, auth_token='Bearer {}'.format(auth_token)) +def _print_unhandled_exception(): + """ Outputs details of an unhandled exception that is being handled right now """ + click.secho("Unhandled exception!", fg='red') + for line in traceback.format_exception(*sys.exc_info()): + click.echo(line) + + @click.group() def cli(): pass @@ -99,7 +108,7 @@ def init(project, directory, public): c.create_project(project, directory, is_public=public) click.echo('Done') except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() @cli.command() @@ -145,7 +154,7 @@ def download(project, directory): print("Cancelling...") download_project_cancel(job) except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() def num_version(name): @@ -201,7 +210,7 @@ def push(): print("Cancelling...") push_project_cancel(job) except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() @cli.command() @@ -236,7 +245,7 @@ def pull(): print("Cancelling...") pull_project_cancel(job) except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() @cli.command() @@ -281,7 +290,7 @@ def remove(project): shutil.rmtree(os.path.join(os.getcwd())) click.echo('Done') except Exception as e: - click.secho(str(e), fg='red') + _print_unhandled_exception() if __name__ == '__main__': diff --git a/mergin/client_pull.py b/mergin/client_pull.py index 7b24cfe3..31a3c639 100644 --- a/mergin/client_pull.py +++ b/mergin/client_pull.py @@ -367,6 +367,9 @@ def pull_project_async(mc, directory): # The basefile does not exist for some reason. This should not happen normally (maybe user removed the file # or we removed it within previous pull because we failed to apply patch the older version for some reason). # But it's not a problem - we will download the newest version and we're sorted. + file_path = file['path'] + mp.log.info(f"missing base file for {file_path} -> going to download it (version {server_version})") + file['version'] = server_version items = _download_items(file, temp_dir, diff_only=False) dest_file_path = mp.fpath(file["path"], temp_dir) #dest_file_path = os.path.join(os.path.dirname(os.path.normpath(os.path.join(temp_dir, file['path']))), os.path.basename(file['path'])) diff --git a/mergin/test/test_client.py b/mergin/test/test_client.py index 442de4c1..a46a629d 100644 --- a/mergin/test/test_client.py +++ b/mergin/test/test_client.py @@ -391,3 +391,36 @@ def test_new_project_sync(mc): mp = MerginProject(project_dir) local_changes = mp.get_push_changes() assert not local_changes["added"] and not local_changes["removed"] and not local_changes["updated"] + + +def test_missing_basefile_pull(mc): + """ Test pull of a project where basefile of a .gpkg is missing for some reason + (it should gracefully handle it by downloading the missing basefile) + """ + + test_project = 'test_missing_basefile_pull' + project = API_USER + '/' + test_project + project_dir = os.path.join(TMP_DIR, test_project) # primary project dir for updates + project_dir_2 = os.path.join(TMP_DIR, test_project + '_2') # concurrent project dir + test_data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), test_project) + + cleanup(mc, project, [project_dir, project_dir_2]) + # create remote project + shutil.copytree(test_data_dir, project_dir) + mc.create_project_and_push(test_project, project_dir) + + # update our gpkg in a different directory + mc.download_project(project, project_dir_2) + shutil.copy(os.path.join(TEST_DATA_DIR, "inserted_1_A.gpkg"), os.path.join(project_dir_2, "base.gpkg")) + mc.pull_project(project_dir_2) + mc.push_project(project_dir_2) + + # make some other local change + shutil.copy(os.path.join(TEST_DATA_DIR, "inserted_1_B.gpkg"), os.path.join(project_dir, "base.gpkg")) + + # remove the basefile to simulate the issue + os.remove(os.path.join(project_dir, '.mergin', 'base.gpkg')) + + # try to sync again -- it should not crash + mc.pull_project(project_dir) + mc.push_project(project_dir) diff --git a/mergin/test/test_missing_basefile_pull/base.gpkg b/mergin/test/test_missing_basefile_pull/base.gpkg new file mode 100644 index 0000000000000000000000000000000000000000..6aa18bca5d95b09c07a6517b4ddb8f284431005d GIT binary patch literal 98304 zcmeI5du$s=dcb!@OVpcAat@<9j>k+?lIN8rN|t5$aXwmF(Y#Qk%tsu>H8vY^MJ-J3 zGP|pY@)cK5vU7c)|Fl5=I21t-xU@x!rbUaoy`rf9I-qEQqQw=s{?P+^heHlUdT0{_ z>EjSwXLokVnTwa(1xs3c@51`M=6@7zK9S- zQ8W1Y9DZ7l5dLt)y1?%=MTP%KzfP>ZoUn!4Kh98Rp8unM9MTu~L=S&1@Z-QIqx9(4 zhBim;kKFcuW$0x8w*T^x^L-!C->@fy&n*JG%SZjk3sZx4pVAbuTo#ma?RJ^d#EQV- zMpMMP^|mdRv`tQwR6)_4GD}%Do@Y@$n@l9wEDAg0z48d7tL#c9%c5jD$7b^=lXj;I zBP@Z~_|i3$&D_8V63H~mu_<;bkFxP(j*Y~xX0mw(g^6BBmXOB3Q4vtsr9(K3-V)^s zuL&wD%95%nyeMfGkXcKkdZ@@tlB}UO1XK}J6=|Eigks?+x^a!gnnu#>jWccXmryKv z5wYpz-7f|F$FF0Ir#fN0Bg<8;BD^J3xUE_VtKo$7Qz5!?Xd*1EE$jT{4b+l3Bv7K` zL4ev+@WMIMj?*T63C)-=FO3tJ{XM`uphdRIHEp^H^AX!)OEFJt>;d22(x^W;HATO@ zX!HfAojI?^EW?XmO|i%siy}rQoWxfJlxJVfqjUy83#k-i(=?gRvwF{O#(y<0MocIFU8S#|}xlk^gsBe=0U(gaCU zZK$KsD`Nw}+!RfTQc0+*Z&vU~%JEy8Y+Q5BX5wPbknz12hW)|$dHNpF$r;b7g3FKV zVIy75L6@-OXg3KvG)7faPAs`ww<=0p-PJmJ=#D#-uY1YjP`7M*Ta>k``p_D~(auKR zxO)2N)M{Z;Uw7-OHC_|>3a1DgoVuf;h$$FNj0^;C&iCBHn7q4{_4XLOOZol5g$4Sn zCtDr9EXY+sQ+BwbT-mBhs?q$;z(IFgJ4HG-HbZju@(j&Pw4IvZCD6$>?x1`!y{`Ad zX1A@ngKRC0JF8xpUQ5+(eRj1OJl^XJ3`N@d>=sU4v|C9boxEPa1AVI(x5?u^uFFg9 z4Q~_fscSo}smLAPmXbpQ!CMPmZK>HPrbTeGo;ER@9Nd3a#QQi)7-~G*?+;E-(~X-g z!<5SkZY|>Q3nJfq`IQx+^Yw zxn{q!2l@A&@AC)4Vfx;7t8Q)evMzk?I$2S6L+er@JX_>By(_iLFk%Uh8sdg1C?tYd zFH;iKq9WEb5zmH=5!CAOHk_01yBIKmZ5;0U!Vbo)iK0t(wQ{7WyLi=*oBPMr6}P$UNFAe3-fopr@)%tC(B%)I#l-!QR+x)idz@%4?N)6}uPyJL+LHxfB? z{;aNENzlY1yOv8FeLulw5=*(8Arcv4@J2~t)wtwhi|5aVnA{q^ADBrlw?xj(Eu4?d zEiki-=g-W}FU-!)&7YfN;)VRROg5QczZueV4l$vHnYo$Jty>;3=jLY5=`s3!2Nsu> zPy|8R7K@u9X2wJO{NhYZ7sq=(`J17VP{!g|^2|(Z;q3g}?BeWvY;p1IoR`d-lgy#Jb(ZY00KY&2mk>f00e*l5C8%|00;nqM@e9v9+@~XF?QLHAAPD=tyS=* zjPV5;m0Zov#1^M!&P~P6=VNCt%+Fq!pEf00e*l5C8%|;PD~QTK|*p|9?yc zK7M?(15JPc5C8%|00;m9AOHk_01yBIKmZ5;fx{CRrhOAMx&7~h`~QdM2{`}(AOHk_ z01yBIKmZ5;0U!VbfB+D9JPA1O|9_tfeE;#(6tn^YKmZ5;0U!VbfB+Bx0zd!=00AHX z1bQLxJnf%25$qcrBzOF+13=d@_y||)_y2xG1%A{^ULXSmfB+Bx0zd!=00AHX1b_e# z00KY&2t4)#o<1^h!u;JoYa0N*|Nq#x8Yl<^fB+Bx0zd!=00AHX1b_e#00KbZfCSpV z|4$t~U^b8p2mk>f00e*l5C8%|00;m9AOHk_01$X02f00e*l5C8%|;J^g*^Z#J|e_(u&5C{MPAOHk_01yBIKmZ5;0U!VbfWQ+$0Pg=k z5zPcj0s$ZZ1b_e#00KY&2mk>f00e*l5I7WpvGI?nK;H+{`2UQ4cl^)Be}0r6{o2sx z$o-Mq{;v$3?Emxr?IY*=KA^urt@cpN_(y&394|}_-i>LBSS|}nxpuqEX<|j-syq&= zTW{N9N!#SMY9(G1oH9#UHlAluKATJ=*enV=u}a*hu_pCYxtanCOXQ32FQr6#<1^I)%gN zEm5xE+Nh`~ORA>uqNH7DSE9&ElB}UO1XK}J6=|Eigks?+x^a!gnn%*?jWcaZUqZ3y zMZ~6;cbAX)4XjV~gjJMOLD8Jp_J@_^(Zrfe=h$rikQhmxj}YUR0{-LIv2~v6gz=6n zSGkJtmQXpUZMt%3A~Y>i-asvxLjpaxUD{OevdWoutF%d9LNlgKUK%Gb`+IHHfb#6Cd6drJXCakhY*oeQ#jv_ZZpO8Ft4^_-f>_?v zkSp346^M8Cj_Z`LCf0HPtaGY(M?lM&f?lDfN+fc}J8~(L&gHRdPv=qXHm7M-w3bb- z#@pfU)(_C}T^ufs<9MoQ;+~>Le3sXAV)6Wmm9UPcN}Kf*TJDnjmSa4RtiS z_rkD0I6qI{BUw7zg;NEWAJ@Z1y0)e+VXsYli`whVsEW#oC3oXgMTx7s;Oe0}?oht& zC5uDdvh66#T2*~$EFH~c5xbhourq;q34BxkRV-ONPW zsR>>JoowR{$|uw7dM9f3x~e4oXFRPEMhSDV4pCY^zyNL!cN!l{dPD=DOt z*9&;Q)arz7^0I4rX( z@j{A>Jkj{TK(H{~6_>qSv)|c+{Cm&$`GesweQ&!}x3+p&7e05LtSGyob*T_u$?%-s zmD*((v4owzxFHG(i6GX?lmxY?h&4^biwa}FwW=NtGZkLdxFTL1l=1k6S2OwKYHNf; zmS6-gGBz}^Dn!DQ>r<1}smW4)^4f*T)eDmwuZ5YgByWeKQO54c8D#h1Ox5PNag@zr zJH`cfXHfL+ppP5}@vcqhx_D$DxEk)-!d5Go9l^TnU-b2!lo-C1^ax(Z4(e;!T_0_X zy?fD?aX7{S!}c9veQr~6NRZLPV=mRfquPBUBP(TI67S&Q$g+&4m$gUXjw352JXCoO z-z8x~ltd#7hdDa!E;=@pr-rF-QRC&Yqk}^Of6&h#>F@g{eT({5;Jwjb7~2{7h?*NI z4gby1>pC+798U3T!KEd-F=>EPG(`~bgsWP^%lxK$QDi)~>k@UW4P&T_WGG_f)tsc* z=4^~4L_s=XW%7(x*g+7L@2uS`+AkjG~u;&iL5eUL*T zt8yV^9G=Laklx5412Mi(aHC1(5L#uIlZ910Kt^}hgJj`;vC|^SK3_fPn$sf>=c*I$ zhuEba*}Ah$iRzYW?1q#TJQ!gAXl#hImY%xZ8=E3+iN4TWfF_?MY>)TE_JB)Jd)bfK zBGj%LIwZ^WV?Eg^S#%xFPTTV6Xs71#?osXA5w@lh`(tY+vf1h9da_fK%c1PFEsrMK zN4$CWFj=lY<3D}_@8B(V>htE71lc<{sG)jg(xmCyVK(XT zzV8|;nk0S&cH-N($Mw&`{VloaLrOY1hsZaL&X&Q1*8{j!PGd!|EZlG%whV=-@l zbeJ2F6d@E}$>Tk_Rv!;pQyhF-A&nxEuzkihaj_u zAb&6(ryHlbXr@bg>!rs$+lyAZU{{^=MQT*HPP`K}*qHCEji(}}3a=!y`9eH}@?8Lh zP$8F0CyEHDeVX1QYYAh)CDidosfyP>WJ9cV`Ai!G zhpcOz?%MlenmKuGcbRLZ-TYHzxQ1Jpa0}CQCzv?hM|TTzZDK(0E$d{NOKuyf!kb%y zR20m+NY>@kZJost8PON63>J;*|Nrj`+@S*h82BCh1`i+r1b_e#00KY&2mk>f00e*l z5C8%|;Gq&Y?dzvjPSA-pihds-z(>n8JVX00KY&2mk>f00e*l5C8%|00;m9AOHkDLj;Ct-vmv*CgvmS|97asJD;Hn zLREkO5C8%|00;m9AOHk_01yBIKmZ6lQ3PJ62PRHTy?Kf()~)|PH~#a5{ELq9dr_O3 zR=Y4gy}iAC28-1A;%&YxoWVaAFs;^vVr+J1`o-42z8LAY75sx!Q?gXq(SQG+4*WBP z|KI@xfB+Bx0zd!=00AHX1b_e#00KY&2t0uVUhK!42SeTW4#@xie@F#>9QY*g;S*R% zP#FjS0U!VbfB+Bx0zd!=00AHX1b_e#a1-$PeAJMyPk*8Di}C&cktquQ!2<{Y0U!Vb zfB+Bx0zd!=00BFJUs|J&P`~~X4)U47^n+jF@8jp6e&OGS{tiF?-VE>itG~#E@pJM@ z;NNHPGiHYS9$f6x@Kd=w^(ViLpTBE{!y6C)0zlyZoq)dnr|{1Y!UG5Z0U!VbfB+Bx z0zd!=00AHX1b_e#`0NtU*Z<>v?@?n%NB?1TdE}2rUK;-8A>{wnf!jxZ-}j4s?*;nD z^`8iU2M~A+2sCbljnkBOZ+1ORxg=BsO(6gHVjY9o{WN8g!a7ZPnN6{I7U3hk+fP%L zD<#jvl&`XhWEz>NR^cUxOcm2 z&9-Xz3}4~weyV0BvFc`>U+%11Qyp~bGD@>Ih;GI?(T;*pO?_HiW5WPLw1ERV8Bbs}Ni10bv2BY4O+1?kZHtTs(4(Q%J`4Ta4 zv4{4X-GBSdI@@mrwT?4)cjl&{;lB8Y?7giSu@l%k`l3PT-pf4@itV3J?Ei|;{>}Y~ zG1_v-7;)}?xoeY?AQld%SB^-+w&#%3yJNAk&Y~>4mWnU2`f>G{jw947 z@l=7$A>#A(;fcXcepQrSb&LX0M)&@!eBB*E60CPh;EEvby%9X+53(%Xh;=@fo~!YS zAZeb*)0=TYYryYvJiRFzib!%vkYpVQn_#nOEt_18XV=lo>^ehA(gmW85#$fX<83jQJX4w_-?