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`@roxy3&#1YSzsKCvVA+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
zcmZuz-)kGm9iQ3NN~>RygMEsLW1sTNiQ1qvX)ZZJ;x=4xUasP3(u32vY}MVhEJxD1
zGwaxA6mTI>r-arIEeRY4rW~o$6xz4G9o#=qq;w%-!EkVW>6?u65c1UTclL+0wr59Q
z&CY!1yWjbKKl7RSYc`uAQ2zU$Df8b7A>ZRmP~csZy*B(kB?d8QgH-TeYS5-sktmVK
z9b(9jh!Hc%qU`IF=g@i?zExsEO98F;a|uS^=aq^gMiMZhn##UeaU=PNR1yZgONy!g
z;e_%F>MAR&y2N$gfcT^S%ia_;o)VK(s6i@{K`XK$fr%J8SW%1^7*gEqM^?)=SMAw)
z!#3I4&r$^__ZI5B0JU0}7u|)Gg}S~_sJ9kP-R1?|o3dPOK-av*npL}QwV-WV1$)6P
zG^|=l*wMqKfh*X?Anc6^zuL4-G-!FnAa}_NS}syI?OWHHy!JwJW87@avm|J7G*BqK
z`}m{lbHCsw<8$_+#qFlGJa^mDmz!qG=5wEXVJ}#%IZa!t>i4TPlWQ9EEt!pa%j9!Q
zVGCTW@zT;4uBvJER^8ULwR|KJrJ!GYdIUbbPI?p58!<;6?})mGo<~-Qs}n1{42`Fx
zO%_o_lrI4+ZMqobhI~oBlG{`Z-wg?}A(!9L!o8v-mg~w80BEOSz8hzzZZX3T0n3O!
zFE?vrQNO)j)ZiyUfY;+EobmIW_pU+RzWVp;UTY)%B-P7}ZrJPyXqAg`j6|#2G&Rjl
zX<E}VmK)g4XxeWe(f&w6(+o@3G?s-)Y!t)`A`A$ZSR8v%;8x)`K&*#PUy>BHPf*0E
za;{H85to!P9M~^B!)Q*2Us!^e?G>PF7~g>E!w{bO*sA#<iexLq<0)ZhA082%!Ixl*
zLw2O{Y>=7}FUZ<Tm1|YQFb#&RF`OL{ptaG#r$am)Ma^*#13dK#XAxJE9Z|)y_3+!E
zi6IcU%0TZ^Vh@cC5cM3CUc%2S;61KS6u@EF9+1*)^2_%hQXApzmo_!1Aw_8i<_5G!
z?h7kSrXn6s3Wf|{LEl8Nb3uzB^21rr!P}Ri$1p<;^oIS~8uB9;7j{DJs2$ITVyx`#
z6>_hbauc|z>#drbteG}MzGgBv%`D5-Z~$HsUfQ^u0EMb;GclQ+fDa1Px<>-;@f6)8
zBzIOYpuZ6X<cIU@CN&L+$7DbtW07q8z3+i>{shE2*_k-yOkC(rl$?o@;E`VbxRalD
z@+Clj@|X}Cvpc!`=7VPsw^=uL*~wl0r{P?keU+Q-<Ys##`OWxdyf2fHJG37sCq2~c
zO#N{CwsYY|cj~4ybrTIw^wbQ{)NA$BEA`aY73cKTu6oT;uXTdzK`b^1w1Ag@*r(7L
zr7m_75)~$JD}>U}Y)m-L7Ui%+;Ra%u%3{n5_ep5G8D=)E2c{QccE%ePe5abLx@H4g
zy9sPPs5acV==wEBuknoXNDxTNTov>k-D_Uw`=HCOfq=c1&N=A<uuk$Pz08P{Iq^DE
ze3dC~pK*S0vzwW5GBcf^_GM5Knj%iXjSP=0hN)zE6oUPxv2_6j?q-jykapSeK;Wst
zZ-H13A7sI4Bs*WIs`6o<gknrm;L8VvXB`bks4&QdIO=~lM1~t=9yaqMwIXkjRa(KL
zUUHEL@0y&$SsfuljHt-{Dm4B~;budJDQ5WCisp6=^dCxgxYY@XlF(L&2~Z46TZ&ro
zx3W}TllbXk%$2##T&3EmSGlY3<t3Aecmr|-G!oofg17Nu`{|7;hhshf#=3VDVTuI*
z`YR6k_Y4g14?w&HP~?w)b$9F7*OT4Ji_YZ5ZvNs%dPmJZPCQAxR>xnd<Ig6#>RCrU
z+X<>jL{T{m5N_7@MK>D_O)FA%1wVy5=ck@eC1^4fr*Ysz5a2`b>_^4`BZss+JfNw_
zL4olWnZ%<P@i3gn$0Qo}03*?yJ%nqDIP4;Zk>znS?fne|LZmHiLF-@<CkViDaH(ag
zL>|Lkrw((zrhfqA_7E)E`<(iK;3XRLNevwN3;j6>=IIh#wBd<fnJEy<X>y71*ow3&
ze@0d)C7*!}A5?PLJ<_r?A2N6jz!~n!K>s2SfT<tD8r)cw)wmnKzasu)^UEzg3MV&#
z_iTXT+N3y3jHU_qOrvz8Vd>QdpDFpnMX<)d0Rf5qE`9Wm*MEP#lb_yJy6KXW2AZeI
z^S#kyn^&HVbw<x^#X(@roZHDBdzR>C3r<#0)Fsrs<Y^{4qd(d@1H!A@dG8-d%2N}%
zLhRzKHz)>uhqd0meS28<ppWAv<sX?_s{E}QyOl|No1-8Z<_!))1Q)vrLL7v@@|id@
z92hnO!{<>2;|syqQBoUMe*5{O&-+qL0rw(eS0?eJ!BC21M%T;UK2sTj&P`%MY|CnJ
zhFs;w{SyOG>9MWYS@_L;T>MVG;oW3s81gXgMgA`k5Isu2BS+T7(<2j|Xzj<66t1HO
y#pazqm3OiK&0sKplmclV6ua2>W-u@<Q(*mrV)OVe_P!Ymr4n?sPYwzZmHz`cJs!9K

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
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

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