From 53b49b55debe4ce2c04d689c3605d3cee1b24a51 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 4 Sep 2026 11:51:35 -0400 Subject: [PATCH] docs(mqtt-notifications): document the last five settings, with real renders The README explained the MQTT side well -- payload format, topic wildcards, five Home Assistant examples -- but never listed the plugin's own settings, and had no images. All 20 schema leaves are now documented, verified by a token audit, with three renders. The five that were missing: mqtt.client_id, mqtt.keepalive, text.scroll_gap_width, update_interval and display_duration. display_duration is worth its own note. The core reads it as a plugin's screen time (base_plugin.py:406), but this plugin overrides get_display_duration() and returns display.default_duration instead, so setting the root key alone changes nothing. The two defaults agree at 10s, which hides it until someone sets one and not the other. The README now says which one to set. update_interval, by contrast, is live: the core schedules update() from it (plugin_manager.py:809). Both looked dead to a grep of the plugin alone -- neither name appears in manager.py -- and grepping the core is what separated the two. Also documented: the two font routes. text.font_path takes any path, customization.message_text.font offers the bundled faces as a dropdown, and the customization block wins when its font loads. Colours come from text either way, since customization carries no colour keys. check_plugin.py passes 8/8 with no FAIL. Carries the docs-tooling changes from #423 and the frame-runner --display-mode change from #441, which these renders depend on. Co-Authored-By: Claude Opus 5 --- docs/assets/mqtt-notifications/appearance.png | Bin 0 -> 16126 bytes docs/assets/mqtt-notifications/hero.png | Bin 0 -> 792 bytes .../assets/mqtt-notifications/panel-sizes.png | Bin 0 -> 10715 bytes docs/assets/mqtt-notifications/scroll.png | Bin 0 -> 10324 bytes docs/assets/mqtt-notifications/shots.json | 333 ++++++++++++++++++ plugins.json | 4 +- plugins/mqtt-notifications/README.md | 59 ++++ plugins/mqtt-notifications/manifest.json | 11 +- .../docs_render_support/_docs_frame_runner.py | 15 +- scripts/docs_render_support/sitecustomize.py | 55 ++- scripts/render_docs_assets.py | 3 + 11 files changed, 472 insertions(+), 8 deletions(-) create mode 100644 docs/assets/mqtt-notifications/appearance.png create mode 100644 docs/assets/mqtt-notifications/hero.png create mode 100644 docs/assets/mqtt-notifications/panel-sizes.png create mode 100644 docs/assets/mqtt-notifications/scroll.png create mode 100644 docs/assets/mqtt-notifications/shots.json diff --git a/docs/assets/mqtt-notifications/appearance.png b/docs/assets/mqtt-notifications/appearance.png new file mode 100644 index 0000000000000000000000000000000000000000..3724f0edf74e4374b9125537df7f554a04922451 GIT binary patch literal 16126 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 HDWM4fnxL#x literal 0 HcmV?d00001 diff --git a/docs/assets/mqtt-notifications/panel-sizes.png b/docs/assets/mqtt-notifications/panel-sizes.png new file mode 100644 index 0000000000000000000000000000000000000000..606a053a1f8b85cc233fca74cb319613530a8795 GIT binary patch literal 10715 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 bwsA%E20b38gwl z_~DhYQa68Ye7s2h3~1ND=M%A zM!eNy1mX}*1ktF5Kn%(tZt%YO<92c7m)nOJ#FwXE-Oj8*AmH`~G6J#u^s8GKV)=G? z#O0@hv=eUgW1Z#Y<@ODc5nKbrjrDkp&*;Z-8iCrN5SwTSol6nUDI!iAE@tdTA2#iGz?7xRh4W93897p1@d)3uBZu( zcLHhN%GY=P*2*=hIPT|tKFSqYX^NV=9#D@QSBFQFMF{QUIV zK>a{NlyIg=?TDCPZ-8T9U?4SBx4YC$7QN8ofitzT3T(TWr0${T-cvR~ucQ$$C`HAm zW;y2LT_tN2FJHM*&1L1#EB%a(jibWDUAl^$^czB@RI8?H`f9^6UcA_`eS4@Bmp!WP zJJykJTd9OZHq_OXTz!9O(%e}rmKTjD!AaRxT+gx~*tPMr3TuZwU2~Xqs}VEG z4>s-T7c5Oi_257z-6bxZ&lz|Sy5C@9^qxI?)+?PFY0EOba%K3gxa?U|Q&I6P=d7%@ zlz$tSkgzbIAY8a9DlewkoTBXq>oN(`8v9(~Gx|6-b_2RhC|J_=9b0&JL~@A9L~h@% z!yQQjO<1{dW$j(@(YCDChl)oIx^d(WRg#|wE(m>Xmwui<$C zes73UP(WX0RP%3$tcb)Gj>7uNJTG3n*avFhT(-f4DHS;{;IquB;nD~r$p+!+emSk{ zza0GlGu&Of`TJeABC8df@u-mc2b*;R=gOF=LC&;%+jHmUI_#s`JqO*oOZD9!Y&qcE z|N34G(m3^ii#a?Vr4ba+{?m0bj+Erh=<`+n)BSbf%8^~L!Hxp^rlzJft5@ff=DPyf ztUFS6xW%D3WsAE~g@uI^RkM_`va%dPHJ44pplXWZA3yH(V+1#pD#bB92iIIMu+v5S zu+V337O~~P{lE}?-zqQ`#n7%2moOUT{q`~Q( zPTYSF>(i2Wz$EHzQSs|mxR2| zCba+5D^W0rq=yeZU#*nUSsaXs4r8<0N4cW}NyWV62eo=c6|hp(b(Dt8S%T`1eq$Z$&?9gN@cQ1pd)-OwkM{}k0c`M( z(x7le4@R|AjhtUk!S&A{lnGT{u;6G;5gtrBM(Olh3I)eZ@e72Q|8g3oB z%&Is^8{WHM3gHdDpW z;^Jb*)PUg_W4(aBJ2Dil(OlN*&qOlcP3);d(_>IiyO$SU+4|9?(Fv#!0m#0V*u8k( zN6;>Qz`p*06#Dje;&3YSy+M;^?j_rD<-vKQ#h$)==?N3M(DEZ}idJES{W%TtL1VL2 z6w33{p~Ht?Wt-*v_Wk@G&y0K961oc6()>Kt%~FVfS^<($DA%`&NKyLTsR zT)QN8vF^U~$#_+jpMLsDRn?B@PEXile=KtAX7nZ$jyKg;8^7D~EL`W#6?7k+3ge?w?}RS5c2}RerPPuAMtScRzSn zU>dJS?mleCQi1aopYI7dD8&5n*&=8qd1QEW>ubBnVSLt|sxl9qZB zula`CR{BgVUS(PUom$B_p4fUk6=|ZYK zxoQqpN6NM~G-TCB1jGn6vR=M?=}xae1&{Ozmy(30dr10tRUWqeZFW&`U-_Qf6Pt8~ zB`qBsglE0X8gli%H4UV9R669CJ2;41?8Y1n`q+GYgWg`Rwm}prrjulF_Us2o&2UE^ znq6KO_|jpXvPwQ!d%(Njt8T120u zgoK38t?9Wo1-$6!v~=U7z#~z1-c0PvtM8HRd1hv2qA1sUy{U8NoC)3553SX_{_ve4 zPpU#fOni|39cRKw=HcPt{2|d#vCRaUT|hZi1=UmPHi}%s6mG+)@?R;huW!$>@Mt8V zEDLn}YVVRIE9|GT?g8Em=*!Xwf=*9;GUJqD&dVk%eOP9W*@A0pj=^-MkJ2S5h zh^!FJvn@*R7t~7d2DI06Cg`R%HoU$n@*Szf{Z8Brlu;MI-GVQ=mep21I%c`gS+5(c zSPC7m*cX-6m#}9~3GO=6)`gavsx?IQ8C9C+Ulh92Y_yrTEu#r`UT+)-4?=eC+_^Xr zge&wKx_?@BmMgib*m>x_Iws`B#oA*l+=V8CTXVFCr;`e(QBHJjRd296z1V4>Oa^ak zObDSp42oCb=Yj^XZtZvZI5s0;@gP{xdFxhi^twcKs>T?ITzdWb^*)9s@uV4!GHZbd z+h2+tr=pg^1_xu6PA3WmY$$HCb0fKr9`$b{m^qk;qMD^Gt|#xGj(GIwksfhw{QYb_ znYS2aV*HYI#tf5#06R#DQov~tf4#u?n724e%LD&>?O1&$#Q51^il*nUmpco0Jk{Cn z+A)O@G_+_`LN*2uV57YWL`BDIzJr1^`Az7B(WTZ|qU%moRZ5F(*RO|bJ@!)}+d;9H z80Mt!Ol$Y-+q<{Fzh5swH>b>B2no&=ia(jZBVI82=IE^ zVS|i}M^6IBlG4w*Yg_gSvbg@CCh%u*!Q3~u;Hs(;*DjsB`tH|@Z7VgekDNiH(Pw8+ zjAFar6|-W@tij`BZh)aPE2uSn0U=2$kDdf-6b;oEo9aEs?N=Z5pfyNPDJ zJt_y>Xp|J`kREbKh?ke4SYM{-U8yd+>(`d%;}5!a{B%Ht9nKz&YT~(QTw$To`B6EJ znBWrSojc3>bC%{Ovd)J>hEd#BoTBY}PNyt0Gjry)vhwbqe(BKwxlht=Z;0Q}P+zy> zWL{Fi^>_yH|-7Hr6^)?5B7A}y_?IyZuUmtmQ zky+@-nMlk9B|u_cB$2s+d8$(Ny}gA#q)ItOvJkyt}l-wLROdJN$^4sKnM^A|k>gA|k5Nlr=RM zWm^ui5==lI>LeV$QOJkbH^&XCo}gqh6OtdDWp9mYG8`VftY-PhyS<&ZcGE%{6CYf` z(ls&8uGlH()0q>AId?w6j*M#$GvhVR$mW@~w`!gOmjb6Og-)xF@SE&ij}Gu*l%z?l zMD%Uo_t{^Y7J#zXLoUQ;)mMq1c(YS3e`J$_f&yD4Kvs|%n~Zg6pn&^=a9{qZCfwQo zf|awCiOEI5;}*qOqKn^&K?)>z;I4fqyv(pzY?!iEdU|?rTIVsjeca;N8MGM9(U)L~ z1Uc~U$_8Fh%burgc5Uu{_~?<>myxHL=2{INgJNsW+S#Sz$uDM7tKWWg3we6&BWWzP78lPYZY^_ee;eLiB4nWCN`t#ghnl>tOSC}si?c$?7auFw`S=jW^zrUJ z2^Mr$OwiDfLhr#(<&J_gF?){0`t6j%&ay4stFoxZ3W5GY0UtkfIi%3@u#cqb&Ye%@ zKFX@It9KLyMU}XitJyw^dH8VZG)=x06vr2>Z$OT9R0qGsD1}L7_pWC@&rbRP z{))9AD*2c+%|5$fOxfR$g^voz%a*Wf?<&ceT*3&=A5%Xu>lZGgvN9tM#xjK-l)2;FlZSO<7xObk?}}4H^Pv? z@+CA8V!nE*Xscq^l1l*-9Nu%#gm@dT&P68XrH)_ci9!ivU+i+`KH#)-X6aeAx*uaU z=1NOqTztF-y@K6B!|2vbmT1Lfe2aLsT;_@)+uI4VYOaneMQ-eS$1*ENj)g^eUooB) zM<6Ia(H!O_dIKd9Qe)U}|DOu=KPvNoDb&A8g8u(Vz<+3hAIQ5!{s;I$<4OT?&=TsZ zC@v%e39T1`smc3FW+*8!<4Z$vh*$0Z*v#KK#*GV1`_r8+D+D-^Z!P{EV2B~Fz;ivn z-B`y;V&r(IaouK|AAYr8U^6oL6@HWdE}S1F**I7n$!DTdHHCuYd;)%;pduj$AQ&qrfe9lIronpJd#>xJV}*@5Y1lbWS*C|l-wN`kS!P61O8 zT#Yk|qB3*}gw82%go030Py9eu8JYbWM>guRxRvua(DJo-`;Jax*CIPrU&gpe9@n^y za#;Ht8C9+A_$i~Ig8Mf-^A(SgqC8KD>MLG&uxsh)jeV_8c#~94xwmj`VeQ*btbb@j zquNQtm$l!5w}X1$d%6axR#W*KGWsPzsQdO9m!|0TJy>imnRGW^zi_HICWI--DqFv= z3}#%WM_=J0P|~#-Be<9tH)4~YI)325%#)%^uaNyIA_PZ z{-%jPBxh?Yh}^i~_|um&{Bzp$clh+ROumLsf2z}8t?yr!`zJW-uP69JOkYX(3-S8X zNtai+OxV6+X#aBi6+`>$NzbHocm(i#o{&_2L>y|VgS4#%XyUgh@CARet$!c#cWC#= znp2FfP*YO?kpTwE4AIGk3{)tX6sBr=4;MN#r2<*;n6rUn&5j0uF+WP1EN7-RH8<-7 z+|@F{aVi;5(quZdrUQ4evqt>p_Xrb@!7UO1;pOC4>^gXGesD7@^lDGpbex6VW=PsF z!Y;rE>$V;94-i4%78c~3p*kO|f2JR9XlS@w4B^R}9RJd(e^BVJK__IMjq)VPD1IcN}Br+8X5}h%7sAk ztqENfA*(JAl`zO~GrDY+qTSls8U*yT)U8{p#sfE-^x?yYG>L8~9f1z|O!h(o#D$^r zfQkTl5>wo_Z?@e6GY+%_XrYabO>6pTH2{A1)Bb_|^eZwjWE`+q5P=JT6nG0tS|Fv| z4=2^?8X6Q9JAr}FU!05wK&9V8VA+7wVLbr@^$`OD1KX%=|FA{|L3zj9 zvggukK8K-D&Ij2MU1$@n#wa+PiN@${_LnXxol1C@Wt!34+$=78@OgT=1n5g~@qVvW zyY}pvZa@j9S4iqep%b+ICW@U}p8=$41>~p#2}`J_kk0|QGW|46{w^dbb`|eD*A>2y5lVL^N*DmJceo`9GL{bbeR`lFOsb3M zfx7V9jcxcb(BzG(Hs{Zu*KqpqJR8F&2)TsAx4+YO7gB{hV<nIo+8Ug@HvkP(~1+vj%YGmHb+c@PTVwRSc%WT-jrU%ZQ58O+kuk+-| zlarN`)gH;j5@%arNWe6&b`}l=FjHjEkVpf`avD06JJGf)aW~8Go8^?FmR!Ap|FiUD zK;Ys~{1LHeLUSxZG2A zZ5f;YeBscgOZ{n4dfXfgf7eXAd7hQNZgy^NZvQ+G@onmf2i>-mn}E^J&CQ+p%x%5a z2H>wrwKBJ=iuc4Z)w1IHXH91EY~!}5HeQ>2@Z9@tgzX3^%5OOspp+3kfE$$|tCmJL zzImhRKXpN@4>*)7_!Bg`28#cw!5HIX@*hDOqXe>E=tJlzfH6U0X_9tKUm{lv=)5?P zjgUzaXQG$1#32j$bDvv}h`o991`t>T?{OG-LSKN=z+Zr|Z`SemZ0l6DD$Xn`(+KsX z>TfP0vs4U>ZyR8I=rw^TOkd|UJ+Vs$BrG)s=$C)8Y1O#f-bk2T32Hi*O`Bh z&^R*d0!jniaE)TmwFWtui!}3AndsKNF#xp$TA&U}4{j9nCIgu7GEa)d@cLyGL%~2F z$f{3(CW$K+doTp0-R|LEb`cX3V*tG0d$<|?*Qhv27J_aFOpSG=(Z^4o3>tpos0Zv- z#?D{ygRY7F^j}d&++fN~U)coKrqtC^?5RK?s|Ogx6ZEiJVRSago{3B;%7 z7D7!;4Fri?+scDpDN0y=cqMSh0xC+#`|aimu!Lnue3VMX%@;LR5{^a5+nSpngaFpK zln<`qablvCPbSzL1&<;F!2~?77%Hu7z6^SQ{q@(fGJlW{80Th9ughW=c?;9EVUn1_ z%O~J6F?oMqT6yE{^JnOx4F7=$AibXNpXtN(;>8OLcDioleVT&*r<+GWITue!CY?Hs z1*eo|5Y<~990Y&H3+6J8D;+bVXPFTnA%4qJPPZ)DXbzpQg?Ea77W@B5IFu6&jzvNU z)KZDxsg(Xv$nlVb3K5r&=_22JFSsT}4)*vP{W53+RQ?dEU8V7>gisd*GlKYqmlkfCDe-H0u8 zLE0;I_C)J&)vWA6*X!@=#-T%DRvVzY=sJCKg=n^(i34z6zNjFR(-KTU32y;i>gXtI zT!?zGwZwzb3z*b2@Yn0J!>7%d^J&@>*|wGQz>*Y0p9)7A5D7_uR;6lQh6%mvW*lne z1)Y&{-5v_pnd~R@G(8V5ueW)@OZ4mB_{fwB2)#UhlUh@gq<$)qb@SdmACC!dm&qiL zao7>PlH64gykufbMvF(~ajU{eR)PUX6rt(|6VWX9JtE7B>TnWu^27<-in$_nyO0;T zxnnGM+V<^j0nBltd4A*g`_X#fa^Q~k4ff)5G31F9BJ%MD8Aiv_us$BQ1syPSI3J|? z>WC)3rF$nwF+xC0(>Od77U$2K^-hL9DdbL+) z;AIe6EZ&ZLo80n*Pn5i33$n~|a>8H>^puo_Clpb3l~>r^F}=-MvN7!Sdc$ry${m~rBEyQmnB5lJ z2CX%D_MEV@=YV!8YZkYE(gS3_YvXnnx8ZdtYvX#vEn=_5!c=Yf%9Tr{?kyOyyKUOB zh<$r0kr5Fo>VA})D|P^LUN!U-`VeMOR{uTETL+0%c)rW&07ir8iZlM%aAhX};42(gRKuv-@J@*HDNUrOQ zBUb=|-m#;uy~T~*)t+aSvL1bvp5D?wrVn@~RI0UbNK-nR(--Cq>CDV5F&9HDJrR>0 zXFzFe{3Sem>B4+&e?K1%EKzfSKOb+yB`{(`~NwVidstpYATVH>mSTdG_qt^XG$7XmRFd^e^0_(jU&Y?qU>$2n9fj z@1wwhRQCI}mqR)=`svke$aXwhhj~$Y$w|@Id;!E-l{%LUjvnjbu-`7PSw_ZUF~C$f zT_w~Y5URVRH3^xb=`;cv*b=y2Grg+28N!H2-I`EI>xY@Q+w!a@#llxXSGwX>&8?QC zgVl5Wvkk$a$;o3oPI~26a1%vVgv{}Zj4epav5hJoBi9CkS~J`krFqcFaO<|)itfNv zp)`l^!aErm849&JIeKi=`ltROhKA1V)3vm2~ zY}bTj1wX??$@v&Adq(KOhbA1q`QinQQRL_c1Z&ssQv{5#qxv*sxI@Yf^?BWQPW0ib zZM&7Au{j?WRadu^wBKn9Pv`Dt^$hcni%pQ901rZ^k>!4LTJ4-y*d%|Kd6{zjA zXEi*Pq$59h&?X+;e-C}K{IL#AJv~9sM>o~7sb}^{j_nzdjonk6=YxaF+P;xd##e2f zc(q4Uti>+UyAH_ZkCi#K)=SmMjM8iY5CYHLt*org!p_Y68G($~cI|li6mlw>V=uIG zJ8}c+cf{M6&$bE=Hput2fA8~fi|X<&##yvSRIl&o*Jx8h|3OJ(em=_riYuk+#jXg_ z(%eXAPH{1?Txsa}%&KoevBa3E-4z(&O`v4jr5^a1_&187*_>Rfl9_(raUsVz%#Bg% zwm8$%*qsdlQHjK^KTYy@GuCSl5$a_8AoPr|X#wWJ&)!1UdTRbT1hFh*j%;AAB`NUQ ze1?;gNq*<@j{BO_ssY!(VhlG+D7w-s4ruOh`22zDTxbZf`>%kbp8w0S2!evmOTs65 zR!C$Zd>=tm&={fA;<_!xqxk8B$DnPV;Q3?Mf5C+FgG=+Hc(OxN%}RwJxpO|94)Ms0 z=gmpl4QCODrk+GG?6BAxkeV-e$lX-iVSOpH@W*-PyQw7-2q_yr_whtE6A2mw4S5Ds zC1X!QTAI2)>3{j$=EkQyj!AOJMQvp|NB=t|1n1KZ$RNcJ;5QIC48pz=k(_fiXnpVY`&x-f;(r?S zLu~G};Xe5|)%TrDT?T)QL64&P}JvpLmub`_GZie?Zdnd^~JG`oQqH$70m ze}Z+(s{xYzJywviIWcN6LGsV&=wG9${}UeezYg5~`w5aa6)2%|32*RUm^U-w|DUJZ=5lq=(+0wM(xu)FnR>yguc int: "needed when the shot supplies its state directly") parser.add_argument("--frame-seconds", type=float, default=0.05, help="Frozen-clock seconds to advance between frames") + parser.add_argument("--display-mode", default=None, + help="Display mode to render, for plugins that declare more " + "than one. Matches render_plugin.py, so a scrolling shot " + "can name its mode the same way a still one does.") parser.add_argument("--output", required=True) args = parser.parse_args() @@ -92,7 +96,16 @@ def main() -> int: # force_clear only on the first frame, so a plugin that treats it as "start # over" does not restart its scroll on every step. for frame in range(max(1, args.frames)): - instance.display(force_clear=(frame == 0)) + if args.display_mode: + try: + instance.display(force_clear=(frame == 0), + display_mode=args.display_mode) + except TypeError: + # Same fallback render_plugin.py uses: a plugin whose display() + # takes no mode renders its default screen rather than failing. + instance.display(force_clear=(frame == 0)) + else: + instance.display(force_clear=(frame == 0)) if advance is not None: advance(args.frame_seconds) diff --git a/scripts/docs_render_support/sitecustomize.py b/scripts/docs_render_support/sitecustomize.py index 44eafa3a..64fbb438 100644 --- a/scripts/docs_render_support/sitecustomize.py +++ b/scripts/docs_render_support/sitecustomize.py @@ -89,12 +89,59 @@ def advance(seconds): _WANTED = _json.loads(_ATTRS) _TARGET = "src.plugin_system.plugin_loader" + def _resolve(instance, path): + """Walk a dotted path to the object that owns the final attribute. + + The sports scoreboards keep their per-mode state on sub-managers rather + than on the plugin -- self._managers["live"].current_game -- so a shot + that can only set top-level attributes cannot reach the game it wants + to draw. A segment is tried as a mapping key first, then as an + attribute, so both self._managers["live"] and self.live_manager work. + """ + parts = path.split(".") + target = instance + for part in parts[:-1]: + if hasattr(target, "get") and not hasattr(target, part): + nxt = target.get(part) + else: + nxt = getattr(target, part, None) + if nxt is None and hasattr(target, "get"): + nxt = target.get(part) + if nxt is None: + return None, None + target = nxt + return target, parts[-1] + + def _coerce(name, value): + """Turn JSON into the shapes the plugins actually hold. + + A shots file can only carry JSON, but plugin state is not all strings + and lists: colours are tuples throughout the core, and a logo field is + a pathlib.Path -- the sports renderers call logo_path.parent, so a + string there raises AttributeError and the card silently fails to draw. + """ + if isinstance(value, list) and len(value) == 3 and all( + isinstance(v, int) for v in value): + return tuple(value) # colours are tuples everywhere in the core + if isinstance(value, str) and name.endswith("_path") and value: + import pathlib as _pathlib + return _pathlib.Path(value) + if isinstance(value, dict): + return {k: _coerce(k, v) for k, v in value.items()} + if isinstance(value, list): + return [_coerce(name, v) for v in value] + return value + def _apply(instance): for name, value in _WANTED.items(): - if isinstance(value, list) and len(value) == 3 and all( - isinstance(v, int) for v in value): - value = tuple(value) # colours are tuples everywhere in the core - setattr(instance, name, value) + value = _coerce(name.rsplit(".", 1)[-1], value) + owner, attr = _resolve(instance, name) + if owner is None: + continue # the path does not exist on this plugin; leave it alone + if hasattr(owner, "__setitem__") and not hasattr(owner, attr): + owner[attr] = value + else: + setattr(owner, attr, value) return instance class _PatchingLoader: diff --git a/scripts/render_docs_assets.py b/scripts/render_docs_assets.py index fa38f5f9..2ca204e6 100644 --- a/scripts/render_docs_assets.py +++ b/scripts/render_docs_assets.py @@ -268,6 +268,9 @@ def render_shot( mock_path = resolve_mock_data(mock_spec, shot_list_dir, tmpdir, name) if mock_path: cmd += ["--mock-data", str(mock_path)] + display_mode = shot.get("display_mode", defaults.get("display_mode")) + if display_mode: + cmd += ["--display-mode", str(display_mode)] if shot.get("skip_update", defaults.get("skip_update", False)): cmd.append("--skip-update")