From c77e176928a78b99ec9a7cf3a558b3b031dce4be Mon Sep 17 00:00:00 2001 From: Timothe Jost <timothe.jost@wanadoo.fr> Date: Thu, 5 Oct 2023 23:46:39 +0200 Subject: [PATCH] work in progress --- .coverage | Bin 0 -> 53248 bytes coverage.xml | 45 ++++++ pypelines/__init__.py | 4 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 332 bytes .../__pycache__/examples.cpython-311.pyc | Bin 0 -> 1496 bytes pypelines/__pycache__/loggs.cpython-311.pyc | Bin 0 -> 3748 bytes .../__pycache__/multisession.cpython-311.pyc | Bin 0 -> 596 bytes .../pickle_backend.cpython-311.pyc | Bin 0 -> 506 bytes pypelines/__pycache__/pipe.cpython-311.pyc | Bin 0 -> 6869 bytes .../__pycache__/pipeline.cpython-311.pyc | Bin 0 -> 3274 bytes pypelines/__pycache__/step.cpython-311.pyc | Bin 0 -> 9540 bytes .../__pycache__/versions.cpython-311.pyc | Bin 0 -> 8165 bytes pypelines/examples.py | 20 +++ pypelines/loggs.py | 47 ++++++ pypelines/multisession.py | 2 +- pypelines/pickle_backend.py | 6 + pypelines/pipe.py | 48 +++++-- pypelines/pipeline.py | 8 +- pypelines/step.py | 135 +++++++++++++++++- run_tests.py | 15 ++ tests/__init__.py | 0 tests/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 184 bytes tests/__pycache__/tests.cpython-311.pyc | Bin 0 -> 1686 bytes tests/instances.py | 13 ++ tests/tests.py | 21 +++ .../versions_example.json | 0 26 files changed, 340 insertions(+), 24 deletions(-) create mode 100644 .coverage create mode 100644 coverage.xml create mode 100644 pypelines/__pycache__/__init__.cpython-311.pyc create mode 100644 pypelines/__pycache__/examples.cpython-311.pyc create mode 100644 pypelines/__pycache__/loggs.cpython-311.pyc create mode 100644 pypelines/__pycache__/multisession.cpython-311.pyc create mode 100644 pypelines/__pycache__/pickle_backend.cpython-311.pyc create mode 100644 pypelines/__pycache__/pipe.cpython-311.pyc create mode 100644 pypelines/__pycache__/pipeline.cpython-311.pyc create mode 100644 pypelines/__pycache__/step.cpython-311.pyc create mode 100644 pypelines/__pycache__/versions.cpython-311.pyc create mode 100644 pypelines/examples.py create mode 100644 pypelines/loggs.py create mode 100644 pypelines/pickle_backend.py create mode 100644 run_tests.py create mode 100644 tests/__init__.py create mode 100644 tests/__pycache__/__init__.cpython-311.pyc create mode 100644 tests/__pycache__/tests.cpython-311.pyc create mode 100644 tests/instances.py create mode 100644 tests/tests.py rename pypelines/versions.json => tests/versions_example.json (100%) diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..c7b5fd8c2fd5aeaf44e3768d819c42ec925cddfe GIT binary patch literal 53248 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 V1Rwwb2tWV=5P$##AOHaf{0}UivmXEe literal 0 HcmV?d00001 diff --git a/coverage.xml b/coverage.xml new file mode 100644 index 0000000..8aa3a33 --- /dev/null +++ b/coverage.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" ?> +<coverage version="7.3.2" timestamp="1696514560568" lines-valid="13" lines-covered="5" line-rate="0.3846" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0"> + <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.3.2 --> + <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd --> + <sources> + <source>C:\Users\tjostmou\Documents\Python\__packages__\Pypelines</source> + </sources> + <packages> + <package name="pypelines" line-rate="1" branch-rate="0" complexity="0"> + <classes> + <class name="__init__.py" filename="pypelines/__init__.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + </lines> + </class> + </classes> + </package> + <package name="tests" line-rate="0.3333" branch-rate="0" complexity="0"> + <classes> + <class name="__init__.py" filename="tests/__init__.py" complexity="0" line-rate="1" branch-rate="0"> + <methods/> + <lines/> + </class> + <class name="tests.py" filename="tests/tests.py" complexity="0" line-rate="0.3333" branch-rate="0"> + <methods/> + <lines> + <line number="1" hits="1"/> + <line number="3" hits="1"/> + <line number="5" hits="1"/> + <line number="7" hits="1"/> + <line number="9" hits="0"/> + <line number="11" hits="0"/> + <line number="12" hits="0"/> + <line number="13" hits="0"/> + <line number="15" hits="0"/> + <line number="16" hits="0"/> + <line number="18" hits="0"/> + <line number="19" hits="0"/> + </lines> + </class> + </classes> + </package> + </packages> +</coverage> diff --git a/pypelines/__init__.py b/pypelines/__init__.py index 134c169..38f5db9 100644 --- a/pypelines/__init__.py +++ b/pypelines/__init__.py @@ -1,2 +1,6 @@ __version__ = "0.0.1" +from .pipe import * +from .pipeline import * +from .step import * +from .versions import * \ No newline at end of file diff --git a/pypelines/__pycache__/__init__.cpython-311.pyc b/pypelines/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8550abbac6f7ee0b29e5f4d402f453d9c6cdd4e9 GIT binary patch literal 332 zcmZ3^%ge<81U*OOQj3A~V-N=hn4pZ$T0q8hh7^V<h7`sWrX0o)2+ahgnV~cbkY)~M z&}6A%HPAEAGknPi)ThaKi&4u@ll2yNe0*7IQE_H|UVQv5mV(TJ)LR@NGAA=H^%hHU zNov6@4yc0Sl?<OjcK`Bswu%WY2I`6_$;vM-$;~f~ami0E%}vcKDUJ!KEXl~vi;0gf zNKDR7OiwM2j|YlD3@nZ*fV1M`GxIV_;^XxSDt~d<<mRW8=A_yc@c_*Sxv$s(NPJ*s kWMsU-C)&XMfI;{I4BcQ5zW_rwV1htdWO0asB2XX#03v)}x&QzG literal 0 HcmV?d00001 diff --git a/pypelines/__pycache__/examples.cpython-311.pyc b/pypelines/__pycache__/examples.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a86a6670afd5629a2944bd98be85e7c0ea8f4a56 GIT binary patch literal 1496 zcmb7D%}x|S5bmD8on2V|O(Zcu)M!==33xL`0%$ymB#MX0B%Nia#f6#Kp?fzG<*+6^ z05>>z#uz;K3ckURa3JG_#FMur%ZZa!GYrch9PI7+wz|5jy6UU0uf<}4!1(lQgfoSZ z-?&JJaVE64K-eY@ai~i?YEg=^;wqkMsj;lOnx|VjB?@^#9Bq?0I#+j9LiXTGhGi&( z8@sUBOI5_m@!X1;E@N8*Y;&AuZ7*5n9rHCQ<^NEyEYYa2P_3-Ge4!d}Sar{qWh}c@ zAJ+w0=YhxT?`uvay$S!(7)QGYV4HAaQHNNHL&3EY^*;Yl_JYJII+`!3k;#}}_Bdlv zfibV<G+dOs8Cz?V-DKw*X`|26S96c;MahM<>nk-`_i7FMS*_CWxL=p{!a4-w+l&R} z%4&I;OU6JKaJ)y_!MVhf5adL#E^^>C2CRwf1!$iC4z*l!PIRXyGFhhdx0hhMlO>cw z&64UEj<QL!I!nWITwA8!)m`9gFXb#Q@ujo6qXTofsOtiAkuvqDl7;{Q5R~z2W3=03 zgKhxfUlgQ#9$3$p#d1v2WHeX{>eZU>mtB@Br$iT6K_N8B-FIRD7Ge-UqQ|kZF;Rs7 zsCY&j!@5s?9KI0FL}m-?F8cwR<Y?r6IP)g#UBqcF#$3YFF$G_1R0Ws+kwY9-Cy_K} zF$5PSA>YJ%uMc_Z`jDqEW!aa8{9`@roxy3YSzsKCvVA+V=J9s#>s=m<9uh7pj) z<h>wc0$)=MsFyxK=WVqNV)Qkw&-SL>QmFoxwnzt$RbmXBZzLyoUhYhX`uM&+exQ$| z$!u&gOY!i-OFDEmOOsGAiuqC@>Ivd*W=qf<+;;>r6`27vH};3naH(N!ldP~YqQ11S z>9MFN_;M9a35Kd9#cbNA=98p{pNdiNflpih1Zb(0(j#&&%+5n{JIu~Qax2WvmPV-w zIsF4$?lEdlI$H)Ay0bOCKX~_G@NP)@j%fb@9c?KlMY^uWtp~^P&Pj|wr!sb>O-$!s D8E9B) literal 0 HcmV?d00001 diff --git a/pypelines/__pycache__/loggs.cpython-311.pyc b/pypelines/__pycache__/loggs.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59a5b8beef301a67600a51883a78eafa02fc3586 GIT binary patch literal 3748 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 literal 0 HcmV?d00001 diff --git a/pypelines/__pycache__/multisession.cpython-311.pyc b/pypelines/__pycache__/multisession.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a084f7de3b503704d14479f1d1512ff1d81e897 GIT binary patch literal 596 zcmZ`$O-sW-5S>lZ8f)vxgBPLVsh3^_5y21eWD$x7LtqKXs?mJ8lN{=)hl2it{sTq) zLz1h1AQDeKIoq^Fsm_u&Z)e}U9kMU2Rs$#ok6YrB@`u{Y!<dn>O=JujoPq}<@Yu5k z@J3%17I<18I=VYAsLW=Ya)Zbi1X#=iOY_(WEWOg_n@0|cvn)t`6o`Ht9l9=&Sh@@E znUhYrtWTXhpu1S_6*^jOpa`z=wZxGkN;#$?LDkuAPx{Au_5})wc6u9Q8pfG@61!PQ zI<(IR=}jEjoF|TZ=UfZqoN9>({79gkELiwsv~DuUP0szuPdR_s{%`W`TvDSsaD`w9 z?`HGKD2YO^V<?lz2?ghQgYz)<vOw`B=l7WtR3~zkv{E@p^*GX0pnky=6u}*W;bIjA xtF;PPhN@q6^g67v_-#8e_p2EW<T|O;tKlv|p)<ycy3WXU89r6*>yLx!eczA&jLrZ6 literal 0 HcmV?d00001 diff --git a/pypelines/__pycache__/pickle_backend.cpython-311.pyc b/pypelines/__pycache__/pickle_backend.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b724cfb7ee20e8dbeae3727c4c62d0942f3a241 GIT binary patch literal 506 zcmZutJx{|h5Is9-iAoDAVqnk0$bb+7MPe(JN|z{-6=DlY^I_s-=+=QBz}A02h4?d! zRalUix)qhF6K98l5S;DjcfRvG-A!Aq2B4k4?1>)UZ`xT~?TaiAkW7FBClPqSDYqgL zQ;!m$a0cAI16~vK!2)=~Q<-OR(gU|X*KTJ{5M5F_CxH^(ffQktRiO@QTW%ptK!8U$ zc+?2cve6xc!^r&VxK`;fmISe2tZXnAr@V+XZ!&gU1W^@vgr$~S_1lMjUx`fl`Aw?w zI4%6sG%R9~<jU`k^6ND58IwUc46cM?4Bw=P28mF<{9&d#yTmM!@O?ST>Zol%4@hkz zjK9~+-Wa#D&2@%rmvw0bN?T&vnZ1r=dhS(X6WPi+s(Lxjw4k1dx<FW1gpe7u#%8_3 Y?(^@xunBP%@E7iP-u3IpKOs}~6O&ANh5!Hn literal 0 HcmV?d00001 diff --git a/pypelines/__pycache__/pipe.cpython-311.pyc b/pypelines/__pycache__/pipe.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c73a501c7a2e462bc56295d5ffe27f1cbab1ea7 GIT binary patch literal 6869 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 MR{!JAv|>8^4^BMp#{d8T literal 0 HcmV?d00001 diff --git a/pypelines/__pycache__/pipeline.cpython-311.pyc b/pypelines/__pycache__/pipeline.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..caf0bc1b20115b3044272f07dc526daaa7680c37 GIT binary patch literal 3274 zcma)8O>7&-6`uVewfqwq{X~*w%VW!dL?I$tyD0+KNzq8MY$#xj*likC@pi=>k!z8= z%<NJ>#HtWnADF;Eg&=^9rUrC!9r)ry4?VO%k3~*Pfq;nx1PBOv@Qs0UFnnp>?2;6z zI6#KWxAXJfoA1qg^WOd~77HV2iLWQf7ZO6>utB?tt<LlJp>q$ZNaYHs$Yr=9pW%x_ zMktCIu^7k%I79+8sPd{%2p0P?eH`M^B2vXWNDVyX5qb=NzhxvfcpWACp0Qyv;7W5! zp`ff3h%4UMDiK$@Y7y#<rEAo%jI2>`V>dp!wjj@4S(sb8nqGXyF-gvq-cw9+O)HVC zui2*^$nV1V9wI2isVKv1C?lvG5mlautD-905i<cbtOo9InV=dZeXG3M2Xm4dQA02n zQbhn6b))S)3Yt#ba0zykvjxR8-5@1and;e&VX&Rh`aeJE?ad>bv(ZC<`<OxY8V7Tr znXCNuL#mWYgksCUnr>Q(p4AkKU`1E4Vp&Qy4}JJ~y8%a@P|ecx9A2eH5w~Gs&0-2H zU}h)5P`0&^R2a9~4OnK|!EQck)06O3Z)jHDC|fw2SM(g)K(kU`p(O8$CMm4aD8M$E zkr==H_1wkf-<X7&%hoN!w2DS~dEUsDi$u4~<!f7@M_-oZl9F9ha>SHnnDa52%cY}U z3umgd<;Ez<X(l*aW+z)Y(Kd_Ok?L9VV4MILJE$2%p}}f+_h&U}v@VT0(&%A)%#p^P zvja}|IE3M5PI*2A<euja8G3$G(RK6{mu8W)4mN3qZYFt}fJOQ`ke1Q(HR!vNENi-E z$#P}52V;ABkO{<q?4ZUdu8MbKRrpah?Z#T}C9<v*Ne{jttb6$W2_1%S58mHzD%TrC z%V5_wzb%B2jc#>9%jU;AqT8Y^-s*%V<!uOOm|=T)%@&S$ucJw1!Od^CbBGRc$O^T` z2&ufyeTh`zfzXTNHn)K`g^$n%mlV@4xl|>Z?gTTzm2)_+n0SQ{9m3A{Gf~^&${fzz z3@IRBmI|8X2H2G8Mob980+AssO0WdfbfqP-wLqyslL6mBqX5ngF#+#T-4NsRyP~Hi z01RIA`~K^r#54-)q%zdwzSgYg?%N;%-ovv`@r8YS;mKSLzg@>~*Q8U`(C*bH9}LM{ zV{qh)i}$tq;B<Aq5g)9_Pd5=4JJ}c+-;ErtInn7C2k{YD0+DWdFmS>d|M<}?5dWzS z+^i4WbfPz#0^@!0;)NGPe;IxUJ&C@9{x$Kdap9Q?z_QGqc4NNsqJ{?#L)7p)d=BEl z7QDA$bs_>mL|L6EK&Uqp(_tI!b*;A5GpSxbZ*!2ygmpwGtiB^I73S2gm}3#*#ffMO zU!n)WULCiAdrv%)KI$Fc4Pr0SwR$Y(Y;n!gLgN;@Z><JZrYFtb=HNsEHg~IocOHGb zv>gPCyCS_U4HgG&I6qr_2)ZA4P`2(_kgcwBa9;cmSa8f%Nb-Rn%X#;G8*s`UoN!8A zH}43c%J*6bd%&$+<)N4a)0Kfajb_UQg<`E(GN^?w;%EPZRtdw^`=qQvptJk9p=I-! zY(mO4p-kYCLRsp@c@0V~&E~g2tg?E;FmSO9jFe={US50?mvu^%0?27ute1-`P)Uqc z?A<h0I3PFbxfH&tuM=R&u{uWBbv3a8i1aK7n*<k?O}V2p8Fs@OTy?MV5mzuRN+-cw zxBr9ow%(0*gRD&Isw)(T?uLJ>6w1V3klL2H!#QGsX3tJmj39SF4RVE&QA+lC3B#2- zJN361RWHMsZeQyZ+*l7jcetx6t8ML&p1Srinb*85gRz%qXbL7OXM1wYE3skVGglyF zA>%~GoXDBRsngZ;VPv5C%f{=Y)y4lthIjK1%vxl!9+_k*Bn3I<p8dJKccnHmTOXON zUO9{m?ru1d33v_$a1)`k3;dJZKUV&}=J$R2pWk(lpl@??eD~D12n}B5zw;=}$1d}S zQvA>TpY`u<)+Ah)up{AvNPl(X(@S?R-Jji$OgWLMgMpL4J8_w3(u<z-BGVdM<Qw>` zBb_=NpRA9+*+gRK=N_ppHD*#zXXf{3=KuYB=Yu~uE1I*mS)19a&urDAQ`PkSrN-dI zUdkE#2|SI&#M8tZ`-wO9)@q6K^~8B6alR2f^E5iWADw<QUW>j}kG|!2&*90RR;9W$ z)&S6v&mz0;eHy<Tuf`AKr|xe$@hL}|@`^AWh67Iu=`=mZI$>7OSv8hrH!RCVLoGwa zk=>{)e^OQo{t9JZajs~=mkS+$E*)WHnh_SLp08<~^;ntr{m&8?WrSrJ^G`r_I={nc z|IR{F3<OR*8fqeVJvrySj+1^aa0Xo7LxNI3=Jv8p#NMx<L*s$zCi(&5<724O$&eca zgW$GORzax92&*lWeTq;P&-5%KEC+dp9JBDHbjd%hcj+&Hg_Rofk3gV+aU6V^aSNP- j=6}p{C?vp2?sP+(aC)Dnc!2|JzE8eobKkvU!L#uHl?*xv literal 0 HcmV?d00001 diff --git a/pypelines/__pycache__/step.cpython-311.pyc b/pypelines/__pycache__/step.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..820550bcc69593057ce4d311f66a9064ab58d1e1 GIT binary patch literal 9540 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 CHD@aT literal 0 HcmV?d00001 diff --git a/pypelines/__pycache__/versions.cpython-311.pyc b/pypelines/__pycache__/versions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb02e1806733ca7950fff3eddc97958cdb7bb6bc GIT binary patch literal 8165 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 B^nm~X literal 0 HcmV?d00001 diff --git a/pypelines/examples.py b/pypelines/examples.py new file mode 100644 index 0000000..eb6d392 --- /dev/null +++ b/pypelines/examples.py @@ -0,0 +1,20 @@ + +from .pickle_backend import PicklePipe +from .pipeline import BasePipeline +from .step import stepmethod + +class ExamplePipeline(BasePipeline): + ... + +example_pipeline = ExamplePipeline() + +@example_pipeline.register_pipe +class ExamplePipe(PicklePipe): + + @stepmethod() + def example_step1(self, argument1, optionnal_argument2 = "23"): + return {"argument1" : argument1, "optionnal_argument2" : optionnal_argument2} + + @stepmethod(requires = [example_step1]) + def example_step2(self, argument1, argument2): + return {"argument1" : argument1, "argument2" : argument2} diff --git a/pypelines/loggs.py b/pypelines/loggs.py new file mode 100644 index 0000000..a1b907b --- /dev/null +++ b/pypelines/loggs.py @@ -0,0 +1,47 @@ + +import logging +from functools import wraps + +class ContextFilter(logging.Filter): + """ + This is a filter which injects contextual information into the log. + """ + def __init__(self, context_msg): + self.context_msg = context_msg + + def filter(self, record): + record.msg = f"{self.context_msg} {record.msg}" + return True + +class LogContext: + def __init__(self, context_msg): + self.context_msg = context_msg + self.filter_was_added = False + + def __enter__(self): + self.root_logger = logging.getLogger() + for filter in self.root_logger.filters: + if getattr(filter, "context_msg" , "") == self.context_msg: + return + + self.filter_was_added = True + self.context_filter = ContextFilter(self.context_msg) + self.root_logger.addFilter(self.context_filter) + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.filter_was_added : + self.root_logger.removeFilter(self.context_filter) + +class LogSession(LogContext): + def __init__(self, session): + context_msg = "s#" + str(session.alias) + super().__init__(context_msg) + +def loggedmethod(func): + @wraps(func) + def wrapper(session, *args,**kwargs): + if kwargs.get("no_session_log", False) : + return func(*args,**kwargs) + with LogSession(session): + return func(session, *args,**kwargs) + return wrapper \ No newline at end of file diff --git a/pypelines/multisession.py b/pypelines/multisession.py index f3d62cd..2606192 100644 --- a/pypelines/multisession.py +++ b/pypelines/multisession.py @@ -2,5 +2,5 @@ class BaseMultisessionAccessor: - def __init__(self, parent_step): + def __init__(self, parent): pass \ No newline at end of file diff --git a/pypelines/pickle_backend.py b/pypelines/pickle_backend.py new file mode 100644 index 0000000..26a1bee --- /dev/null +++ b/pypelines/pickle_backend.py @@ -0,0 +1,6 @@ +import pickle + +from .pipe import BasePipe + +class PicklePipe(BasePipe): + ... \ No newline at end of file diff --git a/pypelines/pipe.py b/pypelines/pipe.py index 0f8d858..e3f27ec 100644 --- a/pypelines/pipe.py +++ b/pypelines/pipe.py @@ -1,11 +1,22 @@ -from . step import BaseStep, step +from . step import BaseStep from . multisession import BaseMultisessionAccessor -from typing import Callable, Type, Iterable +from functools import wraps + +from typing import Callable, Type, Iterable, Protocol, TYPE_CHECKING + +if TYPE_CHECKING: + from .pipeline import BasePipeline + +class OutputData(Protocol): + """Can be a mapping, iterable, single element, or None. + + This class is defined for typehints, and is not a real class useable at runtime""" class PipeMetaclass(type): def __new__(cls : Type, pipe_name : str, bases : Iterable[Type], attributes : dict) -> Type: + print(pipe_name, attributes) attributes["pipe_name"] = pipe_name steps = {} @@ -14,16 +25,16 @@ class PipeMetaclass(type): if getattr(attribute, "is_step", False): steps[name] = PipeMetaclass.make_step_attributes(attribute , pipe_name , name) + attributes["steps"] = steps + if len(attributes["steps"]) > 1 and attributes["single_step"]: raise ValueError(f"Cannot set single_step to True if you registered more than one step inside {pipe_name} class") - attributes["steps"] = steps - return super().__new__(cls, pipe_name, bases, attributes) @staticmethod def make_step_attributes(step : Callable, pipe_name : str, step_name : str) -> Callable: - + print(f"init of {pipe_name}") setattr(step, "pipe_name", pipe_name) setattr(step, "step_name", step_name) @@ -39,7 +50,7 @@ class BasePipe(metaclass = PipeMetaclass): step_class = BaseStep multisession_class = BaseMultisessionAccessor - def __init__(self, parent_pipeline : BasePipeline) -> None : + def __init__(self, parent_pipeline : "BasePipeline") -> None : self.multisession = self.multisession_class(self) self.pipeline = parent_pipeline @@ -62,10 +73,22 @@ class BasePipe(metaclass = PipeMetaclass): self.pipeline.pipes[self.pipe_name] = self setattr(self.pipeline, self.pipe_name, self) + self._make_wrapped_functions() + + def _make_wrapped_functions(self): + self.make_wrapped_save() + self.make_wrapped_load() + def __repr__(self) -> str: return f"<{self.__class__.__bases__[0].__name__}.{self.pipe_name} PipeObject>" - def file_getter(self, session, extra, version) -> | : + def make_wrapped_save(self): + self.save = self.dispatcher(self.file_saver) + + def make_wrapped_load(self): + self.load = self.dispatcher(self.file_loader) + + def file_getter(self, session, extra, version) -> OutputData : #finds file, opens it, and return data. #if it cannot find the file, it returns a IOError ... @@ -73,7 +96,8 @@ class BasePipe(metaclass = PipeMetaclass): def _check_version(self, step_name , found_version): #checks the found_version of the file is above or equal in the requirement order, to the step we are looking for - ... + #TODO + self.pipeline.get_requirement_stack(step_name) def step_version(self, step): #simply returns the current string of the version that is in . @@ -83,10 +107,10 @@ class BasePipe(metaclass = PipeMetaclass): #simply returns the version string of the file(s) that it found. ... - def file_saver(self, session, dumped_object, version ): + def file_saver(self, session, dumped_object, extra, version ): ... - def file_loader(self, session, dumped_object, version ): + def file_loader(self, session, extra, version ): ... def file_checker(self, session): @@ -96,7 +120,3 @@ class BasePipe(metaclass = PipeMetaclass): # the dispatcher must be return a wrapped function ... -class ExamplePipe(BasePipe): - - @step(requires = []) - def diff --git a/pypelines/pipeline.py b/pypelines/pipeline.py index 274307a..ec6abf8 100644 --- a/pypelines/pipeline.py +++ b/pypelines/pipeline.py @@ -1,13 +1,17 @@ +from typing import Callable, Type, Iterable, Protocol, TYPE_CHECKING - +if TYPE_CHECKING: + from .pipe import BasePipe class BasePipeline: pipes = {} - def register_pipe(self,pipe_class): + def register_pipe(self, pipe_class : type) -> type: + """Wrapper to instanciate and attache a a class inheriting from BasePipe it to the Pipeline instance. + The Wraper returns the class without changing it.""" pipe_class(self) return pipe_class diff --git a/pypelines/step.py b/pypelines/step.py index bcd0f0f..18bc5ff 100644 --- a/pypelines/step.py +++ b/pypelines/step.py @@ -1,6 +1,9 @@ from functools import wraps, partial, update_wrapper +from .loggs import loggedmethod +import logging +from typing import Callable -def step(requires = []): +def stepmethod(requires = []): # This method allows to register class methods inheriting of BasePipe as steps. # It basically just step an "is_step" stamp on the method that are defined as steps. # This stamp will later be used in the metaclass __new__ to set additionnal usefull attributes to those methods @@ -28,11 +31,9 @@ class BaseStep: self.requirement_stack = partial( self.pipeline.get_requirement_stack, instance = self ) self.step_version = partial( self.pipe.step_version, step = self ) - self.load = self._loading_wrapper(self.pipe.file_loader) - self.save = self._saving_wrapper(self.pipe.file_saver) - self.generate = self._generating_wrapper(self.step) update_wrapper(self, self.step) + self._make_wrapped_functions() def __call__(self, *args, **kwargs): return self.step(*args, **kwargs) @@ -47,13 +48,133 @@ class BaseStep: return self.pipe.dispatcher(self._version_wrapper(function, self.pipe.step_version)) def _generating_wrapper(self, function): - return self.pipe.dispatcher( - session_log_decorator - ) + return + + def _make_wrapped_functions(self): + self.make_wrapped_save() + self.make_wrapped_load() + self.make_wrapped_generate() + + def make_wrapped_save(self): + self.save = self._saving_wrapper(self.pipe.file_saver) + + def make_wrapped_load(self): + self.load = self._loading_wrapper(self.pipe.file_loader) + + def make_wrapped_generate(self): + self.generate = loggedmethod( + self._version_wrapper( + self.pipe.dispatcher( + self._loading_wrapper( + self._saving_wrapper( + self.pipe.pre_run_wrapper(self.step) + ) + ) + ) + ) + ) + def _version_wrapper(self, function_to_wrap, version_getter): @wraps(function_to_wrap) def wrapper(*args,**kwargs): version = version_getter(self) return function_to_wrap(*args, version=version, **kwargs) return wrapper + + def _loading_wrapper(self, func: Callable): + """ + Decorator to load instead of calculating if not refreshing and saved data exists + """ + + @wraps(func) + def wrap(session_details, *args, **kwargs): + """ + Decorator function + + Parameters + ---------- + *args : TYPE + DESCRIPTION. + **kwargs : TYPE + DESCRIPTION. + + Returns + ------- + TYPE + DESCRIPTION. + + """ + logger = logging.getLogger("load_pipeline") + + kwargs = kwargs.copy() + extra = kwargs.get("extra", None) + skipping = kwargs.pop("skip", False) + # we raise if file not found only if skipping is True + refresh = kwargs.get("refresh", False) + refresh_main_only = kwargs.get("refresh_main_only", False) + + if refresh_main_only: + # we set refresh true no matter what and then set + # refresh_main_only to False so that possible childs functions will never do this again + refresh = True + kwargs["refresh"] = False + kwargs["refresh_main_only"] = False + + if refresh and skipping: + raise ValueError( + """You tried to set refresh (or refresh_main_only) to True and skipping to True simultaneouly. + Stopped code to prevent mistakes : You probably set this by error as both have antagonistic effects. + (skipping passes without loading if file exists, refresh overwrites after generating output if file exists) + Please change arguments according to your clarified intention.""" + ) + + if not refresh: + if skipping and self.pipe.file_checker(session_details, extra): + logger.load_info( + f"File exists for {self.pipe_name}{'.' + extra if extra else ''}. Loading and processing have been skipped" + ) + return None + logger.debug(f"Trying to load saved data") + try: + result = self.pipe.file_loader(session_details, extra=extra) + logger.load_info( + f"Found and loaded {self.pipe_name}{'.' + extra if extra else ''} file. Processing has been skipped " + ) + return result + except IOError: + logger.load_info( + f"Could not find or load {self.pipe_name}{'.' + extra if extra else ''} saved file." + ) + + logger.load_info( + f"Performing the computation to generate {self.pipe_name}{'.' + extra if extra else ''}. Hold tight." + ) + return func(session_details, *args, **kwargs) + + return wrap + + def _saving_wrapper(self, func: Callable): + # decorator to load instead of calculating if not refreshing and saved data exists + @wraps(func) + def wrap(session_details, *args, **kwargs): + + logger = logging.getLogger("save_pipeline") + + kwargs = kwargs.copy() + extra = kwargs.get("extra", "") + save_pipeline = kwargs.pop("save_pipeline", True) + + + result = func(session_details, *args, **kwargs) + if session_details is not None: + if save_pipeline: + # we overwrite inside saver, if file exists and save_pipeline is True + self.pipe.file_checker(result, session_details, extra=extra) + else: + logger.warning( + f"Cannot guess data saving location for {self.pipe_name}: 'session_details' argument must be supplied." + ) + return result + + return wrap diff --git a/run_tests.py b/run_tests.py new file mode 100644 index 0000000..3b8b948 --- /dev/null +++ b/run_tests.py @@ -0,0 +1,15 @@ +import os + +# Executing "coverage run -m unittest discover" +print("Running tests with coverage...") +os.system("coverage run --source=pypelines -m unittest discover -s tests/") + +# Executing "coverage xml" +print("Generating XML report...") +os.system("coverage report") + +# Executing "pycobertura lcov --output report.lcov coverage.xml" +#print("Converting report to lcov format...") +#os.system("pycobertura lcov --output report.lcov coverage.xml") + +print("Done") \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__pycache__/__init__.cpython-311.pyc b/tests/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be96c6b190b406a5be0b54b97546fcd84cf2ba81 GIT binary patch literal 184 zcmZ3^%ge<81WiZeQbF`%5CH>>P{wCAAY(d13PUi1CZpd<h9V{)|1(JPm$$Q3OlWax zQE^O3R(^3wZhmQuOMY@`ZfaghaZEsENk)ENOniJnVsdt3dTMceJW#A4H77GK6{sP# z7^o&bJ~J<~BtBlRpz;@oO>TZlX-=wL5i8JaknP3%K;i>4BO~Jn1{hJq3={(Z5&|#q literal 0 HcmV?d00001 diff --git a/tests/__pycache__/tests.cpython-311.pyc b/tests/__pycache__/tests.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..443c11517a3b8b79ca849efedb82d20132fd6c2f GIT binary patch literal 1686 zcmah}O>7%Q6n^{Tc%3-4W1xyFgoA`AKExFWiNA`fDwG^Z>ZBAZ%hIrTCiS}Zdf8bc zV&!0vC34_UAtZB*5K1XQ;)=Myk)s{0)M%wj5uAE63a6fUvzy)Qs+E|XdGqGIH}B`0 zXWyNlp9LJh{r((#Q3m*19Kw+~VUFLI07gK8N^PhGyHu0>vs{yz)KbDLyK2bAcP7Uy zQgKblCoE<{X?#yuq~n^9XWAa-xsNLtRNlji_HhLxsZE}wpsGwQLlvsFXFGGXISFKV z4`^x#bcR7I8~MR%_oIqy^Xv>w-}a-}3o@W(F0w>sKRf`&kgofytUVOcwSKBnxlpM* z6e*V_@2q85tZkT#IE*{QFu9|f4c3S}AQM)1d=3vIj3(ZpCR1q$HHFGZRjOQvvg&0& zKqntEn;Tt|H^O_um&jeL<0_I7I5Ax-;;EEGXd6g`q!W7~lt*t)0VB(6{laHKSi(Nn zJ64-jtgUj&%UD}MdE6_k=)8H7+lp?|HnY84c&Ql(Y~j|^LQL*Sp2}H!lZy<O9<5w? zvwnkPn7Y&Ia;MYn)!*qhdL3puyk6aMnq9L_h^04f>$eyu1Z5||>jGT7CMYK6r8skL zSp7mAu8J?4!$|yjBm;O*ddV%lHNIG#l-AwS`i|;q&pueJ?5JPQ@8EZgMv%Z*B=2MV zF)}0AhF#nrAKP$>_-=%6+-?sKr#*zPf>x(JE&A5S+)L~975eh#t7XNT(>WsJy!&ZS zZ`<PM?JeA54%zIP4F{WnG!cATv~z%^eMXoR1RzP9ekp;;;|8M9`7)9L9BSG7+Tww> z_}$ZParu7n(n0akq<GmaUjE~aiMHx$tK)FmdAzZP4WSWY>K#UiH%myTOM7j>X9*FB z2O3+1u%AJ45Bz(6vbN|ypNPLG{u+`&^i5Scbzuq-(WKOxG=(SAz^8C0vgzoCNk}>G zX+6_$M7^HEx40*Fxu;sX)AVL^%VH))v_Ij#Ky7sm(@Rss76}U{n}#UE)`ZVY_#5UY z-7EmTqT}oM6`U0|s)%YAkv%Q&k#;e_<8AvqDn!fkzmQB7Ns<m>ar}St2$Z4rRqo5& z9r@ezDECcnSN<V=H~W3|$Bmyq`i1;Ne!V$azUnSt{WCpTs=7<nNq*hUuTNmZg^e+6 z96^2@&LgOdlXI%3C4uRa>7Cczr(XESSN<C}DS(>Y=^rS?u~Ix#7X~ZC)i16MuMPZf Ms(^Y96+Q<40lWf&A^-pY literal 0 HcmV?d00001 diff --git a/tests/instances.py b/tests/instances.py new file mode 100644 index 0000000..c1aed34 --- /dev/null +++ b/tests/instances.py @@ -0,0 +1,13 @@ + +import pypelines + +pipeline_test_instance = pypelines.BasePipeline() + + + + + + +@pipeline_test_instance.register_pipe +class TestPipe(pypelines.BasePipe): + \ No newline at end of file diff --git a/tests/tests.py b/tests/tests.py new file mode 100644 index 0000000..9a61035 --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,21 @@ +import unittest, sys, os + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) + +import pypelines + +from . instances import pipeline_test_instance + +class TestVersions(unittest.TestCase): + + def setUp(self): + self.version_handler = pypelines.HashVersionHandler('version_example.py') + self.pipeline = pipeline_test_instance + + def test_function_hash(self): + self.assertEqual(self.version_handler.get_function_hash(), "ad2d1f4") + +if __name__ == '__main__': + unittest.main() + + diff --git a/pypelines/versions.json b/tests/versions_example.json similarity index 100% rename from pypelines/versions.json rename to tests/versions_example.json -- GitLab