From f9de312610168171419c58b105d63b32e8cadc6a Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Thu, 17 Sep 2026 10:30:32 -0700 Subject: [PATCH] Added test file, Ruff pass --- src/wavinfo/__init__.py | 2 +- src/wavinfo/__main__.py | 16 ++++++------ src/wavinfo/rf64_parser.py | 2 +- src/wavinfo/riff_parser.py | 5 ++-- src/wavinfo/wave_bext_reader.py | 2 +- src/wavinfo/wave_cues_reader.py | 6 ++--- src/wavinfo/wave_dbmd_reader.py | 9 +++---- src/wavinfo/wave_info_reader.py | 4 +-- src/wavinfo/wave_ixml_reader.py | 8 +++--- src/wavinfo/wave_reader.py | 16 +++++------- src/wavinfo/wave_smpl_reader.py | 3 +-- src/wavinfo/wavfind.py | 2 +- tests/__init__.py | 42 ------------------------------ tests/test_cue.py | 2 +- tests/test_dolby.py | 2 +- tests/test_files/adm/test_bwf.wav | Bin 0 -> 153890 bytes tests/test_main.py | 18 ++++++------- tests/test_rf64.py | 3 +-- tests/test_smpl.py | 2 +- tests/test_walk.py | 10 +++---- tests/test_wave_parsing.py | 7 +++-- tests/test_zoom_f8.py | 8 +----- tests/utils.py | 8 +++--- 23 files changed, 61 insertions(+), 116 deletions(-) create mode 100644 tests/test_files/adm/test_bwf.wav diff --git a/src/wavinfo/__init__.py b/src/wavinfo/__init__.py index f7fc1ad..82e9cb0 100644 --- a/src/wavinfo/__init__.py +++ b/src/wavinfo/__init__.py @@ -4,5 +4,5 @@ Probe WAVE Files for iXML, Broadcast-WAVE and other metadata. __all__ = ["WavInfoReader", "WavInfoEOFError"] -from .wave_reader import WavInfoReader from .riff_parser import WavInfoEOFError +from .wave_reader import WavInfoReader diff --git a/src/wavinfo/__main__.py b/src/wavinfo/__main__.py index 5a57a6e..9d1ddf7 100644 --- a/src/wavinfo/__main__.py +++ b/src/wavinfo/__main__.py @@ -1,16 +1,16 @@ -from . import WavInfoReader - import datetime -from optparse import OptionParser -import sys -import os -import json -from enum import Enum import importlib.metadata +import json +import os +import sys from base64 import b64encode from cmd import Cmd +from enum import Enum +from optparse import OptionParser from shlex import split -from typing import List, Dict, Union +from typing import Dict, List, Union + +from . import WavInfoReader class MyJSONEncoder(json.JSONEncoder): diff --git a/src/wavinfo/rf64_parser.py b/src/wavinfo/rf64_parser.py index ccdb904..92d3937 100644 --- a/src/wavinfo/rf64_parser.py +++ b/src/wavinfo/rf64_parser.py @@ -1,7 +1,7 @@ import struct # from collections import namedtuple -from typing import NamedTuple, Dict +from typing import Dict, NamedTuple from . import riff_parser diff --git a/src/wavinfo/riff_parser.py b/src/wavinfo/riff_parser.py index 8bb8289..ab317b3 100644 --- a/src/wavinfo/riff_parser.py +++ b/src/wavinfo/riff_parser.py @@ -1,7 +1,8 @@ # from optparse import Option import struct -from .rf64_parser import parse_rf64, RF64Context -from typing import NamedTuple, Union, List, Optional +from typing import List, NamedTuple, Optional, Union + +from .rf64_parser import RF64Context, parse_rf64 class WavInfoEOFError(EOFError): diff --git a/src/wavinfo/wave_bext_reader.py b/src/wavinfo/wave_bext_reader.py index e10f723..56e7112 100644 --- a/src/wavinfo/wave_bext_reader.py +++ b/src/wavinfo/wave_bext_reader.py @@ -1,6 +1,6 @@ import struct -# from .umid_parser import UMIDParser +# from .umid_parser import UMIDParser from typing import Optional diff --git a/src/wavinfo/wave_cues_reader.py b/src/wavinfo/wave_cues_reader.py index 039832d..fbe51b5 100644 --- a/src/wavinfo/wave_cues_reader.py +++ b/src/wavinfo/wave_cues_reader.py @@ -9,10 +9,10 @@ https://www.aelius.com/njh/wavemetatools/doc/riffmci.pdf """ from dataclasses import dataclass -from .riff_parser import ChunkDescriptor +from struct import calcsize, unpack +from typing import Any, Dict, Generator, List, NamedTuple, Optional, Tuple -from struct import unpack, calcsize -from typing import Optional, Tuple, NamedTuple, List, Dict, Any, Generator +from .riff_parser import ChunkDescriptor #: Country Codes used in the RIFF standard to resolve locale. These codes #: appear in CSET and LTXT metadata. diff --git a/src/wavinfo/wave_dbmd_reader.py b/src/wavinfo/wave_dbmd_reader.py index 7336604..e471f3a 100644 --- a/src/wavinfo/wave_dbmd_reader.py +++ b/src/wavinfo/wave_dbmd_reader.py @@ -7,12 +7,11 @@ Unless otherwise stated, all ยง references here are to .. _EBU Tech 3285 Supplement 6: https://tech.ebu.ch/docs/tech/tech3285s6.pdf """ -from enum import IntEnum, Enum -from struct import unpack -from dataclasses import dataclass, asdict -from typing import List, Tuple, Any, Union - +from dataclasses import asdict, dataclass +from enum import Enum, IntEnum from io import BytesIO +from struct import unpack +from typing import Any, List, Tuple, Union class SegmentType(IntEnum): diff --git a/src/wavinfo/wave_info_reader.py b/src/wavinfo/wave_info_reader.py index 2b62ff5..9a9679d 100644 --- a/src/wavinfo/wave_info_reader.py +++ b/src/wavinfo/wave_info_reader.py @@ -1,7 +1,7 @@ -from .riff_parser import parse_chunk, ListChunkDescriptor - from typing import Optional +from .riff_parser import ListChunkDescriptor, parse_chunk + class WavInfoChunkReader: def __init__(self, f, encoding): diff --git a/src/wavinfo/wave_ixml_reader.py b/src/wavinfo/wave_ixml_reader.py index 2278fc0..4d74f9f 100644 --- a/src/wavinfo/wave_ixml_reader.py +++ b/src/wavinfo/wave_ixml_reader.py @@ -1,10 +1,10 @@ -from lxml import etree as ET import io +from enum import IntEnum # from collections import namedtuple -from typing import Optional -from enum import IntEnum -from typing import NamedTuple +from typing import NamedTuple, Optional + +from lxml import etree as ET class IXMLTrack(NamedTuple): diff --git a/src/wavinfo/wave_reader.py b/src/wavinfo/wave_reader.py index a05a0bc..bccc6e5 100644 --- a/src/wavinfo/wave_reader.py +++ b/src/wavinfo/wave_reader.py @@ -1,18 +1,16 @@ # -*- coding: utf-8 -*- -import struct import os -from typing import Optional, Generator, Any, NamedTuple - import pathlib +import struct +from typing import Any, Generator, NamedTuple, Optional - -from .riff_parser import parse_chunk, ChunkDescriptor, ListChunkDescriptor -from .wave_ixml_reader import WavIXMLFormat -from .wave_bext_reader import WavBextReader -from .wave_info_reader import WavInfoChunkReader +from .riff_parser import ChunkDescriptor, ListChunkDescriptor, parse_chunk from .wave_adm_reader import WavADMReader -from .wave_dbmd_reader import WavDolbyMetadataReader +from .wave_bext_reader import WavBextReader from .wave_cues_reader import WavCuesReader +from .wave_dbmd_reader import WavDolbyMetadataReader +from .wave_info_reader import WavInfoChunkReader +from .wave_ixml_reader import WavIXMLFormat from .wave_smpl_reader import WavSmplReader #: Calculated statistics about the audio data. diff --git a/src/wavinfo/wave_smpl_reader.py b/src/wavinfo/wave_smpl_reader.py index 483cf2f..772141d 100644 --- a/src/wavinfo/wave_smpl_reader.py +++ b/src/wavinfo/wave_smpl_reader.py @@ -1,6 +1,5 @@ import struct - -from typing import Tuple, NamedTuple, List +from typing import List, NamedTuple, Tuple class WaveSmplLoop(NamedTuple): diff --git a/src/wavinfo/wavfind.py b/src/wavinfo/wavfind.py index 7b9b348..ca64042 100644 --- a/src/wavinfo/wavfind.py +++ b/src/wavinfo/wavfind.py @@ -1,7 +1,7 @@ """ """ -from optparse import OptionParser, OptionGroup import sys +from optparse import OptionGroup, OptionParser def main(): diff --git a/tests/__init__.py b/tests/__init__.py index ba1231f..e69de29 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,42 +0,0 @@ -from . import test_wave_parsing - -from io import BytesIO -from typing import Generator -import zipfile as zf -import os.path -import os -from contextlib import contextmanager - -# class TestFileLoader: -# """ -# This guy manages the test_files archive. -# """ - -# def __init__(self, test_file_arch_name = 'archive.zip') -> None: -# self.base_path = os.path.join(os.path.dirname(__file__), "test_files") -# self.test_file_arch_name = test_file_arch_name -# self._gather_test_files_into_archive() - -# @property -# def arch_path(self): -# return os.path.join(self.base_path, self.test_file_arch_name) - -# @contextmanager -# def open(self, name) -> Generator[BytesIO]: -# z = zf.ZipFile(self.arch_path, 'r') -# member = z.open(name, 'r') -# try: -# yield member -# finally: -# zf.close() - -# def _gather_test_files_into_archive(self): -# with zf.ZipFile(self.arch_path, 'a') as zip: -# for root, _, files in os.walk(self.base_path): -# for name in files: -# if root == self.base_path and name == self.test_file_arch_name: -# continue -# else: -# p = os.path.join(root, name) -# zip.write(p) -# os.unlink(p) diff --git a/tests/test_cue.py b/tests/test_cue.py index 27e27e1..4d0a406 100644 --- a/tests/test_cue.py +++ b/tests/test_cue.py @@ -1,5 +1,5 @@ -from unittest import TestCase from glob import glob +from unittest import TestCase import wavinfo diff --git a/tests/test_dolby.py b/tests/test_dolby.py index 9ad0c4b..89eb13d 100644 --- a/tests/test_dolby.py +++ b/tests/test_dolby.py @@ -1,7 +1,7 @@ from unittest import TestCase import wavinfo -from wavinfo.wave_dbmd_reader import SegmentType, DolbyDigitalPlusMetadata +from wavinfo.wave_dbmd_reader import DolbyDigitalPlusMetadata, SegmentType class TestDolby(TestCase): diff --git a/tests/test_files/adm/test_bwf.wav b/tests/test_files/adm/test_bwf.wav new file mode 100644 index 0000000000000000000000000000000000000000..e67e02df5c5eceb2a904057929404e81d75a3981 GIT binary patch literal 153890 zcmeI53!K$uwfFbjFMFsoM@mJJQW22rOsR2^xCDqoAaK$MMi^j3W^5QdUP`bmUyoAS zLB!MR!FaWRAn}4I8W6`z0%jzL+SOa(csa%FnLYP8-?e|UpV@`jVLW1eweu)MdW^cO)2UEPmju%wIV@mta zQ(&R}_o-ip-*!^{P_!F(gtSYJki*q3`THZQ-Jm0&T>>dTVBaMt!jaXk=m=?-oNtHQ zcgf!$S?!9CkakH}I9%<{~o z^|7+5%$CId{m)K(`u6Y9efFZt5;Yd?4j5S6sYMo&%PJNoMCv}PqM~wkS$RnnY)Y;w zTd=5VUXP*u`z9I~+%HMi0a=MvGI_hxuS+j2y*$zKKlpw7Tusq0mAh8%lrP_}H9*RP ztzmi}5qq35=$~p4`s0#C3oe_O?2|IwmdeX2E=^c-d};aIs(JLW`lQFu0fYOcQgDQEN_)PHcl zPWOU?_4i>ZfZ7M2#Q&Q*G3i{q|H=2y?L@Yu@23;b>)pSI75lI|v8ecPmZszw=zej< zqVm~8dMqfpqQ}tU!@~4;uqrF>G4#C$#@}0RkHms`x)XuXI=RYn7y>(UniC<1YZXX3?c^V4zzqf!=S^# z*2>=TZ^c~9&WWH_`|{B7{gGSKI}NI2TqZR9Zd!;a;CwqthioY}P#`I6@ZVd@r1R3j z=|j>oxs!euQyf_>Q713I+BQYytkQ+7@w;@q(TXE^Its^dCw#Ce@I!`g5r zq6ZQr8QBXB6GU%^op(@$K@5n$a|iVS04`O_gCQ>UJAqv4oz7NE!dxoFhhjB5@;EY9 zI|RT?)$(wymI|Ax{Ql`yYu^eT0jnJ%@}_F}L9CX<-&B4diq-5m?1PUrv)Um*c&e5k z#A-<(Pv!TaSj`RxkBrq0VJ%42@?fi_psQdlJ-v2puD*7PuyF$;qsWb4<)Hx&v@9(RV#F&FA-W5y_ zMV&$KU!5fQq*^X@FA!u&die(ns~n(I966XGut;s=P}C_pG<6QiB|2Ex4t0`PaWKUP zR;TlUv1!o5_V(Kw_V!ML&-d@j zuQ^cHcX%3{`4m^juA7<$!5^=RzPPGp-~2R~KsIPl%_-NW(a@hyEZ}~{a?=P_(4!p! zKex01K6TsDXr1QZKk&IUzWU|k1+-iCMjDbJ64(ETq>sCxmHSXWQJBuR(8KM`Ug=rK zHNl4#3(iT0gQ->+J#|WYadAEE^3Ss_Nv9R$ffbgV`g|>Na(~>ibR>}qW7eeqs80;N z;4^GXI?|#J{QEwe{)GuO*nxHn_ot(g>=SFY``8Uhb{v%fJ!;bWic>S-gPmz{`WG_b z4->BZoObCKD0j}z;9Jy(Th)~r$VoqP|CsM(zyV6=*R0AwFQkRCXj2BQU>3gJxHAJg z&}-c3{ilpDfR@K>z}$0s8$Ykh&EyIhO~aXnHZN&G>}3NoxnlwsMt6_OMBt4tH(`hL z3p4qa=CDuC@=O%pIJJ@c=X@&@N*d)let9May!MrPD95k0QHR)Y8@eTvj;Q*6oECfh z#zr6lBNdHuf5yx@n)|N?qep#ME$)&9pUW<)g{;rXS#U;KK0AxDXt(=!V%XuLFN z8L`jrFUmsBPrp>d{RtIWw8$UO-}kL7nmE0(20q!#vuK^}fIk+-4xc}}hIX%cGz&>! z!gbX%Sr~--P(Ju_7T-b-w})%9P#iwAxGpyv4)8~+rN?KZAnhXg*ZOAD3i6;`x8d0+ zf}GsnG1+>S5A?8t0KMSz*DFj&$hbN8#%vnG4z#=Lj% zv;DV*Dv)8HyWh;AAB8`aEjh9b*Pb8CrQd}=Po9`-))W5p%VnGhe||A67Y?vOsuL#K zec{iHnMMy=>^XV#qwwdEsyrl-3ZMD54S3;C#VvXCChEW+3ouTEKbzL) zp^@x!-y?Qk`16COj2<;)jUM%3bzCG0AK_1Ww0-cX7id@bQ`J3+JeU#twDz*0F8nDg zwrP&3q2D+xieAE>m&QiXm+pZ7Ta%;MLHP5+^eB?RgzJyzMlp!+XVTZB7y){?F%nQ* z_;c=crmS^kVf418Q4|#ZY+oKlDda)Br&mT%MEJ95bvt-Mf9<_d2!ubqHrpx;8T{|v z9;G4S&!=|UjF5dW0{4YK-}X&t?#+LI3;&i!k^8j*v!&@m-Z>ZZo;3Yfws!?PvoC> zRsnVv{@g#zW*lFk@9#z#fCztHKG#44GVJsCmklU{Ki{2ZGm)QgWdNYE@aN8p%`4#N zeAO_wfN>)H>9?Q&4w_#hD($}TXVAYIJ#3Lb@(tr7{8_ui`13*E0pBqK;m@r%7!$~_ z56D16!k?O3Onz8F|Msn>m+q;axj!=dAQIO_cbVOUKU;oc_Jkg8 zudFpY3x8f)ZyZdu!YJR;FT$V2_uHgE9#|dwpiKnf&kYZmBvRpp%?0$k@F(l%=B=R) z{JU%|V4Mhl4s0txBiZM^?RH=IGvP6#M@?EsLih-Ox;<(9VZwF7FRfkS&sU!^c~Bp2 zqj#8`!k_3)lOIz+zka9bCH(2V%k;%8e0$w4vxD$wbHXwjKd1(Q@Q3>_fsCf1ry=1F ze7FNERE9qS3x8-ArDzT&$b;g-ALN9RM!6zC1{D6Fmvv$Sf6y0G3xBW!98e9Bun+fz zKiCa=)Q1)JgpcqCJHr`e`CxzA75>mK$b%WN5B-Il!XNq(`E9CJ$6$_L!XNq_ed!MP zGaj&m@P~1NBrpl%NBG0I!U)jAjq!%!!XL&V9IPt~ql`}!6#g)7Q3`p`j**5U!XL)D zgomE-4}tK9c>xp1;Lm)aA>j}62w`L&<`?&cKg>H*DuF-DNB9VTn5XbZB(5@`@P~N~ z6U2ra^PL|Gf4~8VQ4M1FqU6P2dlBOIHeiz+pIOenHHA z;SabCJ#3L5Jm)Ln4>%8JKIjYnLm>QNU4RK>*oXCkhJ-(?BM1X4=viM7Sop)bgHkjH ze>kAH@P~B@%ODb0tzW_))-~)2J=|FDu(R-obr24wT49v+k$w^Wux`>{$O9|ZQwE0c zhjkW7qyks^UHHSg%y>W@__JO!PJ}s1G+Ug^0{-CVgz=N=|KJzFMEHZh zL_@+K{3wKh70RL=0twfAAZjM}1gfN%#nV@H63zvV8DAX;=7zUy3}K5&Ph;BB$^NKNk6Is#eGF zZ_!KmgWrq3bO-$LhXelLCu1L&aD^cT5&qy;V+82ohQE#C!XNx_I9OMr^)tCBDEz^1 zM=9h%JN$VR5&q!kOL*w<{~-|murGiKWbkK%(2($leFVbDKI|{JFZ^NOVM-5RT_N~yv7WvuF!bkYSJ{Qh>(3e&q5dN?)h6!ZY zhy5}Q34ho}LuNyl{mAjz2rT?z-;Gi<2Y>eC0e{%1BMBmL)&5=h!@eGSLJv1I#m>SX z;s9_k)e57;2j~~!4{-zf3wdBgJb```{t#!do=T`d{DFQK{t%a7JfIHziB~XAgg?YF z&`9<{c~H20iM-ig+4)gg?aD z;Eb|-h`-UU@Q1h@@?b{ng9VUN_(L2I`E9CJ$B6Htm+*(UAL7v+@FyM^@P{}d_JIjk z#Seu)#1%0D^l&5Ih~mN@;*fB#u4MiCX&MR&e~4S66!M@Q@k|sE{t)Ms@X!6@nHyrKg5l>Df}Ux9Po!YbLxMnto_&0YKtQBPvk#-ZyRtAn$9=2%LYfmeDgg?8tw8EJW`d+=J6$0T;&tpLq5Loz=H>?$uH))qLJYK74k*0#_u!k;H@wJ03&z=|HBAB8^~=eHnQnhm+)ujm}Y*CS@?Ebkqso_&z_T-!}uwS z8Uek@&-3CKcS{qB3xBqLvk6KX z<$8Zv69yFi+&{&l)`$(aUkq=eBZNP{Kf^d^e!-3V!k@v}MvwZiT5_NfKEj`8|Ii3$ zl;u;jvypa%KX+|vL>|nDeR|y0h@8To4L3K^qD|H6*m>V-Wu@6kRPVe7{L4-eJyES42=;1axzY$S|Ka=APaImf{j8?wdfP%uGAMI{HDda)B zm$x*ai14R)ogo7sSqTE+&&RJfCXm5@MO6b034aD%+<-8$&z~mPec{iYLyVs0XnkSt z2KWenzVd0~&rPnAbF5wA&&*njP9rwlpyY?bp9gmv2ULSTvaKEh;m`D+)pHZ|`SzLr zThAZzf2Q78AI8t$uB^vy!k<<1>ai#PME-fx>anx%XXI$>alW!%E3T(sgg-+*Yo`*B zVV?^>Sx-L-f0m}#L&;CL?)zJuei#0n`BK~_sqp95amI=8XXv_laPxySAcf9_mq z^sq&KECwIp&& zcDK_LnuGsUN5xTG_|q#rj(re`D+b4I!k_0}u~QZ3;r5$7))B&={g2nd!Bi`ZcHLBm ziG)8TtLykV^1$lx+w15@;m^n$>ySh${O%hzz=S_Hm)9{KPzU~u9L9<8XUwEJG?IPJ z8)^52KMOu*^r%Vet9u$B;ZNIf#vdkJE4x^`!k@H=$%FcETX&!qIfXw9U$YsCDWHcK zy@Wqgch;gWX5rg~TWYa`@MrzKwPE~Z++7QS@aOUswJ?EF-xV1>#{ z7SR7uk{CP9q=+OvPueHVCBm8NJnekCpAKDfE-1x^B@?b{nleX8k62hP5JIt6i zRaw8b#n4OmbK-+`yhwMz|LApgv?}~LbyW;WV8Zn)D{RRV{;dC=EeFuUjTTW{`18Qk zHrcH!3#0QYV<;&68B!KQDda)BfitaZgg;}ZwM!2DHRs155dQ2PWv6(M!TP54R{`>c5Yp|Q}C-Z4LCFGyTf7=r^*je~9 zew$g0uh921n{B`gf4V(jlL9jAbN0_{;s}4PTy2)*CtOjGei#1CxWi5r`8i)@uc%?1 z2!DS4gBmz!em%O>?hAi*{U4);E%LX0+xQ57rYtu8e9-q-S6RElpGz(`c_70+^ehbt ze+tS?ejB>gv7;|By@Wqsm}C0V9Q?1p*z6$uiGR)PgGgNe?IN?A@MrkHm_4C~+X++5 z&cdJLCYk+BwL%Np&@aNDCF8Brkq1_ci~Ut7wxncb>M&C zoElgOf4U5-K_l7cydid9__N^)Mvt1b&IpH(@aN?-jXzAdZuq>lEByI?pEG$-A8rp9 zo1DU*Sdqz(DWIQJWO@mI_6#z8F$>>bJ;>}J{27&)SsFjEff0%csR04mPkMI%xFi+u+NLG@$v&*_+!y|^?n957w2nUjAK?#v0{mgZ75{;D zg+KTe$b4q{@~YHCnoR*e~*q3{@@3~ zLGug$ko&?P{6^?$5mwxRkMIXS6V52h2mh0Hg+KVE$b%WN5B@503V-lpk>93jbqxO& zy@Wsbz35AKz#o4&;17N>#(@b}q{1M=AN*>J06pCBw^3a9gC7nD>q=%#|2PT?fAHJ+ zIr5+#{yd5ZfAI4qJoNbg5D0(R7r+EE_+uFw68^A{Kp5GF{RQ`hKkPe9sRaJq^K0WH z{9&I0e?;QS{so4@ANDmcL2S6O-{FVCAND~Iqnb%o+_C>+-^5MS=Uetu{2}~dpVdBk zB9YYn7yB~oDg0r-hMk2!?Bmdguh5tM9sMHwVc$o8L56*hoPHGkuun8gCh&*-BmFM? zVP9#JG=V?tHyJ0wANHYe(EMV5%6;Jv`&Q^-i~Q_o;UoNEp9_CJvJwQsANIvCfeibw zU#2195Bq3@v7yU;x**ju8G3 z2Y`dARv0BdKoL6Df^j1JA&!AY zvJZ`NU-(1Z!v<{ve~5>`NBBdW1pYALO8kU&g+IhqkO%eQM!W?%g+Ih$kRMY(Pke?Z zgg?Y>&{z1wNMroS{~^v3#!qVehqw?-AfsvGMKmP*A&$fySfMiUB?K1!5O+c;n!`TC zqflJV?Fre^afbBm5!G z24|GzL;Q_)g+IjQkOwnjA0$Uk;SX^<q^$I3tCZ7_(R+hrH}{hh-adR@P|03gomE^Cj`PD z;-WBt4F1GRX-N1(92H??AL6Ur7yb};HKl|^DKW~+_!3444 zM!c6F3V(Mt2;SX`mf|5^Tz=Ra%z<@wLre|i41_Fta=to@hgKWqQx`Ok{~ zc>c5EKc4@r_>bp5EB@p8&x${L{-^pGpLPE3`Oi9k_xxv_zkB|(@_#)4S@}Pn|E&BU&wp0_kLN!t z|IqWFJ^$JBpOt^;`Ooe7ho1lJ`OlvJ{C#_Z*z=z~|Jmz5>-h_>|J?rkh1Y*hJb&T! zpY{BO*MGK0hj^;R>pv%+|ML3J3^7_xp|MB|I?a!Zj{pZB{FTDP<@_)Sk zv+{qu{UTimoMNK%no`G*v{ z4)y1G?p!Qy|J3V0 zd;MqLj~w2A>h+(!{xk1SC*{WLKfl%%3-4d{`p;hfIeSG7p<1v1eCntg-n;GfpC2x^ z7vFpR=c6yN7qWZ(XDq<^h1Y-n%8D3qd$0fO^`E`|^V0PCngexS|M}zBH-!8{QWHb| zVez_#kbn5{mWGgj$aBOY|FH7ahLC?aIo=TR4@c)WhWx{^-5Mj2$gW)lT&MSM4EV!) zop`$wZX4WKsYCpyp{x-xRDbonZ#IVW_YF5U(qFj!uZ z)$)6RoSy#(vpIh7Vn54{5=9kxh7XAQ40e`&yv)6wfQCYzH<@KMtY%K`;FR%aX z^`FV~U>))L&(D9NAmsme{b#TL+g)PpS}L`zFV3?{MqY2d;RBm)9MLSdi`etw&DEU z>py$_=W*L&%nPso{Ai2iNqYU~UtMJhGhY9h$U`{)^7_wS|GDkkHSDvz{`0;9d+x^T zKd0@D5r^^m&lrKYobrFX{9K*&E_^;mPrKb-r5C885YHL>4r4*7@Cj27<8|EccU67mms_h||F zhrDMk=b9P3^KdibkgZ;bej~(;93?{qq$Ll|P{_~IjY?-SJ39g?1%<_aO zJpbAApPzg(Klndh{~2(=i@WyTD1hnppS}JwQ-kq<^A?^zU3|Yil!V*Q?%>!5Eq_(< zpBo-3(BOgoh0O(wADzEv{k$MNf9mWkJ|~b$hZP;13lE!v1T*V+CRVb>DWo zul*N!<6-}WJ;VMBJBR%j_7D3n`UQ}e|3iPVPvjh&9-$xEKk^5?2J>+K_1gLZoJQPH zM15s#LC8Pc@)LV<8JCsoqPwi_vDbh0`p-6Wt7Eqcr7a;|%p8u@#_k~+( z!}Q%IiNflIbrt zSp=-S{FA=JlT~m7*}}^`BYJSgpMNv-}^g|1AH<>p$!KtLr8!;HKxl z_CH=1&cA-Mrw&7D|HX5Hcp5tYlK-spFZ^oKDRDoTU-;VqujfB|{Uf^$~Sa%l~0F z7yKX3f7bJ-y?0s00$UH}%le&`SFGpXqdP6%Sn=o4J8YrR`v<@JlzEc!e?0$L{*Tvx zW~pv3@0UwB` z5s35p&tCr-gk)-X{b!s>KGdJV!S?#kUjJF=U%a0;oPY7&-f;fK`+P(EnfLsL_%qNN z;?KMnIK-bX_GmW_U1uy#6!J1LMK#KjTk=9%Q(& ze(C+EUjJFoUvwE(6P~{~a86Cwe>Hy5GL0ELe8@j1?#;Op@N)T=?2r*NGQVCQYcG~n z{f#B#En`slgYyvbe~z0}6Y_sfm{Jq+e}?~yRo;LJ*MGaHCglIbzg7dD!vSvBUtGgP zR{r4^=2!#?{z&Ed&zKSWc>XhodW;{>f0qB_`Om^1&wrNxbKxgD_FqFj+p+(`sfIxB z9~?Ql9=rv9L08_V!yiOlxU*_rJu^%HdH%EKKeyc76!u@9|1AH<^Pd?K_=neC&=%r9 zZz<_e?0$L`9Gfj%rIlLFhUp@ z%Kuq5Ad@=`MfMlFM`bd8l>d`{VP?qx=~-WL>pyG%<@KN0#EBJuKkzSf7bqM-~2RzC-IHzrlt`f((~_qho^B!N4S{rv$uB|`*%J6e#NnAtT=l9 z-SeLn|MC211`i+4f9AM^=%435EC0vypLPDlc|DH0&c8Uv2T>J&##;;VAI|+l{D*vi z5Px2pUeC5tL z24H;V;`i|$)8BaS?ff%abi8T(@&&ws`rVElOfXZb%kOZY`z|Jmz5 z3qrmAv-}^g|1AH<^PlDac>Xgk8TkO7|IB#6fAIWg@Bx3)^Pk)E4?X`m@dyu7BkaF; zE;#JJcwU%&BIn=?8=fO(Gote^_Sfty_5RbzaeL^J?H)|L{xhavetG`0=RbEH*2YL+ zQgHSBXT^Vr=ZE+YasCkhA^somhjW2|Kc`%q7VxKHxjoS){9!c@_*1tnE#S|<=hFB} z@Bb=$BMr|-{?C6z(n(<8K9o-srjx`AUcv3nUg<0Zsy|k6PCA`sp)IffoP7V6=RfQD z(^b!8F@E&^iw9rMV*Kd&(}!!b7(aUcbX{&X<460irN?IzVpaY5ul3C)Xyf_Mk34J{ z%S=F<^`8Hnz#r<}1^nrHc3!}r{bTY%{^5U4%VYcin?TvwbMgp;5+?_y9;wP>Wf6pu zf5HG${hx|k@)(4`Dc1Jep8xFm&z}G6`On*zTQY$5U!MQ$`Oo;7cviTed_4b|<&xFY^PdUb5OwkVXYIc{ z|5^JlcKu=h<@wK^|4e<4kbg+s4?u(Ul=#Mli`$qudj6d{A*?UB99)T?Gk)~^J8^Z} zqe%}SrZ!&y?n=8OEFjXN`#8q^8rPVYZuFthaE@|YD2XY}&( zy4*~zkTL!}|5^Cs`Ol08Mpyq?IsCymVf?JA%1I8N$j82$!}wAC`RTXiFn;(a3 zIpDnNZ+QN*p1(N#ggmUL{6o)w)(}d4|N9Md^8^0WFU)8BDF1NW63ghubgb8lZ^{QS zReyf@ihMv-@gE}aEHu1llNINZ4f#+qXt{d+v-}^=e`aVg9z6fq^Pe{-Dgy|A*ct@? zhx-A4uG>`*@Td1Kdty%bvwo)~U%J|LOLm zRn^9AgBvRX9l<_=7AHJbfEc)Th;-j}yRY-F1KW%q_YYQGwi+M3|2^yH#$Q{Q7dBhF z+JD{fkjcY*$8|aOL6cMaFY?%F(Y7kpF_?$_7kTfruKgGJ@Yq57uPr~Z$_es+JpWn# zkJo>;q3iXZlkp$?jj;d14`GHfm9P)~N!Wkkw*VSUE55~_VIXP$g`Xqv;hO6IOgg{A z|G8;+hySytsKftZvIqRR>9`L6XL6Sg|EIOh@{@%>OAlB-onMep50p%ZZ&Vr`_F(1!= z*8a=$pY152FzWfwia&e)GoZo#0svv(q4=}sKWqQB{@&WK|H`<#HpG7}Ur~#@g1NwF z@`XbDXX9c!5mx+X$->&O|0!b{*M|MqQODYS&woCDh~+u!{N3}P_59b2J8H~HNId`L`OoryJpWn#kLN$j|MC21 z`9GfjEdR&zpA~=h{AZnidHyr&1xI_H|BU}Y6a_CGzg_R&@%(4ce~xCfg#A}_*OqYp zwYyJCIRDxQu?jHC{~_oU@aMl8G6Vi_o*3|_&&gQp$cEu{H4g zXPjX^UjG?)3-8PGpB3Nm{Aa=u_*b6)%%s5I#_JCGKc4^W`OlvJto@hgKWqP07PSY& zwExPBHvt#;+u$cpnBr*Z`SeRaZLx6vb>f3D@K)zvN3V-vD8+wHT@?%QXXTG6{=EKsu@Ha8 zlL_(X2d=g>65-Fh%2c5eAJ2al{&@bg{2$MMmjC1V&+>mf z{~3H>)$sgh)(g*nmh-@Pz;X5bXN>TR;Z6AAp8xFm&)R=^{b%jJuK8&s?7#MJi*N|8 z=TC_w5XhyDF+1puZ&YA6m$|R{V>7lVlDZS;?>Fr+KB__3+fOuepm5aTgdp){)>8sA^z<7&wBsGOJglPK=J3d zCPzd3+4G-;KY#7ok$*@1(D%fkRrTjR z|5^2asO!so^!#V9|1A9R`p@!zUOv~JV3Pmi`OmoPc$>U0fQ=rgi+{*-G$5+yKl5IR z;Qx63GtM3#&ws`Z20c9g+4G-yE*rG;{Ab=%M&yS{!4-!*#D6^h+4G-0|Jn1O_ng$6 z+`dOV|5@=LBD5j?bM1&m?1K}|b$b6s;vULB9NVptO$zjQH%iL>eX_sQ`Fz?P*4 zy!HI&_V~}V`VjvaIojU1#7cyH@YdMZ5VvGIGvu@NI4zKIdPO}n7H+DbaDQofJ(T=} z>%PCm!}-^lFU9#*=U?~yIv&oyhHi_8^RHL#vHPn3bLUEbF*U;e%j-XT{iN&CEZE2MpBWyEpKik~4_W75p8u@) z&-UNug!s?hZ{~#fPi0F^z@MHU%MJ0LCr```@n`Z4Lj0NcP-9_O5t2G#qTN^gdB#km zhb=>qCpF-s`18soxthGp;ve3e8~mTecjm$bGL9b3-H;pN&#hZ?L;Tt6KNGLRKA!)~ z;L$3;_+egy{8infj34GZ_G#^9`C~*OxL;Om!A$MHHV%toK+k_3k!}ydDE_=MuOt8P z`A>AzU-kTF