diff --git a/.gitignore b/.gitignore index 0c67d955c..e9fd392f0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,8 @@ nghdl* tags build/ dist/ +credentials.json +token.json +.env +*.env +logs/ \ No newline at end of file diff --git a/images/tracker.png b/images/tracker.png new file mode 100644 index 000000000..3d772fa4c Binary files /dev/null and b/images/tracker.png differ diff --git a/library/SubcircuitLibrary/DAC0800/D.lib b/library/SubcircuitLibrary/DAC0800/D.lib new file mode 100644 index 000000000..f53bf3e03 --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/D.lib @@ -0,0 +1,2 @@ +.model 1N4148 D(is=2.495E-09 rs=4.755E-01 n=1.679E+00 tt=3.030E-09 cjo=1.700E-12 vj=1 m=1.959E-01 bv=1.000E+02 ibv=1.000E-04) + diff --git a/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit-cache.lib b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit-cache.lib new file mode 100644 index 000000000..6184ddb38 --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit-cache.lib @@ -0,0 +1,126 @@ +EESchema-LIBRARY Version 2.3 +#encoding utf-8 +# +# PORT +# +DEF PORT U 0 40 Y Y 26 F N +F0 "U" 50 100 30 H V C CNN +F1 "PORT" 0 0 30 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +A 325 225 285 -1421 -1278 0 1 0 N 100 50 150 0 +A 376 -275 356 1294 1408 0 1 0 N 150 0 100 -50 +S -100 50 100 -50 0 1 0 N +X ~ 1 250 0 100 L 30 30 1 1 B +X ~ 2 250 0 100 L 30 30 2 1 B +X ~ 3 250 0 100 L 30 30 3 1 B +X ~ 4 250 0 100 L 30 30 4 1 B +X ~ 5 250 0 100 L 30 30 5 1 B +X ~ 6 250 0 100 L 30 30 6 1 B +X ~ 7 250 0 100 L 30 30 7 1 B +X ~ 8 250 0 100 L 30 30 8 1 B +X ~ 9 250 0 100 L 30 30 9 1 B +X ~ 10 250 0 100 L 30 30 10 1 B +X ~ 11 250 0 100 L 30 30 11 1 B +X ~ 12 250 0 100 L 30 30 12 1 B +X ~ 13 250 0 100 L 30 30 13 1 B +X ~ 14 250 0 100 L 30 30 14 1 B +X ~ 15 250 0 100 L 30 30 15 1 B +X ~ 16 250 0 100 L 30 30 16 1 B +X ~ 17 250 0 100 L 30 30 17 1 B +X ~ 18 250 0 100 L 30 30 18 1 B +X ~ 19 250 0 100 L 30 30 19 1 B +X ~ 20 250 0 100 L 30 30 20 1 B +X ~ 21 250 0 100 L 30 30 21 1 B +X ~ 22 250 0 100 L 30 30 22 1 B +X ~ 23 250 0 100 L 30 30 23 1 B +X ~ 24 250 0 100 L 30 30 24 1 B +X ~ 25 250 0 100 L 30 30 25 1 B +X ~ 26 250 0 100 L 30 30 26 1 B +ENDDRAW +ENDDEF +# +# eSim_Diode +# +DEF eSim_Diode D 0 40 N N 1 F N +F0 "D" 0 100 50 H V C CNN +F1 "eSim_Diode" 0 -100 50 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + TO-???* + *SingleDiode + *_Diode_* + *SingleDiode* + D_* +$ENDFPLIST +DRAW +T 0 -100 50 60 0 0 0 A Normal 0 C C +T 0 100 50 60 0 0 0 K Normal 0 C C +P 2 0 1 6 50 50 50 -50 N +P 3 0 1 0 -50 50 50 0 -50 -50 F +X A 1 -150 0 100 R 40 40 1 1 P +X K 2 150 0 100 L 40 40 1 1 P +ENDDRAW +ENDDEF +# +# eSim_NPN +# +DEF eSim_NPN Q 0 0 Y N 1 F N +F0 "Q" -100 50 50 H V R CNN +F1 "eSim_NPN" -50 150 50 H V R CNN +F2 "" 200 100 29 H V C CNN +F3 "" 0 0 60 H V C CNN +ALIAS BC547 Q2N2222 +DRAW +C 50 0 111 0 1 10 N +P 2 0 1 0 25 25 100 100 N +P 3 0 1 0 25 -25 100 -100 100 -100 N +P 3 0 1 20 25 75 25 -75 25 -75 N +P 5 0 1 0 50 -70 70 -50 90 -90 50 -70 50 -70 F +X C 1 100 200 100 D 50 50 1 1 P +X B 2 -200 0 225 R 50 50 1 1 P +X E 3 100 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_PNP +# +DEF eSim_PNP Q 0 0 Y N 1 F N +F0 "Q" -100 50 50 H V R CNN +F1 "eSim_PNP" -50 150 50 H V R CNN +F2 "" 200 100 29 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +C 50 0 111 0 1 10 N +P 2 0 1 0 25 25 100 100 N +P 3 0 1 0 25 -25 100 -100 100 -100 N +P 3 0 1 20 25 75 25 -75 25 -75 N +P 5 0 1 0 90 -70 70 -90 50 -50 90 -70 90 -70 F +X C 1 100 200 100 D 50 50 1 1 P +X B 2 -200 0 225 R 50 50 1 1 P +X E 3 100 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_R +# +DEF eSim_R R 0 0 N Y 1 F N +F0 "R" 50 130 50 H V C CNN +F1 "eSim_R" 50 -50 50 H V C CNN +F2 "" 50 -20 30 H V C CNN +F3 "" 50 50 30 V V C CNN +ALIAS resistor +$FPLIST + R_* + Resistor_* +$ENDFPLIST +DRAW +S 150 10 -50 90 0 1 10 N +X ~ 1 -100 50 50 R 60 60 1 1 P +X ~ 2 200 50 50 L 60 60 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.cir b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.cir new file mode 100644 index 000000000..242f83959 --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.cir @@ -0,0 +1,92 @@ +* C:\FOSSEE\eSim\library\SubcircuitLibrary\DAC0800_Subcircuit\DAC0800_Subcircuit.cir + +* EESchema Netlist Version 1.1 (Spice format) creation date: 12/28/24 13:16:39 + +* To exclude a component from the Spice Netlist add [Spice_Netlist_Enabled] user FIELD set to: N +* To reorder the component spice node sequence add [Spice_Node_Sequence] user FIELD and define sequence: 2,1,0 + +* Sheet Name: / +U1 Net-_Q1-Pad2_ Net-_Q3-Pad2_ Net-_Q4-Pad2_ Net-_D2-Pad1_ Net-_D3-Pad2_ Net-_D1-Pad2_ Net-_Q11-Pad2_ Net-_Q16-Pad2_ Net-_Q21-Pad2_ Net-_Q26-Pad2_ Net-_Q32-Pad2_ Net-_Q37-Pad2_ Net-_Q42-Pad2_ Net-_Q47-Pad2_ Net-_Q14-Pad1_ Net-_Q12-Pad1_ PORT +D2 Net-_D2-Pad1_ Net-_D2-Pad2_ eSim_Diode +D3 Net-_D2-Pad2_ Net-_D3-Pad2_ eSim_Diode +Q1 Net-_D1-Pad1_ Net-_Q1-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q3 Net-_Q2-Pad1_ Net-_Q3-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q2 Net-_Q2-Pad1_ Net-_D1-Pad1_ Net-_D1-Pad2_ eSim_NPN +D1 Net-_D1-Pad1_ Net-_D1-Pad2_ eSim_Diode +Q4 Net-_D2-Pad1_ Net-_Q4-Pad2_ Net-_D1-Pad2_ eSim_NPN +Q8 Net-_Q8-Pad1_ Net-_D2-Pad1_ Net-_D2-Pad1_ eSim_PNP +Q11 Net-_Q11-Pad1_ Net-_Q11-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q13 Net-_Q13-Pad1_ Net-_D2-Pad1_ Net-_D2-Pad1_ eSim_PNP +Q16 Net-_Q16-Pad1_ Net-_Q16-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q18 Net-_Q18-Pad1_ Net-_D2-Pad1_ Net-_D2-Pad1_ eSim_PNP +Q21 Net-_Q21-Pad1_ Net-_Q21-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q23 Net-_Q23-Pad1_ Net-_D2-Pad1_ Net-_D2-Pad1_ eSim_PNP +Q26 Net-_Q26-Pad1_ Net-_Q26-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q29 Net-_Q29-Pad1_ Net-_D2-Pad1_ Net-_D2-Pad1_ eSim_PNP +Q32 Net-_Q32-Pad1_ Net-_Q32-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q34 Net-_Q34-Pad1_ Net-_D2-Pad1_ Net-_D2-Pad1_ eSim_PNP +Q37 Net-_Q37-Pad1_ Net-_Q37-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q39 Net-_Q39-Pad1_ Net-_D2-Pad1_ Net-_D2-Pad1_ eSim_PNP +Q42 Net-_Q42-Pad1_ Net-_Q42-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q45 Net-_D5-Pad1_ Net-_D2-Pad1_ Net-_D2-Pad1_ eSim_PNP +Q47 Net-_D6-Pad1_ Net-_Q47-Pad2_ Net-_D2-Pad1_ eSim_PNP +Q9 Net-_Q14-Pad1_ Net-_Q8-Pad1_ Net-_Q10-Pad1_ eSim_NPN +Q12 Net-_Q12-Pad1_ Net-_Q11-Pad1_ Net-_Q10-Pad1_ eSim_NPN +R2 Net-_Q8-Pad1_ Net-_D2-Pad1_ 800 +R4 Net-_Q11-Pad1_ Net-_D2-Pad1_ 800 +Q6 Net-_Q1-Pad2_ Net-_D2-Pad1_ Net-_Q5-Pad1_ eSim_NPN +Q5 Net-_Q5-Pad1_ Net-_D1-Pad2_ Net-_Q5-Pad3_ eSim_NPN +R1 Net-_Q5-Pad3_ Net-_D1-Pad2_ 5k +Q14 Net-_Q14-Pad1_ Net-_Q13-Pad1_ Net-_Q14-Pad3_ eSim_NPN +Q17 Net-_Q12-Pad1_ Net-_Q16-Pad1_ Net-_Q14-Pad3_ eSim_NPN +R6 Net-_Q13-Pad1_ Net-_D2-Pad1_ 1.6k +R8 Net-_Q16-Pad1_ Net-_D2-Pad1_ 1.6k +Q19 Net-_Q14-Pad1_ Net-_Q18-Pad1_ Net-_Q19-Pad3_ eSim_NPN +Q22 Net-_Q12-Pad1_ Net-_Q21-Pad1_ Net-_Q19-Pad3_ eSim_NPN +R10 Net-_Q18-Pad1_ Net-_D2-Pad1_ 3.2k +R12 Net-_Q21-Pad1_ Net-_D2-Pad1_ 3.2k +Q24 Net-_Q14-Pad1_ Net-_Q23-Pad1_ Net-_Q24-Pad3_ eSim_NPN +Q27 Net-_Q12-Pad1_ Net-_Q26-Pad1_ Net-_Q24-Pad3_ eSim_NPN +R14 Net-_Q23-Pad1_ Net-_D2-Pad1_ 6.4k +R17 Net-_Q26-Pad1_ Net-_D2-Pad1_ 6.4k +Q30 Net-_Q14-Pad1_ Net-_Q29-Pad1_ Net-_Q30-Pad3_ eSim_NPN +Q33 Net-_Q14-Pad1_ Net-_Q32-Pad1_ Net-_Q30-Pad3_ eSim_NPN +R18 Net-_Q29-Pad1_ Net-_D2-Pad1_ 6.4k +R20 Net-_Q32-Pad1_ Net-_D2-Pad1_ 6.4k +Q35 Net-_Q14-Pad1_ Net-_Q34-Pad1_ Net-_Q35-Pad3_ eSim_NPN +Q38 Net-_Q12-Pad1_ Net-_Q37-Pad1_ Net-_Q35-Pad3_ eSim_NPN +R22 Net-_Q34-Pad1_ Net-_D2-Pad1_ 6.4k +R23 Net-_Q37-Pad1_ Net-_D2-Pad1_ 6.4k +Q40 Net-_Q14-Pad1_ Net-_Q39-Pad1_ Net-_Q40-Pad3_ eSim_NPN +Q43 Net-_Q12-Pad1_ Net-_Q42-Pad1_ Net-_Q40-Pad3_ eSim_NPN +R24 Net-_Q39-Pad1_ Net-_D2-Pad1_ 6.4k +R25 Net-_Q42-Pad1_ Net-_D2-Pad1_ 6.4k +Q44 Net-_Q14-Pad1_ Net-_D5-Pad1_ Net-_Q44-Pad3_ eSim_NPN +D5 Net-_D5-Pad1_ Net-_D5-Pad2_ eSim_Diode +D6 Net-_D6-Pad1_ Net-_D5-Pad2_ eSim_Diode +R26 Net-_D5-Pad1_ Net-_D2-Pad1_ 6.4k +R27 Net-_D6-Pad1_ Net-_D2-Pad1_ 6.4k +Q48 Net-_Q12-Pad1_ Net-_D6-Pad1_ Net-_Q44-Pad3_ eSim_NPN +D4 Net-_D2-Pad1_ Net-_D4-Pad2_ eSim_Diode +Q10 Net-_Q10-Pad1_ Net-_D1-Pad2_ Net-_Q10-Pad3_ eSim_NPN +Q7 Net-_D1-Pad2_ Net-_D1-Pad2_ Net-_D4-Pad2_ eSim_PNP +Q15 Net-_Q14-Pad3_ Net-_D1-Pad2_ Net-_Q15-Pad3_ eSim_NPN +Q20 Net-_Q19-Pad3_ Net-_D1-Pad2_ Net-_Q20-Pad3_ eSim_NPN +Q25 Net-_Q24-Pad3_ Net-_D1-Pad2_ Net-_Q25-Pad3_ eSim_NPN +Q31 Net-_Q30-Pad3_ Net-_D2-Pad1_ Net-_Q31-Pad3_ eSim_NPN +Q36 Net-_Q35-Pad3_ Net-_D2-Pad1_ Net-_Q36-Pad3_ eSim_NPN +Q41 Net-_Q40-Pad3_ Net-_D2-Pad1_ Net-_Q36-Pad3_ eSim_NPN +Q46 Net-_D5-Pad2_ Net-_D2-Pad1_ Net-_Q36-Pad3_ eSim_NPN +Q28 Net-_Q28-Pad1_ Net-_D1-Pad2_ Net-_Q28-Pad3_ eSim_NPN +R3 Net-_Q10-Pad3_ Net-_D1-Pad2_ 10k +R7 Net-_Q15-Pad3_ Net-_R5-Pad1_ 10k +R11 Net-_Q20-Pad3_ Net-_R11-Pad2_ 10k +R15 Net-_Q25-Pad3_ Net-_R13-Pad1_ 10k +R19 Net-_Q31-Pad3_ Net-_Q28-Pad1_ 5k +R16 Net-_Q28-Pad3_ Net-_R13-Pad1_ 10k +R13 Net-_R13-Pad1_ Net-_R11-Pad2_ 5k +R9 Net-_R11-Pad2_ Net-_R5-Pad1_ 5k +R5 Net-_R5-Pad1_ Net-_D1-Pad2_ 5k +R21 Net-_Q36-Pad3_ Net-_Q28-Pad1_ 5k + +.end diff --git a/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.cir.out b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.cir.out new file mode 100644 index 000000000..1f37ec145 --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.cir.out @@ -0,0 +1,96 @@ +* c:\fossee\esim\library\subcircuitlibrary\dac0800_subcircuit\dac0800_subcircuit.cir + +.include PNP.lib +.include D.lib +.include NPN.lib +* u1 net-_q1-pad2_ net-_q3-pad2_ net-_q4-pad2_ net-_d2-pad1_ net-_d3-pad2_ net-_d1-pad2_ net-_q11-pad2_ net-_q16-pad2_ net-_q21-pad2_ net-_q26-pad2_ net-_q32-pad2_ net-_q37-pad2_ net-_q42-pad2_ net-_q47-pad2_ net-_q14-pad1_ net-_q12-pad1_ port +d2 net-_d2-pad1_ net-_d2-pad2_ 1N4148 +d3 net-_d2-pad2_ net-_d3-pad2_ 1N4148 +q1 net-_d1-pad1_ net-_q1-pad2_ net-_d2-pad1_ Q2N2907A +q3 net-_q2-pad1_ net-_q3-pad2_ net-_d2-pad1_ Q2N2907A +q2 net-_q2-pad1_ net-_d1-pad1_ net-_d1-pad2_ Q2N2222 +d1 net-_d1-pad1_ net-_d1-pad2_ 1N4148 +q4 net-_d2-pad1_ net-_q4-pad2_ net-_d1-pad2_ Q2N2222 +q8 net-_q8-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q11 net-_q11-pad1_ net-_q11-pad2_ net-_d2-pad1_ Q2N2907A +q13 net-_q13-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q16 net-_q16-pad1_ net-_q16-pad2_ net-_d2-pad1_ Q2N2907A +q18 net-_q18-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q21 net-_q21-pad1_ net-_q21-pad2_ net-_d2-pad1_ Q2N2907A +q23 net-_q23-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q26 net-_q26-pad1_ net-_q26-pad2_ net-_d2-pad1_ Q2N2907A +q29 net-_q29-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q32 net-_q32-pad1_ net-_q32-pad2_ net-_d2-pad1_ Q2N2907A +q34 net-_q34-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q37 net-_q37-pad1_ net-_q37-pad2_ net-_d2-pad1_ Q2N2907A +q39 net-_q39-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q42 net-_q42-pad1_ net-_q42-pad2_ net-_d2-pad1_ Q2N2907A +q45 net-_d5-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q47 net-_d6-pad1_ net-_q47-pad2_ net-_d2-pad1_ Q2N2907A +q9 net-_q14-pad1_ net-_q8-pad1_ net-_q10-pad1_ Q2N2222 +q12 net-_q12-pad1_ net-_q11-pad1_ net-_q10-pad1_ Q2N2222 +r2 net-_q8-pad1_ net-_d2-pad1_ 800 +r4 net-_q11-pad1_ net-_d2-pad1_ 800 +q6 net-_q1-pad2_ net-_d2-pad1_ net-_q5-pad1_ Q2N2222 +q5 net-_q5-pad1_ net-_d1-pad2_ net-_q5-pad3_ Q2N2222 +r1 net-_q5-pad3_ net-_d1-pad2_ 5k +q14 net-_q14-pad1_ net-_q13-pad1_ net-_q14-pad3_ Q2N2222 +q17 net-_q12-pad1_ net-_q16-pad1_ net-_q14-pad3_ Q2N2222 +r6 net-_q13-pad1_ net-_d2-pad1_ 1.6k +r8 net-_q16-pad1_ net-_d2-pad1_ 1.6k +q19 net-_q14-pad1_ net-_q18-pad1_ net-_q19-pad3_ Q2N2222 +q22 net-_q12-pad1_ net-_q21-pad1_ net-_q19-pad3_ Q2N2222 +r10 net-_q18-pad1_ net-_d2-pad1_ 3.2k +r12 net-_q21-pad1_ net-_d2-pad1_ 3.2k +q24 net-_q14-pad1_ net-_q23-pad1_ net-_q24-pad3_ Q2N2222 +q27 net-_q12-pad1_ net-_q26-pad1_ net-_q24-pad3_ Q2N2222 +r14 net-_q23-pad1_ net-_d2-pad1_ 6.4k +r17 net-_q26-pad1_ net-_d2-pad1_ 6.4k +q30 net-_q14-pad1_ net-_q29-pad1_ net-_q30-pad3_ Q2N2222 +q33 net-_q14-pad1_ net-_q32-pad1_ net-_q30-pad3_ Q2N2222 +r18 net-_q29-pad1_ net-_d2-pad1_ 6.4k +r20 net-_q32-pad1_ net-_d2-pad1_ 6.4k +q35 net-_q14-pad1_ net-_q34-pad1_ net-_q35-pad3_ Q2N2222 +q38 net-_q12-pad1_ net-_q37-pad1_ net-_q35-pad3_ Q2N2222 +r22 net-_q34-pad1_ net-_d2-pad1_ 6.4k +r23 net-_q37-pad1_ net-_d2-pad1_ 6.4k +q40 net-_q14-pad1_ net-_q39-pad1_ net-_q40-pad3_ Q2N2222 +q43 net-_q12-pad1_ net-_q42-pad1_ net-_q40-pad3_ Q2N2222 +r24 net-_q39-pad1_ net-_d2-pad1_ 6.4k +r25 net-_q42-pad1_ net-_d2-pad1_ 6.4k +q44 net-_q14-pad1_ net-_d5-pad1_ net-_q44-pad3_ Q2N2222 +d5 net-_d5-pad1_ net-_d5-pad2_ 1N4148 +d6 net-_d6-pad1_ net-_d5-pad2_ 1N4148 +r26 net-_d5-pad1_ net-_d2-pad1_ 6.4k +r27 net-_d6-pad1_ net-_d2-pad1_ 6.4k +q48 net-_q12-pad1_ net-_d6-pad1_ net-_q44-pad3_ Q2N2222 +d4 net-_d2-pad1_ net-_d4-pad2_ 1N4148 +q10 net-_q10-pad1_ net-_d1-pad2_ net-_q10-pad3_ Q2N2222 +q7 net-_d1-pad2_ net-_d1-pad2_ net-_d4-pad2_ Q2N2907A +q15 net-_q14-pad3_ net-_d1-pad2_ net-_q15-pad3_ Q2N2222 +q20 net-_q19-pad3_ net-_d1-pad2_ net-_q20-pad3_ Q2N2222 +q25 net-_q24-pad3_ net-_d1-pad2_ net-_q25-pad3_ Q2N2222 +q31 net-_q30-pad3_ net-_d2-pad1_ net-_q31-pad3_ Q2N2222 +q36 net-_q35-pad3_ net-_d2-pad1_ net-_q36-pad3_ Q2N2222 +q41 net-_q40-pad3_ net-_d2-pad1_ net-_q36-pad3_ Q2N2222 +q46 net-_d5-pad2_ net-_d2-pad1_ net-_q36-pad3_ Q2N2222 +q28 net-_q28-pad1_ net-_d1-pad2_ net-_q28-pad3_ Q2N2222 +r3 net-_q10-pad3_ net-_d1-pad2_ 10k +r7 net-_q15-pad3_ net-_r5-pad1_ 10k +r11 net-_q20-pad3_ net-_r11-pad2_ 10k +r15 net-_q25-pad3_ net-_r13-pad1_ 10k +r19 net-_q31-pad3_ net-_q28-pad1_ 5k +r16 net-_q28-pad3_ net-_r13-pad1_ 10k +r13 net-_r13-pad1_ net-_r11-pad2_ 5k +r9 net-_r11-pad2_ net-_r5-pad1_ 5k +r5 net-_r5-pad1_ net-_d1-pad2_ 5k +r21 net-_q36-pad3_ net-_q28-pad1_ 5k +.tran 0e-00 0e-00 0e-00 + +* Control Statements +.control +run +print allv > plot_data_v.txt +print alli > plot_data_i.txt +.endc +.end diff --git a/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.pro b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.pro new file mode 100644 index 000000000..e27a398be --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.pro @@ -0,0 +1,73 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] +LibName1=adc-dac +LibName2=memory +LibName3=xilinx +LibName4=microcontrollers +LibName5=dsp +LibName6=microchip +LibName7=analog_switches +LibName8=motorola +LibName9=texas +LibName10=intel +LibName11=audio +LibName12=interface +LibName13=digital-audio +LibName14=philips +LibName15=display +LibName16=cypress +LibName17=siliconi +LibName18=opto +LibName19=atmel +LibName20=contrib +LibName21=power +LibName22=eSim_Plot +LibName23=transistors +LibName24=conn +LibName25=eSim_User +LibName26=regul +LibName27=74xx +LibName28=cmos4000 +LibName29=eSim_Analog +LibName30=eSim_Devices +LibName31=eSim_Digital +LibName32=eSim_Hybrid +LibName33=eSim_Miscellaneous +LibName34=eSim_Power +LibName35=eSim_Sources +LibName36=eSim_Subckt +LibName37=eSim_Nghdl +LibName38=eSim_Ngveri +LibName39=eSim_SKY130 +LibName40=eSim_SKY130_Subckts diff --git a/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.sch b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.sch new file mode 100644 index 000000000..f083c62bd --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.sch @@ -0,0 +1,1690 @@ +EESchema Schematic File Version 2 +LIBS:adc-dac +LIBS:memory +LIBS:xilinx +LIBS:microcontrollers +LIBS:dsp +LIBS:microchip +LIBS:analog_switches +LIBS:motorola +LIBS:texas +LIBS:intel +LIBS:audio +LIBS:interface +LIBS:digital-audio +LIBS:philips +LIBS:display +LIBS:cypress +LIBS:siliconi +LIBS:opto +LIBS:atmel +LIBS:contrib +LIBS:power +LIBS:eSim_Plot +LIBS:transistors +LIBS:conn +LIBS:eSim_User +LIBS:regul +LIBS:74xx +LIBS:cmos4000 +LIBS:eSim_Analog +LIBS:eSim_Devices +LIBS:eSim_Digital +LIBS:eSim_Hybrid +LIBS:eSim_Miscellaneous +LIBS:eSim_Power +LIBS:eSim_Sources +LIBS:eSim_Subckt +LIBS:eSim_Nghdl +LIBS:eSim_Ngveri +LIBS:eSim_SKY130 +LIBS:eSim_SKY130_Subckts +LIBS:DAC0800_Subcircuit-cache +EELAYER 25 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L PORT U1 +U 7 1 675D6EED +P 2450 400 +F 0 "U1" H 2500 500 30 0000 C CNN +F 1 "PORT" H 2450 400 30 0000 C CNN +F 2 "" H 2450 400 60 0000 C CNN +F 3 "" H 2450 400 60 0000 C CNN + 7 2450 400 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 8 1 675D6F6B +P 3700 450 +F 0 "U1" H 3750 550 30 0000 C CNN +F 1 "PORT" H 3700 450 30 0000 C CNN +F 2 "" H 3700 450 60 0000 C CNN +F 3 "" H 3700 450 60 0000 C CNN + 8 3700 450 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 9 1 675D6FB4 +P 4950 450 +F 0 "U1" H 5000 550 30 0000 C CNN +F 1 "PORT" H 4950 450 30 0000 C CNN +F 2 "" H 4950 450 60 0000 C CNN +F 3 "" H 4950 450 60 0000 C CNN + 9 4950 450 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 10 1 675D7003 +P 6250 450 +F 0 "U1" H 6300 550 30 0000 C CNN +F 1 "PORT" H 6250 450 30 0000 C CNN +F 2 "" H 6250 450 60 0000 C CNN +F 3 "" H 6250 450 60 0000 C CNN + 10 6250 450 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 11 1 675D7076 +P 7600 450 +F 0 "U1" H 7650 550 30 0000 C CNN +F 1 "PORT" H 7600 450 30 0000 C CNN +F 2 "" H 7600 450 60 0000 C CNN +F 3 "" H 7600 450 60 0000 C CNN + 11 7600 450 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 12 1 675D70C1 +P 8950 430 +F 0 "U1" H 9000 530 30 0000 C CNN +F 1 "PORT" H 8950 430 30 0000 C CNN +F 2 "" H 8950 430 60 0000 C CNN +F 3 "" H 8950 430 60 0000 C CNN + 12 8950 430 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 13 1 675D710C +P 10200 450 +F 0 "U1" H 10250 550 30 0000 C CNN +F 1 "PORT" H 10200 450 30 0000 C CNN +F 2 "" H 10200 450 60 0000 C CNN +F 3 "" H 10200 450 60 0000 C CNN + 13 10200 450 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D2 +U 1 1 675DDBE7 +P 1270 2850 +F 0 "D2" H 1270 2950 50 0000 C CNN +F 1 "eSim_Diode" H 1270 2750 50 0000 C CNN +F 2 "" H 1270 2850 60 0000 C CNN +F 3 "" H 1270 2850 60 0000 C CNN + 1 1270 2850 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D3 +U 1 1 675DDC9A +P 1270 3240 +F 0 "D3" H 1270 3340 50 0000 C CNN +F 1 "eSim_Diode" H 1270 3140 50 0000 C CNN +F 2 "" H 1270 3240 60 0000 C CNN +F 3 "" H 1270 3240 60 0000 C CNN + 1 1270 3240 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 5 1 675DE0B9 +P 770 390 +F 0 "U1" H 820 490 30 0000 C CNN +F 1 "PORT" H 770 390 30 0000 C CNN +F 2 "" H 770 390 60 0000 C CNN +F 3 "" H 770 390 60 0000 C CNN + 5 770 390 + 0 1 1 0 +$EndComp +$Comp +L eSim_PNP Q1 +U 1 1 675E0F37 +P -680 4310 +F 0 "Q1" H -780 4360 50 0000 R CNN +F 1 "eSim_PNP" H -730 4460 50 0000 R CNN +F 2 "" H -480 4410 29 0000 C CNN +F 3 "" H -680 4310 60 0000 C CNN + 1 -680 4310 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q3 +U 1 1 675E1114 +P -80 4310 +F 0 "Q3" H -180 4360 50 0000 R CNN +F 1 "eSim_PNP" H -130 4460 50 0000 R CNN +F 2 "" H 120 4410 29 0000 C CNN +F 3 "" H -80 4310 60 0000 C CNN + 1 -80 4310 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 4 1 675E12F5 +P -380 380 +F 0 "U1" H -330 480 30 0000 C CNN +F 1 "PORT" H -380 380 30 0000 C CNN +F 2 "" H -380 380 60 0000 C CNN +F 3 "" H -380 380 60 0000 C CNN + 4 -380 380 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q2 +U 1 1 675E32F4 +P -280 5420 +F 0 "Q2" H -380 5470 50 0000 R CNN +F 1 "eSim_NPN" H -330 5570 50 0000 R CNN +F 2 "" H -80 5520 29 0000 C CNN +F 3 "" H -280 5420 60 0000 C CNN + 1 -280 5420 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D1 +U 1 1 675E3BA9 +P -580 5870 +F 0 "D1" H -580 5970 50 0000 C CNN +F 1 "eSim_Diode" H -580 5770 50 0000 C CNN +F 2 "" H -580 5870 60 0000 C CNN +F 3 "" H -580 5870 60 0000 C CNN + 1 -580 5870 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q4 +U 1 1 675E3EB7 +P 280 4990 +F 0 "Q4" H 180 5040 50 0000 R CNN +F 1 "eSim_NPN" H 230 5140 50 0000 R CNN +F 2 "" H 480 5090 29 0000 C CNN +F 3 "" H 280 4990 60 0000 C CNN + 1 280 4990 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 3 1 675E407F +P -1440 4990 +F 0 "U1" H -1390 5090 30 0000 C CNN +F 1 "PORT" H -1440 4990 30 0000 C CNN +F 2 "" H -1440 4990 60 0000 C CNN +F 3 "" H -1440 4990 60 0000 C CNN + 3 -1440 4990 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 2 1 675E4A7F +P -1440 4710 +F 0 "U1" H -1390 4810 30 0000 C CNN +F 1 "PORT" H -1440 4710 30 0000 C CNN +F 2 "" H -1440 4710 60 0000 C CNN +F 3 "" H -1440 4710 60 0000 C CNN + 2 -1440 4710 + 1 0 0 -1 +$EndComp +$Comp +L eSim_PNP Q8 +U 1 1 675E6C7F +P 1600 2650 +F 0 "Q8" H 1500 2700 50 0000 R CNN +F 1 "eSim_PNP" H 1550 2800 50 0000 R CNN +F 2 "" H 1800 2750 29 0000 C CNN +F 3 "" H 1600 2650 60 0000 C CNN + 1 1600 2650 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q11 +U 1 1 675E6C85 +P 2200 2650 +F 0 "Q11" H 2100 2700 50 0000 R CNN +F 1 "eSim_PNP" H 2150 2800 50 0000 R CNN +F 2 "" H 2400 2750 29 0000 C CNN +F 3 "" H 2200 2650 60 0000 C CNN + 1 2200 2650 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q13 +U 1 1 675E7583 +P 2850 2650 +F 0 "Q13" H 2750 2700 50 0000 R CNN +F 1 "eSim_PNP" H 2800 2800 50 0000 R CNN +F 2 "" H 3050 2750 29 0000 C CNN +F 3 "" H 2850 2650 60 0000 C CNN + 1 2850 2650 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q16 +U 1 1 675E7589 +P 3450 2650 +F 0 "Q16" H 3350 2700 50 0000 R CNN +F 1 "eSim_PNP" H 3400 2800 50 0000 R CNN +F 2 "" H 3650 2750 29 0000 C CNN +F 3 "" H 3450 2650 60 0000 C CNN + 1 3450 2650 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q18 +U 1 1 675E768D +P 4150 2650 +F 0 "Q18" H 4050 2700 50 0000 R CNN +F 1 "eSim_PNP" H 4100 2800 50 0000 R CNN +F 2 "" H 4350 2750 29 0000 C CNN +F 3 "" H 4150 2650 60 0000 C CNN + 1 4150 2650 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q21 +U 1 1 675E7693 +P 4750 2650 +F 0 "Q21" H 4650 2700 50 0000 R CNN +F 1 "eSim_PNP" H 4700 2800 50 0000 R CNN +F 2 "" H 4950 2750 29 0000 C CNN +F 3 "" H 4750 2650 60 0000 C CNN + 1 4750 2650 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q23 +U 1 1 675E77EB +P 5450 2650 +F 0 "Q23" H 5350 2700 50 0000 R CNN +F 1 "eSim_PNP" H 5400 2800 50 0000 R CNN +F 2 "" H 5650 2750 29 0000 C CNN +F 3 "" H 5450 2650 60 0000 C CNN + 1 5450 2650 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q26 +U 1 1 675E77F1 +P 6050 2650 +F 0 "Q26" H 5950 2700 50 0000 R CNN +F 1 "eSim_PNP" H 6000 2800 50 0000 R CNN +F 2 "" H 6250 2750 29 0000 C CNN +F 3 "" H 6050 2650 60 0000 C CNN + 1 6050 2650 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q29 +U 1 1 675E78FB +P 6750 2650 +F 0 "Q29" H 6650 2700 50 0000 R CNN +F 1 "eSim_PNP" H 6700 2800 50 0000 R CNN +F 2 "" H 6950 2750 29 0000 C CNN +F 3 "" H 6750 2650 60 0000 C CNN + 1 6750 2650 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q32 +U 1 1 675E7901 +P 7350 2650 +F 0 "Q32" H 7250 2700 50 0000 R CNN +F 1 "eSim_PNP" H 7300 2800 50 0000 R CNN +F 2 "" H 7550 2750 29 0000 C CNN +F 3 "" H 7350 2650 60 0000 C CNN + 1 7350 2650 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q34 +U 1 1 675E7A41 +P 8150 2650 +F 0 "Q34" H 8050 2700 50 0000 R CNN +F 1 "eSim_PNP" H 8100 2800 50 0000 R CNN +F 2 "" H 8350 2750 29 0000 C CNN +F 3 "" H 8150 2650 60 0000 C CNN + 1 8150 2650 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q37 +U 1 1 675E7A47 +P 8750 2650 +F 0 "Q37" H 8650 2700 50 0000 R CNN +F 1 "eSim_PNP" H 8700 2800 50 0000 R CNN +F 2 "" H 8950 2750 29 0000 C CNN +F 3 "" H 8750 2650 60 0000 C CNN + 1 8750 2650 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q39 +U 1 1 675E7B67 +P 9400 2650 +F 0 "Q39" H 9300 2700 50 0000 R CNN +F 1 "eSim_PNP" H 9350 2800 50 0000 R CNN +F 2 "" H 9600 2750 29 0000 C CNN +F 3 "" H 9400 2650 60 0000 C CNN + 1 9400 2650 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q42 +U 1 1 675E7B6D +P 10000 2650 +F 0 "Q42" H 9900 2700 50 0000 R CNN +F 1 "eSim_PNP" H 9950 2800 50 0000 R CNN +F 2 "" H 10200 2750 29 0000 C CNN +F 3 "" H 10000 2650 60 0000 C CNN + 1 10000 2650 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q45 +U 1 1 675E7C53 +P 11000 2650 +F 0 "Q45" H 10900 2700 50 0000 R CNN +F 1 "eSim_PNP" H 10950 2800 50 0000 R CNN +F 2 "" H 11200 2750 29 0000 C CNN +F 3 "" H 11000 2650 60 0000 C CNN + 1 11000 2650 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q47 +U 1 1 675E7D08 +P 11990 2640 +F 0 "Q47" H 11890 2690 50 0000 R CNN +F 1 "eSim_PNP" H 11940 2790 50 0000 R CNN +F 2 "" H 12190 2740 29 0000 C CNN +F 3 "" H 11990 2640 60 0000 C CNN + 1 11990 2640 + -1 0 0 1 +$EndComp +$Comp +L eSim_NPN Q9 +U 1 1 675E80A6 +P 1600 3830 +F 0 "Q9" H 1500 3880 50 0000 R CNN +F 1 "eSim_NPN" H 1550 3980 50 0000 R CNN +F 2 "" H 1800 3930 29 0000 C CNN +F 3 "" H 1600 3830 60 0000 C CNN + 1 1600 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q12 +U 1 1 675E8197 +P 2200 3830 +F 0 "Q12" H 2100 3880 50 0000 R CNN +F 1 "eSim_NPN" H 2150 3980 50 0000 R CNN +F 2 "" H 2400 3930 29 0000 C CNN +F 3 "" H 2200 3830 60 0000 C CNN + 1 2200 3830 + -1 0 0 -1 +$EndComp +$Comp +L resistor R2 +U 1 1 675E8716 +P 1350 4200 +F 0 "R2" H 1400 4330 50 0000 C CNN +F 1 "800" H 1400 4150 50 0000 C CNN +F 2 "" H 1400 4180 30 0000 C CNN +F 3 "" V 1400 4250 30 0000 C CNN + 1 1350 4200 + 0 1 1 0 +$EndComp +$Comp +L resistor R4 +U 1 1 675E87C6 +P 2350 4220 +F 0 "R4" H 2400 4350 50 0000 C CNN +F 1 "800" H 2400 4170 50 0000 C CNN +F 2 "" H 2400 4200 30 0000 C CNN +F 3 "" V 2400 4270 30 0000 C CNN + 1 2350 4220 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q6 +U 1 1 675E8CE9 +P 960 4720 +F 0 "Q6" H 860 4770 50 0000 R CNN +F 1 "eSim_NPN" H 910 4870 50 0000 R CNN +F 2 "" H 1160 4820 29 0000 C CNN +F 3 "" H 960 4720 60 0000 C CNN + 1 960 4720 + -1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 1 1 675E8E0C +P -1440 3860 +F 0 "U1" H -1390 3960 30 0000 C CNN +F 1 "PORT" H -1440 3860 30 0000 C CNN +F 2 "" H -1440 3860 60 0000 C CNN +F 3 "" H -1440 3860 60 0000 C CNN + 1 -1440 3860 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q5 +U 1 1 675E9A7E +P 760 5400 +F 0 "Q5" H 660 5450 50 0000 R CNN +F 1 "eSim_NPN" H 710 5550 50 0000 R CNN +F 2 "" H 960 5500 29 0000 C CNN +F 3 "" H 760 5400 60 0000 C CNN + 1 760 5400 + 1 0 0 -1 +$EndComp +$Comp +L resistor R1 +U 1 1 675E9D55 +P 810 5830 +F 0 "R1" H 860 5960 50 0000 C CNN +F 1 "5k" H 860 5780 50 0000 C CNN +F 2 "" H 860 5810 30 0000 C CNN +F 3 "" V 860 5880 30 0000 C CNN + 1 810 5830 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q14 +U 1 1 675EA672 +P 2850 3830 +F 0 "Q14" H 2750 3880 50 0000 R CNN +F 1 "eSim_NPN" H 2800 3980 50 0000 R CNN +F 2 "" H 3050 3930 29 0000 C CNN +F 3 "" H 2850 3830 60 0000 C CNN + 1 2850 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q17 +U 1 1 675EA678 +P 3450 3830 +F 0 "Q17" H 3350 3880 50 0000 R CNN +F 1 "eSim_NPN" H 3400 3980 50 0000 R CNN +F 2 "" H 3650 3930 29 0000 C CNN +F 3 "" H 3450 3830 60 0000 C CNN + 1 3450 3830 + -1 0 0 -1 +$EndComp +$Comp +L resistor R6 +U 1 1 675EA685 +P 2600 4200 +F 0 "R6" H 2650 4330 50 0000 C CNN +F 1 "1.6k" H 2650 4150 50 0000 C CNN +F 2 "" H 2650 4180 30 0000 C CNN +F 3 "" V 2650 4250 30 0000 C CNN + 1 2600 4200 + 0 1 1 0 +$EndComp +$Comp +L resistor R8 +U 1 1 675EA68B +P 3600 4220 +F 0 "R8" H 3650 4350 50 0000 C CNN +F 1 "1.6k" H 3650 4170 50 0000 C CNN +F 2 "" H 3650 4200 30 0000 C CNN +F 3 "" V 3650 4270 30 0000 C CNN + 1 3600 4220 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q19 +U 1 1 675EA8F7 +P 4150 3830 +F 0 "Q19" H 4050 3880 50 0000 R CNN +F 1 "eSim_NPN" H 4100 3980 50 0000 R CNN +F 2 "" H 4350 3930 29 0000 C CNN +F 3 "" H 4150 3830 60 0000 C CNN + 1 4150 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q22 +U 1 1 675EA8FD +P 4750 3830 +F 0 "Q22" H 4650 3880 50 0000 R CNN +F 1 "eSim_NPN" H 4700 3980 50 0000 R CNN +F 2 "" H 4950 3930 29 0000 C CNN +F 3 "" H 4750 3830 60 0000 C CNN + 1 4750 3830 + -1 0 0 -1 +$EndComp +$Comp +L resistor R10 +U 1 1 675EA90A +P 3900 4200 +F 0 "R10" H 3950 4330 50 0000 C CNN +F 1 "3.2k" H 3950 4150 50 0000 C CNN +F 2 "" H 3950 4180 30 0000 C CNN +F 3 "" V 3950 4250 30 0000 C CNN + 1 3900 4200 + 0 1 1 0 +$EndComp +$Comp +L resistor R12 +U 1 1 675EA910 +P 4900 4220 +F 0 "R12" H 4950 4350 50 0000 C CNN +F 1 "3.2k" H 4950 4170 50 0000 C CNN +F 2 "" H 4950 4200 30 0000 C CNN +F 3 "" V 4950 4270 30 0000 C CNN + 1 4900 4220 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q24 +U 1 1 675EB778 +P 5450 3830 +F 0 "Q24" H 5350 3880 50 0000 R CNN +F 1 "eSim_NPN" H 5400 3980 50 0000 R CNN +F 2 "" H 5650 3930 29 0000 C CNN +F 3 "" H 5450 3830 60 0000 C CNN + 1 5450 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q27 +U 1 1 675EB77E +P 6050 3830 +F 0 "Q27" H 5950 3880 50 0000 R CNN +F 1 "eSim_NPN" H 6000 3980 50 0000 R CNN +F 2 "" H 6250 3930 29 0000 C CNN +F 3 "" H 6050 3830 60 0000 C CNN + 1 6050 3830 + -1 0 0 -1 +$EndComp +$Comp +L resistor R14 +U 1 1 675EB78B +P 5200 4200 +F 0 "R14" H 5250 4330 50 0000 C CNN +F 1 "6.4k" H 5250 4150 50 0000 C CNN +F 2 "" H 5250 4180 30 0000 C CNN +F 3 "" V 5250 4250 30 0000 C CNN + 1 5200 4200 + 0 1 1 0 +$EndComp +$Comp +L resistor R17 +U 1 1 675EB791 +P 6200 4220 +F 0 "R17" H 6250 4350 50 0000 C CNN +F 1 "6.4k" H 6250 4170 50 0000 C CNN +F 2 "" H 6250 4200 30 0000 C CNN +F 3 "" V 6250 4270 30 0000 C CNN + 1 6200 4220 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q30 +U 1 1 675EB799 +P 6750 3830 +F 0 "Q30" H 6650 3880 50 0000 R CNN +F 1 "eSim_NPN" H 6700 3980 50 0000 R CNN +F 2 "" H 6950 3930 29 0000 C CNN +F 3 "" H 6750 3830 60 0000 C CNN + 1 6750 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q33 +U 1 1 675EB79F +P 7350 3830 +F 0 "Q33" H 7250 3880 50 0000 R CNN +F 1 "eSim_NPN" H 7300 3980 50 0000 R CNN +F 2 "" H 7550 3930 29 0000 C CNN +F 3 "" H 7350 3830 60 0000 C CNN + 1 7350 3830 + -1 0 0 -1 +$EndComp +$Comp +L resistor R18 +U 1 1 675EB7AC +P 6500 4200 +F 0 "R18" H 6550 4330 50 0000 C CNN +F 1 "6.4k" H 6550 4150 50 0000 C CNN +F 2 "" H 6550 4180 30 0000 C CNN +F 3 "" V 6550 4250 30 0000 C CNN + 1 6500 4200 + 0 1 1 0 +$EndComp +$Comp +L resistor R20 +U 1 1 675EB7B2 +P 7500 4220 +F 0 "R20" H 7550 4350 50 0000 C CNN +F 1 "6.4k" H 7550 4170 50 0000 C CNN +F 2 "" H 7550 4200 30 0000 C CNN +F 3 "" V 7550 4270 30 0000 C CNN + 1 7500 4220 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q35 +U 1 1 675EB7BA +P 8150 3830 +F 0 "Q35" H 8050 3880 50 0000 R CNN +F 1 "eSim_NPN" H 8100 3980 50 0000 R CNN +F 2 "" H 8350 3930 29 0000 C CNN +F 3 "" H 8150 3830 60 0000 C CNN + 1 8150 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q38 +U 1 1 675EB7C0 +P 8750 3830 +F 0 "Q38" H 8650 3880 50 0000 R CNN +F 1 "eSim_NPN" H 8700 3980 50 0000 R CNN +F 2 "" H 8950 3930 29 0000 C CNN +F 3 "" H 8750 3830 60 0000 C CNN + 1 8750 3830 + -1 0 0 -1 +$EndComp +$Comp +L resistor R22 +U 1 1 675EB7CD +P 7900 4200 +F 0 "R22" H 7950 4330 50 0000 C CNN +F 1 "6.4k" H 7950 4150 50 0000 C CNN +F 2 "" H 7950 4180 30 0000 C CNN +F 3 "" V 7950 4250 30 0000 C CNN + 1 7900 4200 + 0 1 1 0 +$EndComp +$Comp +L resistor R23 +U 1 1 675EB7D3 +P 8900 4220 +F 0 "R23" H 8950 4350 50 0000 C CNN +F 1 "6.4k" H 8950 4170 50 0000 C CNN +F 2 "" H 8950 4200 30 0000 C CNN +F 3 "" V 8950 4270 30 0000 C CNN + 1 8900 4220 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q40 +U 1 1 675EC029 +P 9400 3830 +F 0 "Q40" H 9300 3880 50 0000 R CNN +F 1 "eSim_NPN" H 9350 3980 50 0000 R CNN +F 2 "" H 9600 3930 29 0000 C CNN +F 3 "" H 9400 3830 60 0000 C CNN + 1 9400 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q43 +U 1 1 675EC02F +P 10000 3830 +F 0 "Q43" H 9900 3880 50 0000 R CNN +F 1 "eSim_NPN" H 9950 3980 50 0000 R CNN +F 2 "" H 10200 3930 29 0000 C CNN +F 3 "" H 10000 3830 60 0000 C CNN + 1 10000 3830 + -1 0 0 -1 +$EndComp +$Comp +L resistor R24 +U 1 1 675EC03C +P 9150 4200 +F 0 "R24" H 9200 4330 50 0000 C CNN +F 1 "6.4k" H 9200 4150 50 0000 C CNN +F 2 "" H 9200 4180 30 0000 C CNN +F 3 "" V 9200 4250 30 0000 C CNN + 1 9150 4200 + 0 1 1 0 +$EndComp +$Comp +L resistor R25 +U 1 1 675EC042 +P 10150 4220 +F 0 "R25" H 10200 4350 50 0000 C CNN +F 1 "6.4k" H 10200 4170 50 0000 C CNN +F 2 "" H 10200 4200 30 0000 C CNN +F 3 "" V 10200 4270 30 0000 C CNN + 1 10150 4220 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q44 +U 1 1 675EC36A +P 10820 3830 +F 0 "Q44" H 10720 3880 50 0000 R CNN +F 1 "eSim_NPN" H 10770 3980 50 0000 R CNN +F 2 "" H 11020 3930 29 0000 C CNN +F 3 "" H 10820 3830 60 0000 C CNN + 1 10820 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D5 +U 1 1 675ED4D2 +P 11240 3830 +F 0 "D5" H 11240 3930 50 0000 C CNN +F 1 "eSim_Diode" H 11240 3730 50 0000 C CNN +F 2 "" H 11240 3830 60 0000 C CNN +F 3 "" H 11240 3830 60 0000 C CNN + 1 11240 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D6 +U 1 1 675EEB4B +P 11790 3830 +F 0 "D6" H 11790 3930 50 0000 C CNN +F 1 "eSim_Diode" H 11790 3730 50 0000 C CNN +F 2 "" H 11790 3830 60 0000 C CNN +F 3 "" H 11790 3830 60 0000 C CNN + 1 11790 3830 + -1 0 0 1 +$EndComp +$Comp +L resistor R26 +U 1 1 675EFE6E +P 10570 4230 +F 0 "R26" H 10620 4360 50 0000 C CNN +F 1 "6.4k" H 10620 4180 50 0000 C CNN +F 2 "" H 10620 4210 30 0000 C CNN +F 3 "" V 10620 4280 30 0000 C CNN + 1 10570 4230 + 0 1 1 0 +$EndComp +$Comp +L resistor R27 +U 1 1 675EFF87 +P 12330 4270 +F 0 "R27" H 12380 4400 50 0000 C CNN +F 1 "6.4k" H 12380 4220 50 0000 C CNN +F 2 "" H 12380 4250 30 0000 C CNN +F 3 "" V 12380 4320 30 0000 C CNN + 1 12330 4270 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q48 +U 1 1 675F2477 +P 12180 3830 +F 0 "Q48" H 12080 3880 50 0000 R CNN +F 1 "eSim_NPN" H 12130 3980 50 0000 R CNN +F 2 "" H 12380 3930 29 0000 C CNN +F 3 "" H 12180 3830 60 0000 C CNN + 1 12180 3830 + -1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 15 1 675F37F6 +P 12900 3340 +F 0 "U1" H 12950 3440 30 0000 C CNN +F 1 "PORT" H 12900 3340 30 0000 C CNN +F 2 "" H 12900 3340 60 0000 C CNN +F 3 "" H 12900 3340 60 0000 C CNN + 15 12900 3340 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 16 1 675F391A +P 12900 3520 +F 0 "U1" H 12950 3620 30 0000 C CNN +F 1 "PORT" H 12900 3520 30 0000 C CNN +F 2 "" H 12900 3520 60 0000 C CNN +F 3 "" H 12900 3520 60 0000 C CNN + 16 12900 3520 + -1 0 0 1 +$EndComp +Wire Wire Line + 1700 2450 1700 2400 +Wire Wire Line + 1700 2400 2100 2400 +Wire Wire Line + 2100 2400 2100 2450 +Wire Wire Line + 1900 1080 1900 2400 +Wire Wire Line + 2450 2650 2400 2650 +Connection ~ 1900 2400 +Wire Wire Line + 2950 2450 2950 2400 +Wire Wire Line + 2950 2400 3350 2400 +Wire Wire Line + 3350 2400 3350 2450 +Wire Wire Line + 3150 1080 3150 2400 +Wire Wire Line + 3700 2650 3650 2650 +Connection ~ 3150 2400 +Wire Wire Line + 4250 2450 4250 2400 +Wire Wire Line + 4250 2400 4650 2400 +Wire Wire Line + 4450 1080 4450 2400 +Connection ~ 4450 2400 +Wire Wire Line + 5550 2450 5550 2400 +Wire Wire Line + 5550 2400 5950 2400 +Wire Wire Line + 5750 1080 5750 2400 +Connection ~ 5750 2400 +Wire Wire Line + 6850 2450 6850 2400 +Wire Wire Line + 6850 2400 7250 2400 +Wire Wire Line + 7050 1080 7050 2400 +Connection ~ 7050 2400 +Wire Wire Line + 8250 2450 8250 2400 +Wire Wire Line + 8250 2400 8650 2400 +Wire Wire Line + 8450 1080 8450 2400 +Connection ~ 8450 2400 +Wire Wire Line + 9500 2450 9500 2400 +Wire Wire Line + 9500 2400 9900 2400 +Wire Wire Line + 9700 1080 9700 2400 +Connection ~ 9700 2400 +Wire Wire Line + 4650 2400 4650 2450 +Wire Wire Line + 5950 2400 5950 2450 +Wire Wire Line + 7250 2400 7250 2450 +Wire Wire Line + 8650 2400 8650 2450 +Wire Wire Line + 9900 2400 9900 2450 +Wire Wire Line + 1270 1080 1270 2700 +Wire Wire Line + 1270 2650 1400 2650 +Wire Wire Line + 2650 2650 2600 2650 +Wire Wire Line + 2600 2650 2600 2150 +Connection ~ 2600 2150 +Wire Wire Line + 3950 2650 3850 2650 +Wire Wire Line + 3850 2650 3850 2150 +Connection ~ 3850 2150 +Wire Wire Line + 5250 2650 5150 2650 +Wire Wire Line + 5150 2650 5150 2150 +Connection ~ 5150 2150 +Wire Wire Line + 6550 2650 6450 2650 +Wire Wire Line + 6450 2650 6450 2150 +Connection ~ 6450 2150 +Wire Wire Line + 7950 2650 7850 2650 +Wire Wire Line + 7850 2650 7850 2150 +Connection ~ 7850 2150 +Wire Wire Line + 9200 2650 9100 2650 +Wire Wire Line + 9100 2650 9100 2150 +Connection ~ 9100 2150 +Wire Wire Line + 10350 2150 10350 2650 +Wire Wire Line + 10350 2650 10800 2650 +Wire Wire Line + 2450 650 2450 2650 +Wire Wire Line + 3700 2650 3700 700 +Wire Wire Line + 4950 2650 4950 700 +Wire Wire Line + 6250 700 6250 2650 +Wire Wire Line + 7600 2650 7550 2650 +Wire Wire Line + 7600 700 7600 2650 +Wire Wire Line + 8950 680 8950 2650 +Wire Wire Line + 10200 2650 10200 700 +Wire Wire Line + 1270 3000 1270 3090 +Wire Wire Line + 1270 3390 1270 3560 +Wire Wire Line + 1270 3560 770 3560 +Wire Wire Line + 770 3560 770 640 +Wire Wire Line + 11100 2450 11100 2360 +Wire Wire Line + 11100 2360 11890 2360 +Wire Wire Line + 11890 2360 11890 2440 +Connection ~ 1270 2650 +Wire Wire Line + 11500 1080 11500 2360 +Connection ~ 11500 2360 +Connection ~ 5750 1080 +Connection ~ 7050 1080 +Connection ~ 8450 1080 +Connection ~ 9700 1080 +Connection ~ 4450 1080 +Connection ~ 3150 1080 +Wire Wire Line + -380 1080 11500 1080 +Connection ~ 1900 1080 +Wire Wire Line + -580 4060 -180 4060 +Wire Wire Line + -380 630 -380 4060 +Connection ~ -380 4060 +Wire Wire Line + -180 4060 -180 4110 +Wire Wire Line + -580 4110 -580 4060 +Wire Wire Line + -180 4510 -180 5220 +Wire Wire Line + -580 4510 -580 5720 +Wire Wire Line + -580 5420 -480 5420 +Wire Wire Line + -1190 4990 80 4990 +Connection ~ -580 5420 +Wire Wire Line + -1190 4710 200 4710 +Wire Wire Line + 200 4710 200 4310 +Wire Wire Line + 200 4310 120 4310 +Connection ~ -380 1080 +Connection ~ 1270 1080 +Wire Wire Line + 380 4790 380 1080 +Connection ~ 380 1080 +Wire Wire Line + 1330 2150 1330 2650 +Connection ~ 1330 2650 +Wire Wire Line + 10350 2150 1330 2150 +Wire Wire Line + 1700 2850 1700 3150 +Wire Wire Line + 1700 3150 1400 3150 +Wire Wire Line + 1400 3150 1400 4100 +Wire Wire Line + 2400 3140 2400 4120 +Wire Wire Line + 2400 3140 2100 3140 +Wire Wire Line + 2100 3140 2100 2850 +Wire Wire Line + 1700 4030 2100 4030 +Connection ~ 1400 3830 +Connection ~ 2400 3830 +Wire Wire Line + 860 4520 860 3860 +Wire Wire Line + 860 3860 -1190 3860 +Wire Wire Line + -880 4310 -880 3860 +Connection ~ -880 3860 +Wire Wire Line + 860 4920 860 5200 +Wire Wire Line + 380 5190 380 6370 +Wire Wire Line + 860 5600 860 5730 +Wire Wire Line + 2950 2850 2950 3150 +Wire Wire Line + 2950 3150 2650 3150 +Wire Wire Line + 2650 3150 2650 4100 +Wire Wire Line + 3650 3140 3650 4120 +Wire Wire Line + 3650 3140 3350 3140 +Wire Wire Line + 3350 3140 3350 2850 +Wire Wire Line + 2950 4030 3350 4030 +Connection ~ 2650 3830 +Connection ~ 3650 3830 +Wire Wire Line + 4250 2850 4250 3150 +Wire Wire Line + 4250 3150 3950 3150 +Wire Wire Line + 3950 3150 3950 4100 +Wire Wire Line + 4950 3140 4950 4120 +Wire Wire Line + 4950 3140 4650 3140 +Wire Wire Line + 4650 3140 4650 2850 +Wire Wire Line + 4250 4030 4650 4030 +Connection ~ 3950 3830 +Connection ~ 4950 3830 +Wire Wire Line + 5550 2850 5550 3150 +Wire Wire Line + 5550 3150 5250 3150 +Wire Wire Line + 5250 3150 5250 4100 +Wire Wire Line + 6250 3140 6250 4120 +Wire Wire Line + 6250 3140 5950 3140 +Wire Wire Line + 5950 3140 5950 2850 +Wire Wire Line + 5550 4030 5950 4030 +Connection ~ 5250 3830 +Connection ~ 6250 3830 +Wire Wire Line + 6850 2850 6850 3150 +Wire Wire Line + 6850 3150 6550 3150 +Wire Wire Line + 6550 3150 6550 4100 +Wire Wire Line + 7550 3140 7550 4120 +Wire Wire Line + 7550 3140 7250 3140 +Wire Wire Line + 7250 3140 7250 2850 +Wire Wire Line + 6850 4030 7250 4030 +Connection ~ 6550 3830 +Connection ~ 7550 3830 +Wire Wire Line + 8250 2850 8250 3150 +Wire Wire Line + 8250 3150 7950 3150 +Wire Wire Line + 7950 3150 7950 4100 +Wire Wire Line + 8950 3140 8950 4120 +Wire Wire Line + 8950 3140 8650 3140 +Wire Wire Line + 8650 3140 8650 2850 +Wire Wire Line + 8250 4030 8650 4030 +Connection ~ 7950 3830 +Connection ~ 8950 3830 +Wire Wire Line + 9500 2850 9500 3150 +Wire Wire Line + 9500 3150 9200 3150 +Wire Wire Line + 9200 3150 9200 4100 +Wire Wire Line + 10200 3140 10200 4120 +Wire Wire Line + 10200 3140 9900 3140 +Wire Wire Line + 9900 3140 9900 2850 +Wire Wire Line + 9500 4030 9900 4030 +Connection ~ 9200 3830 +Connection ~ 10200 3830 +Wire Wire Line + 11100 2850 11100 3140 +Wire Wire Line + 11100 3140 10620 3140 +Wire Wire Line + 12380 3140 11890 3140 +Wire Wire Line + 11890 3140 11890 2840 +Wire Wire Line + 11390 3830 11640 3830 +Wire Wire Line + 10620 3140 10620 4130 +Connection ~ 10620 3830 +Wire Wire Line + 12380 3140 12380 4170 +Connection ~ 12380 3830 +Wire Wire Line + 11940 3830 12380 3830 +Wire Wire Line + 11090 3830 10620 3830 +Wire Wire Line + 1700 3630 1700 3340 +Wire Wire Line + 1700 3340 12650 3340 +Wire Wire Line + 2100 3630 2100 3520 +Wire Wire Line + 2100 3520 12650 3520 +Wire Wire Line + 2950 3630 2950 3340 +Connection ~ 2950 3340 +Wire Wire Line + 3350 3630 3350 3520 +Connection ~ 3350 3520 +Wire Wire Line + 4250 3630 4250 3340 +Connection ~ 4250 3340 +Wire Wire Line + 4650 3630 4650 3520 +Connection ~ 4650 3520 +Wire Wire Line + 5550 3630 5550 3340 +Connection ~ 5550 3340 +Wire Wire Line + 5950 3630 5950 3520 +Connection ~ 5950 3520 +Wire Wire Line + 6850 3630 6850 3340 +Connection ~ 6850 3340 +Wire Wire Line + 7250 3630 7250 3340 +Connection ~ 7250 3340 +Wire Wire Line + 8250 3630 8250 3340 +Connection ~ 8250 3340 +Wire Wire Line + 8650 3630 8650 3520 +Connection ~ 8650 3520 +Wire Wire Line + 9500 3630 9500 3340 +Connection ~ 9500 3340 +Wire Wire Line + 9900 3630 9900 3520 +Connection ~ 9900 3520 +Wire Wire Line + 10920 3630 10920 3340 +Connection ~ 10920 3340 +Wire Wire Line + 12080 3630 12080 3520 +Connection ~ 12080 3520 +Wire Wire Line + 10920 4030 12080 4030 +Wire Wire Line + 12380 4720 12380 4470 +Wire Wire Line + 1160 4720 12380 4720 +Wire Wire Line + 1400 4720 1400 4400 +$Comp +L eSim_Diode D4 +U 1 1 675FA513 +P 1270 4970 +F 0 "D4" H 1270 5070 50 0000 C CNN +F 1 "eSim_Diode" H 1270 4870 50 0000 C CNN +F 2 "" H 1270 4970 60 0000 C CNN +F 3 "" H 1270 4970 60 0000 C CNN + 1 1270 4970 + 0 1 1 0 +$EndComp +Connection ~ 1400 4720 +Connection ~ 1270 4720 +Wire Wire Line + 1270 4080 380 4080 +Connection ~ 380 4080 +Wire Wire Line + 2400 4420 2400 4720 +Connection ~ 2400 4720 +Wire Wire Line + 2650 4400 2650 4720 +Connection ~ 2650 4720 +Wire Wire Line + 3650 4420 3650 4720 +Connection ~ 3650 4720 +Wire Wire Line + 3950 4400 3950 4720 +Connection ~ 3950 4720 +Wire Wire Line + 4950 4420 4950 4720 +Connection ~ 4950 4720 +Wire Wire Line + 5250 4400 5250 4720 +Connection ~ 5250 4720 +Wire Wire Line + 6250 4420 6250 5450 +Connection ~ 6250 4720 +Wire Wire Line + 6550 4400 6550 4720 +Connection ~ 6550 4720 +Wire Wire Line + 7550 4420 7550 4720 +Connection ~ 7550 4720 +Wire Wire Line + 7950 4400 7950 4720 +Connection ~ 7950 4720 +Wire Wire Line + 8950 4420 8950 4720 +Connection ~ 8950 4720 +Wire Wire Line + 9200 4400 9200 4720 +Connection ~ 9200 4720 +Wire Wire Line + 10200 4420 10200 4720 +Connection ~ 10200 4720 +Wire Wire Line + 10620 4430 10620 4720 +Connection ~ 10620 4720 +$Comp +L eSim_NPN Q10 +U 1 1 675FEC98 +P 1790 5400 +F 0 "Q10" H 1690 5450 50 0000 R CNN +F 1 "eSim_NPN" H 1740 5550 50 0000 R CNN +F 2 "" H 1990 5500 29 0000 C CNN +F 3 "" H 1790 5400 60 0000 C CNN + 1 1790 5400 + 1 0 0 -1 +$EndComp +Wire Wire Line + 1270 4080 1270 4820 +Wire Wire Line + 1270 5200 1270 5120 +Wire Wire Line + 380 5400 1590 5400 +Connection ~ 560 5400 +$Comp +L eSim_PNP Q7 +U 1 1 676EC938 +P 1170 5400 +F 0 "Q7" H 1070 5450 50 0000 R CNN +F 1 "eSim_PNP" H 1120 5550 50 0000 R CNN +F 2 "" H 1370 5500 29 0000 C CNN +F 3 "" H 1170 5400 60 0000 C CNN + 1 1170 5400 + 1 0 0 1 +$EndComp +$Comp +L eSim_NPN Q15 +U 1 1 676ED46A +P 3030 5420 +F 0 "Q15" H 2930 5470 50 0000 R CNN +F 1 "eSim_NPN" H 2980 5570 50 0000 R CNN +F 2 "" H 3230 5520 29 0000 C CNN +F 3 "" H 3030 5420 60 0000 C CNN + 1 3030 5420 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q20 +U 1 1 676ED630 +P 4340 5430 +F 0 "Q20" H 4240 5480 50 0000 R CNN +F 1 "eSim_NPN" H 4290 5580 50 0000 R CNN +F 2 "" H 4540 5530 29 0000 C CNN +F 3 "" H 4340 5430 60 0000 C CNN + 1 4340 5430 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q25 +U 1 1 676ED636 +P 5630 5450 +F 0 "Q25" H 5530 5500 50 0000 R CNN +F 1 "eSim_NPN" H 5580 5600 50 0000 R CNN +F 2 "" H 5830 5550 29 0000 C CNN +F 3 "" H 5630 5450 60 0000 C CNN + 1 5630 5450 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q31 +U 1 1 676ED948 +P 6960 5450 +F 0 "Q31" H 6860 5500 50 0000 R CNN +F 1 "eSim_NPN" H 6910 5600 50 0000 R CNN +F 2 "" H 7160 5550 29 0000 C CNN +F 3 "" H 6960 5450 60 0000 C CNN + 1 6960 5450 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q36 +U 1 1 676ED94E +P 8340 5470 +F 0 "Q36" H 8240 5520 50 0000 R CNN +F 1 "eSim_NPN" H 8290 5620 50 0000 R CNN +F 2 "" H 8540 5570 29 0000 C CNN +F 3 "" H 8340 5470 60 0000 C CNN + 1 8340 5470 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q41 +U 1 1 676ED954 +P 9620 5480 +F 0 "Q41" H 9520 5530 50 0000 R CNN +F 1 "eSim_NPN" H 9570 5630 50 0000 R CNN +F 2 "" H 9820 5580 29 0000 C CNN +F 3 "" H 9620 5480 60 0000 C CNN + 1 9620 5480 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q46 +U 1 1 676ED95A +P 11420 5500 +F 0 "Q46" H 11320 5550 50 0000 R CNN +F 1 "eSim_NPN" H 11370 5650 50 0000 R CNN +F 2 "" H 11620 5600 29 0000 C CNN +F 3 "" H 11420 5500 60 0000 C CNN + 1 11420 5500 + 1 0 0 -1 +$EndComp +Connection ~ 1890 4030 +Wire Wire Line + 3130 5220 3130 4030 +Connection ~ 3130 4030 +Wire Wire Line + 4440 5230 4440 4030 +Connection ~ 4440 4030 +Wire Wire Line + 5730 5250 5730 4030 +Connection ~ 5730 4030 +Wire Wire Line + 7060 5250 7060 4030 +Connection ~ 7060 4030 +Wire Wire Line + 8440 5270 8440 4030 +Connection ~ 8440 4030 +Wire Wire Line + 9720 5280 9720 4030 +Connection ~ 9720 4030 +Wire Wire Line + 11520 5300 11520 3830 +Connection ~ 11520 3830 +Wire Wire Line + 11220 5500 9420 5500 +Wire Wire Line + 9420 5500 9420 5480 +Wire Wire Line + 9420 5480 8140 5480 +Wire Wire Line + 8140 5480 8140 5470 +Wire Wire Line + 8140 5470 6760 5470 +Wire Wire Line + 6760 5470 6760 5450 +Wire Wire Line + 4140 5450 4140 5430 +Wire Wire Line + 4140 5430 2830 5430 +Wire Wire Line + 2830 5430 2830 5420 +Wire Wire Line + 2830 5420 1590 5420 +Wire Wire Line + 1590 5420 1590 5400 +Connection ~ 970 5400 +Wire Wire Line + 5430 5450 4140 5450 +Wire Wire Line + 6760 5450 6250 5450 +$Comp +L eSim_NPN Q28 +U 1 1 676FA4E3 +P 6170 5980 +F 0 "Q28" H 6070 6030 50 0000 R CNN +F 1 "eSim_NPN" H 6120 6130 50 0000 R CNN +F 2 "" H 6370 6080 29 0000 C CNN +F 3 "" H 6170 5980 60 0000 C CNN + 1 6170 5980 + 1 0 0 -1 +$EndComp +Wire Wire Line + 5970 5980 5970 5490 +Wire Wire Line + 5970 5490 5430 5490 +Wire Wire Line + 5430 5490 5430 5450 +$Comp +L resistor R3 +U 1 1 67702BB0 +P 1840 5830 +F 0 "R3" H 1890 5960 50 0000 C CNN +F 1 "10k" H 1890 5780 50 0000 C CNN +F 2 "" H 1890 5810 30 0000 C CNN +F 3 "" V 1890 5880 30 0000 C CNN + 1 1840 5830 + 0 1 1 0 +$EndComp +$Comp +L resistor R7 +U 1 1 677031F4 +P 3080 5830 +F 0 "R7" H 3130 5960 50 0000 C CNN +F 1 "10k" H 3130 5780 50 0000 C CNN +F 2 "" H 3130 5810 30 0000 C CNN +F 3 "" V 3130 5880 30 0000 C CNN + 1 3080 5830 + 0 1 1 0 +$EndComp +$Comp +L resistor R11 +U 1 1 67703574 +P 4390 5830 +F 0 "R11" H 4440 5960 50 0000 C CNN +F 1 "10k" H 4440 5780 50 0000 C CNN +F 2 "" H 4440 5810 30 0000 C CNN +F 3 "" V 4440 5880 30 0000 C CNN + 1 4390 5830 + 0 1 1 0 +$EndComp +$Comp +L resistor R15 +U 1 1 6770357A +P 5680 5830 +F 0 "R15" H 5730 5960 50 0000 C CNN +F 1 "10k" H 5730 5780 50 0000 C CNN +F 2 "" H 5730 5810 30 0000 C CNN +F 3 "" V 5730 5880 30 0000 C CNN + 1 5680 5830 + 0 1 1 0 +$EndComp +$Comp +L resistor R19 +U 1 1 677041F3 +P 7010 5830 +F 0 "R19" H 7060 5960 50 0000 C CNN +F 1 "5k" H 7060 5780 50 0000 C CNN +F 2 "" H 7060 5810 30 0000 C CNN +F 3 "" V 7060 5880 30 0000 C CNN + 1 7010 5830 + 0 1 1 0 +$EndComp +$Comp +L resistor R16 +U 1 1 677049B7 +P 6040 6320 +F 0 "R16" H 6090 6450 50 0000 C CNN +F 1 "10k" H 6090 6270 50 0000 C CNN +F 2 "" H 6090 6300 30 0000 C CNN +F 3 "" V 6090 6370 30 0000 C CNN + 1 6040 6320 + -1 0 0 1 +$EndComp +$Comp +L resistor R13 +U 1 1 67704B20 +P 5170 6320 +F 0 "R13" H 5220 6450 50 0000 C CNN +F 1 "5k" H 5220 6270 50 0000 C CNN +F 2 "" H 5220 6300 30 0000 C CNN +F 3 "" V 5220 6370 30 0000 C CNN + 1 5170 6320 + -1 0 0 1 +$EndComp +$Comp +L resistor R9 +U 1 1 67704CEE +P 3810 6320 +F 0 "R9" H 3860 6450 50 0000 C CNN +F 1 "5k" H 3860 6270 50 0000 C CNN +F 2 "" H 3860 6300 30 0000 C CNN +F 3 "" V 3860 6370 30 0000 C CNN + 1 3810 6320 + -1 0 0 1 +$EndComp +$Comp +L resistor R5 +U 1 1 67704F7E +P 2590 6320 +F 0 "R5" H 2640 6450 50 0000 C CNN +F 1 "5k" H 2640 6270 50 0000 C CNN +F 2 "" H 2640 6300 30 0000 C CNN +F 3 "" V 2640 6370 30 0000 C CNN + 1 2590 6320 + -1 0 0 1 +$EndComp +$Comp +L resistor R21 +U 1 1 67705310 +P 7830 6320 +F 0 "R21" H 7880 6450 50 0000 C CNN +F 1 "5k" H 7880 6270 50 0000 C CNN +F 2 "" H 7880 6300 30 0000 C CNN +F 3 "" V 7880 6370 30 0000 C CNN + 1 7830 6320 + -1 0 0 1 +$EndComp +Wire Wire Line + -580 6020 -580 6370 +Wire Wire Line + -580 6370 2390 6370 +Wire Wire Line + 2690 6370 3610 6370 +Wire Wire Line + 3910 6370 4970 6370 +Wire Wire Line + 5270 6370 5840 6370 +Wire Wire Line + 7930 6370 11520 6370 +Wire Wire Line + 11520 6370 11520 5700 +Wire Wire Line + 1890 5730 1890 5600 +Wire Wire Line + 3130 5730 3130 5620 +Wire Wire Line + 4440 5730 4440 5630 +Wire Wire Line + 5730 5730 5730 5650 +Wire Wire Line + 7060 5730 7060 5650 +Wire Wire Line + 1890 6030 1890 6370 +Connection ~ 1890 6370 +Wire Wire Line + 3130 6030 3130 6370 +Connection ~ 3130 6370 +Wire Wire Line + 4440 6030 4440 6370 +Connection ~ 4440 6370 +Wire Wire Line + 5730 6030 5730 6370 +Connection ~ 5730 6370 +Wire Wire Line + 6640 6370 7630 6370 +Wire Wire Line + 7060 6370 7060 6030 +Wire Wire Line + 6270 6180 6270 6370 +Wire Wire Line + 6270 6370 6140 6370 +Wire Wire Line + 6270 5780 6270 5740 +Wire Wire Line + 6270 5740 6640 5740 +Wire Wire Line + 6640 5740 6640 6370 +Connection ~ 7060 6370 +Wire Wire Line + 9720 5680 9720 6370 +Connection ~ 9720 6370 +Wire Wire Line + 8440 5670 8440 6370 +Connection ~ 8440 6370 +$Comp +L PORT U1 +U 14 1 6770EB8E +P 12190 440 +F 0 "U1" H 12240 540 30 0000 C CNN +F 1 "PORT" H 12190 440 30 0000 C CNN +F 2 "" H 12190 440 60 0000 C CNN +F 3 "" H 12190 440 60 0000 C CNN + 14 12190 440 + 0 1 1 0 +$EndComp +Wire Wire Line + 12190 2640 12190 690 +Wire Wire Line + -180 5620 -180 6370 +Connection ~ -180 6370 +Wire Wire Line + 860 6030 860 6370 +Connection ~ 860 6370 +$Comp +L PORT U1 +U 6 1 67713869 +P 1270 6890 +F 0 "U1" H 1320 6990 30 0000 C CNN +F 1 "PORT" H 1270 6890 30 0000 C CNN +F 2 "" H 1270 6890 60 0000 C CNN +F 3 "" H 1270 6890 60 0000 C CNN + 6 1270 6890 + 0 -1 -1 0 +$EndComp +Wire Wire Line + 1270 5600 1270 6640 +Connection ~ 1270 6370 +Wire Wire Line + 1890 5200 1890 4030 +Connection ~ 380 6370 +Connection ~ 380 5400 +$EndSCHEMATC diff --git a/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.sub b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.sub new file mode 100644 index 000000000..b282b9c54 --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit.sub @@ -0,0 +1,90 @@ +* Subcircuit DAC0800_Subcircuit +.subckt DAC0800_Subcircuit net-_q1-pad2_ net-_q3-pad2_ net-_q4-pad2_ net-_d2-pad1_ net-_d3-pad2_ net-_d1-pad2_ net-_q11-pad2_ net-_q16-pad2_ net-_q21-pad2_ net-_q26-pad2_ net-_q32-pad2_ net-_q37-pad2_ net-_q42-pad2_ net-_q47-pad2_ net-_q14-pad1_ net-_q12-pad1_ +* c:\fossee\esim\library\subcircuitlibrary\dac0800_subcircuit\dac0800_subcircuit.cir +.include PNP.lib +.include D.lib +.include NPN.lib +d2 net-_d2-pad1_ net-_d2-pad2_ 1N4148 +d3 net-_d2-pad2_ net-_d3-pad2_ 1N4148 +q1 net-_d1-pad1_ net-_q1-pad2_ net-_d2-pad1_ Q2N2907A +q3 net-_q2-pad1_ net-_q3-pad2_ net-_d2-pad1_ Q2N2907A +q2 net-_q2-pad1_ net-_d1-pad1_ net-_d1-pad2_ Q2N2222 +d1 net-_d1-pad1_ net-_d1-pad2_ 1N4148 +q4 net-_d2-pad1_ net-_q4-pad2_ net-_d1-pad2_ Q2N2222 +q8 net-_q8-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q11 net-_q11-pad1_ net-_q11-pad2_ net-_d2-pad1_ Q2N2907A +q13 net-_q13-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q16 net-_q16-pad1_ net-_q16-pad2_ net-_d2-pad1_ Q2N2907A +q18 net-_q18-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q21 net-_q21-pad1_ net-_q21-pad2_ net-_d2-pad1_ Q2N2907A +q23 net-_q23-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q26 net-_q26-pad1_ net-_q26-pad2_ net-_d2-pad1_ Q2N2907A +q29 net-_q29-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q32 net-_q32-pad1_ net-_q32-pad2_ net-_d2-pad1_ Q2N2907A +q34 net-_q34-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q37 net-_q37-pad1_ net-_q37-pad2_ net-_d2-pad1_ Q2N2907A +q39 net-_q39-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q42 net-_q42-pad1_ net-_q42-pad2_ net-_d2-pad1_ Q2N2907A +q45 net-_d5-pad1_ net-_d2-pad1_ net-_d2-pad1_ Q2N2907A +q47 net-_d6-pad1_ net-_q47-pad2_ net-_d2-pad1_ Q2N2907A +q9 net-_q14-pad1_ net-_q8-pad1_ net-_q10-pad1_ Q2N2222 +q12 net-_q12-pad1_ net-_q11-pad1_ net-_q10-pad1_ Q2N2222 +r2 net-_q8-pad1_ net-_d2-pad1_ 800 +r4 net-_q11-pad1_ net-_d2-pad1_ 800 +q6 net-_q1-pad2_ net-_d2-pad1_ net-_q5-pad1_ Q2N2222 +q5 net-_q5-pad1_ net-_d1-pad2_ net-_q5-pad3_ Q2N2222 +r1 net-_q5-pad3_ net-_d1-pad2_ 5k +q14 net-_q14-pad1_ net-_q13-pad1_ net-_q14-pad3_ Q2N2222 +q17 net-_q12-pad1_ net-_q16-pad1_ net-_q14-pad3_ Q2N2222 +r6 net-_q13-pad1_ net-_d2-pad1_ 1.6k +r8 net-_q16-pad1_ net-_d2-pad1_ 1.6k +q19 net-_q14-pad1_ net-_q18-pad1_ net-_q19-pad3_ Q2N2222 +q22 net-_q12-pad1_ net-_q21-pad1_ net-_q19-pad3_ Q2N2222 +r10 net-_q18-pad1_ net-_d2-pad1_ 3.2k +r12 net-_q21-pad1_ net-_d2-pad1_ 3.2k +q24 net-_q14-pad1_ net-_q23-pad1_ net-_q24-pad3_ Q2N2222 +q27 net-_q12-pad1_ net-_q26-pad1_ net-_q24-pad3_ Q2N2222 +r14 net-_q23-pad1_ net-_d2-pad1_ 6.4k +r17 net-_q26-pad1_ net-_d2-pad1_ 6.4k +q30 net-_q14-pad1_ net-_q29-pad1_ net-_q30-pad3_ Q2N2222 +q33 net-_q14-pad1_ net-_q32-pad1_ net-_q30-pad3_ Q2N2222 +r18 net-_q29-pad1_ net-_d2-pad1_ 6.4k +r20 net-_q32-pad1_ net-_d2-pad1_ 6.4k +q35 net-_q14-pad1_ net-_q34-pad1_ net-_q35-pad3_ Q2N2222 +q38 net-_q12-pad1_ net-_q37-pad1_ net-_q35-pad3_ Q2N2222 +r22 net-_q34-pad1_ net-_d2-pad1_ 6.4k +r23 net-_q37-pad1_ net-_d2-pad1_ 6.4k +q40 net-_q14-pad1_ net-_q39-pad1_ net-_q40-pad3_ Q2N2222 +q43 net-_q12-pad1_ net-_q42-pad1_ net-_q40-pad3_ Q2N2222 +r24 net-_q39-pad1_ net-_d2-pad1_ 6.4k +r25 net-_q42-pad1_ net-_d2-pad1_ 6.4k +q44 net-_q14-pad1_ net-_d5-pad1_ net-_q44-pad3_ Q2N2222 +d5 net-_d5-pad1_ net-_d5-pad2_ 1N4148 +d6 net-_d6-pad1_ net-_d5-pad2_ 1N4148 +r26 net-_d5-pad1_ net-_d2-pad1_ 6.4k +r27 net-_d6-pad1_ net-_d2-pad1_ 6.4k +q48 net-_q12-pad1_ net-_d6-pad1_ net-_q44-pad3_ Q2N2222 +d4 net-_d2-pad1_ net-_d4-pad2_ 1N4148 +q10 net-_q10-pad1_ net-_d1-pad2_ net-_q10-pad3_ Q2N2222 +q7 net-_d1-pad2_ net-_d1-pad2_ net-_d4-pad2_ Q2N2907A +q15 net-_q14-pad3_ net-_d1-pad2_ net-_q15-pad3_ Q2N2222 +q20 net-_q19-pad3_ net-_d1-pad2_ net-_q20-pad3_ Q2N2222 +q25 net-_q24-pad3_ net-_d1-pad2_ net-_q25-pad3_ Q2N2222 +q31 net-_q30-pad3_ net-_d2-pad1_ net-_q31-pad3_ Q2N2222 +q36 net-_q35-pad3_ net-_d2-pad1_ net-_q36-pad3_ Q2N2222 +q41 net-_q40-pad3_ net-_d2-pad1_ net-_q36-pad3_ Q2N2222 +q46 net-_d5-pad2_ net-_d2-pad1_ net-_q36-pad3_ Q2N2222 +q28 net-_q28-pad1_ net-_d1-pad2_ net-_q28-pad3_ Q2N2222 +r3 net-_q10-pad3_ net-_d1-pad2_ 10k +r7 net-_q15-pad3_ net-_r5-pad1_ 10k +r11 net-_q20-pad3_ net-_r11-pad2_ 10k +r15 net-_q25-pad3_ net-_r13-pad1_ 10k +r19 net-_q31-pad3_ net-_q28-pad1_ 5k +r16 net-_q28-pad3_ net-_r13-pad1_ 10k +r13 net-_r13-pad1_ net-_r11-pad2_ 5k +r9 net-_r11-pad2_ net-_r5-pad1_ 5k +r5 net-_r5-pad1_ net-_d1-pad2_ 5k +r21 net-_q36-pad3_ net-_q28-pad1_ 5k +* Control Statements + +.ends DAC0800_Subcircuit \ No newline at end of file diff --git a/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit_Previous_Values.xml b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit_Previous_Values.xml new file mode 100644 index 000000000..d4931eccd --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/DAC0800_Subcircuit_Previous_Values.xml @@ -0,0 +1 @@ +C:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libtruefalsefalseHzHz0Volts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or Amperessecsecsec \ No newline at end of file diff --git a/library/SubcircuitLibrary/DAC0800/NPN.lib b/library/SubcircuitLibrary/DAC0800/NPN.lib new file mode 100644 index 000000000..be5f3073a --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/NPN.lib @@ -0,0 +1,4 @@ +.model Q2N2222 NPN( Is=14.34f Xti=3 Eg=1.11 Vaf=74.03 Bf=400 Ne=1.307 ++ Ise=14.34f Ikf=0.2847 Xtb=1.5 Br=6.092 Nc=2 Isc=0 Ikr=0 Rc=1 Cjc=7.306p ++ Mjc=0.3416 Vjc=0.75 Fc=0.5 Cje=22.01p Mje=0.377 Vje=0.75 Tr=46.91n Tf=411.1p ++ Itf=0.6 Vtf=1.7 Xtf=3 Rb=10) diff --git a/library/SubcircuitLibrary/DAC0800/PNP.lib b/library/SubcircuitLibrary/DAC0800/PNP.lib new file mode 100644 index 000000000..7edda0eab --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/PNP.lib @@ -0,0 +1,4 @@ +.model Q2N2907A PNP(Is=650.6E-18 Xti=3 Eg=1.11 Vaf=115.7 Bf=231.7 Ne=1.829 ++ Ise=54.81f Ikf=1.079 Xtb=1.5 Br=3.563 Nc=2 Isc=0 Ikr=0 Rc=.715 ++ Cjc=14.76p Mjc=.5383 Vjc=.75 Fc=.5 Cje=19.82p Mje=.3357 Vje=.75 ++ Tr=111.3n Tf=603.7p Itf=.65 Vtf=5 Xtf=1.7 Rb=10) diff --git a/library/SubcircuitLibrary/DAC0800/analysis b/library/SubcircuitLibrary/DAC0800/analysis new file mode 100644 index 000000000..ebd5c0a94 --- /dev/null +++ b/library/SubcircuitLibrary/DAC0800/analysis @@ -0,0 +1 @@ +.tran 0e-00 0e-00 0e-00 \ No newline at end of file diff --git a/library/SubcircuitLibrary/OP497/D.lib b/library/SubcircuitLibrary/OP497/D.lib new file mode 100644 index 000000000..f53bf3e03 --- /dev/null +++ b/library/SubcircuitLibrary/OP497/D.lib @@ -0,0 +1,2 @@ +.model 1N4148 D(is=2.495E-09 rs=4.755E-01 n=1.679E+00 tt=3.030E-09 cjo=1.700E-12 vj=1 m=1.959E-01 bv=1.000E+02 ibv=1.000E-04) + diff --git a/library/SubcircuitLibrary/OP497/NPN.lib b/library/SubcircuitLibrary/OP497/NPN.lib new file mode 100644 index 000000000..be5f3073a --- /dev/null +++ b/library/SubcircuitLibrary/OP497/NPN.lib @@ -0,0 +1,4 @@ +.model Q2N2222 NPN( Is=14.34f Xti=3 Eg=1.11 Vaf=74.03 Bf=400 Ne=1.307 ++ Ise=14.34f Ikf=0.2847 Xtb=1.5 Br=6.092 Nc=2 Isc=0 Ikr=0 Rc=1 Cjc=7.306p ++ Mjc=0.3416 Vjc=0.75 Fc=0.5 Cje=22.01p Mje=0.377 Vje=0.75 Tr=46.91n Tf=411.1p ++ Itf=0.6 Vtf=1.7 Xtf=3 Rb=10) diff --git a/library/SubcircuitLibrary/OP497/OP497_Subcircuit-cache.lib b/library/SubcircuitLibrary/OP497/OP497_Subcircuit-cache.lib new file mode 100644 index 000000000..76ef5bfc3 --- /dev/null +++ b/library/SubcircuitLibrary/OP497/OP497_Subcircuit-cache.lib @@ -0,0 +1,145 @@ +EESchema-LIBRARY Version 2.3 +#encoding utf-8 +# +# PORT +# +DEF PORT U 0 40 Y Y 26 F N +F0 "U" 50 100 30 H V C CNN +F1 "PORT" 0 0 30 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +A 325 225 285 -1421 -1278 0 1 0 N 100 50 150 0 +A 376 -275 356 1294 1408 0 1 0 N 150 0 100 -50 +S -100 50 100 -50 0 1 0 N +X ~ 1 250 0 100 L 30 30 1 1 B +X ~ 2 250 0 100 L 30 30 2 1 B +X ~ 3 250 0 100 L 30 30 3 1 B +X ~ 4 250 0 100 L 30 30 4 1 B +X ~ 5 250 0 100 L 30 30 5 1 B +X ~ 6 250 0 100 L 30 30 6 1 B +X ~ 7 250 0 100 L 30 30 7 1 B +X ~ 8 250 0 100 L 30 30 8 1 B +X ~ 9 250 0 100 L 30 30 9 1 B +X ~ 10 250 0 100 L 30 30 10 1 B +X ~ 11 250 0 100 L 30 30 11 1 B +X ~ 12 250 0 100 L 30 30 12 1 B +X ~ 13 250 0 100 L 30 30 13 1 B +X ~ 14 250 0 100 L 30 30 14 1 B +X ~ 15 250 0 100 L 30 30 15 1 B +X ~ 16 250 0 100 L 30 30 16 1 B +X ~ 17 250 0 100 L 30 30 17 1 B +X ~ 18 250 0 100 L 30 30 18 1 B +X ~ 19 250 0 100 L 30 30 19 1 B +X ~ 20 250 0 100 L 30 30 20 1 B +X ~ 21 250 0 100 L 30 30 21 1 B +X ~ 22 250 0 100 L 30 30 22 1 B +X ~ 23 250 0 100 L 30 30 23 1 B +X ~ 24 250 0 100 L 30 30 24 1 B +X ~ 25 250 0 100 L 30 30 25 1 B +X ~ 26 250 0 100 L 30 30 26 1 B +ENDDRAW +ENDDEF +# +# eSim_C +# +DEF eSim_C C 0 10 N Y 1 F N +F0 "C" 25 100 50 H V L CNN +F1 "eSim_C" 25 -100 50 H V L CNN +F2 "" 38 -150 30 H V C CNN +F3 "" 0 0 60 H V C CNN +ALIAS capacitor +$FPLIST + C_* +$ENDFPLIST +DRAW +P 2 0 1 20 -80 -30 80 -30 N +P 2 0 1 20 -80 30 80 30 N +X ~ 1 0 150 110 D 40 40 1 1 P +X ~ 2 0 -150 110 U 40 40 1 1 P +ENDDRAW +ENDDEF +# +# eSim_Diode +# +DEF eSim_Diode D 0 40 N N 1 F N +F0 "D" 0 100 50 H V C CNN +F1 "eSim_Diode" 0 -100 50 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + TO-???* + *SingleDiode + *_Diode_* + *SingleDiode* + D_* +$ENDFPLIST +DRAW +T 0 -100 50 60 0 0 0 A Normal 0 C C +T 0 100 50 60 0 0 0 K Normal 0 C C +P 2 0 1 6 50 50 50 -50 N +P 3 0 1 0 -50 50 50 0 -50 -50 F +X A 1 -150 0 100 R 40 40 1 1 P +X K 2 150 0 100 L 40 40 1 1 P +ENDDRAW +ENDDEF +# +# eSim_NPN +# +DEF eSim_NPN Q 0 0 Y N 1 F N +F0 "Q" -100 50 50 H V R CNN +F1 "eSim_NPN" -50 150 50 H V R CNN +F2 "" 200 100 29 H V C CNN +F3 "" 0 0 60 H V C CNN +ALIAS BC547 Q2N2222 +DRAW +C 50 0 111 0 1 10 N +P 2 0 1 0 25 25 100 100 N +P 3 0 1 0 25 -25 100 -100 100 -100 N +P 3 0 1 20 25 75 25 -75 25 -75 N +P 5 0 1 0 50 -70 70 -50 90 -90 50 -70 50 -70 F +X C 1 100 200 100 D 50 50 1 1 P +X B 2 -200 0 225 R 50 50 1 1 P +X E 3 100 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_PNP +# +DEF eSim_PNP Q 0 0 Y N 1 F N +F0 "Q" -100 50 50 H V R CNN +F1 "eSim_PNP" -50 150 50 H V R CNN +F2 "" 200 100 29 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +C 50 0 111 0 1 10 N +P 2 0 1 0 25 25 100 100 N +P 3 0 1 0 25 -25 100 -100 100 -100 N +P 3 0 1 20 25 75 25 -75 25 -75 N +P 5 0 1 0 90 -70 70 -90 50 -50 90 -70 90 -70 F +X C 1 100 200 100 D 50 50 1 1 P +X B 2 -200 0 225 R 50 50 1 1 P +X E 3 100 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_R +# +DEF eSim_R R 0 0 N Y 1 F N +F0 "R" 50 130 50 H V C CNN +F1 "eSim_R" 50 -50 50 H V C CNN +F2 "" 50 -20 30 H V C CNN +F3 "" 50 50 30 V V C CNN +ALIAS resistor +$FPLIST + R_* + Resistor_* +$ENDFPLIST +DRAW +S 150 10 -50 90 0 1 10 N +X ~ 1 -100 50 50 R 60 60 1 1 P +X ~ 2 200 50 50 L 60 60 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/library/SubcircuitLibrary/OP497/OP497_Subcircuit.cir b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.cir new file mode 100644 index 000000000..3e7498187 --- /dev/null +++ b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.cir @@ -0,0 +1,46 @@ +* C:\FOSSEE\eSim\library\SubcircuitLibrary\OP497_Subcircuit\OP497_Subcircuit.cir + +* EESchema Netlist Version 1.1 (Spice format) creation date: 02/04/25 21:20:34 + +* To exclude a component from the Spice Netlist add [Spice_Netlist_Enabled] user FIELD set to: N +* To reorder the component spice node sequence add [Spice_Node_Sequence] user FIELD and define sequence: 2,1,0 + +* Sheet Name: / +R1 Net-_R1-Pad1_ Net-_Q12-Pad1_ 2.5k +Q1 Net-_Q1-Pad1_ ? Net-_Q1-Pad3_ eSim_NPN +Q2 Net-_Q1-Pad1_ Net-_Q1-Pad3_ Net-_Q12-Pad1_ eSim_NPN +Q3 Net-_Q3-Pad1_ Net-_Q12-Pad1_ Net-_C3-Pad2_ eSim_NPN +R2 Net-_R2-Pad1_ Net-_Q1-Pad3_ 2.5k +R3 Net-_Q1-Pad1_ Net-_Q7-Pad3_ 10k +Q6 Net-_Q5-Pad3_ Net-_Q1-Pad3_ Net-_C3-Pad2_ eSim_NPN +Q4 Net-_C1-Pad2_ Net-_C1-Pad1_ Net-_Q3-Pad1_ eSim_NPN +Q5 Net-_C2-Pad1_ Net-_C1-Pad1_ Net-_Q5-Pad3_ eSim_NPN +R4 Net-_C1-Pad1_ Net-_C1-Pad2_ 10k +R5 Net-_C1-Pad1_ Net-_C2-Pad1_ 10k +Q8 Net-_C1-Pad1_ Net-_C1-Pad1_ Net-_Q7-Pad3_ eSim_NPN +Q7 Net-_C3-Pad2_ Net-_C3-Pad2_ Net-_Q7-Pad3_ eSim_PNP +Q9 Net-_Q10-Pad3_ Net-_Q14-Pad1_ Net-_C3-Pad2_ eSim_NPN +Q10 Net-_Q1-Pad3_ Net-_C3-Pad2_ Net-_Q10-Pad3_ eSim_PNP +Q12 Net-_Q12-Pad1_ Net-_C3-Pad2_ Net-_Q10-Pad3_ eSim_PNP +Q14 Net-_Q14-Pad1_ Net-_C3-Pad2_ Net-_Q10-Pad3_ eSim_PNP +Q11 Net-_Q10-Pad3_ Net-_C3-Pad2_ Net-_C1-Pad1_ eSim_PNP +Q13 Net-_C3-Pad2_ Net-_C3-Pad2_ Net-_C1-Pad1_ eSim_PNP +C1 Net-_C1-Pad1_ Net-_C1-Pad2_ 10nF +Q15 Net-_Q15-Pad1_ Net-_C1-Pad2_ Net-_C1-Pad1_ eSim_PNP +Q18 Net-_C3-Pad1_ Net-_C2-Pad1_ Net-_C1-Pad1_ eSim_PNP +C2 Net-_C2-Pad1_ Net-_C2-Pad2_ 10pF +R6 Net-_C2-Pad2_ Net-_C3-Pad1_ 10k +D1 Net-_C1-Pad1_ Net-_D1-Pad2_ eSim_Diode +D2 Net-_D1-Pad2_ Net-_C3-Pad2_ eSim_Diode +Q16 Net-_Q15-Pad1_ Net-_Q15-Pad1_ Net-_C3-Pad2_ eSim_NPN +Q17 Net-_C3-Pad1_ Net-_Q15-Pad1_ Net-_C3-Pad2_ eSim_NPN +C3 Net-_C3-Pad1_ Net-_C3-Pad2_ 10nF +Q19 Net-_Q19-Pad1_ Net-_C3-Pad1_ Net-_C3-Pad2_ eSim_NPN +Q21 Net-_C3-Pad2_ Net-_C3-Pad2_ Net-_D3-Pad2_ eSim_PNP +Q23 Net-_C3-Pad2_ Net-_C3-Pad2_ Net-_Q22-Pad3_ eSim_PNP +D3 Net-_C1-Pad1_ Net-_D3-Pad2_ eSim_Diode +Q20 Net-_C1-Pad1_ Net-_C1-Pad1_ Net-_Q19-Pad1_ eSim_NPN +Q22 Net-_C1-Pad1_ Net-_C1-Pad1_ Net-_Q22-Pad3_ eSim_NPN +U1 Net-_R1-Pad1_ Net-_R2-Pad1_ Net-_Q22-Pad3_ Net-_C1-Pad1_ Net-_C3-Pad2_ PORT + +.end diff --git a/library/SubcircuitLibrary/OP497/OP497_Subcircuit.cir.out b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.cir.out new file mode 100644 index 000000000..869eda86c --- /dev/null +++ b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.cir.out @@ -0,0 +1,50 @@ +* c:\fossee\esim\library\subcircuitlibrary\op497_subcircuit\op497_subcircuit.cir + +.include NPN.lib +.include PNP.lib +.include D.lib +r1 net-_r1-pad1_ net-_q12-pad1_ 2.5k +q1 net-_q1-pad1_ ? net-_q1-pad3_ Q2N2222 +q2 net-_q1-pad1_ net-_q1-pad3_ net-_q12-pad1_ Q2N2222 +q3 net-_q3-pad1_ net-_q12-pad1_ net-_c3-pad2_ Q2N2222 +r2 net-_r2-pad1_ net-_q1-pad3_ 2.5k +r3 net-_q1-pad1_ net-_q7-pad3_ 10k +q6 net-_q5-pad3_ net-_q1-pad3_ net-_c3-pad2_ Q2N2222 +q4 net-_c1-pad2_ net-_c1-pad1_ net-_q3-pad1_ Q2N2222 +q5 net-_c2-pad1_ net-_c1-pad1_ net-_q5-pad3_ Q2N2222 +r4 net-_c1-pad1_ net-_c1-pad2_ 10k +r5 net-_c1-pad1_ net-_c2-pad1_ 10k +q8 net-_c1-pad1_ net-_c1-pad1_ net-_q7-pad3_ Q2N2222 +q7 net-_c3-pad2_ net-_c3-pad2_ net-_q7-pad3_ Q2N2907A +q9 net-_q10-pad3_ net-_q14-pad1_ net-_c3-pad2_ Q2N2222 +q10 net-_q1-pad3_ net-_c3-pad2_ net-_q10-pad3_ Q2N2907A +q12 net-_q12-pad1_ net-_c3-pad2_ net-_q10-pad3_ Q2N2907A +q14 net-_q14-pad1_ net-_c3-pad2_ net-_q10-pad3_ Q2N2907A +q11 net-_q10-pad3_ net-_c3-pad2_ net-_c1-pad1_ Q2N2907A +q13 net-_c3-pad2_ net-_c3-pad2_ net-_c1-pad1_ Q2N2907A +c1 net-_c1-pad1_ net-_c1-pad2_ 10pf +q15 net-_q15-pad1_ net-_c1-pad2_ net-_c1-pad1_ Q2N2907A +q18 net-_c3-pad1_ net-_c2-pad1_ net-_c1-pad1_ Q2N2907A +c2 net-_c2-pad1_ net-_c2-pad2_ 10pf +r6 net-_c2-pad2_ net-_c3-pad1_ 10k +d1 net-_c1-pad1_ net-_d1-pad2_ 1N4148 +d2 net-_d1-pad2_ net-_c3-pad2_ 1N4148 +q16 net-_q15-pad1_ net-_q15-pad1_ net-_c3-pad2_ Q2N2222 +q17 net-_c3-pad1_ net-_q15-pad1_ net-_c3-pad2_ Q2N2222 +c3 net-_c3-pad1_ net-_c3-pad2_ 10uf +q19 net-_q19-pad1_ net-_c3-pad1_ net-_c3-pad2_ Q2N2222 +q21 net-_c3-pad2_ net-_c3-pad2_ net-_d3-pad2_ Q2N2907A +q23 net-_c3-pad2_ net-_c3-pad2_ net-_q22-pad3_ Q2N2907A +d3 net-_c1-pad1_ net-_d3-pad2_ 1N4148 +q20 net-_c1-pad1_ net-_c1-pad1_ net-_q19-pad1_ Q2N2222 +q22 net-_c1-pad1_ net-_c1-pad1_ net-_q22-pad3_ Q2N2222 +* u1 net-_r1-pad1_ net-_r2-pad1_ net-_c1-pad1_ net-_c3-pad2_ net-_q22-pad3_ port +.tran 0e-00 0e-00 0e-00 + +* Control Statements +.control +run +print allv > plot_data_v.txt +print alli > plot_data_i.txt +.endc +.end diff --git a/library/SubcircuitLibrary/OP497/OP497_Subcircuit.pro b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.pro new file mode 100644 index 000000000..e27a398be --- /dev/null +++ b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.pro @@ -0,0 +1,73 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] +LibName1=adc-dac +LibName2=memory +LibName3=xilinx +LibName4=microcontrollers +LibName5=dsp +LibName6=microchip +LibName7=analog_switches +LibName8=motorola +LibName9=texas +LibName10=intel +LibName11=audio +LibName12=interface +LibName13=digital-audio +LibName14=philips +LibName15=display +LibName16=cypress +LibName17=siliconi +LibName18=opto +LibName19=atmel +LibName20=contrib +LibName21=power +LibName22=eSim_Plot +LibName23=transistors +LibName24=conn +LibName25=eSim_User +LibName26=regul +LibName27=74xx +LibName28=cmos4000 +LibName29=eSim_Analog +LibName30=eSim_Devices +LibName31=eSim_Digital +LibName32=eSim_Hybrid +LibName33=eSim_Miscellaneous +LibName34=eSim_Power +LibName35=eSim_Sources +LibName36=eSim_Subckt +LibName37=eSim_Nghdl +LibName38=eSim_Ngveri +LibName39=eSim_SKY130 +LibName40=eSim_SKY130_Subckts diff --git a/library/SubcircuitLibrary/OP497/OP497_Subcircuit.sch b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.sch new file mode 100644 index 000000000..4f39bc9ca --- /dev/null +++ b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.sch @@ -0,0 +1,801 @@ +EESchema Schematic File Version 2 +LIBS:adc-dac +LIBS:memory +LIBS:xilinx +LIBS:microcontrollers +LIBS:dsp +LIBS:microchip +LIBS:analog_switches +LIBS:motorola +LIBS:texas +LIBS:intel +LIBS:audio +LIBS:interface +LIBS:digital-audio +LIBS:philips +LIBS:display +LIBS:cypress +LIBS:siliconi +LIBS:opto +LIBS:atmel +LIBS:contrib +LIBS:power +LIBS:eSim_Plot +LIBS:transistors +LIBS:conn +LIBS:eSim_User +LIBS:regul +LIBS:74xx +LIBS:cmos4000 +LIBS:eSim_Analog +LIBS:eSim_Devices +LIBS:eSim_Digital +LIBS:eSim_Hybrid +LIBS:eSim_Miscellaneous +LIBS:eSim_Power +LIBS:eSim_Sources +LIBS:eSim_Subckt +LIBS:eSim_Nghdl +LIBS:eSim_Ngveri +LIBS:eSim_SKY130 +LIBS:eSim_SKY130_Subckts +LIBS:OP497_Subcircuit-cache +EELAYER 25 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L resistor R1 +U 1 1 679F6A14 +P 1680 3080 +F 0 "R1" H 1730 3210 50 0000 C CNN +F 1 "2.5k" H 1730 3030 50 0000 C CNN +F 2 "" H 1730 3060 30 0000 C CNN +F 3 "" V 1730 3130 30 0000 C CNN + 1 1680 3080 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q1 +U 1 1 679F6B29 +P 2200 3240 +F 0 "Q1" H 2100 3290 50 0000 R CNN +F 1 "eSim_NPN" H 2150 3390 50 0000 R CNN +F 2 "" H 2400 3340 29 0000 C CNN +F 3 "" H 2200 3240 60 0000 C CNN + 1 2200 3240 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q2 +U 1 1 679F6B86 +P 2460 3680 +F 0 "Q2" H 2360 3730 50 0000 R CNN +F 1 "eSim_NPN" H 2410 3830 50 0000 R CNN +F 2 "" H 2660 3780 29 0000 C CNN +F 3 "" H 2460 3680 60 0000 C CNN + 1 2460 3680 + 0 1 -1 0 +$EndComp +$Comp +L eSim_NPN Q3 +U 1 1 679F6C10 +P 2970 3030 +F 0 "Q3" H 2870 3080 50 0000 R CNN +F 1 "eSim_NPN" H 2920 3180 50 0000 R CNN +F 2 "" H 3170 3130 29 0000 C CNN +F 3 "" H 2970 3030 60 0000 C CNN + 1 2970 3030 + 1 0 0 -1 +$EndComp +$Comp +L resistor R2 +U 1 1 679F6C82 +P 1700 3630 +F 0 "R2" H 1750 3760 50 0000 C CNN +F 1 "2.5k" H 1750 3580 50 0000 C CNN +F 2 "" H 1750 3610 30 0000 C CNN +F 3 "" V 1750 3680 30 0000 C CNN + 1 1700 3630 + 1 0 0 -1 +$EndComp +$Comp +L resistor R3 +U 1 1 67A21583 +P 2850 3470 +F 0 "R3" H 2900 3600 50 0000 C CNN +F 1 "10k" H 2900 3420 50 0000 C CNN +F 2 "" H 2900 3450 30 0000 C CNN +F 3 "" V 2900 3520 30 0000 C CNN + 1 2850 3470 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q6 +U 1 1 67A215FD +P 3850 3030 +F 0 "Q6" H 3750 3080 50 0000 R CNN +F 1 "eSim_NPN" H 3800 3180 50 0000 R CNN +F 2 "" H 4050 3130 29 0000 C CNN +F 3 "" H 3850 3030 60 0000 C CNN + 1 3850 3030 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q4 +U 1 1 67A216BC +P 3170 2530 +F 0 "Q4" H 3070 2580 50 0000 R CNN +F 1 "eSim_NPN" H 3120 2680 50 0000 R CNN +F 2 "" H 3370 2630 29 0000 C CNN +F 3 "" H 3170 2530 60 0000 C CNN + 1 3170 2530 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q5 +U 1 1 67A21748 +P 3650 2530 +F 0 "Q5" H 3550 2580 50 0000 R CNN +F 1 "eSim_NPN" H 3600 2680 50 0000 R CNN +F 2 "" H 3850 2630 29 0000 C CNN +F 3 "" H 3650 2530 60 0000 C CNN + 1 3650 2530 + 1 0 0 -1 +$EndComp +$Comp +L resistor R4 +U 1 1 67A21A6B +P 3020 1770 +F 0 "R4" H 3070 1900 50 0000 C CNN +F 1 "10k" H 3070 1720 50 0000 C CNN +F 2 "" H 3070 1750 30 0000 C CNN +F 3 "" V 3070 1820 30 0000 C CNN + 1 3020 1770 + 0 1 1 0 +$EndComp +$Comp +L resistor R5 +U 1 1 67A21AD4 +P 3700 1770 +F 0 "R5" H 3750 1900 50 0000 C CNN +F 1 "10k" H 3750 1720 50 0000 C CNN +F 2 "" H 3750 1750 30 0000 C CNN +F 3 "" V 3750 1820 30 0000 C CNN + 1 3700 1770 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q8 +U 1 1 67A21C5A +P 4260 2530 +F 0 "Q8" H 4160 2580 50 0000 R CNN +F 1 "eSim_NPN" H 4210 2680 50 0000 R CNN +F 2 "" H 4460 2630 29 0000 C CNN +F 3 "" H 4260 2530 60 0000 C CNN + 1 4260 2530 + 1 0 0 -1 +$EndComp +$Comp +L eSim_PNP Q7 +U 1 1 67A21DA2 +P 3970 4290 +F 0 "Q7" H 3870 4340 50 0000 R CNN +F 1 "eSim_PNP" H 3920 4440 50 0000 R CNN +F 2 "" H 4170 4390 29 0000 C CNN +F 3 "" H 3970 4290 60 0000 C CNN + 1 3970 4290 + 1 0 0 1 +$EndComp +$Comp +L eSim_NPN Q9 +U 1 1 67A21DFE +P 4490 4100 +F 0 "Q9" H 4390 4150 50 0000 R CNN +F 1 "eSim_NPN" H 4440 4250 50 0000 R CNN +F 2 "" H 4690 4200 29 0000 C CNN +F 3 "" H 4490 4100 60 0000 C CNN + 1 4490 4100 + -1 0 0 -1 +$EndComp +$Comp +L eSim_PNP Q10 +U 1 1 67A220BA +P 4950 3400 +F 0 "Q10" H 4850 3450 50 0000 R CNN +F 1 "eSim_PNP" H 4900 3550 50 0000 R CNN +F 2 "" H 5150 3500 29 0000 C CNN +F 3 "" H 4950 3400 60 0000 C CNN + 1 4950 3400 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q12 +U 1 1 67A221C2 +P 5380 3390 +F 0 "Q12" H 5280 3440 50 0000 R CNN +F 1 "eSim_PNP" H 5330 3540 50 0000 R CNN +F 2 "" H 5580 3490 29 0000 C CNN +F 3 "" H 5380 3390 60 0000 C CNN + 1 5380 3390 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q14 +U 1 1 67A22250 +P 5810 3380 +F 0 "Q14" H 5710 3430 50 0000 R CNN +F 1 "eSim_PNP" H 5760 3530 50 0000 R CNN +F 2 "" H 6010 3480 29 0000 C CNN +F 3 "" H 5810 3380 60 0000 C CNN + 1 5810 3380 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q11 +U 1 1 67A2261A +P 5370 2750 +F 0 "Q11" H 5270 2800 50 0000 R CNN +F 1 "eSim_PNP" H 5320 2900 50 0000 R CNN +F 2 "" H 5570 2850 29 0000 C CNN +F 3 "" H 5370 2750 60 0000 C CNN + 1 5370 2750 + -1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q13 +U 1 1 67A22620 +P 5800 2740 +F 0 "Q13" H 5700 2790 50 0000 R CNN +F 1 "eSim_PNP" H 5750 2890 50 0000 R CNN +F 2 "" H 6000 2840 29 0000 C CNN +F 3 "" H 5800 2740 60 0000 C CNN + 1 5800 2740 + -1 0 0 1 +$EndComp +$Comp +L capacitor C1 +U 1 1 67A2272A +P 5430 1840 +F 0 "C1" H 5455 1940 50 0000 L CNN +F 1 "10nF" H 5455 1740 50 0000 L CNN +F 2 "" H 5468 1690 30 0000 C CNN +F 3 "" H 5430 1840 60 0000 C CNN + 1 5430 1840 + 1 0 0 -1 +$EndComp +$Comp +L eSim_PNP Q15 +U 1 1 67A22875 +P 6960 2230 +F 0 "Q15" H 6860 2280 50 0000 R CNN +F 1 "eSim_PNP" H 6910 2380 50 0000 R CNN +F 2 "" H 7160 2330 29 0000 C CNN +F 3 "" H 6960 2230 60 0000 C CNN + 1 6960 2230 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q18 +U 1 1 67A22B87 +P 7930 2230 +F 0 "Q18" H 7830 2280 50 0000 R CNN +F 1 "eSim_PNP" H 7880 2380 50 0000 R CNN +F 2 "" H 8130 2330 29 0000 C CNN +F 3 "" H 7930 2230 60 0000 C CNN + 1 7930 2230 + -1 0 0 1 +$EndComp +$Comp +L capacitor C2 +U 1 1 67A22C9D +P 7460 2650 +F 0 "C2" H 7485 2750 50 0000 L CNN +F 1 "10pF" H 7485 2550 50 0000 L CNN +F 2 "" H 7498 2500 30 0000 C CNN +F 3 "" H 7460 2650 60 0000 C CNN + 1 7460 2650 + 1 0 0 -1 +$EndComp +$Comp +L resistor R6 +U 1 1 67A22D3A +P 7410 3080 +F 0 "R6" H 7460 3210 50 0000 C CNN +F 1 "10k" H 7460 3030 50 0000 C CNN +F 2 "" H 7460 3060 30 0000 C CNN +F 3 "" V 7460 3130 30 0000 C CNN + 1 7410 3080 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D1 +U 1 1 67A23102 +P 6490 2720 +F 0 "D1" H 6490 2820 50 0000 C CNN +F 1 "eSim_Diode" H 6490 2620 50 0000 C CNN +F 2 "" H 6490 2720 60 0000 C CNN +F 3 "" H 6490 2720 60 0000 C CNN + 1 6490 2720 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D2 +U 1 1 67A232E0 +P 6490 3080 +F 0 "D2" H 6490 3180 50 0000 C CNN +F 1 "eSim_Diode" H 6490 2980 50 0000 C CNN +F 2 "" H 6490 3080 60 0000 C CNN +F 3 "" H 6490 3080 60 0000 C CNN + 1 6490 3080 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q16 +U 1 1 67A23B3C +P 7160 3870 +F 0 "Q16" H 7060 3920 50 0000 R CNN +F 1 "eSim_NPN" H 7110 4020 50 0000 R CNN +F 2 "" H 7360 3970 29 0000 C CNN +F 3 "" H 7160 3870 60 0000 C CNN + 1 7160 3870 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q17 +U 1 1 67A23DFE +P 7660 3870 +F 0 "Q17" H 7560 3920 50 0000 R CNN +F 1 "eSim_NPN" H 7610 4020 50 0000 R CNN +F 2 "" H 7860 3970 29 0000 C CNN +F 3 "" H 7660 3870 60 0000 C CNN + 1 7660 3870 + 1 0 0 -1 +$EndComp +$Comp +L capacitor C3 +U 1 1 67A23EC8 +P 8160 4370 +F 0 "C3" H 8185 4470 50 0000 L CNN +F 1 "10nF" H 8185 4270 50 0000 L CNN +F 2 "" H 8198 4220 30 0000 C CNN +F 3 "" H 8160 4370 60 0000 C CNN + 1 8160 4370 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q19 +U 1 1 67A24065 +P 8510 3310 +F 0 "Q19" H 8410 3360 50 0000 R CNN +F 1 "eSim_NPN" H 8460 3460 50 0000 R CNN +F 2 "" H 8710 3410 29 0000 C CNN +F 3 "" H 8510 3310 60 0000 C CNN + 1 8510 3310 + 1 0 0 -1 +$EndComp +$Comp +L eSim_PNP Q21 +U 1 1 67A242DB +P 9270 4120 +F 0 "Q21" H 9170 4170 50 0000 R CNN +F 1 "eSim_PNP" H 9220 4270 50 0000 R CNN +F 2 "" H 9470 4220 29 0000 C CNN +F 3 "" H 9270 4120 60 0000 C CNN + 1 9270 4120 + 1 0 0 1 +$EndComp +$Comp +L eSim_PNP Q23 +U 1 1 67A243EE +P 9750 4120 +F 0 "Q23" H 9650 4170 50 0000 R CNN +F 1 "eSim_PNP" H 9700 4270 50 0000 R CNN +F 2 "" H 9950 4220 29 0000 C CNN +F 3 "" H 9750 4120 60 0000 C CNN + 1 9750 4120 + 1 0 0 1 +$EndComp +$Comp +L eSim_Diode D3 +U 1 1 67A244FA +P 9370 3310 +F 0 "D3" H 9370 3410 50 0000 C CNN +F 1 "eSim_Diode" H 9370 3210 50 0000 C CNN +F 2 "" H 9370 3310 60 0000 C CNN +F 3 "" H 9370 3310 60 0000 C CNN + 1 9370 3310 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q20 +U 1 1 67A247E0 +P 9170 2560 +F 0 "Q20" H 9070 2610 50 0000 R CNN +F 1 "eSim_NPN" H 9120 2710 50 0000 R CNN +F 2 "" H 9370 2660 29 0000 C CNN +F 3 "" H 9170 2560 60 0000 C CNN + 1 9170 2560 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q22 +U 1 1 67A24B4B +P 9570 2210 +F 0 "Q22" H 9470 2260 50 0000 R CNN +F 1 "eSim_NPN" H 9520 2360 50 0000 R CNN +F 2 "" H 9770 2310 29 0000 C CNN +F 3 "" H 9570 2210 60 0000 C CNN + 1 9570 2210 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 1 1 67A24ED7 +P 1050 3030 +F 0 "U1" H 1100 3130 30 0000 C CNN +F 1 "PORT" H 1050 3030 30 0000 C CNN +F 2 "" H 1050 3030 60 0000 C CNN +F 3 "" H 1050 3030 60 0000 C CNN + 1 1050 3030 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 2 1 67A2506E +P 1050 3580 +F 0 "U1" H 1100 3680 30 0000 C CNN +F 1 "PORT" H 1050 3580 30 0000 C CNN +F 2 "" H 1050 3580 60 0000 C CNN +F 3 "" H 1050 3580 60 0000 C CNN + 2 1050 3580 + 1 0 0 -1 +$EndComp +Wire Wire Line + 1580 3030 1300 3030 +Wire Wire Line + 1600 3580 1300 3580 +Wire Wire Line + 2770 3030 1880 3030 +Connection ~ 2200 3030 +Wire Wire Line + 2000 3340 2000 3880 +Wire Wire Line + 2000 3580 1900 3580 +Wire Wire Line + 2000 3880 2830 3880 +Connection ~ 2000 3580 +Wire Wire Line + 2200 3580 2260 3580 +Wire Wire Line + 2200 3580 2200 3030 +Wire Wire Line + 2660 3580 2660 3340 +Wire Wire Line + 2660 3340 2400 3340 +Wire Wire Line + 2750 3420 2660 3420 +Connection ~ 2660 3420 +Wire Wire Line + 3750 3230 3070 3230 +Wire Wire Line + 3070 2830 3070 2730 +Wire Wire Line + 3750 2830 3750 2730 +Wire Wire Line + 3370 2530 3450 2530 +Wire Wire Line + 3070 2330 3070 1970 +Wire Wire Line + 3750 2330 3750 1970 +Wire Wire Line + 4060 2530 4060 2500 +Wire Wire Line + 4060 2500 3410 2500 +Wire Wire Line + 3410 2500 3410 2530 +Connection ~ 3410 2530 +Wire Wire Line + 7060 2030 7060 2010 +Wire Wire Line + 7060 2010 7830 2010 +Wire Wire Line + 7830 2010 7830 2030 +Wire Wire Line + 5270 2550 5270 2510 +Wire Wire Line + 5130 2510 5700 2510 +Wire Wire Line + 5700 2510 5700 2540 +Wire Wire Line + 9670 2010 9670 1590 +Wire Wire Line + 3070 1590 3070 1670 +Wire Wire Line + 3750 1670 3750 1590 +Connection ~ 3750 1590 +Wire Wire Line + 4360 2330 4360 1590 +Connection ~ 4360 1590 +Wire Wire Line + 5430 1690 5430 1590 +Connection ~ 5430 1590 +Wire Wire Line + 6760 2230 3070 2230 +Connection ~ 3070 2230 +Wire Wire Line + 5430 1990 5430 2230 +Connection ~ 5430 2230 +Wire Wire Line + 8130 2230 8240 2230 +Wire Wire Line + 8240 2230 8240 2460 +Wire Wire Line + 8240 2460 5860 2460 +Wire Wire Line + 5860 2460 5860 2290 +Wire Wire Line + 5860 2290 3750 2290 +Connection ~ 3750 2290 +Wire Wire Line + 4360 2310 3940 2310 +Wire Wire Line + 3940 2310 3940 2730 +Connection ~ 3940 2500 +Connection ~ 4360 2310 +Wire Wire Line + 4070 4090 4070 3580 +Wire Wire Line + 4070 3580 4360 3580 +Wire Wire Line + 4360 3580 4360 2730 +Wire Wire Line + 5710 3180 5710 3140 +Wire Wire Line + 5710 3140 4850 3140 +Wire Wire Line + 4850 3140 4850 3200 +Wire Wire Line + 5280 3090 5280 3190 +Connection ~ 5280 3140 +Wire Wire Line + 5270 2950 5270 3090 +Wire Wire Line + 5270 3090 5280 3090 +Wire Wire Line + 5270 3050 4480 3050 +Wire Wire Line + 4480 3050 4480 3870 +Wire Wire Line + 4480 3870 4390 3870 +Wire Wire Line + 4390 3870 4390 3900 +Connection ~ 5270 3050 +Wire Wire Line + 2830 3600 4850 3600 +Wire Wire Line + 2830 3880 2830 3600 +Connection ~ 2460 3880 +Wire Wire Line + 5280 3800 5280 3590 +Wire Wire Line + 2710 3800 5280 3800 +Wire Wire Line + 2710 3800 2710 3030 +Connection ~ 2710 3030 +Wire Wire Line + 5710 3580 5710 4100 +Wire Wire Line + 5710 4100 4690 4100 +Wire Wire Line + 9850 4320 9850 4980 +Wire Wire Line + 3400 4980 3400 3230 +Connection ~ 3400 3230 +Wire Wire Line + 3770 4290 3400 4290 +Connection ~ 3400 4290 +Wire Wire Line + 4390 4300 3660 4300 +Wire Wire Line + 3660 4300 3660 4290 +Connection ~ 3660 4290 +Wire Wire Line + 4070 4490 4070 4980 +Connection ~ 4070 4980 +Wire Wire Line + 6000 2740 6100 2740 +Wire Wire Line + 6100 2740 6100 4980 +Connection ~ 6100 4980 +Wire Wire Line + 5570 2750 5570 2990 +Wire Wire Line + 5570 2990 6030 2990 +Wire Wire Line + 6030 2990 6030 2740 +Connection ~ 6030 2740 +Wire Wire Line + 5700 2940 5700 3070 +Wire Wire Line + 5700 3070 6100 3070 +Connection ~ 6100 3070 +Wire Wire Line + 5150 3400 5150 3710 +Wire Wire Line + 5150 3710 6080 3710 +Wire Wire Line + 6080 3710 6080 3380 +Wire Wire Line + 6010 3380 6490 3380 +Wire Wire Line + 5580 3390 5580 3650 +Wire Wire Line + 5580 3650 6040 3650 +Wire Wire Line + 6040 3650 6040 3380 +Connection ~ 6040 3380 +Wire Wire Line + 6490 2870 6490 2930 +Wire Wire Line + 6490 2570 5780 2570 +Wire Wire Line + 5780 2570 5780 2430 +Wire Wire Line + 5780 2430 4910 2430 +Wire Wire Line + 5130 2430 5130 2510 +Connection ~ 5270 2510 +Wire Wire Line + 4910 2430 4910 2730 +Wire Wire Line + 4910 2730 3940 2730 +Connection ~ 5130 2430 +Wire Wire Line + 6490 3230 6490 4980 +Connection ~ 6490 4980 +Connection ~ 6490 3380 +Connection ~ 6080 3380 +Wire Wire Line + 7060 2430 7060 3670 +Wire Wire Line + 7460 3870 7360 3870 +Wire Wire Line + 7410 3870 7410 3610 +Wire Wire Line + 7410 3610 7060 3610 +Connection ~ 7060 3610 +Connection ~ 7410 3870 +Wire Wire Line + 7060 4070 7060 4980 +Connection ~ 7060 4980 +Wire Wire Line + 7760 4070 7760 4140 +Wire Wire Line + 7060 4140 7800 4140 +Connection ~ 7060 4140 +Wire Wire Line + 9070 4120 7800 4120 +Wire Wire Line + 7800 4120 7800 4140 +Connection ~ 7760 4140 +Wire Wire Line + 8610 3510 8610 4490 +Connection ~ 8610 4120 +Wire Wire Line + 7460 3310 8310 3310 +Wire Wire Line + 7760 3310 7760 3670 +Wire Wire Line + 7460 3280 7460 3310 +Connection ~ 7760 3310 +Wire Wire Line + 8160 4220 8160 3310 +Connection ~ 8160 3310 +Wire Wire Line + 7830 2430 7830 3040 +Wire Wire Line + 7830 3040 7950 3040 +Wire Wire Line + 7950 3040 7950 3310 +Connection ~ 7950 3310 +Wire Wire Line + 7460 2980 7460 2800 +Wire Wire Line + 7460 2500 7460 2460 +Connection ~ 7460 2460 +Wire Wire Line + 7430 2010 7430 1590 +Connection ~ 7430 1590 +Connection ~ 7430 2010 +Wire Wire Line + 9070 2760 9070 2900 +Wire Wire Line + 9070 2900 8610 2900 +Wire Wire Line + 8610 2900 8610 3110 +Wire Wire Line + 9070 2360 9070 1590 +Connection ~ 9070 1590 +Wire Wire Line + 9370 1590 9370 3160 +Connection ~ 9370 1590 +Connection ~ 9370 2210 +Connection ~ 9370 2560 +Wire Wire Line + 9370 3920 9370 3460 +Wire Wire Line + 8160 4520 8160 4980 +Connection ~ 8160 4980 +Wire Wire Line + 9370 4320 9370 4980 +Connection ~ 9370 4980 +Wire Wire Line + 9550 4120 9550 4490 +Wire Wire Line + 9550 4490 8610 4490 +Wire Wire Line + 9670 2410 9850 2410 +Wire Wire Line + 9850 2410 9850 3920 +Wire Wire Line + 10470 3140 9850 3140 +Connection ~ 9850 3140 +Connection ~ 9670 1590 +Connection ~ 9850 4980 +Wire Wire Line + 3050 3420 4360 3420 +Connection ~ 4360 3420 +Wire Wire Line + 4050 3030 4260 3030 +Wire Wire Line + 4260 3030 4260 3600 +Connection ~ 4260 3600 +Wire Wire Line + 3070 1590 9970 1590 +Wire Wire Line + 3400 4980 10080 4980 +$Comp +L PORT U1 +U 3 1 67A3C939 +P 10720 3140 +F 0 "U1" H 10770 3240 30 0000 C CNN +F 1 "PORT" H 10720 3140 30 0000 C CNN +F 2 "" H 10720 3140 60 0000 C CNN +F 3 "" H 10720 3140 60 0000 C CNN + 3 10720 3140 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 4 1 67A3DAF1 +P 10220 1590 +F 0 "U1" H 10270 1690 30 0000 C CNN +F 1 "PORT" H 10220 1590 30 0000 C CNN +F 2 "" H 10220 1590 60 0000 C CNN +F 3 "" H 10220 1590 60 0000 C CNN + 4 10220 1590 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 5 1 67A3DE72 +P 10330 4980 +F 0 "U1" H 10380 5080 30 0000 C CNN +F 1 "PORT" H 10330 4980 30 0000 C CNN +F 2 "" H 10330 4980 60 0000 C CNN +F 3 "" H 10330 4980 60 0000 C CNN + 5 10330 4980 + -1 0 0 1 +$EndComp +$EndSCHEMATC diff --git a/library/SubcircuitLibrary/OP497/OP497_Subcircuit.sub b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.sub new file mode 100644 index 000000000..9b3289d67 --- /dev/null +++ b/library/SubcircuitLibrary/OP497/OP497_Subcircuit.sub @@ -0,0 +1,44 @@ +* Subcircuit OP497_Subcircuit +.subckt OP497_Subcircuit net-_r1-pad1_ net-_r2-pad1_ net-_c1-pad1_ net-_c3-pad2_ net-_q22-pad3_ +* c:\fossee\esim\library\subcircuitlibrary\op497_subcircuit\op497_subcircuit.cir +.include NPN.lib +.include PNP.lib +.include D.lib +r1 net-_r1-pad1_ net-_q12-pad1_ 2.5k +q1 net-_q1-pad1_ ? net-_q1-pad3_ Q2N2222 +q2 net-_q1-pad1_ net-_q1-pad3_ net-_q12-pad1_ Q2N2222 +q3 net-_q3-pad1_ net-_q12-pad1_ net-_c3-pad2_ Q2N2222 +r2 net-_r2-pad1_ net-_q1-pad3_ 2.5k +r3 net-_q1-pad1_ net-_q7-pad3_ 10k +q6 net-_q5-pad3_ net-_q1-pad3_ net-_c3-pad2_ Q2N2222 +q4 net-_c1-pad2_ net-_c1-pad1_ net-_q3-pad1_ Q2N2222 +q5 net-_c2-pad1_ net-_c1-pad1_ net-_q5-pad3_ Q2N2222 +r4 net-_c1-pad1_ net-_c1-pad2_ 10k +r5 net-_c1-pad1_ net-_c2-pad1_ 10k +q8 net-_c1-pad1_ net-_c1-pad1_ net-_q7-pad3_ Q2N2222 +q7 net-_c3-pad2_ net-_c3-pad2_ net-_q7-pad3_ Q2N2907A +q9 net-_q10-pad3_ net-_q14-pad1_ net-_c3-pad2_ Q2N2222 +q10 net-_q1-pad3_ net-_c3-pad2_ net-_q10-pad3_ Q2N2907A +q12 net-_q12-pad1_ net-_c3-pad2_ net-_q10-pad3_ Q2N2907A +q14 net-_q14-pad1_ net-_c3-pad2_ net-_q10-pad3_ Q2N2907A +q11 net-_q10-pad3_ net-_c3-pad2_ net-_c1-pad1_ Q2N2907A +q13 net-_c3-pad2_ net-_c3-pad2_ net-_c1-pad1_ Q2N2907A +c1 net-_c1-pad1_ net-_c1-pad2_ 10pf +q15 net-_q15-pad1_ net-_c1-pad2_ net-_c1-pad1_ Q2N2907A +q18 net-_c3-pad1_ net-_c2-pad1_ net-_c1-pad1_ Q2N2907A +c2 net-_c2-pad1_ net-_c2-pad2_ 10pf +r6 net-_c2-pad2_ net-_c3-pad1_ 10k +d1 net-_c1-pad1_ net-_d1-pad2_ 1N4148 +d2 net-_d1-pad2_ net-_c3-pad2_ 1N4148 +q16 net-_q15-pad1_ net-_q15-pad1_ net-_c3-pad2_ Q2N2222 +q17 net-_c3-pad1_ net-_q15-pad1_ net-_c3-pad2_ Q2N2222 +c3 net-_c3-pad1_ net-_c3-pad2_ 10uf +q19 net-_q19-pad1_ net-_c3-pad1_ net-_c3-pad2_ Q2N2222 +q21 net-_c3-pad2_ net-_c3-pad2_ net-_d3-pad2_ Q2N2907A +q23 net-_c3-pad2_ net-_c3-pad2_ net-_q22-pad3_ Q2N2907A +d3 net-_c1-pad1_ net-_d3-pad2_ 1N4148 +q20 net-_c1-pad1_ net-_c1-pad1_ net-_q19-pad1_ Q2N2222 +q22 net-_c1-pad1_ net-_c1-pad1_ net-_q22-pad3_ Q2N2222 +* Control Statements + +.ends OP497_Subcircuit \ No newline at end of file diff --git a/library/SubcircuitLibrary/OP497/OP497_Subcircuit_Previous_Values.xml b/library/SubcircuitLibrary/OP497/OP497_Subcircuit_Previous_Values.xml new file mode 100644 index 000000000..bb0cc8b7e --- /dev/null +++ b/library/SubcircuitLibrary/OP497/OP497_Subcircuit_Previous_Values.xml @@ -0,0 +1 @@ +truefalsefalseHzHz0Volts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or AmperessecsecsecC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\PNP.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.lib \ No newline at end of file diff --git a/library/SubcircuitLibrary/OP497/PNP.lib b/library/SubcircuitLibrary/OP497/PNP.lib new file mode 100644 index 000000000..7edda0eab --- /dev/null +++ b/library/SubcircuitLibrary/OP497/PNP.lib @@ -0,0 +1,4 @@ +.model Q2N2907A PNP(Is=650.6E-18 Xti=3 Eg=1.11 Vaf=115.7 Bf=231.7 Ne=1.829 ++ Ise=54.81f Ikf=1.079 Xtb=1.5 Br=3.563 Nc=2 Isc=0 Ikr=0 Rc=.715 ++ Cjc=14.76p Mjc=.5383 Vjc=.75 Fc=.5 Cje=19.82p Mje=.3357 Vje=.75 ++ Tr=111.3n Tf=603.7p Itf=.65 Vtf=5 Xtf=1.7 Rb=10) diff --git a/library/SubcircuitLibrary/OP497/analysis b/library/SubcircuitLibrary/OP497/analysis new file mode 100644 index 000000000..ebd5c0a94 --- /dev/null +++ b/library/SubcircuitLibrary/OP497/analysis @@ -0,0 +1 @@ +.tran 0e-00 0e-00 0e-00 \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7404/D.lib b/library/SubcircuitLibrary/SN7404/D.lib new file mode 100644 index 000000000..f53bf3e03 --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/D.lib @@ -0,0 +1,2 @@ +.model 1N4148 D(is=2.495E-09 rs=4.755E-01 n=1.679E+00 tt=3.030E-09 cjo=1.700E-12 vj=1 m=1.959E-01 bv=1.000E+02 ibv=1.000E-04) + diff --git a/library/SubcircuitLibrary/SN7404/NPN.lib b/library/SubcircuitLibrary/SN7404/NPN.lib new file mode 100644 index 000000000..be5f3073a --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/NPN.lib @@ -0,0 +1,4 @@ +.model Q2N2222 NPN( Is=14.34f Xti=3 Eg=1.11 Vaf=74.03 Bf=400 Ne=1.307 ++ Ise=14.34f Ikf=0.2847 Xtb=1.5 Br=6.092 Nc=2 Isc=0 Ikr=0 Rc=1 Cjc=7.306p ++ Mjc=0.3416 Vjc=0.75 Fc=0.5 Cje=22.01p Mje=0.377 Vje=0.75 Tr=46.91n Tf=411.1p ++ Itf=0.6 Vtf=1.7 Xtf=3 Rb=10) diff --git a/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit-cache.lib b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit-cache.lib new file mode 100644 index 000000000..0f688db8a --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit-cache.lib @@ -0,0 +1,126 @@ +EESchema-LIBRARY Version 2.3 +#encoding utf-8 +# +# GNDPWR +# +DEF GNDPWR #PWR 0 0 Y Y 1 F P +F0 "#PWR" 0 -200 50 H I C CNN +F1 "GNDPWR" 0 -130 50 H V C CNN +F2 "" 0 -50 50 H I C CNN +F3 "" 0 -50 50 H I C CNN +DRAW +P 2 0 1 0 0 -50 0 0 N +P 3 0 1 8 -40 -50 -50 -80 -50 -80 N +P 3 0 1 8 -20 -50 -30 -80 -30 -80 N +P 3 0 1 8 0 -50 -10 -80 -10 -80 N +P 3 0 1 8 20 -50 10 -80 10 -80 N +P 3 0 1 8 40 -50 -40 -50 -40 -50 N +P 4 0 1 8 40 -50 30 -80 30 -80 30 -80 N +X GNDPWR 1 0 0 0 U 50 50 1 1 W N +ENDDRAW +ENDDEF +# +# PORT +# +DEF PORT U 0 40 Y Y 26 F N +F0 "U" 50 100 30 H V C CNN +F1 "PORT" 0 0 30 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +A 325 225 285 -1421 -1278 0 1 0 N 100 50 150 0 +A 376 -275 356 1294 1408 0 1 0 N 150 0 100 -50 +S -100 50 100 -50 0 1 0 N +X ~ 1 250 0 100 L 30 30 1 1 B +X ~ 2 250 0 100 L 30 30 2 1 B +X ~ 3 250 0 100 L 30 30 3 1 B +X ~ 4 250 0 100 L 30 30 4 1 B +X ~ 5 250 0 100 L 30 30 5 1 B +X ~ 6 250 0 100 L 30 30 6 1 B +X ~ 7 250 0 100 L 30 30 7 1 B +X ~ 8 250 0 100 L 30 30 8 1 B +X ~ 9 250 0 100 L 30 30 9 1 B +X ~ 10 250 0 100 L 30 30 10 1 B +X ~ 11 250 0 100 L 30 30 11 1 B +X ~ 12 250 0 100 L 30 30 12 1 B +X ~ 13 250 0 100 L 30 30 13 1 B +X ~ 14 250 0 100 L 30 30 14 1 B +X ~ 15 250 0 100 L 30 30 15 1 B +X ~ 16 250 0 100 L 30 30 16 1 B +X ~ 17 250 0 100 L 30 30 17 1 B +X ~ 18 250 0 100 L 30 30 18 1 B +X ~ 19 250 0 100 L 30 30 19 1 B +X ~ 20 250 0 100 L 30 30 20 1 B +X ~ 21 250 0 100 L 30 30 21 1 B +X ~ 22 250 0 100 L 30 30 22 1 B +X ~ 23 250 0 100 L 30 30 23 1 B +X ~ 24 250 0 100 L 30 30 24 1 B +X ~ 25 250 0 100 L 30 30 25 1 B +X ~ 26 250 0 100 L 30 30 26 1 B +ENDDRAW +ENDDEF +# +# eSim_Diode +# +DEF eSim_Diode D 0 40 N N 1 F N +F0 "D" 0 100 50 H V C CNN +F1 "eSim_Diode" 0 -100 50 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + TO-???* + *SingleDiode + *_Diode_* + *SingleDiode* + D_* +$ENDFPLIST +DRAW +T 0 -100 50 60 0 0 0 A Normal 0 C C +T 0 100 50 60 0 0 0 K Normal 0 C C +P 2 0 1 6 50 50 50 -50 N +P 3 0 1 0 -50 50 50 0 -50 -50 F +X A 1 -150 0 100 R 40 40 1 1 P +X K 2 150 0 100 L 40 40 1 1 P +ENDDRAW +ENDDEF +# +# eSim_NPN +# +DEF eSim_NPN Q 0 0 Y N 1 F N +F0 "Q" -100 50 50 H V R CNN +F1 "eSim_NPN" -50 150 50 H V R CNN +F2 "" 200 100 29 H V C CNN +F3 "" 0 0 60 H V C CNN +ALIAS BC547 Q2N2222 +DRAW +C 50 0 111 0 1 10 N +P 2 0 1 0 25 25 100 100 N +P 3 0 1 0 25 -25 100 -100 100 -100 N +P 3 0 1 20 25 75 25 -75 25 -75 N +P 5 0 1 0 50 -70 70 -50 90 -90 50 -70 50 -70 F +X C 1 100 200 100 D 50 50 1 1 P +X B 2 -200 0 225 R 50 50 1 1 P +X E 3 100 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_R +# +DEF eSim_R R 0 0 N Y 1 F N +F0 "R" 50 130 50 H V C CNN +F1 "eSim_R" 50 -50 50 H V C CNN +F2 "" 50 -20 30 H V C CNN +F3 "" 50 50 30 V V C CNN +ALIAS resistor +$FPLIST + R_* + Resistor_* +$ENDFPLIST +DRAW +S 150 10 -50 90 0 1 10 N +X ~ 1 -100 50 50 R 60 60 1 1 P +X ~ 2 200 50 50 L 60 60 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.cir b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.cir new file mode 100644 index 000000000..51b43f71d --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.cir @@ -0,0 +1,71 @@ +* C:\FOSSEE\eSim\library\SubcircuitLibrary\SN7404_Subcircuit\SN7404_Subcircuit.cir + +* EESchema Netlist Version 1.1 (Spice format) creation date: 02/09/25 22:45:10 + +* To exclude a component from the Spice Netlist add [Spice_Netlist_Enabled] user FIELD set to: N +* To reorder the component spice node sequence add [Spice_Node_Sequence] user FIELD and define sequence: 2,1,0 + +* Sheet Name: / +R17 Net-_R1-Pad1_ Net-_Q17-Pad2_ 4k +Q17 Net-_Q17-Pad1_ Net-_Q17-Pad2_ Net-_D9-Pad2_ eSim_NPN +Q19 Net-_Q19-Pad1_ Net-_Q17-Pad1_ Net-_Q19-Pad3_ eSim_NPN +Q21 Net-_Q21-Pad1_ Net-_Q19-Pad1_ Net-_D11-Pad1_ eSim_NPN +D11 Net-_D11-Pad1_ Net-_D11-Pad2_ eSim_Diode +Q22 Net-_D11-Pad2_ Net-_Q19-Pad3_ GNDPWR eSim_NPN +R21 ? Net-_Q19-Pad1_ 1.6k +R23 Net-_R1-Pad1_ Net-_Q21-Pad1_ 130 +R19 Net-_Q19-Pad3_ GNDPWR 1k +D9 GNDPWR Net-_D9-Pad2_ eSim_Diode +U1 Net-_D1-Pad2_ Net-_D2-Pad2_ Net-_D3-Pad2_ Net-_D4-Pad2_ Net-_D5-Pad2_ Net-_D6-Pad2_ Net-_R1-Pad1_ GNDPWR Net-_D7-Pad2_ Net-_D8-Pad2_ Net-_D9-Pad2_ Net-_D10-Pad2_ Net-_D11-Pad2_ Net-_D12-Pad2_ PORT +R18 Net-_R1-Pad1_ Net-_Q18-Pad2_ 4k +Q18 Net-_Q18-Pad1_ Net-_Q18-Pad2_ Net-_D10-Pad2_ eSim_NPN +Q20 Net-_Q20-Pad1_ Net-_Q18-Pad1_ Net-_Q20-Pad3_ eSim_NPN +Q23 Net-_Q23-Pad1_ Net-_Q20-Pad1_ Net-_D12-Pad1_ eSim_NPN +D12 Net-_D12-Pad1_ Net-_D12-Pad2_ eSim_Diode +Q24 Net-_D12-Pad2_ Net-_Q20-Pad3_ GNDPWR eSim_NPN +R22 Net-_R1-Pad1_ Net-_Q20-Pad1_ 1.6k +R24 Net-_R1-Pad1_ Net-_Q23-Pad1_ 130 +R20 Net-_Q20-Pad3_ GNDPWR 1k +D10 GNDPWR Net-_D10-Pad2_ eSim_Diode +R9 Net-_R1-Pad1_ Net-_Q9-Pad2_ 4k +Q9 Net-_Q11-Pad2_ Net-_Q9-Pad2_ Net-_D5-Pad2_ eSim_NPN +Q11 Net-_Q11-Pad1_ Net-_Q11-Pad2_ Net-_Q11-Pad3_ eSim_NPN +Q13 Net-_Q13-Pad1_ Net-_Q11-Pad1_ Net-_D7-Pad1_ eSim_NPN +D7 Net-_D7-Pad1_ Net-_D7-Pad2_ eSim_Diode +Q14 Net-_D7-Pad2_ Net-_Q11-Pad3_ GNDPWR eSim_NPN +R13 ? Net-_Q11-Pad1_ 1.6k +R15 Net-_R1-Pad1_ Net-_Q13-Pad1_ 130 +R11 Net-_Q11-Pad3_ GNDPWR 1k +D5 GNDPWR Net-_D5-Pad2_ eSim_Diode +R10 Net-_R1-Pad1_ Net-_Q10-Pad2_ 4k +Q10 Net-_Q10-Pad1_ Net-_Q10-Pad2_ Net-_D6-Pad2_ eSim_NPN +Q12 Net-_Q12-Pad1_ Net-_Q10-Pad1_ Net-_Q12-Pad3_ eSim_NPN +Q15 Net-_Q15-Pad1_ Net-_Q12-Pad1_ Net-_D8-Pad1_ eSim_NPN +D8 Net-_D8-Pad1_ Net-_D8-Pad2_ eSim_Diode +Q16 Net-_D8-Pad2_ Net-_Q12-Pad3_ GNDPWR eSim_NPN +R14 ? Net-_Q12-Pad1_ 1.6k +R16 Net-_R1-Pad1_ Net-_Q15-Pad1_ 130 +R12 Net-_Q12-Pad3_ GNDPWR 1k +D6 GNDPWR Net-_D6-Pad2_ eSim_Diode +R1 Net-_R1-Pad1_ Net-_Q1-Pad2_ 4k +Q1 Net-_Q1-Pad1_ Net-_Q1-Pad2_ Net-_D1-Pad2_ eSim_NPN +Q3 Net-_Q3-Pad1_ Net-_Q1-Pad1_ Net-_Q3-Pad3_ eSim_NPN +Q5 Net-_Q5-Pad1_ Net-_Q3-Pad1_ Net-_D3-Pad1_ eSim_NPN +D3 Net-_D3-Pad1_ Net-_D3-Pad2_ eSim_Diode +Q6 Net-_D3-Pad2_ Net-_Q3-Pad3_ GNDPWR eSim_NPN +R5 ? Net-_Q3-Pad1_ 1.6k +R7 Net-_R1-Pad1_ Net-_Q5-Pad1_ 130 +R3 Net-_Q3-Pad3_ GNDPWR 1k +D1 GNDPWR Net-_D1-Pad2_ eSim_Diode +R2 Net-_R1-Pad1_ Net-_Q2-Pad2_ 4k +Q2 Net-_Q2-Pad1_ Net-_Q2-Pad2_ Net-_D2-Pad2_ eSim_NPN +Q4 Net-_Q4-Pad1_ Net-_Q2-Pad1_ Net-_Q4-Pad3_ eSim_NPN +Q7 Net-_Q7-Pad1_ Net-_Q4-Pad1_ Net-_D4-Pad1_ eSim_NPN +D4 Net-_D4-Pad1_ Net-_D4-Pad2_ eSim_Diode +Q8 Net-_D4-Pad2_ Net-_Q4-Pad3_ GNDPWR eSim_NPN +R6 ? Net-_Q4-Pad1_ 1.6k +R8 Net-_R1-Pad1_ Net-_Q7-Pad1_ 130 +R4 Net-_Q4-Pad3_ GNDPWR 1k +D2 GNDPWR Net-_D2-Pad2_ eSim_Diode + +.end diff --git a/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.cir.out b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.cir.out new file mode 100644 index 000000000..c180e7d80 --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.cir.out @@ -0,0 +1,74 @@ +* c:\fossee\esim\library\subcircuitlibrary\sn7404_subcircuit\sn7404_subcircuit.cir + +.include NPN.lib +.include D.lib +r17 net-_r1-pad1_ net-_q17-pad2_ 4k +q17 net-_q17-pad1_ net-_q17-pad2_ net-_d9-pad2_ Q2N2222 +q19 net-_q19-pad1_ net-_q17-pad1_ net-_q19-pad3_ Q2N2222 +q21 net-_q21-pad1_ net-_q19-pad1_ net-_d11-pad1_ Q2N2222 +d11 net-_d11-pad1_ net-_d11-pad2_ 1N4148 +q22 net-_d11-pad2_ net-_q19-pad3_ gndpwr Q2N2222 +r21 ? net-_q19-pad1_ 1.6k +r23 net-_r1-pad1_ net-_q21-pad1_ 130 +r19 net-_q19-pad3_ gndpwr 1k +d9 gndpwr net-_d9-pad2_ 1N4148 +* u1 net-_d1-pad2_ net-_d2-pad2_ net-_d3-pad2_ net-_d4-pad2_ net-_d5-pad2_ net-_d6-pad2_ net-_r1-pad1_ gndpwr net-_d7-pad2_ net-_d8-pad2_ net-_d9-pad2_ net-_d10-pad2_ net-_d11-pad2_ net-_d12-pad2_ port +r18 net-_r1-pad1_ net-_q18-pad2_ 4k +q18 net-_q18-pad1_ net-_q18-pad2_ net-_d10-pad2_ Q2N2222 +q20 net-_q20-pad1_ net-_q18-pad1_ net-_q20-pad3_ Q2N2222 +q23 net-_q23-pad1_ net-_q20-pad1_ net-_d12-pad1_ Q2N2222 +d12 net-_d12-pad1_ net-_d12-pad2_ 1N4148 +q24 net-_d12-pad2_ net-_q20-pad3_ gndpwr Q2N2222 +r22 net-_r1-pad1_ net-_q20-pad1_ 1.6k +r24 net-_r1-pad1_ net-_q23-pad1_ 130 +r20 net-_q20-pad3_ gndpwr 1k +d10 gndpwr net-_d10-pad2_ 1N4148 +r9 net-_r1-pad1_ net-_q9-pad2_ 4k +q9 net-_q11-pad2_ net-_q9-pad2_ net-_d5-pad2_ Q2N2222 +q11 net-_q11-pad1_ net-_q11-pad2_ net-_q11-pad3_ Q2N2222 +q13 net-_q13-pad1_ net-_q11-pad1_ net-_d7-pad1_ Q2N2222 +d7 net-_d7-pad1_ net-_d7-pad2_ 1N4148 +q14 net-_d7-pad2_ net-_q11-pad3_ gndpwr Q2N2222 +r13 ? net-_q11-pad1_ 1.6k +r15 net-_r1-pad1_ net-_q13-pad1_ 130 +r11 net-_q11-pad3_ gndpwr 1k +d5 gndpwr net-_d5-pad2_ 1N4148 +r10 net-_r1-pad1_ net-_q10-pad2_ 4k +q10 net-_q10-pad1_ net-_q10-pad2_ net-_d6-pad2_ Q2N2222 +q12 net-_q12-pad1_ net-_q10-pad1_ net-_q12-pad3_ Q2N2222 +q15 net-_q15-pad1_ net-_q12-pad1_ net-_d8-pad1_ Q2N2222 +d8 net-_d8-pad1_ net-_d8-pad2_ 1N4148 +q16 net-_d8-pad2_ net-_q12-pad3_ gndpwr Q2N2222 +r14 ? net-_q12-pad1_ 1.6k +r16 net-_r1-pad1_ net-_q15-pad1_ 130 +r12 net-_q12-pad3_ gndpwr 1k +d6 gndpwr net-_d6-pad2_ 1N4148 +r1 net-_r1-pad1_ net-_q1-pad2_ 4k +q1 net-_q1-pad1_ net-_q1-pad2_ net-_d1-pad2_ Q2N2222 +q3 net-_q3-pad1_ net-_q1-pad1_ net-_q3-pad3_ Q2N2222 +q5 net-_q5-pad1_ net-_q3-pad1_ net-_d3-pad1_ Q2N2222 +d3 net-_d3-pad1_ net-_d3-pad2_ 1N4148 +q6 net-_d3-pad2_ net-_q3-pad3_ gndpwr Q2N2222 +r5 ? net-_q3-pad1_ 1.6k +r7 net-_r1-pad1_ net-_q5-pad1_ 130 +r3 net-_q3-pad3_ gndpwr 1k +d1 gndpwr net-_d1-pad2_ 1N4148 +r2 net-_r1-pad1_ net-_q2-pad2_ 4k +q2 net-_q2-pad1_ net-_q2-pad2_ net-_d2-pad2_ Q2N2222 +q4 net-_q4-pad1_ net-_q2-pad1_ net-_q4-pad3_ Q2N2222 +q7 net-_q7-pad1_ net-_q4-pad1_ net-_d4-pad1_ Q2N2222 +d4 net-_d4-pad1_ net-_d4-pad2_ 1N4148 +q8 net-_d4-pad2_ net-_q4-pad3_ gndpwr Q2N2222 +r6 ? net-_q4-pad1_ 1.6k +r8 net-_r1-pad1_ net-_q7-pad1_ 130 +r4 net-_q4-pad3_ gndpwr 1k +d2 gndpwr net-_d2-pad2_ 1N4148 +.tran 0e-00 0e-00 0e-00 + +* Control Statements +.control +run +print allv > plot_data_v.txt +print alli > plot_data_i.txt +.endc +.end diff --git a/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.pro b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.pro new file mode 100644 index 000000000..e27a398be --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.pro @@ -0,0 +1,73 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] +LibName1=adc-dac +LibName2=memory +LibName3=xilinx +LibName4=microcontrollers +LibName5=dsp +LibName6=microchip +LibName7=analog_switches +LibName8=motorola +LibName9=texas +LibName10=intel +LibName11=audio +LibName12=interface +LibName13=digital-audio +LibName14=philips +LibName15=display +LibName16=cypress +LibName17=siliconi +LibName18=opto +LibName19=atmel +LibName20=contrib +LibName21=power +LibName22=eSim_Plot +LibName23=transistors +LibName24=conn +LibName25=eSim_User +LibName26=regul +LibName27=74xx +LibName28=cmos4000 +LibName29=eSim_Analog +LibName30=eSim_Devices +LibName31=eSim_Digital +LibName32=eSim_Hybrid +LibName33=eSim_Miscellaneous +LibName34=eSim_Power +LibName35=eSim_Sources +LibName36=eSim_Subckt +LibName37=eSim_Nghdl +LibName38=eSim_Ngveri +LibName39=eSim_SKY130 +LibName40=eSim_SKY130_Subckts diff --git a/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.sch b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.sch new file mode 100644 index 000000000..68a868957 --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.sch @@ -0,0 +1,1295 @@ +EESchema Schematic File Version 2 +LIBS:adc-dac +LIBS:memory +LIBS:xilinx +LIBS:microcontrollers +LIBS:dsp +LIBS:microchip +LIBS:analog_switches +LIBS:motorola +LIBS:texas +LIBS:intel +LIBS:audio +LIBS:interface +LIBS:digital-audio +LIBS:philips +LIBS:display +LIBS:cypress +LIBS:siliconi +LIBS:opto +LIBS:atmel +LIBS:contrib +LIBS:power +LIBS:eSim_Plot +LIBS:transistors +LIBS:conn +LIBS:eSim_User +LIBS:regul +LIBS:74xx +LIBS:cmos4000 +LIBS:eSim_Analog +LIBS:eSim_Devices +LIBS:eSim_Digital +LIBS:eSim_Hybrid +LIBS:eSim_Miscellaneous +LIBS:eSim_Power +LIBS:eSim_Sources +LIBS:eSim_Subckt +LIBS:eSim_Nghdl +LIBS:eSim_Ngveri +LIBS:eSim_SKY130 +LIBS:eSim_SKY130_Subckts +EELAYER 25 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L resistor R17 +U 1 1 67A8D41A +P 8310 1340 +F 0 "R17" H 8360 1470 50 0000 C CNN +F 1 "4k" H 8360 1290 50 0000 C CNN +F 2 "" H 8360 1320 30 0000 C CNN +F 3 "" V 8360 1390 30 0000 C CNN + 1 8310 1340 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q17 +U 1 1 67A8D45F +P 8360 2130 +F 0 "Q17" H 8260 2180 50 0000 R CNN +F 1 "eSim_NPN" H 8310 2280 50 0000 R CNN +F 2 "" H 8560 2230 29 0000 C CNN +F 3 "" H 8360 2130 60 0000 C CNN + 1 8360 2130 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q19 +U 1 1 67A8D53A +P 9060 2440 +F 0 "Q19" H 8960 2490 50 0000 R CNN +F 1 "eSim_NPN" H 9010 2590 50 0000 R CNN +F 2 "" H 9260 2540 29 0000 C CNN +F 3 "" H 9060 2440 60 0000 C CNN + 1 9060 2440 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q21 +U 1 1 67A8D58D +P 9670 2100 +F 0 "Q21" H 9570 2150 50 0000 R CNN +F 1 "eSim_NPN" H 9620 2250 50 0000 R CNN +F 2 "" H 9870 2200 29 0000 C CNN +F 3 "" H 9670 2100 60 0000 C CNN + 1 9670 2100 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D11 +U 1 1 67A8D5E0 +P 9770 2570 +F 0 "D11" H 9770 2670 50 0000 C CNN +F 1 "eSim_Diode" H 9770 2470 50 0000 C CNN +F 2 "" H 9770 2570 60 0000 C CNN +F 3 "" H 9770 2570 60 0000 C CNN + 1 9770 2570 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q22 +U 1 1 67A8D651 +P 9670 3320 +F 0 "Q22" H 9570 3370 50 0000 R CNN +F 1 "eSim_NPN" H 9620 3470 50 0000 R CNN +F 2 "" H 9870 3420 29 0000 C CNN +F 3 "" H 9670 3320 60 0000 C CNN + 1 9670 3320 + 1 0 0 -1 +$EndComp +$Comp +L resistor R21 +U 1 1 67A8D6B7 +P 9180 1340 +F 0 "R21" H 9230 1470 50 0000 C CNN +F 1 "1.6k" H 9230 1290 50 0000 C CNN +F 2 "" H 9230 1320 30 0000 C CNN +F 3 "" V 9230 1390 30 0000 C CNN + 1 9180 1340 + 0 1 1 0 +$EndComp +$Comp +L resistor R23 +U 1 1 67A8D748 +P 9720 1350 +F 0 "R23" H 9770 1480 50 0000 C CNN +F 1 "130" H 9770 1300 50 0000 C CNN +F 2 "" H 9770 1330 30 0000 C CNN +F 3 "" V 9770 1400 30 0000 C CNN + 1 9720 1350 + 0 1 1 0 +$EndComp +$Comp +L resistor R19 +U 1 1 67A8D7B4 +P 9150 3640 +F 0 "R19" H 9200 3770 50 0000 C CNN +F 1 "1k" H 9200 3590 50 0000 C CNN +F 2 "" H 9200 3620 30 0000 C CNN +F 3 "" V 9200 3690 30 0000 C CNN + 1 9150 3640 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D9 +U 1 1 67A8DB8E +P 8080 3160 +F 0 "D9" H 8080 3260 50 0000 C CNN +F 1 "eSim_Diode" H 8080 3060 50 0000 C CNN +F 2 "" H 8080 3160 60 0000 C CNN +F 3 "" H 8080 3160 60 0000 C CNN + 1 8080 3160 + 0 -1 -1 0 +$EndComp +$Comp +L GNDPWR #PWR01 +U 1 1 67A8DC1C +P 8230 3870 +F 0 "#PWR01" H 8230 3670 50 0001 C CNN +F 1 "GNDPWR" H 8230 3740 50 0000 C CNN +F 2 "" H 8230 3820 50 0001 C CNN +F 3 "" H 8230 3820 50 0001 C CNN + 1 8230 3870 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 11 1 67A8E528 +P 7440 2230 +F 0 "U1" H 7490 2330 30 0000 C CNN +F 1 "PORT" H 7440 2230 30 0000 C CNN +F 2 "" H 7440 2230 60 0000 C CNN +F 3 "" H 7440 2230 60 0000 C CNN + 11 7440 2230 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 13 1 67A8E964 +P 10300 2930 +F 0 "U1" H 10350 3030 30 0000 C CNN +F 1 "PORT" H 10300 2930 30 0000 C CNN +F 2 "" H 10300 2930 60 0000 C CNN +F 3 "" H 10300 2930 60 0000 C CNN + 13 10300 2930 + -1 0 0 1 +$EndComp +$Comp +L resistor R18 +U 1 1 67A907F1 +P 8310 4420 +F 0 "R18" H 8360 4550 50 0000 C CNN +F 1 "4k" H 8360 4370 50 0000 C CNN +F 2 "" H 8360 4400 30 0000 C CNN +F 3 "" V 8360 4470 30 0000 C CNN + 1 8310 4420 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q18 +U 1 1 67A907F7 +P 8360 5210 +F 0 "Q18" H 8260 5260 50 0000 R CNN +F 1 "eSim_NPN" H 8310 5360 50 0000 R CNN +F 2 "" H 8560 5310 29 0000 C CNN +F 3 "" H 8360 5210 60 0000 C CNN + 1 8360 5210 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q20 +U 1 1 67A907FD +P 9060 5520 +F 0 "Q20" H 8960 5570 50 0000 R CNN +F 1 "eSim_NPN" H 9010 5670 50 0000 R CNN +F 2 "" H 9260 5620 29 0000 C CNN +F 3 "" H 9060 5520 60 0000 C CNN + 1 9060 5520 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q23 +U 1 1 67A90803 +P 9670 5180 +F 0 "Q23" H 9570 5230 50 0000 R CNN +F 1 "eSim_NPN" H 9620 5330 50 0000 R CNN +F 2 "" H 9870 5280 29 0000 C CNN +F 3 "" H 9670 5180 60 0000 C CNN + 1 9670 5180 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D12 +U 1 1 67A90809 +P 9770 5650 +F 0 "D12" H 9770 5750 50 0000 C CNN +F 1 "eSim_Diode" H 9770 5550 50 0000 C CNN +F 2 "" H 9770 5650 60 0000 C CNN +F 3 "" H 9770 5650 60 0000 C CNN + 1 9770 5650 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q24 +U 1 1 67A9080F +P 9670 6400 +F 0 "Q24" H 9570 6450 50 0000 R CNN +F 1 "eSim_NPN" H 9620 6550 50 0000 R CNN +F 2 "" H 9870 6500 29 0000 C CNN +F 3 "" H 9670 6400 60 0000 C CNN + 1 9670 6400 + 1 0 0 -1 +$EndComp +$Comp +L resistor R22 +U 1 1 67A90815 +P 9180 4420 +F 0 "R22" H 9230 4550 50 0000 C CNN +F 1 "1.6k" H 9230 4370 50 0000 C CNN +F 2 "" H 9230 4400 30 0000 C CNN +F 3 "" V 9230 4470 30 0000 C CNN + 1 9180 4420 + 0 1 1 0 +$EndComp +$Comp +L resistor R24 +U 1 1 67A9081B +P 9720 4430 +F 0 "R24" H 9770 4560 50 0000 C CNN +F 1 "130" H 9770 4380 50 0000 C CNN +F 2 "" H 9770 4410 30 0000 C CNN +F 3 "" V 9770 4480 30 0000 C CNN + 1 9720 4430 + 0 1 1 0 +$EndComp +$Comp +L resistor R20 +U 1 1 67A90821 +P 9150 6720 +F 0 "R20" H 9200 6850 50 0000 C CNN +F 1 "1k" H 9200 6670 50 0000 C CNN +F 2 "" H 9200 6700 30 0000 C CNN +F 3 "" V 9200 6770 30 0000 C CNN + 1 9150 6720 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D10 +U 1 1 67A90827 +P 8080 6240 +F 0 "D10" H 8080 6340 50 0000 C CNN +F 1 "eSim_Diode" H 8080 6140 50 0000 C CNN +F 2 "" H 8080 6240 60 0000 C CNN +F 3 "" H 8080 6240 60 0000 C CNN + 1 8080 6240 + 0 -1 -1 0 +$EndComp +$Comp +L GNDPWR #PWR02 +U 1 1 67A9082D +P 8230 6950 +F 0 "#PWR02" H 8230 6750 50 0001 C CNN +F 1 "GNDPWR" H 8230 6820 50 0000 C CNN +F 2 "" H 8230 6900 50 0001 C CNN +F 3 "" H 8230 6900 50 0001 C CNN + 1 8230 6950 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 12 1 67A9084B +P 7440 5310 +F 0 "U1" H 7490 5410 30 0000 C CNN +F 1 "PORT" H 7440 5310 30 0000 C CNN +F 2 "" H 7440 5310 60 0000 C CNN +F 3 "" H 7440 5310 60 0000 C CNN + 12 7440 5310 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 14 1 67A90854 +P 10300 6010 +F 0 "U1" H 10350 6110 30 0000 C CNN +F 1 "PORT" H 10300 6010 30 0000 C CNN +F 2 "" H 10300 6010 60 0000 C CNN +F 3 "" H 10300 6010 60 0000 C CNN + 14 10300 6010 + -1 0 0 1 +$EndComp +$Comp +L resistor R9 +U 1 1 67A945DC +P 5120 1340 +F 0 "R9" H 5170 1470 50 0000 C CNN +F 1 "4k" H 5170 1290 50 0000 C CNN +F 2 "" H 5170 1320 30 0000 C CNN +F 3 "" V 5170 1390 30 0000 C CNN + 1 5120 1340 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q9 +U 1 1 67A945E2 +P 5170 2130 +F 0 "Q9" H 5070 2180 50 0000 R CNN +F 1 "eSim_NPN" H 5120 2280 50 0000 R CNN +F 2 "" H 5370 2230 29 0000 C CNN +F 3 "" H 5170 2130 60 0000 C CNN + 1 5170 2130 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q11 +U 1 1 67A945E8 +P 5870 2440 +F 0 "Q11" H 5770 2490 50 0000 R CNN +F 1 "eSim_NPN" H 5820 2590 50 0000 R CNN +F 2 "" H 6070 2540 29 0000 C CNN +F 3 "" H 5870 2440 60 0000 C CNN + 1 5870 2440 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q13 +U 1 1 67A945EE +P 6480 2100 +F 0 "Q13" H 6380 2150 50 0000 R CNN +F 1 "eSim_NPN" H 6430 2250 50 0000 R CNN +F 2 "" H 6680 2200 29 0000 C CNN +F 3 "" H 6480 2100 60 0000 C CNN + 1 6480 2100 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D7 +U 1 1 67A945F4 +P 6580 2570 +F 0 "D7" H 6580 2670 50 0000 C CNN +F 1 "eSim_Diode" H 6580 2470 50 0000 C CNN +F 2 "" H 6580 2570 60 0000 C CNN +F 3 "" H 6580 2570 60 0000 C CNN + 1 6580 2570 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q14 +U 1 1 67A945FA +P 6480 3320 +F 0 "Q14" H 6380 3370 50 0000 R CNN +F 1 "eSim_NPN" H 6430 3470 50 0000 R CNN +F 2 "" H 6680 3420 29 0000 C CNN +F 3 "" H 6480 3320 60 0000 C CNN + 1 6480 3320 + 1 0 0 -1 +$EndComp +$Comp +L resistor R13 +U 1 1 67A94600 +P 5990 1340 +F 0 "R13" H 6040 1470 50 0000 C CNN +F 1 "1.6k" H 6040 1290 50 0000 C CNN +F 2 "" H 6040 1320 30 0000 C CNN +F 3 "" V 6040 1390 30 0000 C CNN + 1 5990 1340 + 0 1 1 0 +$EndComp +$Comp +L resistor R15 +U 1 1 67A94606 +P 6530 1350 +F 0 "R15" H 6580 1480 50 0000 C CNN +F 1 "130" H 6580 1300 50 0000 C CNN +F 2 "" H 6580 1330 30 0000 C CNN +F 3 "" V 6580 1400 30 0000 C CNN + 1 6530 1350 + 0 1 1 0 +$EndComp +$Comp +L resistor R11 +U 1 1 67A9460C +P 5960 3640 +F 0 "R11" H 6010 3770 50 0000 C CNN +F 1 "1k" H 6010 3590 50 0000 C CNN +F 2 "" H 6010 3620 30 0000 C CNN +F 3 "" V 6010 3690 30 0000 C CNN + 1 5960 3640 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D5 +U 1 1 67A94612 +P 4890 3160 +F 0 "D5" H 4890 3260 50 0000 C CNN +F 1 "eSim_Diode" H 4890 3060 50 0000 C CNN +F 2 "" H 4890 3160 60 0000 C CNN +F 3 "" H 4890 3160 60 0000 C CNN + 1 4890 3160 + 0 -1 -1 0 +$EndComp +$Comp +L GNDPWR #PWR03 +U 1 1 67A94618 +P 5040 3870 +F 0 "#PWR03" H 5040 3670 50 0001 C CNN +F 1 "GNDPWR" H 5040 3740 50 0000 C CNN +F 2 "" H 5040 3820 50 0001 C CNN +F 3 "" H 5040 3820 50 0001 C CNN + 1 5040 3870 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 5 1 67A94636 +P 4250 2230 +F 0 "U1" H 4300 2330 30 0000 C CNN +F 1 "PORT" H 4250 2230 30 0000 C CNN +F 2 "" H 4250 2230 60 0000 C CNN +F 3 "" H 4250 2230 60 0000 C CNN + 5 4250 2230 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 9 1 67A9463F +P 7110 2930 +F 0 "U1" H 7160 3030 30 0000 C CNN +F 1 "PORT" H 7110 2930 30 0000 C CNN +F 2 "" H 7110 2930 60 0000 C CNN +F 3 "" H 7110 2930 60 0000 C CNN + 9 7110 2930 + -1 0 0 1 +$EndComp +$Comp +L resistor R10 +U 1 1 67A94647 +P 5120 4420 +F 0 "R10" H 5170 4550 50 0000 C CNN +F 1 "4k" H 5170 4370 50 0000 C CNN +F 2 "" H 5170 4400 30 0000 C CNN +F 3 "" V 5170 4470 30 0000 C CNN + 1 5120 4420 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q10 +U 1 1 67A9464D +P 5170 5210 +F 0 "Q10" H 5070 5260 50 0000 R CNN +F 1 "eSim_NPN" H 5120 5360 50 0000 R CNN +F 2 "" H 5370 5310 29 0000 C CNN +F 3 "" H 5170 5210 60 0000 C CNN + 1 5170 5210 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q12 +U 1 1 67A94653 +P 5870 5520 +F 0 "Q12" H 5770 5570 50 0000 R CNN +F 1 "eSim_NPN" H 5820 5670 50 0000 R CNN +F 2 "" H 6070 5620 29 0000 C CNN +F 3 "" H 5870 5520 60 0000 C CNN + 1 5870 5520 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q15 +U 1 1 67A94659 +P 6480 5180 +F 0 "Q15" H 6380 5230 50 0000 R CNN +F 1 "eSim_NPN" H 6430 5330 50 0000 R CNN +F 2 "" H 6680 5280 29 0000 C CNN +F 3 "" H 6480 5180 60 0000 C CNN + 1 6480 5180 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D8 +U 1 1 67A9465F +P 6580 5650 +F 0 "D8" H 6580 5750 50 0000 C CNN +F 1 "eSim_Diode" H 6580 5550 50 0000 C CNN +F 2 "" H 6580 5650 60 0000 C CNN +F 3 "" H 6580 5650 60 0000 C CNN + 1 6580 5650 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q16 +U 1 1 67A94665 +P 6480 6400 +F 0 "Q16" H 6380 6450 50 0000 R CNN +F 1 "eSim_NPN" H 6430 6550 50 0000 R CNN +F 2 "" H 6680 6500 29 0000 C CNN +F 3 "" H 6480 6400 60 0000 C CNN + 1 6480 6400 + 1 0 0 -1 +$EndComp +$Comp +L resistor R14 +U 1 1 67A9466B +P 5990 4420 +F 0 "R14" H 6040 4550 50 0000 C CNN +F 1 "1.6k" H 6040 4370 50 0000 C CNN +F 2 "" H 6040 4400 30 0000 C CNN +F 3 "" V 6040 4470 30 0000 C CNN + 1 5990 4420 + 0 1 1 0 +$EndComp +$Comp +L resistor R16 +U 1 1 67A94671 +P 6530 4430 +F 0 "R16" H 6580 4560 50 0000 C CNN +F 1 "130" H 6580 4380 50 0000 C CNN +F 2 "" H 6580 4410 30 0000 C CNN +F 3 "" V 6580 4480 30 0000 C CNN + 1 6530 4430 + 0 1 1 0 +$EndComp +$Comp +L resistor R12 +U 1 1 67A94677 +P 5960 6720 +F 0 "R12" H 6010 6850 50 0000 C CNN +F 1 "1k" H 6010 6670 50 0000 C CNN +F 2 "" H 6010 6700 30 0000 C CNN +F 3 "" V 6010 6770 30 0000 C CNN + 1 5960 6720 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D6 +U 1 1 67A9467D +P 4890 6240 +F 0 "D6" H 4890 6340 50 0000 C CNN +F 1 "eSim_Diode" H 4890 6140 50 0000 C CNN +F 2 "" H 4890 6240 60 0000 C CNN +F 3 "" H 4890 6240 60 0000 C CNN + 1 4890 6240 + 0 -1 -1 0 +$EndComp +$Comp +L GNDPWR #PWR04 +U 1 1 67A94683 +P 5040 6950 +F 0 "#PWR04" H 5040 6750 50 0001 C CNN +F 1 "GNDPWR" H 5040 6820 50 0000 C CNN +F 2 "" H 5040 6900 50 0001 C CNN +F 3 "" H 5040 6900 50 0001 C CNN + 1 5040 6950 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 6 1 67A946A1 +P 4250 5310 +F 0 "U1" H 4300 5410 30 0000 C CNN +F 1 "PORT" H 4250 5310 30 0000 C CNN +F 2 "" H 4250 5310 60 0000 C CNN +F 3 "" H 4250 5310 60 0000 C CNN + 6 4250 5310 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 10 1 67A946AA +P 7110 6010 +F 0 "U1" H 7160 6110 30 0000 C CNN +F 1 "PORT" H 7110 6010 30 0000 C CNN +F 2 "" H 7110 6010 60 0000 C CNN +F 3 "" H 7110 6010 60 0000 C CNN + 10 7110 6010 + -1 0 0 1 +$EndComp +$Comp +L resistor R1 +U 1 1 67A9B5FA +P 2100 1340 +F 0 "R1" H 2150 1470 50 0000 C CNN +F 1 "4k" H 2150 1290 50 0000 C CNN +F 2 "" H 2150 1320 30 0000 C CNN +F 3 "" V 2150 1390 30 0000 C CNN + 1 2100 1340 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q1 +U 1 1 67A9B600 +P 2150 2130 +F 0 "Q1" H 2050 2180 50 0000 R CNN +F 1 "eSim_NPN" H 2100 2280 50 0000 R CNN +F 2 "" H 2350 2230 29 0000 C CNN +F 3 "" H 2150 2130 60 0000 C CNN + 1 2150 2130 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q3 +U 1 1 67A9B606 +P 2850 2440 +F 0 "Q3" H 2750 2490 50 0000 R CNN +F 1 "eSim_NPN" H 2800 2590 50 0000 R CNN +F 2 "" H 3050 2540 29 0000 C CNN +F 3 "" H 2850 2440 60 0000 C CNN + 1 2850 2440 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q5 +U 1 1 67A9B60C +P 3460 2100 +F 0 "Q5" H 3360 2150 50 0000 R CNN +F 1 "eSim_NPN" H 3410 2250 50 0000 R CNN +F 2 "" H 3660 2200 29 0000 C CNN +F 3 "" H 3460 2100 60 0000 C CNN + 1 3460 2100 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D3 +U 1 1 67A9B612 +P 3560 2570 +F 0 "D3" H 3560 2670 50 0000 C CNN +F 1 "eSim_Diode" H 3560 2470 50 0000 C CNN +F 2 "" H 3560 2570 60 0000 C CNN +F 3 "" H 3560 2570 60 0000 C CNN + 1 3560 2570 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q6 +U 1 1 67A9B618 +P 3460 3320 +F 0 "Q6" H 3360 3370 50 0000 R CNN +F 1 "eSim_NPN" H 3410 3470 50 0000 R CNN +F 2 "" H 3660 3420 29 0000 C CNN +F 3 "" H 3460 3320 60 0000 C CNN + 1 3460 3320 + 1 0 0 -1 +$EndComp +$Comp +L resistor R5 +U 1 1 67A9B61E +P 2970 1340 +F 0 "R5" H 3020 1470 50 0000 C CNN +F 1 "1.6k" H 3020 1290 50 0000 C CNN +F 2 "" H 3020 1320 30 0000 C CNN +F 3 "" V 3020 1390 30 0000 C CNN + 1 2970 1340 + 0 1 1 0 +$EndComp +$Comp +L resistor R7 +U 1 1 67A9B624 +P 3510 1350 +F 0 "R7" H 3560 1480 50 0000 C CNN +F 1 "130" H 3560 1300 50 0000 C CNN +F 2 "" H 3560 1330 30 0000 C CNN +F 3 "" V 3560 1400 30 0000 C CNN + 1 3510 1350 + 0 1 1 0 +$EndComp +$Comp +L resistor R3 +U 1 1 67A9B62A +P 2940 3640 +F 0 "R3" H 2990 3770 50 0000 C CNN +F 1 "1k" H 2990 3590 50 0000 C CNN +F 2 "" H 2990 3620 30 0000 C CNN +F 3 "" V 2990 3690 30 0000 C CNN + 1 2940 3640 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D1 +U 1 1 67A9B630 +P 1870 3160 +F 0 "D1" H 1870 3260 50 0000 C CNN +F 1 "eSim_Diode" H 1870 3060 50 0000 C CNN +F 2 "" H 1870 3160 60 0000 C CNN +F 3 "" H 1870 3160 60 0000 C CNN + 1 1870 3160 + 0 -1 -1 0 +$EndComp +$Comp +L GNDPWR #PWR05 +U 1 1 67A9B636 +P 2020 3870 +F 0 "#PWR05" H 2020 3670 50 0001 C CNN +F 1 "GNDPWR" H 2020 3740 50 0000 C CNN +F 2 "" H 2020 3820 50 0001 C CNN +F 3 "" H 2020 3820 50 0001 C CNN + 1 2020 3870 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 1 1 67A9B654 +P 1230 2230 +F 0 "U1" H 1280 2330 30 0000 C CNN +F 1 "PORT" H 1230 2230 30 0000 C CNN +F 2 "" H 1230 2230 60 0000 C CNN +F 3 "" H 1230 2230 60 0000 C CNN + 1 1230 2230 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 3 1 67A9B65D +P 4090 2930 +F 0 "U1" H 4140 3030 30 0000 C CNN +F 1 "PORT" H 4090 2930 30 0000 C CNN +F 2 "" H 4090 2930 60 0000 C CNN +F 3 "" H 4090 2930 60 0000 C CNN + 3 4090 2930 + -1 0 0 1 +$EndComp +$Comp +L resistor R2 +U 1 1 67A9B665 +P 2100 4420 +F 0 "R2" H 2150 4550 50 0000 C CNN +F 1 "4k" H 2150 4370 50 0000 C CNN +F 2 "" H 2150 4400 30 0000 C CNN +F 3 "" V 2150 4470 30 0000 C CNN + 1 2100 4420 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q2 +U 1 1 67A9B66B +P 2150 5210 +F 0 "Q2" H 2050 5260 50 0000 R CNN +F 1 "eSim_NPN" H 2100 5360 50 0000 R CNN +F 2 "" H 2350 5310 29 0000 C CNN +F 3 "" H 2150 5210 60 0000 C CNN + 1 2150 5210 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q4 +U 1 1 67A9B671 +P 2850 5520 +F 0 "Q4" H 2750 5570 50 0000 R CNN +F 1 "eSim_NPN" H 2800 5670 50 0000 R CNN +F 2 "" H 3050 5620 29 0000 C CNN +F 3 "" H 2850 5520 60 0000 C CNN + 1 2850 5520 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q7 +U 1 1 67A9B677 +P 3460 5180 +F 0 "Q7" H 3360 5230 50 0000 R CNN +F 1 "eSim_NPN" H 3410 5330 50 0000 R CNN +F 2 "" H 3660 5280 29 0000 C CNN +F 3 "" H 3460 5180 60 0000 C CNN + 1 3460 5180 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D4 +U 1 1 67A9B67D +P 3560 5650 +F 0 "D4" H 3560 5750 50 0000 C CNN +F 1 "eSim_Diode" H 3560 5550 50 0000 C CNN +F 2 "" H 3560 5650 60 0000 C CNN +F 3 "" H 3560 5650 60 0000 C CNN + 1 3560 5650 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q8 +U 1 1 67A9B683 +P 3460 6400 +F 0 "Q8" H 3360 6450 50 0000 R CNN +F 1 "eSim_NPN" H 3410 6550 50 0000 R CNN +F 2 "" H 3660 6500 29 0000 C CNN +F 3 "" H 3460 6400 60 0000 C CNN + 1 3460 6400 + 1 0 0 -1 +$EndComp +$Comp +L resistor R6 +U 1 1 67A9B689 +P 2970 4420 +F 0 "R6" H 3020 4550 50 0000 C CNN +F 1 "1.6k" H 3020 4370 50 0000 C CNN +F 2 "" H 3020 4400 30 0000 C CNN +F 3 "" V 3020 4470 30 0000 C CNN + 1 2970 4420 + 0 1 1 0 +$EndComp +$Comp +L resistor R8 +U 1 1 67A9B68F +P 3510 4430 +F 0 "R8" H 3560 4560 50 0000 C CNN +F 1 "130" H 3560 4380 50 0000 C CNN +F 2 "" H 3560 4410 30 0000 C CNN +F 3 "" V 3560 4480 30 0000 C CNN + 1 3510 4430 + 0 1 1 0 +$EndComp +$Comp +L resistor R4 +U 1 1 67A9B695 +P 2940 6720 +F 0 "R4" H 2990 6850 50 0000 C CNN +F 1 "1k" H 2990 6670 50 0000 C CNN +F 2 "" H 2990 6700 30 0000 C CNN +F 3 "" V 2990 6770 30 0000 C CNN + 1 2940 6720 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D2 +U 1 1 67A9B69B +P 1870 6240 +F 0 "D2" H 1870 6340 50 0000 C CNN +F 1 "eSim_Diode" H 1870 6140 50 0000 C CNN +F 2 "" H 1870 6240 60 0000 C CNN +F 3 "" H 1870 6240 60 0000 C CNN + 1 1870 6240 + 0 -1 -1 0 +$EndComp +$Comp +L GNDPWR #PWR06 +U 1 1 67A9B6A1 +P 2020 6950 +F 0 "#PWR06" H 2020 6750 50 0001 C CNN +F 1 "GNDPWR" H 2020 6820 50 0000 C CNN +F 2 "" H 2020 6900 50 0001 C CNN +F 3 "" H 2020 6900 50 0001 C CNN + 1 2020 6950 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 2 1 67A9B6BF +P 1230 5310 +F 0 "U1" H 1280 5410 30 0000 C CNN +F 1 "PORT" H 1230 5310 30 0000 C CNN +F 2 "" H 1230 5310 60 0000 C CNN +F 3 "" H 1230 5310 60 0000 C CNN + 2 1230 5310 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 4 1 67A9B6C8 +P 4090 6010 +F 0 "U1" H 4140 6110 30 0000 C CNN +F 1 "PORT" H 4090 6010 30 0000 C CNN +F 2 "" H 4090 6010 60 0000 C CNN +F 3 "" H 4090 6010 60 0000 C CNN + 4 4090 6010 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 7 1 67A9B8CA +P 5900 680 +F 0 "U1" H 5950 780 30 0000 C CNN +F 1 "PORT" H 5900 680 30 0000 C CNN +F 2 "" H 5900 680 60 0000 C CNN +F 3 "" H 5900 680 60 0000 C CNN + 7 5900 680 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 8 1 67A9B9F6 +P 6040 7590 +F 0 "U1" H 6090 7690 30 0000 C CNN +F 1 "PORT" H 6040 7590 30 0000 C CNN +F 2 "" H 6040 7590 60 0000 C CNN +F 3 "" H 6040 7590 60 0000 C CNN + 8 6040 7590 + 0 -1 -1 0 +$EndComp +Wire Wire Line + 8360 1540 8360 1930 +Wire Wire Line + 8560 2230 8710 2230 +Wire Wire Line + 8710 2230 8710 2440 +Wire Wire Line + 8710 2440 8860 2440 +Wire Wire Line + 9160 2240 9160 2100 +Wire Wire Line + 9160 2100 9470 2100 +Wire Wire Line + 9770 2300 9770 2420 +Wire Wire Line + 9770 2720 9770 3120 +Wire Wire Line + 9770 3870 9770 3520 +Wire Wire Line + 8080 3870 10010 3870 +Wire Wire Line + 8080 3310 8080 3870 +Connection ~ 8230 3870 +Wire Wire Line + 9200 3840 9200 3870 +Connection ~ 9200 3870 +Wire Wire Line + 9160 3320 9470 3320 +Wire Wire Line + 9200 3320 9200 3540 +Wire Wire Line + 9160 2640 9160 3320 +Connection ~ 9200 3320 +Wire Wire Line + 9230 1540 9230 2100 +Connection ~ 9230 2100 +Wire Wire Line + 9770 1550 9770 1900 +Wire Wire Line + 9770 1040 9770 1250 +Wire Wire Line + 9770 1200 8360 1200 +Wire Wire Line + 8360 1200 8360 1240 +Wire Wire Line + 8160 2230 7690 2230 +Wire Wire Line + 8080 3010 8080 2230 +Connection ~ 8080 2230 +Wire Wire Line + 10050 2930 9770 2930 +Connection ~ 9770 2930 +Wire Wire Line + 8360 4620 8360 5010 +Wire Wire Line + 8560 5310 8710 5310 +Wire Wire Line + 8710 5310 8710 5520 +Wire Wire Line + 8710 5520 8860 5520 +Wire Wire Line + 9160 5320 9160 5180 +Wire Wire Line + 9160 5180 9470 5180 +Wire Wire Line + 9770 5380 9770 5500 +Wire Wire Line + 9770 5800 9770 6200 +Wire Wire Line + 9770 6600 9770 7210 +Wire Wire Line + 8080 6950 9770 6950 +Wire Wire Line + 8080 6390 8080 6950 +Connection ~ 8230 6950 +Wire Wire Line + 9200 6920 9200 6950 +Connection ~ 9200 6950 +Wire Wire Line + 9160 6400 9470 6400 +Wire Wire Line + 9200 6400 9200 6620 +Wire Wire Line + 9160 5720 9160 6400 +Connection ~ 9200 6400 +Wire Wire Line + 9230 4620 9230 5180 +Connection ~ 9230 5180 +Wire Wire Line + 9770 4630 9770 4980 +Wire Wire Line + 9770 4330 9770 4280 +Wire Wire Line + 8360 4280 8360 4320 +Wire Wire Line + 8160 5310 7690 5310 +Wire Wire Line + 8080 6090 8080 5310 +Connection ~ 8080 5310 +Wire Wire Line + 10050 6010 9770 6010 +Connection ~ 9770 6010 +Wire Wire Line + 5170 1540 5170 1930 +Wire Wire Line + 5370 2230 5520 2230 +Wire Wire Line + 5520 2230 5520 2440 +Wire Wire Line + 5520 2440 5670 2440 +Wire Wire Line + 5970 2240 5970 2100 +Wire Wire Line + 5970 2100 6280 2100 +Wire Wire Line + 6580 2300 6580 2420 +Wire Wire Line + 6580 2720 6580 3120 +Wire Wire Line + 6580 3870 6580 3520 +Wire Wire Line + 4890 3870 6820 3870 +Wire Wire Line + 4890 3310 4890 3870 +Connection ~ 5040 3870 +Wire Wire Line + 6010 3840 6010 3870 +Connection ~ 6010 3870 +Wire Wire Line + 5970 3320 6280 3320 +Wire Wire Line + 6010 3320 6010 3540 +Wire Wire Line + 5970 2640 5970 3320 +Connection ~ 6010 3320 +Wire Wire Line + 6040 1540 6040 2100 +Connection ~ 6040 2100 +Wire Wire Line + 6580 1550 6580 1900 +Wire Wire Line + 6580 1200 5170 1200 +Wire Wire Line + 5170 1200 5170 1240 +Wire Wire Line + 4970 2230 4500 2230 +Wire Wire Line + 4890 3010 4890 2230 +Connection ~ 4890 2230 +Wire Wire Line + 6860 2930 6580 2930 +Connection ~ 6580 2930 +Wire Wire Line + 5170 4620 5170 5010 +Wire Wire Line + 5370 5310 5520 5310 +Wire Wire Line + 5520 5310 5520 5520 +Wire Wire Line + 5520 5520 5670 5520 +Wire Wire Line + 5970 5320 5970 5180 +Wire Wire Line + 5970 5180 6280 5180 +Wire Wire Line + 6580 5380 6580 5500 +Wire Wire Line + 6580 5800 6580 6200 +Wire Wire Line + 6580 6600 6580 7210 +Wire Wire Line + 4890 6950 6580 6950 +Wire Wire Line + 4890 6390 4890 6950 +Connection ~ 5040 6950 +Wire Wire Line + 6010 6920 6010 6950 +Connection ~ 6010 6950 +Wire Wire Line + 5970 6400 6280 6400 +Wire Wire Line + 6010 6400 6010 6620 +Wire Wire Line + 5970 5720 5970 6400 +Connection ~ 6010 6400 +Wire Wire Line + 6040 4620 6040 5180 +Connection ~ 6040 5180 +Wire Wire Line + 6580 4630 6580 4980 +Wire Wire Line + 6580 4330 6580 4280 +Wire Wire Line + 5170 4280 6760 4280 +Wire Wire Line + 5170 4280 5170 4320 +Wire Wire Line + 4970 5310 4500 5310 +Wire Wire Line + 4890 6090 4890 5310 +Connection ~ 4890 5310 +Wire Wire Line + 6860 6010 6580 6010 +Connection ~ 6580 6010 +Wire Wire Line + 2150 1540 2150 1930 +Wire Wire Line + 2350 2230 2500 2230 +Wire Wire Line + 2500 2230 2500 2440 +Wire Wire Line + 2500 2440 2650 2440 +Wire Wire Line + 2950 2240 2950 2100 +Wire Wire Line + 2950 2100 3260 2100 +Wire Wire Line + 3560 2300 3560 2420 +Wire Wire Line + 3560 2720 3560 3120 +Wire Wire Line + 3560 3870 3560 3520 +Wire Wire Line + 1870 3870 3680 3870 +Wire Wire Line + 1870 3310 1870 3870 +Connection ~ 2020 3870 +Wire Wire Line + 2990 3840 2990 3870 +Connection ~ 2990 3870 +Wire Wire Line + 2950 3320 3260 3320 +Wire Wire Line + 2990 3320 2990 3540 +Wire Wire Line + 2950 2640 2950 3320 +Connection ~ 2990 3320 +Wire Wire Line + 3020 1540 3020 2100 +Connection ~ 3020 2100 +Wire Wire Line + 3560 1550 3560 1900 +Wire Wire Line + 3560 1200 2150 1200 +Wire Wire Line + 2150 1200 2150 1240 +Wire Wire Line + 1950 2230 1480 2230 +Wire Wire Line + 1870 3010 1870 2230 +Connection ~ 1870 2230 +Wire Wire Line + 3840 2930 3560 2930 +Connection ~ 3560 2930 +Wire Wire Line + 2150 4620 2150 5010 +Wire Wire Line + 2350 5310 2500 5310 +Wire Wire Line + 2500 5310 2500 5520 +Wire Wire Line + 2500 5520 2650 5520 +Wire Wire Line + 2950 5320 2950 5180 +Wire Wire Line + 2950 5180 3260 5180 +Wire Wire Line + 3560 5380 3560 5500 +Wire Wire Line + 3560 5800 3560 6200 +Wire Wire Line + 3560 6600 3560 7210 +Wire Wire Line + 1870 6950 3560 6950 +Wire Wire Line + 1870 6390 1870 6950 +Connection ~ 2020 6950 +Wire Wire Line + 2990 6920 2990 6950 +Connection ~ 2990 6950 +Wire Wire Line + 2950 6400 3260 6400 +Wire Wire Line + 2990 6400 2990 6620 +Wire Wire Line + 2950 5720 2950 6400 +Connection ~ 2990 6400 +Wire Wire Line + 3020 4620 3020 5180 +Connection ~ 3020 5180 +Wire Wire Line + 3560 4630 3560 4980 +Wire Wire Line + 3560 4330 3560 4280 +Wire Wire Line + 2150 4280 3800 4280 +Wire Wire Line + 2150 4280 2150 4320 +Wire Wire Line + 1950 5310 1480 5310 +Wire Wire Line + 1870 6090 1870 5310 +Connection ~ 1870 5310 +Wire Wire Line + 3840 6010 3560 6010 +Connection ~ 3560 6010 +Connection ~ 3560 1200 +Connection ~ 6580 1200 +Wire Wire Line + 3560 1040 9920 1040 +Connection ~ 3560 1040 +Wire Wire Line + 3560 1040 3560 1250 +Wire Wire Line + 6580 1250 6580 1040 +Wire Wire Line + 5900 930 5900 1040 +Connection ~ 5900 1040 +Connection ~ 6580 1040 +Connection ~ 9770 1200 +Wire Wire Line + 3800 4280 3800 1040 +Connection ~ 3800 1040 +Connection ~ 3560 4280 +Wire Wire Line + 6760 4280 6760 1040 +Connection ~ 6760 1040 +Connection ~ 6580 4280 +Wire Wire Line + 9920 1040 9920 4280 +Connection ~ 9770 1040 +Connection ~ 9770 4280 +Wire Wire Line + 3560 7210 10010 7210 +Connection ~ 3560 6950 +Connection ~ 9770 6950 +Connection ~ 6580 7210 +Connection ~ 6580 6950 +Wire Wire Line + 6040 7340 6040 7210 +Connection ~ 6040 7210 +Wire Wire Line + 3680 3870 3680 7210 +Connection ~ 3680 7210 +Connection ~ 3560 3870 +Wire Wire Line + 6820 3870 6820 7210 +Connection ~ 6820 7210 +Connection ~ 6580 3870 +Connection ~ 9770 7210 +Connection ~ 9920 4280 +Wire Wire Line + 9920 4280 8360 4280 +Wire Wire Line + 9230 4320 9230 4280 +Connection ~ 9230 4280 +Wire Wire Line + 10010 7210 10010 3870 +Connection ~ 9770 3870 +$EndSCHEMATC diff --git a/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.sub b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.sub new file mode 100644 index 000000000..a86a8a625 --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit.sub @@ -0,0 +1,68 @@ +* Subcircuit SN7404_Subcircuit +.subckt SN7404_Subcircuit net-_d1-pad2_ net-_d2-pad2_ net-_d3-pad2_ net-_d4-pad2_ net-_d5-pad2_ net-_d6-pad2_ net-_r1-pad1_ gndpwr net-_d7-pad2_ net-_d8-pad2_ net-_d9-pad2_ net-_d10-pad2_ net-_d11-pad2_ net-_d12-pad2_ +* c:\fossee\esim\library\subcircuitlibrary\sn7404_subcircuit\sn7404_subcircuit.cir +.include NPN.lib +.include D.lib +r17 net-_r1-pad1_ net-_q17-pad2_ 4k +q17 net-_q17-pad1_ net-_q17-pad2_ net-_d9-pad2_ Q2N2222 +q19 net-_q19-pad1_ net-_q17-pad1_ net-_q19-pad3_ Q2N2222 +q21 net-_q21-pad1_ net-_q19-pad1_ net-_d11-pad1_ Q2N2222 +d11 net-_d11-pad1_ net-_d11-pad2_ 1N4148 +q22 net-_d11-pad2_ net-_q19-pad3_ gndpwr Q2N2222 +r21 ? net-_q19-pad1_ 1.6k +r23 net-_r1-pad1_ net-_q21-pad1_ 130 +r19 net-_q19-pad3_ gndpwr 1k +d9 gndpwr net-_d9-pad2_ 1N4148 +r18 net-_r1-pad1_ net-_q18-pad2_ 4k +q18 net-_q18-pad1_ net-_q18-pad2_ net-_d10-pad2_ Q2N2222 +q20 net-_q20-pad1_ net-_q18-pad1_ net-_q20-pad3_ Q2N2222 +q23 net-_q23-pad1_ net-_q20-pad1_ net-_d12-pad1_ Q2N2222 +d12 net-_d12-pad1_ net-_d12-pad2_ 1N4148 +q24 net-_d12-pad2_ net-_q20-pad3_ gndpwr Q2N2222 +r22 net-_r1-pad1_ net-_q20-pad1_ 1.6k +r24 net-_r1-pad1_ net-_q23-pad1_ 130 +r20 net-_q20-pad3_ gndpwr 1k +d10 gndpwr net-_d10-pad2_ 1N4148 +r9 net-_r1-pad1_ net-_q9-pad2_ 4k +q9 net-_q11-pad2_ net-_q9-pad2_ net-_d5-pad2_ Q2N2222 +q11 net-_q11-pad1_ net-_q11-pad2_ net-_q11-pad3_ Q2N2222 +q13 net-_q13-pad1_ net-_q11-pad1_ net-_d7-pad1_ Q2N2222 +d7 net-_d7-pad1_ net-_d7-pad2_ 1N4148 +q14 net-_d7-pad2_ net-_q11-pad3_ gndpwr Q2N2222 +r13 ? net-_q11-pad1_ 1.6k +r15 net-_r1-pad1_ net-_q13-pad1_ 130 +r11 net-_q11-pad3_ gndpwr 1k +d5 gndpwr net-_d5-pad2_ 1N4148 +r10 net-_r1-pad1_ net-_q10-pad2_ 4k +q10 net-_q10-pad1_ net-_q10-pad2_ net-_d6-pad2_ Q2N2222 +q12 net-_q12-pad1_ net-_q10-pad1_ net-_q12-pad3_ Q2N2222 +q15 net-_q15-pad1_ net-_q12-pad1_ net-_d8-pad1_ Q2N2222 +d8 net-_d8-pad1_ net-_d8-pad2_ 1N4148 +q16 net-_d8-pad2_ net-_q12-pad3_ gndpwr Q2N2222 +r14 ? net-_q12-pad1_ 1.6k +r16 net-_r1-pad1_ net-_q15-pad1_ 130 +r12 net-_q12-pad3_ gndpwr 1k +d6 gndpwr net-_d6-pad2_ 1N4148 +r1 net-_r1-pad1_ net-_q1-pad2_ 4k +q1 net-_q1-pad1_ net-_q1-pad2_ net-_d1-pad2_ Q2N2222 +q3 net-_q3-pad1_ net-_q1-pad1_ net-_q3-pad3_ Q2N2222 +q5 net-_q5-pad1_ net-_q3-pad1_ net-_d3-pad1_ Q2N2222 +d3 net-_d3-pad1_ net-_d3-pad2_ 1N4148 +q6 net-_d3-pad2_ net-_q3-pad3_ gndpwr Q2N2222 +r5 ? net-_q3-pad1_ 1.6k +r7 net-_r1-pad1_ net-_q5-pad1_ 130 +r3 net-_q3-pad3_ gndpwr 1k +d1 gndpwr net-_d1-pad2_ 1N4148 +r2 net-_r1-pad1_ net-_q2-pad2_ 4k +q2 net-_q2-pad1_ net-_q2-pad2_ net-_d2-pad2_ Q2N2222 +q4 net-_q4-pad1_ net-_q2-pad1_ net-_q4-pad3_ Q2N2222 +q7 net-_q7-pad1_ net-_q4-pad1_ net-_d4-pad1_ Q2N2222 +d4 net-_d4-pad1_ net-_d4-pad2_ 1N4148 +q8 net-_d4-pad2_ net-_q4-pad3_ gndpwr Q2N2222 +r6 ? net-_q4-pad1_ 1.6k +r8 net-_r1-pad1_ net-_q7-pad1_ 130 +r4 net-_q4-pad3_ gndpwr 1k +d2 gndpwr net-_d2-pad2_ 1N4148 +* Control Statements + +.ends SN7404_Subcircuit \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit_Previous_Values.xml b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit_Previous_Values.xml new file mode 100644 index 000000000..140b1ef3a --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/SN7404_Subcircuit_Previous_Values.xml @@ -0,0 +1 @@ +C:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libtruefalsefalseHzHz0Volts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or Amperessecsecsec \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7404/analysis b/library/SubcircuitLibrary/SN7404/analysis new file mode 100644 index 000000000..ebd5c0a94 --- /dev/null +++ b/library/SubcircuitLibrary/SN7404/analysis @@ -0,0 +1 @@ +.tran 0e-00 0e-00 0e-00 \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7408/D.lib b/library/SubcircuitLibrary/SN7408/D.lib new file mode 100644 index 000000000..f53bf3e03 --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/D.lib @@ -0,0 +1,2 @@ +.model 1N4148 D(is=2.495E-09 rs=4.755E-01 n=1.679E+00 tt=3.030E-09 cjo=1.700E-12 vj=1 m=1.959E-01 bv=1.000E+02 ibv=1.000E-04) + diff --git a/library/SubcircuitLibrary/SN7408/NPN.lib b/library/SubcircuitLibrary/SN7408/NPN.lib new file mode 100644 index 000000000..be5f3073a --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/NPN.lib @@ -0,0 +1,4 @@ +.model Q2N2222 NPN( Is=14.34f Xti=3 Eg=1.11 Vaf=74.03 Bf=400 Ne=1.307 ++ Ise=14.34f Ikf=0.2847 Xtb=1.5 Br=6.092 Nc=2 Isc=0 Ikr=0 Rc=1 Cjc=7.306p ++ Mjc=0.3416 Vjc=0.75 Fc=0.5 Cje=22.01p Mje=0.377 Vje=0.75 Tr=46.91n Tf=411.1p ++ Itf=0.6 Vtf=1.7 Xtf=3 Rb=10) diff --git a/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit-cache.lib b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit-cache.lib new file mode 100644 index 000000000..0f688db8a --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit-cache.lib @@ -0,0 +1,126 @@ +EESchema-LIBRARY Version 2.3 +#encoding utf-8 +# +# GNDPWR +# +DEF GNDPWR #PWR 0 0 Y Y 1 F P +F0 "#PWR" 0 -200 50 H I C CNN +F1 "GNDPWR" 0 -130 50 H V C CNN +F2 "" 0 -50 50 H I C CNN +F3 "" 0 -50 50 H I C CNN +DRAW +P 2 0 1 0 0 -50 0 0 N +P 3 0 1 8 -40 -50 -50 -80 -50 -80 N +P 3 0 1 8 -20 -50 -30 -80 -30 -80 N +P 3 0 1 8 0 -50 -10 -80 -10 -80 N +P 3 0 1 8 20 -50 10 -80 10 -80 N +P 3 0 1 8 40 -50 -40 -50 -40 -50 N +P 4 0 1 8 40 -50 30 -80 30 -80 30 -80 N +X GNDPWR 1 0 0 0 U 50 50 1 1 W N +ENDDRAW +ENDDEF +# +# PORT +# +DEF PORT U 0 40 Y Y 26 F N +F0 "U" 50 100 30 H V C CNN +F1 "PORT" 0 0 30 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +A 325 225 285 -1421 -1278 0 1 0 N 100 50 150 0 +A 376 -275 356 1294 1408 0 1 0 N 150 0 100 -50 +S -100 50 100 -50 0 1 0 N +X ~ 1 250 0 100 L 30 30 1 1 B +X ~ 2 250 0 100 L 30 30 2 1 B +X ~ 3 250 0 100 L 30 30 3 1 B +X ~ 4 250 0 100 L 30 30 4 1 B +X ~ 5 250 0 100 L 30 30 5 1 B +X ~ 6 250 0 100 L 30 30 6 1 B +X ~ 7 250 0 100 L 30 30 7 1 B +X ~ 8 250 0 100 L 30 30 8 1 B +X ~ 9 250 0 100 L 30 30 9 1 B +X ~ 10 250 0 100 L 30 30 10 1 B +X ~ 11 250 0 100 L 30 30 11 1 B +X ~ 12 250 0 100 L 30 30 12 1 B +X ~ 13 250 0 100 L 30 30 13 1 B +X ~ 14 250 0 100 L 30 30 14 1 B +X ~ 15 250 0 100 L 30 30 15 1 B +X ~ 16 250 0 100 L 30 30 16 1 B +X ~ 17 250 0 100 L 30 30 17 1 B +X ~ 18 250 0 100 L 30 30 18 1 B +X ~ 19 250 0 100 L 30 30 19 1 B +X ~ 20 250 0 100 L 30 30 20 1 B +X ~ 21 250 0 100 L 30 30 21 1 B +X ~ 22 250 0 100 L 30 30 22 1 B +X ~ 23 250 0 100 L 30 30 23 1 B +X ~ 24 250 0 100 L 30 30 24 1 B +X ~ 25 250 0 100 L 30 30 25 1 B +X ~ 26 250 0 100 L 30 30 26 1 B +ENDDRAW +ENDDEF +# +# eSim_Diode +# +DEF eSim_Diode D 0 40 N N 1 F N +F0 "D" 0 100 50 H V C CNN +F1 "eSim_Diode" 0 -100 50 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + TO-???* + *SingleDiode + *_Diode_* + *SingleDiode* + D_* +$ENDFPLIST +DRAW +T 0 -100 50 60 0 0 0 A Normal 0 C C +T 0 100 50 60 0 0 0 K Normal 0 C C +P 2 0 1 6 50 50 50 -50 N +P 3 0 1 0 -50 50 50 0 -50 -50 F +X A 1 -150 0 100 R 40 40 1 1 P +X K 2 150 0 100 L 40 40 1 1 P +ENDDRAW +ENDDEF +# +# eSim_NPN +# +DEF eSim_NPN Q 0 0 Y N 1 F N +F0 "Q" -100 50 50 H V R CNN +F1 "eSim_NPN" -50 150 50 H V R CNN +F2 "" 200 100 29 H V C CNN +F3 "" 0 0 60 H V C CNN +ALIAS BC547 Q2N2222 +DRAW +C 50 0 111 0 1 10 N +P 2 0 1 0 25 25 100 100 N +P 3 0 1 0 25 -25 100 -100 100 -100 N +P 3 0 1 20 25 75 25 -75 25 -75 N +P 5 0 1 0 50 -70 70 -50 90 -90 50 -70 50 -70 F +X C 1 100 200 100 D 50 50 1 1 P +X B 2 -200 0 225 R 50 50 1 1 P +X E 3 100 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_R +# +DEF eSim_R R 0 0 N Y 1 F N +F0 "R" 50 130 50 H V C CNN +F1 "eSim_R" 50 -50 50 H V C CNN +F2 "" 50 -20 30 H V C CNN +F3 "" 50 50 30 V V C CNN +ALIAS resistor +$FPLIST + R_* + Resistor_* +$ENDFPLIST +DRAW +S 150 10 -50 90 0 1 10 N +X ~ 1 -100 50 50 R 60 60 1 1 P +X ~ 2 200 50 50 L 60 60 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.cir b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.cir new file mode 100644 index 000000000..79fe8ae1e --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.cir @@ -0,0 +1,79 @@ +* C:\FOSSEE\eSim\library\SubcircuitLibrary\SN7408_Subcircuit\SN7408_Subcircuit.cir + +* EESchema Netlist Version 1.1 (Spice format) creation date: 02/09/25 22:43:20 + +* To exclude a component from the Spice Netlist add [Spice_Netlist_Enabled] user FIELD set to: N +* To reorder the component spice node sequence add [Spice_Node_Sequence] user FIELD and define sequence: 2,1,0 + +* Sheet Name: / +R1 Net-_R1-Pad1_ Net-_Q1-Pad2_ 4k +R3 Net-_R1-Pad1_ Net-_D5-Pad1_ 2k +R7 Net-_R1-Pad1_ Net-_Q11-Pad2_ 1.6k +R11 Net-_R1-Pad1_ Net-_Q11-Pad1_ 130 +Q1 Net-_Q1-Pad1_ Net-_Q1-Pad2_ Net-_D1-Pad2_ eSim_NPN +Q2 Net-_Q1-Pad1_ Net-_Q1-Pad2_ Net-_D3-Pad2_ eSim_NPN +D1 GNDPWR Net-_D1-Pad2_ eSim_Diode +D3 GNDPWR Net-_D3-Pad2_ eSim_Diode +Q5 Net-_D5-Pad1_ Net-_Q1-Pad1_ Net-_Q5-Pad3_ eSim_NPN +Q7 Net-_D5-Pad2_ Net-_Q5-Pad3_ GNDPWR eSim_NPN +D5 Net-_D5-Pad1_ Net-_D5-Pad2_ eSim_Diode +Q9 Net-_Q11-Pad2_ Net-_D5-Pad2_ Net-_Q12-Pad2_ eSim_NPN +Q11 Net-_Q11-Pad1_ Net-_Q11-Pad2_ Net-_D7-Pad1_ eSim_NPN +Q12 Net-_D7-Pad2_ Net-_Q12-Pad2_ GNDPWR eSim_NPN +D7 Net-_D7-Pad1_ Net-_D7-Pad2_ eSim_Diode +R4 Net-_Q5-Pad3_ GNDPWR 800 +R8 Net-_Q12-Pad2_ GNDPWR 1k +U1 Net-_D1-Pad2_ Net-_D3-Pad2_ Net-_D2-Pad2_ Net-_D4-Pad2_ Net-_D7-Pad2_ Net-_D8-Pad2_ Net-_R1-Pad1_ GNDPWR Net-_D9-Pad2_ Net-_D10-Pad2_ Net-_D15-Pad2_ Net-_D13-Pad2_ Net-_D16-Pad2_ Net-_D14-Pad2_ PORT +R2 Net-_R1-Pad1_ Net-_Q3-Pad2_ 4k +R5 Net-_R1-Pad1_ Net-_D6-Pad1_ 2k +R9 Net-_R1-Pad1_ Net-_Q10-Pad1_ 1.6k +R12 Net-_R1-Pad1_ Net-_Q13-Pad1_ 130 +Q3 Net-_Q3-Pad1_ Net-_Q3-Pad2_ Net-_D2-Pad2_ eSim_NPN +Q4 Net-_Q3-Pad1_ Net-_Q3-Pad2_ Net-_D4-Pad2_ eSim_NPN +D2 GNDPWR Net-_D2-Pad2_ eSim_Diode +D4 GNDPWR Net-_D4-Pad2_ eSim_Diode +Q6 Net-_D6-Pad1_ Net-_Q3-Pad1_ Net-_Q6-Pad3_ eSim_NPN +Q8 Net-_D6-Pad2_ Net-_Q6-Pad3_ GNDPWR eSim_NPN +D6 Net-_D6-Pad1_ Net-_D6-Pad2_ eSim_Diode +Q10 Net-_Q10-Pad1_ Net-_D6-Pad2_ Net-_Q10-Pad3_ eSim_NPN +Q13 Net-_Q13-Pad1_ Net-_Q10-Pad1_ Net-_D8-Pad1_ eSim_NPN +Q14 Net-_D8-Pad2_ Net-_Q10-Pad3_ GNDPWR eSim_NPN +D8 Net-_D8-Pad1_ Net-_D8-Pad2_ eSim_Diode +R6 Net-_Q6-Pad3_ GNDPWR 800 +R10 Net-_Q10-Pad3_ GNDPWR 1k +R23 Net-_R1-Pad1_ Net-_Q25-Pad2_ 4k +R19 Net-_R1-Pad1_ Net-_D11-Pad1_ 2k +R15 Net-_R1-Pad1_ Net-_Q15-Pad2_ 1.6k +R13 Net-_R1-Pad1_ Net-_Q15-Pad1_ 130 +Q25 Net-_Q23-Pad2_ Net-_Q25-Pad2_ Net-_D15-Pad2_ eSim_NPN +Q26 Net-_Q23-Pad2_ Net-_Q25-Pad2_ Net-_D13-Pad2_ eSim_NPN +D15 GNDPWR Net-_D15-Pad2_ eSim_Diode +D13 GNDPWR Net-_D13-Pad2_ eSim_Diode +Q23 Net-_D11-Pad1_ Net-_Q23-Pad2_ Net-_Q21-Pad2_ eSim_NPN +Q21 Net-_D11-Pad2_ Net-_Q21-Pad2_ GNDPWR eSim_NPN +D11 Net-_D11-Pad1_ Net-_D11-Pad2_ eSim_Diode +Q19 Net-_Q15-Pad2_ Net-_D11-Pad2_ Net-_Q16-Pad2_ eSim_NPN +Q15 Net-_Q15-Pad1_ Net-_Q15-Pad2_ Net-_D9-Pad1_ eSim_NPN +Q16 Net-_D9-Pad2_ Net-_Q16-Pad2_ GNDPWR eSim_NPN +D9 Net-_D9-Pad1_ Net-_D9-Pad2_ eSim_Diode +R20 Net-_Q21-Pad2_ GNDPWR 800 +R16 Net-_Q16-Pad2_ GNDPWR 1k +R24 Net-_R1-Pad1_ Net-_Q27-Pad2_ 4k +R21 Net-_R1-Pad1_ Net-_D12-Pad1_ 2k +R17 Net-_R1-Pad1_ Net-_Q17-Pad2_ 1.6k +R14 Net-_R1-Pad1_ Net-_Q17-Pad1_ 130 +Q27 Net-_Q24-Pad2_ Net-_Q27-Pad2_ Net-_D16-Pad2_ eSim_NPN +Q28 Net-_Q24-Pad2_ Net-_Q27-Pad2_ Net-_D14-Pad2_ eSim_NPN +D16 GNDPWR Net-_D16-Pad2_ eSim_Diode +D14 GNDPWR Net-_D14-Pad2_ eSim_Diode +Q24 Net-_D12-Pad1_ Net-_Q24-Pad2_ Net-_Q22-Pad2_ eSim_NPN +Q22 Net-_D12-Pad2_ Net-_Q22-Pad2_ GNDPWR eSim_NPN +D12 Net-_D12-Pad1_ Net-_D12-Pad2_ eSim_Diode +Q20 Net-_Q17-Pad2_ Net-_D12-Pad2_ Net-_Q18-Pad2_ eSim_NPN +Q17 Net-_Q17-Pad1_ Net-_Q17-Pad2_ Net-_D10-Pad1_ eSim_NPN +Q18 Net-_D10-Pad2_ Net-_Q18-Pad2_ GNDPWR eSim_NPN +D10 Net-_D10-Pad1_ Net-_D10-Pad2_ eSim_Diode +R22 Net-_Q22-Pad2_ GNDPWR 800 +R18 Net-_Q18-Pad2_ GNDPWR 1k + +.end diff --git a/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.cir.out b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.cir.out new file mode 100644 index 000000000..d25a0635e --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.cir.out @@ -0,0 +1,82 @@ +* c:\fossee\esim\library\subcircuitlibrary\sn7408_subcircuit\sn7408_subcircuit.cir + +.include NPN.lib +.include D.lib +r1 net-_r1-pad1_ net-_q1-pad2_ 4k +r3 net-_r1-pad1_ net-_d5-pad1_ 2k +r7 net-_r1-pad1_ net-_q11-pad2_ 1.6k +r11 net-_r1-pad1_ net-_q11-pad1_ 130 +q1 net-_q1-pad1_ net-_q1-pad2_ net-_d1-pad2_ Q2N2222 +q2 net-_q1-pad1_ net-_q1-pad2_ net-_d3-pad2_ Q2N2222 +d1 gndpwr net-_d1-pad2_ 1N4148 +d3 gndpwr net-_d3-pad2_ 1N4148 +q5 net-_d5-pad1_ net-_q1-pad1_ net-_q5-pad3_ Q2N2222 +q7 net-_d5-pad2_ net-_q5-pad3_ gndpwr Q2N2222 +d5 net-_d5-pad1_ net-_d5-pad2_ 1N4148 +q9 net-_q11-pad2_ net-_d5-pad2_ net-_q12-pad2_ Q2N2222 +q11 net-_q11-pad1_ net-_q11-pad2_ net-_d7-pad1_ Q2N2222 +q12 net-_d7-pad2_ net-_q12-pad2_ gndpwr Q2N2222 +d7 net-_d7-pad1_ net-_d7-pad2_ 1N4148 +r4 net-_q5-pad3_ gndpwr 800 +r8 net-_q12-pad2_ gndpwr 1k +* u1 net-_d1-pad2_ net-_d3-pad2_ net-_d2-pad2_ net-_d4-pad2_ net-_d7-pad2_ net-_d8-pad2_ net-_r1-pad1_ gndpwr net-_d9-pad2_ net-_d10-pad2_ net-_d15-pad2_ net-_d13-pad2_ net-_d16-pad2_ net-_d14-pad2_ port +r2 net-_r1-pad1_ net-_q3-pad2_ 4k +r5 net-_r1-pad1_ net-_d6-pad1_ 2k +r9 net-_r1-pad1_ net-_q10-pad1_ 1.6k +r12 net-_r1-pad1_ net-_q13-pad1_ 130 +q3 net-_q3-pad1_ net-_q3-pad2_ net-_d2-pad2_ Q2N2222 +q4 net-_q3-pad1_ net-_q3-pad2_ net-_d4-pad2_ Q2N2222 +d2 gndpwr net-_d2-pad2_ 1N4148 +d4 gndpwr net-_d4-pad2_ 1N4148 +q6 net-_d6-pad1_ net-_q3-pad1_ net-_q6-pad3_ Q2N2222 +q8 net-_d6-pad2_ net-_q6-pad3_ gndpwr Q2N2222 +d6 net-_d6-pad1_ net-_d6-pad2_ 1N4148 +q10 net-_q10-pad1_ net-_d6-pad2_ net-_q10-pad3_ Q2N2222 +q13 net-_q13-pad1_ net-_q10-pad1_ net-_d8-pad1_ Q2N2222 +q14 net-_d8-pad2_ net-_q10-pad3_ gndpwr Q2N2222 +d8 net-_d8-pad1_ net-_d8-pad2_ 1N4148 +r6 net-_q6-pad3_ gndpwr 800 +r10 net-_q10-pad3_ gndpwr 1k +r23 net-_r1-pad1_ net-_q25-pad2_ 4k +r19 net-_r1-pad1_ net-_d11-pad1_ 2k +r15 net-_r1-pad1_ net-_q15-pad2_ 1.6k +r13 net-_r1-pad1_ net-_q15-pad1_ 130 +q25 net-_q23-pad2_ net-_q25-pad2_ net-_d15-pad2_ Q2N2222 +q26 net-_q23-pad2_ net-_q25-pad2_ net-_d13-pad2_ Q2N2222 +d15 gndpwr net-_d15-pad2_ 1N4148 +d13 gndpwr net-_d13-pad2_ 1N4148 +q23 net-_d11-pad1_ net-_q23-pad2_ net-_q21-pad2_ Q2N2222 +q21 net-_d11-pad2_ net-_q21-pad2_ gndpwr Q2N2222 +d11 net-_d11-pad1_ net-_d11-pad2_ 1N4148 +q19 net-_q15-pad2_ net-_d11-pad2_ net-_q16-pad2_ Q2N2222 +q15 net-_q15-pad1_ net-_q15-pad2_ net-_d9-pad1_ Q2N2222 +q16 net-_d9-pad2_ net-_q16-pad2_ gndpwr Q2N2222 +d9 net-_d9-pad1_ net-_d9-pad2_ 1N4148 +r20 net-_q21-pad2_ gndpwr 800 +r16 net-_q16-pad2_ gndpwr 1k +r24 net-_r1-pad1_ net-_q27-pad2_ 4k +r21 net-_r1-pad1_ net-_d12-pad1_ 2k +r17 net-_r1-pad1_ net-_q17-pad2_ 1.6k +r14 net-_r1-pad1_ net-_q17-pad1_ 130 +q27 net-_q24-pad2_ net-_q27-pad2_ net-_d16-pad2_ Q2N2222 +q28 net-_q24-pad2_ net-_q27-pad2_ net-_d14-pad2_ Q2N2222 +d16 gndpwr net-_d16-pad2_ 1N4148 +d14 gndpwr net-_d14-pad2_ 1N4148 +q24 net-_d12-pad1_ net-_q24-pad2_ net-_q22-pad2_ Q2N2222 +q22 net-_d12-pad2_ net-_q22-pad2_ gndpwr Q2N2222 +d12 net-_d12-pad1_ net-_d12-pad2_ 1N4148 +q20 net-_q17-pad2_ net-_d12-pad2_ net-_q18-pad2_ Q2N2222 +q17 net-_q17-pad1_ net-_q17-pad2_ net-_d10-pad1_ Q2N2222 +q18 net-_d10-pad2_ net-_q18-pad2_ gndpwr Q2N2222 +d10 net-_d10-pad1_ net-_d10-pad2_ 1N4148 +r22 net-_q22-pad2_ gndpwr 800 +r18 net-_q18-pad2_ gndpwr 1k +.tran 0e-00 0e-00 0e-00 + +* Control Statements +.control +run +print allv > plot_data_v.txt +print alli > plot_data_i.txt +.endc +.end diff --git a/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.pro b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.pro new file mode 100644 index 000000000..e27a398be --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.pro @@ -0,0 +1,73 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] +LibName1=adc-dac +LibName2=memory +LibName3=xilinx +LibName4=microcontrollers +LibName5=dsp +LibName6=microchip +LibName7=analog_switches +LibName8=motorola +LibName9=texas +LibName10=intel +LibName11=audio +LibName12=interface +LibName13=digital-audio +LibName14=philips +LibName15=display +LibName16=cypress +LibName17=siliconi +LibName18=opto +LibName19=atmel +LibName20=contrib +LibName21=power +LibName22=eSim_Plot +LibName23=transistors +LibName24=conn +LibName25=eSim_User +LibName26=regul +LibName27=74xx +LibName28=cmos4000 +LibName29=eSim_Analog +LibName30=eSim_Devices +LibName31=eSim_Digital +LibName32=eSim_Hybrid +LibName33=eSim_Miscellaneous +LibName34=eSim_Power +LibName35=eSim_Sources +LibName36=eSim_Subckt +LibName37=eSim_Nghdl +LibName38=eSim_Ngveri +LibName39=eSim_SKY130 +LibName40=eSim_SKY130_Subckts diff --git a/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.sch b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.sch new file mode 100644 index 000000000..123da2b02 --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.sch @@ -0,0 +1,1367 @@ +EESchema Schematic File Version 2 +LIBS:adc-dac +LIBS:memory +LIBS:xilinx +LIBS:microcontrollers +LIBS:dsp +LIBS:microchip +LIBS:analog_switches +LIBS:motorola +LIBS:texas +LIBS:intel +LIBS:audio +LIBS:interface +LIBS:digital-audio +LIBS:philips +LIBS:display +LIBS:cypress +LIBS:siliconi +LIBS:opto +LIBS:atmel +LIBS:contrib +LIBS:power +LIBS:eSim_Plot +LIBS:transistors +LIBS:conn +LIBS:eSim_User +LIBS:regul +LIBS:74xx +LIBS:cmos4000 +LIBS:eSim_Analog +LIBS:eSim_Devices +LIBS:eSim_Digital +LIBS:eSim_Hybrid +LIBS:eSim_Miscellaneous +LIBS:eSim_Power +LIBS:eSim_Sources +LIBS:eSim_Subckt +LIBS:eSim_Nghdl +LIBS:eSim_Ngveri +LIBS:eSim_SKY130 +LIBS:eSim_SKY130_Subckts +LIBS:SN7408_Subcircuit-cache +EELAYER 25 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L resistor R1 +U 1 1 678204C6 +P 2050 1270 +F 0 "R1" H 2100 1400 50 0000 C CNN +F 1 "4k" H 2100 1220 50 0000 C CNN +F 2 "" H 2100 1250 30 0000 C CNN +F 3 "" V 2100 1320 30 0000 C CNN + 1 2050 1270 + 0 1 1 0 +$EndComp +$Comp +L resistor R3 +U 1 1 678205B9 +P 2680 1270 +F 0 "R3" H 2730 1400 50 0000 C CNN +F 1 "2k" H 2730 1220 50 0000 C CNN +F 2 "" H 2730 1250 30 0000 C CNN +F 3 "" V 2730 1320 30 0000 C CNN + 1 2680 1270 + 0 1 1 0 +$EndComp +$Comp +L resistor R7 +U 1 1 67820653 +P 3550 1270 +F 0 "R7" H 3600 1400 50 0000 C CNN +F 1 "1.6k" H 3600 1220 50 0000 C CNN +F 2 "" H 3600 1250 30 0000 C CNN +F 3 "" V 3600 1320 30 0000 C CNN + 1 3550 1270 + 0 1 1 0 +$EndComp +$Comp +L resistor R11 +U 1 1 678206FC +P 3920 1270 +F 0 "R11" H 3970 1400 50 0000 C CNN +F 1 "130" H 3970 1220 50 0000 C CNN +F 2 "" H 3970 1250 30 0000 C CNN +F 3 "" V 3970 1320 30 0000 C CNN + 1 3920 1270 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q1 +U 1 1 678207BE +P 2100 2070 +F 0 "Q1" H 2000 2120 50 0000 R CNN +F 1 "eSim_NPN" H 2050 2220 50 0000 R CNN +F 2 "" H 2300 2170 29 0000 C CNN +F 3 "" H 2100 2070 60 0000 C CNN + 1 2100 2070 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q2 +U 1 1 67820877 +P 2100 2480 +F 0 "Q2" H 2000 2530 50 0000 R CNN +F 1 "eSim_NPN" H 2050 2630 50 0000 R CNN +F 2 "" H 2300 2580 29 0000 C CNN +F 3 "" H 2100 2480 60 0000 C CNN + 1 2100 2480 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D1 +U 1 1 67820907 +P 1700 2980 +F 0 "D1" H 1700 3080 50 0000 C CNN +F 1 "eSim_Diode" H 1700 2880 50 0000 C CNN +F 2 "" H 1700 2980 60 0000 C CNN +F 3 "" H 1700 2980 60 0000 C CNN + 1 1700 2980 + 0 -1 -1 0 +$EndComp +$Comp +L eSim_Diode D3 +U 1 1 67820AB9 +P 1900 3330 +F 0 "D3" H 1900 3430 50 0000 C CNN +F 1 "eSim_Diode" H 1900 3230 50 0000 C CNN +F 2 "" H 1900 3330 60 0000 C CNN +F 3 "" H 1900 3330 60 0000 C CNN + 1 1900 3330 + 0 -1 -1 0 +$EndComp +Wire Wire Line + 2100 2280 2370 2280 +Wire Wire Line + 2370 2280 2370 1870 +Wire Wire Line + 2370 1870 2100 1870 +Wire Wire Line + 2300 2170 2300 2580 +Wire Wire Line + 2300 2580 2430 2580 +$Comp +L eSim_NPN Q5 +U 1 1 67826BC4 +P 2630 2580 +F 0 "Q5" H 2530 2630 50 0000 R CNN +F 1 "eSim_NPN" H 2580 2730 50 0000 R CNN +F 2 "" H 2830 2680 29 0000 C CNN +F 3 "" H 2630 2580 60 0000 C CNN + 1 2630 2580 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q7 +U 1 1 67826CF0 +P 3090 2780 +F 0 "Q7" H 2990 2830 50 0000 R CNN +F 1 "eSim_NPN" H 3040 2930 50 0000 R CNN +F 2 "" H 3290 2880 29 0000 C CNN +F 3 "" H 3090 2780 60 0000 C CNN + 1 3090 2780 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D5 +U 1 1 67826D94 +P 2970 2380 +F 0 "D5" H 2970 2480 50 0000 C CNN +F 1 "eSim_Diode" H 2970 2280 50 0000 C CNN +F 2 "" H 2970 2380 60 0000 C CNN +F 3 "" H 2970 2380 60 0000 C CNN + 1 2970 2380 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q9 +U 1 1 67826E95 +P 3500 2380 +F 0 "Q9" H 3400 2430 50 0000 R CNN +F 1 "eSim_NPN" H 3450 2530 50 0000 R CNN +F 2 "" H 3700 2480 29 0000 C CNN +F 3 "" H 3500 2380 60 0000 C CNN + 1 3500 2380 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q11 +U 1 1 67826F1F +P 3870 2120 +F 0 "Q11" H 3770 2170 50 0000 R CNN +F 1 "eSim_NPN" H 3820 2270 50 0000 R CNN +F 2 "" H 4070 2220 29 0000 C CNN +F 3 "" H 3870 2120 60 0000 C CNN + 1 3870 2120 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q12 +U 1 1 67827003 +P 3870 3050 +F 0 "Q12" H 3770 3100 50 0000 R CNN +F 1 "eSim_NPN" H 3820 3200 50 0000 R CNN +F 2 "" H 4070 3150 29 0000 C CNN +F 3 "" H 3870 3050 60 0000 C CNN + 1 3870 3050 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D7 +U 1 1 6782721B +P 3970 2580 +F 0 "D7" H 3970 2680 50 0000 C CNN +F 1 "eSim_Diode" H 3970 2480 50 0000 C CNN +F 2 "" H 3970 2580 60 0000 C CNN +F 3 "" H 3970 2580 60 0000 C CNN + 1 3970 2580 + 0 1 1 0 +$EndComp +$Comp +L resistor R4 +U 1 1 67827B70 +P 2680 3280 +F 0 "R4" H 2730 3410 50 0000 C CNN +F 1 "800" H 2730 3230 50 0000 C CNN +F 2 "" H 2730 3260 30 0000 C CNN +F 3 "" V 2730 3330 30 0000 C CNN + 1 2680 3280 + 0 1 1 0 +$EndComp +$Comp +L resistor R8 +U 1 1 67827E34 +P 3550 3280 +F 0 "R8" H 3600 3410 50 0000 C CNN +F 1 "1k" H 3600 3230 50 0000 C CNN +F 2 "" H 3600 3260 30 0000 C CNN +F 3 "" V 3600 3330 30 0000 C CNN + 1 3550 3280 + 0 1 1 0 +$EndComp +Wire Wire Line + 2820 2380 2730 2380 +Wire Wire Line + 2730 2380 2730 1470 +Wire Wire Line + 2100 1870 2100 1470 +Wire Wire Line + 3670 2120 3600 2120 +Wire Wire Line + 3600 1470 3600 2180 +Connection ~ 3600 2120 +Wire Wire Line + 3970 1920 3970 1470 +Wire Wire Line + 3300 2380 3120 2380 +Wire Wire Line + 3190 2580 3190 2380 +Connection ~ 3190 2380 +Wire Wire Line + 2890 2780 2730 2780 +Wire Wire Line + 2730 2780 2730 3180 +Wire Wire Line + 1900 2580 1900 3180 +Wire Wire Line + 1900 2170 1700 2170 +Wire Wire Line + 1700 2170 1700 2830 +Wire Wire Line + 3600 3050 3670 3050 +Wire Wire Line + 3600 2580 3600 3180 +Connection ~ 3600 3050 +Wire Wire Line + 3970 2730 3970 2850 +Wire Wire Line + 3970 2320 3970 2430 +Wire Wire Line + 3970 3250 3970 3560 +Wire Wire Line + 1700 3560 9420 3560 +Wire Wire Line + 1700 3560 1700 3130 +Wire Wire Line + 1900 3480 1900 3560 +Connection ~ 1900 3560 +Wire Wire Line + 2730 3480 2730 3560 +Connection ~ 2730 3560 +Wire Wire Line + 3600 3480 3600 3560 +Connection ~ 3600 3560 +Wire Wire Line + 3190 2980 3190 3560 +Connection ~ 3190 3560 +Wire Wire Line + 3970 1170 3970 1070 +Wire Wire Line + 2100 1070 9020 1070 +Wire Wire Line + 2100 1070 2100 1170 +Wire Wire Line + 2730 1170 2730 1070 +Connection ~ 2730 1070 +Wire Wire Line + 3600 1170 3600 1070 +Connection ~ 3600 1070 +$Comp +L PORT U1 +U 1 1 6782A31C +P 1330 2460 +F 0 "U1" H 1380 2560 30 0000 C CNN +F 1 "PORT" H 1330 2460 30 0000 C CNN +F 2 "" H 1330 2460 60 0000 C CNN +F 3 "" H 1330 2460 60 0000 C CNN + 1 1330 2460 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 2 1 6782A41F +P 1330 2750 +F 0 "U1" H 1380 2850 30 0000 C CNN +F 1 "PORT" H 1330 2750 30 0000 C CNN +F 2 "" H 1330 2750 60 0000 C CNN +F 3 "" H 1330 2750 60 0000 C CNN + 2 1330 2750 + 1 0 0 -1 +$EndComp +Wire Wire Line + 1580 2750 1900 2750 +Connection ~ 1900 2750 +Wire Wire Line + 1580 2460 1700 2460 +Connection ~ 1700 2460 +$Comp +L PORT U1 +U 5 1 6782A738 +P 4320 2790 +F 0 "U1" H 4370 2890 30 0000 C CNN +F 1 "PORT" H 4320 2790 30 0000 C CNN +F 2 "" H 4320 2790 60 0000 C CNN +F 3 "" H 4320 2790 60 0000 C CNN + 5 4320 2790 + -1 0 0 1 +$EndComp +Wire Wire Line + 4070 2790 3970 2790 +Connection ~ 3970 2790 +$Comp +L resistor R2 +U 1 1 6782CE49 +P 2050 4380 +F 0 "R2" H 2100 4510 50 0000 C CNN +F 1 "4k" H 2100 4330 50 0000 C CNN +F 2 "" H 2100 4360 30 0000 C CNN +F 3 "" V 2100 4430 30 0000 C CNN + 1 2050 4380 + 0 1 1 0 +$EndComp +$Comp +L resistor R5 +U 1 1 6782CE4F +P 2680 4380 +F 0 "R5" H 2730 4510 50 0000 C CNN +F 1 "2k" H 2730 4330 50 0000 C CNN +F 2 "" H 2730 4360 30 0000 C CNN +F 3 "" V 2730 4430 30 0000 C CNN + 1 2680 4380 + 0 1 1 0 +$EndComp +$Comp +L resistor R9 +U 1 1 6782CE55 +P 3550 4380 +F 0 "R9" H 3600 4510 50 0000 C CNN +F 1 "1.6k" H 3600 4330 50 0000 C CNN +F 2 "" H 3600 4360 30 0000 C CNN +F 3 "" V 3600 4430 30 0000 C CNN + 1 3550 4380 + 0 1 1 0 +$EndComp +$Comp +L resistor R12 +U 1 1 6782CE5B +P 3920 4380 +F 0 "R12" H 3970 4510 50 0000 C CNN +F 1 "130" H 3970 4330 50 0000 C CNN +F 2 "" H 3970 4360 30 0000 C CNN +F 3 "" V 3970 4430 30 0000 C CNN + 1 3920 4380 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q3 +U 1 1 6782CE61 +P 2100 5180 +F 0 "Q3" H 2000 5230 50 0000 R CNN +F 1 "eSim_NPN" H 2050 5330 50 0000 R CNN +F 2 "" H 2300 5280 29 0000 C CNN +F 3 "" H 2100 5180 60 0000 C CNN + 1 2100 5180 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q4 +U 1 1 6782CE67 +P 2100 5590 +F 0 "Q4" H 2000 5640 50 0000 R CNN +F 1 "eSim_NPN" H 2050 5740 50 0000 R CNN +F 2 "" H 2300 5690 29 0000 C CNN +F 3 "" H 2100 5590 60 0000 C CNN + 1 2100 5590 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D2 +U 1 1 6782CE6D +P 1700 6090 +F 0 "D2" H 1700 6190 50 0000 C CNN +F 1 "eSim_Diode" H 1700 5990 50 0000 C CNN +F 2 "" H 1700 6090 60 0000 C CNN +F 3 "" H 1700 6090 60 0000 C CNN + 1 1700 6090 + 0 -1 -1 0 +$EndComp +$Comp +L eSim_Diode D4 +U 1 1 6782CE73 +P 1900 6440 +F 0 "D4" H 1900 6540 50 0000 C CNN +F 1 "eSim_Diode" H 1900 6340 50 0000 C CNN +F 2 "" H 1900 6440 60 0000 C CNN +F 3 "" H 1900 6440 60 0000 C CNN + 1 1900 6440 + 0 -1 -1 0 +$EndComp +Wire Wire Line + 2100 5390 2370 5390 +Wire Wire Line + 2370 5390 2370 4980 +Wire Wire Line + 2370 4980 2100 4980 +Wire Wire Line + 2300 5280 2300 5690 +Wire Wire Line + 2300 5690 2430 5690 +$Comp +L eSim_NPN Q6 +U 1 1 6782CE7E +P 2630 5690 +F 0 "Q6" H 2530 5740 50 0000 R CNN +F 1 "eSim_NPN" H 2580 5840 50 0000 R CNN +F 2 "" H 2830 5790 29 0000 C CNN +F 3 "" H 2630 5690 60 0000 C CNN + 1 2630 5690 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q8 +U 1 1 6782CE84 +P 3090 5890 +F 0 "Q8" H 2990 5940 50 0000 R CNN +F 1 "eSim_NPN" H 3040 6040 50 0000 R CNN +F 2 "" H 3290 5990 29 0000 C CNN +F 3 "" H 3090 5890 60 0000 C CNN + 1 3090 5890 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D6 +U 1 1 6782CE8A +P 2970 5490 +F 0 "D6" H 2970 5590 50 0000 C CNN +F 1 "eSim_Diode" H 2970 5390 50 0000 C CNN +F 2 "" H 2970 5490 60 0000 C CNN +F 3 "" H 2970 5490 60 0000 C CNN + 1 2970 5490 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q10 +U 1 1 6782CE90 +P 3500 5490 +F 0 "Q10" H 3400 5540 50 0000 R CNN +F 1 "eSim_NPN" H 3450 5640 50 0000 R CNN +F 2 "" H 3700 5590 29 0000 C CNN +F 3 "" H 3500 5490 60 0000 C CNN + 1 3500 5490 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q13 +U 1 1 6782CE96 +P 3870 5230 +F 0 "Q13" H 3770 5280 50 0000 R CNN +F 1 "eSim_NPN" H 3820 5380 50 0000 R CNN +F 2 "" H 4070 5330 29 0000 C CNN +F 3 "" H 3870 5230 60 0000 C CNN + 1 3870 5230 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q14 +U 1 1 6782CE9C +P 3870 6160 +F 0 "Q14" H 3770 6210 50 0000 R CNN +F 1 "eSim_NPN" H 3820 6310 50 0000 R CNN +F 2 "" H 4070 6260 29 0000 C CNN +F 3 "" H 3870 6160 60 0000 C CNN + 1 3870 6160 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D8 +U 1 1 6782CEA2 +P 3970 5690 +F 0 "D8" H 3970 5790 50 0000 C CNN +F 1 "eSim_Diode" H 3970 5590 50 0000 C CNN +F 2 "" H 3970 5690 60 0000 C CNN +F 3 "" H 3970 5690 60 0000 C CNN + 1 3970 5690 + 0 1 1 0 +$EndComp +$Comp +L resistor R6 +U 1 1 6782CEA8 +P 2680 6390 +F 0 "R6" H 2730 6520 50 0000 C CNN +F 1 "800" H 2730 6340 50 0000 C CNN +F 2 "" H 2730 6370 30 0000 C CNN +F 3 "" V 2730 6440 30 0000 C CNN + 1 2680 6390 + 0 1 1 0 +$EndComp +$Comp +L resistor R10 +U 1 1 6782CEAE +P 3550 6390 +F 0 "R10" H 3600 6520 50 0000 C CNN +F 1 "1k" H 3600 6340 50 0000 C CNN +F 2 "" H 3600 6370 30 0000 C CNN +F 3 "" V 3600 6440 30 0000 C CNN + 1 3550 6390 + 0 1 1 0 +$EndComp +Wire Wire Line + 2820 5490 2730 5490 +Wire Wire Line + 2730 5490 2730 4580 +Wire Wire Line + 2100 4980 2100 4580 +Wire Wire Line + 3670 5230 3600 5230 +Wire Wire Line + 3600 4580 3600 5290 +Connection ~ 3600 5230 +Wire Wire Line + 3970 5030 3970 4580 +Wire Wire Line + 3300 5490 3120 5490 +Wire Wire Line + 3190 5690 3190 5490 +Connection ~ 3190 5490 +Wire Wire Line + 2890 5890 2730 5890 +Wire Wire Line + 2730 5890 2730 6290 +Wire Wire Line + 1900 5690 1900 6290 +Wire Wire Line + 1900 5280 1700 5280 +Wire Wire Line + 1700 5280 1700 5940 +Wire Wire Line + 3600 6160 3670 6160 +Wire Wire Line + 3600 5690 3600 6290 +Connection ~ 3600 6160 +Wire Wire Line + 3970 5840 3970 5960 +Wire Wire Line + 3970 5430 3970 5540 +Wire Wire Line + 3970 6360 3970 6670 +Wire Wire Line + 1700 6670 9420 6670 +Wire Wire Line + 1700 6670 1700 6240 +Wire Wire Line + 1900 6590 1900 6670 +Connection ~ 1900 6670 +Wire Wire Line + 2730 6590 2730 6670 +Connection ~ 2730 6670 +Wire Wire Line + 3600 6590 3600 6670 +Connection ~ 3600 6670 +Wire Wire Line + 3190 6090 3190 6670 +Connection ~ 3190 6670 +Wire Wire Line + 3970 4280 3970 4180 +Wire Wire Line + 2100 4180 9020 4180 +Wire Wire Line + 2100 4180 2100 4280 +Wire Wire Line + 2730 4280 2730 4180 +Connection ~ 2730 4180 +Wire Wire Line + 3600 4280 3600 4180 +Connection ~ 3600 4180 +$Comp +L PORT U1 +U 3 1 6782CEDA +P 1330 5570 +F 0 "U1" H 1380 5670 30 0000 C CNN +F 1 "PORT" H 1330 5570 30 0000 C CNN +F 2 "" H 1330 5570 60 0000 C CNN +F 3 "" H 1330 5570 60 0000 C CNN + 3 1330 5570 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 4 1 6782CEE0 +P 1330 5860 +F 0 "U1" H 1380 5960 30 0000 C CNN +F 1 "PORT" H 1330 5860 30 0000 C CNN +F 2 "" H 1330 5860 60 0000 C CNN +F 3 "" H 1330 5860 60 0000 C CNN + 4 1330 5860 + 1 0 0 -1 +$EndComp +Wire Wire Line + 1580 5860 1900 5860 +Connection ~ 1900 5860 +Wire Wire Line + 1580 5570 1700 5570 +Connection ~ 1700 5570 +$Comp +L PORT U1 +U 6 1 6782CEEA +P 4320 5900 +F 0 "U1" H 4370 6000 30 0000 C CNN +F 1 "PORT" H 4320 5900 30 0000 C CNN +F 2 "" H 4320 5900 60 0000 C CNN +F 3 "" H 4320 5900 60 0000 C CNN + 6 4320 5900 + -1 0 0 1 +$EndComp +Wire Wire Line + 4070 5900 3970 5900 +Connection ~ 3970 5900 +$Comp +L resistor R23 +U 1 1 6782F80A +P 9070 1270 +F 0 "R23" H 9120 1400 50 0000 C CNN +F 1 "4k" H 9120 1220 50 0000 C CNN +F 2 "" H 9120 1250 30 0000 C CNN +F 3 "" V 9120 1320 30 0000 C CNN + 1 9070 1270 + 0 -1 1 0 +$EndComp +$Comp +L resistor R19 +U 1 1 6782F810 +P 8440 1270 +F 0 "R19" H 8490 1400 50 0000 C CNN +F 1 "2k" H 8490 1220 50 0000 C CNN +F 2 "" H 8490 1250 30 0000 C CNN +F 3 "" V 8490 1320 30 0000 C CNN + 1 8440 1270 + 0 -1 1 0 +$EndComp +$Comp +L resistor R15 +U 1 1 6782F816 +P 7570 1270 +F 0 "R15" H 7620 1400 50 0000 C CNN +F 1 "1.6k" H 7620 1220 50 0000 C CNN +F 2 "" H 7620 1250 30 0000 C CNN +F 3 "" V 7620 1320 30 0000 C CNN + 1 7570 1270 + 0 -1 1 0 +$EndComp +$Comp +L resistor R13 +U 1 1 6782F81C +P 7200 1270 +F 0 "R13" H 7250 1400 50 0000 C CNN +F 1 "130" H 7250 1220 50 0000 C CNN +F 2 "" H 7250 1250 30 0000 C CNN +F 3 "" V 7250 1320 30 0000 C CNN + 1 7200 1270 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q25 +U 1 1 6782F822 +P 9020 2070 +F 0 "Q25" H 8920 2120 50 0000 R CNN +F 1 "eSim_NPN" H 8970 2220 50 0000 R CNN +F 2 "" H 9220 2170 29 0000 C CNN +F 3 "" H 9020 2070 60 0000 C CNN + 1 9020 2070 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q26 +U 1 1 6782F828 +P 9020 2480 +F 0 "Q26" H 8920 2530 50 0000 R CNN +F 1 "eSim_NPN" H 8970 2630 50 0000 R CNN +F 2 "" H 9220 2580 29 0000 C CNN +F 3 "" H 9020 2480 60 0000 C CNN + 1 9020 2480 + 0 -1 1 0 +$EndComp +$Comp +L eSim_Diode D15 +U 1 1 6782F82E +P 9420 2980 +F 0 "D15" H 9420 3080 50 0000 C CNN +F 1 "eSim_Diode" H 9420 2880 50 0000 C CNN +F 2 "" H 9420 2980 60 0000 C CNN +F 3 "" H 9420 2980 60 0000 C CNN + 1 9420 2980 + 0 1 -1 0 +$EndComp +$Comp +L eSim_Diode D13 +U 1 1 6782F834 +P 9220 3330 +F 0 "D13" H 9220 3430 50 0000 C CNN +F 1 "eSim_Diode" H 9220 3230 50 0000 C CNN +F 2 "" H 9220 3330 60 0000 C CNN +F 3 "" H 9220 3330 60 0000 C CNN + 1 9220 3330 + 0 1 -1 0 +$EndComp +Wire Wire Line + 9020 2280 8750 2280 +Wire Wire Line + 8750 2280 8750 1870 +Wire Wire Line + 8750 1870 9020 1870 +Wire Wire Line + 8820 2170 8820 2580 +Wire Wire Line + 8820 2580 8690 2580 +$Comp +L eSim_NPN Q23 +U 1 1 6782F83F +P 8490 2580 +F 0 "Q23" H 8390 2630 50 0000 R CNN +F 1 "eSim_NPN" H 8440 2730 50 0000 R CNN +F 2 "" H 8690 2680 29 0000 C CNN +F 3 "" H 8490 2580 60 0000 C CNN + 1 8490 2580 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q21 +U 1 1 6782F845 +P 8030 2780 +F 0 "Q21" H 7930 2830 50 0000 R CNN +F 1 "eSim_NPN" H 7980 2930 50 0000 R CNN +F 2 "" H 8230 2880 29 0000 C CNN +F 3 "" H 8030 2780 60 0000 C CNN + 1 8030 2780 + -1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D11 +U 1 1 6782F84B +P 8150 2380 +F 0 "D11" H 8150 2480 50 0000 C CNN +F 1 "eSim_Diode" H 8150 2280 50 0000 C CNN +F 2 "" H 8150 2380 60 0000 C CNN +F 3 "" H 8150 2380 60 0000 C CNN + 1 8150 2380 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q19 +U 1 1 6782F851 +P 7620 2380 +F 0 "Q19" H 7520 2430 50 0000 R CNN +F 1 "eSim_NPN" H 7570 2530 50 0000 R CNN +F 2 "" H 7820 2480 29 0000 C CNN +F 3 "" H 7620 2380 60 0000 C CNN + 1 7620 2380 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q15 +U 1 1 6782F857 +P 7250 2120 +F 0 "Q15" H 7150 2170 50 0000 R CNN +F 1 "eSim_NPN" H 7200 2270 50 0000 R CNN +F 2 "" H 7450 2220 29 0000 C CNN +F 3 "" H 7250 2120 60 0000 C CNN + 1 7250 2120 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q16 +U 1 1 6782F85D +P 7250 3050 +F 0 "Q16" H 7150 3100 50 0000 R CNN +F 1 "eSim_NPN" H 7200 3200 50 0000 R CNN +F 2 "" H 7450 3150 29 0000 C CNN +F 3 "" H 7250 3050 60 0000 C CNN + 1 7250 3050 + -1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D9 +U 1 1 6782F863 +P 7150 2580 +F 0 "D9" H 7150 2680 50 0000 C CNN +F 1 "eSim_Diode" H 7150 2480 50 0000 C CNN +F 2 "" H 7150 2580 60 0000 C CNN +F 3 "" H 7150 2580 60 0000 C CNN + 1 7150 2580 + 0 -1 1 0 +$EndComp +$Comp +L resistor R20 +U 1 1 6782F869 +P 8440 3280 +F 0 "R20" H 8490 3410 50 0000 C CNN +F 1 "800" H 8490 3230 50 0000 C CNN +F 2 "" H 8490 3260 30 0000 C CNN +F 3 "" V 8490 3330 30 0000 C CNN + 1 8440 3280 + 0 -1 1 0 +$EndComp +$Comp +L resistor R16 +U 1 1 6782F86F +P 7570 3280 +F 0 "R16" H 7620 3410 50 0000 C CNN +F 1 "1k" H 7620 3230 50 0000 C CNN +F 2 "" H 7620 3260 30 0000 C CNN +F 3 "" V 7620 3330 30 0000 C CNN + 1 7570 3280 + 0 -1 1 0 +$EndComp +Wire Wire Line + 8300 2380 8390 2380 +Wire Wire Line + 8390 2380 8390 1470 +Wire Wire Line + 9020 1870 9020 1470 +Wire Wire Line + 7450 2120 7520 2120 +Wire Wire Line + 7520 1470 7520 2180 +Connection ~ 7520 2120 +Wire Wire Line + 7150 1920 7150 1470 +Wire Wire Line + 7820 2380 8000 2380 +Wire Wire Line + 7930 2580 7930 2380 +Connection ~ 7930 2380 +Wire Wire Line + 8230 2780 8390 2780 +Wire Wire Line + 8390 2780 8390 3180 +Wire Wire Line + 9220 2580 9220 3180 +Wire Wire Line + 9220 2170 9420 2170 +Wire Wire Line + 9420 2170 9420 2830 +Wire Wire Line + 7520 3050 7450 3050 +Wire Wire Line + 7520 2580 7520 3180 +Connection ~ 7520 3050 +Wire Wire Line + 7150 2730 7150 2850 +Wire Wire Line + 7150 2320 7150 2430 +Wire Wire Line + 7150 3560 7150 3250 +Wire Wire Line + 9420 3560 9420 3130 +Wire Wire Line + 9220 3480 9220 3560 +Connection ~ 9220 3560 +Wire Wire Line + 8390 3480 8390 3560 +Connection ~ 8390 3560 +Wire Wire Line + 7520 3480 7520 3560 +Connection ~ 7520 3560 +Wire Wire Line + 7930 2980 7930 3560 +Connection ~ 7930 3560 +Wire Wire Line + 7150 1070 7150 1170 +Wire Wire Line + 9020 1070 9020 1170 +Wire Wire Line + 8390 1170 8390 1070 +Connection ~ 8390 1070 +Wire Wire Line + 7520 1170 7520 1070 +Connection ~ 7520 1070 +$Comp +L PORT U1 +U 11 1 6782F89B +P 9790 2460 +F 0 "U1" H 9840 2560 30 0000 C CNN +F 1 "PORT" H 9790 2460 30 0000 C CNN +F 2 "" H 9790 2460 60 0000 C CNN +F 3 "" H 9790 2460 60 0000 C CNN + 11 9790 2460 + -1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 12 1 6782F8A1 +P 9790 2750 +F 0 "U1" H 9840 2850 30 0000 C CNN +F 1 "PORT" H 9790 2750 30 0000 C CNN +F 2 "" H 9790 2750 60 0000 C CNN +F 3 "" H 9790 2750 60 0000 C CNN + 12 9790 2750 + -1 0 0 -1 +$EndComp +Wire Wire Line + 9540 2750 9220 2750 +Connection ~ 9220 2750 +Wire Wire Line + 9540 2460 9420 2460 +Connection ~ 9420 2460 +$Comp +L PORT U1 +U 9 1 6782F8AB +P 6800 2790 +F 0 "U1" H 6850 2890 30 0000 C CNN +F 1 "PORT" H 6800 2790 30 0000 C CNN +F 2 "" H 6800 2790 60 0000 C CNN +F 3 "" H 6800 2790 60 0000 C CNN + 9 6800 2790 + 1 0 0 1 +$EndComp +Wire Wire Line + 7050 2790 7150 2790 +Connection ~ 7150 2790 +$Comp +L resistor R24 +U 1 1 6782F8B3 +P 9070 4380 +F 0 "R24" H 9120 4510 50 0000 C CNN +F 1 "4k" H 9120 4330 50 0000 C CNN +F 2 "" H 9120 4360 30 0000 C CNN +F 3 "" V 9120 4430 30 0000 C CNN + 1 9070 4380 + 0 -1 1 0 +$EndComp +$Comp +L resistor R21 +U 1 1 6782F8B9 +P 8440 4380 +F 0 "R21" H 8490 4510 50 0000 C CNN +F 1 "2k" H 8490 4330 50 0000 C CNN +F 2 "" H 8490 4360 30 0000 C CNN +F 3 "" V 8490 4430 30 0000 C CNN + 1 8440 4380 + 0 -1 1 0 +$EndComp +$Comp +L resistor R17 +U 1 1 6782F8BF +P 7570 4380 +F 0 "R17" H 7620 4510 50 0000 C CNN +F 1 "1.6k" H 7620 4330 50 0000 C CNN +F 2 "" H 7620 4360 30 0000 C CNN +F 3 "" V 7620 4430 30 0000 C CNN + 1 7570 4380 + 0 -1 1 0 +$EndComp +$Comp +L resistor R14 +U 1 1 6782F8C5 +P 7200 4380 +F 0 "R14" H 7250 4510 50 0000 C CNN +F 1 "130" H 7250 4330 50 0000 C CNN +F 2 "" H 7250 4360 30 0000 C CNN +F 3 "" V 7250 4430 30 0000 C CNN + 1 7200 4380 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q27 +U 1 1 6782F8CB +P 9020 5180 +F 0 "Q27" H 8920 5230 50 0000 R CNN +F 1 "eSim_NPN" H 8970 5330 50 0000 R CNN +F 2 "" H 9220 5280 29 0000 C CNN +F 3 "" H 9020 5180 60 0000 C CNN + 1 9020 5180 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q28 +U 1 1 6782F8D1 +P 9020 5590 +F 0 "Q28" H 8920 5640 50 0000 R CNN +F 1 "eSim_NPN" H 8970 5740 50 0000 R CNN +F 2 "" H 9220 5690 29 0000 C CNN +F 3 "" H 9020 5590 60 0000 C CNN + 1 9020 5590 + 0 -1 1 0 +$EndComp +$Comp +L eSim_Diode D16 +U 1 1 6782F8D7 +P 9420 6090 +F 0 "D16" H 9420 6190 50 0000 C CNN +F 1 "eSim_Diode" H 9420 5990 50 0000 C CNN +F 2 "" H 9420 6090 60 0000 C CNN +F 3 "" H 9420 6090 60 0000 C CNN + 1 9420 6090 + 0 1 -1 0 +$EndComp +$Comp +L eSim_Diode D14 +U 1 1 6782F8DD +P 9220 6440 +F 0 "D14" H 9220 6540 50 0000 C CNN +F 1 "eSim_Diode" H 9220 6340 50 0000 C CNN +F 2 "" H 9220 6440 60 0000 C CNN +F 3 "" H 9220 6440 60 0000 C CNN + 1 9220 6440 + 0 1 -1 0 +$EndComp +Wire Wire Line + 9020 5390 8750 5390 +Wire Wire Line + 8750 5390 8750 4980 +Wire Wire Line + 8750 4980 9020 4980 +Wire Wire Line + 8820 5280 8820 5690 +Wire Wire Line + 8820 5690 8690 5690 +$Comp +L eSim_NPN Q24 +U 1 1 6782F8E8 +P 8490 5690 +F 0 "Q24" H 8390 5740 50 0000 R CNN +F 1 "eSim_NPN" H 8440 5840 50 0000 R CNN +F 2 "" H 8690 5790 29 0000 C CNN +F 3 "" H 8490 5690 60 0000 C CNN + 1 8490 5690 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q22 +U 1 1 6782F8EE +P 8030 5890 +F 0 "Q22" H 7930 5940 50 0000 R CNN +F 1 "eSim_NPN" H 7980 6040 50 0000 R CNN +F 2 "" H 8230 5990 29 0000 C CNN +F 3 "" H 8030 5890 60 0000 C CNN + 1 8030 5890 + -1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D12 +U 1 1 6782F8F4 +P 8150 5490 +F 0 "D12" H 8150 5590 50 0000 C CNN +F 1 "eSim_Diode" H 8150 5390 50 0000 C CNN +F 2 "" H 8150 5490 60 0000 C CNN +F 3 "" H 8150 5490 60 0000 C CNN + 1 8150 5490 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q20 +U 1 1 6782F8FA +P 7620 5490 +F 0 "Q20" H 7520 5540 50 0000 R CNN +F 1 "eSim_NPN" H 7570 5640 50 0000 R CNN +F 2 "" H 7820 5590 29 0000 C CNN +F 3 "" H 7620 5490 60 0000 C CNN + 1 7620 5490 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q17 +U 1 1 6782F900 +P 7250 5230 +F 0 "Q17" H 7150 5280 50 0000 R CNN +F 1 "eSim_NPN" H 7200 5380 50 0000 R CNN +F 2 "" H 7450 5330 29 0000 C CNN +F 3 "" H 7250 5230 60 0000 C CNN + 1 7250 5230 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q18 +U 1 1 6782F906 +P 7250 6160 +F 0 "Q18" H 7150 6210 50 0000 R CNN +F 1 "eSim_NPN" H 7200 6310 50 0000 R CNN +F 2 "" H 7450 6260 29 0000 C CNN +F 3 "" H 7250 6160 60 0000 C CNN + 1 7250 6160 + -1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D10 +U 1 1 6782F90C +P 7150 5690 +F 0 "D10" H 7150 5790 50 0000 C CNN +F 1 "eSim_Diode" H 7150 5590 50 0000 C CNN +F 2 "" H 7150 5690 60 0000 C CNN +F 3 "" H 7150 5690 60 0000 C CNN + 1 7150 5690 + 0 -1 1 0 +$EndComp +$Comp +L resistor R22 +U 1 1 6782F912 +P 8440 6390 +F 0 "R22" H 8490 6520 50 0000 C CNN +F 1 "800" H 8490 6340 50 0000 C CNN +F 2 "" H 8490 6370 30 0000 C CNN +F 3 "" V 8490 6440 30 0000 C CNN + 1 8440 6390 + 0 -1 1 0 +$EndComp +$Comp +L resistor R18 +U 1 1 6782F918 +P 7570 6390 +F 0 "R18" H 7620 6520 50 0000 C CNN +F 1 "1k" H 7620 6340 50 0000 C CNN +F 2 "" H 7620 6370 30 0000 C CNN +F 3 "" V 7620 6440 30 0000 C CNN + 1 7570 6390 + 0 -1 1 0 +$EndComp +Wire Wire Line + 8300 5490 8390 5490 +Wire Wire Line + 8390 5490 8390 4580 +Wire Wire Line + 9020 4980 9020 4580 +Wire Wire Line + 7450 5230 7520 5230 +Wire Wire Line + 7520 4580 7520 5290 +Connection ~ 7520 5230 +Wire Wire Line + 7150 5030 7150 4580 +Wire Wire Line + 7820 5490 8000 5490 +Wire Wire Line + 7930 5690 7930 5490 +Connection ~ 7930 5490 +Wire Wire Line + 8230 5890 8390 5890 +Wire Wire Line + 8390 5890 8390 6290 +Wire Wire Line + 9220 5690 9220 6290 +Wire Wire Line + 9220 5280 9420 5280 +Wire Wire Line + 9420 5280 9420 5940 +Wire Wire Line + 7520 6160 7450 6160 +Wire Wire Line + 7520 5690 7520 6290 +Connection ~ 7520 6160 +Wire Wire Line + 7150 5840 7150 5960 +Wire Wire Line + 7150 5430 7150 5540 +Wire Wire Line + 7150 6670 7150 6360 +Wire Wire Line + 9420 6670 9420 6240 +Wire Wire Line + 9220 6590 9220 6670 +Connection ~ 9220 6670 +Wire Wire Line + 8390 6590 8390 6670 +Connection ~ 8390 6670 +Wire Wire Line + 7520 6590 7520 6670 +Connection ~ 7520 6670 +Wire Wire Line + 7930 6090 7930 6670 +Connection ~ 7930 6670 +Wire Wire Line + 7150 4180 7150 4280 +Wire Wire Line + 9020 4180 9020 4280 +Wire Wire Line + 8390 4280 8390 4180 +Connection ~ 8390 4180 +Wire Wire Line + 7520 4280 7520 4180 +Connection ~ 7520 4180 +$Comp +L PORT U1 +U 13 1 6782F944 +P 9790 5570 +F 0 "U1" H 9840 5670 30 0000 C CNN +F 1 "PORT" H 9790 5570 30 0000 C CNN +F 2 "" H 9790 5570 60 0000 C CNN +F 3 "" H 9790 5570 60 0000 C CNN + 13 9790 5570 + -1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 14 1 6782F94A +P 9790 5860 +F 0 "U1" H 9840 5960 30 0000 C CNN +F 1 "PORT" H 9790 5860 30 0000 C CNN +F 2 "" H 9790 5860 60 0000 C CNN +F 3 "" H 9790 5860 60 0000 C CNN + 14 9790 5860 + -1 0 0 -1 +$EndComp +Wire Wire Line + 9540 5860 9220 5860 +Connection ~ 9220 5860 +Wire Wire Line + 9540 5570 9420 5570 +Connection ~ 9420 5570 +$Comp +L PORT U1 +U 10 1 6782F954 +P 6800 5900 +F 0 "U1" H 6850 6000 30 0000 C CNN +F 1 "PORT" H 6800 5900 30 0000 C CNN +F 2 "" H 6800 5900 60 0000 C CNN +F 3 "" H 6800 5900 60 0000 C CNN + 10 6800 5900 + 1 0 0 1 +$EndComp +Wire Wire Line + 7050 5900 7150 5900 +Connection ~ 7150 5900 +$Comp +L PORT U1 +U 7 1 6783256E +P 5300 620 +F 0 "U1" H 5350 720 30 0000 C CNN +F 1 "PORT" H 5300 620 30 0000 C CNN +F 2 "" H 5300 620 60 0000 C CNN +F 3 "" H 5300 620 60 0000 C CNN + 7 5300 620 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 8 1 678327B9 +P 5740 620 +F 0 "U1" H 5790 720 30 0000 C CNN +F 1 "PORT" H 5740 620 30 0000 C CNN +F 2 "" H 5740 620 60 0000 C CNN +F 3 "" H 5740 620 60 0000 C CNN + 8 5740 620 + 0 1 1 0 +$EndComp +Wire Wire Line + 5300 870 5300 4180 +Connection ~ 3970 1070 +Connection ~ 5300 1070 +Connection ~ 7150 1070 +Connection ~ 3970 4180 +Connection ~ 5300 4180 +Connection ~ 7150 4180 +Wire Wire Line + 5740 870 5740 6670 +Connection ~ 3970 3560 +Connection ~ 5740 3560 +Connection ~ 7150 3560 +Connection ~ 3970 6670 +Connection ~ 5740 6670 +Connection ~ 7150 6670 +$Comp +L GNDPWR #PWR01 +U 1 1 6783B5E4 +P 2940 3630 +F 0 "#PWR01" H 2940 3430 50 0001 C CNN +F 1 "GNDPWR" H 2940 3500 50 0000 C CNN +F 2 "" H 2940 3580 50 0001 C CNN +F 3 "" H 2940 3580 50 0001 C CNN + 1 2940 3630 + 1 0 0 -1 +$EndComp +$Comp +L GNDPWR #PWR02 +U 1 1 6783B6EC +P 2920 6750 +F 0 "#PWR02" H 2920 6550 50 0001 C CNN +F 1 "GNDPWR" H 2920 6620 50 0000 C CNN +F 2 "" H 2920 6700 50 0001 C CNN +F 3 "" H 2920 6700 50 0001 C CNN + 1 2920 6750 + 1 0 0 -1 +$EndComp +$Comp +L GNDPWR #PWR03 +U 1 1 6783BB21 +P 8140 6740 +F 0 "#PWR03" H 8140 6540 50 0001 C CNN +F 1 "GNDPWR" H 8140 6610 50 0000 C CNN +F 2 "" H 8140 6690 50 0001 C CNN +F 3 "" H 8140 6690 50 0001 C CNN + 1 8140 6740 + 1 0 0 -1 +$EndComp +$Comp +L GNDPWR #PWR04 +U 1 1 6783BFA8 +P 8150 3630 +F 0 "#PWR04" H 8150 3430 50 0001 C CNN +F 1 "GNDPWR" H 8150 3500 50 0000 C CNN +F 2 "" H 8150 3580 50 0001 C CNN +F 3 "" H 8150 3580 50 0001 C CNN + 1 8150 3630 + 1 0 0 -1 +$EndComp +Wire Wire Line + 8150 3630 8150 3560 +Connection ~ 8150 3560 +Wire Wire Line + 2940 3630 2940 3560 +Connection ~ 2940 3560 +Wire Wire Line + 2920 6750 2920 6670 +Connection ~ 2920 6670 +Wire Wire Line + 8140 6740 8140 6670 +Connection ~ 8140 6670 +$EndSCHEMATC diff --git a/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.sub b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.sub new file mode 100644 index 000000000..75d406e70 --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit.sub @@ -0,0 +1,76 @@ +* Subcircuit SN7408_Subcircuit +.subckt SN7408_Subcircuit net-_d1-pad2_ net-_d3-pad2_ net-_d2-pad2_ net-_d4-pad2_ net-_d7-pad2_ net-_d8-pad2_ net-_r1-pad1_ gndpwr net-_d9-pad2_ net-_d10-pad2_ net-_d15-pad2_ net-_d13-pad2_ net-_d16-pad2_ net-_d14-pad2_ +* c:\fossee\esim\library\subcircuitlibrary\sn7408_subcircuit\sn7408_subcircuit.cir +.include NPN.lib +.include D.lib +r1 net-_r1-pad1_ net-_q1-pad2_ 4k +r3 net-_r1-pad1_ net-_d5-pad1_ 2k +r7 net-_r1-pad1_ net-_q11-pad2_ 1.6k +r11 net-_r1-pad1_ net-_q11-pad1_ 130 +q1 net-_q1-pad1_ net-_q1-pad2_ net-_d1-pad2_ Q2N2222 +q2 net-_q1-pad1_ net-_q1-pad2_ net-_d3-pad2_ Q2N2222 +d1 gndpwr net-_d1-pad2_ 1N4148 +d3 gndpwr net-_d3-pad2_ 1N4148 +q5 net-_d5-pad1_ net-_q1-pad1_ net-_q5-pad3_ Q2N2222 +q7 net-_d5-pad2_ net-_q5-pad3_ gndpwr Q2N2222 +d5 net-_d5-pad1_ net-_d5-pad2_ 1N4148 +q9 net-_q11-pad2_ net-_d5-pad2_ net-_q12-pad2_ Q2N2222 +q11 net-_q11-pad1_ net-_q11-pad2_ net-_d7-pad1_ Q2N2222 +q12 net-_d7-pad2_ net-_q12-pad2_ gndpwr Q2N2222 +d7 net-_d7-pad1_ net-_d7-pad2_ 1N4148 +r4 net-_q5-pad3_ gndpwr 800 +r8 net-_q12-pad2_ gndpwr 1k +r2 net-_r1-pad1_ net-_q3-pad2_ 4k +r5 net-_r1-pad1_ net-_d6-pad1_ 2k +r9 net-_r1-pad1_ net-_q10-pad1_ 1.6k +r12 net-_r1-pad1_ net-_q13-pad1_ 130 +q3 net-_q3-pad1_ net-_q3-pad2_ net-_d2-pad2_ Q2N2222 +q4 net-_q3-pad1_ net-_q3-pad2_ net-_d4-pad2_ Q2N2222 +d2 gndpwr net-_d2-pad2_ 1N4148 +d4 gndpwr net-_d4-pad2_ 1N4148 +q6 net-_d6-pad1_ net-_q3-pad1_ net-_q6-pad3_ Q2N2222 +q8 net-_d6-pad2_ net-_q6-pad3_ gndpwr Q2N2222 +d6 net-_d6-pad1_ net-_d6-pad2_ 1N4148 +q10 net-_q10-pad1_ net-_d6-pad2_ net-_q10-pad3_ Q2N2222 +q13 net-_q13-pad1_ net-_q10-pad1_ net-_d8-pad1_ Q2N2222 +q14 net-_d8-pad2_ net-_q10-pad3_ gndpwr Q2N2222 +d8 net-_d8-pad1_ net-_d8-pad2_ 1N4148 +r6 net-_q6-pad3_ gndpwr 800 +r10 net-_q10-pad3_ gndpwr 1k +r23 net-_r1-pad1_ net-_q25-pad2_ 4k +r19 net-_r1-pad1_ net-_d11-pad1_ 2k +r15 net-_r1-pad1_ net-_q15-pad2_ 1.6k +r13 net-_r1-pad1_ net-_q15-pad1_ 130 +q25 net-_q23-pad2_ net-_q25-pad2_ net-_d15-pad2_ Q2N2222 +q26 net-_q23-pad2_ net-_q25-pad2_ net-_d13-pad2_ Q2N2222 +d15 gndpwr net-_d15-pad2_ 1N4148 +d13 gndpwr net-_d13-pad2_ 1N4148 +q23 net-_d11-pad1_ net-_q23-pad2_ net-_q21-pad2_ Q2N2222 +q21 net-_d11-pad2_ net-_q21-pad2_ gndpwr Q2N2222 +d11 net-_d11-pad1_ net-_d11-pad2_ 1N4148 +q19 net-_q15-pad2_ net-_d11-pad2_ net-_q16-pad2_ Q2N2222 +q15 net-_q15-pad1_ net-_q15-pad2_ net-_d9-pad1_ Q2N2222 +q16 net-_d9-pad2_ net-_q16-pad2_ gndpwr Q2N2222 +d9 net-_d9-pad1_ net-_d9-pad2_ 1N4148 +r20 net-_q21-pad2_ gndpwr 800 +r16 net-_q16-pad2_ gndpwr 1k +r24 net-_r1-pad1_ net-_q27-pad2_ 4k +r21 net-_r1-pad1_ net-_d12-pad1_ 2k +r17 net-_r1-pad1_ net-_q17-pad2_ 1.6k +r14 net-_r1-pad1_ net-_q17-pad1_ 130 +q27 net-_q24-pad2_ net-_q27-pad2_ net-_d16-pad2_ Q2N2222 +q28 net-_q24-pad2_ net-_q27-pad2_ net-_d14-pad2_ Q2N2222 +d16 gndpwr net-_d16-pad2_ 1N4148 +d14 gndpwr net-_d14-pad2_ 1N4148 +q24 net-_d12-pad1_ net-_q24-pad2_ net-_q22-pad2_ Q2N2222 +q22 net-_d12-pad2_ net-_q22-pad2_ gndpwr Q2N2222 +d12 net-_d12-pad1_ net-_d12-pad2_ 1N4148 +q20 net-_q17-pad2_ net-_d12-pad2_ net-_q18-pad2_ Q2N2222 +q17 net-_q17-pad1_ net-_q17-pad2_ net-_d10-pad1_ Q2N2222 +q18 net-_d10-pad2_ net-_q18-pad2_ gndpwr Q2N2222 +d10 net-_d10-pad1_ net-_d10-pad2_ 1N4148 +r22 net-_q22-pad2_ gndpwr 800 +r18 net-_q18-pad2_ gndpwr 1k +* Control Statements + +.ends SN7408_Subcircuit \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit_Previous_Values.xml b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit_Previous_Values.xml new file mode 100644 index 000000000..d363fca4c --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/SN7408_Subcircuit_Previous_Values.xml @@ -0,0 +1 @@ +C:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libtruefalsefalseHzHz0Volts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or Amperessecsecsec \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7408/analysis b/library/SubcircuitLibrary/SN7408/analysis new file mode 100644 index 000000000..ebd5c0a94 --- /dev/null +++ b/library/SubcircuitLibrary/SN7408/analysis @@ -0,0 +1 @@ +.tran 0e-00 0e-00 0e-00 \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7432/D.lib b/library/SubcircuitLibrary/SN7432/D.lib new file mode 100644 index 000000000..f53bf3e03 --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/D.lib @@ -0,0 +1,2 @@ +.model 1N4148 D(is=2.495E-09 rs=4.755E-01 n=1.679E+00 tt=3.030E-09 cjo=1.700E-12 vj=1 m=1.959E-01 bv=1.000E+02 ibv=1.000E-04) + diff --git a/library/SubcircuitLibrary/SN7432/NPN.lib b/library/SubcircuitLibrary/SN7432/NPN.lib new file mode 100644 index 000000000..be5f3073a --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/NPN.lib @@ -0,0 +1,4 @@ +.model Q2N2222 NPN( Is=14.34f Xti=3 Eg=1.11 Vaf=74.03 Bf=400 Ne=1.307 ++ Ise=14.34f Ikf=0.2847 Xtb=1.5 Br=6.092 Nc=2 Isc=0 Ikr=0 Rc=1 Cjc=7.306p ++ Mjc=0.3416 Vjc=0.75 Fc=0.5 Cje=22.01p Mje=0.377 Vje=0.75 Tr=46.91n Tf=411.1p ++ Itf=0.6 Vtf=1.7 Xtf=3 Rb=10) diff --git a/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit-cache.lib b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit-cache.lib new file mode 100644 index 000000000..0f688db8a --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit-cache.lib @@ -0,0 +1,126 @@ +EESchema-LIBRARY Version 2.3 +#encoding utf-8 +# +# GNDPWR +# +DEF GNDPWR #PWR 0 0 Y Y 1 F P +F0 "#PWR" 0 -200 50 H I C CNN +F1 "GNDPWR" 0 -130 50 H V C CNN +F2 "" 0 -50 50 H I C CNN +F3 "" 0 -50 50 H I C CNN +DRAW +P 2 0 1 0 0 -50 0 0 N +P 3 0 1 8 -40 -50 -50 -80 -50 -80 N +P 3 0 1 8 -20 -50 -30 -80 -30 -80 N +P 3 0 1 8 0 -50 -10 -80 -10 -80 N +P 3 0 1 8 20 -50 10 -80 10 -80 N +P 3 0 1 8 40 -50 -40 -50 -40 -50 N +P 4 0 1 8 40 -50 30 -80 30 -80 30 -80 N +X GNDPWR 1 0 0 0 U 50 50 1 1 W N +ENDDRAW +ENDDEF +# +# PORT +# +DEF PORT U 0 40 Y Y 26 F N +F0 "U" 50 100 30 H V C CNN +F1 "PORT" 0 0 30 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +A 325 225 285 -1421 -1278 0 1 0 N 100 50 150 0 +A 376 -275 356 1294 1408 0 1 0 N 150 0 100 -50 +S -100 50 100 -50 0 1 0 N +X ~ 1 250 0 100 L 30 30 1 1 B +X ~ 2 250 0 100 L 30 30 2 1 B +X ~ 3 250 0 100 L 30 30 3 1 B +X ~ 4 250 0 100 L 30 30 4 1 B +X ~ 5 250 0 100 L 30 30 5 1 B +X ~ 6 250 0 100 L 30 30 6 1 B +X ~ 7 250 0 100 L 30 30 7 1 B +X ~ 8 250 0 100 L 30 30 8 1 B +X ~ 9 250 0 100 L 30 30 9 1 B +X ~ 10 250 0 100 L 30 30 10 1 B +X ~ 11 250 0 100 L 30 30 11 1 B +X ~ 12 250 0 100 L 30 30 12 1 B +X ~ 13 250 0 100 L 30 30 13 1 B +X ~ 14 250 0 100 L 30 30 14 1 B +X ~ 15 250 0 100 L 30 30 15 1 B +X ~ 16 250 0 100 L 30 30 16 1 B +X ~ 17 250 0 100 L 30 30 17 1 B +X ~ 18 250 0 100 L 30 30 18 1 B +X ~ 19 250 0 100 L 30 30 19 1 B +X ~ 20 250 0 100 L 30 30 20 1 B +X ~ 21 250 0 100 L 30 30 21 1 B +X ~ 22 250 0 100 L 30 30 22 1 B +X ~ 23 250 0 100 L 30 30 23 1 B +X ~ 24 250 0 100 L 30 30 24 1 B +X ~ 25 250 0 100 L 30 30 25 1 B +X ~ 26 250 0 100 L 30 30 26 1 B +ENDDRAW +ENDDEF +# +# eSim_Diode +# +DEF eSim_Diode D 0 40 N N 1 F N +F0 "D" 0 100 50 H V C CNN +F1 "eSim_Diode" 0 -100 50 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + TO-???* + *SingleDiode + *_Diode_* + *SingleDiode* + D_* +$ENDFPLIST +DRAW +T 0 -100 50 60 0 0 0 A Normal 0 C C +T 0 100 50 60 0 0 0 K Normal 0 C C +P 2 0 1 6 50 50 50 -50 N +P 3 0 1 0 -50 50 50 0 -50 -50 F +X A 1 -150 0 100 R 40 40 1 1 P +X K 2 150 0 100 L 40 40 1 1 P +ENDDRAW +ENDDEF +# +# eSim_NPN +# +DEF eSim_NPN Q 0 0 Y N 1 F N +F0 "Q" -100 50 50 H V R CNN +F1 "eSim_NPN" -50 150 50 H V R CNN +F2 "" 200 100 29 H V C CNN +F3 "" 0 0 60 H V C CNN +ALIAS BC547 Q2N2222 +DRAW +C 50 0 111 0 1 10 N +P 2 0 1 0 25 25 100 100 N +P 3 0 1 0 25 -25 100 -100 100 -100 N +P 3 0 1 20 25 75 25 -75 25 -75 N +P 5 0 1 0 50 -70 70 -50 90 -90 50 -70 50 -70 F +X C 1 100 200 100 D 50 50 1 1 P +X B 2 -200 0 225 R 50 50 1 1 P +X E 3 100 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_R +# +DEF eSim_R R 0 0 N Y 1 F N +F0 "R" 50 130 50 H V C CNN +F1 "eSim_R" 50 -50 50 H V C CNN +F2 "" 50 -20 30 H V C CNN +F3 "" 50 50 30 V V C CNN +ALIAS resistor +$FPLIST + R_* + Resistor_* +$ENDFPLIST +DRAW +S 150 10 -50 90 0 1 10 N +X ~ 1 -100 50 50 R 60 60 1 1 P +X ~ 2 200 50 50 L 60 60 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.cir b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.cir new file mode 100644 index 000000000..718803685 --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.cir @@ -0,0 +1,87 @@ +* C:\FOSSEE\eSim\library\SubcircuitLibrary\SN7432_Subcirrcuit\SN7432_Subcirrcuit.cir + +* EESchema Netlist Version 1.1 (Spice format) creation date: 01/10/25 23:23:51 + +* To exclude a component from the Spice Netlist add [Spice_Netlist_Enabled] user FIELD set to: N +* To reorder the component spice node sequence add [Spice_Node_Sequence] user FIELD and define sequence: 2,1,0 + +* Sheet Name: / +Q3 Net-_Q3-Pad1_ Net-_Q3-Pad2_ Net-_D2-Pad2_ eSim_NPN +Q4 Net-_Q4-Pad1_ Net-_Q4-Pad2_ Net-_D4-Pad2_ eSim_NPN +Q7 Net-_D6-Pad1_ Net-_Q3-Pad1_ Net-_Q10-Pad2_ eSim_NPN +Q8 Net-_D6-Pad1_ Net-_Q4-Pad1_ Net-_Q10-Pad2_ eSim_NPN +D6 Net-_D6-Pad1_ Net-_D6-Pad2_ eSim_Diode +R2 Net-_R1-Pad1_ Net-_Q3-Pad2_ 4k +R4 Net-_R1-Pad1_ Net-_Q4-Pad2_ 2k +R7 Net-_R1-Pad1_ Net-_D6-Pad1_ 2.5k +Q10 Net-_D6-Pad2_ Net-_Q10-Pad2_ GNDPWR eSim_NPN +Q12 Net-_Q12-Pad1_ Net-_D6-Pad2_ Net-_Q12-Pad3_ eSim_NPN +Q15 Net-_Q15-Pad1_ Net-_Q12-Pad1_ Net-_D8-Pad1_ eSim_NPN +R11 Net-_R1-Pad1_ Net-_Q12-Pad1_ 1.6k +R14 Net-_R1-Pad1_ Net-_Q15-Pad1_ 130 +D4 GNDPWR Net-_D4-Pad2_ eSim_Diode +D2 GNDPWR Net-_D2-Pad2_ eSim_Diode +R8 Net-_Q10-Pad2_ GNDPWR 1k +R12 Net-_Q12-Pad3_ GNDPWR 1k +D8 Net-_D8-Pad1_ Net-_D8-Pad2_ eSim_Diode +Q16 Net-_D8-Pad2_ Net-_Q12-Pad3_ GNDPWR eSim_NPN +Q29 Net-_Q25-Pad2_ Net-_Q29-Pad2_ Net-_D15-Pad2_ eSim_NPN +Q30 Net-_Q26-Pad2_ Net-_Q30-Pad2_ Net-_D13-Pad2_ eSim_NPN +Q25 Net-_D11-Pad1_ Net-_Q25-Pad2_ Net-_Q23-Pad2_ eSim_NPN +Q26 Net-_D11-Pad1_ Net-_Q26-Pad2_ Net-_Q23-Pad2_ eSim_NPN +D11 Net-_D11-Pad1_ Net-_D11-Pad2_ eSim_Diode +R27 Net-_R1-Pad1_ Net-_Q29-Pad2_ 4k +R25 Net-_R1-Pad1_ Net-_Q30-Pad2_ 2k +R22 Net-_R1-Pad1_ Net-_D11-Pad1_ 2.5k +Q23 Net-_D11-Pad2_ Net-_Q23-Pad2_ GNDPWR eSim_NPN +Q21 Net-_Q17-Pad2_ Net-_D11-Pad2_ Net-_Q18-Pad2_ eSim_NPN +Q17 Net-_Q17-Pad1_ Net-_Q17-Pad2_ Net-_D9-Pad1_ eSim_NPN +R17 Net-_R1-Pad1_ Net-_Q17-Pad2_ 1.6k +R15 Net-_R1-Pad1_ Net-_Q17-Pad1_ 130 +D13 GNDPWR Net-_D13-Pad2_ eSim_Diode +D15 GNDPWR Net-_D15-Pad2_ eSim_Diode +R21 Net-_Q23-Pad2_ GNDPWR 1k +R18 Net-_Q18-Pad2_ GNDPWR 1k +D9 Net-_D9-Pad1_ Net-_D9-Pad2_ eSim_Diode +Q18 Net-_D9-Pad2_ Net-_Q18-Pad2_ GNDPWR eSim_NPN +Q1 Net-_Q1-Pad1_ Net-_Q1-Pad2_ Net-_D1-Pad2_ eSim_NPN +Q2 Net-_Q2-Pad1_ Net-_Q2-Pad2_ Net-_D3-Pad2_ eSim_NPN +Q5 Net-_D5-Pad1_ Net-_Q1-Pad1_ Net-_Q5-Pad3_ eSim_NPN +Q6 Net-_D5-Pad1_ Net-_Q2-Pad1_ Net-_Q5-Pad3_ eSim_NPN +D5 Net-_D5-Pad1_ Net-_D5-Pad2_ eSim_Diode +R1 Net-_R1-Pad1_ Net-_Q1-Pad2_ 4k +R3 Net-_R1-Pad1_ Net-_Q2-Pad2_ 2k +R5 Net-_R1-Pad1_ Net-_D5-Pad1_ 2.5k +Q9 Net-_D5-Pad2_ Net-_Q5-Pad3_ GNDPWR eSim_NPN +Q11 Net-_Q11-Pad1_ Net-_D5-Pad2_ Net-_Q11-Pad3_ eSim_NPN +Q13 Net-_Q13-Pad1_ Net-_Q11-Pad1_ Net-_D7-Pad1_ eSim_NPN +R9 Net-_R1-Pad1_ Net-_Q11-Pad1_ 1.6k +R13 Net-_R1-Pad1_ Net-_Q13-Pad1_ 130 +D3 GNDPWR Net-_D3-Pad2_ eSim_Diode +D1 GNDPWR Net-_D1-Pad2_ eSim_Diode +R6 Net-_Q5-Pad3_ GNDPWR 1k +R10 Net-_Q11-Pad3_ GNDPWR 1k +D7 Net-_D7-Pad1_ Net-_D7-Pad2_ eSim_Diode +Q14 Net-_D7-Pad2_ Net-_Q11-Pad3_ GNDPWR eSim_NPN +Q31 Net-_Q27-Pad2_ Net-_Q31-Pad2_ Net-_D16-Pad2_ eSim_NPN +Q32 Net-_Q28-Pad2_ Net-_Q32-Pad2_ Net-_D14-Pad2_ eSim_NPN +Q27 Net-_D12-Pad1_ Net-_Q27-Pad2_ Net-_Q24-Pad2_ eSim_NPN +Q28 Net-_D12-Pad1_ Net-_Q28-Pad2_ Net-_Q24-Pad2_ eSim_NPN +D12 Net-_D12-Pad1_ Net-_D12-Pad2_ eSim_Diode +R28 Net-_R1-Pad1_ Net-_Q31-Pad2_ 4k +R26 Net-_R1-Pad1_ Net-_Q32-Pad2_ 2k +R24 Net-_R1-Pad1_ Net-_D12-Pad1_ 2.5k +Q24 Net-_D12-Pad2_ Net-_Q24-Pad2_ GNDPWR eSim_NPN +Q22 Net-_Q19-Pad2_ Net-_D12-Pad2_ Net-_Q20-Pad2_ eSim_NPN +Q19 Net-_Q19-Pad1_ Net-_Q19-Pad2_ Net-_D10-Pad1_ eSim_NPN +R19 Net-_R1-Pad1_ Net-_Q19-Pad2_ 1.6k +R16 Net-_R1-Pad1_ Net-_Q19-Pad1_ 130 +D14 GNDPWR Net-_D14-Pad2_ eSim_Diode +D16 GNDPWR Net-_D16-Pad2_ eSim_Diode +R23 Net-_Q24-Pad2_ GNDPWR 1k +R20 Net-_Q20-Pad2_ GNDPWR 1k +D10 Net-_D10-Pad1_ Net-_D10-Pad2_ eSim_Diode +Q20 Net-_D10-Pad2_ Net-_Q20-Pad2_ GNDPWR eSim_NPN +U1 Net-_D1-Pad2_ Net-_D3-Pad2_ Net-_D2-Pad2_ Net-_D4-Pad2_ Net-_D7-Pad2_ Net-_D8-Pad2_ Net-_R1-Pad1_ GNDPWR Net-_D9-Pad2_ Net-_D10-Pad2_ Net-_D15-Pad2_ Net-_D13-Pad2_ Net-_D16-Pad2_ Net-_D14-Pad2_ PORT + +.end diff --git a/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.cir.out b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.cir.out new file mode 100644 index 000000000..cb10dab96 --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.cir.out @@ -0,0 +1,90 @@ +* c:\fossee\esim\library\subcircuitlibrary\sn7432_subcirrcuit\sn7432_subcirrcuit.cir + +.include NPN.lib +.include D.lib +q3 net-_q3-pad1_ net-_q3-pad2_ net-_d2-pad2_ Q2N2222 +q4 net-_q4-pad1_ net-_q4-pad2_ net-_d4-pad2_ Q2N2222 +q7 net-_d6-pad1_ net-_q3-pad1_ net-_q10-pad2_ Q2N2222 +q8 net-_d6-pad1_ net-_q4-pad1_ net-_q10-pad2_ Q2N2222 +d6 net-_d6-pad1_ net-_d6-pad2_ 1N4148 +r2 net-_r1-pad1_ net-_q3-pad2_ 4k +r4 net-_r1-pad1_ net-_q4-pad2_ 2k +r7 net-_r1-pad1_ net-_d6-pad1_ 2.5k +q10 net-_d6-pad2_ net-_q10-pad2_ gndpwr Q2N2222 +q12 net-_q12-pad1_ net-_d6-pad2_ net-_q12-pad3_ Q2N2222 +q15 net-_q15-pad1_ net-_q12-pad1_ net-_d8-pad1_ Q2N2222 +r11 net-_r1-pad1_ net-_q12-pad1_ 1.6k +r14 net-_r1-pad1_ net-_q15-pad1_ 130 +d4 gndpwr net-_d4-pad2_ 1N4148 +d2 gndpwr net-_d2-pad2_ 1N4148 +r8 net-_q10-pad2_ gndpwr 1k +r12 net-_q12-pad3_ gndpwr 1k +d8 net-_d8-pad1_ net-_d8-pad2_ 1N4148 +q16 net-_d8-pad2_ net-_q12-pad3_ gndpwr Q2N2222 +q29 net-_q25-pad2_ net-_q29-pad2_ net-_d15-pad2_ Q2N2222 +q30 net-_q26-pad2_ net-_q30-pad2_ net-_d13-pad2_ Q2N2222 +q25 net-_d11-pad1_ net-_q25-pad2_ net-_q23-pad2_ Q2N2222 +q26 net-_d11-pad1_ net-_q26-pad2_ net-_q23-pad2_ Q2N2222 +d11 net-_d11-pad1_ net-_d11-pad2_ 1N4148 +r27 net-_r1-pad1_ net-_q29-pad2_ 4k +r25 net-_r1-pad1_ net-_q30-pad2_ 2k +r22 net-_r1-pad1_ net-_d11-pad1_ 2.5k +q23 net-_d11-pad2_ net-_q23-pad2_ gndpwr Q2N2222 +q21 net-_q17-pad2_ net-_d11-pad2_ net-_q18-pad2_ Q2N2222 +q17 net-_q17-pad1_ net-_q17-pad2_ net-_d9-pad1_ Q2N2222 +r17 net-_r1-pad1_ net-_q17-pad2_ 1.6k +r15 net-_r1-pad1_ net-_q17-pad1_ 130 +d13 gndpwr net-_d13-pad2_ 1N4148 +d15 gndpwr net-_d15-pad2_ 1N4148 +r21 net-_q23-pad2_ gndpwr 1k +r18 net-_q18-pad2_ gndpwr 1k +d9 net-_d9-pad1_ net-_d9-pad2_ 1N4148 +q18 net-_d9-pad2_ net-_q18-pad2_ gndpwr Q2N2222 +q1 net-_q1-pad1_ net-_q1-pad2_ net-_d1-pad2_ Q2N2222 +q2 net-_q2-pad1_ net-_q2-pad2_ net-_d3-pad2_ Q2N2222 +q5 net-_d5-pad1_ net-_q1-pad1_ net-_q5-pad3_ Q2N2222 +q6 net-_d5-pad1_ net-_q2-pad1_ net-_q5-pad3_ Q2N2222 +d5 net-_d5-pad1_ net-_d5-pad2_ 1N4148 +r1 net-_r1-pad1_ net-_q1-pad2_ 4k +r3 net-_r1-pad1_ net-_q2-pad2_ 2k +r5 net-_r1-pad1_ net-_d5-pad1_ 2.5k +q9 net-_d5-pad2_ net-_q5-pad3_ gndpwr Q2N2222 +q11 net-_q11-pad1_ net-_d5-pad2_ net-_q11-pad3_ Q2N2222 +q13 net-_q13-pad1_ net-_q11-pad1_ net-_d7-pad1_ Q2N2222 +r9 net-_r1-pad1_ net-_q11-pad1_ 1.6k +r13 net-_r1-pad1_ net-_q13-pad1_ 130 +d3 gndpwr net-_d3-pad2_ 1N4148 +d1 gndpwr net-_d1-pad2_ 1N4148 +r6 net-_q5-pad3_ gndpwr 1k +r10 net-_q11-pad3_ gndpwr 1k +d7 net-_d7-pad1_ net-_d7-pad2_ 1N4148 +q14 net-_d7-pad2_ net-_q11-pad3_ gndpwr Q2N2222 +q31 net-_q27-pad2_ net-_q31-pad2_ net-_d16-pad2_ Q2N2222 +q32 net-_q28-pad2_ net-_q32-pad2_ net-_d14-pad2_ Q2N2222 +q27 net-_d12-pad1_ net-_q27-pad2_ net-_q24-pad2_ Q2N2222 +q28 net-_d12-pad1_ net-_q28-pad2_ net-_q24-pad2_ Q2N2222 +d12 net-_d12-pad1_ net-_d12-pad2_ 1N4148 +r28 net-_r1-pad1_ net-_q31-pad2_ 4k +r26 net-_r1-pad1_ net-_q32-pad2_ 2k +r24 net-_r1-pad1_ net-_d12-pad1_ 2.5k +q24 net-_d12-pad2_ net-_q24-pad2_ gndpwr Q2N2222 +q22 net-_q19-pad2_ net-_d12-pad2_ net-_q20-pad2_ Q2N2222 +q19 net-_q19-pad1_ net-_q19-pad2_ net-_d10-pad1_ Q2N2222 +r19 net-_r1-pad1_ net-_q19-pad2_ 1.6k +r16 net-_r1-pad1_ net-_q19-pad1_ 130 +d14 gndpwr net-_d14-pad2_ 1N4148 +d16 gndpwr net-_d16-pad2_ 1N4148 +r23 net-_q24-pad2_ gndpwr 1k +r20 net-_q20-pad2_ gndpwr 1k +d10 net-_d10-pad1_ net-_d10-pad2_ 1N4148 +q20 net-_d10-pad2_ net-_q20-pad2_ gndpwr Q2N2222 +* u1 net-_d1-pad2_ net-_d3-pad2_ net-_d2-pad2_ net-_d4-pad2_ net-_d7-pad2_ net-_d8-pad2_ net-_r1-pad1_ gndpwr net-_d9-pad2_ net-_d10-pad2_ net-_d15-pad2_ net-_d13-pad2_ net-_d16-pad2_ net-_d14-pad2_ port +.tran 0e-00 0e-00 0e-00 + +* Control Statements +.control +run +print allv > plot_data_v.txt +print alli > plot_data_i.txt +.endc +.end diff --git a/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.pro b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.pro new file mode 100644 index 000000000..e27a398be --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.pro @@ -0,0 +1,73 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] +LibName1=adc-dac +LibName2=memory +LibName3=xilinx +LibName4=microcontrollers +LibName5=dsp +LibName6=microchip +LibName7=analog_switches +LibName8=motorola +LibName9=texas +LibName10=intel +LibName11=audio +LibName12=interface +LibName13=digital-audio +LibName14=philips +LibName15=display +LibName16=cypress +LibName17=siliconi +LibName18=opto +LibName19=atmel +LibName20=contrib +LibName21=power +LibName22=eSim_Plot +LibName23=transistors +LibName24=conn +LibName25=eSim_User +LibName26=regul +LibName27=74xx +LibName28=cmos4000 +LibName29=eSim_Analog +LibName30=eSim_Devices +LibName31=eSim_Digital +LibName32=eSim_Hybrid +LibName33=eSim_Miscellaneous +LibName34=eSim_Power +LibName35=eSim_Sources +LibName36=eSim_Subckt +LibName37=eSim_Nghdl +LibName38=eSim_Ngveri +LibName39=eSim_SKY130 +LibName40=eSim_SKY130_Subckts diff --git a/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.sch b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.sch new file mode 100644 index 000000000..50ece14b1 --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.sch @@ -0,0 +1,1551 @@ +EESchema Schematic File Version 2 +LIBS:adc-dac +LIBS:memory +LIBS:xilinx +LIBS:microcontrollers +LIBS:dsp +LIBS:microchip +LIBS:analog_switches +LIBS:motorola +LIBS:texas +LIBS:intel +LIBS:audio +LIBS:interface +LIBS:digital-audio +LIBS:philips +LIBS:display +LIBS:cypress +LIBS:siliconi +LIBS:opto +LIBS:atmel +LIBS:contrib +LIBS:power +LIBS:eSim_Plot +LIBS:transistors +LIBS:conn +LIBS:eSim_User +LIBS:regul +LIBS:74xx +LIBS:cmos4000 +LIBS:eSim_Analog +LIBS:eSim_Devices +LIBS:eSim_Digital +LIBS:eSim_Hybrid +LIBS:eSim_Miscellaneous +LIBS:eSim_Power +LIBS:eSim_Sources +LIBS:eSim_Subckt +LIBS:eSim_Nghdl +LIBS:eSim_Ngveri +LIBS:eSim_SKY130 +LIBS:eSim_SKY130_Subckts +LIBS:SN7432_Subcirrcuit-cache +EELAYER 25 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L eSim_NPN Q3 +U 1 1 6778E6CA +P 1990 1910 +F 0 "Q3" H 1890 1960 50 0000 R CNN +F 1 "eSim_NPN" H 1940 2060 50 0000 R CNN +F 2 "" H 2190 2010 29 0000 C CNN +F 3 "" H 1990 1910 60 0000 C CNN + 1 1990 1910 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q4 +U 1 1 6778E778 +P 1990 2640 +F 0 "Q4" H 1890 2690 50 0000 R CNN +F 1 "eSim_NPN" H 1940 2790 50 0000 R CNN +F 2 "" H 2190 2740 29 0000 C CNN +F 3 "" H 1990 2640 60 0000 C CNN + 1 1990 2640 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q7 +U 1 1 6778E848 +P 2690 2010 +F 0 "Q7" H 2590 2060 50 0000 R CNN +F 1 "eSim_NPN" H 2640 2160 50 0000 R CNN +F 2 "" H 2890 2110 29 0000 C CNN +F 3 "" H 2690 2010 60 0000 C CNN + 1 2690 2010 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q8 +U 1 1 6778E8D4 +P 2690 2740 +F 0 "Q8" H 2590 2790 50 0000 R CNN +F 1 "eSim_NPN" H 2640 2890 50 0000 R CNN +F 2 "" H 2890 2840 29 0000 C CNN +F 3 "" H 2690 2740 60 0000 C CNN + 1 2690 2740 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D6 +U 1 1 6778E99C +P 3170 2010 +F 0 "D6" H 3170 2110 50 0000 C CNN +F 1 "eSim_Diode" H 3170 1910 50 0000 C CNN +F 2 "" H 3170 2010 60 0000 C CNN +F 3 "" H 3170 2010 60 0000 C CNN + 1 3170 2010 + 1 0 0 -1 +$EndComp +$Comp +L resistor R2 +U 1 1 6778EA11 +P 1940 1320 +F 0 "R2" H 1990 1450 50 0000 C CNN +F 1 "4k" H 1990 1270 50 0000 C CNN +F 2 "" H 1990 1300 30 0000 C CNN +F 3 "" V 1990 1370 30 0000 C CNN + 1 1940 1320 + 0 1 1 0 +$EndComp +$Comp +L resistor R4 +U 1 1 6778EAB0 +P 2300 1320 +F 0 "R4" H 2350 1450 50 0000 C CNN +F 1 "2k" H 2350 1270 50 0000 C CNN +F 2 "" H 2350 1300 30 0000 C CNN +F 3 "" V 2350 1370 30 0000 C CNN + 1 2300 1320 + 0 1 1 0 +$EndComp +$Comp +L resistor R7 +U 1 1 6778EB19 +P 2930 1320 +F 0 "R7" H 2980 1450 50 0000 C CNN +F 1 "2.5k" H 2980 1270 50 0000 C CNN +F 2 "" H 2980 1300 30 0000 C CNN +F 3 "" V 2980 1370 30 0000 C CNN + 1 2930 1320 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q10 +U 1 1 6778EC17 +P 3350 2980 +F 0 "Q10" H 3250 3030 50 0000 R CNN +F 1 "eSim_NPN" H 3300 3130 50 0000 R CNN +F 2 "" H 3550 3080 29 0000 C CNN +F 3 "" H 3350 2980 60 0000 C CNN + 1 3350 2980 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q12 +U 1 1 6778EC99 +P 3650 2510 +F 0 "Q12" H 3550 2560 50 0000 R CNN +F 1 "eSim_NPN" H 3600 2660 50 0000 R CNN +F 2 "" H 3850 2610 29 0000 C CNN +F 3 "" H 3650 2510 60 0000 C CNN + 1 3650 2510 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q15 +U 1 1 6778ED11 +P 3980 2010 +F 0 "Q15" H 3880 2060 50 0000 R CNN +F 1 "eSim_NPN" H 3930 2160 50 0000 R CNN +F 2 "" H 4180 2110 29 0000 C CNN +F 3 "" H 3980 2010 60 0000 C CNN + 1 3980 2010 + 1 0 0 -1 +$EndComp +$Comp +L resistor R11 +U 1 1 6778EDD7 +P 3700 1320 +F 0 "R11" H 3750 1450 50 0000 C CNN +F 1 "1.6k" H 3750 1270 50 0000 C CNN +F 2 "" H 3750 1300 30 0000 C CNN +F 3 "" V 3750 1370 30 0000 C CNN + 1 3700 1320 + 0 1 1 0 +$EndComp +$Comp +L resistor R14 +U 1 1 6778EE89 +P 4160 1320 +F 0 "R14" H 4210 1450 50 0000 C CNN +F 1 "130" H 4210 1270 50 0000 C CNN +F 2 "" H 4210 1300 30 0000 C CNN +F 3 "" V 4210 1370 30 0000 C CNN + 1 4160 1320 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D4 +U 1 1 6778EF1A +P 1610 3520 +F 0 "D4" H 1610 3620 50 0000 C CNN +F 1 "eSim_Diode" H 1610 3420 50 0000 C CNN +F 2 "" H 1610 3520 60 0000 C CNN +F 3 "" H 1610 3520 60 0000 C CNN + 1 1610 3520 + 0 -1 -1 0 +$EndComp +$Comp +L eSim_Diode D2 +U 1 1 6778EFD0 +P 1330 3230 +F 0 "D2" H 1330 3330 50 0000 C CNN +F 1 "eSim_Diode" H 1330 3130 50 0000 C CNN +F 2 "" H 1330 3230 60 0000 C CNN +F 3 "" H 1330 3230 60 0000 C CNN + 1 1330 3230 + 0 -1 -1 0 +$EndComp +$Comp +L resistor R8 +U 1 1 6778F0B3 +P 2940 3470 +F 0 "R8" H 2990 3600 50 0000 C CNN +F 1 "1k" H 2990 3420 50 0000 C CNN +F 2 "" H 2990 3450 30 0000 C CNN +F 3 "" V 2990 3520 30 0000 C CNN + 1 2940 3470 + 0 1 1 0 +$EndComp +$Comp +L resistor R12 +U 1 1 6778F17D +P 3700 3460 +F 0 "R12" H 3750 3590 50 0000 C CNN +F 1 "1k" H 3750 3410 50 0000 C CNN +F 2 "" H 3750 3440 30 0000 C CNN +F 3 "" V 3750 3510 30 0000 C CNN + 1 3700 3460 + 0 1 1 0 +$EndComp +$Comp +L GNDPWR #PWR2 +U 1 1 6778F2AB +P 3900 3770 +F 0 "#PWR2" H 3900 3570 50 0001 C CNN +F 1 "GNDPWR" H 3900 3640 50 0000 C CNN +F 2 "" H 3900 3720 50 0001 C CNN +F 3 "" H 3900 3720 50 0001 C CNN + 1 3900 3770 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D8 +U 1 1 67790E8F +P 4080 2480 +F 0 "D8" H 4080 2580 50 0000 C CNN +F 1 "eSim_Diode" H 4080 2380 50 0000 C CNN +F 2 "" H 4080 2480 60 0000 C CNN +F 3 "" H 4080 2480 60 0000 C CNN + 1 4080 2480 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q16 +U 1 1 67790FAE +P 3980 2980 +F 0 "Q16" H 3880 3030 50 0000 R CNN +F 1 "eSim_NPN" H 3930 3130 50 0000 R CNN +F 2 "" H 4180 3080 29 0000 C CNN +F 3 "" H 3980 2980 60 0000 C CNN + 1 3980 2980 + 1 0 0 -1 +$EndComp +Wire Wire Line + 1990 1710 1990 1520 +Wire Wire Line + 1990 1180 1990 1220 +Wire Wire Line + 2350 1220 2350 1180 +Connection ~ 2350 1180 +Wire Wire Line + 2980 1220 2980 1180 +Connection ~ 2980 1180 +Wire Wire Line + 2350 1520 2350 2280 +Wire Wire Line + 2350 2280 1990 2280 +Wire Wire Line + 1990 2280 1990 2440 +Wire Wire Line + 2490 2010 2190 2010 +Wire Wire Line + 2490 2740 2190 2740 +Wire Wire Line + 2790 2540 2790 2500 +Wire Wire Line + 2790 2500 2980 2500 +Wire Wire Line + 2980 2500 2980 1520 +Wire Wire Line + 3020 2010 2980 2010 +Connection ~ 2980 2010 +Wire Wire Line + 2790 1810 2790 1720 +Wire Wire Line + 2790 1720 2980 1720 +Connection ~ 2980 1720 +Wire Wire Line + 3150 2980 2790 2980 +Wire Wire Line + 2790 2980 2790 2940 +Wire Wire Line + 2790 2210 2790 2400 +Wire Wire Line + 2790 2400 2910 2400 +Wire Wire Line + 2910 2400 2910 2980 +Connection ~ 2910 2980 +Wire Wire Line + 2990 3370 2990 2980 +Connection ~ 2990 2980 +Wire Wire Line + 3320 2010 3450 2010 +Wire Wire Line + 3450 2010 3450 2780 +Connection ~ 3450 2510 +Wire Wire Line + 3750 2310 3750 1520 +Wire Wire Line + 3750 1220 3750 1180 +Connection ~ 3750 1180 +Wire Wire Line + 4210 1180 4210 1220 +Wire Wire Line + 3780 2010 3750 2010 +Connection ~ 3750 2010 +Wire Wire Line + 4080 1810 4210 1810 +Wire Wire Line + 4210 1810 4210 1520 +Wire Wire Line + 3750 2710 3750 3360 +Wire Wire Line + 3750 2980 3780 2980 +Wire Wire Line + 4080 2630 4080 2780 +Wire Wire Line + 4080 2210 4080 2330 +Connection ~ 3750 2980 +Wire Wire Line + 3750 3760 3750 3660 +Wire Wire Line + 1610 3760 1610 3670 +Wire Wire Line + 1330 3760 1330 3380 +Connection ~ 1610 3760 +Wire Wire Line + 2990 3670 2990 3760 +Connection ~ 2990 3760 +Wire Wire Line + 3450 3180 3450 3760 +Connection ~ 3450 3760 +Wire Wire Line + 4080 3760 4080 3180 +Connection ~ 3750 3760 +Wire Wire Line + 3900 3770 3900 3760 +Connection ~ 3900 3760 +Wire Wire Line + 1790 2740 1110 2740 +Wire Wire Line + 1790 2010 1110 2010 +Wire Wire Line + 1330 3080 1330 2010 +Connection ~ 1330 2010 +Wire Wire Line + 1610 3370 1610 2740 +Connection ~ 1610 2740 +Connection ~ 4210 1180 +Wire Wire Line + 4210 2710 4080 2710 +Connection ~ 4080 2710 +Connection ~ 4080 3760 +Wire Wire Line + 1330 3760 10360 3760 +Wire Wire Line + 1990 1180 9700 1180 +$Comp +L eSim_NPN Q29 +U 1 1 677B2750 +P 9700 1910 +F 0 "Q29" H 9600 1960 50 0000 R CNN +F 1 "eSim_NPN" H 9650 2060 50 0000 R CNN +F 2 "" H 9900 2010 29 0000 C CNN +F 3 "" H 9700 1910 60 0000 C CNN + 1 9700 1910 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q30 +U 1 1 677B2756 +P 9700 2640 +F 0 "Q30" H 9600 2690 50 0000 R CNN +F 1 "eSim_NPN" H 9650 2790 50 0000 R CNN +F 2 "" H 9900 2740 29 0000 C CNN +F 3 "" H 9700 2640 60 0000 C CNN + 1 9700 2640 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q25 +U 1 1 677B275C +P 9000 2010 +F 0 "Q25" H 8900 2060 50 0000 R CNN +F 1 "eSim_NPN" H 8950 2160 50 0000 R CNN +F 2 "" H 9200 2110 29 0000 C CNN +F 3 "" H 9000 2010 60 0000 C CNN + 1 9000 2010 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q26 +U 1 1 677B2762 +P 9000 2740 +F 0 "Q26" H 8900 2790 50 0000 R CNN +F 1 "eSim_NPN" H 8950 2890 50 0000 R CNN +F 2 "" H 9200 2840 29 0000 C CNN +F 3 "" H 9000 2740 60 0000 C CNN + 1 9000 2740 + -1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D11 +U 1 1 677B2768 +P 8520 2010 +F 0 "D11" H 8520 2110 50 0000 C CNN +F 1 "eSim_Diode" H 8520 1910 50 0000 C CNN +F 2 "" H 8520 2010 60 0000 C CNN +F 3 "" H 8520 2010 60 0000 C CNN + 1 8520 2010 + -1 0 0 -1 +$EndComp +$Comp +L resistor R27 +U 1 1 677B276E +P 9750 1320 +F 0 "R27" H 9800 1450 50 0000 C CNN +F 1 "4k" H 9800 1270 50 0000 C CNN +F 2 "" H 9800 1300 30 0000 C CNN +F 3 "" V 9800 1370 30 0000 C CNN + 1 9750 1320 + 0 -1 1 0 +$EndComp +$Comp +L resistor R25 +U 1 1 677B2774 +P 9390 1320 +F 0 "R25" H 9440 1450 50 0000 C CNN +F 1 "2k" H 9440 1270 50 0000 C CNN +F 2 "" H 9440 1300 30 0000 C CNN +F 3 "" V 9440 1370 30 0000 C CNN + 1 9390 1320 + 0 -1 1 0 +$EndComp +$Comp +L resistor R22 +U 1 1 677B277A +P 8760 1320 +F 0 "R22" H 8810 1450 50 0000 C CNN +F 1 "2.5k" H 8810 1270 50 0000 C CNN +F 2 "" H 8810 1300 30 0000 C CNN +F 3 "" V 8810 1370 30 0000 C CNN + 1 8760 1320 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q23 +U 1 1 677B2780 +P 8340 2980 +F 0 "Q23" H 8240 3030 50 0000 R CNN +F 1 "eSim_NPN" H 8290 3130 50 0000 R CNN +F 2 "" H 8540 3080 29 0000 C CNN +F 3 "" H 8340 2980 60 0000 C CNN + 1 8340 2980 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q21 +U 1 1 677B2786 +P 8040 2510 +F 0 "Q21" H 7940 2560 50 0000 R CNN +F 1 "eSim_NPN" H 7990 2660 50 0000 R CNN +F 2 "" H 8240 2610 29 0000 C CNN +F 3 "" H 8040 2510 60 0000 C CNN + 1 8040 2510 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q17 +U 1 1 677B278C +P 7710 2010 +F 0 "Q17" H 7610 2060 50 0000 R CNN +F 1 "eSim_NPN" H 7660 2160 50 0000 R CNN +F 2 "" H 7910 2110 29 0000 C CNN +F 3 "" H 7710 2010 60 0000 C CNN + 1 7710 2010 + -1 0 0 -1 +$EndComp +$Comp +L resistor R17 +U 1 1 677B2792 +P 7990 1320 +F 0 "R17" H 8040 1450 50 0000 C CNN +F 1 "1.6k" H 8040 1270 50 0000 C CNN +F 2 "" H 8040 1300 30 0000 C CNN +F 3 "" V 8040 1370 30 0000 C CNN + 1 7990 1320 + 0 -1 1 0 +$EndComp +$Comp +L resistor R15 +U 1 1 677B2798 +P 7530 1320 +F 0 "R15" H 7580 1450 50 0000 C CNN +F 1 "130" H 7580 1270 50 0000 C CNN +F 2 "" H 7580 1300 30 0000 C CNN +F 3 "" V 7580 1370 30 0000 C CNN + 1 7530 1320 + 0 -1 1 0 +$EndComp +$Comp +L eSim_Diode D13 +U 1 1 677B279E +P 10080 3520 +F 0 "D13" H 10080 3620 50 0000 C CNN +F 1 "eSim_Diode" H 10080 3420 50 0000 C CNN +F 2 "" H 10080 3520 60 0000 C CNN +F 3 "" H 10080 3520 60 0000 C CNN + 1 10080 3520 + 0 1 -1 0 +$EndComp +$Comp +L eSim_Diode D15 +U 1 1 677B27A4 +P 10360 3230 +F 0 "D15" H 10360 3330 50 0000 C CNN +F 1 "eSim_Diode" H 10360 3130 50 0000 C CNN +F 2 "" H 10360 3230 60 0000 C CNN +F 3 "" H 10360 3230 60 0000 C CNN + 1 10360 3230 + 0 1 -1 0 +$EndComp +$Comp +L resistor R21 +U 1 1 677B27AA +P 8750 3470 +F 0 "R21" H 8800 3600 50 0000 C CNN +F 1 "1k" H 8800 3420 50 0000 C CNN +F 2 "" H 8800 3450 30 0000 C CNN +F 3 "" V 8800 3520 30 0000 C CNN + 1 8750 3470 + 0 -1 1 0 +$EndComp +$Comp +L resistor R18 +U 1 1 677B27B0 +P 7990 3460 +F 0 "R18" H 8040 3590 50 0000 C CNN +F 1 "1k" H 8040 3410 50 0000 C CNN +F 2 "" H 8040 3440 30 0000 C CNN +F 3 "" V 8040 3510 30 0000 C CNN + 1 7990 3460 + 0 -1 1 0 +$EndComp +$Comp +L GNDPWR #PWR3 +U 1 1 677B27B6 +P 7790 3770 +F 0 "#PWR3" H 7790 3570 50 0001 C CNN +F 1 "GNDPWR" H 7790 3640 50 0000 C CNN +F 2 "" H 7790 3720 50 0001 C CNN +F 3 "" H 7790 3720 50 0001 C CNN + 1 7790 3770 + -1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D9 +U 1 1 677B27BC +P 7610 2480 +F 0 "D9" H 7610 2580 50 0000 C CNN +F 1 "eSim_Diode" H 7610 2380 50 0000 C CNN +F 2 "" H 7610 2480 60 0000 C CNN +F 3 "" H 7610 2480 60 0000 C CNN + 1 7610 2480 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q18 +U 1 1 677B27C2 +P 7710 2980 +F 0 "Q18" H 7610 3030 50 0000 R CNN +F 1 "eSim_NPN" H 7660 3130 50 0000 R CNN +F 2 "" H 7910 3080 29 0000 C CNN +F 3 "" H 7710 2980 60 0000 C CNN + 1 7710 2980 + -1 0 0 -1 +$EndComp +Wire Wire Line + 9700 1710 9700 1520 +Wire Wire Line + 9700 1180 9700 1220 +Wire Wire Line + 9340 1220 9340 1180 +Connection ~ 9340 1180 +Wire Wire Line + 8710 1220 8710 1180 +Connection ~ 8710 1180 +Wire Wire Line + 9340 1520 9340 2280 +Wire Wire Line + 9340 2280 9700 2280 +Wire Wire Line + 9700 2280 9700 2440 +Wire Wire Line + 9200 2010 9500 2010 +Wire Wire Line + 9200 2740 9500 2740 +Wire Wire Line + 8900 2540 8900 2500 +Wire Wire Line + 8900 2500 8710 2500 +Wire Wire Line + 8710 2500 8710 1520 +Wire Wire Line + 8670 2010 8710 2010 +Connection ~ 8710 2010 +Wire Wire Line + 8900 1810 8900 1720 +Wire Wire Line + 8900 1720 8710 1720 +Connection ~ 8710 1720 +Wire Wire Line + 8540 2980 8900 2980 +Wire Wire Line + 8900 2980 8900 2940 +Wire Wire Line + 8900 2210 8900 2400 +Wire Wire Line + 8900 2400 8780 2400 +Wire Wire Line + 8780 2400 8780 2980 +Connection ~ 8780 2980 +Wire Wire Line + 8700 3370 8700 2980 +Connection ~ 8700 2980 +Wire Wire Line + 8370 2010 8240 2010 +Wire Wire Line + 8240 2010 8240 2780 +Connection ~ 8240 2510 +Wire Wire Line + 7940 2310 7940 1520 +Wire Wire Line + 7940 1220 7940 1180 +Connection ~ 7940 1180 +Wire Wire Line + 7480 1180 7480 1220 +Wire Wire Line + 7910 2010 7940 2010 +Connection ~ 7940 2010 +Wire Wire Line + 7610 1810 7480 1810 +Wire Wire Line + 7480 1810 7480 1520 +Wire Wire Line + 7940 2710 7940 3360 +Wire Wire Line + 7940 2980 7910 2980 +Wire Wire Line + 7610 2630 7610 2780 +Wire Wire Line + 7610 2210 7610 2330 +Connection ~ 7940 2980 +Wire Wire Line + 7940 3760 7940 3660 +Wire Wire Line + 10080 3760 10080 3670 +Wire Wire Line + 10360 3760 10360 3380 +Connection ~ 10080 3760 +Wire Wire Line + 8700 3670 8700 3760 +Connection ~ 8700 3760 +Wire Wire Line + 8240 3180 8240 3760 +Connection ~ 8240 3760 +Wire Wire Line + 7610 3760 7610 3180 +Connection ~ 7940 3760 +Wire Wire Line + 7790 3770 7790 3760 +Connection ~ 7790 3760 +Wire Wire Line + 9900 2740 10580 2740 +Wire Wire Line + 9900 2010 10580 2010 +Wire Wire Line + 10360 3080 10360 2010 +Connection ~ 10360 2010 +Wire Wire Line + 10080 3370 10080 2740 +Connection ~ 10080 2740 +Connection ~ 7480 1180 +Wire Wire Line + 7480 2710 7610 2710 +Connection ~ 7610 2710 +Connection ~ 7610 3760 +$Comp +L eSim_NPN Q1 +U 1 1 677B410D +P 1980 5260 +F 0 "Q1" H 1880 5310 50 0000 R CNN +F 1 "eSim_NPN" H 1930 5410 50 0000 R CNN +F 2 "" H 2180 5360 29 0000 C CNN +F 3 "" H 1980 5260 60 0000 C CNN + 1 1980 5260 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q2 +U 1 1 677B4113 +P 1980 5990 +F 0 "Q2" H 1880 6040 50 0000 R CNN +F 1 "eSim_NPN" H 1930 6140 50 0000 R CNN +F 2 "" H 2180 6090 29 0000 C CNN +F 3 "" H 1980 5990 60 0000 C CNN + 1 1980 5990 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q5 +U 1 1 677B4119 +P 2680 5360 +F 0 "Q5" H 2580 5410 50 0000 R CNN +F 1 "eSim_NPN" H 2630 5510 50 0000 R CNN +F 2 "" H 2880 5460 29 0000 C CNN +F 3 "" H 2680 5360 60 0000 C CNN + 1 2680 5360 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q6 +U 1 1 677B411F +P 2680 6090 +F 0 "Q6" H 2580 6140 50 0000 R CNN +F 1 "eSim_NPN" H 2630 6240 50 0000 R CNN +F 2 "" H 2880 6190 29 0000 C CNN +F 3 "" H 2680 6090 60 0000 C CNN + 1 2680 6090 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D5 +U 1 1 677B4125 +P 3160 5360 +F 0 "D5" H 3160 5460 50 0000 C CNN +F 1 "eSim_Diode" H 3160 5260 50 0000 C CNN +F 2 "" H 3160 5360 60 0000 C CNN +F 3 "" H 3160 5360 60 0000 C CNN + 1 3160 5360 + 1 0 0 -1 +$EndComp +$Comp +L resistor R1 +U 1 1 677B412B +P 1930 4670 +F 0 "R1" H 1980 4800 50 0000 C CNN +F 1 "4k" H 1980 4620 50 0000 C CNN +F 2 "" H 1980 4650 30 0000 C CNN +F 3 "" V 1980 4720 30 0000 C CNN + 1 1930 4670 + 0 1 1 0 +$EndComp +$Comp +L resistor R3 +U 1 1 677B4131 +P 2290 4670 +F 0 "R3" H 2340 4800 50 0000 C CNN +F 1 "2k" H 2340 4620 50 0000 C CNN +F 2 "" H 2340 4650 30 0000 C CNN +F 3 "" V 2340 4720 30 0000 C CNN + 1 2290 4670 + 0 1 1 0 +$EndComp +$Comp +L resistor R5 +U 1 1 677B4137 +P 2920 4670 +F 0 "R5" H 2970 4800 50 0000 C CNN +F 1 "2.5k" H 2970 4620 50 0000 C CNN +F 2 "" H 2970 4650 30 0000 C CNN +F 3 "" V 2970 4720 30 0000 C CNN + 1 2920 4670 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q9 +U 1 1 677B413D +P 3340 6330 +F 0 "Q9" H 3240 6380 50 0000 R CNN +F 1 "eSim_NPN" H 3290 6480 50 0000 R CNN +F 2 "" H 3540 6430 29 0000 C CNN +F 3 "" H 3340 6330 60 0000 C CNN + 1 3340 6330 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q11 +U 1 1 677B4143 +P 3640 5860 +F 0 "Q11" H 3540 5910 50 0000 R CNN +F 1 "eSim_NPN" H 3590 6010 50 0000 R CNN +F 2 "" H 3840 5960 29 0000 C CNN +F 3 "" H 3640 5860 60 0000 C CNN + 1 3640 5860 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q13 +U 1 1 677B4149 +P 3970 5360 +F 0 "Q13" H 3870 5410 50 0000 R CNN +F 1 "eSim_NPN" H 3920 5510 50 0000 R CNN +F 2 "" H 4170 5460 29 0000 C CNN +F 3 "" H 3970 5360 60 0000 C CNN + 1 3970 5360 + 1 0 0 -1 +$EndComp +$Comp +L resistor R9 +U 1 1 677B414F +P 3690 4670 +F 0 "R9" H 3740 4800 50 0000 C CNN +F 1 "1.6k" H 3740 4620 50 0000 C CNN +F 2 "" H 3740 4650 30 0000 C CNN +F 3 "" V 3740 4720 30 0000 C CNN + 1 3690 4670 + 0 1 1 0 +$EndComp +$Comp +L resistor R13 +U 1 1 677B4155 +P 4150 4670 +F 0 "R13" H 4200 4800 50 0000 C CNN +F 1 "130" H 4200 4620 50 0000 C CNN +F 2 "" H 4200 4650 30 0000 C CNN +F 3 "" V 4200 4720 30 0000 C CNN + 1 4150 4670 + 0 1 1 0 +$EndComp +$Comp +L eSim_Diode D3 +U 1 1 677B415B +P 1600 6870 +F 0 "D3" H 1600 6970 50 0000 C CNN +F 1 "eSim_Diode" H 1600 6770 50 0000 C CNN +F 2 "" H 1600 6870 60 0000 C CNN +F 3 "" H 1600 6870 60 0000 C CNN + 1 1600 6870 + 0 -1 -1 0 +$EndComp +$Comp +L eSim_Diode D1 +U 1 1 677B4161 +P 1320 6580 +F 0 "D1" H 1320 6680 50 0000 C CNN +F 1 "eSim_Diode" H 1320 6480 50 0000 C CNN +F 2 "" H 1320 6580 60 0000 C CNN +F 3 "" H 1320 6580 60 0000 C CNN + 1 1320 6580 + 0 -1 -1 0 +$EndComp +$Comp +L resistor R6 +U 1 1 677B4167 +P 2930 6820 +F 0 "R6" H 2980 6950 50 0000 C CNN +F 1 "1k" H 2980 6770 50 0000 C CNN +F 2 "" H 2980 6800 30 0000 C CNN +F 3 "" V 2980 6870 30 0000 C CNN + 1 2930 6820 + 0 1 1 0 +$EndComp +$Comp +L resistor R10 +U 1 1 677B416D +P 3690 6810 +F 0 "R10" H 3740 6940 50 0000 C CNN +F 1 "1k" H 3740 6760 50 0000 C CNN +F 2 "" H 3740 6790 30 0000 C CNN +F 3 "" V 3740 6860 30 0000 C CNN + 1 3690 6810 + 0 1 1 0 +$EndComp +$Comp +L GNDPWR #PWR1 +U 1 1 677B4173 +P 3890 7120 +F 0 "#PWR1" H 3890 6920 50 0001 C CNN +F 1 "GNDPWR" H 3890 6990 50 0000 C CNN +F 2 "" H 3890 7070 50 0001 C CNN +F 3 "" H 3890 7070 50 0001 C CNN + 1 3890 7120 + 1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D7 +U 1 1 677B4179 +P 4070 5830 +F 0 "D7" H 4070 5930 50 0000 C CNN +F 1 "eSim_Diode" H 4070 5730 50 0000 C CNN +F 2 "" H 4070 5830 60 0000 C CNN +F 3 "" H 4070 5830 60 0000 C CNN + 1 4070 5830 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q14 +U 1 1 677B417F +P 3970 6330 +F 0 "Q14" H 3870 6380 50 0000 R CNN +F 1 "eSim_NPN" H 3920 6480 50 0000 R CNN +F 2 "" H 4170 6430 29 0000 C CNN +F 3 "" H 3970 6330 60 0000 C CNN + 1 3970 6330 + 1 0 0 -1 +$EndComp +Wire Wire Line + 1980 5060 1980 4870 +Wire Wire Line + 1980 4530 1980 4570 +Wire Wire Line + 2340 4570 2340 4530 +Connection ~ 2340 4530 +Wire Wire Line + 2970 4570 2970 4530 +Connection ~ 2970 4530 +Wire Wire Line + 2340 4870 2340 5630 +Wire Wire Line + 2340 5630 1980 5630 +Wire Wire Line + 1980 5630 1980 5790 +Wire Wire Line + 2480 5360 2180 5360 +Wire Wire Line + 2480 6090 2180 6090 +Wire Wire Line + 2780 5890 2780 5850 +Wire Wire Line + 2780 5850 2970 5850 +Wire Wire Line + 2970 5850 2970 4870 +Wire Wire Line + 3010 5360 2970 5360 +Connection ~ 2970 5360 +Wire Wire Line + 2780 5160 2780 5070 +Wire Wire Line + 2780 5070 2970 5070 +Connection ~ 2970 5070 +Wire Wire Line + 3140 6330 2780 6330 +Wire Wire Line + 2780 6330 2780 6290 +Wire Wire Line + 2780 5560 2780 5750 +Wire Wire Line + 2780 5750 2900 5750 +Wire Wire Line + 2900 5750 2900 6330 +Connection ~ 2900 6330 +Wire Wire Line + 2980 6720 2980 6330 +Connection ~ 2980 6330 +Wire Wire Line + 3310 5360 3440 5360 +Wire Wire Line + 3440 5360 3440 6130 +Connection ~ 3440 5860 +Wire Wire Line + 3740 5660 3740 4870 +Wire Wire Line + 3740 4570 3740 4530 +Connection ~ 3740 4530 +Wire Wire Line + 4200 4530 4200 4570 +Wire Wire Line + 3770 5360 3740 5360 +Connection ~ 3740 5360 +Wire Wire Line + 4070 5160 4200 5160 +Wire Wire Line + 4200 5160 4200 4870 +Wire Wire Line + 3740 6060 3740 6710 +Wire Wire Line + 3740 6330 3770 6330 +Wire Wire Line + 4070 5980 4070 6130 +Wire Wire Line + 4070 5560 4070 5680 +Connection ~ 3740 6330 +Wire Wire Line + 3740 7110 3740 7010 +Wire Wire Line + 1600 7110 1600 7020 +Wire Wire Line + 1320 7110 1320 6730 +Connection ~ 1600 7110 +Wire Wire Line + 2980 7020 2980 7110 +Connection ~ 2980 7110 +Wire Wire Line + 3440 6530 3440 7110 +Connection ~ 3440 7110 +Wire Wire Line + 4070 7110 4070 6530 +Connection ~ 3740 7110 +Wire Wire Line + 3890 7120 3890 7110 +Connection ~ 3890 7110 +Wire Wire Line + 1780 6090 1100 6090 +Wire Wire Line + 1780 5360 1100 5360 +Wire Wire Line + 1320 6430 1320 5360 +Connection ~ 1320 5360 +Wire Wire Line + 1600 6720 1600 6090 +Connection ~ 1600 6090 +Connection ~ 4200 4530 +Wire Wire Line + 4200 6060 4070 6060 +Connection ~ 4070 6060 +Connection ~ 4070 7110 +Wire Wire Line + 1320 7110 6460 7110 +Wire Wire Line + 6110 4530 1980 4530 +$Comp +L eSim_NPN Q31 +U 1 1 677B500C +P 9750 5000 +F 0 "Q31" H 9650 5050 50 0000 R CNN +F 1 "eSim_NPN" H 9700 5150 50 0000 R CNN +F 2 "" H 9950 5100 29 0000 C CNN +F 3 "" H 9750 5000 60 0000 C CNN + 1 9750 5000 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q32 +U 1 1 677B5012 +P 9750 5730 +F 0 "Q32" H 9650 5780 50 0000 R CNN +F 1 "eSim_NPN" H 9700 5880 50 0000 R CNN +F 2 "" H 9950 5830 29 0000 C CNN +F 3 "" H 9750 5730 60 0000 C CNN + 1 9750 5730 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q27 +U 1 1 677B5018 +P 9050 5100 +F 0 "Q27" H 8950 5150 50 0000 R CNN +F 1 "eSim_NPN" H 9000 5250 50 0000 R CNN +F 2 "" H 9250 5200 29 0000 C CNN +F 3 "" H 9050 5100 60 0000 C CNN + 1 9050 5100 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q28 +U 1 1 677B501E +P 9050 5830 +F 0 "Q28" H 8950 5880 50 0000 R CNN +F 1 "eSim_NPN" H 9000 5980 50 0000 R CNN +F 2 "" H 9250 5930 29 0000 C CNN +F 3 "" H 9050 5830 60 0000 C CNN + 1 9050 5830 + -1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D12 +U 1 1 677B5024 +P 8570 5100 +F 0 "D12" H 8570 5200 50 0000 C CNN +F 1 "eSim_Diode" H 8570 5000 50 0000 C CNN +F 2 "" H 8570 5100 60 0000 C CNN +F 3 "" H 8570 5100 60 0000 C CNN + 1 8570 5100 + -1 0 0 -1 +$EndComp +$Comp +L resistor R28 +U 1 1 677B502A +P 9800 4410 +F 0 "R28" H 9850 4540 50 0000 C CNN +F 1 "4k" H 9850 4360 50 0000 C CNN +F 2 "" H 9850 4390 30 0000 C CNN +F 3 "" V 9850 4460 30 0000 C CNN + 1 9800 4410 + 0 -1 1 0 +$EndComp +$Comp +L resistor R26 +U 1 1 677B5030 +P 9440 4410 +F 0 "R26" H 9490 4540 50 0000 C CNN +F 1 "2k" H 9490 4360 50 0000 C CNN +F 2 "" H 9490 4390 30 0000 C CNN +F 3 "" V 9490 4460 30 0000 C CNN + 1 9440 4410 + 0 -1 1 0 +$EndComp +$Comp +L resistor R24 +U 1 1 677B5036 +P 8810 4410 +F 0 "R24" H 8860 4540 50 0000 C CNN +F 1 "2.5k" H 8860 4360 50 0000 C CNN +F 2 "" H 8860 4390 30 0000 C CNN +F 3 "" V 8860 4460 30 0000 C CNN + 1 8810 4410 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q24 +U 1 1 677B503C +P 8390 6070 +F 0 "Q24" H 8290 6120 50 0000 R CNN +F 1 "eSim_NPN" H 8340 6220 50 0000 R CNN +F 2 "" H 8590 6170 29 0000 C CNN +F 3 "" H 8390 6070 60 0000 C CNN + 1 8390 6070 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q22 +U 1 1 677B5042 +P 8090 5600 +F 0 "Q22" H 7990 5650 50 0000 R CNN +F 1 "eSim_NPN" H 8040 5750 50 0000 R CNN +F 2 "" H 8290 5700 29 0000 C CNN +F 3 "" H 8090 5600 60 0000 C CNN + 1 8090 5600 + -1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q19 +U 1 1 677B5048 +P 7760 5100 +F 0 "Q19" H 7660 5150 50 0000 R CNN +F 1 "eSim_NPN" H 7710 5250 50 0000 R CNN +F 2 "" H 7960 5200 29 0000 C CNN +F 3 "" H 7760 5100 60 0000 C CNN + 1 7760 5100 + -1 0 0 -1 +$EndComp +$Comp +L resistor R19 +U 1 1 677B504E +P 8040 4410 +F 0 "R19" H 8090 4540 50 0000 C CNN +F 1 "1.6k" H 8090 4360 50 0000 C CNN +F 2 "" H 8090 4390 30 0000 C CNN +F 3 "" V 8090 4460 30 0000 C CNN + 1 8040 4410 + 0 -1 1 0 +$EndComp +$Comp +L resistor R16 +U 1 1 677B5054 +P 7580 4410 +F 0 "R16" H 7630 4540 50 0000 C CNN +F 1 "130" H 7630 4360 50 0000 C CNN +F 2 "" H 7630 4390 30 0000 C CNN +F 3 "" V 7630 4460 30 0000 C CNN + 1 7580 4410 + 0 -1 1 0 +$EndComp +$Comp +L eSim_Diode D14 +U 1 1 677B505A +P 10130 6610 +F 0 "D14" H 10130 6710 50 0000 C CNN +F 1 "eSim_Diode" H 10130 6510 50 0000 C CNN +F 2 "" H 10130 6610 60 0000 C CNN +F 3 "" H 10130 6610 60 0000 C CNN + 1 10130 6610 + 0 1 -1 0 +$EndComp +$Comp +L eSim_Diode D16 +U 1 1 677B5060 +P 10410 6320 +F 0 "D16" H 10410 6420 50 0000 C CNN +F 1 "eSim_Diode" H 10410 6220 50 0000 C CNN +F 2 "" H 10410 6320 60 0000 C CNN +F 3 "" H 10410 6320 60 0000 C CNN + 1 10410 6320 + 0 1 -1 0 +$EndComp +$Comp +L resistor R23 +U 1 1 677B5066 +P 8800 6560 +F 0 "R23" H 8850 6690 50 0000 C CNN +F 1 "1k" H 8850 6510 50 0000 C CNN +F 2 "" H 8850 6540 30 0000 C CNN +F 3 "" V 8850 6610 30 0000 C CNN + 1 8800 6560 + 0 -1 1 0 +$EndComp +$Comp +L resistor R20 +U 1 1 677B506C +P 8040 6550 +F 0 "R20" H 8090 6680 50 0000 C CNN +F 1 "1k" H 8090 6500 50 0000 C CNN +F 2 "" H 8090 6530 30 0000 C CNN +F 3 "" V 8090 6600 30 0000 C CNN + 1 8040 6550 + 0 -1 1 0 +$EndComp +$Comp +L GNDPWR #PWR4 +U 1 1 677B5072 +P 7840 6860 +F 0 "#PWR4" H 7840 6660 50 0001 C CNN +F 1 "GNDPWR" H 7840 6730 50 0000 C CNN +F 2 "" H 7840 6810 50 0001 C CNN +F 3 "" H 7840 6810 50 0001 C CNN + 1 7840 6860 + -1 0 0 -1 +$EndComp +$Comp +L eSim_Diode D10 +U 1 1 677B5078 +P 7660 5570 +F 0 "D10" H 7660 5670 50 0000 C CNN +F 1 "eSim_Diode" H 7660 5470 50 0000 C CNN +F 2 "" H 7660 5570 60 0000 C CNN +F 3 "" H 7660 5570 60 0000 C CNN + 1 7660 5570 + 0 -1 1 0 +$EndComp +$Comp +L eSim_NPN Q20 +U 1 1 677B507E +P 7760 6070 +F 0 "Q20" H 7660 6120 50 0000 R CNN +F 1 "eSim_NPN" H 7710 6220 50 0000 R CNN +F 2 "" H 7960 6170 29 0000 C CNN +F 3 "" H 7760 6070 60 0000 C CNN + 1 7760 6070 + -1 0 0 -1 +$EndComp +Wire Wire Line + 9750 4800 9750 4610 +Wire Wire Line + 9750 4270 9750 4310 +Wire Wire Line + 9390 4310 9390 4270 +Connection ~ 9390 4270 +Wire Wire Line + 8760 4310 8760 4270 +Connection ~ 8760 4270 +Wire Wire Line + 9390 4610 9390 5370 +Wire Wire Line + 9390 5370 9750 5370 +Wire Wire Line + 9750 5370 9750 5530 +Wire Wire Line + 9250 5100 9550 5100 +Wire Wire Line + 9250 5830 9550 5830 +Wire Wire Line + 8950 5630 8950 5590 +Wire Wire Line + 8950 5590 8760 5590 +Wire Wire Line + 8760 5590 8760 4610 +Wire Wire Line + 8720 5100 8760 5100 +Connection ~ 8760 5100 +Wire Wire Line + 8950 4900 8950 4810 +Wire Wire Line + 8950 4810 8760 4810 +Connection ~ 8760 4810 +Wire Wire Line + 8590 6070 8950 6070 +Wire Wire Line + 8950 6070 8950 6030 +Wire Wire Line + 8950 5300 8950 5490 +Wire Wire Line + 8950 5490 8830 5490 +Wire Wire Line + 8830 5490 8830 6070 +Connection ~ 8830 6070 +Wire Wire Line + 8750 6460 8750 6070 +Connection ~ 8750 6070 +Wire Wire Line + 8420 5100 8290 5100 +Wire Wire Line + 8290 5100 8290 5870 +Connection ~ 8290 5600 +Wire Wire Line + 7990 5400 7990 4610 +Wire Wire Line + 7990 4310 7990 4270 +Connection ~ 7990 4270 +Wire Wire Line + 7530 4270 7530 4310 +Wire Wire Line + 7960 5100 7990 5100 +Connection ~ 7990 5100 +Wire Wire Line + 7660 4900 7530 4900 +Wire Wire Line + 7530 4900 7530 4610 +Wire Wire Line + 7990 5800 7990 6450 +Wire Wire Line + 7990 6070 7960 6070 +Wire Wire Line + 7660 5720 7660 5870 +Wire Wire Line + 7660 5300 7660 5420 +Connection ~ 7990 6070 +Wire Wire Line + 7990 6850 7990 6750 +Wire Wire Line + 10130 6850 10130 6760 +Wire Wire Line + 10410 6850 10410 6470 +Connection ~ 10130 6850 +Wire Wire Line + 8750 6760 8750 6850 +Connection ~ 8750 6850 +Wire Wire Line + 8290 6270 8290 6850 +Connection ~ 8290 6850 +Wire Wire Line + 7660 6850 7660 6270 +Connection ~ 7990 6850 +Wire Wire Line + 7840 6860 7840 6850 +Connection ~ 7840 6850 +Wire Wire Line + 9950 5830 10630 5830 +Wire Wire Line + 9950 5100 10630 5100 +Wire Wire Line + 10410 6170 10410 5100 +Connection ~ 10410 5100 +Wire Wire Line + 10130 6460 10130 5830 +Connection ~ 10130 5830 +Connection ~ 7530 4270 +Wire Wire Line + 7530 5800 7660 5800 +Connection ~ 7660 5800 +Connection ~ 7660 6850 +Wire Wire Line + 6460 6850 10410 6850 +Wire Wire Line + 6110 4270 9750 4270 +Wire Wire Line + 6110 880 6110 4530 +Connection ~ 6110 1180 +Connection ~ 6110 4270 +Wire Wire Line + 6460 7110 6460 880 +Connection ~ 6460 3760 +Connection ~ 6460 6850 +$Comp +L PORT U1 +U 7 1 678183F3 +P 6110 630 +F 0 "U1" H 6160 730 30 0000 C CNN +F 1 "PORT" H 6110 630 30 0000 C CNN +F 2 "" H 6110 630 60 0000 C CNN +F 3 "" H 6110 630 60 0000 C CNN + 7 6110 630 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 8 1 678184F0 +P 6460 630 +F 0 "U1" H 6510 730 30 0000 C CNN +F 1 "PORT" H 6460 630 30 0000 C CNN +F 2 "" H 6460 630 60 0000 C CNN +F 3 "" H 6460 630 60 0000 C CNN + 8 6460 630 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 3 1 67818995 +P 860 2010 +F 0 "U1" H 910 2110 30 0000 C CNN +F 1 "PORT" H 860 2010 30 0000 C CNN +F 2 "" H 860 2010 60 0000 C CNN +F 3 "" H 860 2010 60 0000 C CNN + 3 860 2010 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 4 1 67818E3A +P 860 2740 +F 0 "U1" H 910 2840 30 0000 C CNN +F 1 "PORT" H 860 2740 30 0000 C CNN +F 2 "" H 860 2740 60 0000 C CNN +F 3 "" H 860 2740 60 0000 C CNN + 4 860 2740 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 6 1 678191F5 +P 4460 2710 +F 0 "U1" H 4510 2810 30 0000 C CNN +F 1 "PORT" H 4460 2710 30 0000 C CNN +F 2 "" H 4460 2710 60 0000 C CNN +F 3 "" H 4460 2710 60 0000 C CNN + 6 4460 2710 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 1 1 6781989C +P 850 5360 +F 0 "U1" H 900 5460 30 0000 C CNN +F 1 "PORT" H 850 5360 30 0000 C CNN +F 2 "" H 850 5360 60 0000 C CNN +F 3 "" H 850 5360 60 0000 C CNN + 1 850 5360 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 2 1 67819C65 +P 850 6090 +F 0 "U1" H 900 6190 30 0000 C CNN +F 1 "PORT" H 850 6090 30 0000 C CNN +F 2 "" H 850 6090 60 0000 C CNN +F 3 "" H 850 6090 60 0000 C CNN + 2 850 6090 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 5 1 6781A132 +P 4450 6060 +F 0 "U1" H 4500 6160 30 0000 C CNN +F 1 "PORT" H 4450 6060 30 0000 C CNN +F 2 "" H 4450 6060 60 0000 C CNN +F 3 "" H 4450 6060 60 0000 C CNN + 5 4450 6060 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 9 1 6781A82F +P 7230 2710 +F 0 "U1" H 7280 2810 30 0000 C CNN +F 1 "PORT" H 7230 2710 30 0000 C CNN +F 2 "" H 7230 2710 60 0000 C CNN +F 3 "" H 7230 2710 60 0000 C CNN + 9 7230 2710 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 10 1 6781AECD +P 7280 5800 +F 0 "U1" H 7330 5900 30 0000 C CNN +F 1 "PORT" H 7280 5800 30 0000 C CNN +F 2 "" H 7280 5800 60 0000 C CNN +F 3 "" H 7280 5800 60 0000 C CNN + 10 7280 5800 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 11 1 6781B57A +P 10830 2010 +F 0 "U1" H 10880 2110 30 0000 C CNN +F 1 "PORT" H 10830 2010 30 0000 C CNN +F 2 "" H 10830 2010 60 0000 C CNN +F 3 "" H 10830 2010 60 0000 C CNN + 11 10830 2010 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 12 1 6781BA07 +P 10830 2740 +F 0 "U1" H 10880 2840 30 0000 C CNN +F 1 "PORT" H 10830 2740 30 0000 C CNN +F 2 "" H 10830 2740 60 0000 C CNN +F 3 "" H 10830 2740 60 0000 C CNN + 12 10830 2740 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 13 1 6781C0E4 +P 10880 5100 +F 0 "U1" H 10930 5200 30 0000 C CNN +F 1 "PORT" H 10880 5100 30 0000 C CNN +F 2 "" H 10880 5100 60 0000 C CNN +F 3 "" H 10880 5100 60 0000 C CNN + 13 10880 5100 + -1 0 0 1 +$EndComp +$Comp +L PORT U1 +U 14 1 6781C506 +P 10880 5830 +F 0 "U1" H 10930 5930 30 0000 C CNN +F 1 "PORT" H 10880 5830 30 0000 C CNN +F 2 "" H 10880 5830 60 0000 C CNN +F 3 "" H 10880 5830 60 0000 C CNN + 14 10880 5830 + -1 0 0 1 +$EndComp +$EndSCHEMATC diff --git a/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.sub b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.sub new file mode 100644 index 000000000..1d1d12c94 --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit.sub @@ -0,0 +1,84 @@ +* Subcircuit SN7432_Subcirrcuit +.subckt SN7432_Subcirrcuit net-_d1-pad2_ net-_d3-pad2_ net-_d2-pad2_ net-_d4-pad2_ net-_d7-pad2_ net-_d8-pad2_ net-_r1-pad1_ gndpwr net-_d9-pad2_ net-_d10-pad2_ net-_d15-pad2_ net-_d13-pad2_ net-_d16-pad2_ net-_d14-pad2_ +* c:\fossee\esim\library\subcircuitlibrary\sn7432_subcirrcuit\sn7432_subcirrcuit.cir +.include NPN.lib +.include D.lib +q3 net-_q3-pad1_ net-_q3-pad2_ net-_d2-pad2_ Q2N2222 +q4 net-_q4-pad1_ net-_q4-pad2_ net-_d4-pad2_ Q2N2222 +q7 net-_d6-pad1_ net-_q3-pad1_ net-_q10-pad2_ Q2N2222 +q8 net-_d6-pad1_ net-_q4-pad1_ net-_q10-pad2_ Q2N2222 +d6 net-_d6-pad1_ net-_d6-pad2_ 1N4148 +r2 net-_r1-pad1_ net-_q3-pad2_ 4k +r4 net-_r1-pad1_ net-_q4-pad2_ 2k +r7 net-_r1-pad1_ net-_d6-pad1_ 2.5k +q10 net-_d6-pad2_ net-_q10-pad2_ gndpwr Q2N2222 +q12 net-_q12-pad1_ net-_d6-pad2_ net-_q12-pad3_ Q2N2222 +q15 net-_q15-pad1_ net-_q12-pad1_ net-_d8-pad1_ Q2N2222 +r11 net-_r1-pad1_ net-_q12-pad1_ 1.6k +r14 net-_r1-pad1_ net-_q15-pad1_ 130 +d4 gndpwr net-_d4-pad2_ 1N4148 +d2 gndpwr net-_d2-pad2_ 1N4148 +r8 net-_q10-pad2_ gndpwr 1k +r12 net-_q12-pad3_ gndpwr 1k +d8 net-_d8-pad1_ net-_d8-pad2_ 1N4148 +q16 net-_d8-pad2_ net-_q12-pad3_ gndpwr Q2N2222 +q29 net-_q25-pad2_ net-_q29-pad2_ net-_d15-pad2_ Q2N2222 +q30 net-_q26-pad2_ net-_q30-pad2_ net-_d13-pad2_ Q2N2222 +q25 net-_d11-pad1_ net-_q25-pad2_ net-_q23-pad2_ Q2N2222 +q26 net-_d11-pad1_ net-_q26-pad2_ net-_q23-pad2_ Q2N2222 +d11 net-_d11-pad1_ net-_d11-pad2_ 1N4148 +r27 net-_r1-pad1_ net-_q29-pad2_ 4k +r25 net-_r1-pad1_ net-_q30-pad2_ 2k +r22 net-_r1-pad1_ net-_d11-pad1_ 2.5k +q23 net-_d11-pad2_ net-_q23-pad2_ gndpwr Q2N2222 +q21 net-_q17-pad2_ net-_d11-pad2_ net-_q18-pad2_ Q2N2222 +q17 net-_q17-pad1_ net-_q17-pad2_ net-_d9-pad1_ Q2N2222 +r17 net-_r1-pad1_ net-_q17-pad2_ 1.6k +r15 net-_r1-pad1_ net-_q17-pad1_ 130 +d13 gndpwr net-_d13-pad2_ 1N4148 +d15 gndpwr net-_d15-pad2_ 1N4148 +r21 net-_q23-pad2_ gndpwr 1k +r18 net-_q18-pad2_ gndpwr 1k +d9 net-_d9-pad1_ net-_d9-pad2_ 1N4148 +q18 net-_d9-pad2_ net-_q18-pad2_ gndpwr Q2N2222 +q1 net-_q1-pad1_ net-_q1-pad2_ net-_d1-pad2_ Q2N2222 +q2 net-_q2-pad1_ net-_q2-pad2_ net-_d3-pad2_ Q2N2222 +q5 net-_d5-pad1_ net-_q1-pad1_ net-_q5-pad3_ Q2N2222 +q6 net-_d5-pad1_ net-_q2-pad1_ net-_q5-pad3_ Q2N2222 +d5 net-_d5-pad1_ net-_d5-pad2_ 1N4148 +r1 net-_r1-pad1_ net-_q1-pad2_ 4k +r3 net-_r1-pad1_ net-_q2-pad2_ 2k +r5 net-_r1-pad1_ net-_d5-pad1_ 2.5k +q9 net-_d5-pad2_ net-_q5-pad3_ gndpwr Q2N2222 +q11 net-_q11-pad1_ net-_d5-pad2_ net-_q11-pad3_ Q2N2222 +q13 net-_q13-pad1_ net-_q11-pad1_ net-_d7-pad1_ Q2N2222 +r9 net-_r1-pad1_ net-_q11-pad1_ 1.6k +r13 net-_r1-pad1_ net-_q13-pad1_ 130 +d3 gndpwr net-_d3-pad2_ 1N4148 +d1 gndpwr net-_d1-pad2_ 1N4148 +r6 net-_q5-pad3_ gndpwr 1k +r10 net-_q11-pad3_ gndpwr 1k +d7 net-_d7-pad1_ net-_d7-pad2_ 1N4148 +q14 net-_d7-pad2_ net-_q11-pad3_ gndpwr Q2N2222 +q31 net-_q27-pad2_ net-_q31-pad2_ net-_d16-pad2_ Q2N2222 +q32 net-_q28-pad2_ net-_q32-pad2_ net-_d14-pad2_ Q2N2222 +q27 net-_d12-pad1_ net-_q27-pad2_ net-_q24-pad2_ Q2N2222 +q28 net-_d12-pad1_ net-_q28-pad2_ net-_q24-pad2_ Q2N2222 +d12 net-_d12-pad1_ net-_d12-pad2_ 1N4148 +r28 net-_r1-pad1_ net-_q31-pad2_ 4k +r26 net-_r1-pad1_ net-_q32-pad2_ 2k +r24 net-_r1-pad1_ net-_d12-pad1_ 2.5k +q24 net-_d12-pad2_ net-_q24-pad2_ gndpwr Q2N2222 +q22 net-_q19-pad2_ net-_d12-pad2_ net-_q20-pad2_ Q2N2222 +q19 net-_q19-pad1_ net-_q19-pad2_ net-_d10-pad1_ Q2N2222 +r19 net-_r1-pad1_ net-_q19-pad2_ 1.6k +r16 net-_r1-pad1_ net-_q19-pad1_ 130 +d14 gndpwr net-_d14-pad2_ 1N4148 +d16 gndpwr net-_d16-pad2_ 1N4148 +r23 net-_q24-pad2_ gndpwr 1k +r20 net-_q20-pad2_ gndpwr 1k +d10 net-_d10-pad1_ net-_d10-pad2_ 1N4148 +q20 net-_d10-pad2_ net-_q20-pad2_ gndpwr Q2N2222 +* Control Statements + +.ends SN7432_Subcirrcuit \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit_Previous_Values.xml b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit_Previous_Values.xml new file mode 100644 index 000000000..40f426fcb --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/SN7432_Subcirrcuit_Previous_Values.xml @@ -0,0 +1 @@ +C:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libtruefalsefalseHzHz0Volts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or Amperessecsecsec \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN7432/analysis b/library/SubcircuitLibrary/SN7432/analysis new file mode 100644 index 000000000..ebd5c0a94 --- /dev/null +++ b/library/SubcircuitLibrary/SN7432/analysis @@ -0,0 +1 @@ +.tran 0e-00 0e-00 0e-00 \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN74LS11/D.lib b/library/SubcircuitLibrary/SN74LS11/D.lib new file mode 100644 index 000000000..f53bf3e03 --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/D.lib @@ -0,0 +1,2 @@ +.model 1N4148 D(is=2.495E-09 rs=4.755E-01 n=1.679E+00 tt=3.030E-09 cjo=1.700E-12 vj=1 m=1.959E-01 bv=1.000E+02 ibv=1.000E-04) + diff --git a/library/SubcircuitLibrary/SN74LS11/NPN.lib b/library/SubcircuitLibrary/SN74LS11/NPN.lib new file mode 100644 index 000000000..be5f3073a --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/NPN.lib @@ -0,0 +1,4 @@ +.model Q2N2222 NPN( Is=14.34f Xti=3 Eg=1.11 Vaf=74.03 Bf=400 Ne=1.307 ++ Ise=14.34f Ikf=0.2847 Xtb=1.5 Br=6.092 Nc=2 Isc=0 Ikr=0 Rc=1 Cjc=7.306p ++ Mjc=0.3416 Vjc=0.75 Fc=0.5 Cje=22.01p Mje=0.377 Vje=0.75 Tr=46.91n Tf=411.1p ++ Itf=0.6 Vtf=1.7 Xtf=3 Rb=10) diff --git a/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11-cache.lib b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11-cache.lib new file mode 100644 index 000000000..0d1f2ae85 --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11-cache.lib @@ -0,0 +1,164 @@ +EESchema-LIBRARY Version 2.3 +#encoding utf-8 +# +# GNDPWR +# +DEF GNDPWR #PWR 0 0 Y Y 1 F P +F0 "#PWR" 0 -200 50 H I C CNN +F1 "GNDPWR" 0 -130 50 H V C CNN +F2 "" 0 -50 50 H I C CNN +F3 "" 0 -50 50 H I C CNN +DRAW +P 2 0 1 0 0 -50 0 0 N +P 3 0 1 8 -40 -50 -50 -80 -50 -80 N +P 3 0 1 8 -20 -50 -30 -80 -30 -80 N +P 3 0 1 8 0 -50 -10 -80 -10 -80 N +P 3 0 1 8 20 -50 10 -80 10 -80 N +P 3 0 1 8 40 -50 -40 -50 -40 -50 N +P 4 0 1 8 40 -50 30 -80 30 -80 30 -80 N +X GNDPWR 1 0 0 0 U 50 50 1 1 W N +ENDDRAW +ENDDEF +# +# PORT +# +DEF PORT U 0 40 Y Y 26 F N +F0 "U" 50 100 30 H V C CNN +F1 "PORT" 0 0 30 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +DRAW +A 325 225 285 -1421 -1278 0 1 0 N 100 50 150 0 +A 376 -275 356 1294 1408 0 1 0 N 150 0 100 -50 +S -100 50 100 -50 0 1 0 N +X ~ 1 250 0 100 L 30 30 1 1 B +X ~ 2 250 0 100 L 30 30 2 1 B +X ~ 3 250 0 100 L 30 30 3 1 B +X ~ 4 250 0 100 L 30 30 4 1 B +X ~ 5 250 0 100 L 30 30 5 1 B +X ~ 6 250 0 100 L 30 30 6 1 B +X ~ 7 250 0 100 L 30 30 7 1 B +X ~ 8 250 0 100 L 30 30 8 1 B +X ~ 9 250 0 100 L 30 30 9 1 B +X ~ 10 250 0 100 L 30 30 10 1 B +X ~ 11 250 0 100 L 30 30 11 1 B +X ~ 12 250 0 100 L 30 30 12 1 B +X ~ 13 250 0 100 L 30 30 13 1 B +X ~ 14 250 0 100 L 30 30 14 1 B +X ~ 15 250 0 100 L 30 30 15 1 B +X ~ 16 250 0 100 L 30 30 16 1 B +X ~ 17 250 0 100 L 30 30 17 1 B +X ~ 18 250 0 100 L 30 30 18 1 B +X ~ 19 250 0 100 L 30 30 19 1 B +X ~ 20 250 0 100 L 30 30 20 1 B +X ~ 21 250 0 100 L 30 30 21 1 B +X ~ 22 250 0 100 L 30 30 22 1 B +X ~ 23 250 0 100 L 30 30 23 1 B +X ~ 24 250 0 100 L 30 30 24 1 B +X ~ 25 250 0 100 L 30 30 25 1 B +X ~ 26 250 0 100 L 30 30 26 1 B +ENDDRAW +ENDDEF +# +# eSim_CP1 +# +DEF eSim_CP1 C 0 10 N N 1 F N +F0 "C" 25 100 50 H V L CNN +F1 "eSim_CP1" 25 -100 50 H V L CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +ALIAS capacitor_polarised +$FPLIST + CP_* +$ENDFPLIST +DRAW +A 0 -150 128 1287 513 0 1 20 N -80 -50 80 -50 +P 2 0 1 20 -80 30 80 30 N +P 2 0 1 0 -70 90 -30 90 N +P 2 0 1 0 -50 70 -50 110 N +X ~ 1 0 150 110 D 50 50 1 1 P +X ~ 2 0 -150 130 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_Diode +# +DEF eSim_Diode D 0 40 N N 1 F N +F0 "D" 0 100 50 H V C CNN +F1 "eSim_Diode" 0 -100 50 H V C CNN +F2 "" 0 0 60 H V C CNN +F3 "" 0 0 60 H V C CNN +$FPLIST + TO-???* + *SingleDiode + *_Diode_* + *SingleDiode* + D_* +$ENDFPLIST +DRAW +T 0 -100 50 60 0 0 0 A Normal 0 C C +T 0 100 50 60 0 0 0 K Normal 0 C C +P 2 0 1 6 50 50 50 -50 N +P 3 0 1 0 -50 50 50 0 -50 -50 F +X A 1 -150 0 100 R 40 40 1 1 P +X K 2 150 0 100 L 40 40 1 1 P +ENDDRAW +ENDDEF +# +# eSim_NPN +# +DEF eSim_NPN Q 0 0 Y N 1 F N +F0 "Q" -100 50 50 H V R CNN +F1 "eSim_NPN" -50 150 50 H V R CNN +F2 "" 200 100 29 H V C CNN +F3 "" 0 0 60 H V C CNN +ALIAS BC547 Q2N2222 +DRAW +C 50 0 111 0 1 10 N +P 2 0 1 0 25 25 100 100 N +P 3 0 1 0 25 -25 100 -100 100 -100 N +P 3 0 1 20 25 75 25 -75 25 -75 N +P 5 0 1 0 50 -70 70 -50 90 -90 50 -70 50 -70 F +X C 1 100 200 100 D 50 50 1 1 P +X B 2 -200 0 225 R 50 50 1 1 P +X E 3 100 -200 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# eSim_R +# +DEF eSim_R R 0 0 N Y 1 F N +F0 "R" 50 130 50 H V C CNN +F1 "eSim_R" 50 -50 50 H V C CNN +F2 "" 50 -20 30 H V C CNN +F3 "" 50 50 30 V V C CNN +ALIAS resistor +$FPLIST + R_* + Resistor_* +$ENDFPLIST +DRAW +S 150 10 -50 90 0 1 10 N +X ~ 1 -100 50 50 R 60 60 1 1 P +X ~ 2 200 50 50 L 60 60 1 1 P +ENDDRAW +ENDDEF +# +# zener +# +DEF zener U 0 40 Y Y 1 F N +F0 "U" -50 -100 60 H V C CNN +F1 "zener" 0 100 60 H V C CNN +F2 "" 50 0 60 H V C CNN +F3 "" 50 0 60 H V C CNN +DRAW +P 2 0 1 0 100 -50 50 -100 N +P 2 0 1 0 100 50 100 -50 N +P 2 0 1 0 100 50 150 100 N +P 4 0 1 0 0 50 0 -50 100 0 0 50 N +X ~ IN -200 0 200 R 50 43 1 1 I +X ~ OUT 300 0 200 L 50 43 1 1 O +ENDDRAW +ENDDEF +# +#End Library diff --git a/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.cir b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.cir new file mode 100644 index 000000000..e049f6b43 --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.cir @@ -0,0 +1,77 @@ +* C:\FOSSEE\eSim\library\SubcircuitLibrary\Subcircuit_SN74LS11\Subcircuit_SN74LS11.cir + +* EESchema Netlist Version 1.1 (Spice format) creation date: 02/10/25 19:11:48 + +* To exclude a component from the Spice Netlist add [Spice_Netlist_Enabled] user FIELD set to: N +* To reorder the component spice node sequence add [Spice_Node_Sequence] user FIELD and define sequence: 2,1,0 + +* Sheet Name: / +R1 Net-_R1-Pad1_ Net-_Q1-Pad2_ 20k +R2 Net-_R1-Pad1_ Net-_Q1-Pad1_ 10k +Q1 Net-_Q1-Pad1_ Net-_Q1-Pad2_ Net-_D1-Pad1_ eSim_NPN +Q2 Net-_Q2-Pad1_ Net-_Q1-Pad1_ Net-_C1-Pad1_ eSim_NPN +Q4 Net-_Q4-Pad1_ Net-_Q2-Pad1_ Net-_Q4-Pad3_ eSim_NPN +R4 Net-_R1-Pad1_ Net-_Q2-Pad1_ 8k +R7 Net-_R1-Pad1_ Net-_Q4-Pad1_ 120 +U2 GNDPWR Net-_U1-Pad1_ zener +U3 GNDPWR Net-_U1-Pad2_ zener +U4 GNDPWR Net-_U1-Pad3_ zener +U7 Net-_Q1-Pad2_ Net-_U1-Pad3_ zener +U6 Net-_Q1-Pad2_ Net-_U1-Pad2_ zener +D1 Net-_D1-Pad1_ GNDPWR eSim_Diode +C1 Net-_C1-Pad1_ Net-_C1-Pad2_ 10pF +R3 Net-_C1-Pad1_ Net-_Q3-Pad2_ 1.5k +R5 Net-_C1-Pad1_ Net-_Q3-Pad1_ 3k +Q3 Net-_Q3-Pad1_ Net-_Q3-Pad2_ GNDPWR eSim_NPN +Q6 Net-_C1-Pad2_ Net-_C1-Pad1_ GNDPWR eSim_NPN +U9 Net-_R6-Pad2_ Net-_Q2-Pad1_ zener +R6 Net-_C1-Pad2_ Net-_R6-Pad2_ 5k +Q5 Net-_Q4-Pad1_ Net-_Q4-Pad3_ Net-_C1-Pad2_ eSim_NPN +U1 Net-_U1-Pad1_ Net-_U1-Pad2_ Net-_U1-Pad3_ Net-_U1-Pad4_ Net-_U1-Pad5_ Net-_U1-Pad6_ Net-_C1-Pad2_ Net-_R1-Pad1_ GNDPWR Net-_U1-Pad10_ Net-_U1-Pad11_ Net-_U1-Pad12_ Net-_C2-Pad2_ Net-_C3-Pad2_ PORT +U5 Net-_Q1-Pad2_ Net-_U1-Pad1_ zener +R15 Net-_R1-Pad1_ Net-_Q13-Pad2_ 20k +R16 Net-_R1-Pad1_ Net-_Q13-Pad1_ 10k +Q13 Net-_Q13-Pad1_ Net-_Q13-Pad2_ Net-_D3-Pad1_ eSim_NPN +Q14 Net-_Q14-Pad1_ Net-_Q13-Pad1_ Net-_C3-Pad1_ eSim_NPN +Q16 Net-_Q16-Pad1_ Net-_Q14-Pad1_ Net-_Q16-Pad3_ eSim_NPN +R18 Net-_R1-Pad1_ Net-_Q14-Pad1_ 8k +R21 Net-_R1-Pad1_ Net-_Q16-Pad1_ 120 +U16 GNDPWR Net-_U1-Pad10_ zener +U17 GNDPWR Net-_U1-Pad11_ zener +U18 GNDPWR Net-_U1-Pad12_ zener +U21 Net-_Q13-Pad2_ Net-_U1-Pad12_ zener +U20 Net-_Q13-Pad2_ Net-_U1-Pad11_ zener +D3 Net-_D3-Pad1_ GNDPWR eSim_Diode +C3 Net-_C3-Pad1_ Net-_C3-Pad2_ 10pF +R17 Net-_C3-Pad1_ Net-_Q15-Pad2_ 1.5k +R19 Net-_C3-Pad1_ Net-_Q15-Pad1_ 3k +Q15 Net-_Q15-Pad1_ Net-_Q15-Pad2_ GNDPWR eSim_NPN +Q18 Net-_C3-Pad2_ Net-_C3-Pad1_ GNDPWR eSim_NPN +U22 Net-_R20-Pad2_ Net-_Q14-Pad1_ zener +R20 Net-_C3-Pad2_ Net-_R20-Pad2_ 5k +Q17 Net-_Q16-Pad1_ Net-_Q16-Pad3_ Net-_C3-Pad2_ eSim_NPN +U19 Net-_Q13-Pad2_ Net-_U1-Pad10_ zener +R8 Net-_R1-Pad1_ Net-_Q7-Pad2_ 20k +R9 Net-_R1-Pad1_ Net-_Q7-Pad1_ 10k +Q7 Net-_Q7-Pad1_ Net-_Q7-Pad2_ Net-_D2-Pad1_ eSim_NPN +Q8 Net-_Q10-Pad2_ Net-_Q7-Pad1_ Net-_C2-Pad1_ eSim_NPN +Q10 Net-_Q10-Pad1_ Net-_Q10-Pad2_ Net-_Q10-Pad3_ eSim_NPN +R11 Net-_R1-Pad1_ Net-_Q10-Pad2_ 8k +R14 Net-_R1-Pad1_ Net-_Q10-Pad1_ 120 +U8 GNDPWR Net-_U1-Pad4_ zener +U10 GNDPWR Net-_U1-Pad5_ zener +U11 GNDPWR Net-_U1-Pad6_ zener +U14 Net-_Q7-Pad2_ Net-_U1-Pad6_ zener +U13 Net-_Q7-Pad2_ Net-_U1-Pad5_ zener +D2 Net-_D2-Pad1_ GNDPWR eSim_Diode +C2 Net-_C2-Pad1_ Net-_C2-Pad2_ 10pF +R10 Net-_C2-Pad1_ Net-_Q9-Pad2_ 1.5k +R12 Net-_C2-Pad1_ Net-_Q9-Pad1_ 3k +Q9 Net-_Q9-Pad1_ Net-_Q9-Pad2_ GNDPWR eSim_NPN +Q12 Net-_C2-Pad2_ Net-_C2-Pad1_ GNDPWR eSim_NPN +U15 Net-_R13-Pad2_ Net-_Q10-Pad2_ zener +R13 Net-_C2-Pad2_ Net-_R13-Pad2_ 5k +Q11 Net-_Q10-Pad1_ Net-_Q10-Pad3_ Net-_C2-Pad2_ eSim_NPN +U12 Net-_Q7-Pad2_ Net-_U1-Pad4_ zener + +.end diff --git a/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.cir.out b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.cir.out new file mode 100644 index 000000000..a8250b0d9 --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.cir.out @@ -0,0 +1,143 @@ +* c:\fossee\esim\library\subcircuitlibrary\subcircuit_sn74ls11\subcircuit_sn74ls11.cir + +.include D.lib +.include NPN.lib +r1 net-_r1-pad1_ net-_q1-pad2_ 20k +r2 net-_r1-pad1_ net-_q1-pad1_ 10k +q1 net-_q1-pad1_ net-_q1-pad2_ net-_d1-pad1_ Q2N2222 +q2 net-_q2-pad1_ net-_q1-pad1_ net-_c1-pad1_ Q2N2222 +q4 net-_q4-pad1_ net-_q2-pad1_ net-_q4-pad3_ Q2N2222 +r4 net-_r1-pad1_ net-_q2-pad1_ 8k +r7 net-_r1-pad1_ net-_q4-pad1_ 120 +* u2 gndpwr net-_u1-pad1_ zener +* u3 gndpwr net-_u1-pad2_ zener +* u4 gndpwr net-_u1-pad3_ zener +* u7 net-_q1-pad2_ net-_u1-pad3_ zener +* u6 net-_q1-pad2_ net-_u1-pad2_ zener +d1 net-_d1-pad1_ gndpwr 1N4148 +c1 net-_c1-pad1_ net-_c1-pad2_ 10pf +r3 net-_c1-pad1_ net-_q3-pad2_ 1.5k +r5 net-_c1-pad1_ net-_q3-pad1_ 3k +q3 net-_q3-pad1_ net-_q3-pad2_ gndpwr Q2N2222 +q6 net-_c1-pad2_ net-_c1-pad1_ gndpwr Q2N2222 +* u9 net-_r6-pad2_ net-_q2-pad1_ zener +r6 net-_c1-pad2_ net-_r6-pad2_ 5k +q5 net-_q4-pad1_ net-_q4-pad3_ net-_c1-pad2_ Q2N2222 +* u1 net-_u1-pad1_ net-_u1-pad2_ net-_u1-pad3_ net-_u1-pad4_ net-_u1-pad5_ net-_u1-pad6_ net-_c1-pad2_ net-_r1-pad1_ gndpwr net-_u1-pad10_ net-_u1-pad11_ net-_u1-pad12_ net-_c2-pad2_ net-_c3-pad2_ port +* u5 net-_q1-pad2_ net-_u1-pad1_ zener +r15 net-_r1-pad1_ net-_q13-pad2_ 20k +r16 net-_r1-pad1_ net-_q13-pad1_ 10k +q13 net-_q13-pad1_ net-_q13-pad2_ net-_d3-pad1_ Q2N2222 +q14 net-_q14-pad1_ net-_q13-pad1_ net-_c3-pad1_ Q2N2222 +q16 net-_q16-pad1_ net-_q14-pad1_ net-_q16-pad3_ Q2N2222 +r18 net-_r1-pad1_ net-_q14-pad1_ 8k +r21 net-_r1-pad1_ net-_q16-pad1_ 120 +* u16 gndpwr net-_u1-pad10_ zener +* u17 gndpwr net-_u1-pad11_ zener +* u18 gndpwr net-_u1-pad12_ zener +* u21 net-_q13-pad2_ net-_u1-pad12_ zener +* u20 net-_q13-pad2_ net-_u1-pad11_ zener +d3 net-_d3-pad1_ gndpwr 1N4148 +c3 net-_c3-pad1_ net-_c3-pad2_ 10pf +r17 net-_c3-pad1_ net-_q15-pad2_ 1.5k +r19 net-_c3-pad1_ net-_q15-pad1_ 3k +q15 net-_q15-pad1_ net-_q15-pad2_ gndpwr Q2N2222 +q18 net-_c3-pad2_ net-_c3-pad1_ gndpwr Q2N2222 +* u22 net-_r20-pad2_ net-_q14-pad1_ zener +r20 net-_c3-pad2_ net-_r20-pad2_ 5k +q17 net-_q16-pad1_ net-_q16-pad3_ net-_c3-pad2_ Q2N2222 +* u19 net-_q13-pad2_ net-_u1-pad10_ zener +r8 net-_r1-pad1_ net-_q7-pad2_ 20k +r9 net-_r1-pad1_ net-_q7-pad1_ 10k +q7 net-_q7-pad1_ net-_q7-pad2_ net-_d2-pad1_ Q2N2222 +q8 net-_q10-pad2_ net-_q7-pad1_ net-_c2-pad1_ Q2N2222 +q10 net-_q10-pad1_ net-_q10-pad2_ net-_q10-pad3_ Q2N2222 +r11 net-_r1-pad1_ net-_q10-pad2_ 8k +r14 net-_r1-pad1_ net-_q10-pad1_ 120 +* u8 gndpwr net-_u1-pad4_ zener +* u10 gndpwr net-_u1-pad5_ zener +* u11 gndpwr net-_u1-pad6_ zener +* u14 net-_q7-pad2_ net-_u1-pad6_ zener +* u13 net-_q7-pad2_ net-_u1-pad5_ zener +d2 net-_d2-pad1_ gndpwr 1N4148 +c2 net-_c2-pad1_ net-_c2-pad2_ 10pf +r10 net-_c2-pad1_ net-_q9-pad2_ 1.5k +r12 net-_c2-pad1_ net-_q9-pad1_ 3k +q9 net-_q9-pad1_ net-_q9-pad2_ gndpwr Q2N2222 +q12 net-_c2-pad2_ net-_c2-pad1_ gndpwr Q2N2222 +* u15 net-_r13-pad2_ net-_q10-pad2_ zener +r13 net-_c2-pad2_ net-_r13-pad2_ 5k +q11 net-_q10-pad1_ net-_q10-pad3_ net-_c2-pad2_ Q2N2222 +* u12 net-_q7-pad2_ net-_u1-pad4_ zener +a1 gndpwr net-_u1-pad1_ u2 +a2 gndpwr net-_u1-pad2_ u3 +a3 gndpwr net-_u1-pad3_ u4 +a4 net-_q1-pad2_ net-_u1-pad3_ u7 +a5 net-_q1-pad2_ net-_u1-pad2_ u6 +a6 net-_r6-pad2_ net-_q2-pad1_ u9 +a7 net-_q1-pad2_ net-_u1-pad1_ u5 +a8 gndpwr net-_u1-pad10_ u16 +a9 gndpwr net-_u1-pad11_ u17 +a10 gndpwr net-_u1-pad12_ u18 +a11 net-_q13-pad2_ net-_u1-pad12_ u21 +a12 net-_q13-pad2_ net-_u1-pad11_ u20 +a13 net-_r20-pad2_ net-_q14-pad1_ u22 +a14 net-_q13-pad2_ net-_u1-pad10_ u19 +a15 gndpwr net-_u1-pad4_ u8 +a16 gndpwr net-_u1-pad5_ u10 +a17 gndpwr net-_u1-pad6_ u11 +a18 net-_q7-pad2_ net-_u1-pad6_ u14 +a19 net-_q7-pad2_ net-_u1-pad5_ u13 +a20 net-_r13-pad2_ net-_q10-pad2_ u15 +a21 net-_q7-pad2_ net-_u1-pad4_ u12 +* Schematic Name: zener, NgSpice Name: zener +.model u2 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u3 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u4 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u7 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u6 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u9 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u5 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u16 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u17 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u18 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u21 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u20 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u22 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u19 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u8 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u10 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u11 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u14 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u13 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u15 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u12 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +.tran 0e-00 0e-00 0e-00 + +* Control Statements +.control +run +print allv > plot_data_v.txt +print alli > plot_data_i.txt +.endc +.end diff --git a/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.pro b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.pro new file mode 100644 index 000000000..e27a398be --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.pro @@ -0,0 +1,73 @@ +update=22/05/2015 07:44:53 +version=1 +last_client=kicad +[general] +version=1 +RootSch= +BoardNm= +[pcbnew] +version=1 +LastNetListRead= +UseCmpFile=1 +PadDrill=0.600000000000 +PadDrillOvalY=0.600000000000 +PadSizeH=1.500000000000 +PadSizeV=1.500000000000 +PcbTextSizeV=1.500000000000 +PcbTextSizeH=1.500000000000 +PcbTextThickness=0.300000000000 +ModuleTextSizeV=1.000000000000 +ModuleTextSizeH=1.000000000000 +ModuleTextSizeThickness=0.150000000000 +SolderMaskClearance=0.000000000000 +SolderMaskMinWidth=0.000000000000 +DrawSegmentWidth=0.200000000000 +BoardOutlineThickness=0.100000000000 +ModuleOutlineThickness=0.150000000000 +[cvpcb] +version=1 +NetIExt=net +[eeschema] +version=1 +LibDir= +[eeschema/libraries] +LibName1=adc-dac +LibName2=memory +LibName3=xilinx +LibName4=microcontrollers +LibName5=dsp +LibName6=microchip +LibName7=analog_switches +LibName8=motorola +LibName9=texas +LibName10=intel +LibName11=audio +LibName12=interface +LibName13=digital-audio +LibName14=philips +LibName15=display +LibName16=cypress +LibName17=siliconi +LibName18=opto +LibName19=atmel +LibName20=contrib +LibName21=power +LibName22=eSim_Plot +LibName23=transistors +LibName24=conn +LibName25=eSim_User +LibName26=regul +LibName27=74xx +LibName28=cmos4000 +LibName29=eSim_Analog +LibName30=eSim_Devices +LibName31=eSim_Digital +LibName32=eSim_Hybrid +LibName33=eSim_Miscellaneous +LibName34=eSim_Power +LibName35=eSim_Sources +LibName36=eSim_Subckt +LibName37=eSim_Nghdl +LibName38=eSim_Ngveri +LibName39=eSim_SKY130 +LibName40=eSim_SKY130_Subckts diff --git a/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.sch b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.sch new file mode 100644 index 000000000..0247342b0 --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.sch @@ -0,0 +1,1351 @@ +EESchema Schematic File Version 2 +LIBS:adc-dac +LIBS:memory +LIBS:xilinx +LIBS:microcontrollers +LIBS:dsp +LIBS:microchip +LIBS:analog_switches +LIBS:motorola +LIBS:texas +LIBS:intel +LIBS:audio +LIBS:interface +LIBS:digital-audio +LIBS:philips +LIBS:display +LIBS:cypress +LIBS:siliconi +LIBS:opto +LIBS:atmel +LIBS:contrib +LIBS:power +LIBS:eSim_Plot +LIBS:transistors +LIBS:conn +LIBS:eSim_User +LIBS:regul +LIBS:74xx +LIBS:cmos4000 +LIBS:eSim_Analog +LIBS:eSim_Devices +LIBS:eSim_Digital +LIBS:eSim_Hybrid +LIBS:eSim_Miscellaneous +LIBS:eSim_Power +LIBS:eSim_Sources +LIBS:eSim_Subckt +LIBS:eSim_Nghdl +LIBS:eSim_Ngveri +LIBS:eSim_SKY130 +LIBS:eSim_SKY130_Subckts +EELAYER 25 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$Comp +L resistor R1 +U 1 1 67A9FAFF +P 1980 1120 +F 0 "R1" H 2030 1250 50 0000 C CNN +F 1 "20k" H 2030 1070 50 0000 C CNN +F 2 "" H 2030 1100 30 0000 C CNN +F 3 "" V 2030 1170 30 0000 C CNN + 1 1980 1120 + 0 1 1 0 +$EndComp +$Comp +L resistor R2 +U 1 1 67A9FB42 +P 2500 1120 +F 0 "R2" H 2550 1250 50 0000 C CNN +F 1 "10k" H 2550 1070 50 0000 C CNN +F 2 "" H 2550 1100 30 0000 C CNN +F 3 "" V 2550 1170 30 0000 C CNN + 1 2500 1120 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q1 +U 1 1 67A9FB67 +P 2450 2560 +F 0 "Q1" H 2350 2610 50 0000 R CNN +F 1 "eSim_NPN" H 2400 2710 50 0000 R CNN +F 2 "" H 2650 2660 29 0000 C CNN +F 3 "" H 2450 2560 60 0000 C CNN + 1 2450 2560 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q2 +U 1 1 67A9FBA3 +P 2830 2140 +F 0 "Q2" H 2730 2190 50 0000 R CNN +F 1 "eSim_NPN" H 2780 2290 50 0000 R CNN +F 2 "" H 3030 2240 29 0000 C CNN +F 3 "" H 2830 2140 60 0000 C CNN + 1 2830 2140 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q4 +U 1 1 67A9FBD9 +P 3500 1690 +F 0 "Q4" H 3400 1740 50 0000 R CNN +F 1 "eSim_NPN" H 3450 1840 50 0000 R CNN +F 2 "" H 3700 1790 29 0000 C CNN +F 3 "" H 3500 1690 60 0000 C CNN + 1 3500 1690 + 1 0 0 -1 +$EndComp +$Comp +L resistor R4 +U 1 1 67A9FC1E +P 3140 1130 +F 0 "R4" H 3190 1260 50 0000 C CNN +F 1 "8k" H 3190 1080 50 0000 C CNN +F 2 "" H 3190 1110 30 0000 C CNN +F 3 "" V 3190 1180 30 0000 C CNN + 1 3140 1130 + 0 1 1 0 +$EndComp +$Comp +L resistor R7 +U 1 1 67A9FC6A +P 4310 1130 +F 0 "R7" H 4360 1260 50 0000 C CNN +F 1 "120" H 4360 1080 50 0000 C CNN +F 2 "" H 4360 1110 30 0000 C CNN +F 3 "" V 4360 1180 30 0000 C CNN + 1 4310 1130 + 0 1 1 0 +$EndComp +$Comp +L zener U2 +U 1 1 67A9FCCE +P 1020 3170 +F 0 "U2" H 970 3070 60 0000 C CNN +F 1 "zener" H 1020 3270 60 0000 C CNN +F 2 "" H 1070 3170 60 0000 C CNN +F 3 "" H 1070 3170 60 0000 C CNN + 1 1020 3170 + 0 1 -1 0 +$EndComp +$Comp +L zener U3 +U 1 1 67A9FD63 +P 1290 3170 +F 0 "U3" H 1240 3070 60 0000 C CNN +F 1 "zener" H 1290 3270 60 0000 C CNN +F 2 "" H 1340 3170 60 0000 C CNN +F 3 "" H 1340 3170 60 0000 C CNN + 1 1290 3170 + 0 1 -1 0 +$EndComp +$Comp +L zener U4 +U 1 1 67A9FDB0 +P 1500 3170 +F 0 "U4" H 1450 3070 60 0000 C CNN +F 1 "zener" H 1500 3270 60 0000 C CNN +F 2 "" H 1550 3170 60 0000 C CNN +F 3 "" H 1550 3170 60 0000 C CNN + 1 1500 3170 + 0 1 -1 0 +$EndComp +$Comp +L zener U7 +U 1 1 67A9FDFC +P 1830 2390 +F 0 "U7" H 1780 2290 60 0000 C CNN +F 1 "zener" H 1830 2490 60 0000 C CNN +F 2 "" H 1880 2390 60 0000 C CNN +F 3 "" H 1880 2390 60 0000 C CNN + 1 1830 2390 + -1 0 0 1 +$EndComp +$Comp +L zener U6 +U 1 1 67A9FE59 +P 1830 2140 +F 0 "U6" H 1780 2040 60 0000 C CNN +F 1 "zener" H 1830 2240 60 0000 C CNN +F 2 "" H 1880 2140 60 0000 C CNN +F 3 "" H 1880 2140 60 0000 C CNN + 1 1830 2140 + -1 0 0 1 +$EndComp +$Comp +L eSim_Diode D1 +U 1 1 67A9FEEB +P 2550 3230 +F 0 "D1" H 2550 3330 50 0000 C CNN +F 1 "eSim_Diode" H 2550 3130 50 0000 C CNN +F 2 "" H 2550 3230 60 0000 C CNN +F 3 "" H 2550 3230 60 0000 C CNN + 1 2550 3230 + 0 1 1 0 +$EndComp +$Comp +L capacitor_polarised C1 +U 1 1 67A9FF3E +P 3470 2690 +F 0 "C1" H 3495 2790 50 0000 L CNN +F 1 "10pF" H 3495 2590 50 0000 L CNN +F 2 "" H 3470 2690 50 0001 C CNN +F 3 "" H 3470 2690 50 0001 C CNN + 1 3470 2690 + 0 -1 -1 0 +$EndComp +$Comp +L resistor R3 +U 1 1 67A9FF79 +P 2880 3560 +F 0 "R3" H 2930 3690 50 0000 C CNN +F 1 "1.5k" H 2930 3510 50 0000 C CNN +F 2 "" H 2930 3540 30 0000 C CNN +F 3 "" V 2930 3610 30 0000 C CNN + 1 2880 3560 + 0 1 1 0 +$EndComp +$Comp +L resistor R5 +U 1 1 67A9FFCE +P 3420 3270 +F 0 "R5" H 3470 3400 50 0000 C CNN +F 1 "3k" H 3470 3220 50 0000 C CNN +F 2 "" H 3470 3250 30 0000 C CNN +F 3 "" V 3470 3320 30 0000 C CNN + 1 3420 3270 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q3 +U 1 1 67AA0018 +P 3370 3830 +F 0 "Q3" H 3270 3880 50 0000 R CNN +F 1 "eSim_NPN" H 3320 3980 50 0000 R CNN +F 2 "" H 3570 3930 29 0000 C CNN +F 3 "" H 3370 3830 60 0000 C CNN + 1 3370 3830 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q6 +U 1 1 67AA0061 +P 4280 3120 +F 0 "Q6" H 4180 3170 50 0000 R CNN +F 1 "eSim_NPN" H 4230 3270 50 0000 R CNN +F 2 "" H 4480 3220 29 0000 C CNN +F 3 "" H 4280 3120 60 0000 C CNN + 1 4280 3120 + 1 0 0 -1 +$EndComp +$Comp +L zener U9 +U 1 1 67AA00E5 +P 3580 2350 +F 0 "U9" H 3530 2250 60 0000 C CNN +F 1 "zener" H 3580 2450 60 0000 C CNN +F 2 "" H 3630 2350 60 0000 C CNN +F 3 "" H 3630 2350 60 0000 C CNN + 1 3580 2350 + -1 0 0 1 +$EndComp +$Comp +L resistor R6 +U 1 1 67AA01B1 +P 4140 2300 +F 0 "R6" H 4190 2430 50 0000 C CNN +F 1 "5k" H 4190 2250 50 0000 C CNN +F 2 "" H 4190 2280 30 0000 C CNN +F 3 "" V 4190 2350 30 0000 C CNN + 1 4140 2300 + -1 0 0 1 +$EndComp +$Comp +L eSim_NPN Q5 +U 1 1 67AA023E +P 4260 1920 +F 0 "Q5" H 4160 1970 50 0000 R CNN +F 1 "eSim_NPN" H 4210 2070 50 0000 R CNN +F 2 "" H 4460 2020 29 0000 C CNN +F 3 "" H 4260 1920 60 0000 C CNN + 1 4260 1920 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 1 1 67AA192A +P 630 1900 +F 0 "U1" H 680 2000 30 0000 C CNN +F 1 "PORT" H 630 1900 30 0000 C CNN +F 2 "" H 630 1900 60 0000 C CNN +F 3 "" H 630 1900 60 0000 C CNN + 1 630 1900 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 2 1 67AA1BA4 +P 630 2140 +F 0 "U1" H 680 2240 30 0000 C CNN +F 1 "PORT" H 630 2140 30 0000 C CNN +F 2 "" H 630 2140 60 0000 C CNN +F 3 "" H 630 2140 60 0000 C CNN + 2 630 2140 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 3 1 67AA1D5E +P 630 2390 +F 0 "U1" H 680 2490 30 0000 C CNN +F 1 "PORT" H 630 2390 30 0000 C CNN +F 2 "" H 630 2390 60 0000 C CNN +F 3 "" H 630 2390 60 0000 C CNN + 3 630 2390 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 7 1 67AA1FEB +P 4780 2350 +F 0 "U1" H 4830 2450 30 0000 C CNN +F 1 "PORT" H 4780 2350 30 0000 C CNN +F 2 "" H 4780 2350 60 0000 C CNN +F 3 "" H 4780 2350 60 0000 C CNN + 7 4780 2350 + -1 0 0 1 +$EndComp +$Comp +L zener U5 +U 1 1 67AA3714 +P 1830 1900 +F 0 "U5" H 1780 1800 60 0000 C CNN +F 1 "zener" H 1830 2000 60 0000 C CNN +F 2 "" H 1880 1900 60 0000 C CNN +F 3 "" H 1880 1900 60 0000 C CNN + 1 1830 1900 + -1 0 0 1 +$EndComp +Wire Wire Line + 1530 2140 880 2140 +Wire Wire Line + 1530 2390 880 2390 +Wire Wire Line + 2030 1320 2030 2560 +Wire Wire Line + 1530 1900 880 1900 +Connection ~ 2030 2140 +Connection ~ 2030 1900 +Wire Wire Line + 2030 2560 2250 2560 +Connection ~ 2030 2390 +Wire Wire Line + 1500 2870 1500 2390 +Connection ~ 1500 2390 +Wire Wire Line + 1290 2870 1290 2140 +Connection ~ 1290 2140 +Wire Wire Line + 1020 2870 1020 1900 +Connection ~ 1020 1900 +$Comp +L GNDPWR #PWR01 +U 1 1 67AA4776 +P 3900 4160 +F 0 "#PWR01" H 3900 3960 50 0001 C CNN +F 1 "GNDPWR" H 3900 4030 50 0000 C CNN +F 2 "" H 3900 4110 50 0001 C CNN +F 3 "" H 3900 4110 50 0001 C CNN + 1 3900 4160 + 1 0 0 -1 +$EndComp +Wire Wire Line + 4380 4160 4380 3320 +Wire Wire Line + 1020 4160 4520 4160 +Wire Wire Line + 1500 4160 1500 3370 +Connection ~ 3900 4160 +Wire Wire Line + 1290 4160 1290 3370 +Connection ~ 1500 4160 +Wire Wire Line + 1020 4160 1020 3370 +Connection ~ 1290 4160 +Wire Wire Line + 2550 3380 2550 4160 +Connection ~ 2550 4160 +Wire Wire Line + 2550 2760 2550 3080 +Wire Wire Line + 2630 2140 2550 2140 +Wire Wire Line + 2550 1320 2550 2360 +Connection ~ 2550 2140 +Wire Wire Line + 2930 1940 2930 1690 +Wire Wire Line + 2930 1690 3300 1690 +Wire Wire Line + 3190 1330 3190 1690 +Connection ~ 3190 1690 +Wire Wire Line + 3280 2350 3070 2350 +Wire Wire Line + 3070 2350 3070 1690 +Connection ~ 3070 1690 +Wire Wire Line + 3320 2690 2930 2690 +Wire Wire Line + 2930 2340 2930 3460 +Connection ~ 2930 2690 +Wire Wire Line + 4080 3120 2930 3120 +Connection ~ 2930 3120 +Wire Wire Line + 3470 3170 3470 3120 +Connection ~ 3470 3120 +Wire Wire Line + 3170 3830 2930 3830 +Wire Wire Line + 2930 3830 2930 3760 +Wire Wire Line + 3470 3470 3470 3630 +Wire Wire Line + 3470 4030 3470 4160 +Connection ~ 3470 4160 +Wire Wire Line + 4060 1920 3600 1920 +Wire Wire Line + 3600 1920 3600 1890 +Wire Wire Line + 3940 2350 3780 2350 +Wire Wire Line + 4360 2120 4360 2410 +Wire Wire Line + 4240 2350 4530 2350 +Connection ~ 4360 2350 +Wire Wire Line + 4380 2920 4380 2410 +Wire Wire Line + 4380 2410 4360 2410 +Wire Wire Line + 4360 1720 4360 1330 +Wire Wire Line + 4360 1030 4360 910 +Wire Wire Line + 2030 910 5030 910 +Wire Wire Line + 2030 910 2030 1020 +Wire Wire Line + 2550 1020 2550 910 +Connection ~ 2550 910 +Wire Wire Line + 3190 1030 3190 910 +Connection ~ 3190 910 +Wire Wire Line + 3600 1490 4360 1490 +Connection ~ 4360 1490 +Wire Wire Line + 3620 2690 4380 2690 +Connection ~ 4380 2690 +$Comp +L resistor R15 +U 1 1 67AA7BED +P 8240 1370 +F 0 "R15" H 8290 1500 50 0000 C CNN +F 1 "20k" H 8290 1320 50 0000 C CNN +F 2 "" H 8290 1350 30 0000 C CNN +F 3 "" V 8290 1420 30 0000 C CNN + 1 8240 1370 + 0 1 1 0 +$EndComp +$Comp +L resistor R16 +U 1 1 67AA7BF3 +P 8760 1370 +F 0 "R16" H 8810 1500 50 0000 C CNN +F 1 "10k" H 8810 1320 50 0000 C CNN +F 2 "" H 8810 1350 30 0000 C CNN +F 3 "" V 8810 1420 30 0000 C CNN + 1 8760 1370 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q13 +U 1 1 67AA7BF9 +P 8710 2810 +F 0 "Q13" H 8610 2860 50 0000 R CNN +F 1 "eSim_NPN" H 8660 2960 50 0000 R CNN +F 2 "" H 8910 2910 29 0000 C CNN +F 3 "" H 8710 2810 60 0000 C CNN + 1 8710 2810 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q14 +U 1 1 67AA7BFF +P 9090 2390 +F 0 "Q14" H 8990 2440 50 0000 R CNN +F 1 "eSim_NPN" H 9040 2540 50 0000 R CNN +F 2 "" H 9290 2490 29 0000 C CNN +F 3 "" H 9090 2390 60 0000 C CNN + 1 9090 2390 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q16 +U 1 1 67AA7C05 +P 9760 1940 +F 0 "Q16" H 9660 1990 50 0000 R CNN +F 1 "eSim_NPN" H 9710 2090 50 0000 R CNN +F 2 "" H 9960 2040 29 0000 C CNN +F 3 "" H 9760 1940 60 0000 C CNN + 1 9760 1940 + 1 0 0 -1 +$EndComp +$Comp +L resistor R18 +U 1 1 67AA7C0B +P 9400 1380 +F 0 "R18" H 9450 1510 50 0000 C CNN +F 1 "8k" H 9450 1330 50 0000 C CNN +F 2 "" H 9450 1360 30 0000 C CNN +F 3 "" V 9450 1430 30 0000 C CNN + 1 9400 1380 + 0 1 1 0 +$EndComp +$Comp +L resistor R21 +U 1 1 67AA7C11 +P 10570 1380 +F 0 "R21" H 10620 1510 50 0000 C CNN +F 1 "120" H 10620 1330 50 0000 C CNN +F 2 "" H 10620 1360 30 0000 C CNN +F 3 "" V 10620 1430 30 0000 C CNN + 1 10570 1380 + 0 1 1 0 +$EndComp +$Comp +L zener U16 +U 1 1 67AA7C17 +P 7280 3420 +F 0 "U16" H 7230 3320 60 0000 C CNN +F 1 "zener" H 7280 3520 60 0000 C CNN +F 2 "" H 7330 3420 60 0000 C CNN +F 3 "" H 7330 3420 60 0000 C CNN + 1 7280 3420 + 0 1 -1 0 +$EndComp +$Comp +L zener U17 +U 1 1 67AA7C1D +P 7550 3420 +F 0 "U17" H 7500 3320 60 0000 C CNN +F 1 "zener" H 7550 3520 60 0000 C CNN +F 2 "" H 7600 3420 60 0000 C CNN +F 3 "" H 7600 3420 60 0000 C CNN + 1 7550 3420 + 0 1 -1 0 +$EndComp +$Comp +L zener U18 +U 1 1 67AA7C23 +P 7760 3420 +F 0 "U18" H 7710 3320 60 0000 C CNN +F 1 "zener" H 7760 3520 60 0000 C CNN +F 2 "" H 7810 3420 60 0000 C CNN +F 3 "" H 7810 3420 60 0000 C CNN + 1 7760 3420 + 0 1 -1 0 +$EndComp +$Comp +L zener U21 +U 1 1 67AA7C29 +P 8090 2640 +F 0 "U21" H 8040 2540 60 0000 C CNN +F 1 "zener" H 8090 2740 60 0000 C CNN +F 2 "" H 8140 2640 60 0000 C CNN +F 3 "" H 8140 2640 60 0000 C CNN + 1 8090 2640 + -1 0 0 1 +$EndComp +$Comp +L zener U20 +U 1 1 67AA7C2F +P 8090 2390 +F 0 "U20" H 8040 2290 60 0000 C CNN +F 1 "zener" H 8090 2490 60 0000 C CNN +F 2 "" H 8140 2390 60 0000 C CNN +F 3 "" H 8140 2390 60 0000 C CNN + 1 8090 2390 + -1 0 0 1 +$EndComp +$Comp +L eSim_Diode D3 +U 1 1 67AA7C35 +P 8810 3480 +F 0 "D3" H 8810 3580 50 0000 C CNN +F 1 "eSim_Diode" H 8810 3380 50 0000 C CNN +F 2 "" H 8810 3480 60 0000 C CNN +F 3 "" H 8810 3480 60 0000 C CNN + 1 8810 3480 + 0 1 1 0 +$EndComp +$Comp +L capacitor_polarised C3 +U 1 1 67AA7C3B +P 9730 2940 +F 0 "C3" H 9755 3040 50 0000 L CNN +F 1 "10pF" H 9755 2840 50 0000 L CNN +F 2 "" H 9730 2940 50 0001 C CNN +F 3 "" H 9730 2940 50 0001 C CNN + 1 9730 2940 + 0 -1 -1 0 +$EndComp +$Comp +L resistor R17 +U 1 1 67AA7C41 +P 9140 3810 +F 0 "R17" H 9190 3940 50 0000 C CNN +F 1 "1.5k" H 9190 3760 50 0000 C CNN +F 2 "" H 9190 3790 30 0000 C CNN +F 3 "" V 9190 3860 30 0000 C CNN + 1 9140 3810 + 0 1 1 0 +$EndComp +$Comp +L resistor R19 +U 1 1 67AA7C47 +P 9680 3520 +F 0 "R19" H 9730 3650 50 0000 C CNN +F 1 "3k" H 9730 3470 50 0000 C CNN +F 2 "" H 9730 3500 30 0000 C CNN +F 3 "" V 9730 3570 30 0000 C CNN + 1 9680 3520 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q15 +U 1 1 67AA7C4D +P 9630 4080 +F 0 "Q15" H 9530 4130 50 0000 R CNN +F 1 "eSim_NPN" H 9580 4230 50 0000 R CNN +F 2 "" H 9830 4180 29 0000 C CNN +F 3 "" H 9630 4080 60 0000 C CNN + 1 9630 4080 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q18 +U 1 1 67AA7C53 +P 10540 3370 +F 0 "Q18" H 10440 3420 50 0000 R CNN +F 1 "eSim_NPN" H 10490 3520 50 0000 R CNN +F 2 "" H 10740 3470 29 0000 C CNN +F 3 "" H 10540 3370 60 0000 C CNN + 1 10540 3370 + 1 0 0 -1 +$EndComp +$Comp +L zener U22 +U 1 1 67AA7C59 +P 9840 2600 +F 0 "U22" H 9790 2500 60 0000 C CNN +F 1 "zener" H 9840 2700 60 0000 C CNN +F 2 "" H 9890 2600 60 0000 C CNN +F 3 "" H 9890 2600 60 0000 C CNN + 1 9840 2600 + -1 0 0 1 +$EndComp +$Comp +L resistor R20 +U 1 1 67AA7C5F +P 10400 2550 +F 0 "R20" H 10450 2680 50 0000 C CNN +F 1 "5k" H 10450 2500 50 0000 C CNN +F 2 "" H 10450 2530 30 0000 C CNN +F 3 "" V 10450 2600 30 0000 C CNN + 1 10400 2550 + -1 0 0 1 +$EndComp +$Comp +L eSim_NPN Q17 +U 1 1 67AA7C65 +P 10520 2170 +F 0 "Q17" H 10420 2220 50 0000 R CNN +F 1 "eSim_NPN" H 10470 2320 50 0000 R CNN +F 2 "" H 10720 2270 29 0000 C CNN +F 3 "" H 10520 2170 60 0000 C CNN + 1 10520 2170 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 10 1 67AA7C6B +P 6890 2150 +F 0 "U1" H 6940 2250 30 0000 C CNN +F 1 "PORT" H 6890 2150 30 0000 C CNN +F 2 "" H 6890 2150 60 0000 C CNN +F 3 "" H 6890 2150 60 0000 C CNN + 10 6890 2150 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 11 1 67AA7C71 +P 6890 2390 +F 0 "U1" H 6940 2490 30 0000 C CNN +F 1 "PORT" H 6890 2390 30 0000 C CNN +F 2 "" H 6890 2390 60 0000 C CNN +F 3 "" H 6890 2390 60 0000 C CNN + 11 6890 2390 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 12 1 67AA7C77 +P 6890 2640 +F 0 "U1" H 6940 2740 30 0000 C CNN +F 1 "PORT" H 6890 2640 30 0000 C CNN +F 2 "" H 6890 2640 60 0000 C CNN +F 3 "" H 6890 2640 60 0000 C CNN + 12 6890 2640 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 14 1 67AA7C7D +P 11040 2600 +F 0 "U1" H 11090 2700 30 0000 C CNN +F 1 "PORT" H 11040 2600 30 0000 C CNN +F 2 "" H 11040 2600 60 0000 C CNN +F 3 "" H 11040 2600 60 0000 C CNN + 14 11040 2600 + -1 0 0 1 +$EndComp +$Comp +L zener U19 +U 1 1 67AA7C83 +P 8090 2150 +F 0 "U19" H 8040 2050 60 0000 C CNN +F 1 "zener" H 8090 2250 60 0000 C CNN +F 2 "" H 8140 2150 60 0000 C CNN +F 3 "" H 8140 2150 60 0000 C CNN + 1 8090 2150 + -1 0 0 1 +$EndComp +Wire Wire Line + 7790 2390 7140 2390 +Wire Wire Line + 7790 2640 7140 2640 +Wire Wire Line + 8290 1570 8290 2810 +Wire Wire Line + 7790 2150 7140 2150 +Connection ~ 8290 2390 +Connection ~ 8290 2150 +Wire Wire Line + 8290 2810 8510 2810 +Connection ~ 8290 2640 +Wire Wire Line + 7760 3120 7760 2640 +Connection ~ 7760 2640 +Wire Wire Line + 7550 3120 7550 2390 +Connection ~ 7550 2390 +Wire Wire Line + 7280 3120 7280 2150 +Connection ~ 7280 2150 +$Comp +L GNDPWR #PWR02 +U 1 1 67AA7C97 +P 10160 4410 +F 0 "#PWR02" H 10160 4210 50 0001 C CNN +F 1 "GNDPWR" H 10160 4280 50 0000 C CNN +F 2 "" H 10160 4360 50 0001 C CNN +F 3 "" H 10160 4360 50 0001 C CNN + 1 10160 4410 + 1 0 0 -1 +$EndComp +Wire Wire Line + 10640 3570 10640 4750 +Wire Wire Line + 7280 4410 10640 4410 +Wire Wire Line + 7760 4410 7760 3620 +Connection ~ 10160 4410 +Wire Wire Line + 7550 4410 7550 3620 +Connection ~ 7760 4410 +Wire Wire Line + 7280 4410 7280 3620 +Connection ~ 7550 4410 +Wire Wire Line + 8810 3630 8810 4410 +Connection ~ 8810 4410 +Wire Wire Line + 8810 3010 8810 3330 +Wire Wire Line + 8890 2390 8810 2390 +Wire Wire Line + 8810 1570 8810 2610 +Connection ~ 8810 2390 +Wire Wire Line + 9190 2190 9190 1940 +Wire Wire Line + 9190 1940 9560 1940 +Wire Wire Line + 9450 1580 9450 1940 +Connection ~ 9450 1940 +Wire Wire Line + 9540 2600 9330 2600 +Wire Wire Line + 9330 2600 9330 1940 +Connection ~ 9330 1940 +Wire Wire Line + 9580 2940 9190 2940 +Wire Wire Line + 9190 2590 9190 3710 +Connection ~ 9190 2940 +Wire Wire Line + 10340 3370 9190 3370 +Connection ~ 9190 3370 +Wire Wire Line + 9730 3420 9730 3370 +Connection ~ 9730 3370 +Wire Wire Line + 9430 4080 9190 4080 +Wire Wire Line + 9190 4080 9190 4010 +Wire Wire Line + 9730 3720 9730 3880 +Wire Wire Line + 9730 4280 9730 4410 +Connection ~ 9730 4410 +Wire Wire Line + 10320 2170 9860 2170 +Wire Wire Line + 9860 2170 9860 2140 +Wire Wire Line + 10200 2600 10040 2600 +Wire Wire Line + 10620 2370 10620 2660 +Wire Wire Line + 10500 2600 10790 2600 +Connection ~ 10620 2600 +Wire Wire Line + 10640 3170 10640 2660 +Wire Wire Line + 10640 2660 10620 2660 +Wire Wire Line + 10620 1970 10620 1580 +Wire Wire Line + 10620 900 10620 1280 +Wire Wire Line + 10620 1160 8290 1160 +Wire Wire Line + 8290 1160 8290 1270 +Wire Wire Line + 8810 1270 8810 1160 +Connection ~ 8810 1160 +Wire Wire Line + 9450 1280 9450 1160 +Connection ~ 9450 1160 +Wire Wire Line + 9860 1740 10620 1740 +Connection ~ 10620 1740 +Wire Wire Line + 9880 2940 10640 2940 +Connection ~ 10640 2940 +$Comp +L resistor R8 +U 1 1 67AA90A0 +P 4520 4430 +F 0 "R8" H 4570 4560 50 0000 C CNN +F 1 "20k" H 4570 4380 50 0000 C CNN +F 2 "" H 4570 4410 30 0000 C CNN +F 3 "" V 4570 4480 30 0000 C CNN + 1 4520 4430 + 0 1 1 0 +$EndComp +$Comp +L resistor R9 +U 1 1 67AA90A6 +P 5040 4430 +F 0 "R9" H 5090 4560 50 0000 C CNN +F 1 "10k" H 5090 4380 50 0000 C CNN +F 2 "" H 5090 4410 30 0000 C CNN +F 3 "" V 5090 4480 30 0000 C CNN + 1 5040 4430 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q7 +U 1 1 67AA90AC +P 4990 5870 +F 0 "Q7" H 4890 5920 50 0000 R CNN +F 1 "eSim_NPN" H 4940 6020 50 0000 R CNN +F 2 "" H 5190 5970 29 0000 C CNN +F 3 "" H 4990 5870 60 0000 C CNN + 1 4990 5870 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q8 +U 1 1 67AA90B2 +P 5370 5450 +F 0 "Q8" H 5270 5500 50 0000 R CNN +F 1 "eSim_NPN" H 5320 5600 50 0000 R CNN +F 2 "" H 5570 5550 29 0000 C CNN +F 3 "" H 5370 5450 60 0000 C CNN + 1 5370 5450 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q10 +U 1 1 67AA90B8 +P 6040 5000 +F 0 "Q10" H 5940 5050 50 0000 R CNN +F 1 "eSim_NPN" H 5990 5150 50 0000 R CNN +F 2 "" H 6240 5100 29 0000 C CNN +F 3 "" H 6040 5000 60 0000 C CNN + 1 6040 5000 + 1 0 0 -1 +$EndComp +$Comp +L resistor R11 +U 1 1 67AA90BE +P 5680 4440 +F 0 "R11" H 5730 4570 50 0000 C CNN +F 1 "8k" H 5730 4390 50 0000 C CNN +F 2 "" H 5730 4420 30 0000 C CNN +F 3 "" V 5730 4490 30 0000 C CNN + 1 5680 4440 + 0 1 1 0 +$EndComp +$Comp +L resistor R14 +U 1 1 67AA90C4 +P 6850 4440 +F 0 "R14" H 6900 4570 50 0000 C CNN +F 1 "120" H 6900 4390 50 0000 C CNN +F 2 "" H 6900 4420 30 0000 C CNN +F 3 "" V 6900 4490 30 0000 C CNN + 1 6850 4440 + 0 1 1 0 +$EndComp +$Comp +L zener U8 +U 1 1 67AA90CA +P 3560 6480 +F 0 "U8" H 3510 6380 60 0000 C CNN +F 1 "zener" H 3560 6580 60 0000 C CNN +F 2 "" H 3610 6480 60 0000 C CNN +F 3 "" H 3610 6480 60 0000 C CNN + 1 3560 6480 + 0 1 -1 0 +$EndComp +$Comp +L zener U10 +U 1 1 67AA90D0 +P 3830 6480 +F 0 "U10" H 3780 6380 60 0000 C CNN +F 1 "zener" H 3830 6580 60 0000 C CNN +F 2 "" H 3880 6480 60 0000 C CNN +F 3 "" H 3880 6480 60 0000 C CNN + 1 3830 6480 + 0 1 -1 0 +$EndComp +$Comp +L zener U11 +U 1 1 67AA90D6 +P 4040 6480 +F 0 "U11" H 3990 6380 60 0000 C CNN +F 1 "zener" H 4040 6580 60 0000 C CNN +F 2 "" H 4090 6480 60 0000 C CNN +F 3 "" H 4090 6480 60 0000 C CNN + 1 4040 6480 + 0 1 -1 0 +$EndComp +$Comp +L zener U14 +U 1 1 67AA90DC +P 4370 5700 +F 0 "U14" H 4320 5600 60 0000 C CNN +F 1 "zener" H 4370 5800 60 0000 C CNN +F 2 "" H 4420 5700 60 0000 C CNN +F 3 "" H 4420 5700 60 0000 C CNN + 1 4370 5700 + -1 0 0 1 +$EndComp +$Comp +L zener U13 +U 1 1 67AA90E2 +P 4370 5450 +F 0 "U13" H 4320 5350 60 0000 C CNN +F 1 "zener" H 4370 5550 60 0000 C CNN +F 2 "" H 4420 5450 60 0000 C CNN +F 3 "" H 4420 5450 60 0000 C CNN + 1 4370 5450 + -1 0 0 1 +$EndComp +$Comp +L eSim_Diode D2 +U 1 1 67AA90E8 +P 5090 6540 +F 0 "D2" H 5090 6640 50 0000 C CNN +F 1 "eSim_Diode" H 5090 6440 50 0000 C CNN +F 2 "" H 5090 6540 60 0000 C CNN +F 3 "" H 5090 6540 60 0000 C CNN + 1 5090 6540 + 0 1 1 0 +$EndComp +$Comp +L capacitor_polarised C2 +U 1 1 67AA90EE +P 6010 6000 +F 0 "C2" H 6035 6100 50 0000 L CNN +F 1 "10pF" H 6035 5900 50 0000 L CNN +F 2 "" H 6010 6000 50 0001 C CNN +F 3 "" H 6010 6000 50 0001 C CNN + 1 6010 6000 + 0 -1 -1 0 +$EndComp +$Comp +L resistor R10 +U 1 1 67AA90F4 +P 5420 6870 +F 0 "R10" H 5470 7000 50 0000 C CNN +F 1 "1.5k" H 5470 6820 50 0000 C CNN +F 2 "" H 5470 6850 30 0000 C CNN +F 3 "" V 5470 6920 30 0000 C CNN + 1 5420 6870 + 0 1 1 0 +$EndComp +$Comp +L resistor R12 +U 1 1 67AA90FA +P 5960 6580 +F 0 "R12" H 6010 6710 50 0000 C CNN +F 1 "3k" H 6010 6530 50 0000 C CNN +F 2 "" H 6010 6560 30 0000 C CNN +F 3 "" V 6010 6630 30 0000 C CNN + 1 5960 6580 + 0 1 1 0 +$EndComp +$Comp +L eSim_NPN Q9 +U 1 1 67AA9100 +P 5910 7140 +F 0 "Q9" H 5810 7190 50 0000 R CNN +F 1 "eSim_NPN" H 5860 7290 50 0000 R CNN +F 2 "" H 6110 7240 29 0000 C CNN +F 3 "" H 5910 7140 60 0000 C CNN + 1 5910 7140 + 1 0 0 -1 +$EndComp +$Comp +L eSim_NPN Q12 +U 1 1 67AA9106 +P 6820 6430 +F 0 "Q12" H 6720 6480 50 0000 R CNN +F 1 "eSim_NPN" H 6770 6580 50 0000 R CNN +F 2 "" H 7020 6530 29 0000 C CNN +F 3 "" H 6820 6430 60 0000 C CNN + 1 6820 6430 + 1 0 0 -1 +$EndComp +$Comp +L zener U15 +U 1 1 67AA910C +P 6120 5660 +F 0 "U15" H 6070 5560 60 0000 C CNN +F 1 "zener" H 6120 5760 60 0000 C CNN +F 2 "" H 6170 5660 60 0000 C CNN +F 3 "" H 6170 5660 60 0000 C CNN + 1 6120 5660 + -1 0 0 1 +$EndComp +$Comp +L resistor R13 +U 1 1 67AA9112 +P 6680 5610 +F 0 "R13" H 6730 5740 50 0000 C CNN +F 1 "5k" H 6730 5560 50 0000 C CNN +F 2 "" H 6730 5590 30 0000 C CNN +F 3 "" V 6730 5660 30 0000 C CNN + 1 6680 5610 + -1 0 0 1 +$EndComp +$Comp +L eSim_NPN Q11 +U 1 1 67AA9118 +P 6800 5230 +F 0 "Q11" H 6700 5280 50 0000 R CNN +F 1 "eSim_NPN" H 6750 5380 50 0000 R CNN +F 2 "" H 7000 5330 29 0000 C CNN +F 3 "" H 6800 5230 60 0000 C CNN + 1 6800 5230 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 4 1 67AA911E +P 3170 5210 +F 0 "U1" H 3220 5310 30 0000 C CNN +F 1 "PORT" H 3170 5210 30 0000 C CNN +F 2 "" H 3170 5210 60 0000 C CNN +F 3 "" H 3170 5210 60 0000 C CNN + 4 3170 5210 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 5 1 67AA9124 +P 3170 5450 +F 0 "U1" H 3220 5550 30 0000 C CNN +F 1 "PORT" H 3170 5450 30 0000 C CNN +F 2 "" H 3170 5450 60 0000 C CNN +F 3 "" H 3170 5450 60 0000 C CNN + 5 3170 5450 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 6 1 67AA912A +P 3170 5700 +F 0 "U1" H 3220 5800 30 0000 C CNN +F 1 "PORT" H 3170 5700 30 0000 C CNN +F 2 "" H 3170 5700 60 0000 C CNN +F 3 "" H 3170 5700 60 0000 C CNN + 6 3170 5700 + 1 0 0 -1 +$EndComp +$Comp +L PORT U1 +U 13 1 67AA9130 +P 7320 5660 +F 0 "U1" H 7370 5760 30 0000 C CNN +F 1 "PORT" H 7320 5660 30 0000 C CNN +F 2 "" H 7320 5660 60 0000 C CNN +F 3 "" H 7320 5660 60 0000 C CNN + 13 7320 5660 + -1 0 0 1 +$EndComp +$Comp +L zener U12 +U 1 1 67AA9136 +P 4370 5210 +F 0 "U12" H 4320 5110 60 0000 C CNN +F 1 "zener" H 4370 5310 60 0000 C CNN +F 2 "" H 4420 5210 60 0000 C CNN +F 3 "" H 4420 5210 60 0000 C CNN + 1 4370 5210 + -1 0 0 1 +$EndComp +Wire Wire Line + 4070 5450 3420 5450 +Wire Wire Line + 4070 5700 3420 5700 +Wire Wire Line + 4570 4630 4570 5870 +Wire Wire Line + 4070 5210 3420 5210 +Connection ~ 4570 5450 +Connection ~ 4570 5210 +Wire Wire Line + 4570 5870 4790 5870 +Connection ~ 4570 5700 +Wire Wire Line + 4040 6180 4040 5700 +Connection ~ 4040 5700 +Wire Wire Line + 3830 6180 3830 5450 +Connection ~ 3830 5450 +Wire Wire Line + 3560 6180 3560 5210 +Connection ~ 3560 5210 +$Comp +L GNDPWR #PWR03 +U 1 1 67AA914A +P 6440 7470 +F 0 "#PWR03" H 6440 7270 50 0001 C CNN +F 1 "GNDPWR" H 6440 7340 50 0000 C CNN +F 2 "" H 6440 7420 50 0001 C CNN +F 3 "" H 6440 7420 50 0001 C CNN + 1 6440 7470 + 1 0 0 -1 +$EndComp +Wire Wire Line + 6920 7470 6920 6630 +Wire Wire Line + 3560 7470 7600 7470 +Wire Wire Line + 4040 7470 4040 6680 +Connection ~ 6440 7470 +Wire Wire Line + 3830 7470 3830 6680 +Connection ~ 4040 7470 +Wire Wire Line + 3560 7470 3560 6680 +Connection ~ 3830 7470 +Wire Wire Line + 5090 6690 5090 7470 +Connection ~ 5090 7470 +Wire Wire Line + 5090 6070 5090 6390 +Wire Wire Line + 5170 5450 5090 5450 +Wire Wire Line + 5090 4630 5090 5670 +Connection ~ 5090 5450 +Wire Wire Line + 5470 5250 5470 5000 +Wire Wire Line + 5470 5000 5840 5000 +Wire Wire Line + 5730 4640 5730 5000 +Connection ~ 5730 5000 +Wire Wire Line + 5820 5660 5610 5660 +Wire Wire Line + 5610 5660 5610 5000 +Connection ~ 5610 5000 +Wire Wire Line + 5860 6000 5470 6000 +Wire Wire Line + 5470 5650 5470 6770 +Connection ~ 5470 6000 +Wire Wire Line + 6620 6430 5470 6430 +Connection ~ 5470 6430 +Wire Wire Line + 6010 6480 6010 6430 +Connection ~ 6010 6430 +Wire Wire Line + 5710 7140 5470 7140 +Wire Wire Line + 5470 7140 5470 7070 +Wire Wire Line + 6010 6780 6010 6940 +Wire Wire Line + 6010 7340 6010 7470 +Connection ~ 6010 7470 +Wire Wire Line + 6600 5230 6140 5230 +Wire Wire Line + 6140 5230 6140 5200 +Wire Wire Line + 6480 5660 6320 5660 +Wire Wire Line + 6900 5430 6900 5720 +Wire Wire Line + 6780 5660 7070 5660 +Connection ~ 6900 5660 +Wire Wire Line + 6920 6230 6920 5720 +Wire Wire Line + 6920 5720 6900 5720 +Wire Wire Line + 6900 5030 6900 4640 +Wire Wire Line + 6900 3620 6900 4340 +Wire Wire Line + 6900 4220 4570 4220 +Wire Wire Line + 4570 4220 4570 4330 +Wire Wire Line + 5090 4330 5090 4220 +Connection ~ 5090 4220 +Wire Wire Line + 5730 4340 5730 4220 +Connection ~ 5730 4220 +Wire Wire Line + 6140 4800 6900 4800 +Connection ~ 6900 4800 +Wire Wire Line + 6160 6000 6920 6000 +Connection ~ 6920 6000 +$Comp +L PORT U1 +U 8 1 67AAA48F +P 5330 830 +F 0 "U1" H 5380 930 30 0000 C CNN +F 1 "PORT" H 5330 830 30 0000 C CNN +F 2 "" H 5330 830 60 0000 C CNN +F 3 "" H 5330 830 60 0000 C CNN + 8 5330 830 + 0 1 1 0 +$EndComp +$Comp +L PORT U1 +U 9 1 67AAA999 +P 5920 830 +F 0 "U1" H 5970 930 30 0000 C CNN +F 1 "PORT" H 5920 830 30 0000 C CNN +F 2 "" H 5920 830 60 0000 C CNN +F 3 "" H 5920 830 60 0000 C CNN + 9 5920 830 + 0 1 1 0 +$EndComp +Wire Wire Line + 5030 910 5030 1300 +Wire Wire Line + 5030 1300 5480 1300 +Wire Wire Line + 5330 1080 5330 3620 +Connection ~ 4360 910 +Wire Wire Line + 5330 3620 6900 3620 +Connection ~ 5330 1300 +Connection ~ 6900 4220 +Wire Wire Line + 10620 900 7960 900 +Wire Wire Line + 7960 900 7960 1750 +Wire Wire Line + 7960 1750 5480 1750 +Wire Wire Line + 5480 1750 5480 1300 +Connection ~ 10620 1160 +Wire Wire Line + 4520 4160 4520 3860 +Wire Wire Line + 4520 3860 5920 3860 +Wire Wire Line + 5920 3860 5920 1080 +Connection ~ 4380 4160 +Wire Wire Line + 10640 4750 7050 4750 +Wire Wire Line + 7050 4750 7050 2950 +Wire Wire Line + 7050 2950 5690 2950 +Connection ~ 5920 2950 +Connection ~ 10640 4410 +Wire Wire Line + 7600 7470 7600 4640 +Wire Wire Line + 7600 4640 7150 4640 +Wire Wire Line + 7150 4640 7150 2790 +Wire Wire Line + 7150 2790 5690 2790 +Wire Wire Line + 5690 2790 5690 2950 +Connection ~ 6920 7470 +$EndSCHEMATC diff --git a/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.sub b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.sub new file mode 100644 index 000000000..a2659577d --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11.sub @@ -0,0 +1,137 @@ +* Subcircuit Subcircuit_SN74LS11 +.subckt Subcircuit_SN74LS11 net-_u1-pad1_ net-_u1-pad2_ net-_u1-pad3_ net-_u1-pad4_ net-_u1-pad5_ net-_u1-pad6_ net-_c1-pad2_ net-_r1-pad1_ gndpwr net-_u1-pad10_ net-_u1-pad11_ net-_u1-pad12_ net-_c2-pad2_ net-_c3-pad2_ +* c:\fossee\esim\library\subcircuitlibrary\subcircuit_sn74ls11\subcircuit_sn74ls11.cir +.include D.lib +.include NPN.lib +r1 net-_r1-pad1_ net-_q1-pad2_ 20k +r2 net-_r1-pad1_ net-_q1-pad1_ 10k +q1 net-_q1-pad1_ net-_q1-pad2_ net-_d1-pad1_ Q2N2222 +q2 net-_q2-pad1_ net-_q1-pad1_ net-_c1-pad1_ Q2N2222 +q4 net-_q4-pad1_ net-_q2-pad1_ net-_q4-pad3_ Q2N2222 +r4 net-_r1-pad1_ net-_q2-pad1_ 8k +r7 net-_r1-pad1_ net-_q4-pad1_ 120 +* u2 gndpwr net-_u1-pad1_ zener +* u3 gndpwr net-_u1-pad2_ zener +* u4 gndpwr net-_u1-pad3_ zener +* u7 net-_q1-pad2_ net-_u1-pad3_ zener +* u6 net-_q1-pad2_ net-_u1-pad2_ zener +d1 net-_d1-pad1_ gndpwr 1N4148 +c1 net-_c1-pad1_ net-_c1-pad2_ 10pf +r3 net-_c1-pad1_ net-_q3-pad2_ 1.5k +r5 net-_c1-pad1_ net-_q3-pad1_ 3k +q3 net-_q3-pad1_ net-_q3-pad2_ gndpwr Q2N2222 +q6 net-_c1-pad2_ net-_c1-pad1_ gndpwr Q2N2222 +* u9 net-_r6-pad2_ net-_q2-pad1_ zener +r6 net-_c1-pad2_ net-_r6-pad2_ 5k +q5 net-_q4-pad1_ net-_q4-pad3_ net-_c1-pad2_ Q2N2222 +* u5 net-_q1-pad2_ net-_u1-pad1_ zener +r15 net-_r1-pad1_ net-_q13-pad2_ 20k +r16 net-_r1-pad1_ net-_q13-pad1_ 10k +q13 net-_q13-pad1_ net-_q13-pad2_ net-_d3-pad1_ Q2N2222 +q14 net-_q14-pad1_ net-_q13-pad1_ net-_c3-pad1_ Q2N2222 +q16 net-_q16-pad1_ net-_q14-pad1_ net-_q16-pad3_ Q2N2222 +r18 net-_r1-pad1_ net-_q14-pad1_ 8k +r21 net-_r1-pad1_ net-_q16-pad1_ 120 +* u16 gndpwr net-_u1-pad10_ zener +* u17 gndpwr net-_u1-pad11_ zener +* u18 gndpwr net-_u1-pad12_ zener +* u21 net-_q13-pad2_ net-_u1-pad12_ zener +* u20 net-_q13-pad2_ net-_u1-pad11_ zener +d3 net-_d3-pad1_ gndpwr 1N4148 +c3 net-_c3-pad1_ net-_c3-pad2_ 10pf +r17 net-_c3-pad1_ net-_q15-pad2_ 1.5k +r19 net-_c3-pad1_ net-_q15-pad1_ 3k +q15 net-_q15-pad1_ net-_q15-pad2_ gndpwr Q2N2222 +q18 net-_c3-pad2_ net-_c3-pad1_ gndpwr Q2N2222 +* u22 net-_r20-pad2_ net-_q14-pad1_ zener +r20 net-_c3-pad2_ net-_r20-pad2_ 5k +q17 net-_q16-pad1_ net-_q16-pad3_ net-_c3-pad2_ Q2N2222 +* u19 net-_q13-pad2_ net-_u1-pad10_ zener +r8 net-_r1-pad1_ net-_q7-pad2_ 20k +r9 net-_r1-pad1_ net-_q7-pad1_ 10k +q7 net-_q7-pad1_ net-_q7-pad2_ net-_d2-pad1_ Q2N2222 +q8 net-_q10-pad2_ net-_q7-pad1_ net-_c2-pad1_ Q2N2222 +q10 net-_q10-pad1_ net-_q10-pad2_ net-_q10-pad3_ Q2N2222 +r11 net-_r1-pad1_ net-_q10-pad2_ 8k +r14 net-_r1-pad1_ net-_q10-pad1_ 120 +* u8 gndpwr net-_u1-pad4_ zener +* u10 gndpwr net-_u1-pad5_ zener +* u11 gndpwr net-_u1-pad6_ zener +* u14 net-_q7-pad2_ net-_u1-pad6_ zener +* u13 net-_q7-pad2_ net-_u1-pad5_ zener +d2 net-_d2-pad1_ gndpwr 1N4148 +c2 net-_c2-pad1_ net-_c2-pad2_ 10pf +r10 net-_c2-pad1_ net-_q9-pad2_ 1.5k +r12 net-_c2-pad1_ net-_q9-pad1_ 3k +q9 net-_q9-pad1_ net-_q9-pad2_ gndpwr Q2N2222 +q12 net-_c2-pad2_ net-_c2-pad1_ gndpwr Q2N2222 +* u15 net-_r13-pad2_ net-_q10-pad2_ zener +r13 net-_c2-pad2_ net-_r13-pad2_ 5k +q11 net-_q10-pad1_ net-_q10-pad3_ net-_c2-pad2_ Q2N2222 +* u12 net-_q7-pad2_ net-_u1-pad4_ zener +a1 gndpwr net-_u1-pad1_ u2 +a2 gndpwr net-_u1-pad2_ u3 +a3 gndpwr net-_u1-pad3_ u4 +a4 net-_q1-pad2_ net-_u1-pad3_ u7 +a5 net-_q1-pad2_ net-_u1-pad2_ u6 +a6 net-_r6-pad2_ net-_q2-pad1_ u9 +a7 net-_q1-pad2_ net-_u1-pad1_ u5 +a8 gndpwr net-_u1-pad10_ u16 +a9 gndpwr net-_u1-pad11_ u17 +a10 gndpwr net-_u1-pad12_ u18 +a11 net-_q13-pad2_ net-_u1-pad12_ u21 +a12 net-_q13-pad2_ net-_u1-pad11_ u20 +a13 net-_r20-pad2_ net-_q14-pad1_ u22 +a14 net-_q13-pad2_ net-_u1-pad10_ u19 +a15 gndpwr net-_u1-pad4_ u8 +a16 gndpwr net-_u1-pad5_ u10 +a17 gndpwr net-_u1-pad6_ u11 +a18 net-_q7-pad2_ net-_u1-pad6_ u14 +a19 net-_q7-pad2_ net-_u1-pad5_ u13 +a20 net-_r13-pad2_ net-_q10-pad2_ u15 +a21 net-_q7-pad2_ net-_u1-pad4_ u12 +* Schematic Name: zener, NgSpice Name: zener +.model u2 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u3 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u4 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u7 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u6 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u9 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u5 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u16 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u17 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u18 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u21 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u20 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u22 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u19 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u8 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u10 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u11 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u14 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u13 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u15 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Schematic Name: zener, NgSpice Name: zener +.model u12 zener(v_breakdown=5.6 i_breakdown=2.0e-2 i_sat=1.0e-12 n_forward=1.0 limit_switch=FALSE ) +* Control Statements + +.ends Subcircuit_SN74LS11 \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11_Previous_Values.xml b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11_Previous_Values.xml new file mode 100644 index 000000000..088a27f77 --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/Subcircuit_SN74LS11_Previous_Values.xml @@ -0,0 +1 @@ +truefalsefalseHzHz0Volts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or AmperesVolts or AmperessecsecseczenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerzenerC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Diode\D.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.libC:\FOSSEE\eSim\library\deviceModelLibrary\Transistor\NPN.lib \ No newline at end of file diff --git a/library/SubcircuitLibrary/SN74LS11/analysis b/library/SubcircuitLibrary/SN74LS11/analysis new file mode 100644 index 000000000..ebd5c0a94 --- /dev/null +++ b/library/SubcircuitLibrary/SN74LS11/analysis @@ -0,0 +1 @@ +.tran 0e-00 0e-00 0e-00 \ No newline at end of file diff --git a/src/TrackerTool/Dockerfile b/src/TrackerTool/Dockerfile new file mode 100644 index 000000000..67dbaf85c --- /dev/null +++ b/src/TrackerTool/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +ENV PYTHONUNBUFFERED=1 + +CMD ["sh", "-c", "gunicorn app:app --bind 0.0.0.0:${PORT:-5000} --workers 1 --threads 8 --timeout 0"] \ No newline at end of file diff --git a/src/TrackerTool/admin-dashboard/backend/.env.example b/src/TrackerTool/admin-dashboard/backend/.env.example new file mode 100644 index 000000000..7c14bf821 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/.env.example @@ -0,0 +1,17 @@ +# Database +DB_HOST=dpg-d61hqsq4d50c73a6686g-a.oregon-postgres.render.com +DB_PORT=5432 +DB_NAME=esim_tracker_qqjz +DB_USER=esim_user +DB_PASSWORD=JPAdTBpD748c1e1F4VV4AONfpmo2d2jJ +DB_SSLMODE=require + +# Admin API +ADMIN_HOST=127.0.0.1 +ADMIN_PORT=5001 + +# Simple admin auth token (change this!) +ADMIN_TOKEN=change-me-strong-token + +# CORS +CORS_ORIGINS=http://127.0.0.1:5500,http://localhost:5500,http://127.0.0.1:8000,http://localhost:8000 diff --git a/src/TrackerTool/admin-dashboard/backend/app.py b/src/TrackerTool/admin-dashboard/backend/app.py new file mode 100644 index 000000000..dc743b099 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/app.py @@ -0,0 +1,954 @@ +import os +from functools import wraps +from datetime import datetime, timedelta + +from flask import Flask, jsonify, request, send_from_directory +from flask_cors import CORS +from dotenv import load_dotenv + +from db import fetch_all, fetch_one, execute, execute_returning_one +from psycopg2 import errors + +load_dotenv() + +# ---------------------------- +# App + Static Frontend +# ---------------------------- +FRONTEND_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "frontend")) +app = Flask(__name__, static_folder=FRONTEND_DIR, static_url_path="") + +# CORS +origins = os.getenv("CORS_ORIGINS", "*") +if origins != "*": + origins = [o.strip() for o in origins.split(",") if o.strip()] +CORS(app, resources={r"/*": {"origins": origins}}) + +ADMIN_TOKEN = os.getenv("ADMIN_TOKEN", "change-me") + + +# ---------------------------- +# Auth +# ---------------------------- +def require_admin(fn): + @wraps(fn) + def wrapper(*args, **kwargs): + token = request.headers.get("X-Admin-Token", "") + if token != ADMIN_TOKEN: + return jsonify({"error": "Unauthorized"}), 401 + return fn(*args, **kwargs) + return wrapper + + +# ---------------------------- +# JSON Safe Helpers +# ---------------------------- +def td_to_hms(td): + if td is None: + return None + if isinstance(td, timedelta): + total_seconds = int(td.total_seconds()) + h = total_seconds // 3600 + m = (total_seconds % 3600) // 60 + s = total_seconds % 60 + return f"{h:02d}:{m:02d}:{s:02d}" + return td + +def make_json_safe(obj): + if isinstance(obj, dict): + return {k: make_json_safe(v) for k, v in obj.items()} + if isinstance(obj, list): + return [make_json_safe(x) for x in obj] + if isinstance(obj, datetime): + return obj.strftime("%Y-%m-%d %H:%M:%S") + if isinstance(obj, timedelta): + return td_to_hms(obj) + return obj + + +def parse_dt(s: str): + """Accepts 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'""" + if not s: + return None + s = s.strip() + for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d"): + try: + return datetime.strptime(s, fmt) + except ValueError: + pass + return None + +def dt_range_default(): + to_dt = datetime.now() + from_dt = to_dt - timedelta(days=30) + return from_dt, to_dt + +def get_from_to(): + dt_from = parse_dt(request.args.get("from", "").strip()) + dt_to = parse_dt(request.args.get("to", "").strip()) + if not dt_from or not dt_to: + return dt_range_default() + return dt_from, dt_to + + +# ---------------------------- +# Frontend Routes +# ---------------------------- +@app.get("/") +def serve_login(): + return send_from_directory(app.static_folder, "admin-login.html") + + +@app.get("/admin-login.html") +def serve_admin_login(): + return send_from_directory(app.static_folder, "admin-login.html") + + +@app.get("/index.html") +def serve_index(): + return send_from_directory(app.static_folder, "index.html") + + +@app.get("/health") +def health(): + return jsonify({"status": "ok"}) + + +# ---------------------------- +# Admin: Users +# ---------------------------- +@app.get("/admin/users") +@require_admin +def admin_users(): + sql = """ + SELECT DISTINCT user_id FROM ( + SELECT user_id FROM sessions + UNION + SELECT user_id FROM logs + UNION + SELECT user_id FROM crashes + ) t + ORDER BY user_id; + """ + rows = fetch_all(sql) + return jsonify([r["user_id"] for r in rows]) + + +# ---------------------------- +# Admin: Overview +# ---------------------------- +@app.get("/admin/overview") +@require_admin +def admin_overview(): + since = datetime.now() - timedelta(days=7) + + users_count = fetch_one(""" + SELECT COUNT(DISTINCT user_id) AS c FROM ( + SELECT user_id FROM sessions + UNION + SELECT user_id FROM crashes + UNION + SELECT user_id FROM logs + ) t; + """)["c"] + + sessions_7d = fetch_one(""" + SELECT COUNT(*) AS c + FROM sessions + WHERE session_start >= %s; + """, [since])["c"] + + crashes_7d = fetch_one(""" + SELECT COUNT(*) AS c + FROM crashes + WHERE crash_time >= %s; + """, [since])["c"] + + latest_sessions = fetch_all(""" + SELECT user_id, session_start, session_end, total_duration + FROM sessions + ORDER BY session_start DESC + LIMIT 8; + """) + + latest_crashes = fetch_all(""" + SELECT crash_id, user_id, crash_time, exception_code, faulting_module, event_id + FROM crashes + ORDER BY crash_time DESC + LIMIT 8; + """) + + return jsonify(make_json_safe({ + "users_count": users_count, + "sessions_7d": sessions_7d, + "crashes_7d": crashes_7d, + "latest_sessions": latest_sessions, + "latest_crashes": latest_crashes, + })) + + +# ---------------------------- +# Admin: Sessions +# ---------------------------- +@app.get("/admin/sessions") +@require_admin +def admin_sessions(): + user = request.args.get("user", "").strip() + from_s = request.args.get("from", "").strip() + to_s = request.args.get("to", "").strip() + limit = int(request.args.get("limit", "200")) + + dt_from = parse_dt(from_s) + dt_to = parse_dt(to_s) + + where = [] + params = [] + + if user: + where.append("user_id = %s") + params.append(user) + if dt_from: + where.append("session_start >= %s") + params.append(dt_from) + if dt_to: + where.append("session_start <= %s") + params.append(dt_to) + + w = ("WHERE " + " AND ".join(where)) if where else "" + + sql = f""" + SELECT session_id, user_id, session_start, session_end, total_duration, location + FROM sessions + {w} + ORDER BY session_start DESC + LIMIT %s; + """ + params.append(limit) + + return jsonify(make_json_safe(fetch_all(sql, params))) + + + +@app.delete("/admin/sessions/") +@require_admin +def admin_delete_session(session_id: int): + n = execute("DELETE FROM sessions WHERE session_id = %s;", [session_id]) + if n == 0: + return jsonify({"error": "Not found"}), 404 + return jsonify({"message": "Session deleted"}) + + +# ---------------------------- +# Admin: Logs +# ---------------------------- +@app.get("/admin/logs") +@require_admin +def admin_logs(): + user = request.args.get("user", "").strip() + limit = int(request.args.get("limit", "200")) + + where = "" + params = [] + if user: + where = "WHERE user_id=%s" + params.append(user) + + sql = f""" + SELECT log_id, user_id, log_timestamp, log_content + FROM logs + {where} + ORDER BY log_timestamp DESC + LIMIT %s; + """ + params.append(limit) + + return jsonify(make_json_safe(fetch_all(sql, params))) + + +# ---------------------------- +# Admin: Crashes (raw + summary) +# ---------------------------- +@app.get("/admin/crashes") +@require_admin +def admin_crashes(): + user = request.args.get("user", "").strip() + exc = request.args.get("exception_code", "").strip() + mod = request.args.get("faulting_module", "").strip() + q = request.args.get("q", "").strip().lower() + limit = int(request.args.get("limit", "500")) + + where = [] + params = [] + + if user: + where.append("user_id = %s") + params.append(user) + if exc: + where.append("exception_code = %s") + params.append(exc) + if mod: + where.append("faulting_module = %s") + params.append(mod) + + if q: + where.append(""" + (LOWER(COALESCE(message,'')) LIKE %s + OR LOWER(COALESCE(faulting_module,'')) LIKE %s + OR LOWER(COALESCE(exception_code,'')) LIKE %s) + """) + params.extend([f"%{q}%", f"%{q}%", f"%{q}%"]) + + w = ("WHERE " + " AND ".join(where)) if where else "" + + sql = f""" + SELECT crash_id, user_id, crash_time, session_start, session_end, + provider, event_id, exception_code, faulting_module, message + FROM crashes + {w} + ORDER BY crash_time DESC + LIMIT %s; + """ + params.append(limit) + + return jsonify(make_json_safe(fetch_all(sql, params))) + + +@app.get("/admin/crashes/summary") +@require_admin +def admin_crashes_summary(): + user = request.args.get("user", "").strip() + limit = int(request.args.get("limit", "50")) + + where = "" + params = [] + if user: + where = "WHERE user_id=%s" + params.append(user) + + sql = f""" + SELECT + CONCAT(COALESCE(exception_code,'no-code'),' | ', + COALESCE(faulting_module,'no-module'),' | ', + COALESCE(event_id::text,'0')) AS signature, + COUNT(*)::int AS count, + MAX(crash_time) AS last_seen, + LEFT(MAX(message), 120) AS example + FROM crashes + {where} + GROUP BY signature + ORDER BY count DESC, last_seen DESC + LIMIT %s; + """ + params.append(limit) + + return jsonify(make_json_safe(fetch_all(sql, params))) + + +# ---------------------------- +# Charts: Sessions +# ---------------------------- +@app.get("/admin/charts/sessions_per_user") +@require_admin +def chart_sessions_per_user(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT user_id, COUNT(*)::int AS sessions + FROM sessions + WHERE session_start >= %s AND session_start <= %s + GROUP BY user_id + ORDER BY sessions DESC; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + +@app.get("/admin/charts/session_duration_daily") +@require_admin +def chart_session_duration_daily(): + dt_from, dt_to = get_from_to() + + # Works if total_duration is INTERVAL + sql = """ + SELECT + to_char(date_trunc('day', session_start), 'YYYY-MM-DD') AS day, + ROUND(SUM(EXTRACT(EPOCH FROM total_duration)) / 3600.0, 4) AS hours + FROM sessions + WHERE session_start >= %s AND session_start <= %s + GROUP BY 1 + ORDER BY 1; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + +@app.get("/admin/charts/activity_hourly") +@require_admin +def chart_activity_hourly(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT + EXTRACT(HOUR FROM session_start)::int AS hour, + COUNT(*)::int AS sessions + FROM sessions + WHERE session_start >= %s AND session_start <= %s + GROUP BY 1 + ORDER BY 1; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + +@app.get("/admin/charts/daily_users") +@require_admin +def chart_daily_users(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT + to_char(date_trunc('day', session_start), 'YYYY-MM-DD') AS day, + COUNT(DISTINCT user_id)::int AS active_users + FROM sessions + WHERE session_start >= %s AND session_start <= %s + GROUP BY 1 + ORDER BY 1; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + + +@app.get("/admin/charts/weekly_users") +@require_admin +def chart_weekly_users(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT + to_char(date_trunc('week', session_start), 'YYYY-MM-DD') AS week, + COUNT(DISTINCT user_id)::int AS active_users + FROM sessions + WHERE session_start >= %s AND session_start <= %s + GROUP BY 1 + ORDER BY 1; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + +@app.get("/admin/charts/new_vs_returning") +@require_admin +def chart_new_vs_returning(): + dt_from, dt_to = get_from_to() + sql = """ + WITH first_seen AS ( + SELECT user_id, MIN(session_start) AS first_time + FROM sessions + GROUP BY user_id + ), + active_in_range AS ( + SELECT DISTINCT user_id + FROM sessions + WHERE session_start >= %s AND session_start <= %s + ) + SELECT + SUM(CASE WHEN f.first_time >= %s AND f.first_time <= %s THEN 1 ELSE 0 END)::int AS new_users, + SUM(CASE WHEN f.first_time < %s THEN 1 ELSE 0 END)::int AS returning_users + FROM active_in_range a + JOIN first_seen f USING(user_id); + """ + row = fetch_one(sql, [dt_from, dt_to, dt_from, dt_to, dt_from]) + return jsonify(make_json_safe(row or {"new_users": 0, "returning_users": 0})) + + +# ---------------------------- +# Charts: Crashes +# ---------------------------- +@app.get("/admin/charts/crashes_daily") +@require_admin +def chart_crashes_daily(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT + to_char(date_trunc('day', crash_time), 'YYYY-MM-DD') AS day, + COUNT(*)::int AS crashes + FROM crashes + WHERE crash_time >= %s AND crash_time <= %s + GROUP BY 1 + ORDER BY 1; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + +@app.get("/admin/charts/crashes_hourly") +@require_admin +def chart_crashes_hourly(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT + EXTRACT(HOUR FROM crash_time)::int AS hour, + COUNT(*)::int AS crashes + FROM crashes + WHERE crash_time >= %s AND crash_time <= %s + GROUP BY 1 + ORDER BY 1; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + + +@app.get("/admin/charts/crashes_by_module") +@require_admin +def chart_crashes_by_module(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT COALESCE(faulting_module,'unknown') AS module, COUNT(*)::int AS crashes + FROM crashes + WHERE crash_time >= %s AND crash_time <= %s + GROUP BY 1 + ORDER BY crashes DESC + LIMIT 12; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + + +@app.get("/admin/charts/crashes_by_exception") +@require_admin +def chart_crashes_by_exception(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT COALESCE(exception_code,'unknown') AS exception, COUNT(*)::int AS crashes + FROM crashes + WHERE crash_time >= %s AND crash_time <= %s + GROUP BY 1 + ORDER BY crashes DESC + LIMIT 12; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) + + +@app.get("/admin/charts/crashes_top_signatures") +@require_admin +def chart_crashes_top_signatures(): + dt_from, dt_to = get_from_to() + sql = """ + SELECT + CONCAT(COALESCE(exception_code,'no-code'),' | ', + COALESCE(faulting_module,'no-module'),' | ', + COALESCE(event_id::text,'0')) AS signature, + COUNT(*)::int AS crashes + FROM crashes + WHERE crash_time >= %s AND crash_time <= %s + GROUP BY 1 + ORDER BY crashes DESC + LIMIT 10; + """ + return jsonify(make_json_safe(fetch_all(sql, [dt_from, dt_to]))) +# ---------------------------- +# Location: Charts + Map Data +# ---------------------------- + +@app.get("/admin/charts/sessions_by_country") +@require_admin +def chart_sessions_by_country(): + """ + Returns: [{ country: "...", sessions: 123 }, ...] + Uses sessions.location->>'country' + """ + dt_from, dt_to = get_from_to() + limit = int(request.args.get("limit", "12")) + + sql = """ + SELECT + COALESCE(NULLIF(location->>'country',''), 'Unknown') AS country, + COUNT(*)::int AS sessions + FROM sessions + WHERE session_start >= %s AND session_start <= %s + GROUP BY 1 + ORDER BY sessions DESC + LIMIT %s; + """ + rows = fetch_all(sql, [dt_from, dt_to, limit]) + return jsonify(make_json_safe(rows)) + + +@app.get("/admin/charts/location_coverage") +@require_admin +def chart_location_coverage(): + """ + Returns: + { + total_sessions: int, + with_location: int, + without_location: int + } + + "with_location" means location is not null and has lat/lon + """ + dt_from, dt_to = get_from_to() + + sql_total = """ + SELECT COUNT(*)::int AS c + FROM sessions + WHERE session_start >= %s AND session_start <= %s; + """ + + sql_with = """ + SELECT COUNT(*)::int AS c + FROM sessions + WHERE session_start >= %s AND session_start <= %s + AND location IS NOT NULL + AND (location ? 'latitude') + AND (location ? 'longitude') + AND NULLIF(location->>'latitude','') IS NOT NULL + AND NULLIF(location->>'longitude','') IS NOT NULL; + """ + + total = fetch_one(sql_total, [dt_from, dt_to])["c"] + with_loc = fetch_one(sql_with, [dt_from, dt_to])["c"] + without_loc = int(total) - int(with_loc) + + return jsonify({ + "total_sessions": int(total), + "with_location": int(with_loc), + "without_location": int(without_loc), + }) + + +@app.get("/admin/locations") +@require_admin +def admin_locations(): + """ + Map feed: returns session points with lat/lon. + Query params: + - user (optional) + - from, to (optional; defaults last 30 days) + - limit (optional; default 2000) + """ + user = request.args.get("user", "").strip() + limit = int(request.args.get("limit", "2000")) + + dt_from, dt_to = get_from_to() + + where = [ + "session_start >= %s", + "session_start <= %s", + "location IS NOT NULL", + "(location ? 'latitude')", + "(location ? 'longitude')", + "NULLIF(location->>'latitude','') IS NOT NULL", + "NULLIF(location->>'longitude','') IS NOT NULL", + ] + params = [dt_from, dt_to] + + if user: + where.append("user_id = %s") + params.append(user) + + w = " AND ".join(where) + + sql = f""" + SELECT + session_id, + user_id, + session_start, + COALESCE(location->>'country','') AS country, + COALESCE(location->>'region','') AS region, + COALESCE(location->>'city','') AS city, + (location->>'latitude')::double precision AS latitude, + (location->>'longitude')::double precision AS longitude + FROM sessions + WHERE {w} + ORDER BY session_start DESC + LIMIT %s; + """ + params.append(limit) + + rows = fetch_all(sql, params) + return jsonify(make_json_safe(rows)) + +# ---------------------------- +# Admin: Tasks +# ---------------------------- +@app.get("/admin/tasks") +@require_admin +def admin_tasks(): + status = request.args.get("status", "").strip() + category = request.args.get("category", "").strip() + platform = request.args.get("platform", "").strip() + component = request.args.get("component", "").strip() + limit = int(request.args.get("limit", "500")) + + where = [] + params = [] + + if status: + where.append("t.status = %s") + params.append(status) + if category: + where.append("t.category = %s") + params.append(category) + if platform: + where.append("t.platform = %s") + params.append(platform) + if component: + where.append("t.component = %s") + params.append(component) + + w = ("WHERE " + " AND ".join(where)) if where else "" + + sql = f""" + SELECT + t.task_id, + t.title, + t.description, + t.category, + t.component, + t.platform, + t.status, + t.priority, + t.severity, + t.assignee, + t.created_at, + t.updated_at, + COALESCE( + ARRAY_REMOVE(ARRAY_AGG(DISTINCT r.version), NULL), + '{{}}' + ) AS release_labels + FROM tasks t + LEFT JOIN release_items ri ON ri.task_id = t.task_id + LEFT JOIN releases r ON r.release_id = ri.release_id + {w} + GROUP BY + t.task_id, t.title, t.description, t.category, t.component, + t.platform, t.status, t.priority, t.severity, t.assignee, + t.created_at, t.updated_at + ORDER BY COALESCE(t.updated_at, t.created_at) DESC + LIMIT %s; + """ + params.append(limit) + + return jsonify(make_json_safe(fetch_all(sql, params))) + + +@app.post("/admin/tasks") +@require_admin +def admin_add_task(): + data = request.get_json(force=True) or {} + + title = (data.get("title") or "").strip() + if not title: + return jsonify({"error": "Title is required"}), 400 + + sql = """ + INSERT INTO tasks ( + title, description, category, component, platform, + status, priority, severity, assignee, created_at, updated_at + ) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW()) + RETURNING task_id; + """ + + row = execute_returning_one(sql, [ + title, + data.get("description"), + data.get("category"), + data.get("component"), + data.get("platform"), + data.get("status"), + data.get("priority"), + data.get("severity"), + data.get("assignee"), + ]) + + return jsonify({"message": "Task created", "task_id": row["task_id"]}), 201 + + +@app.put("/admin/tasks/") +@require_admin +def admin_update_task(task_id: int): + data = request.get_json(force=True) or {} + + title = (data.get("title") or "").strip() + if not title: + return jsonify({"error": "Title is required"}), 400 + + sql = """ + UPDATE tasks + SET + title = %s, + description = %s, + category = %s, + component = %s, + platform = %s, + status = %s, + priority = %s, + severity = %s, + assignee = %s, + updated_at = NOW() + WHERE task_id = %s; + """ + + n = execute(sql, [ + title, + data.get("description"), + data.get("category"), + data.get("component"), + data.get("platform"), + data.get("status"), + data.get("priority"), + data.get("severity"), + data.get("assignee"), + task_id, + ]) + + if n == 0: + return jsonify({"error": "Task not found"}), 404 + + return jsonify({"message": "Task updated"}) + + +@app.delete("/admin/tasks/") +@require_admin +def admin_delete_task(task_id: int): + execute("DELETE FROM release_items WHERE task_id = %s;", [task_id]) + n = execute("DELETE FROM tasks WHERE task_id = %s;", [task_id]) + + if n == 0: + return jsonify({"error": "Task not found"}), 404 + + return jsonify({"message": "Task deleted"}) + + +# ---------------------------- +# Admin: Releases +# ---------------------------- +@app.get("/admin/releases") +@require_admin +def admin_releases(): + sql = """ + SELECT + release_id, + version, + status, + target_date, + release_date, + notes, + created_at, + updated_at + FROM releases + ORDER BY release_id DESC; + """ + return jsonify(make_json_safe(fetch_all(sql))) + + +@app.post("/admin/releases") +@require_admin +def admin_add_release(): + data = request.get_json(force=True) or {} + + version = (data.get("version") or "").strip() + if not version: + return jsonify({"error": "Version is required"}), 400 + + target_date = parse_dt(data.get("target_date") or "") + release_date = parse_dt(data.get("release_date") or "") + + sql = """ + INSERT INTO releases ( + version, status, target_date, release_date, notes, created_at, updated_at + ) + VALUES (%s, %s, %s, %s, %s, NOW(), NOW()) + RETURNING release_id; + """ + + try: + row = execute_returning_one(sql, [ + version, + data.get("status"), + target_date, + release_date, + data.get("notes"), + ]) + return jsonify({"message": "Release created", "release_id": row["release_id"]}), 201 + + except errors.UniqueViolation: + return jsonify({"error": f"Release version '{version}' already exists."}), 409 + + except Exception as e: + return jsonify({"error": str(e)}), 500 + + +@app.post("/admin/releases//items") +@require_admin +def admin_link_task_to_release(release_id: int): + data = request.get_json(force=True) or {} + task_id = data.get("task_id") + + if not task_id: + return jsonify({"error": "task_id is required"}), 400 + + release_exists = fetch_one( + "SELECT release_id FROM releases WHERE release_id = %s;", + [release_id] + ) + if not release_exists: + return jsonify({"error": "Release not found"}), 404 + + task_exists = fetch_one( + "SELECT task_id FROM tasks WHERE task_id = %s;", + [task_id] + ) + if not task_exists: + return jsonify({"error": "Task not found"}), 404 + + existing = fetch_one(""" + SELECT 1 + FROM release_items + WHERE release_id = %s AND task_id = %s; + """, [release_id, task_id]) + + if not existing: + execute(""" + INSERT INTO release_items (release_id, task_id) + VALUES (%s, %s); + """, [release_id, task_id]) + + return jsonify({"message": "Task linked to release"}) + + +@app.get("/admin/releases//progress") +@require_admin +def admin_release_progress(release_id: int): + release_exists = fetch_one( + "SELECT release_id, version, status FROM releases WHERE release_id = %s;", + [release_id] + ) + if not release_exists: + return jsonify({"error": "Release not found"}), 404 + + by_status = fetch_all(""" + SELECT t.status, COUNT(*)::int AS count + FROM release_items ri + JOIN tasks t ON t.task_id = ri.task_id + WHERE ri.release_id = %s + GROUP BY t.status + ORDER BY t.status; + """, [release_id]) + + tasks = fetch_all(""" + SELECT + t.task_id, + t.title, + t.status, + t.priority, + t.assignee, + t.updated_at + FROM release_items ri + JOIN tasks t ON t.task_id = ri.task_id + WHERE ri.release_id = %s + ORDER BY COALESCE(t.updated_at, t.created_at) DESC; + """, [release_id]) + + total_tasks = sum(r["count"] for r in by_status) if by_status else 0 + + return jsonify(make_json_safe({ + "release": release_exists, + "total_tasks": total_tasks, + "by_status": {r["status"]: r["count"] for r in by_status}, + "tasks": tasks, + })) + +if __name__ == "__main__": + host = os.getenv("ADMIN_HOST", "127.0.0.1") + port = int(os.getenv("ADMIN_PORT", "5001")) + app.run(host=host, port=port, debug=True) + + + + + diff --git a/src/TrackerTool/admin-dashboard/backend/db.py b/src/TrackerTool/admin-dashboard/backend/db.py new file mode 100644 index 000000000..eb9bcd690 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/db.py @@ -0,0 +1,76 @@ +import os +import psycopg2 +from psycopg2.extras import RealDictCursor +from datetime import datetime, date, timedelta + +def get_conn(): + return psycopg2.connect( + host=os.getenv("DB_HOST", "localhost"), + port=int(os.getenv("DB_PORT", "5435")), + dbname=os.getenv("DB_NAME", "esim_tracker"), + user=os.getenv("DB_USER", "postgres"), + password=os.getenv("DB_PASSWORD", ""), + sslmode=os.getenv("DB_SSLMODE", "prefer"), + ) + +def execute_returning_one(sql: str, params=None): + conn = get_conn() + try: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute(sql, params or []) + row = cur.fetchone() + conn.commit() + return _row_to_dict(row) if row else None + finally: + conn.close() + + +from datetime import datetime, date, timedelta + +def _json_safe(v): + if isinstance(v, datetime): + return v.isoformat(" ") + elif isinstance(v, date): + return v.isoformat() + elif isinstance(v, timedelta): + return round(v.total_seconds() / 3600, 6) + return v + +def _row_to_dict(row): + d = dict(row) + return {k: _json_safe(v) for k, v in d.items()} + +def fetch_all(sql: str, params=None): + conn = get_conn() + try: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute(sql, params or []) + rows = cur.fetchall() + return [_row_to_dict(r) for r in rows] + finally: + conn.close() + +def fetch_one(sql: str, params=None): + conn = get_conn() + try: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute(sql, params or []) + row = cur.fetchone() + + sql_start = sql.lstrip().split(None, 1)[0].upper() if sql.strip() else "" + if sql_start in {"INSERT", "UPDATE", "DELETE"}: + conn.commit() + + return _row_to_dict(row) if row else None + finally: + conn.close() + +def execute(sql: str, params=None): + conn = get_conn() + try: + with conn.cursor() as cur: + cur.execute(sql, params or []) + conn.commit() + return cur.rowcount + finally: + conn.close() \ No newline at end of file diff --git a/src/TrackerTool/admin-dashboard/backend/requirements.txt b/src/TrackerTool/admin-dashboard/backend/requirements.txt new file mode 100644 index 000000000..087d78da4 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/requirements.txt @@ -0,0 +1,4 @@ +Flask==3.0.3 +Flask-Cors==4.0.1 +python-dotenv==1.0.1 +psycopg2-binary==2.9.9 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/INSTALLER b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/LICENSE b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/LICENSE new file mode 100644 index 000000000..46d932f8d --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/LICENSE @@ -0,0 +1,7 @@ +Copyright (C) 2016 Cory Dolphin, Olin College + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/METADATA b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/METADATA new file mode 100644 index 000000000..32d8407be --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/METADATA @@ -0,0 +1,148 @@ +Metadata-Version: 2.1 +Name: Flask-Cors +Version: 4.0.1 +Summary: A Flask extension adding a decorator for CORS support +Home-page: https://github.com/corydolphin/flask-cors +Author: Cory Dolphin +Author-email: corydolphin@gmail.com +License: MIT +Platform: any +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +License-File: LICENSE +Requires-Dist: Flask >=0.9 + +Flask-CORS +========== + +|Build Status| |Latest Version| |Supported Python versions| +|License| + +A Flask extension for handling Cross Origin Resource Sharing (CORS), making cross-origin AJAX possible. + +This package has a simple philosophy: when you want to enable CORS, you wish to enable it for all use cases on a domain. +This means no mucking around with different allowed headers, methods, etc. + +By default, submission of cookies across domains is disabled due to the security implications. +Please see the documentation for how to enable credential'ed requests, and please make sure you add some sort of `CSRF `__ protection before doing so! + +Installation +------------ + +Install the extension with using pip, or easy\_install. + +.. code:: bash + + $ pip install -U flask-cors + +Usage +----- + +This package exposes a Flask extension which by default enables CORS support on all routes, for all origins and methods. +It allows parameterization of all CORS headers on a per-resource level. +The package also contains a decorator, for those who prefer this approach. + +Simple Usage +~~~~~~~~~~~~ + +In the simplest case, initialize the Flask-Cors extension with default arguments in order to allow CORS for all domains on all routes. +See the full list of options in the `documentation `__. + +.. code:: python + + + from flask import Flask + from flask_cors import CORS + + app = Flask(__name__) + CORS(app) + + @app.route("/") + def helloWorld(): + return "Hello, cross-origin-world!" + +Resource specific CORS +^^^^^^^^^^^^^^^^^^^^^^ + +Alternatively, you can specify CORS options on a resource and origin level of granularity by passing a dictionary as the `resources` option, mapping paths to a set of options. +See the full list of options in the `documentation `__. + +.. code:: python + + app = Flask(__name__) + cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) + + @app.route("/api/v1/users") + def list_users(): + return "user example" + +Route specific CORS via decorator +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This extension also exposes a simple decorator to decorate flask routes with. +Simply add ``@cross_origin()`` below a call to Flask's ``@app.route(..)`` to allow CORS on a given route. +See the full list of options in the `decorator documentation `__. + +.. code:: python + + @app.route("/") + @cross_origin() + def helloWorld(): + return "Hello, cross-origin-world!" + +Documentation +------------- + +For a full list of options, please see the full `documentation `__ + +Troubleshooting +--------------- + +If things aren't working as you expect, enable logging to help understand what is going on under the hood, and why. + +.. code:: python + + logging.getLogger('flask_cors').level = logging.DEBUG + + +Tests +----- + +A simple set of tests is included in ``test/``. +To run, install nose, and simply invoke ``nosetests`` or ``python setup.py test`` to exercise the tests. + +If nosetests does not work for you, due to it no longer working with newer python versions. +You can use pytest to run the tests instead. + +Contributing +------------ + +Questions, comments or improvements? +Please create an issue on `Github `__, tweet at `@corydolphin `__ or send me an email. +I do my best to include every contribution proposed in any way that I can. + +Credits +------- + +This Flask extension is based upon the `Decorator for the HTTP Access Control `__ written by Armin Ronacher. + +.. |Build Status| image:: https://github.com/corydolphin/flask-cors/actions/workflows/unittests.yaml/badge.svg + :target: https://travis-ci.org/corydolphin/flask-cors +.. |Latest Version| image:: https://img.shields.io/pypi/v/Flask-Cors.svg + :target: https://pypi.python.org/pypi/Flask-Cors/ +.. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/Flask-Cors.svg + :target: https://img.shields.io/pypi/pyversions/Flask-Cors.svg +.. |License| image:: http://img.shields.io/:license-mit-blue.svg + :target: https://pypi.python.org/pypi/Flask-Cors/ diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/RECORD b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/RECORD new file mode 100644 index 000000000..daf588b7e --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/RECORD @@ -0,0 +1,17 @@ +Flask_Cors-4.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask_Cors-4.0.1.dist-info/LICENSE,sha256=bhob3FSDTB4HQMvOXV9vLK4chG_Sp_SCsRZJWU-vvV0,1069 +Flask_Cors-4.0.1.dist-info/METADATA,sha256=NyVEWcY6gn4ZrDqXr_pWZYP8WDq_cqBhobJjeAePkcE,5474 +Flask_Cors-4.0.1.dist-info/RECORD,, +Flask_Cors-4.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +Flask_Cors-4.0.1.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110 +Flask_Cors-4.0.1.dist-info/top_level.txt,sha256=aWye_0QNZPp_QtPF4ZluLHqnyVLT9CPJsfiGhwqkWuo,11 +flask_cors/__init__.py,sha256=wZDCvPTHspA2g1VV7KyKN7R-uCdBnirTlsCzgPDcQtI,792 +flask_cors/__pycache__/__init__.cpython-312.pyc,, +flask_cors/__pycache__/core.cpython-312.pyc,, +flask_cors/__pycache__/decorator.cpython-312.pyc,, +flask_cors/__pycache__/extension.cpython-312.pyc,, +flask_cors/__pycache__/version.cpython-312.pyc,, +flask_cors/core.py,sha256=e1u_o5SOcS_gMWGjcQrkyk91uPICnzZ3AXZvy5jQ_FE,14063 +flask_cors/decorator.py,sha256=BeJsyX1wYhVKWN04FAhb6z8YqffiRr7wKqwzHPap4bw,5009 +flask_cors/extension.py,sha256=9IsibtRtr5ylAtvR9R-vQI0RASp0dT5ri6KnhIjLfvU,7857 +flask_cors/version.py,sha256=t3_GiRZvVdqtOM0gRAa8vnfrcrUkhvgVNLEwrIaUIcw,22 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/REQUESTED b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/REQUESTED new file mode 100644 index 000000000..e69de29bb diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/WHEEL b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/WHEEL new file mode 100644 index 000000000..832be1113 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.43.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/top_level.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/top_level.txt new file mode 100644 index 000000000..27af98818 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/Flask_Cors-4.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_cors diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt new file mode 100644 index 000000000..79c9825ad --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright 2010 Jason Kirtland + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA new file mode 100644 index 000000000..6d343f571 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/METADATA @@ -0,0 +1,60 @@ +Metadata-Version: 2.3 +Name: blinker +Version: 1.9.0 +Summary: Fast, simple object-to-object and broadcast signaling +Author: Jason Kirtland +Maintainer-email: Pallets Ecosystem +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://blinker.readthedocs.io +Project-URL: Source, https://github.com/pallets-eco/blinker/ + +# Blinker + +Blinker provides a fast dispatching system that allows any number of +interested parties to subscribe to events, or "signals". + + +## Pallets Community Ecosystem + +> [!IMPORTANT]\ +> This project is part of the Pallets Community Ecosystem. Pallets is the open +> source organization that maintains Flask; Pallets-Eco enables community +> maintenance of related projects. If you are interested in helping maintain +> this project, please reach out on [the Pallets Discord server][discord]. +> +> [discord]: https://discord.gg/pallets + + +## Example + +Signal receivers can subscribe to specific senders or receive signals +sent by any sender. + +```pycon +>>> from blinker import signal +>>> started = signal('round-started') +>>> def each(round): +... print(f"Round {round}") +... +>>> started.connect(each) + +>>> def round_two(round): +... print("This is round two.") +... +>>> started.connect(round_two, sender=2) + +>>> for round in range(1, 4): +... started.send(round) +... +Round 1! +Round 2! +This is round two. +Round 3! +``` + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD new file mode 100644 index 000000000..d4f985b69 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/RECORD @@ -0,0 +1,12 @@ +blinker-1.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +blinker-1.9.0.dist-info/LICENSE.txt,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054 +blinker-1.9.0.dist-info/METADATA,sha256=uIRiM8wjjbHkCtbCyTvctU37IAZk0kEe5kxAld1dvzA,1633 +blinker-1.9.0.dist-info/RECORD,, +blinker-1.9.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 +blinker/__init__.py,sha256=I2EdZqpy4LyjX17Hn1yzJGWCjeLaVaPzsMgHkLfj_cQ,317 +blinker/__pycache__/__init__.cpython-312.pyc,, +blinker/__pycache__/_utilities.cpython-312.pyc,, +blinker/__pycache__/base.cpython-312.pyc,, +blinker/_utilities.py,sha256=0J7eeXXTUx0Ivf8asfpx0ycVkp0Eqfqnj117x2mYX9E,1675 +blinker/base.py,sha256=QpDuvXXcwJF49lUBcH5BiST46Rz9wSG7VW_p7N_027M,19132 +blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL new file mode 100644 index 000000000..e3c6feefa --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker-1.9.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.10.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/__init__.py new file mode 100644 index 000000000..1772fa4a5 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/__init__.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from .base import ANY +from .base import default_namespace +from .base import NamedSignal +from .base import Namespace +from .base import Signal +from .base import signal + +__all__ = [ + "ANY", + "default_namespace", + "NamedSignal", + "Namespace", + "Signal", + "signal", +] diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/_utilities.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/_utilities.py new file mode 100644 index 000000000..000c902a2 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/_utilities.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import collections.abc as c +import inspect +import typing as t +from weakref import ref +from weakref import WeakMethod + +T = t.TypeVar("T") + + +class Symbol: + """A constant symbol, nicer than ``object()``. Repeated calls return the + same instance. + + >>> Symbol('foo') is Symbol('foo') + True + >>> Symbol('foo') + foo + """ + + symbols: t.ClassVar[dict[str, Symbol]] = {} + + def __new__(cls, name: str) -> Symbol: + if name in cls.symbols: + return cls.symbols[name] + + obj = super().__new__(cls) + cls.symbols[name] = obj + return obj + + def __init__(self, name: str) -> None: + self.name = name + + def __repr__(self) -> str: + return self.name + + def __getnewargs__(self) -> tuple[t.Any, ...]: + return (self.name,) + + +def make_id(obj: object) -> c.Hashable: + """Get a stable identifier for a receiver or sender, to be used as a dict + key or in a set. + """ + if inspect.ismethod(obj): + # The id of a bound method is not stable, but the id of the unbound + # function and instance are. + return id(obj.__func__), id(obj.__self__) + + if isinstance(obj, (str, int)): + # Instances with the same value always compare equal and have the same + # hash, even if the id may change. + return obj + + # Assume other types are not hashable but will always be the same instance. + return id(obj) + + +def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]: + if inspect.ismethod(obj): + return WeakMethod(obj, callback) # type: ignore[arg-type, return-value] + + return ref(obj, callback) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/base.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/base.py new file mode 100644 index 000000000..d051b94a3 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/base.py @@ -0,0 +1,512 @@ +from __future__ import annotations + +import collections.abc as c +import sys +import typing as t +import weakref +from collections import defaultdict +from contextlib import contextmanager +from functools import cached_property +from inspect import iscoroutinefunction + +from ._utilities import make_id +from ._utilities import make_ref +from ._utilities import Symbol + +F = t.TypeVar("F", bound=c.Callable[..., t.Any]) + +ANY = Symbol("ANY") +"""Symbol for "any sender".""" + +ANY_ID = 0 + + +class Signal: + """A notification emitter. + + :param doc: The docstring for the signal. + """ + + ANY = ANY + """An alias for the :data:`~blinker.ANY` sender symbol.""" + + set_class: type[set[t.Any]] = set + """The set class to use for tracking connected receivers and senders. + Python's ``set`` is unordered. If receivers must be dispatched in the order + they were connected, an ordered set implementation can be used. + + .. versionadded:: 1.7 + """ + + @cached_property + def receiver_connected(self) -> Signal: + """Emitted at the end of each :meth:`connect` call. + + The signal sender is the signal instance, and the :meth:`connect` + arguments are passed through: ``receiver``, ``sender``, and ``weak``. + + .. versionadded:: 1.2 + """ + return Signal(doc="Emitted after a receiver connects.") + + @cached_property + def receiver_disconnected(self) -> Signal: + """Emitted at the end of each :meth:`disconnect` call. + + The sender is the signal instance, and the :meth:`disconnect` arguments + are passed through: ``receiver`` and ``sender``. + + This signal is emitted **only** when :meth:`disconnect` is called + explicitly. This signal cannot be emitted by an automatic disconnect + when a weakly referenced receiver or sender goes out of scope, as the + instance is no longer be available to be used as the sender for this + signal. + + An alternative approach is available by subscribing to + :attr:`receiver_connected` and setting up a custom weakref cleanup + callback on weak receivers and senders. + + .. versionadded:: 1.2 + """ + return Signal(doc="Emitted after a receiver disconnects.") + + def __init__(self, doc: str | None = None) -> None: + if doc: + self.__doc__ = doc + + self.receivers: dict[ + t.Any, weakref.ref[c.Callable[..., t.Any]] | c.Callable[..., t.Any] + ] = {} + """The map of connected receivers. Useful to quickly check if any + receivers are connected to the signal: ``if s.receivers:``. The + structure and data is not part of the public API, but checking its + boolean value is. + """ + + self.is_muted: bool = False + self._by_receiver: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) + self._by_sender: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) + self._weak_senders: dict[t.Any, weakref.ref[t.Any]] = {} + + def connect(self, receiver: F, sender: t.Any = ANY, weak: bool = True) -> F: + """Connect ``receiver`` to be called when the signal is sent by + ``sender``. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends. + """ + receiver_id = make_id(receiver) + sender_id = ANY_ID if sender is ANY else make_id(sender) + + if weak: + self.receivers[receiver_id] = make_ref( + receiver, self._make_cleanup_receiver(receiver_id) + ) + else: + self.receivers[receiver_id] = receiver + + self._by_sender[sender_id].add(receiver_id) + self._by_receiver[receiver_id].add(sender_id) + + if sender is not ANY and sender_id not in self._weak_senders: + # store a cleanup for weakref-able senders + try: + self._weak_senders[sender_id] = make_ref( + sender, self._make_cleanup_sender(sender_id) + ) + except TypeError: + pass + + if "receiver_connected" in self.__dict__ and self.receiver_connected.receivers: + try: + self.receiver_connected.send( + self, receiver=receiver, sender=sender, weak=weak + ) + except TypeError: + # TODO no explanation or test for this + self.disconnect(receiver, sender) + raise + + return receiver + + def connect_via(self, sender: t.Any, weak: bool = False) -> c.Callable[[F], F]: + """Connect the decorated function to be called when the signal is sent + by ``sender``. + + The decorated function will be called when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument along + with any extra keyword arguments. + + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. A receiver may be connected + to multiple senders by calling :meth:`connect` multiple times. + :param weak: Track the receiver with a :mod:`weakref`. The receiver will + be automatically disconnected when it is garbage collected. When + connecting a receiver defined within a function, set to ``False``, + otherwise it will be disconnected when the function scope ends.= + + .. versionadded:: 1.1 + """ + + def decorator(fn: F) -> F: + self.connect(fn, sender, weak) + return fn + + return decorator + + @contextmanager + def connected_to( + self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY + ) -> c.Generator[None, None, None]: + """A context manager that temporarily connects ``receiver`` to the + signal while a ``with`` block executes. When the block exits, the + receiver is disconnected. Useful for tests. + + :param receiver: The callable to call when :meth:`send` is called with + the given ``sender``, passing ``sender`` as a positional argument + along with any extra keyword arguments. + :param sender: Any object or :data:`ANY`. ``receiver`` will only be + called when :meth:`send` is called with this sender. If ``ANY``, the + receiver will be called for any sender. + + .. versionadded:: 1.1 + """ + self.connect(receiver, sender=sender, weak=False) + + try: + yield None + finally: + self.disconnect(receiver) + + @contextmanager + def muted(self) -> c.Generator[None, None, None]: + """A context manager that temporarily disables the signal. No receivers + will be called if the signal is sent, until the ``with`` block exits. + Useful for tests. + """ + self.is_muted = True + + try: + yield None + finally: + self.is_muted = False + + def send( + self, + sender: t.Any | None = None, + /, + *, + _async_wrapper: c.Callable[ + [c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]]], c.Callable[..., t.Any] + ] + | None = None, + **kwargs: t.Any, + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Call all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _async_wrapper: Will be called on any receivers that are async + coroutines to turn them into sync callables. For example, could run + the receiver with an event loop. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionchanged:: 1.7 + Added the ``_async_wrapper`` argument. + """ + if self.is_muted: + return [] + + results = [] + + for receiver in self.receivers_for(sender): + if iscoroutinefunction(receiver): + if _async_wrapper is None: + raise RuntimeError("Cannot send to a coroutine function.") + + result = _async_wrapper(receiver)(sender, **kwargs) + else: + result = receiver(sender, **kwargs) + + results.append((receiver, result)) + + return results + + async def send_async( + self, + sender: t.Any | None = None, + /, + *, + _sync_wrapper: c.Callable[ + [c.Callable[..., t.Any]], c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]] + ] + | None = None, + **kwargs: t.Any, + ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: + """Await all receivers that are connected to the given ``sender`` + or :data:`ANY`. Each receiver is called with ``sender`` as a positional + argument along with any extra keyword arguments. Return a list of + ``(receiver, return value)`` tuples. + + The order receivers are called is undefined, but can be influenced by + setting :attr:`set_class`. + + If a receiver raises an exception, that exception will propagate up. + This makes debugging straightforward, with an assumption that correctly + implemented receivers will not raise. + + :param sender: Call receivers connected to this sender, in addition to + those connected to :data:`ANY`. + :param _sync_wrapper: Will be called on any receivers that are sync + callables to turn them into async coroutines. For example, + could call the receiver in a thread. + :param kwargs: Extra keyword arguments to pass to each receiver. + + .. versionadded:: 1.7 + """ + if self.is_muted: + return [] + + results = [] + + for receiver in self.receivers_for(sender): + if not iscoroutinefunction(receiver): + if _sync_wrapper is None: + raise RuntimeError("Cannot send to a non-coroutine function.") + + result = await _sync_wrapper(receiver)(sender, **kwargs) + else: + result = await receiver(sender, **kwargs) + + results.append((receiver, result)) + + return results + + def has_receivers_for(self, sender: t.Any) -> bool: + """Check if there is at least one receiver that will be called with the + given ``sender``. A receiver connected to :data:`ANY` will always be + called, regardless of sender. Does not check if weakly referenced + receivers are still live. See :meth:`receivers_for` for a stronger + search. + + :param sender: Check for receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ + if not self.receivers: + return False + + if self._by_sender[ANY_ID]: + return True + + if sender is ANY: + return False + + return make_id(sender) in self._by_sender + + def receivers_for( + self, sender: t.Any + ) -> c.Generator[c.Callable[..., t.Any], None, None]: + """Yield each receiver to be called for ``sender``, in addition to those + to be called for :data:`ANY`. Weakly referenced receivers that are not + live will be disconnected and skipped. + + :param sender: Yield receivers connected to this sender, in addition + to those connected to :data:`ANY`. + """ + # TODO: test receivers_for(ANY) + if not self.receivers: + return + + sender_id = make_id(sender) + + if sender_id in self._by_sender: + ids = self._by_sender[ANY_ID] | self._by_sender[sender_id] + else: + ids = self._by_sender[ANY_ID].copy() + + for receiver_id in ids: + receiver = self.receivers.get(receiver_id) + + if receiver is None: + continue + + if isinstance(receiver, weakref.ref): + strong = receiver() + + if strong is None: + self._disconnect(receiver_id, ANY_ID) + continue + + yield strong + else: + yield receiver + + def disconnect(self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY) -> None: + """Disconnect ``receiver`` from being called when the signal is sent by + ``sender``. + + :param receiver: A connected receiver callable. + :param sender: Disconnect from only this sender. By default, disconnect + from all senders. + """ + sender_id: c.Hashable + + if sender is ANY: + sender_id = ANY_ID + else: + sender_id = make_id(sender) + + receiver_id = make_id(receiver) + self._disconnect(receiver_id, sender_id) + + if ( + "receiver_disconnected" in self.__dict__ + and self.receiver_disconnected.receivers + ): + self.receiver_disconnected.send(self, receiver=receiver, sender=sender) + + def _disconnect(self, receiver_id: c.Hashable, sender_id: c.Hashable) -> None: + if sender_id == ANY_ID: + if self._by_receiver.pop(receiver_id, None) is not None: + for bucket in self._by_sender.values(): + bucket.discard(receiver_id) + + self.receivers.pop(receiver_id, None) + else: + self._by_sender[sender_id].discard(receiver_id) + self._by_receiver[receiver_id].discard(sender_id) + + def _make_cleanup_receiver( + self, receiver_id: c.Hashable + ) -> c.Callable[[weakref.ref[c.Callable[..., t.Any]]], None]: + """Create a callback function to disconnect a weakly referenced + receiver when it is garbage collected. + """ + + def cleanup(ref: weakref.ref[c.Callable[..., t.Any]]) -> None: + # If the interpreter is shutting down, disconnecting can result in a + # weird ignored exception. Don't call it in that case. + if not sys.is_finalizing(): + self._disconnect(receiver_id, ANY_ID) + + return cleanup + + def _make_cleanup_sender( + self, sender_id: c.Hashable + ) -> c.Callable[[weakref.ref[t.Any]], None]: + """Create a callback function to disconnect all receivers for a weakly + referenced sender when it is garbage collected. + """ + assert sender_id != ANY_ID + + def cleanup(ref: weakref.ref[t.Any]) -> None: + self._weak_senders.pop(sender_id, None) + + for receiver_id in self._by_sender.pop(sender_id, ()): + self._by_receiver[receiver_id].discard(sender_id) + + return cleanup + + def _cleanup_bookkeeping(self) -> None: + """Prune unused sender/receiver bookkeeping. Not threadsafe. + + Connecting & disconnecting leaves behind a small amount of bookkeeping + data. Typical workloads using Blinker, for example in most web apps, + Flask, CLI scripts, etc., are not adversely affected by this + bookkeeping. + + With a long-running process performing dynamic signal routing with high + volume, e.g. connecting to function closures, senders are all unique + object instances. Doing all of this over and over may cause memory usage + to grow due to extraneous bookkeeping. (An empty ``set`` for each stale + sender/receiver pair.) + + This method will prune that bookkeeping away, with the caveat that such + pruning is not threadsafe. The risk is that cleanup of a fully + disconnected receiver/sender pair occurs while another thread is + connecting that same pair. If you are in the highly dynamic, unique + receiver/sender situation that has lead you to this method, that failure + mode is perhaps not a big deal for you. + """ + for mapping in (self._by_sender, self._by_receiver): + for ident, bucket in list(mapping.items()): + if not bucket: + mapping.pop(ident, None) + + def _clear_state(self) -> None: + """Disconnect all receivers and senders. Useful for tests.""" + self._weak_senders.clear() + self.receivers.clear() + self._by_sender.clear() + self._by_receiver.clear() + + +class NamedSignal(Signal): + """A named generic notification emitter. The name is not used by the signal + itself, but matches the key in the :class:`Namespace` that it belongs to. + + :param name: The name of the signal within the namespace. + :param doc: The docstring for the signal. + """ + + def __init__(self, name: str, doc: str | None = None) -> None: + super().__init__(doc) + + #: The name of this signal. + self.name: str = name + + def __repr__(self) -> str: + base = super().__repr__() + return f"{base[:-1]}; {self.name!r}>" # noqa: E702 + + +class Namespace(dict[str, NamedSignal]): + """A dict mapping names to signals.""" + + def signal(self, name: str, doc: str | None = None) -> NamedSignal: + """Return the :class:`NamedSignal` for the given ``name``, creating it + if required. Repeated calls with the same name return the same signal. + + :param name: The name of the signal. + :param doc: The docstring of the signal. + """ + if name not in self: + self[name] = NamedSignal(name, doc) + + return self[name] + + +class _PNamespaceSignal(t.Protocol): + def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ... + + +default_namespace: Namespace = Namespace() +"""A default :class:`Namespace` for creating named signals. :func:`signal` +creates a :class:`NamedSignal` in this namespace. +""" + +signal: _PNamespaceSignal = default_namespace.signal +"""Return a :class:`NamedSignal` in :data:`default_namespace` with the given +``name``, creating it if required. Repeated calls with the same name return the +same signal. +""" diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/py.typed b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/blinker/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/INSTALLER b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/METADATA b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/METADATA new file mode 100644 index 000000000..3f433afb9 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: click +Version: 8.3.1 +Summary: Composable command line interface toolkit +Maintainer-email: Pallets +Requires-Python: >=3.10 +Description-Content-Type: text/markdown +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: colorama; platform_system == 'Windows' +Project-URL: Changes, https://click.palletsprojects.com/page/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/click/ + +
+ +# Click + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +## A Simple Example + +```python +import click + +@click.command() +@click.option("--count", default=1, help="Number of greetings.") +@click.option("--name", prompt="Your name", help="The person to greet.") +def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + +if __name__ == '__main__': + hello() +``` + +``` +$ python hello.py --count=3 +Your name: Click +Hello, Click! +Hello, Click! +Hello, Click! +``` + + +## Donate + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/RECORD b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/RECORD new file mode 100644 index 000000000..008d6973b --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/RECORD @@ -0,0 +1,40 @@ +click-8.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.3.1.dist-info/METADATA,sha256=XZeBrMAE0ghTE88SjfrSDuSyNCpBPplxJR1tbwD9oZg,2621 +click-8.3.1.dist-info/RECORD,, +click-8.3.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +click-8.3.1.dist-info/licenses/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click/__init__.py,sha256=6YyS1aeyknZ0LYweWozNZy0A9nZ_11wmYIhv3cbQrYo,4473 +click/__pycache__/__init__.cpython-312.pyc,, +click/__pycache__/_compat.cpython-312.pyc,, +click/__pycache__/_termui_impl.cpython-312.pyc,, +click/__pycache__/_textwrap.cpython-312.pyc,, +click/__pycache__/_utils.cpython-312.pyc,, +click/__pycache__/_winconsole.cpython-312.pyc,, +click/__pycache__/core.cpython-312.pyc,, +click/__pycache__/decorators.cpython-312.pyc,, +click/__pycache__/exceptions.cpython-312.pyc,, +click/__pycache__/formatting.cpython-312.pyc,, +click/__pycache__/globals.cpython-312.pyc,, +click/__pycache__/parser.cpython-312.pyc,, +click/__pycache__/shell_completion.cpython-312.pyc,, +click/__pycache__/termui.cpython-312.pyc,, +click/__pycache__/testing.cpython-312.pyc,, +click/__pycache__/types.cpython-312.pyc,, +click/__pycache__/utils.cpython-312.pyc,, +click/_compat.py,sha256=v3xBZkFbvA1BXPRkFfBJc6-pIwPI7345m-kQEnpVAs4,18693 +click/_termui_impl.py,sha256=rgCb3On8X5A4200rA5L6i13u5iapmFer7sru57Jy6zA,27093 +click/_textwrap.py,sha256=BOae0RQ6vg3FkNgSJyOoGzG1meGMxJ_ukWVZKx_v-0o,1400 +click/_utils.py,sha256=kZwtTf5gMuCilJJceS2iTCvRvCY-0aN5rJq8gKw7p8g,943 +click/_winconsole.py,sha256=_vxUuUaxwBhoR0vUWCNuHY8VUefiMdCIyU2SXPqoF-A,8465 +click/core.py,sha256=U6Bfxt8GkjNDqyJ0HqXvluJHtyZ4sY5USAvM1Cdq7mQ,132105 +click/decorators.py,sha256=5P7abhJtAQYp_KHgjUvhMv464ERwOzrv2enNknlwHyQ,18461 +click/exceptions.py,sha256=8utf8w6V5hJXMnO_ic1FNrtbwuEn1NUu1aDwV8UqnG4,9954 +click/formatting.py,sha256=RVfwwr0rwWNpgGr8NaHodPzkIr7_tUyVh_nDdanLMNc,9730 +click/globals.py,sha256=gM-Nh6A4M0HB_SgkaF5M4ncGGMDHc_flHXu9_oh4GEU,1923 +click/parser.py,sha256=Q31pH0FlQZEq-UXE_ABRzlygEfvxPTuZbWNh4xfXmzw,19010 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=Cc4GQUFuWpfQBa9sF5qXeeYI7n3tI_1k6ZdSn4BZbT0,20994 +click/termui.py,sha256=hqCEjNndU-nzW08nRAkBaVgfZp_FdCA9KxfIWlKYaMc,31037 +click/testing.py,sha256=EERbzcl1br0mW0qBS9EqkknfNfXB9WQEW0ELIpkvuSs,19102 +click/types.py,sha256=ek54BNSFwPKsqtfT7jsqcc4WHui8AIFVMKM4oVZIXhc,39927 +click/utils.py,sha256=gCUoewdAhA-QLBUUHxrLh4uj6m7T1WjZZMNPvR0I7YA,20257 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/WHEEL b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/WHEEL new file mode 100644 index 000000000..d8b9936da --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/licenses/LICENSE.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/licenses/LICENSE.txt new file mode 100644 index 000000000..d12a84918 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click-8.3.1.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/__init__.py new file mode 100644 index 000000000..1aa547c57 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/__init__.py @@ -0,0 +1,123 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" + +from __future__ import annotations + +from .core import Argument as Argument +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + from .core import _BaseCommand + + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + from .core import _MultiCommand + + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + if name == "OptionParser": + from .parser import _OptionParser + + warnings.warn( + "'OptionParser' is deprecated and will be removed in Click 9.0. The" + " old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return _OptionParser + + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Click 9.1. Use feature detection or" + " 'importlib.metadata.version(\"click\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("click") + + raise AttributeError(name) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_compat.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_compat.py new file mode 100644 index 000000000..f2726b93a --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_compat.py @@ -0,0 +1,622 @@ +from __future__ import annotations + +import codecs +import collections.abc as cabc +import io +import os +import re +import sys +import typing as t +from types import TracebackType +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +WIN = sys.platform.startswith("win") +auto_wrap_for_ansi: t.Callable[[t.TextIO], t.TextIO] | None = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: str | None, + errors: str | None, + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO[t.Any]) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: str | None, + errors: str | None, + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write(b"") + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO[t.Any]) -> t.BinaryIO | None: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO[t.Any]) -> t.BinaryIO | None: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: str | None) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: str | None, errors: str | None +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO[t.Any], + encoding: str | None, + errors: str | None, + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.BinaryIO | None], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO[t.Any], + encoding: str | None, + errors: str | None, + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO[t.Any], + encoding: str | None, + errors: str | None, + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr(encoding: str | None = None, errors: str | None = None) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: str | os.PathLike[str] | int, + mode: str, + encoding: str | None, + errors: str | None, +) -> t.IO[t.Any]: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, +) -> tuple[t.IO[t.Any], bool]: + binary = "b" in mode + filename = os.fspath(filename) + + # Standard streams first. These are simple because they ignore the + # atomic flag. Use fsdecode to handle Path("-"). + if os.fsdecode(filename) == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: int | None = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO[t.Any], af), True + + +class _AtomicFile: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> _AtomicFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi(stream: t.TextIO, color: bool | None = None) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s: str) -> int: + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write # type: ignore[method-assign] + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None + ) -> t.TextIO | None: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO[t.Any]) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO | None], + wrapper_func: t.Callable[[], t.TextIO], +) -> t.Callable[[], t.TextIO | None]: + cache: cabc.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO | None: + stream = src_func() + + if stream is None: + return None + + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: cabc.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: cabc.Mapping[str, t.Callable[[str | None, str | None], t.TextIO]] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_termui_impl.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_termui_impl.py new file mode 100644 index 000000000..ee8225c4c --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_termui_impl.py @@ -0,0 +1,852 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" + +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import math +import os +import shlex +import sys +import time +import typing as t +from gettext import gettext as _ +from io import StringIO +from pathlib import Path +from types import TracebackType + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: cabc.Iterable[V] | None, + length: int | None = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + label: str | None = None, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.hidden = hidden + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label: str = label or "" + + if file is None: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + file = StringIO() + + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width: int = width + self.autowidth: bool = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast("cabc.Iterable[V]", range(length)) + self.iter: cabc.Iterable[V] = iter(iterable) + self.length = length + self.pos: int = 0 + self.avg: list[float] = [] + self.last_eta: float + self.start: float + self.start = self.last_eta = time.time() + self.eta_known: bool = False + self.finished: bool = False + self.max_width: int | None = None + self.entered: bool = False + self.current_item: V | None = None + self._is_atty = isatty(self.file) + self._last_line: str | None = None + + def __enter__(self) -> ProgressBar[V]: + self.entered = True + self.render_progress() + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.render_finish() + + def __iter__(self) -> cabc.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.hidden or not self._is_atty: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + if self.hidden: + return + + if not self._is_atty: + # Only output the label once if the output is not a TTY. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + import shutil + + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width and self.max_width is not None: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: V | None = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> cabc.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if not self._is_atty: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: cabc.Iterable[str], color: bool | None = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if stdout is None: + stdout = StringIO() + + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + + # Split and normalize the pager command into parts. + pager_cmd_parts = shlex.split(os.environ.get("PAGER", ""), posix=False) + if pager_cmd_parts: + if WIN: + if _tempfilepager(generator, pager_cmd_parts, color): + return + elif _pipepager(generator, pager_cmd_parts, color): + return + + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if (WIN or sys.platform.startswith("os2")) and _tempfilepager( + generator, ["more"], color + ): + return + if _pipepager(generator, ["less"], color): + return + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if _pipepager(generator, ["more"], color): + return + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager( + generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None +) -> bool: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + + Returns `True` if the command was found, `False` otherwise and thus another + pager should be attempted. + """ + # Split the command into the invoked CLI and its parameters. + if not cmd_parts: + return False + + import shutil + + cmd = cmd_parts[0] + cmd_params = cmd_parts[1:] + + cmd_filepath = shutil.which(cmd) + if not cmd_filepath: + return False + + # Produces a normalized absolute path string. + # multi-call binaries such as busybox derive their identity from the symlink + # less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox) + cmd_path = Path(cmd_filepath).absolute() + cmd_name = cmd_path.name + + import subprocess + + # Make a local copy of the environment to not affect the global one. + env = dict(os.environ) + + # If we're piping to less and the user hasn't decided on colors, we enable + # them by default we find the -R flag in the command line arguments. + if color is None and cmd_name == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_params)}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen( + [str(cmd_path)] + cmd_params, + shell=False, + stdin=subprocess.PIPE, + env=env, + errors="replace", + text=True, + ) + assert c.stdin is not None + try: + for text in generator: + if not color: + text = strip_ansi(text) + + c.stdin.write(text) + except BrokenPipeError: + # In case the pager exited unexpectedly, ignore the broken pipe error. + pass + except Exception as e: + # In case there is an exception we want to close the pager immediately + # and let the caller handle it. + # Otherwise the pager will keep running, and the user may not notice + # the error message, or worse yet it may leave the terminal in a broken state. + c.terminate() + raise e + finally: + # We must close stdin and wait for the pager to exit before we continue + try: + c.stdin.close() + # Close implies flush, so it might throw a BrokenPipeError if the pager + # process exited already. + except BrokenPipeError: + pass + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + return True + + +def _tempfilepager( + generator: cabc.Iterable[str], cmd_parts: list[str], color: bool | None +) -> bool: + """Page through text by invoking a program on a temporary file. + + Returns `True` if the command was found, `False` otherwise and thus another + pager should be attempted. + """ + # Split the command into the invoked CLI and its parameters. + if not cmd_parts: + return False + + import shutil + + cmd = cmd_parts[0] + + cmd_filepath = shutil.which(cmd) + if not cmd_filepath: + return False + # Produces a normalized absolute path string. + # multi-call binaries such as busybox derive their identity from the symlink + # less -> busybox. resolve() causes them to misbehave. (eg. less becomes busybox) + cmd_path = Path(cmd_filepath).absolute() + + import subprocess + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + subprocess.call([str(cmd_path), filename]) + except OSError: + # Command not found + pass + finally: + os.close(fd) + os.unlink(filename) + + return True + + +def _nullpager( + stream: t.TextIO, generator: cabc.Iterable[str], color: bool | None +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + + from shutil import which + + for editor in "sensible-editor", "vim", "nano": + if which(editor) is not None: + return editor + return "vi" + + def edit_files(self, filenames: cabc.Iterable[str]) -> None: + import subprocess + + editor = self.get_editor() + environ: dict[str, str] | None = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + exc_filename = " ".join(f'"{filename}"' for filename in filenames) + + try: + c = subprocess.Popen( + args=f"{editor} {exc_filename}", env=environ, shell=True + ) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + @t.overload + def edit(self, text: bytes | bytearray) -> bytes | None: ... + + # We cannot know whether or not the type expected is str or bytes when None + # is passed, so str is returned as that was what was done before. + @t.overload + def edit(self, text: str | None) -> str | None: ... + + def edit(self, text: str | bytes | bytearray | None) -> str | bytes | None: + import tempfile + + if text is None: + data: bytes | bytearray = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_files((name,)) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url) + args = ["explorer", f"/select,{url}"] + else: + args = ["start"] + if wait: + args.append("/WAIT") + args.append("") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + elif CYGWIN: + if locate: + url = _unquote_file(url) + args = ["cygstart", os.path.dirname(url)] + else: + args = ["cygstart"] + if wait: + args.append("-w") + args.append(url) + try: + return subprocess.call(args) + except OSError: + # Command not found + return 127 + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> None: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if sys.platform == "win32": + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + + if echo: + func = t.cast(t.Callable[[], str], msvcrt.getwche) + else: + func = t.cast(t.Callable[[], str], msvcrt.getwch) + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + +else: + import termios + import tty + + @contextlib.contextmanager + def raw_terminal() -> cabc.Iterator[int]: + f: t.TextIO | None + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_textwrap.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_textwrap.py new file mode 100644 index 000000000..97fbee3dc --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_textwrap.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import collections.abc as cabc +import textwrap +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: list[str], + cur_line: list[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> cabc.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_utils.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_utils.py new file mode 100644 index 000000000..09fb00855 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_utils.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import enum +import typing as t + + +class Sentinel(enum.Enum): + """Enum used to define sentinel values. + + .. seealso:: + + `PEP 661 - Sentinel Values `_. + """ + + UNSET = object() + FLAG_NEEDS_VALUE = object() + + def __repr__(self) -> str: + return f"{self.__class__.__name__}.{self.name}" + + +UNSET = Sentinel.UNSET +"""Sentinel used to indicate that a value is not set.""" + +FLAG_NEEDS_VALUE = Sentinel.FLAG_NEEDS_VALUE +"""Sentinel used to indicate an option was passed as a flag without a +value but is not a flag option. + +``Option.consume_value`` uses this to prompt or use the ``flag_value``. +""" + +T_UNSET = t.Literal[UNSET] # type: ignore[valid-type] +"""Type hint for the :data:`UNSET` sentinel value.""" + +T_FLAG_NEEDS_VALUE = t.Literal[FLAG_NEEDS_VALUE] # type: ignore[valid-type] +"""Type hint for the :data:`FLAG_NEEDS_VALUE` sentinel value.""" diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_winconsole.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_winconsole.py new file mode 100644 index 000000000..e56c7c6ae --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/_winconsole.py @@ -0,0 +1,296 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +from __future__ import annotations + +import collections.abc as cabc +import io +import sys +import time +import typing as t +from ctypes import Array +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +if t.TYPE_CHECKING: + try: + # Using `typing_extensions.Buffer` instead of `collections.abc` + # on Windows for some reason does not have `Sized` implemented. + from collections.abc import Buffer # type: ignore + except ImportError: + from typing_extensions import Buffer + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ # noqa: RUF012 + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj: Buffer, writable: bool = False) -> Array[c_char]: + buf = Py_buffer() + flags: int = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + out: Array[c_char] = buffer_type.from_address(buf.buf) + return out + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle: int | None) -> None: + self.handle = handle + + def isatty(self) -> t.Literal[True]: + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self) -> t.Literal[True]: + return True + + def readinto(self, b: Buffer) -> int: + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self) -> t.Literal[True]: + return True + + @staticmethod + def _get_error_message(errno: int) -> str: + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b: Buffer) -> int: + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: cabc.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self) -> str: + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: cabc.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: str | None, errors: str | None +) -> t.TextIO | None: + if ( + get_buffer is None + or encoding not in {"utf-16-le", None} + or errors not in {"strict", None} + or not _is_console(f) + ): + return None + + func = _stream_factories.get(f.fileno()) + if func is None: + return None + + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/core.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/core.py new file mode 100644 index 000000000..57f549c79 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/core.py @@ -0,0 +1,3415 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import errno +import inspect +import os +import sys +import typing as t +from collections import abc +from collections import Counter +from contextlib import AbstractContextManager +from contextlib import contextmanager +from contextlib import ExitStack +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat +from types import TracebackType + +from . import types +from ._utils import FLAG_NEEDS_VALUE +from ._utils import UNSET +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import NoArgsIsHelpError +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _OptionParser +from .parser import _split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound="t.Callable[..., t.Any]") +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: Context, incomplete: str +) -> cabc.Iterator[tuple[str, Command]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(Group, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_nested_chain( + base_command: Group, cmd_name: str, cmd: Command, register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, Group): + return + + if register: + message = ( + f"It is not possible to add the group {cmd_name!r} to another" + f" group {base_command.name!r} that is in chain mode." + ) + else: + message = ( + f"Found the group {cmd_name!r} as subcommand to another group " + f" {base_command.name!r} that is in chain mode. This is not supported." + ) + + raise RuntimeError(message) + + +def batch(iterable: cabc.Iterable[V], batch_size: int) -> list[tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size), strict=False)) + + +@contextmanager +def augment_usage_errors( + ctx: Context, param: Parameter | None = None +) -> cabc.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: cabc.Sequence[Parameter], + declaration_order: cabc.Sequence[Parameter], +) -> list[Parameter]: + """Returns all declared parameters in the order they should be processed. + + The declared parameters are re-shuffled depending on the order in which + they were invoked, as well as the eagerness of each parameters. + + The invocation order takes precedence over the declaration order. I.e. the + order in which the user provided them to the CLI is respected. + + This behavior and its effect on callback evaluation is detailed at: + https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order + """ + + def sort_key(item: Parameter) -> tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show the default value for commands. If this + value is not set, it defaults to the value from the parent + context. ``Command.show_default`` overrides this default for the + specific command. + + .. versionchanged:: 8.2 + The ``protected_args`` attribute is deprecated and will be removed in + Click 9.0. ``args`` will contain remaining unparsed tokens. + + .. versionchanged:: 8.1 + The ``show_default`` parameter is overridden by + ``Command.show_default``, instead of the other way around. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: type[HelpFormatter] = HelpFormatter + + def __init__( + self, + command: Command, + parent: Context | None = None, + info_name: str | None = None, + obj: t.Any | None = None, + auto_envvar_prefix: str | None = None, + default_map: cabc.MutableMapping[str, t.Any] | None = None, + terminal_width: int | None = None, + max_content_width: int | None = None, + resilient_parsing: bool = False, + allow_extra_args: bool | None = None, + allow_interspersed_args: bool | None = None, + ignore_unknown_options: bool | None = None, + help_option_names: list[str] | None = None, + token_normalize_func: t.Callable[[str], str] | None = None, + color: bool | None = None, + show_default: bool | None = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: dict[str, t.Any] = {} + #: the leftover arguments. + self.args: list[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self._protected_args: list[str] = [] + #: the collected prefixes of the command's options. + self._opt_prefixes: set[str] = set(parent._opt_prefixes) if parent else set() + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: cabc.MutableMapping[str, t.Any] | None = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: str | None = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: int | None = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: int | None = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: list[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Callable[[str], str] | None = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: str | None = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: bool | None = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: bool | None = show_default + + self._close_callbacks: list[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + @property + def protected_args(self) -> list[str]: + import warnings + + warnings.warn( + "'protected_args' is deprecated and will be removed in Click 9.0." + " 'args' will contain remaining unparsed tokens.", + DeprecationWarning, + stacklevel=2, + ) + return self._protected_args + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> Context: + self._depth += 1 + push_context(self) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> bool | None: + self._depth -= 1 + exit_result: bool | None = None + if self._depth == 0: + exit_result = self._close_with_exception_info(exc_type, exc_value, tb) + pop_context() + + return exit_result + + @contextmanager + def scope(self, cleanup: bool = True) -> cabc.Iterator[Context]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: AbstractContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._close_with_exception_info(None, None, None) + + def _close_with_exception_info( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> bool | None: + """Unwind the exit stack by calling its :meth:`__exit__` providing the exception + information to allow for exception handling by the various resources registered + using :meth;`with_resource` + + :return: Whatever ``exit_stack.__exit__()`` returns. + """ + exit_result = self._exit_stack.__exit__(exc_type, exc_value, tb) + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + return exit_result + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> Context: + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: type[V]) -> V | None: + """Finds the closest object of a given type.""" + node: Context | None = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def lookup_default( + self, name: str, call: t.Literal[False] = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def lookup_default(self, name: str, call: bool = True) -> t.Any | None: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name, UNSET) + + if call and callable(value): + return value() + + return value + + return UNSET + + def fail(self, message: str) -> t.NoReturn: + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> t.NoReturn: + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> t.NoReturn: + """Exits the application with a given exit code. + + .. versionchanged:: 8.2 + Callbacks and context managers registered with :meth:`call_on_close` + and :meth:`with_resource` are closed before exiting. + """ + self.close() + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: Command) -> Context: + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + @t.overload + def invoke( + self, callback: t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> V: ... + + @t.overload + def invoke(self, callback: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: ... + + def invoke( + self, callback: Command | t.Callable[..., V], /, *args: t.Any, **kwargs: t.Any + ) -> t.Any | V: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + + .. versionchanged:: 3.2 + A new context is created, and missing arguments use default values. + """ + if isinstance(callback, Command): + other_cmd = callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + callback = t.cast("t.Callable[..., V]", other_cmd.callback) + + ctx = self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + default_value = param.get_default(ctx) + # We explicitly hide the :attr:`UNSET` value to the user, as we + # choose to make it an implementation detail. And because ``invoke`` + # has been designed as part of Click public API, we return ``None`` + # instead. Refs: + # https://github.com/pallets/click/issues/3066 + # https://github.com/pallets/click/issues/3065 + # https://github.com/pallets/click/pull/3068 + if default_value is UNSET: + default_value = None + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, default_value + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = self + + with augment_usage_errors(self): + with ctx: + return callback(*args, **kwargs) + + def forward(self, cmd: Command, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(cmd, Command): + raise TypeError("Callback is not a command.") + + for param in self.params: + if param not in kwargs: + kwargs[param] = self.params[param] + + return self.invoke(cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> ParameterSource | None: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class Command: + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the command is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. + + .. versionchanged:: 8.2 + This is the base class for all commands, not ``BaseCommand``. + ``deprecated`` can be set to a string as well to customize the + deprecation message. + + .. versionchanged:: 8.1 + ``help``, ``epilog``, and ``short_help`` are stored unprocessed, + all formatting is done when outputting help text, not at init, + and is done even if not using the ``@command`` decorator. + + .. versionchanged:: 8.0 + Added a ``repr`` showing the command name. + + .. versionchanged:: 7.1 + Added the ``no_args_is_help`` parameter. + + .. versionchanged:: 2.0 + Added the ``context_settings`` parameter. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: type[Context] = Context + + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: str | None, + context_settings: cabc.MutableMapping[str, t.Any] | None = None, + callback: t.Callable[..., t.Any] | None = None, + params: list[Parameter] | None = None, + help: str | None = None, + epilog: str | None = None, + short_help: str | None = None, + options_metavar: str | None = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool | str = False, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: cabc.MutableMapping[str, t.Any] = context_settings + + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: list[Parameter] = params or [] + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self._help_option = None + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + return { + "name": self.name, + "params": [param.to_info_dict() for param in self.get_params(ctx)], + "help": self.help, + "epilog": self.epilog, + "short_help": self.short_help, + "hidden": self.hidden, + "deprecated": self.deprecated, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> list[Parameter]: + params = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + params = [*params, help_option] + + if __debug__: + import warnings + + opts = [opt for param in params for opt in param.opts] + opts_counter = Counter(opts) + duplicate_opts = (opt for opt, count in opts_counter.items() if count > 1) + + for duplicate_opt in duplicate_opts: + warnings.warn( + ( + f"The parameter {duplicate_opt} is used more than once. " + "Remove its duplicate as parameters should be unique." + ), + stacklevel=3, + ) + + return params + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> list[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> list[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> Option | None: + """Returns the help option object. + + Skipped if :attr:`add_help_option` is ``False``. + + .. versionchanged:: 8.1.8 + The help option is now cached to avoid creating it multiple times. + """ + help_option_names = self.get_help_option_names(ctx) + + if not help_option_names or not self.add_help_option: + return None + + # Cache the help option object in private _help_option attribute to + # avoid creating it multiple times. Not doing this will break the + # callback odering by iter_params_for_processing(), which relies on + # object comparison. + if self._help_option is None: + # Avoid circular import. + from .decorators import help_option + + # Apply help_option decorator and pop resulting option + help_option(*help_option_names)(self) + self._help_option = self.params.pop() # type: ignore[assignment] + + return self._help_option + + def make_parser(self, ctx: Context) -> _OptionParser: + """Creates the underlying option parser for this command.""" + parser = _OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + if self.short_help: + text = inspect.cleandoc(self.short_help) + elif self.help: + text = make_default_short_help(self.help, limit) + else: + text = "" + + if self.deprecated: + deprecated_message = ( + f"(DEPRECATED: {self.deprecated})" + if isinstance(self.deprecated, str) + else "(DEPRECATED)" + ) + text = _("{text} {deprecated_message}").format( + text=text, deprecated_message=deprecated_message + ) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + if self.help is not None: + # truncate the help text to the first form feed + text = inspect.cleandoc(self.help).partition("\f")[0] + else: + text = "" + + if self.deprecated: + deprecated_message = ( + f"(DEPRECATED: {self.deprecated})" + if isinstance(self.deprecated, str) + else "(DEPRECATED)" + ) + text = _("{text} {deprecated_message}").format( + text=text, deprecated_message=deprecated_message + ) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + epilog = inspect.cleandoc(self.epilog) + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(epilog) + + def make_context( + self, + info_name: str | None, + args: list[str], + parent: Context | None = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class(self, info_name=info_name, parent=parent, **extra) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + _, args = param.handle_parse_result(ctx, opts, args) + + # We now have all parameters' values into `ctx.params`, but the data may contain + # the `UNSET` sentinel. + # Convert `UNSET` to `None` to ensure that the user doesn't see `UNSET`. + # + # Waiting until after the initial parse to convert allows us to treat `UNSET` + # more like a missing value when multiple params use the same name. + # Refs: + # https://github.com/pallets/click/issues/3071 + # https://github.com/pallets/click/pull/3079 + for name, value in ctx.params.items(): + if value is UNSET: + ctx.params[name] = None + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + ctx._opt_prefixes.update(parser._opt_prefixes) + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + extra_message = ( + f" {self.deprecated}" if isinstance(self.deprecated, str) else "" + ) + message = _( + "DeprecationWarning: The command {name!r} is deprecated.{extra_message}" + ).format(name=self.name, extra_message=extra_message) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: list[CompletionItem] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, Group) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx._protected_args + ) + + return results + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: t.Literal[True] = True, + **extra: t.Any, + ) -> t.NoReturn: ... + + @t.overload + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: ... + + def main( + self, + args: cabc.Sequence[str] | None = None, + prog_name: str | None = None, + complete_var: str | None = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt) as e: + echo(file=sys.stderr) + raise Abort() from e + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str | None = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + + .. versionchanged:: 8.2.0 + Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). + """ + if complete_var is None: + complete_name = prog_name.replace("-", "_").replace(".", "_") + complete_var = f"_{complete_name}_COMPLETE".upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class _FakeSubclassCheck(type): + def __subclasscheck__(cls, subclass: type) -> bool: + return issubclass(subclass, cls.__bases__[0]) + + def __instancecheck__(cls, instance: t.Any) -> bool: + return isinstance(instance, cls.__bases__[0]) + + +class _BaseCommand(Command, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Command`` instead. + """ + + +class Group(Command): + """A group is a command that nests other commands (or more groups). + + :param name: The name of the group command. + :param commands: Map names to :class:`Command` objects. Can be a list, which + will use :attr:`Command.name` as the keys. + :param invoke_without_command: Invoke the group's callback even if a + subcommand is not given. + :param no_args_is_help: If no arguments are given, show the group's help and + exit. Defaults to the opposite of ``invoke_without_command``. + :param subcommand_metavar: How to represent the subcommand argument in help. + The default will represent whether ``chain`` is set or not. + :param chain: Allow passing more than one subcommand argument. After parsing + a command's arguments, if any arguments remain another command will be + matched, and so on. + :param result_callback: A function to call after the group's and + subcommand's callbacks. The value returned by the subcommand is passed. + If ``chain`` is enabled, the value will be a list of values returned by + all the commands. If ``invoke_without_command`` is enabled, the value + will be the value returned by the group's callback, or an empty list if + ``chain`` is enabled. + :param kwargs: Other arguments passed to :class:`Command`. + + .. versionchanged:: 8.0 + The ``commands`` argument can be a list of command objects. + + .. versionchanged:: 8.2 + Merged with and replaces the ``MultiCommand`` base class. + """ + + allow_extra_args = True + allow_interspersed_args = False + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: type[Command] | None = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: type[Group] | type[type] | None = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: str | None = None, + commands: cabc.MutableMapping[str, Command] + | cabc.Sequence[Command] + | None = None, + invoke_without_command: bool = False, + no_args_is_help: bool | None = None, + subcommand_metavar: str | None = None, + chain: bool = False, + result_callback: t.Callable[..., t.Any] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: cabc.MutableMapping[str, Command] = commands + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "A group in chain mode cannot have optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def add_command(self, cmd: Command, name: str | None = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_nested_chain(self, name, cmd, register=True) + self.commands[name] = cmd + + @t.overload + def command(self, __func: t.Callable[..., t.Any]) -> Command: ... + + @t.overload + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command] | Command: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'command(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.command_class and kwargs.get("cls") is None: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd: Command = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + @t.overload + def group(self, __func: t.Callable[..., t.Any]) -> Group: ... + + @t.overload + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group]: ... + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Group] | Group: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + func: t.Callable[..., t.Any] | None = None + + if args and callable(args[0]): + assert len(args) == 1 and not kwargs, ( + "Use 'group(**kwargs)(callable)' to provide arguments." + ) + (func,) = args + args = () + + if self.group_class is not None and kwargs.get("cls") is None: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> Group: + cmd: Group = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + if func is not None: + return decorator(func) + + return decorator + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(value: t.Any, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + inner = old_callback(value, *args, **kwargs) + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv # type: ignore[return-value] + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + """Given a context and a command name, this returns a :class:`Command` + object if it exists or returns ``None``. + """ + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> list[str]: + """Returns a list of subcommand names in the order they should appear.""" + return sorted(self.commands) + + def collect_usage_pieces(self, ctx: Context) -> list[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + raise NoArgsIsHelpError(ctx) + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx._protected_args = rest + ctx.args = [] + elif rest: + ctx._protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx._protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with the group return value for regular + # groups, or an empty list for chained groups. + with ctx: + rv = super().invoke(ctx) + return _process_result([] if self.chain else rv) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx._protected_args, *ctx.args] + ctx.args = [] + ctx._protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: list[str] + ) -> tuple[str | None, Command | None, list[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if _split_opt(cmd_name)[0]: + self.parse_args(ctx, args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class _MultiCommand(Group, metaclass=_FakeSubclassCheck): + """ + .. deprecated:: 8.2 + Will be removed in Click 9.0. Use ``Group`` instead. + """ + + +class CommandCollection(Group): + """A :class:`Group` that looks up subcommands on other groups. If a command + is not found on this group, each registered source is checked in order. + Parameters on a source are not added to this group, and a source's callback + is not invoked when invoking its commands. In other words, this "flattens" + commands in many groups into this one group. + + :param name: The name of the group command. + :param sources: A list of :class:`Group` objects to look up commands from. + :param kwargs: Other arguments passed to :class:`Group`. + + .. versionchanged:: 8.2 + This is a subclass of ``Group``. Commands are looked up first on this + group, then each of its sources. + """ + + def __init__( + self, + name: str | None = None, + sources: list[Group] | None = None, + **kwargs: t.Any, + ) -> None: + super().__init__(name, **kwargs) + #: The list of registered groups. + self.sources: list[Group] = sources or [] + + def add_source(self, group: Group) -> None: + """Add a group as a source of commands.""" + self.sources.append(group) + + def get_command(self, ctx: Context, cmd_name: str) -> Command | None: + rv = super().get_command(ctx, cmd_name) + + if rv is not None: + return rv + + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_nested_chain(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> list[str]: + rv: set[str] = set(super().list_commands(ctx)) + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> cabc.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The latter is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: environment variable(s) that are used to provide a default value for + this parameter. This can be a string or a sequence of strings. If a sequence is + given, only the first non-empty environment variable is used for the parameter. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + :param deprecated: If ``True`` or non-empty string, issues a message + indicating that the argument is deprecated and highlights + its deprecation in --help. The message can be customized + by using a string as the value. A deprecated parameter + cannot be required, a ValueError will be raised otherwise. + + .. versionchanged:: 8.2.0 + Introduction of ``deprecated``. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.2 + Adding duplicate parameter names to a :class:`~click.core.Command` will + result in a ``UserWarning`` being shown. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + type: types.ParamType | t.Any | None = None, + required: bool = False, + # XXX The default historically embed two concepts: + # - the declaration of a Parameter object carrying the default (handy to + # arbitrage the default value of coupled Parameters sharing the same + # self.name, like flag options), + # - and the actual value of the default. + # It is confusing and is the source of many issues discussed in: + # https://github.com/pallets/click/pull/3030 + # In the future, we might think of splitting it in two, not unlike + # Option.is_flag and Option.flag_value: we could have something like + # Parameter.is_default and Parameter.default_value. + default: t.Any | t.Callable[[], t.Any] | None = UNSET, + callback: t.Callable[[Context, Parameter, t.Any], t.Any] | None = None, + nargs: int | None = None, + multiple: bool = False, + metavar: str | None = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: str | cabc.Sequence[str] | None = None, + shell_complete: t.Callable[ + [Context, Parameter, str], list[CompletionItem] | list[str] + ] + | None = None, + deprecated: bool | str = False, + ) -> None: + self.name: str | None + self.opts: list[str] + self.secondary_opts: list[str] + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type: types.ParamType = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default: t.Any | t.Callable[[], t.Any] | None = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + self._custom_shell_complete = shell_complete + self.deprecated = deprecated + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + if required and deprecated: + raise ValueError( + f"The {self.param_type_name} '{self.human_readable_name}' " + "is deprecated and still required. A deprecated " + f"{self.param_type_name} cannot be required." + ) + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionchanged:: 8.3.0 + Returns ``None`` for the :attr:`default` if it was not set. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + # We explicitly hide the :attr:`UNSET` value to the user, as we choose to + # make it an implementation detail. And because ``to_info_dict`` has been + # designed for documentation purposes, we return ``None`` instead. + "default": self.default if self.default is not UNSET else None, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(param=self, ctx=ctx) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @t.overload + def get_default( + self, ctx: Context, call: t.Literal[True] = True + ) -> t.Any | None: ... + + @t.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Any | t.Callable[[], t.Any] | None: ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Any | t.Callable[[], t.Any] | None: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is UNSET: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, t.Any] + ) -> tuple[t.Any, ParameterSource]: + """Returns the parameter value produced by the parser. + + If the parser did not produce a value from user input, the value is either + sourced from the environment variable, the default map, or the parameter's + default value. In that order of precedence. + + If no value is found, an internal sentinel value is returned. + + :meta private: + """ + # Collect from the parse the value passed by the user to the CLI. + value = opts.get(self.name, UNSET) # type: ignore + # If the value is set, it means it was sourced from the command line by the + # parser, otherwise it left unset by default. + source = ( + ParameterSource.COMMANDLINE + if value is not UNSET + else ParameterSource.DEFAULT + ) + + if value is UNSET: + envvar_value = self.value_from_envvar(ctx) + if envvar_value is not None: + value = envvar_value + source = ParameterSource.ENVIRONMENT + + if value is UNSET: + default_map_value = ctx.lookup_default(self.name) # type: ignore + if default_map_value is not UNSET: + value = default_map_value + source = ParameterSource.DEFAULT_MAP + + if value is UNSET: + default_value = self.get_default(ctx) + if default_value is not UNSET: + value = default_value + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the parameter's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + if self.multiple or self.nargs == -1: + return () + else: + return value + + def check_iter(value: t.Any) -> cabc.Iterator[t.Any]: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + # Define the conversion function based on nargs and type. + + if self.nargs == 1 or self.type.is_composite: + + def convert(value: t.Any) -> t.Any: + return self.type(value, param=self, ctx=ctx) + + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Any: # tuple[t.Any, ...] + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + """A value is considered missing if: + + - it is :attr:`UNSET`, + - or if it is an empty sequence while the parameter is suppose to have + non-single value (i.e. :attr:`nargs` is not ``1`` or :attr:`multiple` is + set). + + :meta private: + """ + if value is UNSET: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + """Process the value of this parameter: + + 1. Type cast the value using :meth:`type_cast_value`. + 2. Check if the value is missing (see: :meth:`value_is_missing`), and raise + :exc:`MissingParameter` if it is required. + 3. If a :attr:`callback` is set, call it to have the value replaced by the + result of the callback. If the value was not set, the callback receive + ``None``. This keep the legacy behavior as it was before the introduction of + the :attr:`UNSET` sentinel. + + :meta private: + """ + # shelter `type_cast_value` from ever seeing an `UNSET` value by handling the + # cases in which `UNSET` gets special treatment explicitly at this layer + # + # Refs: + # https://github.com/pallets/click/issues/3069 + if value is UNSET: + if self.multiple or self.nargs == -1: + value = () + else: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + # Legacy case: UNSET is not exposed directly to the callback, but converted + # to None. + if value is UNSET: + value = None + + # Search for parameters with UNSET values in the context. + unset_keys = {k: None for k, v in ctx.params.items() if v is UNSET} + # No UNSET values, call the callback as usual. + if not unset_keys: + value = self.callback(ctx, self, value) + + # Legacy case: provide a temporarily manipulated context to the callback + # to hide UNSET values as None. + # + # Refs: + # https://github.com/pallets/click/issues/3136 + # https://github.com/pallets/click/pull/3137 + else: + # Add another layer to the context stack to clearly hint that the + # context is temporarily modified. + with ctx: + # Update the context parameters to replace UNSET with None. + ctx.params.update(unset_keys) + # Feed these fake context parameters to the callback. + value = self.callback(ctx, self, value) + # Restore the UNSET values in the context parameters. + ctx.params.update( + { + k: UNSET + for k in unset_keys + # Only restore keys that are present and still None, in case + # the callback modified other parameters. + if k in ctx.params and ctx.params[k] is None + } + ) + + return value + + def resolve_envvar_value(self, ctx: Context) -> str | None: + """Returns the value found in the environment variable(s) attached to this + parameter. + + Environment variables values are `always returned as strings + `_. + + This method returns ``None`` if: + + - the :attr:`envvar` property is not set on the :class:`Parameter`, + - the environment variable is not found in the environment, + - the variable is found in the environment but its value is empty (i.e. the + environment variable is present but has an empty string). + + If :attr:`envvar` is setup with multiple environment variables, + then only the first non-empty value is returned. + + .. caution:: + + The raw value extracted from the environment is not normalized and is + returned as-is. Any normalization or reconciliation is performed later by + the :class:`Parameter`'s :attr:`type`. + + :meta private: + """ + if not self.envvar: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + # Return the first non-empty value of the list of environment variables. + if rv: + return rv + # Else, absence of value is interpreted as an environment variable that + # is not set, so proceed to the next one. + + return None + + def value_from_envvar(self, ctx: Context) -> str | cabc.Sequence[str] | None: + """Process the raw environment variable string for this parameter. + + Returns the string as-is or splits it into a sequence of strings if the + parameter is expecting multiple values (i.e. its :attr:`nargs` property is set + to a value other than ``1``). + + :meta private: + """ + rv = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + return self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: cabc.Mapping[str, t.Any], args: list[str] + ) -> tuple[t.Any, list[str]]: + """Process the value produced by the parser from user input. + + Always process the value through the Parameter's :attr:`type`, wherever it + comes from. + + If the parameter is deprecated, this method warn the user about it. But only if + the value has been explicitly set by the user (and as such, is not coming from + a default). + + :meta private: + """ + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + + ctx.set_parameter_source(self.name, source) # type: ignore + + # Display a deprecation warning if necessary. + if ( + self.deprecated + and value is not UNSET + and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP) + ): + extra_message = ( + f" {self.deprecated}" if isinstance(self.deprecated, str) else "" + ) + message = _( + "DeprecationWarning: The {param_type} {name!r} is deprecated." + "{extra_message}" + ).format( + param_type=self.param_type_name, + name=self.human_readable_name, + extra_message=extra_message, + ) + echo(style(message, fg="red"), err=True) + + # Process the value through the parameter's type. + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + # In resilient parsing mode, we do not want to fail the command if the + # value is incompatible with the parameter type, so we reset the value + # to UNSET, which will be interpreted as a missing value. + value = UNSET + + # Add parameter's value to the context. + if ( + self.expose_value + # We skip adding the value if it was previously set by another parameter + # targeting the same variable name. This prevents parameters competing for + # the same name to override each other. + and (self.name not in ctx.params or ctx.params[self.name] is UNSET) + ): + # Click is logically enforcing that the name is None if the parameter is + # not to be exposed. We still assert it here to please the type checker. + assert self.name is not None, ( + f"{self!r} parameter's name should not be None when exposing value." + ) + ctx.params[self.name] = value + + return value, args + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + pass + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> list[CompletionItem]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast("list[CompletionItem]", results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: Show the default value for this option in its + help text. Values are not shown by default, unless + :attr:`Context.show_default` is ``True``. If this value is a + string, it shows that string in parentheses instead of the + actual value. This is particularly useful for dynamic options. + For single option boolean flags, the default remains hidden if + its value is ``False``. + :param show_envvar: Controls if an environment variable should be + shown on the help page and error messages. + Normally, environment variables are not shown. + :param prompt: If set to ``True`` or a non empty string then the + user will be prompted for input. If set to ``True`` the prompt + will be the option name capitalized. A deprecated option cannot be + prompted. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: If this is ``True`` then the input on the prompt + will be hidden from the user. This is useful for password input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + :param attrs: Other command arguments described in :class:`Parameter`. + + .. versionchanged:: 8.2 + ``envvar`` used with ``flag_value`` will always use the ``flag_value``, + previously it would use the value of the environment variable. + + .. versionchanged:: 8.1 + Help text indentation is cleaned here instead of only in the + ``@option`` decorator. + + .. versionchanged:: 8.1 + The ``show_default`` parameter overrides + ``Context.show_default``. + + .. versionchanged:: 8.1 + The default of a single option boolean flag is not shown if the + default value is ``False``. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: cabc.Sequence[str] | None = None, + show_default: bool | str | None = None, + prompt: bool | str = False, + confirmation_prompt: bool | str = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: bool | None = None, + flag_value: t.Any = UNSET, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: types.ParamType | t.Any | None = None, + help: str | None = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + deprecated: bool | str = False, + **attrs: t.Any, + ) -> None: + if help: + help = inspect.cleandoc(help) + + super().__init__( + param_decls, type=type, multiple=multiple, deprecated=deprecated, **attrs + ) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: str | None = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = prompt + + if deprecated: + deprecated_message = ( + f"(DEPRECATED: {deprecated})" + if isinstance(deprecated, str) + else "(DEPRECATED)" + ) + help = help + deprecated_message if help is not None else deprecated_message + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # The _flag_needs_value property tells the parser that this option is a flag + # that cannot be used standalone and needs a value. With this information, the + # parser can determine whether to consider the next user-provided argument in + # the CLI as a value for this flag or as a new option. + # If prompt is enabled but not required, then it opens the possibility for the + # option to gets its value from the user. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + # Auto-detect if this is a flag or not. + if is_flag is None: + # Implicitly a flag because flag_value was set. + if flag_value is not UNSET: + is_flag = True + # Not a flag, but when used as a flag it shows a prompt. + elif self._flag_needs_value: + is_flag = False + # Implicitly a flag because secondary options names were given. + elif self.secondary_opts: + is_flag = True + # The option is explicitly not a flag. But we do not know yet if it needs a + # value or not. So we look at the default value to determine it. + elif is_flag is False and not self._flag_needs_value: + self._flag_needs_value = self.default is UNSET + + if is_flag: + # Set missing default for flags if not explicitly required or prompted. + if self.default is UNSET and not self.required and not self.prompt: + if multiple: + self.default = () + + # Auto-detect the type of the flag based on the flag_value. + if type is None: + # A flag without a flag_value is a boolean flag. + if flag_value is UNSET: + self.type: types.ParamType = types.BoolParamType() + # If the flag value is a boolean, use BoolParamType. + elif isinstance(flag_value, bool): + self.type = types.BoolParamType() + # Otherwise, guess the type from the flag value. + else: + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = bool(is_flag) + self.is_bool_flag: bool = bool( + is_flag and isinstance(self.type, types.BoolParamType) + ) + self.flag_value: t.Any = flag_value + + # Set boolean flag default to False if unset and not required. + if self.is_bool_flag: + if self.default is UNSET and not self.required: + self.default = False + + # Support the special case of aligning the default value with the flag_value + # for flags whose default is explicitly set to True. Note that as long as we + # have this condition, there is no way a flag can have a default set to True, + # and a flag_value set to something else. Refs: + # https://github.com/pallets/click/issues/3024#issuecomment-3146199461 + # https://github.com/pallets/click/pull/3030/commits/06847da + if self.default is True and self.flag_value is not UNSET: + self.default = self.flag_value + + # Set the default flag_value if it is not set. + if self.flag_value is UNSET: + if self.is_flag: + self.flag_value = True + else: + self.flag_value = None + + # Counting. + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if self.default is UNSET: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if deprecated and prompt: + raise ValueError("`deprecated` options cannot use `prompt`.") + + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> dict[str, t.Any]: + """ + .. versionchanged:: 8.3.0 + Returns ``None`` for the :attr:`flag_value` if it was not set. + """ + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + # We explicitly hide the :attr:`UNSET` value to the user, as we choose to + # make it an implementation detail. And because ``to_info_dict`` has been + # designed for documentation purposes, we return ``None`` instead. + flag_value=self.flag_value if self.flag_value is not UNSET else None, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def get_error_hint(self, ctx: Context) -> str: + result = super().get_error_hint(ctx) + if self.show_envvar and self.envvar is not None: + result += f" (env var: '{self.envvar}')" + return result + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(_split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(_split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError( + f"Could not determine name for option with declarations {decls!r}" + ) + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> tuple[str, str] | None: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: cabc.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar(ctx=ctx)}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + + extra = self.get_help_extra(ctx) + extra_items = [] + if "envvars" in extra: + extra_items.append( + _("env var: {var}").format(var=", ".join(extra["envvars"])) + ) + if "default" in extra: + extra_items.append(_("default: {default}").format(default=extra["default"])) + if "range" in extra: + extra_items.append(extra["range"]) + if "required" in extra: + extra_items.append(_(extra["required"])) + + if extra_items: + extra_str = "; ".join(extra_items) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + def get_help_extra(self, ctx: Context) -> types.OptionHelpExtra: + extra: types.OptionHelpExtra = {} + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + if isinstance(envvar, str): + extra["envvars"] = (envvar,) + else: + extra["envvars"] = tuple(str(d) for d in envvar) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default = False + show_default_is_str = False + + if self.show_default is not None: + if isinstance(self.show_default, str): + show_default_is_str = show_default = True + else: + show_default = self.show_default + elif ctx.show_default is not None: + show_default = ctx.show_default + + if show_default_is_str or ( + show_default and (default_value not in (None, UNSET)) + ): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif isinstance(default_value, enum.Enum): + default_string = default_value.name + elif inspect.isfunction(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = _split_opt( + (self.opts if default_value else self.secondary_opts)[0] + )[1] + elif self.is_bool_flag and not self.secondary_opts and not default_value: + default_string = "" + elif default_value == "": + default_string = '""' + else: + default_string = str(default_value) + + if default_string: + extra["default"] = default_string + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra["range"] = range_str + + if self.required: + extra["required"] = "required" + + return extra + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to lock in the value before + # attempting any user interaction. + default = self.get_default(ctx) + + # A boolean flag can use a simplified [y/n] confirmation prompt. + if self.is_bool_flag: + # If we have no boolean default, we force the user to explicitly provide + # one. + if default in (UNSET, None): + default = None + # Nothing prevent you to declare an option that is simultaneously: + # 1) auto-detected as a boolean flag, + # 2) allowed to prompt, and + # 3) still declare a non-boolean default. + # This forced casting into a boolean is necessary to align any non-boolean + # default to the prompt, which is going to be a [y/n]-style confirmation + # because the option is still a boolean flag. That way, instead of [y/n], + # we get [Y/n] or [y/N] depending on the truthy value of the default. + # Refs: https://github.com/pallets/click/pull/3030#discussion_r2289180249 + else: + default = bool(default) + return confirm(self.prompt, default) + + # If show_default is set to True/False, provide this to `prompt` as well. For + # non-bool values of `show_default`, we use `prompt`'s default behavior + prompt_kwargs: t.Any = {} + if isinstance(self.show_default, bool): + prompt_kwargs["show_default"] = self.show_default + + return prompt( + self.prompt, + # Use ``None`` to inform the prompt() function to reiterate until a valid + # value is provided by the user if we have no default. + default=None if default is UNSET else default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + **prompt_kwargs, + ) + + def resolve_envvar_value(self, ctx: Context) -> str | None: + """:class:`Option` resolves its environment variable the same way as + :func:`Parameter.resolve_envvar_value`, but it also supports + :attr:`Context.auto_envvar_prefix`. If we could not find an environment from + the :attr:`envvar` property, we fallback on :attr:`Context.auto_envvar_prefix` + to build dynamiccaly the environment variable name using the + :python:`{ctx.auto_envvar_prefix}_{self.name.upper()}` template. + + :meta private: + """ + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Any: + """For :class:`Option`, this method processes the raw environment variable + string the same way as :func:`Parameter.value_from_envvar` does. + + But in the case of non-boolean flags, the value is analyzed to determine if the + flag is activated or not, and returns a boolean of its activation, or the + :attr:`flag_value` if the latter is set. + + This method also takes care of repeated options (i.e. options with + :attr:`multiple` set to ``True``). + + :meta private: + """ + rv = self.resolve_envvar_value(ctx) + + # Absent environment variable or an empty string is interpreted as unset. + if rv is None: + return None + + # Non-boolean flags are more liberal in what they accept. But a flag being a + # flag, its envvar value still needs to be analyzed to determine if the flag is + # activated or not. + if self.is_flag and not self.is_bool_flag: + # If the flag_value is set and match the envvar value, return it + # directly. + if self.flag_value is not UNSET and rv == self.flag_value: + return self.flag_value + # Analyze the envvar value as a boolean to know if the flag is + # activated or not. + return types.BoolParamType.str_to_bool(rv) + + # Split the envvar value if it is allowed to be repeated. + value_depth = (self.nargs != 1) + bool(self.multiple) + if value_depth > 0: + multi_rv = self.type.split_envvar_value(rv) + if self.multiple and self.nargs != 1: + multi_rv = batch(multi_rv, self.nargs) # type: ignore[assignment] + + return multi_rv + + return rv + + def consume_value( + self, ctx: Context, opts: cabc.Mapping[str, Parameter] + ) -> tuple[t.Any, ParameterSource]: + """For :class:`Option`, the value can be collected from an interactive prompt + if the option is a flag that needs a value (and the :attr:`prompt` property is + set). + + Additionally, this method handles flag option that are activated without a + value, in which case the :attr:`flag_value` is returned. + + :meta private: + """ + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option is allowed to as a flag + # without a value. + if value is FLAG_NEEDS_VALUE: + # If the option allows for a prompt, we start an interaction with the user. + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + # Else the flag takes its flag_value as value. + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + # A flag which is activated always returns the flag value, unless the value + # comes from the explicitly sets default. + elif ( + self.is_flag + and value is True + and not self.is_bool_flag + and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP) + ): + value = self.flag_value + + # Re-interpret a multiple option which has been sent as-is by the parser. + # Here we replace each occurrence of value-less flags (marked by the + # FLAG_NEEDS_VALUE sentinel) with the flag_value. + elif ( + self.multiple + and value is not UNSET + and source not in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP) + and any(v is FLAG_NEEDS_VALUE for v in value) + ): + value = [self.flag_value if v is FLAG_NEEDS_VALUE else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt for one to the user + # if prompting is enabled. + elif ( + ( + value is UNSET + or source in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP) + ) + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + # process_value has to be overridden on Options in order to capture + # `value == UNSET` cases before `type_cast_value()` gets called. + # + # Refs: + # https://github.com/pallets/click/issues/3069 + if self.is_flag and not self.required and self.is_bool_flag and value is UNSET: + value = False + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + # in the normal case, rely on Parameter.process_value + return super().process_value(ctx, value) + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the constructor of :class:`Parameter`. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: cabc.Sequence[str], + required: bool | None = None, + **attrs: t.Any, + ) -> None: + # Auto-detect the requirement status of the argument if not explicitly set. + if required is None: + # The argument gets automatically required if it has no explicit default + # value set and is setup to match at least one value. + if attrs.get("default", UNSET) is UNSET: + required = attrs.get("nargs", 1) > 0 + # If the argument has a default value, it is not required. + else: + required = False + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self, ctx: Context) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(param=self, ctx=ctx) + if not var: + var = self.name.upper() # type: ignore + if self.deprecated: + var += "!" + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: cabc.Sequence[str], expose_value: bool + ) -> tuple[str | None, list[str], list[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Argument is marked as exposed, but does not have a name.") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}: {decls}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> list[str]: + return [self.make_metavar(ctx)] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar(ctx)}'" + + def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) + + +def __getattr__(name: str) -> object: + import warnings + + if name == "BaseCommand": + warnings.warn( + "'BaseCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Command' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _BaseCommand + + if name == "MultiCommand": + warnings.warn( + "'MultiCommand' is deprecated and will be removed in Click 9.0. Use" + " 'Group' instead.", + DeprecationWarning, + stacklevel=2, + ) + return _MultiCommand + + raise AttributeError(name) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/decorators.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/decorators.py new file mode 100644 index 000000000..21f4c3422 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/decorators.py @@ -0,0 +1,551 @@ +from __future__ import annotations + +import inspect +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") +T = t.TypeVar("T") +_AnyCallable = t.Callable[..., t.Any] +FC = t.TypeVar("FC", bound="_AnyCallable | Command") + + +def pass_context(f: t.Callable[te.Concatenate[Context, P], R]) -> t.Callable[P, R]: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(new_func, f) + + +def pass_obj(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + +def make_pass_decorator( + object_type: type[T], ensure: bool = False +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + + obj: T | None + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: str | None = None +) -> t.Callable[[t.Callable[te.Concatenate[T, P], R]], t.Callable[P, R]]: + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: t.Callable[te.Concatenate[T, P], R]) -> t.Callable[P, R]: + def new_func(*args: P.args, **kwargs: P.kwargs) -> R: + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(new_func, f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +CmdType = t.TypeVar("CmdType", bound=Command) + + +# variant: no call, directly as decorator for a function. +@t.overload +def command(name: _AnyCallable) -> Command: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) +@t.overload +def command( + name: str | None, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) +@t.overload +def command( + name: None = None, + *, + cls: type[CmdType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], CmdType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def command( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Command]: ... + + +def command( + name: str | _AnyCallable | None = None, + cls: type[CmdType] | None = None, + **attrs: t.Any, +) -> Command | t.Callable[[_AnyCallable], Command | CmdType]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function, converted to + lowercase, with underscores ``_`` replaced by dashes ``-``, and the suffixes + ``_command``, ``_cmd``, ``_group``, and ``_grp`` are removed. For example, + ``init_data_command`` becomes ``init-data``. + + All keyword arguments are forwarded to the underlying command class. + For the ``params`` argument, any decorated params are appended to + the end of the list. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: The name of the command. Defaults to modifying the function's + name as described above. + :param cls: The command class to create. Defaults to :class:`Command`. + + .. versionchanged:: 8.2 + The suffixes ``_command``, ``_cmd``, ``_group``, and ``_grp`` are + removed when generating the name. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + + .. versionchanged:: 8.1 + The ``params`` argument can be used. Decorated params are + appended to the end of the list. + """ + + func: t.Callable[[_AnyCallable], t.Any] | None = None + + if callable(name): + func = name + name = None + assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." + assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." + + if cls is None: + cls = t.cast("type[CmdType]", Command) + + def decorator(f: _AnyCallable) -> CmdType: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + attr_params = attrs.pop("params", None) + params = attr_params if attr_params is not None else [] + + try: + decorator_params = f.__click_params__ # type: ignore + except AttributeError: + pass + else: + del f.__click_params__ # type: ignore + params.extend(reversed(decorator_params)) + + if attrs.get("help") is None: + attrs["help"] = f.__doc__ + + if t.TYPE_CHECKING: + assert cls is not None + assert not callable(name) + + if name is not None: + cmd_name = name + else: + cmd_name = f.__name__.lower().replace("_", "-") + cmd_left, sep, suffix = cmd_name.rpartition("-") + + if sep and suffix in {"command", "cmd", "group", "grp"}: + cmd_name = cmd_left + + cmd = cls(name=cmd_name, callback=f, params=params, **attrs) + cmd.__doc__ = f.__doc__ + return cmd + + if func is not None: + return decorator(func) + + return decorator + + +GrpType = t.TypeVar("GrpType", bound=Group) + + +# variant: no call, directly as decorator for a function. +@t.overload +def group(name: _AnyCallable) -> Group: ... + + +# variant: with positional name and with positional or keyword cls argument: +# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) +@t.overload +def group( + name: str | None, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) +@t.overload +def group( + name: None = None, + *, + cls: type[GrpType], + **attrs: t.Any, +) -> t.Callable[[_AnyCallable], GrpType]: ... + + +# variant: with optional string name, no cls argument provided. +@t.overload +def group( + name: str | None = ..., cls: None = None, **attrs: t.Any +) -> t.Callable[[_AnyCallable], Group]: ... + + +def group( + name: str | _AnyCallable | None = None, + cls: type[GrpType] | None = None, + **attrs: t.Any, +) -> Group | t.Callable[[_AnyCallable], Group | GrpType]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + + .. versionchanged:: 8.1 + This decorator can be applied without parentheses. + """ + if cls is None: + cls = t.cast("type[GrpType]", Group) + + if callable(name): + return command(cls=cls, **attrs)(name) + + return command(name, cls, **attrs) + + +def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument( + *param_decls: str, cls: type[Argument] | None = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default argument class, refer to :class:`Argument` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Argument + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def option( + *param_decls: str, cls: type[Option] | None = None, **attrs: t.Any +) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + For the default option class, refer to :class:`Option` and + :class:`Parameter` for descriptions of parameters. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + :param param_decls: Passed as positional arguments to the constructor of + ``cls``. + :param attrs: Passed as keyword arguments to the constructor of ``cls``. + """ + if cls is None: + cls = Option + + def decorator(f: FC) -> FC: + _param_memo(f, cls(param_decls, **attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: str | None = None, + *param_decls: str, + package_name: str | None = None, + prog_name: str | None = None, + message: str | None = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + import importlib.metadata + + try: + version = importlib.metadata.version(package_name) + except importlib.metadata.PackageNotFoundError: + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + message % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Pre-configured ``--help`` option which immediately prints the help page + and exits the program. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def show_help(ctx: Context, param: Parameter, value: bool) -> None: + """Callback that print the help page on ```` and exits.""" + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs.setdefault("callback", show_help) + + return option(*param_decls, **kwargs) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/exceptions.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/exceptions.py new file mode 100644 index 000000000..4d782ee36 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/exceptions.py @@ -0,0 +1,308 @@ +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .globals import resolve_color_default +from .utils import echo +from .utils import format_filename + +if t.TYPE_CHECKING: + from .core import Command + from .core import Context + from .core import Parameter + + +def _join_param_hints(param_hint: cabc.Sequence[str] | str | None) -> str | None: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + # The context will be removed by the time we print the message, so cache + # the color settings here to be used later on (in `show`) + self.show_color: bool | None = resolve_color_default() + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=self.show_color, + ) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: Context | None = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd: Command | None = self.ctx.command if self.ctx else None + + def show(self, file: t.IO[t.Any] | None = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: cabc.Sequence[str] | str | None = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: str | None = None, + ctx: Context | None = None, + param: Parameter | None = None, + param_hint: cabc.Sequence[str] | str | None = None, + param_type: str | None = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: cabc.Sequence[str] | str | None = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message( + param=self.param, ctx=self.ctx + ) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: str | None = None, + possibilities: cabc.Sequence[str] | None = None, + ctx: Context | None = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: Context | None = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class NoArgsIsHelpError(UsageError): + def __init__(self, ctx: Context) -> None: + self.ctx: Context + super().__init__(ctx.get_help(), ctx=ctx) + + def show(self, file: t.IO[t.Any] | None = None) -> None: + echo(self.format_message(), file=file, err=True, color=self.ctx.color) + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: str | None = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename: str = format_filename(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code: int = code diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/formatting.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/formatting.py new file mode 100644 index 000000000..0b64f831b --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +from __future__ import annotations + +import collections.abc as cabc +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import _split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: int | None = None + + +def measure_table(rows: cabc.Iterable[tuple[str, str]]) -> tuple[int, ...]: + widths: dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: cabc.Iterable[tuple[str, str]], col_count: int +) -> cabc.Iterator[tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: list[tuple[int, bool, str]] = [] + buf: list[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: int | None = None, + max_width: int | None = None, + ) -> None: + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + import shutil + + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent: int = 0 + self.buffer: list[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage(self, prog: str, args: str = "", prefix: str | None = None) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: cabc.Sequence[tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> cabc.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> cabc.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: cabc.Sequence[str]) -> tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = _split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/globals.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/globals.py new file mode 100644 index 000000000..a2f91723d --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/globals.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +import typing as t +from threading import local + +if t.TYPE_CHECKING: + from .core import Context + +_local = local() + + +@t.overload +def get_current_context(silent: t.Literal[False] = False) -> Context: ... + + +@t.overload +def get_current_context(silent: bool = ...) -> Context | None: ... + + +def get_current_context(silent: bool = False) -> Context | None: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: Context) -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: bool | None = None) -> bool | None: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/parser.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/parser.py new file mode 100644 index 000000000..1ea1f7166 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/parser.py @@ -0,0 +1,532 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" + +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +from __future__ import annotations + +import collections.abc as cabc +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from ._utils import FLAG_NEEDS_VALUE +from ._utils import UNSET +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + from ._utils import T_FLAG_NEEDS_VALUE + from ._utils import T_UNSET + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + + +def _unpack_args( + args: cabc.Sequence[str], nargs_spec: cabc.Sequence[int] +) -> tuple[cabc.Sequence[str | cabc.Sequence[str | None] | None], list[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with ``UNSET``. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: list[str | tuple[str | T_UNSET, ...] | T_UNSET] = [] + spos: int | None = None + + def _fetch(c: deque[V]) -> V | T_UNSET: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return UNSET + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) # type: ignore[arg-type] + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(UNSET) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def _split_opt(opt: str) -> tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def _normalize_opt(opt: str, ctx: Context | None) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = _split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +class _Option: + def __init__( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes: set[str] = set() + + for opt in opts: + prefix, value = _split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: t.Any, state: _ParsingState) -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class _Argument: + def __init__(self, obj: CoreArgument, dest: str | None, nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: str | cabc.Sequence[str | None] | None | T_UNSET, + state: _ParsingState, + ) -> None: + if self.nargs > 1: + assert isinstance(value, cabc.Sequence) + holes = sum(1 for x in value if x is UNSET) + if holes == len(value): + value = UNSET + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + # We failed to collect any argument value so we consider the argument as unset. + if value == (): + value = UNSET + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class _ParsingState: + def __init__(self, rargs: list[str]) -> None: + self.opts: dict[str, t.Any] = {} + self.largs: list[str] = [] + self.rargs = rargs + self.order: list[CoreParameter] = [] + + +class _OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + + .. deprecated:: 8.2 + Will be removed in Click 9.0. + """ + + def __init__(self, ctx: Context | None = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args: bool = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options: bool = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: dict[str, _Option] = {} + self._long_opt: dict[str, _Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: list[_Argument] = [] + + def add_option( + self, + obj: CoreOption, + opts: cabc.Sequence[str], + dest: str | None, + action: str | None = None, + nargs: int = 1, + const: t.Any | None = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [_normalize_opt(opt, self.ctx) for opt in opts] + option = _Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument(self, obj: CoreArgument, dest: str | None, nargs: int = 1) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(_Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: list[str] + ) -> tuple[dict[str, t.Any], list[str], list[CoreParameter]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = _ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: _ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: _ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: str | None, state: _ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = UNSET + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: _ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = _normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = UNSET + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we recombine the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: _Option, state: _ParsingState + ) -> str | cabc.Sequence[str] | T_FLAG_NEEDS_VALUE: + nargs = option.nargs + + value: str | cabc.Sequence[str] | T_FLAG_NEEDS_VALUE + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = FLAG_NEEDS_VALUE + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = FLAG_NEEDS_VALUE + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: _ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = _normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) + + +def __getattr__(name: str) -> object: + import warnings + + if name in { + "OptionParser", + "Argument", + "Option", + "split_opt", + "normalize_opt", + "ParsingState", + }: + warnings.warn( + f"'parser.{name}' is deprecated and will be removed in Click 9.0." + " The old parser is available in 'optparse'.", + DeprecationWarning, + stacklevel=2, + ) + return globals()[f"_{name}"] + + if name == "split_arg_string": + from .shell_completion import split_arg_string + + warnings.warn( + "Importing 'parser.split_arg_string' is deprecated, it will only be" + " available in 'shell_completion' in Click 9.0.", + DeprecationWarning, + stacklevel=2, + ) + return split_arg_string + + raise AttributeError(name) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/py.typed b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/shell_completion.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/shell_completion.py new file mode 100644 index 000000000..8f1564c49 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/shell_completion.py @@ -0,0 +1,667 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .utils import echo + + +def shell_complete( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: str | None = None, + **kwargs: t.Any, + ) -> None: + self.value: t.Any = value + self.type: str = type + self.help: str | None = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMPREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMPREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +# See ZshComplete.format_completion below, and issue #2703, before +# changing this script. +# +# (TL;DR: _describe is picky about the format, but this Zsh script snippet +# is already widely deployed. So freeze this script, and use clever-ish +# handling of colons in ZshComplet.format_completion.) +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + %(complete_func)s "$@" +else + # eval/source/. command, register function for later + compdef %(complete_func)s %(prog_name)s +fi +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> tuple[list[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions(self, args: list[str], incomplete: str) -> list[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + @staticmethod + def _check_version() -> None: + import shutil + import subprocess + + bash_exe = shutil.which("bash") + + if bash_exe is None: + match = None + else: + output = subprocess.run( + [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], + stdout=subprocess.PIPE, + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + echo( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ), + err=True, + ) + else: + echo( + _("Couldn't detect Bash version, shell completion is not supported."), + err=True, + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + help_ = item.help or "_" + # The zsh completion script uses `_describe` on items with help + # texts (which splits the item help from the item value at the + # first unescaped colon) and `compadd` on items without help + # text (which uses the item value as-is and does not support + # colon escaping). So escape colons in the item value if and + # only if the item help is not the sentinel "_" value, as used + # by the completion script. + # + # (The zsh completion script is potentially widely deployed, and + # thus harder to fix than this method.) + # + # See issue #1812 and issue #2703 for further context. + value = item.value.replace(":", r"\:") if help_ != "_" else item.value + return f"{item.type}\n{value}\n{help_}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> tuple[list[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + if incomplete: + incomplete = split_arg_string(incomplete)[0] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +ShellCompleteType = t.TypeVar("ShellCompleteType", bound="type[ShellComplete]") + + +_available_shells: dict[str, type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: ShellCompleteType, name: str | None = None +) -> ShellCompleteType: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + return cls + + +def get_completion_class(shell: str) -> type[ShellComplete] | None: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def split_arg_string(string: str) -> list[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + + .. versionchanged:: 8.2 + Moved to ``shell_completion`` from ``parser``. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + # Will be None if expose_value is False. + value = ctx.params.get(param.name) + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(ctx: Context, value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + return c in ctx._opt_prefixes + + +def _is_incomplete_option(ctx: Context, args: list[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag or param.count: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(ctx, arg): + last_option = arg + break + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: Command, + ctx_args: cabc.MutableMapping[str, t.Any], + prog_name: str, + args: list[str], +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + with cli.make_context(prog_name, args.copy(), **ctx_args) as ctx: + args = ctx._protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, Group): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, args, parent=ctx, resilient_parsing=True + ) as sub_ctx: + ctx = sub_ctx + args = ctx._protected_args + ctx.args + else: + sub_ctx = ctx + + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + with cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) as sub_sub_ctx: + sub_ctx = sub_sub_ctx + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx._protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: list[str], incomplete: str +) -> tuple[Command | Parameter, str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(ctx, incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(ctx, incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(ctx, args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/termui.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/termui.py new file mode 100644 index 000000000..2e98a0771 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/termui.py @@ -0,0 +1,883 @@ +from __future__ import annotations + +import collections.abc as cabc +import inspect +import io +import itertools +import sys +import typing as t +from contextlib import AbstractContextManager +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Any | None = None, + show_choices: bool = True, + type: ParamType | None = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name + + return default + + +def prompt( + text: str, + default: t.Any | None = None, + hide_input: bool = False, + confirmation_prompt: bool | str = False, + type: ParamType | t.Any | None = None, + value_proc: t.Callable[[str], t.Any] | None = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending an interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionchanged:: 8.3.1 + A space is no longer appended to the prompt. + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text[:-1], nl=False, err=err) + # Echo the last character to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(text[-1:]) + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) + continue + if not confirmation_prompt: + return result + while True: + value2 = prompt_func(confirmation_prompt) + is_empty = not value and not value2 + if value2 or is_empty: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: bool | None = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.3.1 + A space is no longer appended to the prompt. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt[:-1], nl=False, err=err) + # Echo the last character to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(prompt[-1:]).lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def echo_via_pager( + text_or_generator: cabc.Iterable[str] | t.Callable[[], cabc.Iterable[str]] | str, + color: bool | None = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast("t.Callable[[], cabc.Iterable[str]]", text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast("cabc.Iterable[str]", text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +@t.overload +def progressbar( + *, + length: int, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[int]: ... + + +@t.overload +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: ... + + +def progressbar( + iterable: cabc.Iterable[V] | None = None, + length: int | None = None, + label: str | None = None, + hidden: bool = False, + show_eta: bool = True, + show_percent: bool | None = None, + show_pos: bool = False, + item_show_func: t.Callable[[V | None], str | None] | None = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.TextIO | None = None, + color: bool | None = None, + update_min_steps: int = 1, +) -> ProgressBar[V]: + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param hidden: hide the progressbar. Defaults to ``False``. When no tty is + detected, it will only print the progressbar label. Setting this to + ``False`` also disables that. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionadded:: 8.2 + The ``hidden`` argument. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + The ``update_min_steps`` parameter. + + .. versionadded:: 4.0 + The ``color`` parameter and ``update`` method. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + hidden=hidden, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + + # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor + echo("\033[2J\033[1;1H", nl=False) + + +def _interpret_color(color: int | tuple[int, int, int] | str, offset: int = 0) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: int | tuple[int, int, int] | str | None = None, + bg: int | tuple[int, int, int] | str | None = None, + bold: bool | None = None, + dim: bool | None = None, + underline: bool | None = None, + overline: bool | None = None, + italic: bool | None = None, + blink: bool | None = None, + reverse: bool | None = None, + strikethrough: bool | None = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Any | None = None, + file: t.IO[t.AnyStr] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +@t.overload +def edit( + text: bytes | bytearray, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = False, + extension: str = ".txt", +) -> bytes | None: ... + + +@t.overload +def edit( + text: str, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", +) -> str | None: ... + + +@t.overload +def edit( + text: None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str | cabc.Iterable[str] | None = None, +) -> None: ... + + +def edit( + text: str | bytes | bytearray | None = None, + editor: str | None = None, + env: cabc.Mapping[str, str] | None = None, + require_save: bool = True, + extension: str = ".txt", + filename: str | cabc.Iterable[str] | None = None, +) -> str | bytes | bytearray | None: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. If the editor supports + editing multiple files at once, a sequence of files may be + passed as well. Invoke `click.file` once per file instead + if multiple files cannot be managed at once or editing the + files serially is desired. + + .. versionchanged:: 8.2.0 + ``filename`` now accepts any ``Iterable[str]`` in addition to a ``str`` + if the ``editor`` supports editing multiple files at once. + + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + if isinstance(filename, str): + filename = (filename,) + + ed.edit_files(filenames=filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Callable[[bool], str] | None = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> AbstractContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: str | None = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/testing.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/testing.py new file mode 100644 index 000000000..f6f60b809 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/testing.py @@ -0,0 +1,577 @@ +from __future__ import annotations + +import collections.abc as cabc +import contextlib +import io +import os +import shlex +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import _compat +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from _typeshed import ReadableBuffer + + from .core import Command + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> list[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> cabc.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: EchoingStdin | None) -> cabc.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class BytesIOCopy(io.BytesIO): + """Patch ``io.BytesIO`` to let the written stream be copied to another. + + .. versionadded:: 8.2 + """ + + def __init__(self, copy_to: io.BytesIO) -> None: + super().__init__() + self.copy_to = copy_to + + def flush(self) -> None: + super().flush() + self.copy_to.flush() + + def write(self, b: ReadableBuffer) -> int: + self.copy_to.write(b) + return super().write(b) + + +class StreamMixer: + """Mixes `` and `` streams. + + The result is available in the ``output`` attribute. + + .. versionadded:: 8.2 + """ + + def __init__(self) -> None: + self.output: io.BytesIO = io.BytesIO() + self.stdout: io.BytesIO = BytesIOCopy(copy_to=self.output) + self.stderr: io.BytesIO = BytesIOCopy(copy_to=self.output) + + def __del__(self) -> None: + """ + Guarantee that embedded file-like objects are closed in a + predictable order, protecting against races between + self.output being closed and other streams being flushed on close + + .. versionadded:: 8.2.2 + """ + self.stderr.close() + self.stdout.close() + self.output.close() + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: str | bytes | t.IO[t.Any] | None, charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast("t.IO[t.Any]", input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(input) + + +class Result: + """Holds the captured result of an invoked CLI script. + + :param runner: The runner that created the result + :param stdout_bytes: The standard output as bytes. + :param stderr_bytes: The standard error as bytes. + :param output_bytes: A mix of ``stdout_bytes`` and ``stderr_bytes``, as the + user would see it in its terminal. + :param return_value: The value returned from the invoked command. + :param exit_code: The exit code as integer. + :param exception: The exception that happened if one did. + :param exc_info: Exception information (exception type, exception instance, + traceback type). + + .. versionchanged:: 8.2 + ``stderr_bytes`` no longer optional, ``output_bytes`` introduced and + ``mix_stderr`` has been removed. + + .. versionadded:: 8.0 + Added ``return_value``. + """ + + def __init__( + self, + runner: CliRunner, + stdout_bytes: bytes, + stderr_bytes: bytes, + output_bytes: bytes, + return_value: t.Any, + exit_code: int, + exception: BaseException | None, + exc_info: tuple[type[BaseException], BaseException, TracebackType] + | None = None, + ): + self.runner = runner + self.stdout_bytes = stdout_bytes + self.stderr_bytes = stderr_bytes + self.output_bytes = output_bytes + self.return_value = return_value + self.exit_code = exit_code + self.exception = exception + self.exc_info = exc_info + + @property + def output(self) -> str: + """The terminal output as unicode string, as the user would see it. + + .. versionchanged:: 8.2 + No longer a proxy for ``self.stdout``. Now has its own independent stream + that is mixing `` and ``, in the order they were written. + """ + return self.output_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string. + + .. versionchanged:: 8.2 + No longer raise an exception, always returns the `` string. + """ + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from `` writes + to ``. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param catch_exceptions: Whether to catch any exceptions other than + ``SystemExit`` when running :meth:`~CliRunner.invoke`. + + .. versionchanged:: 8.2 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 8.2 + ``mix_stderr`` parameter has been removed. + """ + + def __init__( + self, + charset: str = "utf-8", + env: cabc.Mapping[str, str | None] | None = None, + echo_stdin: bool = False, + catch_exceptions: bool = True, + ) -> None: + self.charset = charset + self.env: cabc.Mapping[str, str | None] = env or {} + self.echo_stdin = echo_stdin + self.catch_exceptions = catch_exceptions + + def get_default_prog_name(self, cli: Command) -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: cabc.Mapping[str, str | None] | None = None + ) -> cabc.Mapping[str, str | None]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + color: bool = False, + ) -> cabc.Iterator[tuple[io.BytesIO, io.BytesIO, io.BytesIO]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up `` with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into `sys.stdin`. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionadded:: 8.2 + An additional output stream is returned, which is a mix of + `` and `` streams. + + .. versionchanged:: 8.2 + Always returns the `` stream. + + .. versionchanged:: 8.0 + `` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + stream_mixer = StreamMixer() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, stream_mixer.stdout) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + stream_mixer.stdout, encoding=self.charset, name="", mode="w" + ) + + sys.stderr = _NamedTextIOWrapper( + stream_mixer.stderr, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: str | None = None) -> str: + sys.stdout.write(prompt or "") + try: + val = next(text_input).rstrip("\r\n") + except StopIteration as e: + raise EOFError() from e + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: str | None = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + try: + return next(text_input).rstrip("\r\n") + except StopIteration as e: + raise EOFError() from e + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.IO[t.Any] | None = None, color: bool | None = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + old__compat_should_strip_ansi = _compat.should_strip_ansi + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + _compat.should_strip_ansi = should_strip_ansi + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (stream_mixer.stdout, stream_mixer.stderr, stream_mixer.output) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + _compat.should_strip_ansi = old__compat_should_strip_ansi + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: Command, + args: str | cabc.Sequence[str] | None = None, + input: str | bytes | t.IO[t.Any] | None = None, + env: cabc.Mapping[str, str | None] | None = None, + catch_exceptions: bool | None = None, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. If :data:`None`, the value + from :class:`CliRunner` is used. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionadded:: 8.2 + The result object has the ``output_bytes`` attribute with + the mix of ``stdout_bytes`` and ``stderr_bytes``, as the user would + see it in its terminal. + + .. versionchanged:: 8.2 + The result object always returns the ``stderr_bytes`` stream. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + if catch_exceptions is None: + catch_exceptions = self.catch_exceptions + + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: BaseException | None = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast("int | t.Any | None", e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + sys.stderr.flush() + stdout = outstreams[0].getvalue() + stderr = outstreams[1].getvalue() + output = outstreams[2].getvalue() + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + output_bytes=output, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: str | os.PathLike[str] | None = None + ) -> cabc.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + dt = tempfile.mkdtemp(dir=temp_dir) + os.chdir(dt) + + try: + yield dt + finally: + os.chdir(cwd) + + if temp_dir is None: + import shutil + + try: + shutil.rmtree(dt) + except OSError: + pass diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/types.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/types.py new file mode 100644 index 000000000..e71c1c21e --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/types.py @@ -0,0 +1,1209 @@ +from __future__ import annotations + +import collections.abc as cabc +import enum +import os +import stat +import sys +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import format_filename +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + +ParamTypeValue = t.TypeVar("ParamTypeValue") + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[str | None] = None + + def to_info_dict(self) -> dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + + # Custom subclasses might not remember to set a name. + if hasattr(self, "name"): + name = self.name + else: + name = param_type + + return {"param_type": param_type, "name": name} + + def __call__( + self, + value: t.Any, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str | None: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> cabc.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: Parameter | None = None, + ctx: Context | None = None, + ) -> t.NoReturn: + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name: str = func.__name__ + self.func = func + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = sys.getfilesystemencoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType, t.Generic[ParamTypeValue]): + """The choice type allows a value to be checked against a fixed set + of supported values. + + You may pass any iterable value which will be converted to a tuple + and thus will only be iterated once. + + The resulting value will always be one of the originally passed choices. + See :meth:`normalize_choice` for more info on the mapping of strings + to choices. See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + + .. versionchanged:: 8.2.0 + Non-``str`` ``choices`` are now supported. It can additionally be any + iterable. Before you were not recommended to pass anything but a list or + tuple. + + .. versionadded:: 8.2.0 + Choice normalization can be overridden via :meth:`normalize_choice`. + """ + + name = "choice" + + def __init__( + self, choices: cabc.Iterable[ParamTypeValue], case_sensitive: bool = True + ) -> None: + self.choices: cabc.Sequence[ParamTypeValue] = tuple(choices) + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def _normalized_mapping( + self, ctx: Context | None = None + ) -> cabc.Mapping[ParamTypeValue, str]: + """ + Returns mapping where keys are the original choices and the values are + the normalized values that are accepted via the command line. + + This is a simple wrapper around :meth:`normalize_choice`, use that + instead which is supported. + """ + return { + choice: self.normalize_choice( + choice=choice, + ctx=ctx, + ) + for choice in self.choices + } + + def normalize_choice(self, choice: ParamTypeValue, ctx: Context | None) -> str: + """ + Normalize a choice value, used to map a passed string to a choice. + Each choice must have a unique normalized value. + + By default uses :meth:`Context.token_normalize_func` and if not case + sensitive, convert it to a casefolded value. + + .. versionadded:: 8.2.0 + """ + normed_value = choice.name if isinstance(choice, enum.Enum) else str(choice) + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(normed_value) + + if not self.case_sensitive: + normed_value = normed_value.casefold() + + return normed_value + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + if param.param_type_name == "option" and not param.show_choices: # type: ignore + choice_metavars = [ + convert_type(type(choice)).name.upper() for choice in self.choices + ] + choices_str = "|".join([*dict.fromkeys(choice_metavars)]) + else: + choices_str = "|".join( + [str(i) for i in self._normalized_mapping(ctx=ctx).values()] + ) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: Parameter, ctx: Context | None) -> str: + """ + Message shown when no choice is passed. + + .. versionchanged:: 8.2.0 Added ``ctx`` argument. + """ + return _("Choose from:\n\t{choices}").format( + choices=",\n\t".join(self._normalized_mapping(ctx=ctx).values()) + ) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> ParamTypeValue: + """ + For a given value from the parser, normalize it and find its + matching normalized value in the list of choices. Then return the + matched "original" choice. + """ + normed_value = self.normalize_choice(choice=value, ctx=ctx) + normalized_mapping = self._normalized_mapping(ctx=ctx) + + try: + return next( + original + for original, normalized in normalized_mapping.items() + if normalized == normed_value + ) + except StopIteration: + self.fail( + self.get_invalid_choice_message(value=value, ctx=ctx), + param=param, + ctx=ctx, + ) + + def get_invalid_choice_message(self, value: t.Any, ctx: Context | None) -> str: + """Get the error message when the given choice is invalid. + + :param value: The invalid value. + + .. versionadded:: 8.2 + """ + choices_str = ", ".join(map(repr, self._normalized_mapping(ctx=ctx).values())) + return ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: cabc.Sequence[str] | None = None): + self.formats: cabc.Sequence[str] = formats or [ + "%Y-%m-%d", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%d %H:%M:%S", + ] + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: Parameter, ctx: Context) -> str | None: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> datetime | None: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[type[t.Any]] + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: float | None = None, + max: float | None = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: t.Literal[1, -1], open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: float | None = None, + max: float | None = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: t.Literal[1, -1], open: bool) -> float: + if not open: + return bound + + # Could use math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + bool_states: dict[str, bool] = { + "1": True, + "0": False, + "yes": True, + "no": False, + "true": True, + "false": False, + "on": True, + "off": False, + "t": True, + "f": False, + "y": True, + "n": False, + # Absence of value is considered False. + "": False, + } + """A mapping of string values to boolean states. + + Mapping is inspired by :py:attr:`configparser.ConfigParser.BOOLEAN_STATES` + and extends it. + + .. caution:: + String values are lower-cased, as the ``str_to_bool`` comparison function + below is case-insensitive. + + .. warning:: + The mapping is not exhaustive, and does not cover all possible boolean strings + representations. It will remains as it is to avoid endless bikeshedding. + + Future work my be considered to make this mapping user-configurable from public + API. + """ + + @staticmethod + def str_to_bool(value: str | bool) -> bool | None: + """Convert a string to a boolean value. + + If the value is already a boolean, it is returned as-is. If the value is a + string, it is stripped of whitespaces and lower-cased, then checked against + the known boolean states pre-defined in the `BoolParamType.bool_states` mapping + above. + + Returns `None` if the value does not match any known boolean state. + """ + if isinstance(value, bool): + return value + return BoolParamType.bool_states.get(value.strip().lower()) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> bool: + normalized = self.str_to_bool(value) + if normalized is None: + self.fail( + _( + "{value!r} is not a valid boolean. Recognized values: {states}" + ).format(value=value, states=", ".join(sorted(self.bool_states))), + param, + ctx, + ) + return normalized + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Files can also be opened atomically in which case all writes go into a + separate file in the same folder and upon completion the file will + be moved over to the original location. This is useful if a file + regularly read by other users is modified. + + See :ref:`file-args` for more information. + + .. versionchanged:: 2.0 + Added the ``atomic`` parameter. + """ + + name = "filename" + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool | None = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: str | os.PathLike[str]) -> bool: + if self.lazy is not None: + return self.lazy + if os.fspath(value) == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, + value: str | os.PathLike[str] | t.IO[t.Any], + param: Parameter | None, + ctx: Context | None, + ) -> t.IO[t.Any]: + if _is_file_like(value): + return value + + value = t.cast("str | os.PathLike[str]", value) + + try: + lazy = self.resolve_lazy_flag(value) + + if lazy: + lf = LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + if ctx is not None: + ctx.call_on_close(lf.close_intelligently) + + return t.cast("t.IO[t.Any]", lf) + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: + self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +def _is_file_like(value: t.Any) -> te.TypeGuard[t.IO[t.Any]]: + return hasattr(value, "read") or hasattr(value, "write") + + +class Path(ParamType): + """The ``Path`` type is similar to the :class:`File` type, but + returns the filename instead of an open file. Various checks can be + enabled to validate the type of file and permissions. + + :param exists: The file or directory needs to exist for the value to + be valid. If this is not set to ``True``, and the file does not + exist, then all further checks are silently skipped. + :param file_okay: Allow a file as a value. + :param dir_okay: Allow a directory as a value. + :param readable: if true, a readable check is performed. + :param writable: if true, a writable check is performed. + :param executable: if true, an executable check is performed. + :param resolve_path: Make the value absolute and resolve any + symlinks. A ``~`` is not expanded, as this is supposed to be + done by the shell only. + :param allow_dash: Allow a single dash as a value, which indicates + a standard stream (but does not open it). Use + :func:`~click.open_file` to handle opening this value. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.1 + Added the ``executable`` parameter. + + .. versionchanged:: 8.0 + Allow passing ``path_type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter: t.ClassVar[str] = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: type[t.Any] | None = None, + executable: bool = False, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.readable = readable + self.writable = writable + self.executable = executable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name: str = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result( + self, value: str | os.PathLike[str] + ) -> str | bytes | os.PathLike[str]: + if self.type is not None and not isinstance(value, self.type): + if self.type is str: + return os.fsdecode(value) + elif self.type is bytes: + return os.fsencode(value) + else: + return t.cast("os.PathLike[str]", self.type(value)) + + return value + + def convert( + self, + value: str | os.PathLike[str], + param: Parameter | None, + ctx: Context | None, + ) -> str | bytes | os.PathLike[str]: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + rv = os.path.realpath(rv) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + if self.executable and not os.access(value, os.X_OK): + self.fail( + _("{name} {filename!r} is not executable.").format( + name=self.name.title(), filename=format_filename(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: Context, param: Parameter, incomplete: str + ) -> list[CompletionItem]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: cabc.Sequence[type[t.Any] | ParamType]) -> None: + self.types: cabc.Sequence[ParamType] = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: Parameter | None, ctx: Context | None + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple( + ty(x, param, ctx) for ty, x in zip(self.types, value, strict=False) + ) + + +def convert_type(ty: t.Any | None, default: t.Any | None = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() + + +class OptionHelpExtra(t.TypedDict, total=False): + envvars: tuple[str, ...] + default: str + range: str + required: str diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/utils.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/utils.py new file mode 100644 index 000000000..beae26f76 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/click/utils.py @@ -0,0 +1,627 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import re +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType +from types import TracebackType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + + P = te.ParamSpec("P") + +R = t.TypeVar("R") + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: t.Callable[P, R]) -> t.Callable[P, R | None]: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | None: + try: + return func(*args, **kwargs) + except Exception: + pass + return None + + return update_wrapper(wrapper, func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(sys.getfilesystemencoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + atomic: bool = False, + ): + self.name: str = os.fspath(filename) + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.IO[t.Any] | None + self.should_close: bool + + if self.name == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO[t.Any]: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> LazyFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.close_intelligently() + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO[t.Any]) -> None: + self._file: t.IO[t.Any] = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> KeepOpenFile: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> cabc.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Any | None = None, + file: t.IO[t.Any] | None = None, + nl: bool = True, + err: bool = False, + color: bool | None = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # There are no standard streams attached to write to. For example, + # pythonw on Windows. + if file is None: + return + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: str | bytes | bytearray | None = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file, color) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: t.Literal["stdin", "stdout", "stderr"]) -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: t.Literal["stdin", "stdout", "stderr"], + encoding: str | None = None, + errors: str | None = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str | os.PathLike[str], + mode: str = "r", + encoding: str | None = None, + errors: str | None = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO[t.Any]: + """Open a file, with extra behavior to handle ``'-'`` to indicate + a standard stream, lazy open on write, and atomic write. Similar to + the behavior of the :class:`~click.File` param type. + + If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is + wrapped so that using it in a context manager will not close it. + This makes it possible to use the function without accidentally + closing a standard stream: + + .. code-block:: python + + with open_file(filename) as f: + ... + + :param filename: The name or Path of the file to open, or ``'-'`` for + ``stdin``/``stdout``. + :param mode: The mode in which to open the file. + :param encoding: The encoding to decode or encode a file opened in + text mode. + :param errors: The error handling mode. + :param lazy: Wait to open the file until it is accessed. For read + mode, the file is temporarily opened to raise access errors + early, then closed until it is read again. + :param atomic: Write to a temporary file and replace the given file + on close. + + .. versionadded:: 3.0 + """ + if lazy: + return t.cast( + "t.IO[t.Any]", LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) + + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + + if not should_close: + f = t.cast("t.IO[t.Any]", KeepOpenFile(f)) + + return f + + +def format_filename( + filename: str | bytes | os.PathLike[str] | os.PathLike[bytes], + shorten: bool = False, +) -> str: + """Format a filename as a string for display. Ensures the filename can be + displayed by replacing any invalid bytes or surrogate escapes in the name + with the replacement character ``�``. + + Invalid bytes or surrogate escapes will raise an error when written to a + stream with ``errors="strict"``. This will typically happen with ``stdout`` + when the locale is something like ``en_GB.UTF-8``. + + Many scenarios *are* safe to write surrogates though, due to PEP 538 and + PEP 540, including: + + - Writing to ``stderr``, which uses ``errors="backslashreplace"``. + - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens + stdout and stderr with ``errors="surrogateescape"``. + - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. + - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. + Python opens stdout and stderr with ``errors="surrogateescape"``. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + else: + filename = os.fspath(filename) + + if isinstance(filename, bytes): + filename = filename.decode(sys.getfilesystemencoding(), "replace") + else: + filename = filename.encode("utf-8", "surrogateescape").decode( + "utf-8", "replace" + ) + + return filename + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no effect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO[t.Any]) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: str | None = None, _main: ModuleType | None = None +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if _main is None: + _main = sys.modules["__main__"] + + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + # It is set to "" inside a Shiv or PEX zipapp. + if getattr(_main, "__package__", None) in {None, ""} or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: cabc.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> list[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This is intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionchanged:: 8.1 + Invalid glob patterns are treated as empty expansions rather + than raising an error. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + try: + matches = glob(arg, recursive=glob_recursive) + except re.error: + matches = [] + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA new file mode 100644 index 000000000..a1b5c5754 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA @@ -0,0 +1,441 @@ +Metadata-Version: 2.1 +Name: colorama +Version: 0.4.6 +Summary: Cross-platform colored terminal text. +Project-URL: Homepage, https://github.com/tartley/colorama +Author-email: Jonathan Hartley +License-File: LICENSE.txt +Keywords: ansi,color,colour,crossplatform,terminal,text,windows,xplatform +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Terminals +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 +Description-Content-Type: text/x-rst + +.. image:: https://img.shields.io/pypi/v/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Latest Version + +.. image:: https://img.shields.io/pypi/pyversions/colorama.svg + :target: https://pypi.org/project/colorama/ + :alt: Supported Python versions + +.. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg + :target: https://github.com/tartley/colorama/actions/workflows/test.yml + :alt: Build Status + +Colorama +======== + +Makes ANSI escape character sequences (for producing colored terminal text and +cursor positioning) work under MS Windows. + +.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif + :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD + :alt: Donate with Paypal + +`PyPI for releases `_ | +`Github for source `_ | +`Colorama for enterprise on Tidelift `_ + +If you find Colorama useful, please |donate| to the authors. Thank you! + +Installation +------------ + +Tested on CPython 2.7, 3.7, 3.8, 3.9 and 3.10 and Pypy 2.7 and 3.8. + +No requirements other than the standard library. + +.. code-block:: bash + + pip install colorama + # or + conda install -c anaconda colorama + +Description +----------- + +ANSI escape character sequences have long been used to produce colored terminal +text and cursor positioning on Unix and Macs. Colorama makes this work on +Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which +would appear as gobbledygook in the output), and converting them into the +appropriate win32 calls to modify the state of the terminal. On other platforms, +Colorama does nothing. + +This has the upshot of providing a simple cross-platform API for printing +colored terminal text from Python, and has the happy side-effect that existing +applications or libraries which use ANSI sequences to produce colored output on +Linux or Macs can now also work on Windows, simply by calling +``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()`` +(all versions, but may have other side-effects – see below). + +An alternative approach is to install ``ansi.sys`` on Windows machines, which +provides the same behaviour for all applications running in terminals. Colorama +is intended for situations where that isn't easy (e.g., maybe your app doesn't +have an installer.) + +Demo scripts in the source code repository print some colored text using +ANSI sequences. Compare their output under Gnome-terminal's built in ANSI +handling, versus on Windows Command-Prompt using Colorama: + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png + :width: 661 + :height: 357 + :alt: ANSI sequences on Ubuntu under gnome-terminal. + +.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png + :width: 668 + :height: 325 + :alt: Same ANSI sequences on Windows, using Colorama. + +These screenshots show that, on Windows, Colorama does not support ANSI 'dim +text'; it looks the same as 'normal text'. + +Usage +----- + +Initialisation +.............. + +If the only thing you want from Colorama is to get ANSI escapes to work on +Windows, then run: + +.. code-block:: python + + from colorama import just_fix_windows_console + just_fix_windows_console() + +If you're on a recent version of Windows 10 or better, and your stdout/stderr +are pointing to a Windows console, then this will flip the magic configuration +switch to enable Windows' built-in ANSI support. + +If you're on an older version of Windows, and your stdout/stderr are pointing to +a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a +magic file object that intercepts ANSI escape sequences and issues the +appropriate Win32 calls to emulate them. + +In all other circumstances, it does nothing whatsoever. Basically the idea is +that this makes Windows act like Unix with respect to ANSI escape handling. + +It's safe to call this function multiple times. It's safe to call this function +on non-Windows platforms, but it won't do anything. It's safe to call this +function when one or both of your stdout/stderr are redirected to a file – it +won't do anything to those streams. + +Alternatively, you can use the older interface with more features (but also more +potential footguns): + +.. code-block:: python + + from colorama import init + init() + +This does the same thing as ``just_fix_windows_console``, except for the +following differences: + +- It's not safe to call ``init`` multiple times; you can end up with multiple + layers of wrapping and broken ANSI support. + +- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI, + and if it thinks they don't, then it will wrap ``sys.stdout`` and + ``sys.stderr`` in a magic file object that strips out ANSI escape sequences + before printing them. This happens on all platforms, and can be convenient if + you want to write your code to emit ANSI escape sequences unconditionally, and + let Colorama decide whether they should actually be output. But note that + Colorama's heuristic is not particularly clever. + +- ``init`` also accepts explicit keyword args to enable/disable various + functionality – see below. + +To stop using Colorama before your program exits, simply call ``deinit()``. +This will restore ``stdout`` and ``stderr`` to their original values, so that +Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is +cheaper than calling ``init()`` again (but does the same thing). + +Most users should depend on ``colorama >= 0.4.6``, and use +``just_fix_windows_console``. The old ``init`` interface will be supported +indefinitely for backwards compatibility, but we don't plan to fix any issues +with it, also for backwards compatibility. + +Colored Output +.............. + +Cross-platform printing of colored text can then be done using Colorama's +constant shorthand for ANSI escape sequences. These are deliberately +rudimentary, see below. + +.. code-block:: python + + from colorama import Fore, Back, Style + print(Fore.RED + 'some red text') + print(Back.GREEN + 'and with a green background') + print(Style.DIM + 'and in dim text') + print(Style.RESET_ALL) + print('back to normal now') + +...or simply by manually printing ANSI sequences from your own code: + +.. code-block:: python + + print('\033[31m' + 'some red text') + print('\033[39m') # and reset to default color + +...or, Colorama can be used in conjunction with existing ANSI libraries +such as the venerable `Termcolor `_ +the fabulous `Blessings `_, +or the incredible `_Rich `_. + +If you wish Colorama's Fore, Back and Style constants were more capable, +then consider using one of the above highly capable libraries to generate +colors, etc, and use Colorama just for its primary purpose: to convert +those ANSI sequences to also work on Windows: + +SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama. +We are only interested in converting ANSI codes to win32 API calls, not +shortcuts like the above to generate ANSI characters. + +.. code-block:: python + + from colorama import just_fix_windows_console + from termcolor import colored + + # use Colorama to make Termcolor work on Windows too + just_fix_windows_console() + + # then use Termcolor for all colored text output + print(colored('Hello, World!', 'green', 'on_red')) + +Available formatting constants are:: + + Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. + Style: DIM, NORMAL, BRIGHT, RESET_ALL + +``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will +perform this reset automatically on program exit. + +These are fairly well supported, but not part of the standard:: + + Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX + +Cursor Positioning +.................. + +ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for +an example of how to generate them. + +Init Keyword Args +................. + +``init()`` accepts some ``**kwargs`` to override default behaviour. + +init(autoreset=False): + If you find yourself repeatedly sending reset sequences to turn off color + changes at the end of every print, then ``init(autoreset=True)`` will + automate that: + + .. code-block:: python + + from colorama import init + init(autoreset=True) + print(Fore.RED + 'some red text') + print('automatically back to default color again') + +init(strip=None): + Pass ``True`` or ``False`` to override whether ANSI codes should be + stripped from the output. The default behaviour is to strip if on Windows + or if output is redirected (not a tty). + +init(convert=None): + Pass ``True`` or ``False`` to override whether to convert ANSI codes in the + output into win32 calls. The default behaviour is to convert if on Windows + and output is to a tty (terminal). + +init(wrap=True): + On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` + with proxy objects, which override the ``.write()`` method to do their work. + If this wrapping causes you problems, then this can be disabled by passing + ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or + ``strip`` or ``convert`` are True. + + When wrapping is disabled, colored printing on non-Windows platforms will + continue to work as normal. To do cross-platform colored output, you can + use Colorama's ``AnsiToWin32`` proxy directly: + + .. code-block:: python + + import sys + from colorama import init, AnsiToWin32 + init(wrap=False) + stream = AnsiToWin32(sys.stderr).stream + + # Python 2 + print >>stream, Fore.BLUE + 'blue text on stderr' + + # Python 3 + print(Fore.BLUE + 'blue text on stderr', file=stream) + +Recognised ANSI Sequences +......................... + +ANSI sequences generally take the form:: + + ESC [ ; ... + +Where ```` is an integer, and ```` is a single letter. Zero or +more params are passed to a ````. If no params are passed, it is +generally synonymous with passing a single zero. No spaces exist in the +sequence; they have been inserted here simply to read more easily. + +The only ANSI sequences that Colorama converts into win32 calls are:: + + ESC [ 0 m # reset all (colors and brightness) + ESC [ 1 m # bright + ESC [ 2 m # dim (looks same as normal brightness) + ESC [ 22 m # normal brightness + + # FOREGROUND: + ESC [ 30 m # black + ESC [ 31 m # red + ESC [ 32 m # green + ESC [ 33 m # yellow + ESC [ 34 m # blue + ESC [ 35 m # magenta + ESC [ 36 m # cyan + ESC [ 37 m # white + ESC [ 39 m # reset + + # BACKGROUND + ESC [ 40 m # black + ESC [ 41 m # red + ESC [ 42 m # green + ESC [ 43 m # yellow + ESC [ 44 m # blue + ESC [ 45 m # magenta + ESC [ 46 m # cyan + ESC [ 47 m # white + ESC [ 49 m # reset + + # cursor positioning + ESC [ y;x H # position cursor at x across, y down + ESC [ y;x f # position cursor at x across, y down + ESC [ n A # move cursor n lines up + ESC [ n B # move cursor n lines down + ESC [ n C # move cursor n characters forward + ESC [ n D # move cursor n characters backward + + # clear the screen + ESC [ mode J # clear the screen + + # clear the line + ESC [ mode K # clear the line + +Multiple numeric params to the ``'m'`` command can be combined into a single +sequence:: + + ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background + +All other ANSI sequences of the form ``ESC [ ; ... `` +are silently stripped from the output on Windows. + +Any other form of ANSI sequence, such as single-character codes or alternative +initial characters, are not recognised or stripped. It would be cool to add +them though. Let me know if it would be useful for you, via the Issues on +GitHub. + +Status & Known Problems +----------------------- + +I've personally only tested it on Windows XP (CMD, Console2), Ubuntu +(gnome-terminal, xterm), and OS X. + +Some valid ANSI sequences aren't recognised. + +If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the +explanation there of why we do not want PRs that allow Colorama to generate new +types of ANSI codes. + +See outstanding issues and wish-list: +https://github.com/tartley/colorama/issues + +If anything doesn't work for you, or doesn't do what you expected or hoped for, +I'd love to hear about it on that issues list, would be delighted by patches, +and would be happy to grant commit access to anyone who submits a working patch +or two. + +.. _README-hacking.md: README-hacking.md + +License +------- + +Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see +LICENSE file. + +Professional support +-------------------- + +.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png + :alt: Tidelift + :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for colorama is available as part of the + `Tidelift Subscription`_. + Tidelift gives software development teams a single source for purchasing + and maintaining their software, with professional grade assurances from + the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme + +Thanks +------ + +See the CHANGELOG for more thanks! + +* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. +* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, + providing a solution to issue #7's setuptools/distutils debate, + and other fixes. +* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. +* Matthew McCormick for politely pointing out a longstanding crash on non-Win. +* Ben Hoyt, for a magnificent fix under 64-bit Windows. +* Jesse at Empty Square for submitting a fix for examples in the README. +* User 'jamessp', an observant documentation fix for cursor positioning. +* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 + fix. +* Julien Stuyck, for wisely suggesting Python3 compatible updates to README. +* Daniel Griffith for multiple fabulous patches. +* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty + output. +* Roger Binns, for many suggestions, valuable feedback, & bug reports. +* Tim Golden for thought and much appreciated feedback on the initial idea. +* User 'Zearin' for updates to the README file. +* John Szakmeister for adding support for light colors +* Charles Merriam for adding documentation to demos +* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes +* Florian Bruhin for a fix when stdout or stderr are None +* Thomas Weininger for fixing ValueError on Windows +* Remi Rampin for better Github integration and fixes to the README file +* Simeon Visser for closing a file handle using 'with' and updating classifiers + to include Python 3.3 and 3.4 +* Andy Neff for fixing RESET of LIGHT_EX colors. +* Jonathan Hartley for the initial idea and implementation. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD new file mode 100644 index 000000000..cd6b130d4 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD @@ -0,0 +1,31 @@ +colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158 +colorama-0.4.6.dist-info/RECORD,, +colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105 +colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 +colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 +colorama/__pycache__/__init__.cpython-312.pyc,, +colorama/__pycache__/ansi.cpython-312.pyc,, +colorama/__pycache__/ansitowin32.cpython-312.pyc,, +colorama/__pycache__/initialise.cpython-312.pyc,, +colorama/__pycache__/win32.cpython-312.pyc,, +colorama/__pycache__/winterm.cpython-312.pyc,, +colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 +colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 +colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 +colorama/tests/__pycache__/__init__.cpython-312.pyc,, +colorama/tests/__pycache__/ansi_test.cpython-312.pyc,, +colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc,, +colorama/tests/__pycache__/initialise_test.cpython-312.pyc,, +colorama/tests/__pycache__/isatty_test.cpython-312.pyc,, +colorama/tests/__pycache__/utils.cpython-312.pyc,, +colorama/tests/__pycache__/winterm_test.cpython-312.pyc,, +colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 +colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 +colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 +colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 +colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 +colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 +colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 +colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL new file mode 100644 index 000000000..d79189fda --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.11.1 +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 000000000..3105888ec --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/__init__.py new file mode 100644 index 000000000..383101cdb --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.6' + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/ansi.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/ansi.py new file mode 100644 index 000000000..11ec695ff --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/ansi.py @@ -0,0 +1,102 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +''' +This module generates ANSI character codes to printing colors to terminals. +See: http://en.wikipedia.org/wiki/ANSI_escape_code +''' + +CSI = '\033[' +OSC = '\033]' +BEL = '\a' + + +def code_to_chars(code): + return CSI + str(code) + 'm' + +def set_title(title): + return OSC + '2;' + title + BEL + +def clear_screen(mode=2): + return CSI + str(mode) + 'J' + +def clear_line(mode=2): + return CSI + str(mode) + 'K' + + +class AnsiCodes(object): + def __init__(self): + # the subclasses declare class attributes which are numbers. + # Upon instantiation we define instance attributes, which are the same + # as the class attributes but wrapped with the ANSI escape sequence + for name in dir(self): + if not name.startswith('_'): + value = getattr(self, name) + setattr(self, name, code_to_chars(value)) + + +class AnsiCursor(object): + def UP(self, n=1): + return CSI + str(n) + 'A' + def DOWN(self, n=1): + return CSI + str(n) + 'B' + def FORWARD(self, n=1): + return CSI + str(n) + 'C' + def BACK(self, n=1): + return CSI + str(n) + 'D' + def POS(self, x=1, y=1): + return CSI + str(y) + ';' + str(x) + 'H' + + +class AnsiFore(AnsiCodes): + BLACK = 30 + RED = 31 + GREEN = 32 + YELLOW = 33 + BLUE = 34 + MAGENTA = 35 + CYAN = 36 + WHITE = 37 + RESET = 39 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 90 + LIGHTRED_EX = 91 + LIGHTGREEN_EX = 92 + LIGHTYELLOW_EX = 93 + LIGHTBLUE_EX = 94 + LIGHTMAGENTA_EX = 95 + LIGHTCYAN_EX = 96 + LIGHTWHITE_EX = 97 + + +class AnsiBack(AnsiCodes): + BLACK = 40 + RED = 41 + GREEN = 42 + YELLOW = 43 + BLUE = 44 + MAGENTA = 45 + CYAN = 46 + WHITE = 47 + RESET = 49 + + # These are fairly well supported, but not part of the standard. + LIGHTBLACK_EX = 100 + LIGHTRED_EX = 101 + LIGHTGREEN_EX = 102 + LIGHTYELLOW_EX = 103 + LIGHTBLUE_EX = 104 + LIGHTMAGENTA_EX = 105 + LIGHTCYAN_EX = 106 + LIGHTWHITE_EX = 107 + + +class AnsiStyle(AnsiCodes): + BRIGHT = 1 + DIM = 2 + NORMAL = 22 + RESET_ALL = 0 + +Fore = AnsiFore() +Back = AnsiBack() +Style = AnsiStyle() +Cursor = AnsiCursor() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/ansitowin32.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/ansitowin32.py new file mode 100644 index 000000000..abf209e60 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/ansitowin32.py @@ -0,0 +1,277 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import re +import sys +import os + +from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL +from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle +from .win32 import windll, winapi_test + + +winterm = None +if windll is not None: + winterm = WinTerm() + + +class StreamWrapper(object): + ''' + Wraps a stream (such as stdout), acting as a transparent proxy for all + attribute access apart from method 'write()', which is delegated to our + Converter instance. + ''' + def __init__(self, wrapped, converter): + # double-underscore everything to prevent clashes with names of + # attributes on the wrapped stream object. + self.__wrapped = wrapped + self.__convertor = converter + + def __getattr__(self, name): + return getattr(self.__wrapped, name) + + def __enter__(self, *args, **kwargs): + # special method lookup bypasses __getattr__/__getattribute__, see + # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit + # thus, contextlib magic methods are not proxied via __getattr__ + return self.__wrapped.__enter__(*args, **kwargs) + + def __exit__(self, *args, **kwargs): + return self.__wrapped.__exit__(*args, **kwargs) + + def __setstate__(self, state): + self.__dict__ = state + + def __getstate__(self): + return self.__dict__ + + def write(self, text): + self.__convertor.write(text) + + def isatty(self): + stream = self.__wrapped + if 'PYCHARM_HOSTED' in os.environ: + if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): + return True + try: + stream_isatty = stream.isatty + except AttributeError: + return False + else: + return stream_isatty() + + @property + def closed(self): + stream = self.__wrapped + try: + return stream.closed + # AttributeError in the case that the stream doesn't support being closed + # ValueError for the case that the stream has already been detached when atexit runs + except (AttributeError, ValueError): + return True + + +class AnsiToWin32(object): + ''' + Implements a 'write()' method which, on Windows, will strip ANSI character + sequences from the text, and if outputting to a tty, will convert them into + win32 function calls. + ''' + ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer + ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command + + def __init__(self, wrapped, convert=None, strip=None, autoreset=False): + # The wrapped stream (normally sys.stdout or sys.stderr) + self.wrapped = wrapped + + # should we reset colors to defaults after every .write() + self.autoreset = autoreset + + # create the proxy wrapping our output stream + self.stream = StreamWrapper(wrapped, self) + + on_windows = os.name == 'nt' + # We test if the WinAPI works, because even if we are on Windows + # we may be using a terminal that doesn't support the WinAPI + # (e.g. Cygwin Terminal). In this case it's up to the terminal + # to support the ANSI codes. + conversion_supported = on_windows and winapi_test() + try: + fd = wrapped.fileno() + except Exception: + fd = -1 + system_has_native_ansi = not on_windows or enable_vt_processing(fd) + have_tty = not self.stream.closed and self.stream.isatty() + need_conversion = conversion_supported and not system_has_native_ansi + + # should we strip ANSI sequences from our output? + if strip is None: + strip = need_conversion or not have_tty + self.strip = strip + + # should we should convert ANSI sequences into win32 calls? + if convert is None: + convert = need_conversion and have_tty + self.convert = convert + + # dict of ansi codes to win32 functions and parameters + self.win32_calls = self.get_win32_calls() + + # are we wrapping stderr? + self.on_stderr = self.wrapped is sys.stderr + + def should_wrap(self): + ''' + True if this class is actually needed. If false, then the output + stream will not be affected, nor will win32 calls be issued, so + wrapping stdout is not actually required. This will generally be + False on non-Windows platforms, unless optional functionality like + autoreset has been requested using kwargs to init() + ''' + return self.convert or self.strip or self.autoreset + + def get_win32_calls(self): + if self.convert and winterm: + return { + AnsiStyle.RESET_ALL: (winterm.reset_all, ), + AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), + AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), + AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), + AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), + AnsiFore.RED: (winterm.fore, WinColor.RED), + AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), + AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), + AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), + AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), + AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), + AnsiFore.WHITE: (winterm.fore, WinColor.GREY), + AnsiFore.RESET: (winterm.fore, ), + AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), + AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), + AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), + AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), + AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), + AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), + AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), + AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), + AnsiBack.BLACK: (winterm.back, WinColor.BLACK), + AnsiBack.RED: (winterm.back, WinColor.RED), + AnsiBack.GREEN: (winterm.back, WinColor.GREEN), + AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), + AnsiBack.BLUE: (winterm.back, WinColor.BLUE), + AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), + AnsiBack.CYAN: (winterm.back, WinColor.CYAN), + AnsiBack.WHITE: (winterm.back, WinColor.GREY), + AnsiBack.RESET: (winterm.back, ), + AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), + AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), + AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), + AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), + AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), + AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), + AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), + AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), + } + return dict() + + def write(self, text): + if self.strip or self.convert: + self.write_and_convert(text) + else: + self.wrapped.write(text) + self.wrapped.flush() + if self.autoreset: + self.reset_all() + + + def reset_all(self): + if self.convert: + self.call_win32('m', (0,)) + elif not self.strip and not self.stream.closed: + self.wrapped.write(Style.RESET_ALL) + + + def write_and_convert(self, text): + ''' + Write the given text to our wrapped stream, stripping any ANSI + sequences from the text, and optionally converting them into win32 + calls. + ''' + cursor = 0 + text = self.convert_osc(text) + for match in self.ANSI_CSI_RE.finditer(text): + start, end = match.span() + self.write_plain_text(text, cursor, start) + self.convert_ansi(*match.groups()) + cursor = end + self.write_plain_text(text, cursor, len(text)) + + + def write_plain_text(self, text, start, end): + if start < end: + self.wrapped.write(text[start:end]) + self.wrapped.flush() + + + def convert_ansi(self, paramstring, command): + if self.convert: + params = self.extract_params(command, paramstring) + self.call_win32(command, params) + + + def extract_params(self, command, paramstring): + if command in 'Hf': + params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) + while len(params) < 2: + # defaults: + params = params + (1,) + else: + params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) + if len(params) == 0: + # defaults: + if command in 'JKm': + params = (0,) + elif command in 'ABCD': + params = (1,) + + return params + + + def call_win32(self, command, params): + if command == 'm': + for param in params: + if param in self.win32_calls: + func_args = self.win32_calls[param] + func = func_args[0] + args = func_args[1:] + kwargs = dict(on_stderr=self.on_stderr) + func(*args, **kwargs) + elif command in 'J': + winterm.erase_screen(params[0], on_stderr=self.on_stderr) + elif command in 'K': + winterm.erase_line(params[0], on_stderr=self.on_stderr) + elif command in 'Hf': # cursor position - absolute + winterm.set_cursor_position(params, on_stderr=self.on_stderr) + elif command in 'ABCD': # cursor position - relative + n = params[0] + # A - up, B - down, C - forward, D - back + x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] + winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) + + + def convert_osc(self, text): + for match in self.ANSI_OSC_RE.finditer(text): + start, end = match.span() + text = text[:start] + text[end:] + paramstring, command = match.groups() + if command == BEL: + if paramstring.count(";") == 1: + params = paramstring.split(";") + # 0 - change title and icon (we will only change title) + # 1 - change icon (we don't support this) + # 2 - change title + if params[0] in '02': + winterm.set_title(params[1]) + return text + + + def flush(self): + self.wrapped.flush() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/initialise.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/initialise.py new file mode 100644 index 000000000..d5fd4b71f --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/initialise.py @@ -0,0 +1,121 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import atexit +import contextlib +import sys + +from .ansitowin32 import AnsiToWin32 + + +def _wipe_internal_state_for_tests(): + global orig_stdout, orig_stderr + orig_stdout = None + orig_stderr = None + + global wrapped_stdout, wrapped_stderr + wrapped_stdout = None + wrapped_stderr = None + + global atexit_done + atexit_done = False + + global fixed_windows_console + fixed_windows_console = False + + try: + # no-op if it wasn't registered + atexit.unregister(reset_all) + except AttributeError: + # python 2: no atexit.unregister. Oh well, we did our best. + pass + + +def reset_all(): + if AnsiToWin32 is not None: # Issue #74: objects might become None at exit + AnsiToWin32(orig_stdout).reset_all() + + +def init(autoreset=False, convert=None, strip=None, wrap=True): + + if not wrap and any([autoreset, convert, strip]): + raise ValueError('wrap=False conflicts with any other arg=True') + + global wrapped_stdout, wrapped_stderr + global orig_stdout, orig_stderr + + orig_stdout = sys.stdout + orig_stderr = sys.stderr + + if sys.stdout is None: + wrapped_stdout = None + else: + sys.stdout = wrapped_stdout = \ + wrap_stream(orig_stdout, convert, strip, autoreset, wrap) + if sys.stderr is None: + wrapped_stderr = None + else: + sys.stderr = wrapped_stderr = \ + wrap_stream(orig_stderr, convert, strip, autoreset, wrap) + + global atexit_done + if not atexit_done: + atexit.register(reset_all) + atexit_done = True + + +def deinit(): + if orig_stdout is not None: + sys.stdout = orig_stdout + if orig_stderr is not None: + sys.stderr = orig_stderr + + +def just_fix_windows_console(): + global fixed_windows_console + + if sys.platform != "win32": + return + if fixed_windows_console: + return + if wrapped_stdout is not None or wrapped_stderr is not None: + # Someone already ran init() and it did stuff, so we won't second-guess them + return + + # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the + # native ANSI support in the console as a side-effect. We only need to actually + # replace sys.stdout/stderr if we're in the old-style conversion mode. + new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) + if new_stdout.convert: + sys.stdout = new_stdout + new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) + if new_stderr.convert: + sys.stderr = new_stderr + + fixed_windows_console = True + +@contextlib.contextmanager +def colorama_text(*args, **kwargs): + init(*args, **kwargs) + try: + yield + finally: + deinit() + + +def reinit(): + if wrapped_stdout is not None: + sys.stdout = wrapped_stdout + if wrapped_stderr is not None: + sys.stderr = wrapped_stderr + + +def wrap_stream(stream, convert, strip, autoreset, wrap): + if wrap: + wrapper = AnsiToWin32(stream, + convert=convert, strip=strip, autoreset=autoreset) + if wrapper.should_wrap(): + stream = wrapper.stream + return stream + + +# Use this for initial setup as well, to reduce code duplication +_wipe_internal_state_for_tests() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/__init__.py new file mode 100644 index 000000000..8c5661e93 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/__init__.py @@ -0,0 +1 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/ansi_test.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/ansi_test.py new file mode 100644 index 000000000..0a20c80f8 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/ansi_test.py @@ -0,0 +1,76 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansi import Back, Fore, Style +from ..ansitowin32 import AnsiToWin32 + +stdout_orig = sys.stdout +stderr_orig = sys.stderr + + +class AnsiTest(TestCase): + + def setUp(self): + # sanity check: stdout should be a file or StringIO object. + # It will only be AnsiToWin32 if init() has previously wrapped it + self.assertNotEqual(type(sys.stdout), AnsiToWin32) + self.assertNotEqual(type(sys.stderr), AnsiToWin32) + + def tearDown(self): + sys.stdout = stdout_orig + sys.stderr = stderr_orig + + + def testForeAttributes(self): + self.assertEqual(Fore.BLACK, '\033[30m') + self.assertEqual(Fore.RED, '\033[31m') + self.assertEqual(Fore.GREEN, '\033[32m') + self.assertEqual(Fore.YELLOW, '\033[33m') + self.assertEqual(Fore.BLUE, '\033[34m') + self.assertEqual(Fore.MAGENTA, '\033[35m') + self.assertEqual(Fore.CYAN, '\033[36m') + self.assertEqual(Fore.WHITE, '\033[37m') + self.assertEqual(Fore.RESET, '\033[39m') + + # Check the light, extended versions. + self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') + self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') + self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') + self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') + self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') + self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') + self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') + self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') + + + def testBackAttributes(self): + self.assertEqual(Back.BLACK, '\033[40m') + self.assertEqual(Back.RED, '\033[41m') + self.assertEqual(Back.GREEN, '\033[42m') + self.assertEqual(Back.YELLOW, '\033[43m') + self.assertEqual(Back.BLUE, '\033[44m') + self.assertEqual(Back.MAGENTA, '\033[45m') + self.assertEqual(Back.CYAN, '\033[46m') + self.assertEqual(Back.WHITE, '\033[47m') + self.assertEqual(Back.RESET, '\033[49m') + + # Check the light, extended versions. + self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') + self.assertEqual(Back.LIGHTRED_EX, '\033[101m') + self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') + self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') + self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') + self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') + self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') + self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') + + + def testStyleAttributes(self): + self.assertEqual(Style.DIM, '\033[2m') + self.assertEqual(Style.NORMAL, '\033[22m') + self.assertEqual(Style.BRIGHT, '\033[1m') + + +if __name__ == '__main__': + main() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py new file mode 100644 index 000000000..91ca551f9 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/ansitowin32_test.py @@ -0,0 +1,294 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from io import StringIO, TextIOWrapper +from unittest import TestCase, main +try: + from contextlib import ExitStack +except ImportError: + # python 2 + from contextlib2 import ExitStack + +try: + from unittest.mock import MagicMock, Mock, patch +except ImportError: + from mock import MagicMock, Mock, patch + +from ..ansitowin32 import AnsiToWin32, StreamWrapper +from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING +from .utils import osname + + +class StreamWrapperTest(TestCase): + + def testIsAProxy(self): + mockStream = Mock() + wrapper = StreamWrapper(mockStream, None) + self.assertTrue( wrapper.random_attr is mockStream.random_attr ) + + def testDelegatesWrite(self): + mockStream = Mock() + mockConverter = Mock() + wrapper = StreamWrapper(mockStream, mockConverter) + wrapper.write('hello') + self.assertTrue(mockConverter.write.call_args, (('hello',), {})) + + def testDelegatesContext(self): + mockConverter = Mock() + s = StringIO() + with StreamWrapper(s, mockConverter) as fp: + fp.write(u'hello') + self.assertTrue(s.closed) + + def testProxyNoContextManager(self): + mockStream = MagicMock() + mockStream.__enter__.side_effect = AttributeError() + mockConverter = Mock() + with self.assertRaises(AttributeError) as excinfo: + with StreamWrapper(mockStream, mockConverter) as wrapper: + wrapper.write('hello') + + def test_closed_shouldnt_raise_on_closed_stream(self): + stream = StringIO() + stream.close() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + + def test_closed_shouldnt_raise_on_detached_stream(self): + stream = TextIOWrapper(StringIO()) + stream.detach() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + +class AnsiToWin32Test(TestCase): + + def testInit(self): + mockStdout = Mock() + auto = Mock() + stream = AnsiToWin32(mockStdout, autoreset=auto) + self.assertEqual(stream.wrapped, mockStdout) + self.assertEqual(stream.autoreset, auto) + + @patch('colorama.ansitowin32.winterm', None) + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + def testStripIsTrueOnWindows(self): + with osname('nt'): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + self.assertTrue(stream.strip) + + def testStripIsFalseOffWindows(self): + with osname('posix'): + mockStdout = Mock(closed=False) + stream = AnsiToWin32(mockStdout) + self.assertFalse(stream.strip) + + def testWriteStripsAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = True + + stream.write('abc') + + self.assertFalse(stream.wrapped.write.called) + self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) + + def testWriteDoesNotStripAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = False + stream.convert = False + + stream.write('abc') + + self.assertFalse(stream.write_and_convert.called) + self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) + + def assert_autoresets(self, convert, autoreset=True): + stream = AnsiToWin32(Mock()) + stream.convert = convert + stream.reset_all = Mock() + stream.autoreset = autoreset + stream.winterm = Mock() + + stream.write('abc') + + self.assertEqual(stream.reset_all.called, autoreset) + + def testWriteAutoresets(self): + self.assert_autoresets(convert=True) + self.assert_autoresets(convert=False) + self.assert_autoresets(convert=True, autoreset=False) + self.assert_autoresets(convert=False, autoreset=False) + + def testWriteAndConvertWritesPlainText(self): + stream = AnsiToWin32(Mock()) + stream.write_and_convert( 'abc' ) + self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) + + def testWriteAndConvertStripsAllValidAnsi(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + data = [ + 'abc\033[mdef', + 'abc\033[0mdef', + 'abc\033[2mdef', + 'abc\033[02mdef', + 'abc\033[002mdef', + 'abc\033[40mdef', + 'abc\033[040mdef', + 'abc\033[0;1mdef', + 'abc\033[40;50mdef', + 'abc\033[50;30;40mdef', + 'abc\033[Adef', + 'abc\033[0Gdef', + 'abc\033[1;20;128Hdef', + ] + for datum in data: + stream.wrapped.write.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( + [args[0] for args in stream.wrapped.write.call_args_list], + [ ('abc',), ('def',) ] + ) + + def testWriteAndConvertSkipsEmptySnippets(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + stream.write_and_convert( '\033[40m\033[41m' ) + self.assertFalse( stream.wrapped.write.called ) + + def testWriteAndConvertCallsWin32WithParamsAndCommand(self): + stream = AnsiToWin32(Mock()) + stream.convert = True + stream.call_win32 = Mock() + stream.extract_params = Mock(return_value='params') + data = { + 'abc\033[adef': ('a', 'params'), + 'abc\033[;;bdef': ('b', 'params'), + 'abc\033[0cdef': ('c', 'params'), + 'abc\033[;;0;;Gdef': ('G', 'params'), + 'abc\033[1;20;128Hdef': ('H', 'params'), + } + for datum, expected in data.items(): + stream.call_win32.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( stream.call_win32.call_args[0], expected ) + + def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + converter = AnsiToWin32(stream) + stream.close() + + converter.reset_all() + + def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + stream.close() + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(stream) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def test_wrap_shouldnt_raise_on_missing_closed_attr(self): + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(object()) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def testExtractParams(self): + stream = AnsiToWin32(Mock()) + data = { + '': (0,), + ';;': (0,), + '2': (2,), + ';;002;;': (2,), + '0;1': (0, 1), + ';;003;;456;;': (3, 456), + '11;22;33;44;55': (11, 22, 33, 44, 55), + } + for datum, expected in data.items(): + self.assertEqual(stream.extract_params('m', datum), expected) + + def testCallWin32UsesLookup(self): + listener = Mock() + stream = AnsiToWin32(listener) + stream.win32_calls = { + 1: (lambda *_, **__: listener(11),), + 2: (lambda *_, **__: listener(22),), + 3: (lambda *_, **__: listener(33),), + } + stream.call_win32('m', (3, 1, 99, 2)) + self.assertEqual( + [a[0][0] for a in listener.call_args_list], + [33, 11, 22] ) + + def test_osc_codes(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout, convert=True) + with patch('colorama.ansitowin32.winterm') as winterm: + data = [ + '\033]0\x07', # missing arguments + '\033]0;foo\x08', # wrong OSC command + '\033]0;colorama_test_title\x07', # should work + '\033]1;colorama_test_title\x07', # wrong set command + '\033]2;colorama_test_title\x07', # should work + '\033]' + ';' * 64 + '\x08', # see issue #247 + ] + for code in data: + stream.write(code) + self.assertEqual(winterm.set_title.call_count, 2) + + def test_native_windows_ansi(self): + with ExitStack() as stack: + def p(a, b): + stack.enter_context(patch(a, b, create=True)) + # Pretend to be on Windows + p("colorama.ansitowin32.os.name", "nt") + p("colorama.ansitowin32.winapi_test", lambda: True) + p("colorama.win32.winapi_test", lambda: True) + p("colorama.winterm.win32.windll", "non-None") + p("colorama.winterm.get_osfhandle", lambda _: 1234) + + # Pretend that our mock stream has native ANSI support + p( + "colorama.winterm.win32.GetConsoleMode", + lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = True + stdout.fileno.return_value = 1 + + # Our fake console says it has native vt support, so AnsiToWin32 should + # enable that support and do nothing else. + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertFalse(stream.strip) + self.assertFalse(stream.convert) + self.assertFalse(stream.should_wrap()) + + # Now let's pretend we're on an old Windows console, that doesn't have + # native ANSI support. + p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertTrue(stream.strip) + self.assertTrue(stream.convert) + self.assertTrue(stream.should_wrap()) + + +if __name__ == '__main__': + main() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/initialise_test.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/initialise_test.py new file mode 100644 index 000000000..89f9b0751 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/initialise_test.py @@ -0,0 +1,189 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import patch, Mock +except ImportError: + from mock import patch, Mock + +from ..ansitowin32 import StreamWrapper +from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests +from .utils import osname, replace_by + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + + +class InitTest(TestCase): + + @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") + def setUp(self): + # sanity check + self.assertNotWrapped() + + def tearDown(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def assertWrapped(self): + self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') + self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') + self.assertTrue(isinstance(sys.stdout, StreamWrapper), + 'bad stdout wrapper') + self.assertTrue(isinstance(sys.stderr, StreamWrapper), + 'bad stderr wrapper') + + def assertNotWrapped(self): + self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') + self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) + def testInitWrapsOnWindows(self, _): + with osname("nt"): + init() + self.assertWrapped() + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: False) + def testInitDoesntWrapOnEmulatedWindows(self, _): + with osname("nt"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapOnNonWindows(self): + with osname("posix"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapIfNone(self): + with replace_by(None): + init() + # We can't use assertNotWrapped here because replace_by(None) + # changes stdout/stderr already. + self.assertIsNone(sys.stdout) + self.assertIsNone(sys.stderr) + + def testInitAutoresetOnWrapsOnAllPlatforms(self): + with osname("posix"): + init(autoreset=True) + self.assertWrapped() + + def testInitWrapOffDoesntWrapOnWindows(self): + with osname("nt"): + init(wrap=False) + self.assertNotWrapped() + + def testInitWrapOffIncompatibleWithAutoresetOn(self): + self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) + + @patch('colorama.win32.SetConsoleTextAttribute') + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetPassedOn(self, mockATW32, _): + with osname("nt"): + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 2) + self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) + + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetChangeable(self, mockATW32): + with osname("nt"): + init() + + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 4) + self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) + + init() + self.assertEqual(len(mockATW32.call_args_list), 6) + self.assertEqual( + mockATW32.call_args_list[4][1]['autoreset'], False) + self.assertEqual( + mockATW32.call_args_list[5][1]['autoreset'], False) + + + @patch('colorama.initialise.atexit.register') + def testAtexitRegisteredOnlyOnce(self, mockRegister): + init() + self.assertTrue(mockRegister.called) + mockRegister.reset_mock() + init() + self.assertFalse(mockRegister.called) + + +class JustFixWindowsConsoleTest(TestCase): + def _reset(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def tearDown(self): + self._reset() + + @patch("colorama.ansitowin32.winapi_test", lambda: True) + def testJustFixWindowsConsole(self): + if sys.platform != "win32": + # just_fix_windows_console should be a no-op + just_fix_windows_console() + self.assertIs(sys.stdout, orig_stdout) + self.assertIs(sys.stderr, orig_stderr) + else: + def fake_std(): + # Emulate stdout=not a tty, stderr=tty + # to check that we handle both cases correctly + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = False + stdout.fileno.return_value = 1 + sys.stdout = stdout + + stderr = Mock() + stderr.closed = False + stderr.isatty.return_value = True + stderr.fileno.return_value = 2 + sys.stderr = stderr + + for native_ansi in [False, True]: + with patch( + 'colorama.ansitowin32.enable_vt_processing', + lambda *_: native_ansi + ): + self._reset() + fake_std() + + # Regular single-call test + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + if native_ansi: + self.assertIs(sys.stderr, prev_stderr) + else: + self.assertIsNot(sys.stderr, prev_stderr) + + # second call without resetting is always a no-op + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + self.assertIs(sys.stderr, prev_stderr) + + self._reset() + fake_std() + + # If init() runs first, just_fix_windows_console should be a no-op + init() + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(prev_stdout, sys.stdout) + self.assertIs(prev_stderr, sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/isatty_test.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/isatty_test.py new file mode 100644 index 000000000..0f84e4bef --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/isatty_test.py @@ -0,0 +1,57 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansitowin32 import StreamWrapper, AnsiToWin32 +from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY + + +def is_a_tty(stream): + return StreamWrapper(stream, None).isatty() + +class IsattyTest(TestCase): + + def test_TTY(self): + tty = StreamTTY() + self.assertTrue(is_a_tty(tty)) + with pycharm(): + self.assertTrue(is_a_tty(tty)) + + def test_nonTTY(self): + non_tty = StreamNonTTY() + self.assertFalse(is_a_tty(non_tty)) + with pycharm(): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharm(self): + with pycharm(): + self.assertTrue(is_a_tty(sys.stderr)) + self.assertTrue(is_a_tty(sys.stdout)) + + def test_withPycharmTTYOverride(self): + tty = StreamTTY() + with pycharm(), replace_by(tty): + self.assertTrue(is_a_tty(tty)) + + def test_withPycharmNonTTYOverride(self): + non_tty = StreamNonTTY() + with pycharm(), replace_by(non_tty): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharmNoneOverride(self): + with pycharm(): + with replace_by(None), replace_original_by(None): + self.assertFalse(is_a_tty(None)) + self.assertFalse(is_a_tty(StreamNonTTY())) + self.assertTrue(is_a_tty(StreamTTY())) + + def test_withPycharmStreamWrapped(self): + with pycharm(): + self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) + self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) + + +if __name__ == '__main__': + main() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/utils.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/utils.py new file mode 100644 index 000000000..472fafb44 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/utils.py @@ -0,0 +1,49 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from contextlib import contextmanager +from io import StringIO +import sys +import os + + +class StreamTTY(StringIO): + def isatty(self): + return True + +class StreamNonTTY(StringIO): + def isatty(self): + return False + +@contextmanager +def osname(name): + orig = os.name + os.name = name + yield + os.name = orig + +@contextmanager +def replace_by(stream): + orig_stdout = sys.stdout + orig_stderr = sys.stderr + sys.stdout = stream + sys.stderr = stream + yield + sys.stdout = orig_stdout + sys.stderr = orig_stderr + +@contextmanager +def replace_original_by(stream): + orig_stdout = sys.__stdout__ + orig_stderr = sys.__stderr__ + sys.__stdout__ = stream + sys.__stderr__ = stream + yield + sys.__stdout__ = orig_stdout + sys.__stderr__ = orig_stderr + +@contextmanager +def pycharm(): + os.environ["PYCHARM_HOSTED"] = "1" + non_tty = StreamNonTTY() + with replace_by(non_tty), replace_original_by(non_tty): + yield + del os.environ["PYCHARM_HOSTED"] diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/winterm_test.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/winterm_test.py new file mode 100644 index 000000000..d0955f9e6 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/tests/winterm_test.py @@ -0,0 +1,131 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import Mock, patch +except ImportError: + from mock import Mock, patch + +from ..winterm import WinColor, WinStyle, WinTerm + + +class WinTermTest(TestCase): + + @patch('colorama.winterm.win32') + def testInit(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 7 + 6 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + self.assertEqual(term._fore, 7) + self.assertEqual(term._back, 6) + self.assertEqual(term._style, 8) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testGetAttrs(self): + term = WinTerm() + + term._fore = 0 + term._back = 0 + term._style = 0 + self.assertEqual(term.get_attrs(), 0) + + term._fore = WinColor.YELLOW + self.assertEqual(term.get_attrs(), WinColor.YELLOW) + + term._back = WinColor.MAGENTA + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16) + + term._style = WinStyle.BRIGHT + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) + + @patch('colorama.winterm.win32') + def testResetAll(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 1 + 2 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + + term.set_console = Mock() + term._fore = -1 + term._back = -1 + term._style = -1 + + term.reset_all() + + self.assertEqual(term._fore, 1) + self.assertEqual(term._back, 2) + self.assertEqual(term._style, 8) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testFore(self): + term = WinTerm() + term.set_console = Mock() + term._fore = 0 + + term.fore(5) + + self.assertEqual(term._fore, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testBack(self): + term = WinTerm() + term.set_console = Mock() + term._back = 0 + + term.back(5) + + self.assertEqual(term._back, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testStyle(self): + term = WinTerm() + term.set_console = Mock() + term._style = 0 + + term.style(22) + + self.assertEqual(term._style, 22) + self.assertEqual(term.set_console.called, True) + + @patch('colorama.winterm.win32') + def testSetConsole(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console() + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDOUT, term.get_attrs()), {}) + ) + + @patch('colorama.winterm.win32') + def testSetConsoleOnStderr(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console(on_stderr=True) + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDERR, term.get_attrs()), {}) + ) + + +if __name__ == '__main__': + main() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/win32.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/win32.py new file mode 100644 index 000000000..841b0e270 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/win32.py @@ -0,0 +1,180 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW + _SetConsoleTitleW.argtypes = [ + wintypes.LPCWSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + _GetConsoleMode = windll.kernel32.GetConsoleMode + _GetConsoleMode.argtypes = [ + wintypes.HANDLE, + POINTER(wintypes.DWORD) + ] + _GetConsoleMode.restype = wintypes.BOOL + + _SetConsoleMode = windll.kernel32.SetConsoleMode + _SetConsoleMode.argtypes = [ + wintypes.HANDLE, + wintypes.DWORD + ] + _SetConsoleMode.restype = wintypes.BOOL + + def _winapi_test(handle): + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def winapi_test(): + return any(_winapi_test(h) for h in + (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = _GetStdHandle(stream_id) + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = _GetStdHandle(stream_id) + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = _GetStdHandle(stream_id) + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = _GetStdHandle(stream_id) + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = _GetStdHandle(stream_id) + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) + + def GetConsoleMode(handle): + mode = wintypes.DWORD() + success = _GetConsoleMode(handle, byref(mode)) + if not success: + raise ctypes.WinError() + return mode.value + + def SetConsoleMode(handle, mode): + success = _SetConsoleMode(handle, mode) + if not success: + raise ctypes.WinError() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/winterm.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/winterm.py new file mode 100644 index 000000000..aad867e8c --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/colorama/winterm.py @@ -0,0 +1,195 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +try: + from msvcrt import get_osfhandle +except ImportError: + def get_osfhandle(_): + raise OSError("This isn't windows!") + + +from . import win32 + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + self._light = 0 + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + elif mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + elif mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) + + +def enable_vt_processing(fd): + if win32.windll is None or not win32.winapi_test(): + return False + + try: + handle = get_osfhandle(fd) + mode = win32.GetConsoleMode(handle) + win32.SetConsoleMode( + handle, + mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + + mode = win32.GetConsoleMode(handle) + if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: + return True + # Can get TypeError in testsuite where 'fd' is a Mock() + except (OSError, TypeError): + return False diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/__init__.py new file mode 100644 index 000000000..7f4c631ba --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/__init__.py @@ -0,0 +1,49 @@ +from typing import Any, Optional + +from .main import (dotenv_values, find_dotenv, get_key, load_dotenv, set_key, + unset_key) + + +def load_ipython_extension(ipython: Any) -> None: + from .ipython import load_ipython_extension + load_ipython_extension(ipython) + + +def get_cli_string( + path: Optional[str] = None, + action: Optional[str] = None, + key: Optional[str] = None, + value: Optional[str] = None, + quote: Optional[str] = None, +): + """Returns a string suitable for running as a shell script. + + Useful for converting a arguments passed to a fabric task + to be passed to a `local` or `run` command. + """ + command = ['dotenv'] + if quote: + command.append(f'-q {quote}') + if path: + command.append(f'-f {path}') + if action: + command.append(action) + if key: + command.append(key) + if value: + if ' ' in value: + command.append(f'"{value}"') + else: + command.append(value) + + return ' '.join(command).strip() + + +__all__ = ['get_cli_string', + 'load_dotenv', + 'dotenv_values', + 'get_key', + 'set_key', + 'unset_key', + 'find_dotenv', + 'load_ipython_extension'] diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/__main__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/__main__.py new file mode 100644 index 000000000..3977f55a8 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/__main__.py @@ -0,0 +1,6 @@ +"""Entry point for cli, enables execution with `python -m dotenv`""" + +from .cli import cli + +if __name__ == "__main__": + cli() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/cli.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/cli.py new file mode 100644 index 000000000..65ead4615 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/cli.py @@ -0,0 +1,199 @@ +import json +import os +import shlex +import sys +from contextlib import contextmanager +from subprocess import Popen +from typing import Any, Dict, IO, Iterator, List + +try: + import click +except ImportError: + sys.stderr.write('It seems python-dotenv is not installed with cli option. \n' + 'Run pip install "python-dotenv[cli]" to fix this.') + sys.exit(1) + +from .main import dotenv_values, set_key, unset_key +from .version import __version__ + + +def enumerate_env(): + """ + Return a path for the ${pwd}/.env file. + + If pwd does not exist, return None. + """ + try: + cwd = os.getcwd() + except FileNotFoundError: + return None + path = os.path.join(cwd, '.env') + return path + + +@click.group() +@click.option('-f', '--file', default=enumerate_env(), + type=click.Path(file_okay=True), + help="Location of the .env file, defaults to .env file in current working directory.") +@click.option('-q', '--quote', default='always', + type=click.Choice(['always', 'never', 'auto']), + help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.") +@click.option('-e', '--export', default=False, + type=click.BOOL, + help="Whether to write the dot file as an executable bash script.") +@click.version_option(version=__version__) +@click.pass_context +def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None: + """This script is used to set, get or unset values from a .env file.""" + ctx.obj = {'QUOTE': quote, 'EXPORT': export, 'FILE': file} + + +@contextmanager +def stream_file(path: os.PathLike) -> Iterator[IO[str]]: + """ + Open a file and yield the corresponding (decoded) stream. + + Exits with error code 2 if the file cannot be opened. + """ + + try: + with open(path) as stream: + yield stream + except OSError as exc: + print(f"Error opening env file: {exc}", file=sys.stderr) + exit(2) + + +@cli.command() +@click.pass_context +@click.option('--format', default='simple', + type=click.Choice(['simple', 'json', 'shell', 'export']), + help="The format in which to display the list. Default format is simple, " + "which displays name=value without quotes.") +def list(ctx: click.Context, format: bool) -> None: + """Display all the stored key/value.""" + file = ctx.obj['FILE'] + + with stream_file(file) as stream: + values = dotenv_values(stream=stream) + + if format == 'json': + click.echo(json.dumps(values, indent=2, sort_keys=True)) + else: + prefix = 'export ' if format == 'export' else '' + for k in sorted(values): + v = values[k] + if v is not None: + if format in ('export', 'shell'): + v = shlex.quote(v) + click.echo(f'{prefix}{k}={v}') + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +@click.argument('value', required=True) +def set(ctx: click.Context, key: Any, value: Any) -> None: + """Store the given key/value.""" + file = ctx.obj['FILE'] + quote = ctx.obj['QUOTE'] + export = ctx.obj['EXPORT'] + success, key, value = set_key(file, key, value, quote, export) + if success: + click.echo(f'{key}={value}') + else: + exit(1) + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +def get(ctx: click.Context, key: Any) -> None: + """Retrieve the value for the given key.""" + file = ctx.obj['FILE'] + + with stream_file(file) as stream: + values = dotenv_values(stream=stream) + + stored_value = values.get(key) + if stored_value: + click.echo(stored_value) + else: + exit(1) + + +@cli.command() +@click.pass_context +@click.argument('key', required=True) +def unset(ctx: click.Context, key: Any) -> None: + """Removes the given key.""" + file = ctx.obj['FILE'] + quote = ctx.obj['QUOTE'] + success, key = unset_key(file, key, quote) + if success: + click.echo(f"Successfully removed {key}") + else: + exit(1) + + +@cli.command(context_settings={'ignore_unknown_options': True}) +@click.pass_context +@click.option( + "--override/--no-override", + default=True, + help="Override variables from the environment file with those from the .env file.", +) +@click.argument('commandline', nargs=-1, type=click.UNPROCESSED) +def run(ctx: click.Context, override: bool, commandline: List[str]) -> None: + """Run command with environment variables present.""" + file = ctx.obj['FILE'] + if not os.path.isfile(file): + raise click.BadParameter( + f'Invalid value for \'-f\' "{file}" does not exist.', + ctx=ctx + ) + dotenv_as_dict = { + k: v + for (k, v) in dotenv_values(file).items() + if v is not None and (override or k not in os.environ) + } + + if not commandline: + click.echo('No command given.') + exit(1) + ret = run_command(commandline, dotenv_as_dict) + exit(ret) + + +def run_command(command: List[str], env: Dict[str, str]) -> int: + """Run command in sub process. + + Runs the command in a sub process with the variables from `env` + added in the current environment variables. + + Parameters + ---------- + command: List[str] + The command and it's parameters + env: Dict + The additional environment variables + + Returns + ------- + int + The return code of the command + + """ + # copy the current environment variables and add the vales from + # `env` + cmd_env = os.environ.copy() + cmd_env.update(env) + + p = Popen(command, + universal_newlines=True, + bufsize=0, + shell=False, + env=cmd_env) + _, _ = p.communicate() + + return p.returncode diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/ipython.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/ipython.py new file mode 100644 index 000000000..7df727cd0 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/ipython.py @@ -0,0 +1,39 @@ +from IPython.core.magic import Magics, line_magic, magics_class # type: ignore +from IPython.core.magic_arguments import (argument, magic_arguments, # type: ignore + parse_argstring) # type: ignore + +from .main import find_dotenv, load_dotenv + + +@magics_class +class IPythonDotEnv(Magics): + + @magic_arguments() + @argument( + '-o', '--override', action='store_true', + help="Indicate to override existing variables" + ) + @argument( + '-v', '--verbose', action='store_true', + help="Indicate function calls to be verbose" + ) + @argument('dotenv_path', nargs='?', type=str, default='.env', + help='Search in increasingly higher folders for the `dotenv_path`') + @line_magic + def dotenv(self, line): + args = parse_argstring(self.dotenv, line) + # Locate the .env file + dotenv_path = args.dotenv_path + try: + dotenv_path = find_dotenv(dotenv_path, True, True) + except IOError: + print("cannot find .env file") + return + + # Load the .env file + load_dotenv(dotenv_path, verbose=args.verbose, override=args.override) + + +def load_ipython_extension(ipython): + """Register the %dotenv magic.""" + ipython.register_magics(IPythonDotEnv) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/main.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/main.py new file mode 100644 index 000000000..7bc542857 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/main.py @@ -0,0 +1,392 @@ +import io +import logging +import os +import pathlib +import shutil +import sys +import tempfile +from collections import OrderedDict +from contextlib import contextmanager +from typing import (IO, Dict, Iterable, Iterator, Mapping, Optional, Tuple, + Union) + +from .parser import Binding, parse_stream +from .variables import parse_variables + +# A type alias for a string path to be used for the paths in this file. +# These paths may flow to `open()` and `shutil.move()`; `shutil.move()` +# only accepts string paths, not byte paths or file descriptors. See +# https://github.com/python/typeshed/pull/6832. +StrPath = Union[str, 'os.PathLike[str]'] + +logger = logging.getLogger(__name__) + + +def with_warn_for_invalid_lines(mappings: Iterator[Binding]) -> Iterator[Binding]: + for mapping in mappings: + if mapping.error: + logger.warning( + "Python-dotenv could not parse statement starting at line %s", + mapping.original.line, + ) + yield mapping + + +class DotEnv: + def __init__( + self, + dotenv_path: Optional[StrPath], + stream: Optional[IO[str]] = None, + verbose: bool = False, + encoding: Optional[str] = None, + interpolate: bool = True, + override: bool = True, + ) -> None: + self.dotenv_path: Optional[StrPath] = dotenv_path + self.stream: Optional[IO[str]] = stream + self._dict: Optional[Dict[str, Optional[str]]] = None + self.verbose: bool = verbose + self.encoding: Optional[str] = encoding + self.interpolate: bool = interpolate + self.override: bool = override + + @contextmanager + def _get_stream(self) -> Iterator[IO[str]]: + if self.dotenv_path and os.path.isfile(self.dotenv_path): + with open(self.dotenv_path, encoding=self.encoding) as stream: + yield stream + elif self.stream is not None: + yield self.stream + else: + if self.verbose: + logger.info( + "Python-dotenv could not find configuration file %s.", + self.dotenv_path or '.env', + ) + yield io.StringIO('') + + def dict(self) -> Dict[str, Optional[str]]: + """Return dotenv as dict""" + if self._dict: + return self._dict + + raw_values = self.parse() + + if self.interpolate: + self._dict = OrderedDict(resolve_variables(raw_values, override=self.override)) + else: + self._dict = OrderedDict(raw_values) + + return self._dict + + def parse(self) -> Iterator[Tuple[str, Optional[str]]]: + with self._get_stream() as stream: + for mapping in with_warn_for_invalid_lines(parse_stream(stream)): + if mapping.key is not None: + yield mapping.key, mapping.value + + def set_as_environment_variables(self) -> bool: + """ + Load the current dotenv as system environment variable. + """ + if not self.dict(): + return False + + for k, v in self.dict().items(): + if k in os.environ and not self.override: + continue + if v is not None: + os.environ[k] = v + + return True + + def get(self, key: str) -> Optional[str]: + """ + """ + data = self.dict() + + if key in data: + return data[key] + + if self.verbose: + logger.warning("Key %s not found in %s.", key, self.dotenv_path) + + return None + + +def get_key( + dotenv_path: StrPath, + key_to_get: str, + encoding: Optional[str] = "utf-8", +) -> Optional[str]: + """ + Get the value of a given key from the given .env. + + Returns `None` if the key isn't found or doesn't have a value. + """ + return DotEnv(dotenv_path, verbose=True, encoding=encoding).get(key_to_get) + + +@contextmanager +def rewrite( + path: StrPath, + encoding: Optional[str], +) -> Iterator[Tuple[IO[str], IO[str]]]: + pathlib.Path(path).touch() + + with tempfile.NamedTemporaryFile(mode="w", encoding=encoding, delete=False) as dest: + error = None + try: + with open(path, encoding=encoding) as source: + yield (source, dest) + except BaseException as err: + error = err + + if error is None: + shutil.move(dest.name, path) + else: + os.unlink(dest.name) + raise error from None + + +def set_key( + dotenv_path: StrPath, + key_to_set: str, + value_to_set: str, + quote_mode: str = "always", + export: bool = False, + encoding: Optional[str] = "utf-8", +) -> Tuple[Optional[bool], str, str]: + """ + Adds or Updates a key/value to the given .env + + If the .env path given doesn't exist, fails instead of risking creating + an orphan .env somewhere in the filesystem + """ + if quote_mode not in ("always", "auto", "never"): + raise ValueError(f"Unknown quote_mode: {quote_mode}") + + quote = ( + quote_mode == "always" + or (quote_mode == "auto" and not value_to_set.isalnum()) + ) + + if quote: + value_out = "'{}'".format(value_to_set.replace("'", "\\'")) + else: + value_out = value_to_set + if export: + line_out = f'export {key_to_set}={value_out}\n' + else: + line_out = f"{key_to_set}={value_out}\n" + + with rewrite(dotenv_path, encoding=encoding) as (source, dest): + replaced = False + missing_newline = False + for mapping in with_warn_for_invalid_lines(parse_stream(source)): + if mapping.key == key_to_set: + dest.write(line_out) + replaced = True + else: + dest.write(mapping.original.string) + missing_newline = not mapping.original.string.endswith("\n") + if not replaced: + if missing_newline: + dest.write("\n") + dest.write(line_out) + + return True, key_to_set, value_to_set + + +def unset_key( + dotenv_path: StrPath, + key_to_unset: str, + quote_mode: str = "always", + encoding: Optional[str] = "utf-8", +) -> Tuple[Optional[bool], str]: + """ + Removes a given key from the given `.env` file. + + If the .env path given doesn't exist, fails. + If the given key doesn't exist in the .env, fails. + """ + if not os.path.exists(dotenv_path): + logger.warning("Can't delete from %s - it doesn't exist.", dotenv_path) + return None, key_to_unset + + removed = False + with rewrite(dotenv_path, encoding=encoding) as (source, dest): + for mapping in with_warn_for_invalid_lines(parse_stream(source)): + if mapping.key == key_to_unset: + removed = True + else: + dest.write(mapping.original.string) + + if not removed: + logger.warning("Key %s not removed from %s - key doesn't exist.", key_to_unset, dotenv_path) + return None, key_to_unset + + return removed, key_to_unset + + +def resolve_variables( + values: Iterable[Tuple[str, Optional[str]]], + override: bool, +) -> Mapping[str, Optional[str]]: + new_values: Dict[str, Optional[str]] = {} + + for (name, value) in values: + if value is None: + result = None + else: + atoms = parse_variables(value) + env: Dict[str, Optional[str]] = {} + if override: + env.update(os.environ) # type: ignore + env.update(new_values) + else: + env.update(new_values) + env.update(os.environ) # type: ignore + result = "".join(atom.resolve(env) for atom in atoms) + + new_values[name] = result + + return new_values + + +def _walk_to_root(path: str) -> Iterator[str]: + """ + Yield directories starting from the given directory up to the root + """ + if not os.path.exists(path): + raise IOError('Starting path not found') + + if os.path.isfile(path): + path = os.path.dirname(path) + + last_dir = None + current_dir = os.path.abspath(path) + while last_dir != current_dir: + yield current_dir + parent_dir = os.path.abspath(os.path.join(current_dir, os.path.pardir)) + last_dir, current_dir = current_dir, parent_dir + + +def find_dotenv( + filename: str = '.env', + raise_error_if_not_found: bool = False, + usecwd: bool = False, +) -> str: + """ + Search in increasingly higher folders for the given file + + Returns path to the file if found, or an empty string otherwise + """ + + def _is_interactive(): + """ Decide whether this is running in a REPL or IPython notebook """ + try: + main = __import__('__main__', None, None, fromlist=['__file__']) + except ModuleNotFoundError: + return False + return not hasattr(main, '__file__') + + if usecwd or _is_interactive() or getattr(sys, 'frozen', False): + # Should work without __file__, e.g. in REPL or IPython notebook. + path = os.getcwd() + else: + # will work for .py files + frame = sys._getframe() + current_file = __file__ + + while frame.f_code.co_filename == current_file or not os.path.exists( + frame.f_code.co_filename + ): + assert frame.f_back is not None + frame = frame.f_back + frame_filename = frame.f_code.co_filename + path = os.path.dirname(os.path.abspath(frame_filename)) + + for dirname in _walk_to_root(path): + check_path = os.path.join(dirname, filename) + if os.path.isfile(check_path): + return check_path + + if raise_error_if_not_found: + raise IOError('File not found') + + return '' + + +def load_dotenv( + dotenv_path: Optional[StrPath] = None, + stream: Optional[IO[str]] = None, + verbose: bool = False, + override: bool = False, + interpolate: bool = True, + encoding: Optional[str] = "utf-8", +) -> bool: + """Parse a .env file and then load all the variables found as environment variables. + + Parameters: + dotenv_path: Absolute or relative path to .env file. + stream: Text stream (such as `io.StringIO`) with .env content, used if + `dotenv_path` is `None`. + verbose: Whether to output a warning the .env file is missing. + override: Whether to override the system environment variables with the variables + from the `.env` file. + encoding: Encoding to be used to read the file. + Returns: + Bool: True if at least one environment variable is set else False + + If both `dotenv_path` and `stream` are `None`, `find_dotenv()` is used to find the + .env file. + """ + if dotenv_path is None and stream is None: + dotenv_path = find_dotenv() + + dotenv = DotEnv( + dotenv_path=dotenv_path, + stream=stream, + verbose=verbose, + interpolate=interpolate, + override=override, + encoding=encoding, + ) + return dotenv.set_as_environment_variables() + + +def dotenv_values( + dotenv_path: Optional[StrPath] = None, + stream: Optional[IO[str]] = None, + verbose: bool = False, + interpolate: bool = True, + encoding: Optional[str] = "utf-8", +) -> Dict[str, Optional[str]]: + """ + Parse a .env file and return its content as a dict. + + The returned dict will have `None` values for keys without values in the .env file. + For example, `foo=bar` results in `{"foo": "bar"}` whereas `foo` alone results in + `{"foo": None}` + + Parameters: + dotenv_path: Absolute or relative path to the .env file. + stream: `StringIO` object with .env content, used if `dotenv_path` is `None`. + verbose: Whether to output a warning if the .env file is missing. + encoding: Encoding to be used to read the file. + + If both `dotenv_path` and `stream` are `None`, `find_dotenv()` is used to find the + .env file. + """ + if dotenv_path is None and stream is None: + dotenv_path = find_dotenv() + + return DotEnv( + dotenv_path=dotenv_path, + stream=stream, + verbose=verbose, + interpolate=interpolate, + override=True, + encoding=encoding, + ).dict() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/parser.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/parser.py new file mode 100644 index 000000000..735f14a3b --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/parser.py @@ -0,0 +1,175 @@ +import codecs +import re +from typing import (IO, Iterator, Match, NamedTuple, Optional, # noqa:F401 + Pattern, Sequence, Tuple) + + +def make_regex(string: str, extra_flags: int = 0) -> Pattern[str]: + return re.compile(string, re.UNICODE | extra_flags) + + +_newline = make_regex(r"(\r\n|\n|\r)") +_multiline_whitespace = make_regex(r"\s*", extra_flags=re.MULTILINE) +_whitespace = make_regex(r"[^\S\r\n]*") +_export = make_regex(r"(?:export[^\S\r\n]+)?") +_single_quoted_key = make_regex(r"'([^']+)'") +_unquoted_key = make_regex(r"([^=\#\s]+)") +_equal_sign = make_regex(r"(=[^\S\r\n]*)") +_single_quoted_value = make_regex(r"'((?:\\'|[^'])*)'") +_double_quoted_value = make_regex(r'"((?:\\"|[^"])*)"') +_unquoted_value = make_regex(r"([^\r\n]*)") +_comment = make_regex(r"(?:[^\S\r\n]*#[^\r\n]*)?") +_end_of_line = make_regex(r"[^\S\r\n]*(?:\r\n|\n|\r|$)") +_rest_of_line = make_regex(r"[^\r\n]*(?:\r|\n|\r\n)?") +_double_quote_escapes = make_regex(r"\\[\\'\"abfnrtv]") +_single_quote_escapes = make_regex(r"\\[\\']") + + +class Original(NamedTuple): + string: str + line: int + + +class Binding(NamedTuple): + key: Optional[str] + value: Optional[str] + original: Original + error: bool + + +class Position: + def __init__(self, chars: int, line: int) -> None: + self.chars = chars + self.line = line + + @classmethod + def start(cls) -> "Position": + return cls(chars=0, line=1) + + def set(self, other: "Position") -> None: + self.chars = other.chars + self.line = other.line + + def advance(self, string: str) -> None: + self.chars += len(string) + self.line += len(re.findall(_newline, string)) + + +class Error(Exception): + pass + + +class Reader: + def __init__(self, stream: IO[str]) -> None: + self.string = stream.read() + self.position = Position.start() + self.mark = Position.start() + + def has_next(self) -> bool: + return self.position.chars < len(self.string) + + def set_mark(self) -> None: + self.mark.set(self.position) + + def get_marked(self) -> Original: + return Original( + string=self.string[self.mark.chars:self.position.chars], + line=self.mark.line, + ) + + def peek(self, count: int) -> str: + return self.string[self.position.chars:self.position.chars + count] + + def read(self, count: int) -> str: + result = self.string[self.position.chars:self.position.chars + count] + if len(result) < count: + raise Error("read: End of string") + self.position.advance(result) + return result + + def read_regex(self, regex: Pattern[str]) -> Sequence[str]: + match = regex.match(self.string, self.position.chars) + if match is None: + raise Error("read_regex: Pattern not found") + self.position.advance(self.string[match.start():match.end()]) + return match.groups() + + +def decode_escapes(regex: Pattern[str], string: str) -> str: + def decode_match(match: Match[str]) -> str: + return codecs.decode(match.group(0), 'unicode-escape') # type: ignore + + return regex.sub(decode_match, string) + + +def parse_key(reader: Reader) -> Optional[str]: + char = reader.peek(1) + if char == "#": + return None + elif char == "'": + (key,) = reader.read_regex(_single_quoted_key) + else: + (key,) = reader.read_regex(_unquoted_key) + return key + + +def parse_unquoted_value(reader: Reader) -> str: + (part,) = reader.read_regex(_unquoted_value) + return re.sub(r"\s+#.*", "", part).rstrip() + + +def parse_value(reader: Reader) -> str: + char = reader.peek(1) + if char == u"'": + (value,) = reader.read_regex(_single_quoted_value) + return decode_escapes(_single_quote_escapes, value) + elif char == u'"': + (value,) = reader.read_regex(_double_quoted_value) + return decode_escapes(_double_quote_escapes, value) + elif char in (u"", u"\n", u"\r"): + return u"" + else: + return parse_unquoted_value(reader) + + +def parse_binding(reader: Reader) -> Binding: + reader.set_mark() + try: + reader.read_regex(_multiline_whitespace) + if not reader.has_next(): + return Binding( + key=None, + value=None, + original=reader.get_marked(), + error=False, + ) + reader.read_regex(_export) + key = parse_key(reader) + reader.read_regex(_whitespace) + if reader.peek(1) == "=": + reader.read_regex(_equal_sign) + value: Optional[str] = parse_value(reader) + else: + value = None + reader.read_regex(_comment) + reader.read_regex(_end_of_line) + return Binding( + key=key, + value=value, + original=reader.get_marked(), + error=False, + ) + except Error: + reader.read_regex(_rest_of_line) + return Binding( + key=None, + value=None, + original=reader.get_marked(), + error=True, + ) + + +def parse_stream(stream: IO[str]) -> Iterator[Binding]: + reader = Reader(stream) + while reader.has_next(): + yield parse_binding(reader) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/py.typed b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/py.typed new file mode 100644 index 000000000..7632ecf77 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/variables.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/variables.py new file mode 100644 index 000000000..667f2f26f --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/variables.py @@ -0,0 +1,86 @@ +import re +from abc import ABCMeta, abstractmethod +from typing import Iterator, Mapping, Optional, Pattern + +_posix_variable: Pattern[str] = re.compile( + r""" + \$\{ + (?P[^\}:]*) + (?::- + (?P[^\}]*) + )? + \} + """, + re.VERBOSE, +) + + +class Atom(metaclass=ABCMeta): + def __ne__(self, other: object) -> bool: + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + return not result + + @abstractmethod + def resolve(self, env: Mapping[str, Optional[str]]) -> str: ... + + +class Literal(Atom): + def __init__(self, value: str) -> None: + self.value = value + + def __repr__(self) -> str: + return f"Literal(value={self.value})" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.value == other.value + + def __hash__(self) -> int: + return hash((self.__class__, self.value)) + + def resolve(self, env: Mapping[str, Optional[str]]) -> str: + return self.value + + +class Variable(Atom): + def __init__(self, name: str, default: Optional[str]) -> None: + self.name = name + self.default = default + + def __repr__(self) -> str: + return f"Variable(name={self.name}, default={self.default})" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return (self.name, self.default) == (other.name, other.default) + + def __hash__(self) -> int: + return hash((self.__class__, self.name, self.default)) + + def resolve(self, env: Mapping[str, Optional[str]]) -> str: + default = self.default if self.default is not None else "" + result = env.get(self.name, default) + return result if result is not None else "" + + +def parse_variables(value: str) -> Iterator[Atom]: + cursor = 0 + + for match in _posix_variable.finditer(value): + (start, end) = match.span() + name = match["name"] + default = match["default"] + + if start > cursor: + yield Literal(value=value[cursor:start]) + + yield Variable(name=name, default=default) + cursor = end + + length = len(value) + if cursor < length: + yield Literal(value=value[cursor:length]) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/version.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/version.py new file mode 100644 index 000000000..5c4105cd3 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/dotenv/version.py @@ -0,0 +1 @@ +__version__ = "1.0.1" diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/INSTALLER b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/LICENSE.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/LICENSE.txt new file mode 100644 index 000000000..9d227a0cc --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/METADATA b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/METADATA new file mode 100644 index 000000000..5a0210724 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/METADATA @@ -0,0 +1,101 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 3.0.3 +Summary: A simple framework for building complex web applications. +Maintainer-email: Pallets +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Classifier: Typing :: Typed +Requires-Dist: Werkzeug>=3.0.0 +Requires-Dist: Jinja2>=3.1.2 +Requires-Dist: itsdangerous>=2.1.2 +Requires-Dist: click>=8.1.3 +Requires-Dist: blinker>=1.6.2 +Requires-Dist: importlib-metadata>=3.6.0; python_version < '3.10' +Requires-Dist: asgiref>=3.2 ; extra == "async" +Requires-Dist: python-dotenv ; extra == "dotenv" +Project-URL: Changes, https://flask.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://flask.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/flask/ +Provides-Extra: async +Provides-Extra: dotenv + +# Flask + +Flask is a lightweight [WSGI][] web application framework. It is designed +to make getting started quick and easy, with the ability to scale up to +complex applications. It began as a simple wrapper around [Werkzeug][] +and [Jinja][], and has become one of the most popular Python web +application frameworks. + +Flask offers suggestions, but doesn't enforce any dependencies or +project layout. It is up to the developer to choose the tools and +libraries they want to use. There are many extensions provided by the +community that make adding new functionality easy. + +[WSGI]: https://wsgi.readthedocs.io/ +[Werkzeug]: https://werkzeug.palletsprojects.com/ +[Jinja]: https://jinja.palletsprojects.com/ + + +## Installing + +Install and update from [PyPI][] using an installer such as [pip][]: + +``` +$ pip install -U Flask +``` + +[PyPI]: https://pypi.org/project/Flask/ +[pip]: https://pip.pypa.io/en/stable/getting-started/ + + +## A Simple Example + +```python +# save this as app.py +from flask import Flask + +app = Flask(__name__) + +@app.route("/") +def hello(): + return "Hello, World!" +``` + +``` +$ flask run + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) +``` + + +## Contributing + +For guidance on setting up a development environment and how to make a +contribution to Flask, see the [contributing guidelines][]. + +[contributing guidelines]: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst + + +## Donate + +The Pallets organization develops and supports Flask and the libraries +it uses. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/RECORD b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/RECORD new file mode 100644 index 000000000..420ba2ce6 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/RECORD @@ -0,0 +1,58 @@ +../../Scripts/flask.exe,sha256=UY0oxul7WUTRXwmeJdPWjhftlr5P5CifJtkrx8A0Vbg,108458 +flask-3.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +flask-3.0.3.dist-info/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +flask-3.0.3.dist-info/METADATA,sha256=exPahy4aahjV-mYqd9qb5HNP8haB_IxTuaotoSvCtag,3177 +flask-3.0.3.dist-info/RECORD,, +flask-3.0.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask-3.0.3.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +flask-3.0.3.dist-info/entry_points.txt,sha256=bBP7hTOS5fz9zLtC7sPofBZAlMkEvBxu7KqS6l5lvc4,40 +flask/__init__.py,sha256=6xMqdVA0FIQ2U1KVaGX3lzNCdXPzoHPaa0hvQCNcfSk,2625 +flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +flask/__pycache__/__init__.cpython-312.pyc,, +flask/__pycache__/__main__.cpython-312.pyc,, +flask/__pycache__/app.cpython-312.pyc,, +flask/__pycache__/blueprints.cpython-312.pyc,, +flask/__pycache__/cli.cpython-312.pyc,, +flask/__pycache__/config.cpython-312.pyc,, +flask/__pycache__/ctx.cpython-312.pyc,, +flask/__pycache__/debughelpers.cpython-312.pyc,, +flask/__pycache__/globals.cpython-312.pyc,, +flask/__pycache__/helpers.cpython-312.pyc,, +flask/__pycache__/logging.cpython-312.pyc,, +flask/__pycache__/sessions.cpython-312.pyc,, +flask/__pycache__/signals.cpython-312.pyc,, +flask/__pycache__/templating.cpython-312.pyc,, +flask/__pycache__/testing.cpython-312.pyc,, +flask/__pycache__/typing.cpython-312.pyc,, +flask/__pycache__/views.cpython-312.pyc,, +flask/__pycache__/wrappers.cpython-312.pyc,, +flask/app.py,sha256=7-lh6cIj27riTE1Q18Ok1p5nOZ8qYiMux4Btc6o6mNc,60143 +flask/blueprints.py,sha256=7INXPwTkUxfOQXOOv1yu52NpHPmPGI5fMTMFZ-BG9yY,4430 +flask/cli.py,sha256=OOaf_Efqih1i2in58j-5ZZZmQnPpaSfiUFbEjlL9bzw,35825 +flask/config.py,sha256=bLzLVAj-cq-Xotu9erqOFte0xSFaVXyfz0AkP4GbwmY,13312 +flask/ctx.py,sha256=4atDhJJ_cpV1VMq4qsfU4E_61M1oN93jlS2H9gjrl58,15120 +flask/debughelpers.py,sha256=PGIDhStW_efRjpaa3zHIpo-htStJOR41Ip3OJWPYBwo,6080 +flask/globals.py,sha256=XdQZmStBmPIs8t93tjx6pO7Bm3gobAaONWkFcUHaGas,1713 +flask/helpers.py,sha256=tYrcQ_73GuSZVEgwFr-eMmV69UriFQDBmt8wZJIAqvg,23084 +flask/json/__init__.py,sha256=hLNR898paqoefdeAhraa5wyJy-bmRB2k2dV4EgVy2Z8,5602 +flask/json/__pycache__/__init__.cpython-312.pyc,, +flask/json/__pycache__/provider.cpython-312.pyc,, +flask/json/__pycache__/tag.cpython-312.pyc,, +flask/json/provider.py,sha256=q6iB83lSiopy80DZPrU-9mGcWwrD0mvLjiv9fHrRZgc,7646 +flask/json/tag.py,sha256=DhaNwuIOhdt2R74oOC9Y4Z8ZprxFYiRb5dUP5byyINw,9281 +flask/logging.py,sha256=8sM3WMTubi1cBb2c_lPkWpN0J8dMAqrgKRYLLi1dCVI,2377 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/sansio/README.md,sha256=-0X1tECnilmz1cogx-YhNw5d7guK7GKrq_DEV2OzlU0,228 +flask/sansio/__pycache__/app.cpython-312.pyc,, +flask/sansio/__pycache__/blueprints.cpython-312.pyc,, +flask/sansio/__pycache__/scaffold.cpython-312.pyc,, +flask/sansio/app.py,sha256=YG5Gf7JVf1c0yccWDZ86q5VSfJUidOVp27HFxFNxC7U,38053 +flask/sansio/blueprints.py,sha256=Tqe-7EkZ-tbWchm8iDoCfD848f0_3nLv6NNjeIPvHwM,24637 +flask/sansio/scaffold.py,sha256=WLV9TRQMMhGlXz-1OKtQ3lv6mtIBQZxdW2HezYrGxoI,30633 +flask/sessions.py,sha256=RU4lzm9MQW9CtH8rVLRTDm8USMJyT4LbvYe7sxM2__k,14807 +flask/signals.py,sha256=V7lMUww7CqgJ2ThUBn1PiatZtQanOyt7OZpu2GZI-34,750 +flask/templating.py,sha256=2TcXLT85Asflm2W9WOSFxKCmYn5e49w_Jkg9-NaaJWo,7537 +flask/testing.py,sha256=3BFXb3bP7R5r-XLBuobhczbxDu8-1LWRzYuhbr-lwaE,10163 +flask/typing.py,sha256=ZavK-wV28Yv8CQB7u73qZp_jLalpbWdrXS37QR1ftN0,3190 +flask/views.py,sha256=B66bTvYBBcHMYk4dA1ScZD0oTRTBl0I5smp1lRm9riI,6939 +flask/wrappers.py,sha256=m1j5tIJxIu8_sPPgTAB_G4TTh52Q-HoDuw_qHV5J59g,5831 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/REQUESTED b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/REQUESTED new file mode 100644 index 000000000..e69de29bb diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/WHEEL b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/WHEEL new file mode 100644 index 000000000..3b5e64b5e --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/entry_points.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/entry_points.txt new file mode 100644 index 000000000..eec6733e5 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask-3.0.3.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +flask=flask.cli:main + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/__init__.py new file mode 100644 index 000000000..e86eb43ee --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/__init__.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import typing as t + +from . import json as json +from .app import Flask as Flask +from .blueprints import Blueprint as Blueprint +from .config import Config as Config +from .ctx import after_this_request as after_this_request +from .ctx import copy_current_request_context as copy_current_request_context +from .ctx import has_app_context as has_app_context +from .ctx import has_request_context as has_request_context +from .globals import current_app as current_app +from .globals import g as g +from .globals import request as request +from .globals import session as session +from .helpers import abort as abort +from .helpers import flash as flash +from .helpers import get_flashed_messages as get_flashed_messages +from .helpers import get_template_attribute as get_template_attribute +from .helpers import make_response as make_response +from .helpers import redirect as redirect +from .helpers import send_file as send_file +from .helpers import send_from_directory as send_from_directory +from .helpers import stream_with_context as stream_with_context +from .helpers import url_for as url_for +from .json import jsonify as jsonify +from .signals import appcontext_popped as appcontext_popped +from .signals import appcontext_pushed as appcontext_pushed +from .signals import appcontext_tearing_down as appcontext_tearing_down +from .signals import before_render_template as before_render_template +from .signals import got_request_exception as got_request_exception +from .signals import message_flashed as message_flashed +from .signals import request_finished as request_finished +from .signals import request_started as request_started +from .signals import request_tearing_down as request_tearing_down +from .signals import template_rendered as template_rendered +from .templating import render_template as render_template +from .templating import render_template_string as render_template_string +from .templating import stream_template as stream_template +from .templating import stream_template_string as stream_template_string +from .wrappers import Request as Request +from .wrappers import Response as Response + + +def __getattr__(name: str) -> t.Any: + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " Flask 3.1. Use feature detection or" + " 'importlib.metadata.version(\"flask\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("flask") + + raise AttributeError(name) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/__main__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/__main__.py new file mode 100644 index 000000000..4e28416e1 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/__main__.py @@ -0,0 +1,3 @@ +from .cli import main + +main() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/app.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/app.py new file mode 100644 index 000000000..7622b5e83 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/app.py @@ -0,0 +1,1498 @@ +from __future__ import annotations + +import collections.abc as cabc +import os +import sys +import typing as t +import weakref +from datetime import timedelta +from inspect import iscoroutinefunction +from itertools import chain +from types import TracebackType +from urllib.parse import quote as _url_quote + +import click +from werkzeug.datastructures import Headers +from werkzeug.datastructures import ImmutableDict +from werkzeug.exceptions import BadRequestKeyError +from werkzeug.exceptions import HTTPException +from werkzeug.exceptions import InternalServerError +from werkzeug.routing import BuildError +from werkzeug.routing import MapAdapter +from werkzeug.routing import RequestRedirect +from werkzeug.routing import RoutingException +from werkzeug.routing import Rule +from werkzeug.serving import is_running_from_reloader +from werkzeug.wrappers import Response as BaseResponse + +from . import cli +from . import typing as ft +from .ctx import AppContext +from .ctx import RequestContext +from .globals import _cv_app +from .globals import _cv_request +from .globals import current_app +from .globals import g +from .globals import request +from .globals import request_ctx +from .globals import session +from .helpers import get_debug_flag +from .helpers import get_flashed_messages +from .helpers import get_load_dotenv +from .helpers import send_from_directory +from .sansio.app import App +from .sansio.scaffold import _sentinel +from .sessions import SecureCookieSessionInterface +from .sessions import SessionInterface +from .signals import appcontext_tearing_down +from .signals import got_request_exception +from .signals import request_finished +from .signals import request_started +from .signals import request_tearing_down +from .templating import Environment +from .wrappers import Request +from .wrappers import Response + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIEnvironment + + from .testing import FlaskClient + from .testing import FlaskCliRunner + +T_shell_context_processor = t.TypeVar( + "T_shell_context_processor", bound=ft.ShellContextProcessorCallable +) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) +T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) +T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) + + +def _make_timedelta(value: timedelta | int | None) -> timedelta | None: + if value is None or isinstance(value, timedelta): + return value + + return timedelta(seconds=value) + + +class Flask(App): + """The flask object implements a WSGI application and acts as the central + object. It is passed the name of the module or package of the + application. Once it is created it will act as a central registry for + the view functions, the URL rules, template configuration and much more. + + The name of the package is used to resolve resources from inside the + package or the folder the module is contained in depending on if the + package parameter resolves to an actual python package (a folder with + an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). + + For more information about resource loading, see :func:`open_resource`. + + Usually you create a :class:`Flask` instance in your main module or + in the :file:`__init__.py` file of your package like this:: + + from flask import Flask + app = Flask(__name__) + + .. admonition:: About the First Parameter + + The idea of the first parameter is to give Flask an idea of what + belongs to your application. This name is used to find resources + on the filesystem, can be used by extensions to improve debugging + information and a lot more. + + So it's important what you provide there. If you are using a single + module, `__name__` is always the correct value. If you however are + using a package, it's usually recommended to hardcode the name of + your package there. + + For example if your application is defined in :file:`yourapplication/app.py` + you should create it with one of the two versions below:: + + app = Flask('yourapplication') + app = Flask(__name__.split('.')[0]) + + Why is that? The application will work even with `__name__`, thanks + to how resources are looked up. However it will make debugging more + painful. Certain extensions can make assumptions based on the + import name of your application. For example the Flask-SQLAlchemy + extension will look for the code in your application that triggered + an SQL query in debug mode. If the import name is not properly set + up, that debugging information is lost. (For example it would only + pick up SQL queries in `yourapplication.app` and not + `yourapplication.views.frontend`) + + .. versionadded:: 0.7 + The `static_url_path`, `static_folder`, and `template_folder` + parameters were added. + + .. versionadded:: 0.8 + The `instance_path` and `instance_relative_config` parameters were + added. + + .. versionadded:: 0.11 + The `root_path` parameter was added. + + .. versionadded:: 1.0 + The ``host_matching`` and ``static_host`` parameters were added. + + .. versionadded:: 1.0 + The ``subdomain_matching`` parameter was added. Subdomain + matching needs to be enabled manually now. Setting + :data:`SERVER_NAME` does not implicitly enable it. + + :param import_name: the name of the application package + :param static_url_path: can be used to specify a different path for the + static files on the web. Defaults to the name + of the `static_folder` folder. + :param static_folder: The folder with static files that is served at + ``static_url_path``. Relative to the application ``root_path`` + or an absolute path. Defaults to ``'static'``. + :param static_host: the host to use when adding the static route. + Defaults to None. Required when using ``host_matching=True`` + with a ``static_folder`` configured. + :param host_matching: set ``url_map.host_matching`` attribute. + Defaults to False. + :param subdomain_matching: consider the subdomain relative to + :data:`SERVER_NAME` when matching routes. Defaults to False. + :param template_folder: the folder that contains the templates that should + be used by the application. Defaults to + ``'templates'`` folder in the root path of the + application. + :param instance_path: An alternative instance path for the application. + By default the folder ``'instance'`` next to the + package or module is assumed to be the instance + path. + :param instance_relative_config: if set to ``True`` relative filenames + for loading the config are assumed to + be relative to the instance path instead + of the application root. + :param root_path: The path to the root of the application files. + This should only be set manually when it can't be detected + automatically, such as for namespace packages. + """ + + default_config = ImmutableDict( + { + "DEBUG": None, + "TESTING": False, + "PROPAGATE_EXCEPTIONS": None, + "SECRET_KEY": None, + "PERMANENT_SESSION_LIFETIME": timedelta(days=31), + "USE_X_SENDFILE": False, + "SERVER_NAME": None, + "APPLICATION_ROOT": "/", + "SESSION_COOKIE_NAME": "session", + "SESSION_COOKIE_DOMAIN": None, + "SESSION_COOKIE_PATH": None, + "SESSION_COOKIE_HTTPONLY": True, + "SESSION_COOKIE_SECURE": False, + "SESSION_COOKIE_SAMESITE": None, + "SESSION_REFRESH_EACH_REQUEST": True, + "MAX_CONTENT_LENGTH": None, + "SEND_FILE_MAX_AGE_DEFAULT": None, + "TRAP_BAD_REQUEST_ERRORS": None, + "TRAP_HTTP_EXCEPTIONS": False, + "EXPLAIN_TEMPLATE_LOADING": False, + "PREFERRED_URL_SCHEME": "http", + "TEMPLATES_AUTO_RELOAD": None, + "MAX_COOKIE_SIZE": 4093, + } + ) + + #: The class that is used for request objects. See :class:`~flask.Request` + #: for more information. + request_class: type[Request] = Request + + #: The class that is used for response objects. See + #: :class:`~flask.Response` for more information. + response_class: type[Response] = Response + + #: the session interface to use. By default an instance of + #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. + #: + #: .. versionadded:: 0.8 + session_interface: SessionInterface = SecureCookieSessionInterface() + + def __init__( + self, + import_name: str, + static_url_path: str | None = None, + static_folder: str | os.PathLike[str] | None = "static", + static_host: str | None = None, + host_matching: bool = False, + subdomain_matching: bool = False, + template_folder: str | os.PathLike[str] | None = "templates", + instance_path: str | None = None, + instance_relative_config: bool = False, + root_path: str | None = None, + ): + super().__init__( + import_name=import_name, + static_url_path=static_url_path, + static_folder=static_folder, + static_host=static_host, + host_matching=host_matching, + subdomain_matching=subdomain_matching, + template_folder=template_folder, + instance_path=instance_path, + instance_relative_config=instance_relative_config, + root_path=root_path, + ) + + #: The Click command group for registering CLI commands for this + #: object. The commands are available from the ``flask`` command + #: once the application has been discovered and blueprints have + #: been registered. + self.cli = cli.AppGroup() + + # Set the name of the Click group in case someone wants to add + # the app's commands to another CLI tool. + self.cli.name = self.name + + # Add a static route using the provided static_url_path, static_host, + # and static_folder if there is a configured static_folder. + # Note we do this without checking if static_folder exists. + # For one, it might be created while the server is running (e.g. during + # development). Also, Google App Engine stores static files somewhere + if self.has_static_folder: + assert ( + bool(static_host) == host_matching + ), "Invalid static_host/host_matching combination" + # Use a weakref to avoid creating a reference cycle between the app + # and the view function (see #3761). + self_ref = weakref.ref(self) + self.add_url_rule( + f"{self.static_url_path}/", + endpoint="static", + host=static_host, + view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950 + ) + + def get_send_file_max_age(self, filename: str | None) -> int | None: + """Used by :func:`send_file` to determine the ``max_age`` cache + value for a given file path if it wasn't passed. + + By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from + the configuration of :data:`~flask.current_app`. This defaults + to ``None``, which tells the browser to use conditional requests + instead of a timed cache, which is usually preferable. + + Note this is a duplicate of the same method in the Flask + class. + + .. versionchanged:: 2.0 + The default configuration is ``None`` instead of 12 hours. + + .. versionadded:: 0.9 + """ + value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] + + if value is None: + return None + + if isinstance(value, timedelta): + return int(value.total_seconds()) + + return value # type: ignore[no-any-return] + + def send_static_file(self, filename: str) -> Response: + """The view function used to serve files from + :attr:`static_folder`. A route is automatically registered for + this view at :attr:`static_url_path` if :attr:`static_folder` is + set. + + Note this is a duplicate of the same method in the Flask + class. + + .. versionadded:: 0.5 + + """ + if not self.has_static_folder: + raise RuntimeError("'static_folder' must be set to serve static_files.") + + # send_file only knows to call get_send_file_max_age on the app, + # call it here so it works for blueprints too. + max_age = self.get_send_file_max_age(filename) + return send_from_directory( + t.cast(str, self.static_folder), filename, max_age=max_age + ) + + def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: + """Open a resource file relative to :attr:`root_path` for + reading. + + For example, if the file ``schema.sql`` is next to the file + ``app.py`` where the ``Flask`` app is defined, it can be opened + with: + + .. code-block:: python + + with app.open_resource("schema.sql") as f: + conn.executescript(f.read()) + + :param resource: Path to the resource relative to + :attr:`root_path`. + :param mode: Open the file in this mode. Only reading is + supported, valid values are "r" (or "rt") and "rb". + + Note this is a duplicate of the same method in the Flask + class. + + """ + if mode not in {"r", "rt", "rb"}: + raise ValueError("Resources can only be opened for reading.") + + return open(os.path.join(self.root_path, resource), mode) + + def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: + """Opens a resource from the application's instance folder + (:attr:`instance_path`). Otherwise works like + :meth:`open_resource`. Instance resources can also be opened for + writing. + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. + :param mode: resource file opening mode, default is 'rb'. + """ + return open(os.path.join(self.instance_path, resource), mode) + + def create_jinja_environment(self) -> Environment: + """Create the Jinja environment based on :attr:`jinja_options` + and the various Jinja-related methods of the app. Changing + :attr:`jinja_options` after this will have no effect. Also adds + Flask-related globals and filters to the environment. + + .. versionchanged:: 0.11 + ``Environment.auto_reload`` set in accordance with + ``TEMPLATES_AUTO_RELOAD`` configuration option. + + .. versionadded:: 0.5 + """ + options = dict(self.jinja_options) + + if "autoescape" not in options: + options["autoescape"] = self.select_jinja_autoescape + + if "auto_reload" not in options: + auto_reload = self.config["TEMPLATES_AUTO_RELOAD"] + + if auto_reload is None: + auto_reload = self.debug + + options["auto_reload"] = auto_reload + + rv = self.jinja_environment(self, **options) + rv.globals.update( + url_for=self.url_for, + get_flashed_messages=get_flashed_messages, + config=self.config, + # request, session and g are normally added with the + # context processor for efficiency reasons but for imported + # templates we also want the proxies in there. + request=request, + session=session, + g=g, + ) + rv.policies["json.dumps_function"] = self.json.dumps + return rv + + def create_url_adapter(self, request: Request | None) -> MapAdapter | None: + """Creates a URL adapter for the given request. The URL adapter + is created at a point where the request context is not yet set + up so the request is passed explicitly. + + .. versionadded:: 0.6 + + .. versionchanged:: 0.9 + This can now also be called without a request object when the + URL adapter is created for the application context. + + .. versionchanged:: 1.0 + :data:`SERVER_NAME` no longer implicitly enables subdomain + matching. Use :attr:`subdomain_matching` instead. + """ + if request is not None: + # If subdomain matching is disabled (the default), use the + # default subdomain in all cases. This should be the default + # in Werkzeug but it currently does not have that feature. + if not self.subdomain_matching: + subdomain = self.url_map.default_subdomain or None + else: + subdomain = None + + return self.url_map.bind_to_environ( + request.environ, + server_name=self.config["SERVER_NAME"], + subdomain=subdomain, + ) + # We need at the very least the server name to be set for this + # to work. + if self.config["SERVER_NAME"] is not None: + return self.url_map.bind( + self.config["SERVER_NAME"], + script_name=self.config["APPLICATION_ROOT"], + url_scheme=self.config["PREFERRED_URL_SCHEME"], + ) + + return None + + def raise_routing_exception(self, request: Request) -> t.NoReturn: + """Intercept routing exceptions and possibly do something else. + + In debug mode, intercept a routing redirect and replace it with + an error if the body will be discarded. + + With modern Werkzeug this shouldn't occur, since it now uses a + 308 status which tells the browser to resend the method and + body. + + .. versionchanged:: 2.1 + Don't intercept 307 and 308 redirects. + + :meta private: + :internal: + """ + if ( + not self.debug + or not isinstance(request.routing_exception, RequestRedirect) + or request.routing_exception.code in {307, 308} + or request.method in {"GET", "HEAD", "OPTIONS"} + ): + raise request.routing_exception # type: ignore[misc] + + from .debughelpers import FormDataRoutingRedirect + + raise FormDataRoutingRedirect(request) + + def update_template_context(self, context: dict[str, t.Any]) -> None: + """Update the template context with some commonly used variables. + This injects request, session, config and g into the template + context as well as everything template context processors want + to inject. Note that the as of Flask 0.6, the original values + in the context will not be overridden if a context processor + decides to return a value with the same key. + + :param context: the context as a dictionary that is updated in place + to add extra variables. + """ + names: t.Iterable[str | None] = (None,) + + # A template may be rendered outside a request context. + if request: + names = chain(names, reversed(request.blueprints)) + + # The values passed to render_template take precedence. Keep a + # copy to re-apply after all context functions. + orig_ctx = context.copy() + + for name in names: + if name in self.template_context_processors: + for func in self.template_context_processors[name]: + context.update(self.ensure_sync(func)()) + + context.update(orig_ctx) + + def make_shell_context(self) -> dict[str, t.Any]: + """Returns the shell context for an interactive shell for this + application. This runs all the registered shell context + processors. + + .. versionadded:: 0.11 + """ + rv = {"app": self, "g": g} + for processor in self.shell_context_processors: + rv.update(processor()) + return rv + + def run( + self, + host: str | None = None, + port: int | None = None, + debug: bool | None = None, + load_dotenv: bool = True, + **options: t.Any, + ) -> None: + """Runs the application on a local development server. + + Do not use ``run()`` in a production setting. It is not intended to + meet security and performance requirements for a production server. + Instead, see :doc:`/deploying/index` for WSGI server recommendations. + + If the :attr:`debug` flag is set the server will automatically reload + for code changes and show a debugger in case an exception happened. + + If you want to run the application in debug mode, but disable the + code execution on the interactive debugger, you can pass + ``use_evalex=False`` as parameter. This will keep the debugger's + traceback screen active, but disable code execution. + + It is not recommended to use this function for development with + automatic reloading as this is badly supported. Instead you should + be using the :command:`flask` command line script's ``run`` support. + + .. admonition:: Keep in Mind + + Flask will suppress any server error with a generic error page + unless it is in debug mode. As such to enable just the + interactive debugger without the code reloading, you have to + invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``. + Setting ``use_debugger`` to ``True`` without being in debug mode + won't catch any exceptions because there won't be any to + catch. + + :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to + have the server available externally as well. Defaults to + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable + if present. + :param port: the port of the webserver. Defaults to ``5000`` or the + port defined in the ``SERVER_NAME`` config variable if present. + :param debug: if given, enable or disable debug mode. See + :attr:`debug`. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param options: the options to be forwarded to the underlying Werkzeug + server. See :func:`werkzeug.serving.run_simple` for more + information. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment + variables from :file:`.env` and :file:`.flaskenv` files. + + The :envvar:`FLASK_DEBUG` environment variable will override :attr:`debug`. + + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. + """ + # Ignore this call so that it doesn't start another server if + # the 'flask run' command is used. + if os.environ.get("FLASK_RUN_FROM_CLI") == "true": + if not is_running_from_reloader(): + click.secho( + " * Ignoring a call to 'app.run()' that would block" + " the current 'flask' CLI command.\n" + " Only call 'app.run()' in an 'if __name__ ==" + ' "__main__"\' guard.', + fg="red", + ) + + return + + if get_load_dotenv(load_dotenv): + cli.load_dotenv() + + # if set, env var overrides existing value + if "FLASK_DEBUG" in os.environ: + self.debug = get_debug_flag() + + # debug passed to method overrides all other sources + if debug is not None: + self.debug = bool(debug) + + server_name = self.config.get("SERVER_NAME") + sn_host = sn_port = None + + if server_name: + sn_host, _, sn_port = server_name.partition(":") + + if not host: + if sn_host: + host = sn_host + else: + host = "127.0.0.1" + + if port or port == 0: + port = int(port) + elif sn_port: + port = int(sn_port) + else: + port = 5000 + + options.setdefault("use_reloader", self.debug) + options.setdefault("use_debugger", self.debug) + options.setdefault("threaded", True) + + cli.show_server_banner(self.debug, self.name) + + from werkzeug.serving import run_simple + + try: + run_simple(t.cast(str, host), port, self, **options) + finally: + # reset the first request information if the development server + # reset normally. This makes it possible to restart the server + # without reloader and that stuff from an interactive shell. + self._got_first_request = False + + def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> FlaskClient: + """Creates a test client for this application. For information + about unit testing head over to :doc:`/testing`. + + Note that if you are testing for assertions or exceptions in your + application code, you must set ``app.testing = True`` in order for the + exceptions to propagate to the test client. Otherwise, the exception + will be handled by the application (not visible to the test client) and + the only indication of an AssertionError or other exception will be a + 500 status code response to the test client. See the :attr:`testing` + attribute. For example:: + + app.testing = True + client = app.test_client() + + The test client can be used in a ``with`` block to defer the closing down + of the context until the end of the ``with`` block. This is useful if + you want to access the context locals for testing:: + + with app.test_client() as c: + rv = c.get('/?vodka=42') + assert request.args['vodka'] == '42' + + Additionally, you may pass optional keyword arguments that will then + be passed to the application's :attr:`test_client_class` constructor. + For example:: + + from flask.testing import FlaskClient + + class CustomClient(FlaskClient): + def __init__(self, *args, **kwargs): + self._authentication = kwargs.pop("authentication") + super(CustomClient,self).__init__( *args, **kwargs) + + app.test_client_class = CustomClient + client = app.test_client(authentication='Basic ....') + + See :class:`~flask.testing.FlaskClient` for more information. + + .. versionchanged:: 0.4 + added support for ``with`` block usage for the client. + + .. versionadded:: 0.7 + The `use_cookies` parameter was added as well as the ability + to override the client to be used by setting the + :attr:`test_client_class` attribute. + + .. versionchanged:: 0.11 + Added `**kwargs` to support passing additional keyword arguments to + the constructor of :attr:`test_client_class`. + """ + cls = self.test_client_class + if cls is None: + from .testing import FlaskClient as cls + return cls( # type: ignore + self, self.response_class, use_cookies=use_cookies, **kwargs + ) + + def test_cli_runner(self, **kwargs: t.Any) -> FlaskCliRunner: + """Create a CLI runner for testing CLI commands. + See :ref:`testing-cli`. + + Returns an instance of :attr:`test_cli_runner_class`, by default + :class:`~flask.testing.FlaskCliRunner`. The Flask app object is + passed as the first argument. + + .. versionadded:: 1.0 + """ + cls = self.test_cli_runner_class + + if cls is None: + from .testing import FlaskCliRunner as cls + + return cls(self, **kwargs) # type: ignore + + def handle_http_exception( + self, e: HTTPException + ) -> HTTPException | ft.ResponseReturnValue: + """Handles an HTTP exception. By default this will invoke the + registered error handlers and fall back to returning the + exception as response. + + .. versionchanged:: 1.0.3 + ``RoutingException``, used internally for actions such as + slash redirects during routing, is not passed to error + handlers. + + .. versionchanged:: 1.0 + Exceptions are looked up by code *and* by MRO, so + ``HTTPException`` subclasses can be handled with a catch-all + handler for the base ``HTTPException``. + + .. versionadded:: 0.3 + """ + # Proxy exceptions don't have error codes. We want to always return + # those unchanged as errors + if e.code is None: + return e + + # RoutingExceptions are used internally to trigger routing + # actions, such as slash redirects raising RequestRedirect. They + # are not raised or handled in user code. + if isinstance(e, RoutingException): + return e + + handler = self._find_error_handler(e, request.blueprints) + if handler is None: + return e + return self.ensure_sync(handler)(e) # type: ignore[no-any-return] + + def handle_user_exception( + self, e: Exception + ) -> HTTPException | ft.ResponseReturnValue: + """This method is called whenever an exception occurs that + should be handled. A special case is :class:`~werkzeug + .exceptions.HTTPException` which is forwarded to the + :meth:`handle_http_exception` method. This function will either + return a response value or reraise the exception with the same + traceback. + + .. versionchanged:: 1.0 + Key errors raised from request data like ``form`` show the + bad key in debug mode rather than a generic bad request + message. + + .. versionadded:: 0.7 + """ + if isinstance(e, BadRequestKeyError) and ( + self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"] + ): + e.show_exception = True + + if isinstance(e, HTTPException) and not self.trap_http_exception(e): + return self.handle_http_exception(e) + + handler = self._find_error_handler(e, request.blueprints) + + if handler is None: + raise + + return self.ensure_sync(handler)(e) # type: ignore[no-any-return] + + def handle_exception(self, e: Exception) -> Response: + """Handle an exception that did not have an error handler + associated with it, or that was raised from an error handler. + This always causes a 500 ``InternalServerError``. + + Always sends the :data:`got_request_exception` signal. + + If :data:`PROPAGATE_EXCEPTIONS` is ``True``, such as in debug + mode, the error will be re-raised so that the debugger can + display it. Otherwise, the original exception is logged, and + an :exc:`~werkzeug.exceptions.InternalServerError` is returned. + + If an error handler is registered for ``InternalServerError`` or + ``500``, it will be used. For consistency, the handler will + always receive the ``InternalServerError``. The original + unhandled exception is available as ``e.original_exception``. + + .. versionchanged:: 1.1.0 + Always passes the ``InternalServerError`` instance to the + handler, setting ``original_exception`` to the unhandled + error. + + .. versionchanged:: 1.1.0 + ``after_request`` functions and other finalization is done + even for the default 500 response when there is no handler. + + .. versionadded:: 0.3 + """ + exc_info = sys.exc_info() + got_request_exception.send(self, _async_wrapper=self.ensure_sync, exception=e) + propagate = self.config["PROPAGATE_EXCEPTIONS"] + + if propagate is None: + propagate = self.testing or self.debug + + if propagate: + # Re-raise if called with an active exception, otherwise + # raise the passed in exception. + if exc_info[1] is e: + raise + + raise e + + self.log_exception(exc_info) + server_error: InternalServerError | ft.ResponseReturnValue + server_error = InternalServerError(original_exception=e) + handler = self._find_error_handler(server_error, request.blueprints) + + if handler is not None: + server_error = self.ensure_sync(handler)(server_error) + + return self.finalize_request(server_error, from_error_handler=True) + + def log_exception( + self, + exc_info: (tuple[type, BaseException, TracebackType] | tuple[None, None, None]), + ) -> None: + """Logs an exception. This is called by :meth:`handle_exception` + if debugging is disabled and right before the handler is called. + The default implementation logs the exception as error on the + :attr:`logger`. + + .. versionadded:: 0.8 + """ + self.logger.error( + f"Exception on {request.path} [{request.method}]", exc_info=exc_info + ) + + def dispatch_request(self) -> ft.ResponseReturnValue: + """Does the request dispatching. Matches the URL and returns the + return value of the view or error handler. This does not have to + be a response object. In order to convert the return value to a + proper response object, call :func:`make_response`. + + .. versionchanged:: 0.7 + This no longer does the exception handling, this code was + moved to the new :meth:`full_dispatch_request`. + """ + req = request_ctx.request + if req.routing_exception is not None: + self.raise_routing_exception(req) + rule: Rule = req.url_rule # type: ignore[assignment] + # if we provide automatic options for this URL and the + # request came with the OPTIONS method, reply automatically + if ( + getattr(rule, "provide_automatic_options", False) + and req.method == "OPTIONS" + ): + return self.make_default_options_response() + # otherwise dispatch to the handler for that endpoint + view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment] + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + + def full_dispatch_request(self) -> Response: + """Dispatches the request and on top of that performs request + pre and postprocessing as well as HTTP exception catching and + error handling. + + .. versionadded:: 0.7 + """ + self._got_first_request = True + + try: + request_started.send(self, _async_wrapper=self.ensure_sync) + rv = self.preprocess_request() + if rv is None: + rv = self.dispatch_request() + except Exception as e: + rv = self.handle_user_exception(e) + return self.finalize_request(rv) + + def finalize_request( + self, + rv: ft.ResponseReturnValue | HTTPException, + from_error_handler: bool = False, + ) -> Response: + """Given the return value from a view function this finalizes + the request by converting it into a response and invoking the + postprocessing functions. This is invoked for both normal + request dispatching as well as error handlers. + + Because this means that it might be called as a result of a + failure a special safe mode is available which can be enabled + with the `from_error_handler` flag. If enabled, failures in + response processing will be logged and otherwise ignored. + + :internal: + """ + response = self.make_response(rv) + try: + response = self.process_response(response) + request_finished.send( + self, _async_wrapper=self.ensure_sync, response=response + ) + except Exception: + if not from_error_handler: + raise + self.logger.exception( + "Request finalizing failed with an error while handling an error" + ) + return response + + def make_default_options_response(self) -> Response: + """This method is called to create the default ``OPTIONS`` response. + This can be changed through subclassing to change the default + behavior of ``OPTIONS`` responses. + + .. versionadded:: 0.7 + """ + adapter = request_ctx.url_adapter + methods = adapter.allowed_methods() # type: ignore[union-attr] + rv = self.response_class() + rv.allow.update(methods) + return rv + + def ensure_sync(self, func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Ensure that the function is synchronous for WSGI workers. + Plain ``def`` functions are returned as-is. ``async def`` + functions are wrapped to run and wait for the response. + + Override this method to change how the app runs async views. + + .. versionadded:: 2.0 + """ + if iscoroutinefunction(func): + return self.async_to_sync(func) + + return func + + def async_to_sync( + self, func: t.Callable[..., t.Coroutine[t.Any, t.Any, t.Any]] + ) -> t.Callable[..., t.Any]: + """Return a sync function that will run the coroutine function. + + .. code-block:: python + + result = app.async_to_sync(func)(*args, **kwargs) + + Override this method to change how the app converts async code + to be synchronously callable. + + .. versionadded:: 2.0 + """ + try: + from asgiref.sync import async_to_sync as asgiref_async_to_sync + except ImportError: + raise RuntimeError( + "Install Flask with the 'async' extra in order to use async views." + ) from None + + return asgiref_async_to_sync(func) + + def url_for( + self, + /, + endpoint: str, + *, + _anchor: str | None = None, + _method: str | None = None, + _scheme: str | None = None, + _external: bool | None = None, + **values: t.Any, + ) -> str: + """Generate a URL to the given endpoint with the given values. + + This is called by :func:`flask.url_for`, and can be called + directly as well. + + An *endpoint* is the name of a URL rule, usually added with + :meth:`@app.route() `, and usually the same name as the + view function. A route defined in a :class:`~flask.Blueprint` + will prepend the blueprint's name separated by a ``.`` to the + endpoint. + + In some cases, such as email messages, you want URLs to include + the scheme and domain, like ``https://example.com/hello``. When + not in an active request, URLs will be external by default, but + this requires setting :data:`SERVER_NAME` so Flask knows what + domain to use. :data:`APPLICATION_ROOT` and + :data:`PREFERRED_URL_SCHEME` should also be configured as + needed. This config is only used when not in an active request. + + Functions can be decorated with :meth:`url_defaults` to modify + keyword arguments before the URL is built. + + If building fails for some reason, such as an unknown endpoint + or incorrect values, the app's :meth:`handle_url_build_error` + method is called. If that returns a string, that is returned, + otherwise a :exc:`~werkzeug.routing.BuildError` is raised. + + :param endpoint: The endpoint name associated with the URL to + generate. If this starts with a ``.``, the current blueprint + name (if any) will be used. + :param _anchor: If given, append this as ``#anchor`` to the URL. + :param _method: If given, generate the URL associated with this + method for the endpoint. + :param _scheme: If given, the URL will have this scheme if it + is external. + :param _external: If given, prefer the URL to be internal + (False) or require it to be external (True). External URLs + include the scheme and domain. When not in an active + request, URLs are external by default. + :param values: Values to use for the variable parts of the URL + rule. Unknown keys are appended as query string arguments, + like ``?a=b&c=d``. + + .. versionadded:: 2.2 + Moved from ``flask.url_for``, which calls this method. + """ + req_ctx = _cv_request.get(None) + + if req_ctx is not None: + url_adapter = req_ctx.url_adapter + blueprint_name = req_ctx.request.blueprint + + # If the endpoint starts with "." and the request matches a + # blueprint, the endpoint is relative to the blueprint. + if endpoint[:1] == ".": + if blueprint_name is not None: + endpoint = f"{blueprint_name}{endpoint}" + else: + endpoint = endpoint[1:] + + # When in a request, generate a URL without scheme and + # domain by default, unless a scheme is given. + if _external is None: + _external = _scheme is not None + else: + app_ctx = _cv_app.get(None) + + # If called by helpers.url_for, an app context is active, + # use its url_adapter. Otherwise, app.url_for was called + # directly, build an adapter. + if app_ctx is not None: + url_adapter = app_ctx.url_adapter + else: + url_adapter = self.create_url_adapter(None) + + if url_adapter is None: + raise RuntimeError( + "Unable to build URLs outside an active request" + " without 'SERVER_NAME' configured. Also configure" + " 'APPLICATION_ROOT' and 'PREFERRED_URL_SCHEME' as" + " needed." + ) + + # When outside a request, generate a URL with scheme and + # domain by default. + if _external is None: + _external = True + + # It is an error to set _scheme when _external=False, in order + # to avoid accidental insecure URLs. + if _scheme is not None and not _external: + raise ValueError("When specifying '_scheme', '_external' must be True.") + + self.inject_url_defaults(endpoint, values) + + try: + rv = url_adapter.build( # type: ignore[union-attr] + endpoint, + values, + method=_method, + url_scheme=_scheme, + force_external=_external, + ) + except BuildError as error: + values.update( + _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external + ) + return self.handle_url_build_error(error, endpoint, values) + + if _anchor is not None: + _anchor = _url_quote(_anchor, safe="%!#$&'()*+,/:;=?@") + rv = f"{rv}#{_anchor}" + + return rv + + def make_response(self, rv: ft.ResponseReturnValue) -> Response: + """Convert the return value from a view function to an instance of + :attr:`response_class`. + + :param rv: the return value from the view function. The view function + must return a response. Returning ``None``, or the view ending + without returning, is not allowed. The following types are allowed + for ``view_rv``: + + ``str`` + A response object is created with the string encoded to UTF-8 + as the body. + + ``bytes`` + A response object is created with the bytes as the body. + + ``dict`` + A dictionary that will be jsonify'd before being returned. + + ``list`` + A list that will be jsonify'd before being returned. + + ``generator`` or ``iterator`` + A generator that returns ``str`` or ``bytes`` to be + streamed as the response. + + ``tuple`` + Either ``(body, status, headers)``, ``(body, status)``, or + ``(body, headers)``, where ``body`` is any of the other types + allowed here, ``status`` is a string or an integer, and + ``headers`` is a dictionary or a list of ``(key, value)`` + tuples. If ``body`` is a :attr:`response_class` instance, + ``status`` overwrites the exiting value and ``headers`` are + extended. + + :attr:`response_class` + The object is returned unchanged. + + other :class:`~werkzeug.wrappers.Response` class + The object is coerced to :attr:`response_class`. + + :func:`callable` + The function is called as a WSGI application. The result is + used to create a response object. + + .. versionchanged:: 2.2 + A generator will be converted to a streaming response. + A list will be converted to a JSON response. + + .. versionchanged:: 1.1 + A dict will be converted to a JSON response. + + .. versionchanged:: 0.9 + Previously a tuple was interpreted as the arguments for the + response object. + """ + + status = headers = None + + # unpack tuple returns + if isinstance(rv, tuple): + len_rv = len(rv) + + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv # type: ignore[misc] + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv + else: + rv, status = rv # type: ignore[assignment,misc] + # other sized tuples are not allowed + else: + raise TypeError( + "The view function did not return a valid response tuple." + " The tuple must have the form (body, status, headers)," + " (body, status), or (body, headers)." + ) + + # the body must not be None + if rv is None: + raise TypeError( + f"The view function for {request.endpoint!r} did not" + " return a valid response. The function either returned" + " None or ended without a return statement." + ) + + # make sure the body is an instance of the response class + if not isinstance(rv, self.response_class): + if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, cabc.Iterator): + # let the response class set the status and headers instead of + # waiting to do it manually, so that the class can handle any + # special logic + rv = self.response_class( + rv, + status=status, + headers=headers, # type: ignore[arg-type] + ) + status = headers = None + elif isinstance(rv, (dict, list)): + rv = self.json.response(rv) + elif isinstance(rv, BaseResponse) or callable(rv): + # evaluate a WSGI callable, or coerce a different response + # class to the correct type + try: + rv = self.response_class.force_type( + rv, # type: ignore[arg-type] + request.environ, + ) + except TypeError as e: + raise TypeError( + f"{e}\nThe view function did not return a valid" + " response. The return type must be a string," + " dict, list, tuple with headers or status," + " Response instance, or WSGI callable, but it" + f" was a {type(rv).__name__}." + ).with_traceback(sys.exc_info()[2]) from None + else: + raise TypeError( + "The view function did not return a valid" + " response. The return type must be a string," + " dict, list, tuple with headers or status," + " Response instance, or WSGI callable, but it was a" + f" {type(rv).__name__}." + ) + + rv = t.cast(Response, rv) + # prefer the status if it was provided + if status is not None: + if isinstance(status, (str, bytes, bytearray)): + rv.status = status + else: + rv.status_code = status + + # extend existing headers with provided headers + if headers: + rv.headers.update(headers) # type: ignore[arg-type] + + return rv + + def preprocess_request(self) -> ft.ResponseReturnValue | None: + """Called before the request is dispatched. Calls + :attr:`url_value_preprocessors` registered with the app and the + current blueprint (if any). Then calls :attr:`before_request_funcs` + registered with the app and the blueprint. + + If any :meth:`before_request` handler returns a non-None value, the + value is handled as if it was the return value from the view, and + further request handling is stopped. + """ + names = (None, *reversed(request.blueprints)) + + for name in names: + if name in self.url_value_preprocessors: + for url_func in self.url_value_preprocessors[name]: + url_func(request.endpoint, request.view_args) + + for name in names: + if name in self.before_request_funcs: + for before_func in self.before_request_funcs[name]: + rv = self.ensure_sync(before_func)() + + if rv is not None: + return rv # type: ignore[no-any-return] + + return None + + def process_response(self, response: Response) -> Response: + """Can be overridden in order to modify the response object + before it's sent to the WSGI server. By default this will + call all the :meth:`after_request` decorated functions. + + .. versionchanged:: 0.5 + As of Flask 0.5 the functions registered for after request + execution are called in reverse order of registration. + + :param response: a :attr:`response_class` object. + :return: a new response object or the same, has to be an + instance of :attr:`response_class`. + """ + ctx = request_ctx._get_current_object() # type: ignore[attr-defined] + + for func in ctx._after_request_functions: + response = self.ensure_sync(func)(response) + + for name in chain(request.blueprints, (None,)): + if name in self.after_request_funcs: + for func in reversed(self.after_request_funcs[name]): + response = self.ensure_sync(func)(response) + + if not self.session_interface.is_null_session(ctx.session): + self.session_interface.save_session(self, ctx.session, response) + + return response + + def do_teardown_request( + self, + exc: BaseException | None = _sentinel, # type: ignore[assignment] + ) -> None: + """Called after the request is dispatched and the response is + returned, right before the request context is popped. + + This calls all functions decorated with + :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` + if a blueprint handled the request. Finally, the + :data:`request_tearing_down` signal is sent. + + This is called by + :meth:`RequestContext.pop() `, + which may be delayed during testing to maintain access to + resources. + + :param exc: An unhandled exception raised while dispatching the + request. Detected from the current exception information if + not passed. Passed to each teardown function. + + .. versionchanged:: 0.9 + Added the ``exc`` argument. + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + + for name in chain(request.blueprints, (None,)): + if name in self.teardown_request_funcs: + for func in reversed(self.teardown_request_funcs[name]): + self.ensure_sync(func)(exc) + + request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc) + + def do_teardown_appcontext( + self, + exc: BaseException | None = _sentinel, # type: ignore[assignment] + ) -> None: + """Called right before the application context is popped. + + When handling a request, the application context is popped + after the request context. See :meth:`do_teardown_request`. + + This calls all functions decorated with + :meth:`teardown_appcontext`. Then the + :data:`appcontext_tearing_down` signal is sent. + + This is called by + :meth:`AppContext.pop() `. + + .. versionadded:: 0.9 + """ + if exc is _sentinel: + exc = sys.exc_info()[1] + + for func in reversed(self.teardown_appcontext_funcs): + self.ensure_sync(func)(exc) + + appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc) + + def app_context(self) -> AppContext: + """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` + block to push the context, which will make :data:`current_app` + point at this application. + + An application context is automatically pushed by + :meth:`RequestContext.push() ` + when handling a request, and when running a CLI command. Use + this to manually create a context outside of these situations. + + :: + + with app.app_context(): + init_db() + + See :doc:`/appcontext`. + + .. versionadded:: 0.9 + """ + return AppContext(self) + + def request_context(self, environ: WSGIEnvironment) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` representing a + WSGI environment. Use a ``with`` block to push the context, + which will make :data:`request` point at this request. + + See :doc:`/reqcontext`. + + Typically you should not call this from your own code. A request + context is automatically pushed by the :meth:`wsgi_app` when + handling a request. Use :meth:`test_request_context` to create + an environment and context instead of this method. + + :param environ: a WSGI environment + """ + return RequestContext(self, environ) + + def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext: + """Create a :class:`~flask.ctx.RequestContext` for a WSGI + environment created from the given values. This is mostly useful + during testing, where you may want to run a function that uses + request data without dispatching a full request. + + See :doc:`/reqcontext`. + + Use a ``with`` block to push the context, which will make + :data:`request` point at the request for the created + environment. :: + + with app.test_request_context(...): + generate_report() + + When using the shell, it may be easier to push and pop the + context manually to avoid indentation. :: + + ctx = app.test_request_context(...) + ctx.push() + ... + ctx.pop() + + Takes the same arguments as Werkzeug's + :class:`~werkzeug.test.EnvironBuilder`, with some defaults from + the application. See the linked Werkzeug docs for most of the + available arguments. Flask-specific behavior is listed here. + + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to + :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param data: The request body, either as a string or a dict of + form keys and values. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + from .testing import EnvironBuilder + + builder = EnvironBuilder(self, *args, **kwargs) + + try: + return self.request_context(builder.get_environ()) + finally: + builder.close() + + def wsgi_app( + self, environ: WSGIEnvironment, start_response: StartResponse + ) -> cabc.Iterable[bytes]: + """The actual WSGI application. This is not implemented in + :meth:`__call__` so that middlewares can be applied without + losing a reference to the app object. Instead of doing this:: + + app = MyMiddleware(app) + + It's a better idea to do this instead:: + + app.wsgi_app = MyMiddleware(app.wsgi_app) + + Then you still have the original application object around and + can continue to call methods on it. + + .. versionchanged:: 0.7 + Teardown events for the request and app contexts are called + even if an unhandled error occurs. Other events may not be + called depending on when an error occurs during dispatch. + See :ref:`callbacks-and-errors`. + + :param environ: A WSGI environment. + :param start_response: A callable accepting a status code, + a list of headers, and an optional exception context to + start the response. + """ + ctx = self.request_context(environ) + error: BaseException | None = None + try: + try: + ctx.push() + response = self.full_dispatch_request() + except Exception as e: + error = e + response = self.handle_exception(e) + except: # noqa: B001 + error = sys.exc_info()[1] + raise + return response(environ, start_response) + finally: + if "werkzeug.debug.preserve_context" in environ: + environ["werkzeug.debug.preserve_context"](_cv_app.get()) + environ["werkzeug.debug.preserve_context"](_cv_request.get()) + + if error is not None and self.should_ignore_error(error): + error = None + + ctx.pop(error) + + def __call__( + self, environ: WSGIEnvironment, start_response: StartResponse + ) -> cabc.Iterable[bytes]: + """The WSGI server calls the Flask application object as the + WSGI application. This calls :meth:`wsgi_app`, which can be + wrapped to apply middleware. + """ + return self.wsgi_app(environ, start_response) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/blueprints.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/blueprints.py new file mode 100644 index 000000000..aa9eacf21 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/blueprints.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +import os +import typing as t +from datetime import timedelta + +from .cli import AppGroup +from .globals import current_app +from .helpers import send_from_directory +from .sansio.blueprints import Blueprint as SansioBlueprint +from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa +from .sansio.scaffold import _sentinel + +if t.TYPE_CHECKING: # pragma: no cover + from .wrappers import Response + + +class Blueprint(SansioBlueprint): + def __init__( + self, + name: str, + import_name: str, + static_folder: str | os.PathLike[str] | None = None, + static_url_path: str | None = None, + template_folder: str | os.PathLike[str] | None = None, + url_prefix: str | None = None, + subdomain: str | None = None, + url_defaults: dict[str, t.Any] | None = None, + root_path: str | None = None, + cli_group: str | None = _sentinel, # type: ignore + ) -> None: + super().__init__( + name, + import_name, + static_folder, + static_url_path, + template_folder, + url_prefix, + subdomain, + url_defaults, + root_path, + cli_group, + ) + + #: The Click command group for registering CLI commands for this + #: object. The commands are available from the ``flask`` command + #: once the application has been discovered and blueprints have + #: been registered. + self.cli = AppGroup() + + # Set the name of the Click group in case someone wants to add + # the app's commands to another CLI tool. + self.cli.name = self.name + + def get_send_file_max_age(self, filename: str | None) -> int | None: + """Used by :func:`send_file` to determine the ``max_age`` cache + value for a given file path if it wasn't passed. + + By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from + the configuration of :data:`~flask.current_app`. This defaults + to ``None``, which tells the browser to use conditional requests + instead of a timed cache, which is usually preferable. + + Note this is a duplicate of the same method in the Flask + class. + + .. versionchanged:: 2.0 + The default configuration is ``None`` instead of 12 hours. + + .. versionadded:: 0.9 + """ + value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] + + if value is None: + return None + + if isinstance(value, timedelta): + return int(value.total_seconds()) + + return value # type: ignore[no-any-return] + + def send_static_file(self, filename: str) -> Response: + """The view function used to serve files from + :attr:`static_folder`. A route is automatically registered for + this view at :attr:`static_url_path` if :attr:`static_folder` is + set. + + Note this is a duplicate of the same method in the Flask + class. + + .. versionadded:: 0.5 + + """ + if not self.has_static_folder: + raise RuntimeError("'static_folder' must be set to serve static_files.") + + # send_file only knows to call get_send_file_max_age on the app, + # call it here so it works for blueprints too. + max_age = self.get_send_file_max_age(filename) + return send_from_directory( + t.cast(str, self.static_folder), filename, max_age=max_age + ) + + def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: + """Open a resource file relative to :attr:`root_path` for + reading. + + For example, if the file ``schema.sql`` is next to the file + ``app.py`` where the ``Flask`` app is defined, it can be opened + with: + + .. code-block:: python + + with app.open_resource("schema.sql") as f: + conn.executescript(f.read()) + + :param resource: Path to the resource relative to + :attr:`root_path`. + :param mode: Open the file in this mode. Only reading is + supported, valid values are "r" (or "rt") and "rb". + + Note this is a duplicate of the same method in the Flask + class. + + """ + if mode not in {"r", "rt", "rb"}: + raise ValueError("Resources can only be opened for reading.") + + return open(os.path.join(self.root_path, resource), mode) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/cli.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/cli.py new file mode 100644 index 000000000..ecb292a01 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/cli.py @@ -0,0 +1,1109 @@ +from __future__ import annotations + +import ast +import collections.abc as cabc +import importlib.metadata +import inspect +import os +import platform +import re +import sys +import traceback +import typing as t +from functools import update_wrapper +from operator import itemgetter +from types import ModuleType + +import click +from click.core import ParameterSource +from werkzeug import run_simple +from werkzeug.serving import is_running_from_reloader +from werkzeug.utils import import_string + +from .globals import current_app +from .helpers import get_debug_flag +from .helpers import get_load_dotenv + +if t.TYPE_CHECKING: + import ssl + + from _typeshed.wsgi import StartResponse + from _typeshed.wsgi import WSGIApplication + from _typeshed.wsgi import WSGIEnvironment + + from .app import Flask + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(module: ModuleType) -> Flask: + """Given a module instance this tries to find the best possible + application in the module or raises an exception. + """ + from . import Flask + + # Search for the most common names first. + for attr_name in ("app", "application"): + app = getattr(module, attr_name, None) + + if isinstance(app, Flask): + return app + + # Otherwise find the only object that is a Flask instance. + matches = [v for v in module.__dict__.values() if isinstance(v, Flask)] + + if len(matches) == 1: + return matches[0] + elif len(matches) > 1: + raise NoAppException( + "Detected multiple Flask applications in module" + f" '{module.__name__}'. Use '{module.__name__}:name'" + " to specify the correct one." + ) + + # Search for app factory functions. + for attr_name in ("create_app", "make_app"): + app_factory = getattr(module, attr_name, None) + + if inspect.isfunction(app_factory): + try: + app = app_factory() + + if isinstance(app, Flask): + return app + except TypeError as e: + if not _called_with_wrong_args(app_factory): + raise + + raise NoAppException( + f"Detected factory '{attr_name}' in module '{module.__name__}'," + " but could not call it without arguments. Use" + f" '{module.__name__}:{attr_name}(args)'" + " to specify arguments." + ) from e + + raise NoAppException( + "Failed to find Flask application or factory in module" + f" '{module.__name__}'. Use '{module.__name__}:name'" + " to specify one." + ) + + +def _called_with_wrong_args(f: t.Callable[..., Flask]) -> bool: + """Check whether calling a function raised a ``TypeError`` because + the call failed or because something in the factory raised the + error. + + :param f: The function that was called. + :return: ``True`` if the call failed. + """ + tb = sys.exc_info()[2] + + try: + while tb is not None: + if tb.tb_frame.f_code is f.__code__: + # In the function, it was called successfully. + return False + + tb = tb.tb_next + + # Didn't reach the function. + return True + finally: + # Delete tb to break a circular reference. + # https://docs.python.org/2/library/sys.html#sys.exc_info + del tb + + +def find_app_by_string(module: ModuleType, app_name: str) -> Flask: + """Check if the given string is a variable name or a function. Call + a function to get the app instance, or return the variable directly. + """ + from . import Flask + + # Parse app_name as a single expression to determine if it's a valid + # attribute name or function call. + try: + expr = ast.parse(app_name.strip(), mode="eval").body + except SyntaxError: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) from None + + if isinstance(expr, ast.Name): + name = expr.id + args = [] + kwargs = {} + elif isinstance(expr, ast.Call): + # Ensure the function name is an attribute name only. + if not isinstance(expr.func, ast.Name): + raise NoAppException( + f"Function reference must be a simple name: {app_name!r}." + ) + + name = expr.func.id + + # Parse the positional and keyword arguments as literals. + try: + args = [ast.literal_eval(arg) for arg in expr.args] + kwargs = { + kw.arg: ast.literal_eval(kw.value) + for kw in expr.keywords + if kw.arg is not None + } + except ValueError: + # literal_eval gives cryptic error messages, show a generic + # message with the full expression instead. + raise NoAppException( + f"Failed to parse arguments as literal values: {app_name!r}." + ) from None + else: + raise NoAppException( + f"Failed to parse {app_name!r} as an attribute name or function call." + ) + + try: + attr = getattr(module, name) + except AttributeError as e: + raise NoAppException( + f"Failed to find attribute {name!r} in {module.__name__!r}." + ) from e + + # If the attribute is a function, call it with any args and kwargs + # to get the real application. + if inspect.isfunction(attr): + try: + app = attr(*args, **kwargs) + except TypeError as e: + if not _called_with_wrong_args(attr): + raise + + raise NoAppException( + f"The factory {app_name!r} in module" + f" {module.__name__!r} could not be called with the" + " specified arguments." + ) from e + else: + app = attr + + if isinstance(app, Flask): + return app + + raise NoAppException( + "A valid Flask application was not obtained from" + f" '{module.__name__}:{app_name}'." + ) + + +def prepare_import(path: str) -> str: + """Given a filename this will try to calculate the python path, add it + to the search path and return the actual module name that is expected. + """ + path = os.path.realpath(path) + + fname, ext = os.path.splitext(path) + if ext == ".py": + path = fname + + if os.path.basename(path) == "__init__": + path = os.path.dirname(path) + + module_name = [] + + # move up until outside package structure (no __init__.py) + while True: + path, name = os.path.split(path) + module_name.append(name) + + if not os.path.exists(os.path.join(path, "__init__.py")): + break + + if sys.path[0] != path: + sys.path.insert(0, path) + + return ".".join(module_name[::-1]) + + +@t.overload +def locate_app( + module_name: str, app_name: str | None, raise_if_not_found: t.Literal[True] = True +) -> Flask: ... + + +@t.overload +def locate_app( + module_name: str, app_name: str | None, raise_if_not_found: t.Literal[False] = ... +) -> Flask | None: ... + + +def locate_app( + module_name: str, app_name: str | None, raise_if_not_found: bool = True +) -> Flask | None: + try: + __import__(module_name) + except ImportError: + # Reraise the ImportError if it occurred within the imported module. + # Determine this by checking whether the trace has a depth > 1. + if sys.exc_info()[2].tb_next: # type: ignore[union-attr] + raise NoAppException( + f"While importing {module_name!r}, an ImportError was" + f" raised:\n\n{traceback.format_exc()}" + ) from None + elif raise_if_not_found: + raise NoAppException(f"Could not import {module_name!r}.") from None + else: + return None + + module = sys.modules[module_name] + + if app_name is None: + return find_best_app(module) + else: + return find_app_by_string(module, app_name) + + +def get_version(ctx: click.Context, param: click.Parameter, value: t.Any) -> None: + if not value or ctx.resilient_parsing: + return + + flask_version = importlib.metadata.version("flask") + werkzeug_version = importlib.metadata.version("werkzeug") + + click.echo( + f"Python {platform.python_version()}\n" + f"Flask {flask_version}\n" + f"Werkzeug {werkzeug_version}", + color=ctx.color, + ) + ctx.exit() + + +version_option = click.Option( + ["--version"], + help="Show the Flask version.", + expose_value=False, + callback=get_version, + is_flag=True, + is_eager=True, +) + + +class ScriptInfo: + """Helper object to deal with Flask applications. This is usually not + necessary to interface with as it's used internally in the dispatching + to click. In future versions of Flask this object will most likely play + a bigger role. Typically it's created automatically by the + :class:`FlaskGroup` but you can also manually create it and pass it + onwards as click object. + """ + + def __init__( + self, + app_import_path: str | None = None, + create_app: t.Callable[..., Flask] | None = None, + set_debug_flag: bool = True, + ) -> None: + #: Optionally the import path for the Flask application. + self.app_import_path = app_import_path + #: Optionally a function that is passed the script info to create + #: the instance of the application. + self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data: dict[t.Any, t.Any] = {} + self.set_debug_flag = set_debug_flag + self._loaded_app: Flask | None = None + + def load_app(self) -> Flask: + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ + if self._loaded_app is not None: + return self._loaded_app + + if self.create_app is not None: + app: Flask | None = self.create_app() + else: + if self.app_import_path: + path, name = ( + re.split(r":(?![\\/])", self.app_import_path, maxsplit=1) + [None] + )[:2] + import_name = prepare_import(path) + app = locate_app(import_name, name) + else: + for path in ("wsgi.py", "app.py"): + import_name = prepare_import(path) + app = locate_app(import_name, None, raise_if_not_found=False) + + if app is not None: + break + + if app is None: + raise NoAppException( + "Could not locate a Flask application. Use the" + " 'flask --app' option, 'FLASK_APP' environment" + " variable, or a 'wsgi.py' or 'app.py' file in the" + " current directory." + ) + + if self.set_debug_flag: + # Update the app's debug flag through the descriptor so that + # other values repopulate as well. + app.debug = get_debug_flag() + + self._loaded_app = app + return app + + +pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def with_appcontext(f: F) -> F: + """Wraps a callback so that it's guaranteed to be executed with the + script's application context. + + Custom commands (and their options) registered under ``app.cli`` or + ``blueprint.cli`` will always have an app context available, this + decorator is not required in that case. + + .. versionchanged:: 2.2 + The app context is active for subcommands as well as the + decorated callback. The app context is always available to + ``app.cli`` command and parameter callbacks. + """ + + @click.pass_context + def decorator(ctx: click.Context, /, *args: t.Any, **kwargs: t.Any) -> t.Any: + if not current_app: + app = ctx.ensure_object(ScriptInfo).load_app() + ctx.with_resource(app.app_context()) + + return ctx.invoke(f, *args, **kwargs) + + return update_wrapper(decorator, f) # type: ignore[return-value] + + +class AppGroup(click.Group): + """This works similar to a regular click :class:`~click.Group` but it + changes the behavior of the :meth:`command` decorator so that it + automatically wraps the functions in :func:`with_appcontext`. + + Not to be confused with :class:`FlaskGroup`. + """ + + def command( # type: ignore[override] + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], click.Command]: + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it wraps callbacks in :func:`with_appcontext` + unless it's disabled by passing ``with_appcontext=False``. + """ + wrap_for_ctx = kwargs.pop("with_appcontext", True) + + def decorator(f: t.Callable[..., t.Any]) -> click.Command: + if wrap_for_ctx: + f = with_appcontext(f) + return super(AppGroup, self).command(*args, **kwargs)(f) # type: ignore[no-any-return] + + return decorator + + def group( # type: ignore[override] + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], click.Group]: + """This works exactly like the method of the same name on a regular + :class:`click.Group` but it defaults the group class to + :class:`AppGroup`. + """ + kwargs.setdefault("cls", AppGroup) + return super().group(*args, **kwargs) # type: ignore[no-any-return] + + +def _set_app(ctx: click.Context, param: click.Option, value: str | None) -> str | None: + if value is None: + return None + + info = ctx.ensure_object(ScriptInfo) + info.app_import_path = value + return value + + +# This option is eager so the app will be available if --help is given. +# --help is also eager, so --app must be before it in the param list. +# no_args_is_help bypasses eager processing, so this option must be +# processed manually in that case to ensure FLASK_APP gets picked up. +_app_option = click.Option( + ["-A", "--app"], + metavar="IMPORT", + help=( + "The Flask application or factory function to load, in the form 'module:name'." + " Module can be a dotted import or file path. Name is not required if it is" + " 'app', 'application', 'create_app', or 'make_app', and can be 'name(args)' to" + " pass arguments." + ), + is_eager=True, + expose_value=False, + callback=_set_app, +) + + +def _set_debug(ctx: click.Context, param: click.Option, value: bool) -> bool | None: + # If the flag isn't provided, it will default to False. Don't use + # that, let debug be set by env in that case. + source = ctx.get_parameter_source(param.name) # type: ignore[arg-type] + + if source is not None and source in ( + ParameterSource.DEFAULT, + ParameterSource.DEFAULT_MAP, + ): + return None + + # Set with env var instead of ScriptInfo.load so that it can be + # accessed early during a factory function. + os.environ["FLASK_DEBUG"] = "1" if value else "0" + return value + + +_debug_option = click.Option( + ["--debug/--no-debug"], + help="Set debug mode.", + expose_value=False, + callback=_set_debug, +) + + +def _env_file_callback( + ctx: click.Context, param: click.Option, value: str | None +) -> str | None: + if value is None: + return None + + import importlib + + try: + importlib.import_module("dotenv") + except ImportError: + raise click.BadParameter( + "python-dotenv must be installed to load an env file.", + ctx=ctx, + param=param, + ) from None + + # Don't check FLASK_SKIP_DOTENV, that only disables automatically + # loading .env and .flaskenv files. + load_dotenv(value) + return value + + +# This option is eager so env vars are loaded as early as possible to be +# used by other options. +_env_file_option = click.Option( + ["-e", "--env-file"], + type=click.Path(exists=True, dir_okay=False), + help="Load environment variables from this file. python-dotenv must be installed.", + is_eager=True, + expose_value=False, + callback=_env_file_callback, +) + + +class FlaskGroup(AppGroup): + """Special subclass of the :class:`AppGroup` group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced use cases for which it makes sense to create an + instance of this. see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands will be added. + :param add_version_option: adds the ``--version`` option. + :param create_app: an optional callback that is passed the script info and + returns the loaded app. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param set_debug_flag: Set the app's debug flag. + + .. versionchanged:: 2.2 + Added the ``-A/--app``, ``--debug/--no-debug``, ``-e/--env-file`` options. + + .. versionchanged:: 2.2 + An app context is pushed when running ``app.cli`` commands, so + ``@with_appcontext`` is no longer required for those commands. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment variables + from :file:`.env` and :file:`.flaskenv` files. + """ + + def __init__( + self, + add_default_commands: bool = True, + create_app: t.Callable[..., Flask] | None = None, + add_version_option: bool = True, + load_dotenv: bool = True, + set_debug_flag: bool = True, + **extra: t.Any, + ) -> None: + params = list(extra.pop("params", None) or ()) + # Processing is done with option callbacks instead of a group + # callback. This allows users to make a custom group callback + # without losing the behavior. --env-file must come first so + # that it is eagerly evaluated before --app. + params.extend((_env_file_option, _app_option, _debug_option)) + + if add_version_option: + params.append(version_option) + + if "context_settings" not in extra: + extra["context_settings"] = {} + + extra["context_settings"].setdefault("auto_envvar_prefix", "FLASK") + + super().__init__(params=params, **extra) + + self.create_app = create_app + self.load_dotenv = load_dotenv + self.set_debug_flag = set_debug_flag + + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + self.add_command(routes_command) + + self._loaded_plugin_commands = False + + def _load_plugin_commands(self) -> None: + if self._loaded_plugin_commands: + return + + if sys.version_info >= (3, 10): + from importlib import metadata + else: + # Use a backport on Python < 3.10. We technically have + # importlib.metadata on 3.8+, but the API changed in 3.10, + # so use the backport for consistency. + import importlib_metadata as metadata + + for ep in metadata.entry_points(group="flask.commands"): + self.add_command(ep.load(), ep.name) + + self._loaded_plugin_commands = True + + def get_command(self, ctx: click.Context, name: str) -> click.Command | None: + self._load_plugin_commands() + # Look up built-in and plugin commands, which should be + # available even if the app fails to load. + rv = super().get_command(ctx, name) + + if rv is not None: + return rv + + info = ctx.ensure_object(ScriptInfo) + + # Look up commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + app = info.load_app() + except NoAppException as e: + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + return None + + # Push an app context for the loaded app unless it is already + # active somehow. This makes the context available to parameter + # and command callbacks without needing @with_appcontext. + if not current_app or current_app._get_current_object() is not app: # type: ignore[attr-defined] + ctx.with_resource(app.app_context()) + + return app.cli.get_command(ctx, name) + + def list_commands(self, ctx: click.Context) -> list[str]: + self._load_plugin_commands() + # Start with the built-in and plugin commands. + rv = set(super().list_commands(ctx)) + info = ctx.ensure_object(ScriptInfo) + + # Add commands provided by the app, showing an error and + # continuing if the app couldn't be loaded. + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except NoAppException as e: + # When an app couldn't be loaded, show the error message + # without the traceback. + click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") + except Exception: + # When any other errors occurred during loading, show the + # full traceback. + click.secho(f"{traceback.format_exc()}\n", err=True, fg="red") + + return sorted(rv) + + def make_context( + self, + info_name: str | None, + args: list[str], + parent: click.Context | None = None, + **extra: t.Any, + ) -> click.Context: + # Set a flag to tell app.run to become a no-op. If app.run was + # not in a __name__ == __main__ guard, it would start the server + # when importing, blocking whatever command is being called. + os.environ["FLASK_RUN_FROM_CLI"] = "true" + + # Attempt to load .env and .flask env files. The --env-file + # option can cause another file to be loaded. + if get_load_dotenv(self.load_dotenv): + load_dotenv() + + if "obj" not in extra and "obj" not in self.context_settings: + extra["obj"] = ScriptInfo( + create_app=self.create_app, set_debug_flag=self.set_debug_flag + ) + + return super().make_context(info_name, args, parent=parent, **extra) + + def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]: + if not args and self.no_args_is_help: + # Attempt to load --env-file and --app early in case they + # were given as env vars. Otherwise no_args_is_help will not + # see commands from app.cli. + _env_file_option.handle_parse_result(ctx, {}, []) + _app_option.handle_parse_result(ctx, {}, []) + + return super().parse_args(ctx, args) + + +def _path_is_ancestor(path: str, other: str) -> bool: + """Take ``other`` and remove the length of ``path`` from it. Then join it + to ``path``. If it is the original value, ``path`` is an ancestor of + ``other``.""" + return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other + + +def load_dotenv(path: str | os.PathLike[str] | None = None) -> bool: + """Load "dotenv" files in order of precedence to set environment variables. + + If an env var is already set it is not overwritten, so earlier files in the + list are preferred over later files. + + This is a no-op if `python-dotenv`_ is not installed. + + .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + :param path: Load the file at this location instead of searching. + :return: ``True`` if a file was loaded. + + .. versionchanged:: 2.0 + The current directory is not changed to the location of the + loaded file. + + .. versionchanged:: 2.0 + When loading the env files, set the default encoding to UTF-8. + + .. versionchanged:: 1.1.0 + Returns ``False`` when python-dotenv is not installed, or when + the given path isn't a file. + + .. versionadded:: 1.0 + """ + try: + import dotenv + except ImportError: + if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"): + click.secho( + " * Tip: There are .env or .flaskenv files present." + ' Do "pip install python-dotenv" to use them.', + fg="yellow", + err=True, + ) + + return False + + # Always return after attempting to load a given path, don't load + # the default files. + if path is not None: + if os.path.isfile(path): + return dotenv.load_dotenv(path, encoding="utf-8") + + return False + + loaded = False + + for name in (".env", ".flaskenv"): + path = dotenv.find_dotenv(name, usecwd=True) + + if not path: + continue + + dotenv.load_dotenv(path, encoding="utf-8") + loaded = True + + return loaded # True if at least one file was located and loaded. + + +def show_server_banner(debug: bool, app_import_path: str | None) -> None: + """Show extra startup messages the first time the server is run, + ignoring the reloader. + """ + if is_running_from_reloader(): + return + + if app_import_path is not None: + click.echo(f" * Serving Flask app '{app_import_path}'") + + if debug is not None: + click.echo(f" * Debug mode: {'on' if debug else 'off'}") + + +class CertParamType(click.ParamType): + """Click option type for the ``--cert`` option. Allows either an + existing file, the string ``'adhoc'``, or an import for a + :class:`~ssl.SSLContext` object. + """ + + name = "path" + + def __init__(self) -> None: + self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True) + + def convert( + self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None + ) -> t.Any: + try: + import ssl + except ImportError: + raise click.BadParameter( + 'Using "--cert" requires Python to be compiled with SSL support.', + ctx, + param, + ) from None + + try: + return self.path_type(value, param, ctx) + except click.BadParameter: + value = click.STRING(value, param, ctx).lower() + + if value == "adhoc": + try: + import cryptography # noqa: F401 + except ImportError: + raise click.BadParameter( + "Using ad-hoc certificates requires the cryptography library.", + ctx, + param, + ) from None + + return value + + obj = import_string(value, silent=True) + + if isinstance(obj, ssl.SSLContext): + return obj + + raise + + +def _validate_key(ctx: click.Context, param: click.Parameter, value: t.Any) -> t.Any: + """The ``--key`` option must be specified when ``--cert`` is a file. + Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. + """ + cert = ctx.params.get("cert") + is_adhoc = cert == "adhoc" + + try: + import ssl + except ImportError: + is_context = False + else: + is_context = isinstance(cert, ssl.SSLContext) + + if value is not None: + if is_adhoc: + raise click.BadParameter( + 'When "--cert" is "adhoc", "--key" is not used.', ctx, param + ) + + if is_context: + raise click.BadParameter( + 'When "--cert" is an SSLContext object, "--key" is not used.', + ctx, + param, + ) + + if not cert: + raise click.BadParameter('"--cert" must also be specified.', ctx, param) + + ctx.params["cert"] = cert, value + + else: + if cert and not (is_adhoc or is_context): + raise click.BadParameter('Required when using "--cert".', ctx, param) + + return value + + +class SeparatedPathType(click.Path): + """Click option type that accepts a list of values separated by the + OS's path separator (``:``, ``;`` on Windows). Each value is + validated as a :class:`click.Path` type. + """ + + def convert( + self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None + ) -> t.Any: + items = self.split_envvar_value(value) + # can't call no-arg super() inside list comprehension until Python 3.12 + super_convert = super().convert + return [super_convert(item, param, ctx) for item in items] + + +@click.command("run", short_help="Run a development server.") +@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.") +@click.option("--port", "-p", default=5000, help="The port to bind to.") +@click.option( + "--cert", + type=CertParamType(), + help="Specify a certificate file to use HTTPS.", + is_eager=True, +) +@click.option( + "--key", + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + callback=_validate_key, + expose_value=False, + help="The key file to use when specifying a certificate.", +) +@click.option( + "--reload/--no-reload", + default=None, + help="Enable or disable the reloader. By default the reloader " + "is active if debug is enabled.", +) +@click.option( + "--debugger/--no-debugger", + default=None, + help="Enable or disable the debugger. By default the debugger " + "is active if debug is enabled.", +) +@click.option( + "--with-threads/--without-threads", + default=True, + help="Enable or disable multithreading.", +) +@click.option( + "--extra-files", + default=None, + type=SeparatedPathType(), + help=( + "Extra files that trigger a reload on change. Multiple paths" + f" are separated by {os.path.pathsep!r}." + ), +) +@click.option( + "--exclude-patterns", + default=None, + type=SeparatedPathType(), + help=( + "Files matching these fnmatch patterns will not trigger a reload" + " on change. Multiple patterns are separated by" + f" {os.path.pathsep!r}." + ), +) +@pass_script_info +def run_command( + info: ScriptInfo, + host: str, + port: int, + reload: bool, + debugger: bool, + with_threads: bool, + cert: ssl.SSLContext | tuple[str, str | None] | t.Literal["adhoc"] | None, + extra_files: list[str] | None, + exclude_patterns: list[str] | None, +) -> None: + """Run a local development server. + + This server is for development purposes only. It does not provide + the stability, security, or performance of production WSGI servers. + + The reloader and debugger are enabled by default with the '--debug' + option. + """ + try: + app: WSGIApplication = info.load_app() + except Exception as e: + if is_running_from_reloader(): + # When reloading, print out the error immediately, but raise + # it later so the debugger or server can handle it. + traceback.print_exc() + err = e + + def app( + environ: WSGIEnvironment, start_response: StartResponse + ) -> cabc.Iterable[bytes]: + raise err from None + + else: + # When not reloading, raise the error immediately so the + # command fails. + raise e from None + + debug = get_debug_flag() + + if reload is None: + reload = debug + + if debugger is None: + debugger = debug + + show_server_banner(debug, info.app_import_path) + + run_simple( + host, + port, + app, + use_reloader=reload, + use_debugger=debugger, + threaded=with_threads, + ssl_context=cert, + extra_files=extra_files, + exclude_patterns=exclude_patterns, + ) + + +run_command.params.insert(0, _debug_option) + + +@click.command("shell", short_help="Run a shell in the app context.") +@with_appcontext +def shell_command() -> None: + """Run an interactive Python shell in the context of a given + Flask application. The application will populate the default + namespace of this shell according to its configuration. + + This is useful for executing small snippets of management code + without having to manually configure the application. + """ + import code + + banner = ( + f"Python {sys.version} on {sys.platform}\n" + f"App: {current_app.import_name}\n" + f"Instance: {current_app.instance_path}" + ) + ctx: dict[str, t.Any] = {} + + # Support the regular Python interpreter startup script if someone + # is using it. + startup = os.environ.get("PYTHONSTARTUP") + if startup and os.path.isfile(startup): + with open(startup) as f: + eval(compile(f.read(), startup, "exec"), ctx) + + ctx.update(current_app.make_shell_context()) + + # Site, customize, or startup script can set a hook to call when + # entering interactive mode. The default one sets up readline with + # tab and history completion. + interactive_hook = getattr(sys, "__interactivehook__", None) + + if interactive_hook is not None: + try: + import readline + from rlcompleter import Completer + except ImportError: + pass + else: + # rlcompleter uses __main__.__dict__ by default, which is + # flask.__main__. Use the shell context instead. + readline.set_completer(Completer(ctx).complete) + + interactive_hook() + + code.interact(banner=banner, local=ctx) + + +@click.command("routes", short_help="Show the routes for the app.") +@click.option( + "--sort", + "-s", + type=click.Choice(("endpoint", "methods", "domain", "rule", "match")), + default="endpoint", + help=( + "Method to sort routes by. 'match' is the order that Flask will match routes" + " when dispatching a request." + ), +) +@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.") +@with_appcontext +def routes_command(sort: str, all_methods: bool) -> None: + """Show all registered routes with endpoints and methods.""" + rules = list(current_app.url_map.iter_rules()) + + if not rules: + click.echo("No routes were registered.") + return + + ignored_methods = set() if all_methods else {"HEAD", "OPTIONS"} + host_matching = current_app.url_map.host_matching + has_domain = any(rule.host if host_matching else rule.subdomain for rule in rules) + rows = [] + + for rule in rules: + row = [ + rule.endpoint, + ", ".join(sorted((rule.methods or set()) - ignored_methods)), + ] + + if has_domain: + row.append((rule.host if host_matching else rule.subdomain) or "") + + row.append(rule.rule) + rows.append(row) + + headers = ["Endpoint", "Methods"] + sorts = ["endpoint", "methods"] + + if has_domain: + headers.append("Host" if host_matching else "Subdomain") + sorts.append("domain") + + headers.append("Rule") + sorts.append("rule") + + try: + rows.sort(key=itemgetter(sorts.index(sort))) + except ValueError: + pass + + rows.insert(0, headers) + widths = [max(len(row[i]) for row in rows) for i in range(len(headers))] + rows.insert(1, ["-" * w for w in widths]) + template = " ".join(f"{{{i}:<{w}}}" for i, w in enumerate(widths)) + + for row in rows: + click.echo(template.format(*row)) + + +cli = FlaskGroup( + name="flask", + help="""\ +A general utility script for Flask applications. + +An application to load must be given with the '--app' option, +'FLASK_APP' environment variable, or with a 'wsgi.py' or 'app.py' file +in the current directory. +""", +) + + +def main() -> None: + cli.main() + + +if __name__ == "__main__": + main() diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/config.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/config.py new file mode 100644 index 000000000..7e3ba1790 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/config.py @@ -0,0 +1,370 @@ +from __future__ import annotations + +import errno +import json +import os +import types +import typing as t + +from werkzeug.utils import import_string + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .sansio.app import App + + +T = t.TypeVar("T") + + +class ConfigAttribute(t.Generic[T]): + """Makes an attribute forward to the config""" + + def __init__( + self, name: str, get_converter: t.Callable[[t.Any], T] | None = None + ) -> None: + self.__name__ = name + self.get_converter = get_converter + + @t.overload + def __get__(self, obj: None, owner: None) -> te.Self: ... + + @t.overload + def __get__(self, obj: App, owner: type[App]) -> T: ... + + def __get__(self, obj: App | None, owner: type[App] | None = None) -> T | te.Self: + if obj is None: + return self + + rv = obj.config[self.__name__] + + if self.get_converter is not None: + rv = self.get_converter(rv) + + return rv # type: ignore[no-any-return] + + def __set__(self, obj: App, value: t.Any) -> None: + obj.config[self.__name__] = value + + +class Config(dict): # type: ignore[type-arg] + """Works exactly like a dict but provides ways to fill it from files + or special dictionaries. There are two common patterns to populate the + config. + + Either you can fill the config from a config file:: + + app.config.from_pyfile('yourconfig.cfg') + + Or alternatively you can define the configuration options in the + module that calls :meth:`from_object` or provide an import path to + a module that should be loaded. It is also possible to tell it to + use the same module and with that provide the configuration values + just before the call:: + + DEBUG = True + SECRET_KEY = 'development key' + app.config.from_object(__name__) + + In both cases (loading from any Python file or loading from modules), + only uppercase keys are added to the config. This makes it possible to use + lowercase values in the config file for temporary values that are not added + to the config or to define the config keys in the same file that implements + the application. + + Probably the most interesting way to load configurations is from an + environment variable pointing to a file:: + + app.config.from_envvar('YOURAPPLICATION_SETTINGS') + + In this case before launching the application you have to set this + environment variable to the file you want to use. On Linux and OS X + use the export statement:: + + export YOURAPPLICATION_SETTINGS='/path/to/config/file' + + On windows use `set` instead. + + :param root_path: path to which files are read relative from. When the + config object is created by the application, this is + the application's :attr:`~flask.Flask.root_path`. + :param defaults: an optional dictionary of default values + """ + + def __init__( + self, + root_path: str | os.PathLike[str], + defaults: dict[str, t.Any] | None = None, + ) -> None: + super().__init__(defaults or {}) + self.root_path = root_path + + def from_envvar(self, variable_name: str, silent: bool = False) -> bool: + """Loads a configuration from an environment variable pointing to + a configuration file. This is basically just a shortcut with nicer + error messages for this line of code:: + + app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) + + :param variable_name: name of the environment variable + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: ``True`` if the file was loaded successfully. + """ + rv = os.environ.get(variable_name) + if not rv: + if silent: + return False + raise RuntimeError( + f"The environment variable {variable_name!r} is not set" + " and as such configuration could not be loaded. Set" + " this variable and make it point to a configuration" + " file" + ) + return self.from_pyfile(rv, silent=silent) + + def from_prefixed_env( + self, prefix: str = "FLASK", *, loads: t.Callable[[str], t.Any] = json.loads + ) -> bool: + """Load any environment variables that start with ``FLASK_``, + dropping the prefix from the env key for the config key. Values + are passed through a loading function to attempt to convert them + to more specific types than strings. + + Keys are loaded in :func:`sorted` order. + + The default loading function attempts to parse values as any + valid JSON type, including dicts and lists. + + Specific items in nested dicts can be set by separating the + keys with double underscores (``__``). If an intermediate key + doesn't exist, it will be initialized to an empty dict. + + :param prefix: Load env vars that start with this prefix, + separated with an underscore (``_``). + :param loads: Pass each string value to this function and use + the returned value as the config value. If any error is + raised it is ignored and the value remains a string. The + default is :func:`json.loads`. + + .. versionadded:: 2.1 + """ + prefix = f"{prefix}_" + len_prefix = len(prefix) + + for key in sorted(os.environ): + if not key.startswith(prefix): + continue + + value = os.environ[key] + + try: + value = loads(value) + except Exception: + # Keep the value as a string if loading failed. + pass + + # Change to key.removeprefix(prefix) on Python >= 3.9. + key = key[len_prefix:] + + if "__" not in key: + # A non-nested key, set directly. + self[key] = value + continue + + # Traverse nested dictionaries with keys separated by "__". + current = self + *parts, tail = key.split("__") + + for part in parts: + # If an intermediate dict does not exist, create it. + if part not in current: + current[part] = {} + + current = current[part] + + current[tail] = value + + return True + + def from_pyfile( + self, filename: str | os.PathLike[str], silent: bool = False + ) -> bool: + """Updates the values in the config from a Python file. This function + behaves as if the file was imported as module with the + :meth:`from_object` function. + + :param filename: the filename of the config. This can either be an + absolute filename or a filename relative to the + root path. + :param silent: set to ``True`` if you want silent failure for missing + files. + :return: ``True`` if the file was loaded successfully. + + .. versionadded:: 0.7 + `silent` parameter. + """ + filename = os.path.join(self.root_path, filename) + d = types.ModuleType("config") + d.__file__ = filename + try: + with open(filename, mode="rb") as config_file: + exec(compile(config_file.read(), filename, "exec"), d.__dict__) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR): + return False + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + self.from_object(d) + return True + + def from_object(self, obj: object | str) -> None: + """Updates the values from the given object. An object can be of one + of the following two types: + + - a string: in this case the object with that name will be imported + - an actual object reference: that object is used directly + + Objects are usually either modules or classes. :meth:`from_object` + loads only the uppercase attributes of the module/class. A ``dict`` + object will not work with :meth:`from_object` because the keys of a + ``dict`` are not attributes of the ``dict`` class. + + Example of module-based configuration:: + + app.config.from_object('yourapplication.default_config') + from yourapplication import default_config + app.config.from_object(default_config) + + Nothing is done to the object before loading. If the object is a + class and has ``@property`` attributes, it needs to be + instantiated before being passed to this method. + + You should not use this function to load the actual configuration but + rather configuration defaults. The actual config should be loaded + with :meth:`from_pyfile` and ideally from a location not within the + package because the package might be installed system wide. + + See :ref:`config-dev-prod` for an example of class-based configuration + using :meth:`from_object`. + + :param obj: an import name or object + """ + if isinstance(obj, str): + obj = import_string(obj) + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) + + def from_file( + self, + filename: str | os.PathLike[str], + load: t.Callable[[t.IO[t.Any]], t.Mapping[str, t.Any]], + silent: bool = False, + text: bool = True, + ) -> bool: + """Update the values in the config from a file that is loaded + using the ``load`` parameter. The loaded data is passed to the + :meth:`from_mapping` method. + + .. code-block:: python + + import json + app.config.from_file("config.json", load=json.load) + + import tomllib + app.config.from_file("config.toml", load=tomllib.load, text=False) + + :param filename: The path to the data file. This can be an + absolute path or relative to the config root path. + :param load: A callable that takes a file handle and returns a + mapping of loaded data from the file. + :type load: ``Callable[[Reader], Mapping]`` where ``Reader`` + implements a ``read`` method. + :param silent: Ignore the file if it doesn't exist. + :param text: Open the file in text or binary mode. + :return: ``True`` if the file was loaded successfully. + + .. versionchanged:: 2.3 + The ``text`` parameter was added. + + .. versionadded:: 2.0 + """ + filename = os.path.join(self.root_path, filename) + + try: + with open(filename, "r" if text else "rb") as f: + obj = load(f) + except OSError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR): + return False + + e.strerror = f"Unable to load configuration file ({e.strerror})" + raise + + return self.from_mapping(obj) + + def from_mapping( + self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any + ) -> bool: + """Updates the config like :meth:`update` ignoring items with + non-upper keys. + + :return: Always returns ``True``. + + .. versionadded:: 0.11 + """ + mappings: dict[str, t.Any] = {} + if mapping is not None: + mappings.update(mapping) + mappings.update(kwargs) + for key, value in mappings.items(): + if key.isupper(): + self[key] = value + return True + + def get_namespace( + self, namespace: str, lowercase: bool = True, trim_namespace: bool = True + ) -> dict[str, t.Any]: + """Returns a dictionary containing a subset of configuration options + that match the specified namespace/prefix. Example usage:: + + app.config['IMAGE_STORE_TYPE'] = 'fs' + app.config['IMAGE_STORE_PATH'] = '/var/app/images' + app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' + image_store_config = app.config.get_namespace('IMAGE_STORE_') + + The resulting dictionary `image_store_config` would look like:: + + { + 'type': 'fs', + 'path': '/var/app/images', + 'base_url': 'http://img.website.com' + } + + This is often useful when configuration options map directly to + keyword arguments in functions or class constructors. + + :param namespace: a configuration namespace + :param lowercase: a flag indicating if the keys of the resulting + dictionary should be lowercase + :param trim_namespace: a flag indicating if the keys of the resulting + dictionary should not include the namespace + + .. versionadded:: 0.11 + """ + rv = {} + for k, v in self.items(): + if not k.startswith(namespace): + continue + if trim_namespace: + key = k[len(namespace) :] + else: + key = k + if lowercase: + key = key.lower() + rv[key] = v + return rv + + def __repr__(self) -> str: + return f"<{type(self).__name__} {dict.__repr__(self)}>" diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/ctx.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/ctx.py new file mode 100644 index 000000000..9b164d390 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/ctx.py @@ -0,0 +1,449 @@ +from __future__ import annotations + +import contextvars +import sys +import typing as t +from functools import update_wrapper +from types import TracebackType + +from werkzeug.exceptions import HTTPException + +from . import typing as ft +from .globals import _cv_app +from .globals import _cv_request +from .signals import appcontext_popped +from .signals import appcontext_pushed + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import WSGIEnvironment + + from .app import Flask + from .sessions import SessionMixin + from .wrappers import Request + + +# a singleton sentinel value for parameter defaults +_sentinel = object() + + +class _AppCtxGlobals: + """A plain object. Used as a namespace for storing data during an + application context. + + Creating an app context automatically creates this object, which is + made available as the :data:`g` proxy. + + .. describe:: 'key' in g + + Check whether an attribute is present. + + .. versionadded:: 0.10 + + .. describe:: iter(g) + + Return an iterator over the attribute names. + + .. versionadded:: 0.10 + """ + + # Define attr methods to let mypy know this is a namespace object + # that has arbitrary attributes. + + def __getattr__(self, name: str) -> t.Any: + try: + return self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def __setattr__(self, name: str, value: t.Any) -> None: + self.__dict__[name] = value + + def __delattr__(self, name: str) -> None: + try: + del self.__dict__[name] + except KeyError: + raise AttributeError(name) from None + + def get(self, name: str, default: t.Any | None = None) -> t.Any: + """Get an attribute by name, or a default value. Like + :meth:`dict.get`. + + :param name: Name of attribute to get. + :param default: Value to return if the attribute is not present. + + .. versionadded:: 0.10 + """ + return self.__dict__.get(name, default) + + def pop(self, name: str, default: t.Any = _sentinel) -> t.Any: + """Get and remove an attribute by name. Like :meth:`dict.pop`. + + :param name: Name of attribute to pop. + :param default: Value to return if the attribute is not present, + instead of raising a ``KeyError``. + + .. versionadded:: 0.11 + """ + if default is _sentinel: + return self.__dict__.pop(name) + else: + return self.__dict__.pop(name, default) + + def setdefault(self, name: str, default: t.Any = None) -> t.Any: + """Get the value of an attribute if it is present, otherwise + set and return a default value. Like :meth:`dict.setdefault`. + + :param name: Name of attribute to get. + :param default: Value to set and return if the attribute is not + present. + + .. versionadded:: 0.11 + """ + return self.__dict__.setdefault(name, default) + + def __contains__(self, item: str) -> bool: + return item in self.__dict__ + + def __iter__(self) -> t.Iterator[str]: + return iter(self.__dict__) + + def __repr__(self) -> str: + ctx = _cv_app.get(None) + if ctx is not None: + return f"" + return object.__repr__(self) + + +def after_this_request( + f: ft.AfterRequestCallable[t.Any], +) -> ft.AfterRequestCallable[t.Any]: + """Executes a function after this request. This is useful to modify + response objects. The function is passed the response object and has + to return the same or a new one. + + Example:: + + @app.route('/') + def index(): + @after_this_request + def add_header(response): + response.headers['X-Foo'] = 'Parachute' + return response + return 'Hello World!' + + This is more useful if a function other than the view function wants to + modify a response. For instance think of a decorator that wants to add + some headers without converting the return value into a response object. + + .. versionadded:: 0.9 + """ + ctx = _cv_request.get(None) + + if ctx is None: + raise RuntimeError( + "'after_this_request' can only be used when a request" + " context is active, such as in a view function." + ) + + ctx._after_request_functions.append(f) + return f + + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def copy_current_request_context(f: F) -> F: + """A helper function that decorates a function to retain the current + request context. This is useful when working with greenlets. The moment + the function is decorated a copy of the request context is created and + then pushed when the function is called. The current session is also + included in the copied request context. + + Example:: + + import gevent + from flask import copy_current_request_context + + @app.route('/') + def index(): + @copy_current_request_context + def do_some_work(): + # do some work here, it can access flask.request or + # flask.session like you would otherwise in the view function. + ... + gevent.spawn(do_some_work) + return 'Regular response' + + .. versionadded:: 0.10 + """ + ctx = _cv_request.get(None) + + if ctx is None: + raise RuntimeError( + "'copy_current_request_context' can only be used when a" + " request context is active, such as in a view function." + ) + + ctx = ctx.copy() + + def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any: + with ctx: # type: ignore[union-attr] + return ctx.app.ensure_sync(f)(*args, **kwargs) # type: ignore[union-attr] + + return update_wrapper(wrapper, f) # type: ignore[return-value] + + +def has_request_context() -> bool: + """If you have code that wants to test if a request context is there or + not this function can be used. For instance, you may want to take advantage + of request information if the request object is available, but fail + silently if it is unavailable. + + :: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and has_request_context(): + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + Alternatively you can also just test any of the context bound objects + (such as :class:`request` or :class:`g`) for truthness:: + + class User(db.Model): + + def __init__(self, username, remote_addr=None): + self.username = username + if remote_addr is None and request: + remote_addr = request.remote_addr + self.remote_addr = remote_addr + + .. versionadded:: 0.7 + """ + return _cv_request.get(None) is not None + + +def has_app_context() -> bool: + """Works like :func:`has_request_context` but for the application + context. You can also just do a boolean check on the + :data:`current_app` object instead. + + .. versionadded:: 0.9 + """ + return _cv_app.get(None) is not None + + +class AppContext: + """The app context contains application-specific information. An app + context is created and pushed at the beginning of each request if + one is not already active. An app context is also pushed when + running CLI commands. + """ + + def __init__(self, app: Flask) -> None: + self.app = app + self.url_adapter = app.create_url_adapter(None) + self.g: _AppCtxGlobals = app.app_ctx_globals_class() + self._cv_tokens: list[contextvars.Token[AppContext]] = [] + + def push(self) -> None: + """Binds the app context to the current context.""" + self._cv_tokens.append(_cv_app.set(self)) + appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync) + + def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore + """Pops the app context.""" + try: + if len(self._cv_tokens) == 1: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_appcontext(exc) + finally: + ctx = _cv_app.get() + _cv_app.reset(self._cv_tokens.pop()) + + if ctx is not self: + raise AssertionError( + f"Popped wrong app context. ({ctx!r} instead of {self!r})" + ) + + appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync) + + def __enter__(self) -> AppContext: + self.push() + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.pop(exc_value) + + +class RequestContext: + """The request context contains per-request information. The Flask + app creates and pushes it at the beginning of the request, then pops + it at the end of the request. It will create the URL adapter and + request object for the WSGI environment provided. + + Do not attempt to use this class directly, instead use + :meth:`~flask.Flask.test_request_context` and + :meth:`~flask.Flask.request_context` to create this object. + + When the request context is popped, it will evaluate all the + functions registered on the application for teardown execution + (:meth:`~flask.Flask.teardown_request`). + + The request context is automatically popped at the end of the + request. When using the interactive debugger, the context will be + restored so ``request`` is still accessible. Similarly, the test + client can preserve the context after the request ends. However, + teardown functions may already have closed some resources such as + database connections. + """ + + def __init__( + self, + app: Flask, + environ: WSGIEnvironment, + request: Request | None = None, + session: SessionMixin | None = None, + ) -> None: + self.app = app + if request is None: + request = app.request_class(environ) + request.json_module = app.json + self.request: Request = request + self.url_adapter = None + try: + self.url_adapter = app.create_url_adapter(self.request) + except HTTPException as e: + self.request.routing_exception = e + self.flashes: list[tuple[str, str]] | None = None + self.session: SessionMixin | None = session + # Functions that should be executed after the request on the response + # object. These will be called before the regular "after_request" + # functions. + self._after_request_functions: list[ft.AfterRequestCallable[t.Any]] = [] + + self._cv_tokens: list[ + tuple[contextvars.Token[RequestContext], AppContext | None] + ] = [] + + def copy(self) -> RequestContext: + """Creates a copy of this request context with the same request object. + This can be used to move a request context to a different greenlet. + Because the actual request object is the same this cannot be used to + move a request context to a different thread unless access to the + request object is locked. + + .. versionadded:: 0.10 + + .. versionchanged:: 1.1 + The current session object is used instead of reloading the original + data. This prevents `flask.session` pointing to an out-of-date object. + """ + return self.__class__( + self.app, + environ=self.request.environ, + request=self.request, + session=self.session, + ) + + def match_request(self) -> None: + """Can be overridden by a subclass to hook into the matching + of the request. + """ + try: + result = self.url_adapter.match(return_rule=True) # type: ignore + self.request.url_rule, self.request.view_args = result # type: ignore + except HTTPException as e: + self.request.routing_exception = e + + def push(self) -> None: + # Before we push the request context we have to ensure that there + # is an application context. + app_ctx = _cv_app.get(None) + + if app_ctx is None or app_ctx.app is not self.app: + app_ctx = self.app.app_context() + app_ctx.push() + else: + app_ctx = None + + self._cv_tokens.append((_cv_request.set(self), app_ctx)) + + # Open the session at the moment that the request context is available. + # This allows a custom open_session method to use the request context. + # Only open a new session if this is the first time the request was + # pushed, otherwise stream_with_context loses the session. + if self.session is None: + session_interface = self.app.session_interface + self.session = session_interface.open_session(self.app, self.request) + + if self.session is None: + self.session = session_interface.make_null_session(self.app) + + # Match the request URL after loading the session, so that the + # session is available in custom URL converters. + if self.url_adapter is not None: + self.match_request() + + def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore + """Pops the request context and unbinds it by doing that. This will + also trigger the execution of functions registered by the + :meth:`~flask.Flask.teardown_request` decorator. + + .. versionchanged:: 0.9 + Added the `exc` argument. + """ + clear_request = len(self._cv_tokens) == 1 + + try: + if clear_request: + if exc is _sentinel: + exc = sys.exc_info()[1] + self.app.do_teardown_request(exc) + + request_close = getattr(self.request, "close", None) + if request_close is not None: + request_close() + finally: + ctx = _cv_request.get() + token, app_ctx = self._cv_tokens.pop() + _cv_request.reset(token) + + # get rid of circular dependencies at the end of the request + # so that we don't require the GC to be active. + if clear_request: + ctx.request.environ["werkzeug.request"] = None + + if app_ctx is not None: + app_ctx.pop(exc) + + if ctx is not self: + raise AssertionError( + f"Popped wrong request context. ({ctx!r} instead of {self!r})" + ) + + def __enter__(self) -> RequestContext: + self.push() + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.pop(exc_value) + + def __repr__(self) -> str: + return ( + f"<{type(self).__name__} {self.request.url!r}" + f" [{self.request.method}] of {self.app.name}>" + ) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/debughelpers.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/debughelpers.py new file mode 100644 index 000000000..2c8c4c483 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/debughelpers.py @@ -0,0 +1,178 @@ +from __future__ import annotations + +import typing as t + +from jinja2.loaders import BaseLoader +from werkzeug.routing import RequestRedirect + +from .blueprints import Blueprint +from .globals import request_ctx +from .sansio.app import App + +if t.TYPE_CHECKING: + from .sansio.scaffold import Scaffold + from .wrappers import Request + + +class UnexpectedUnicodeError(AssertionError, UnicodeError): + """Raised in places where we want some better error reporting for + unexpected unicode or binary data. + """ + + +class DebugFilesKeyError(KeyError, AssertionError): + """Raised from request.files during debugging. The idea is that it can + provide a better error message than just a generic KeyError/BadRequest. + """ + + def __init__(self, request: Request, key: str) -> None: + form_matches = request.form.getlist(key) + buf = [ + f"You tried to access the file {key!r} in the request.files" + " dictionary but it does not exist. The mimetype for the" + f" request is {request.mimetype!r} instead of" + " 'multipart/form-data' which means that no file contents" + " were transmitted. To fix this error you should provide" + ' enctype="multipart/form-data" in your form.' + ] + if form_matches: + names = ", ".join(repr(x) for x in form_matches) + buf.append( + "\n\nThe browser instead transmitted some file names. " + f"This was submitted: {names}" + ) + self.msg = "".join(buf) + + def __str__(self) -> str: + return self.msg + + +class FormDataRoutingRedirect(AssertionError): + """This exception is raised in debug mode if a routing redirect + would cause the browser to drop the method or body. This happens + when method is not GET, HEAD or OPTIONS and the status code is not + 307 or 308. + """ + + def __init__(self, request: Request) -> None: + exc = request.routing_exception + assert isinstance(exc, RequestRedirect) + buf = [ + f"A request was sent to '{request.url}', but routing issued" + f" a redirect to the canonical URL '{exc.new_url}'." + ] + + if f"{request.base_url}/" == exc.new_url.partition("?")[0]: + buf.append( + " The URL was defined with a trailing slash. Flask" + " will redirect to the URL with a trailing slash if it" + " was accessed without one." + ) + + buf.append( + " Send requests to the canonical URL, or use 307 or 308 for" + " routing redirects. Otherwise, browsers will drop form" + " data.\n\n" + "This exception is only raised in debug mode." + ) + super().__init__("".join(buf)) + + +def attach_enctype_error_multidict(request: Request) -> None: + """Patch ``request.files.__getitem__`` to raise a descriptive error + about ``enctype=multipart/form-data``. + + :param request: The request to patch. + :meta private: + """ + oldcls = request.files.__class__ + + class newcls(oldcls): # type: ignore[valid-type, misc] + def __getitem__(self, key: str) -> t.Any: + try: + return super().__getitem__(key) + except KeyError as e: + if key not in request.form: + raise + + raise DebugFilesKeyError(request, key).with_traceback( + e.__traceback__ + ) from None + + newcls.__name__ = oldcls.__name__ + newcls.__module__ = oldcls.__module__ + request.files.__class__ = newcls + + +def _dump_loader_info(loader: BaseLoader) -> t.Iterator[str]: + yield f"class: {type(loader).__module__}.{type(loader).__name__}" + for key, value in sorted(loader.__dict__.items()): + if key.startswith("_"): + continue + if isinstance(value, (tuple, list)): + if not all(isinstance(x, str) for x in value): + continue + yield f"{key}:" + for item in value: + yield f" - {item}" + continue + elif not isinstance(value, (str, int, float, bool)): + continue + yield f"{key}: {value!r}" + + +def explain_template_loading_attempts( + app: App, + template: str, + attempts: list[ + tuple[ + BaseLoader, + Scaffold, + tuple[str, str | None, t.Callable[[], bool] | None] | None, + ] + ], +) -> None: + """This should help developers understand what failed""" + info = [f"Locating template {template!r}:"] + total_found = 0 + blueprint = None + if request_ctx and request_ctx.request.blueprint is not None: + blueprint = request_ctx.request.blueprint + + for idx, (loader, srcobj, triple) in enumerate(attempts): + if isinstance(srcobj, App): + src_info = f"application {srcobj.import_name!r}" + elif isinstance(srcobj, Blueprint): + src_info = f"blueprint {srcobj.name!r} ({srcobj.import_name})" + else: + src_info = repr(srcobj) + + info.append(f"{idx + 1:5}: trying loader of {src_info}") + + for line in _dump_loader_info(loader): + info.append(f" {line}") + + if triple is None: + detail = "no match" + else: + detail = f"found ({triple[1] or ''!r})" + total_found += 1 + info.append(f" -> {detail}") + + seems_fishy = False + if total_found == 0: + info.append("Error: the template could not be found.") + seems_fishy = True + elif total_found > 1: + info.append("Warning: multiple loaders returned a match for the template.") + seems_fishy = True + + if blueprint is not None and seems_fishy: + info.append( + " The template was looked up from an endpoint that belongs" + f" to the blueprint {blueprint!r}." + ) + info.append(" Maybe you did not place a template in the right folder?") + info.append(" See https://flask.palletsprojects.com/blueprints/#templates") + + app.logger.info("\n".join(info)) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/globals.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/globals.py new file mode 100644 index 000000000..e2c410cc5 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/globals.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import typing as t +from contextvars import ContextVar + +from werkzeug.local import LocalProxy + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .ctx import _AppCtxGlobals + from .ctx import AppContext + from .ctx import RequestContext + from .sessions import SessionMixin + from .wrappers import Request + + +_no_app_msg = """\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +the current application. To solve this, set up an application context +with app.app_context(). See the documentation for more information.\ +""" +_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx") +app_ctx: AppContext = LocalProxy( # type: ignore[assignment] + _cv_app, unbound_message=_no_app_msg +) +current_app: Flask = LocalProxy( # type: ignore[assignment] + _cv_app, "app", unbound_message=_no_app_msg +) +g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment] + _cv_app, "g", unbound_message=_no_app_msg +) + +_no_req_msg = """\ +Working outside of request context. + +This typically means that you attempted to use functionality that needed +an active HTTP request. Consult the documentation on testing for +information about how to avoid this problem.\ +""" +_cv_request: ContextVar[RequestContext] = ContextVar("flask.request_ctx") +request_ctx: RequestContext = LocalProxy( # type: ignore[assignment] + _cv_request, unbound_message=_no_req_msg +) +request: Request = LocalProxy( # type: ignore[assignment] + _cv_request, "request", unbound_message=_no_req_msg +) +session: SessionMixin = LocalProxy( # type: ignore[assignment] + _cv_request, "session", unbound_message=_no_req_msg +) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/helpers.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/helpers.py new file mode 100644 index 000000000..359a842af --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/helpers.py @@ -0,0 +1,621 @@ +from __future__ import annotations + +import importlib.util +import os +import sys +import typing as t +from datetime import datetime +from functools import lru_cache +from functools import update_wrapper + +import werkzeug.utils +from werkzeug.exceptions import abort as _wz_abort +from werkzeug.utils import redirect as _wz_redirect +from werkzeug.wrappers import Response as BaseResponse + +from .globals import _cv_request +from .globals import current_app +from .globals import request +from .globals import request_ctx +from .globals import session +from .signals import message_flashed + +if t.TYPE_CHECKING: # pragma: no cover + from .wrappers import Response + + +def get_debug_flag() -> bool: + """Get whether debug mode should be enabled for the app, indicated by the + :envvar:`FLASK_DEBUG` environment variable. The default is ``False``. + """ + val = os.environ.get("FLASK_DEBUG") + return bool(val and val.lower() not in {"0", "false", "no"}) + + +def get_load_dotenv(default: bool = True) -> bool: + """Get whether the user has disabled loading default dotenv files by + setting :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load + the files. + + :param default: What to return if the env var isn't set. + """ + val = os.environ.get("FLASK_SKIP_DOTENV") + + if not val: + return default + + return val.lower() in ("0", "false", "no") + + +def stream_with_context( + generator_or_function: t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]], +) -> t.Iterator[t.AnyStr]: + """Request contexts disappear when the response is started on the server. + This is done for efficiency reasons and to make it less likely to encounter + memory leaks with badly written WSGI middlewares. The downside is that if + you are using streamed responses, the generator cannot access request bound + information any more. + + This function however can help you keep the context around for longer:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + @stream_with_context + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(generate()) + + Alternatively it can also be used around a specific generator:: + + from flask import stream_with_context, request, Response + + @app.route('/stream') + def streamed_response(): + def generate(): + yield 'Hello ' + yield request.args['name'] + yield '!' + return Response(stream_with_context(generate())) + + .. versionadded:: 0.9 + """ + try: + gen = iter(generator_or_function) # type: ignore[arg-type] + except TypeError: + + def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any: + gen = generator_or_function(*args, **kwargs) # type: ignore[operator] + return stream_with_context(gen) + + return update_wrapper(decorator, generator_or_function) # type: ignore[arg-type] + + def generator() -> t.Iterator[t.AnyStr | None]: + ctx = _cv_request.get(None) + if ctx is None: + raise RuntimeError( + "'stream_with_context' can only be used when a request" + " context is active, such as in a view function." + ) + with ctx: + # Dummy sentinel. Has to be inside the context block or we're + # not actually keeping the context around. + yield None + + # The try/finally is here so that if someone passes a WSGI level + # iterator in we're still running the cleanup logic. Generators + # don't need that because they are closed on their destruction + # automatically. + try: + yield from gen + finally: + if hasattr(gen, "close"): + gen.close() + + # The trick is to start the generator. Then the code execution runs until + # the first dummy None is yielded at which point the context was already + # pushed. This item is discarded. Then when the iteration continues the + # real generator is executed. + wrapped_g = generator() + next(wrapped_g) + return wrapped_g # type: ignore[return-value] + + +def make_response(*args: t.Any) -> Response: + """Sometimes it is necessary to set additional headers in a view. Because + views do not have to return response objects but can return a value that + is converted into a response object by Flask itself, it becomes tricky to + add headers to it. This function can be called instead of using a return + and you will get a response object which you can use to attach headers. + + If view looked like this and you want to add a new header:: + + def index(): + return render_template('index.html', foo=42) + + You can now do something like this:: + + def index(): + response = make_response(render_template('index.html', foo=42)) + response.headers['X-Parachutes'] = 'parachutes are cool' + return response + + This function accepts the very same arguments you can return from a + view function. This for example creates a response with a 404 error + code:: + + response = make_response(render_template('not_found.html'), 404) + + The other use case of this function is to force the return value of a + view function into a response which is helpful with view + decorators:: + + response = make_response(view_function()) + response.headers['X-Parachutes'] = 'parachutes are cool' + + Internally this function does the following things: + + - if no arguments are passed, it creates a new response argument + - if one argument is passed, :meth:`flask.Flask.make_response` + is invoked with it. + - if more than one argument is passed, the arguments are passed + to the :meth:`flask.Flask.make_response` function as tuple. + + .. versionadded:: 0.6 + """ + if not args: + return current_app.response_class() + if len(args) == 1: + args = args[0] + return current_app.make_response(args) + + +def url_for( + endpoint: str, + *, + _anchor: str | None = None, + _method: str | None = None, + _scheme: str | None = None, + _external: bool | None = None, + **values: t.Any, +) -> str: + """Generate a URL to the given endpoint with the given values. + + This requires an active request or application context, and calls + :meth:`current_app.url_for() `. See that method + for full documentation. + + :param endpoint: The endpoint name associated with the URL to + generate. If this starts with a ``.``, the current blueprint + name (if any) will be used. + :param _anchor: If given, append this as ``#anchor`` to the URL. + :param _method: If given, generate the URL associated with this + method for the endpoint. + :param _scheme: If given, the URL will have this scheme if it is + external. + :param _external: If given, prefer the URL to be internal (False) or + require it to be external (True). External URLs include the + scheme and domain. When not in an active request, URLs are + external by default. + :param values: Values to use for the variable parts of the URL rule. + Unknown keys are appended as query string arguments, like + ``?a=b&c=d``. + + .. versionchanged:: 2.2 + Calls ``current_app.url_for``, allowing an app to override the + behavior. + + .. versionchanged:: 0.10 + The ``_scheme`` parameter was added. + + .. versionchanged:: 0.9 + The ``_anchor`` and ``_method`` parameters were added. + + .. versionchanged:: 0.9 + Calls ``app.handle_url_build_error`` on build errors. + """ + return current_app.url_for( + endpoint, + _anchor=_anchor, + _method=_method, + _scheme=_scheme, + _external=_external, + **values, + ) + + +def redirect( + location: str, code: int = 302, Response: type[BaseResponse] | None = None +) -> BaseResponse: + """Create a redirect response object. + + If :data:`~flask.current_app` is available, it will use its + :meth:`~flask.Flask.redirect` method, otherwise it will use + :func:`werkzeug.utils.redirect`. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + :param Response: The response class to use. Not used when + ``current_app`` is active, which uses ``app.response_class``. + + .. versionadded:: 2.2 + Calls ``current_app.redirect`` if available instead of always + using Werkzeug's default ``redirect``. + """ + if current_app: + return current_app.redirect(location, code=code) + + return _wz_redirect(location, code=code, Response=Response) + + +def abort(code: int | BaseResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: + """Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given + status code. + + If :data:`~flask.current_app` is available, it will call its + :attr:`~flask.Flask.aborter` object, otherwise it will use + :func:`werkzeug.exceptions.abort`. + + :param code: The status code for the exception, which must be + registered in ``app.aborter``. + :param args: Passed to the exception. + :param kwargs: Passed to the exception. + + .. versionadded:: 2.2 + Calls ``current_app.aborter`` if available instead of always + using Werkzeug's default ``abort``. + """ + if current_app: + current_app.aborter(code, *args, **kwargs) + + _wz_abort(code, *args, **kwargs) + + +def get_template_attribute(template_name: str, attribute: str) -> t.Any: + """Loads a macro (or variable) a template exports. This can be used to + invoke a macro from within Python code. If you for example have a + template named :file:`_cider.html` with the following contents: + + .. sourcecode:: html+jinja + + {% macro hello(name) %}Hello {{ name }}!{% endmacro %} + + You can access this from Python code like this:: + + hello = get_template_attribute('_cider.html', 'hello') + return hello('World') + + .. versionadded:: 0.2 + + :param template_name: the name of the template + :param attribute: the name of the variable of macro to access + """ + return getattr(current_app.jinja_env.get_template(template_name).module, attribute) + + +def flash(message: str, category: str = "message") -> None: + """Flashes a message to the next request. In order to remove the + flashed message from the session and to display it to the user, + the template has to call :func:`get_flashed_messages`. + + .. versionchanged:: 0.3 + `category` parameter added. + + :param message: the message to be flashed. + :param category: the category for the message. The following values + are recommended: ``'message'`` for any kind of message, + ``'error'`` for errors, ``'info'`` for information + messages and ``'warning'`` for warnings. However any + kind of string can be used as category. + """ + # Original implementation: + # + # session.setdefault('_flashes', []).append((category, message)) + # + # This assumed that changes made to mutable structures in the session are + # always in sync with the session object, which is not true for session + # implementations that use external storage for keeping their keys/values. + flashes = session.get("_flashes", []) + flashes.append((category, message)) + session["_flashes"] = flashes + app = current_app._get_current_object() # type: ignore + message_flashed.send( + app, + _async_wrapper=app.ensure_sync, + message=message, + category=category, + ) + + +def get_flashed_messages( + with_categories: bool = False, category_filter: t.Iterable[str] = () +) -> list[str] | list[tuple[str, str]]: + """Pulls all flashed messages from the session and returns them. + Further calls in the same request to the function will return + the same messages. By default just the messages are returned, + but when `with_categories` is set to ``True``, the return value will + be a list of tuples in the form ``(category, message)`` instead. + + Filter the flashed messages to one or more categories by providing those + categories in `category_filter`. This allows rendering categories in + separate html blocks. The `with_categories` and `category_filter` + arguments are distinct: + + * `with_categories` controls whether categories are returned with message + text (``True`` gives a tuple, where ``False`` gives just the message text). + * `category_filter` filters the messages down to only those matching the + provided categories. + + See :doc:`/patterns/flashing` for examples. + + .. versionchanged:: 0.3 + `with_categories` parameter added. + + .. versionchanged:: 0.9 + `category_filter` parameter added. + + :param with_categories: set to ``True`` to also receive categories. + :param category_filter: filter of categories to limit return values. Only + categories in the list will be returned. + """ + flashes = request_ctx.flashes + if flashes is None: + flashes = session.pop("_flashes") if "_flashes" in session else [] + request_ctx.flashes = flashes + if category_filter: + flashes = list(filter(lambda f: f[0] in category_filter, flashes)) + if not with_categories: + return [x[1] for x in flashes] + return flashes + + +def _prepare_send_file_kwargs(**kwargs: t.Any) -> dict[str, t.Any]: + if kwargs.get("max_age") is None: + kwargs["max_age"] = current_app.get_send_file_max_age + + kwargs.update( + environ=request.environ, + use_x_sendfile=current_app.config["USE_X_SENDFILE"], + response_class=current_app.response_class, + _root_path=current_app.root_path, # type: ignore + ) + return kwargs + + +def send_file( + path_or_file: os.PathLike[t.AnyStr] | str | t.BinaryIO, + mimetype: str | None = None, + as_attachment: bool = False, + download_name: str | None = None, + conditional: bool = True, + etag: bool | str = True, + last_modified: datetime | int | float | None = None, + max_age: None | (int | t.Callable[[str | None], int | None]) = None, +) -> Response: + """Send the contents of a file to the client. + + The first argument can be a file path or a file-like object. Paths + are preferred in most cases because Werkzeug can manage the file and + get extra information from the path. Passing a file-like object + requires that the file is opened in binary mode, and is mostly + useful when building a file in memory with :class:`io.BytesIO`. + + Never pass file paths provided by a user. The path is assumed to be + trusted, so a user could craft a path to access a file you didn't + intend. Use :func:`send_from_directory` to safely serve + user-requested paths from within a directory. + + If the WSGI server sets a ``file_wrapper`` in ``environ``, it is + used, otherwise Werkzeug's built-in wrapper is used. Alternatively, + if the HTTP server supports ``X-Sendfile``, configuring Flask with + ``USE_X_SENDFILE = True`` will tell the server to send the given + path, which is much more efficient than reading it in Python. + + :param path_or_file: The path to the file to send, relative to the + current working directory if a relative path is given. + Alternatively, a file-like object opened in binary mode. Make + sure the file pointer is seeked to the start of the data. + :param mimetype: The MIME type to send for the file. If not + provided, it will try to detect it from the file name. + :param as_attachment: Indicate to a browser that it should offer to + save the file instead of displaying it. + :param download_name: The default name browsers will use when saving + the file. Defaults to the passed file name. + :param conditional: Enable conditional and range responses based on + request headers. Requires passing a file path and ``environ``. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. + :param last_modified: The last modified time to send for the file, + in seconds. If not provided, it will try to detect it from the + file path. + :param max_age: How long the client should cache the file, in + seconds. If set, ``Cache-Control`` will be ``public``, otherwise + it will be ``no-cache`` to prefer conditional caching. + + .. versionchanged:: 2.0 + ``download_name`` replaces the ``attachment_filename`` + parameter. If ``as_attachment=False``, it is passed with + ``Content-Disposition: inline`` instead. + + .. versionchanged:: 2.0 + ``max_age`` replaces the ``cache_timeout`` parameter. + ``conditional`` is enabled and ``max_age`` is not set by + default. + + .. versionchanged:: 2.0 + ``etag`` replaces the ``add_etags`` parameter. It can be a + string to use instead of generating one. + + .. versionchanged:: 2.0 + Passing a file-like object that inherits from + :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather + than sending an empty file. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionchanged:: 1.1 + ``filename`` may be a :class:`~os.PathLike` object. + + .. versionchanged:: 1.1 + Passing a :class:`~io.BytesIO` object supports range requests. + + .. versionchanged:: 1.0.3 + Filenames are encoded with ASCII instead of Latin-1 for broader + compatibility with WSGI servers. + + .. versionchanged:: 1.0 + UTF-8 filenames as specified in :rfc:`2231` are supported. + + .. versionchanged:: 0.12 + The filename is no longer automatically inferred from file + objects. If you want to use automatic MIME and etag support, + pass a filename via ``filename_or_fp`` or + ``attachment_filename``. + + .. versionchanged:: 0.12 + ``attachment_filename`` is preferred over ``filename`` for MIME + detection. + + .. versionchanged:: 0.9 + ``cache_timeout`` defaults to + :meth:`Flask.get_send_file_max_age`. + + .. versionchanged:: 0.7 + MIME guessing and etag support for file-like objects was + removed because it was unreliable. Pass a filename if you are + able to, otherwise attach an etag yourself. + + .. versionchanged:: 0.5 + The ``add_etags``, ``cache_timeout`` and ``conditional`` + parameters were added. The default behavior is to add etags. + + .. versionadded:: 0.2 + """ + return werkzeug.utils.send_file( # type: ignore[return-value] + **_prepare_send_file_kwargs( + path_or_file=path_or_file, + environ=request.environ, + mimetype=mimetype, + as_attachment=as_attachment, + download_name=download_name, + conditional=conditional, + etag=etag, + last_modified=last_modified, + max_age=max_age, + ) + ) + + +def send_from_directory( + directory: os.PathLike[str] | str, + path: os.PathLike[str] | str, + **kwargs: t.Any, +) -> Response: + """Send a file from within a directory using :func:`send_file`. + + .. code-block:: python + + @app.route("/uploads/") + def download_file(name): + return send_from_directory( + app.config['UPLOAD_FOLDER'], name, as_attachment=True + ) + + This is a secure way to serve files from a folder, such as static + files or uploads. Uses :func:`~werkzeug.security.safe_join` to + ensure the path coming from the client is not maliciously crafted to + point outside the specified directory. + + If the final path does not point to an existing regular file, + raises a 404 :exc:`~werkzeug.exceptions.NotFound` error. + + :param directory: The directory that ``path`` must be located under, + relative to the current application's root path. + :param path: The path to the file to send, relative to + ``directory``. + :param kwargs: Arguments to pass to :func:`send_file`. + + .. versionchanged:: 2.0 + ``path`` replaces the ``filename`` parameter. + + .. versionadded:: 2.0 + Moved the implementation to Werkzeug. This is now a wrapper to + pass some Flask-specific arguments. + + .. versionadded:: 0.5 + """ + return werkzeug.utils.send_from_directory( # type: ignore[return-value] + directory, path, **_prepare_send_file_kwargs(**kwargs) + ) + + +def get_root_path(import_name: str) -> str: + """Find the root path of a package, or the path that contains a + module. If it cannot be found, returns the current working + directory. + + Not to be confused with the value returned by :func:`find_package`. + + :meta private: + """ + # Module already imported and has a file attribute. Use that first. + mod = sys.modules.get(import_name) + + if mod is not None and hasattr(mod, "__file__") and mod.__file__ is not None: + return os.path.dirname(os.path.abspath(mod.__file__)) + + # Next attempt: check the loader. + try: + spec = importlib.util.find_spec(import_name) + + if spec is None: + raise ValueError + except (ImportError, ValueError): + loader = None + else: + loader = spec.loader + + # Loader does not exist or we're referring to an unloaded main + # module or a main module without path (interactive sessions), go + # with the current working directory. + if loader is None: + return os.getcwd() + + if hasattr(loader, "get_filename"): + filepath = loader.get_filename(import_name) + else: + # Fall back to imports. + __import__(import_name) + mod = sys.modules[import_name] + filepath = getattr(mod, "__file__", None) + + # If we don't have a file path it might be because it is a + # namespace package. In this case pick the root path from the + # first module that is contained in the package. + if filepath is None: + raise RuntimeError( + "No root path can be found for the provided module" + f" {import_name!r}. This can happen because the module" + " came from an import hook that does not provide file" + " name information or because it's a namespace package." + " In this case the root path needs to be explicitly" + " provided." + ) + + # filepath is import_name.py for a module, or __init__.py for a package. + return os.path.dirname(os.path.abspath(filepath)) # type: ignore[no-any-return] + + +@lru_cache(maxsize=None) +def _split_blueprint_path(name: str) -> list[str]: + out: list[str] = [name] + + if "." in name: + out.extend(_split_blueprint_path(name.rpartition(".")[0])) + + return out diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/__init__.py new file mode 100644 index 000000000..c0941d049 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/__init__.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import json as _json +import typing as t + +from ..globals import current_app +from .provider import _default + +if t.TYPE_CHECKING: # pragma: no cover + from ..wrappers import Response + + +def dumps(obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.dumps() ` + method, otherwise it will use :func:`json.dumps`. + + :param obj: The data to serialize. + :param kwargs: Arguments passed to the ``dumps`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.dumps``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0.2 + :class:`decimal.Decimal` is supported by converting to a string. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + if current_app: + return current_app.json.dumps(obj, **kwargs) + + kwargs.setdefault("default", _default) + return _json.dumps(obj, **kwargs) + + +def dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: + """Serialize data as JSON and write to a file. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.dump() ` + method, otherwise it will use :func:`json.dump`. + + :param obj: The data to serialize. + :param fp: A file opened for writing text. Should use the UTF-8 + encoding to be valid JSON. + :param kwargs: Arguments passed to the ``dump`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.dump``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0 + Writing to a binary file, and the ``encoding`` argument, will be + removed in Flask 2.1. + """ + if current_app: + current_app.json.dump(obj, fp, **kwargs) + else: + kwargs.setdefault("default", _default) + _json.dump(obj, fp, **kwargs) + + +def loads(s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.loads() ` + method, otherwise it will use :func:`json.loads`. + + :param s: Text or UTF-8 bytes. + :param kwargs: Arguments passed to the ``loads`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.loads``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. The data must be a + string or UTF-8 bytes. + + .. versionchanged:: 1.0.3 + ``app`` can be passed directly, rather than requiring an app + context for configuration. + """ + if current_app: + return current_app.json.loads(s, **kwargs) + + return _json.loads(s, **kwargs) + + +def load(fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON read from a file. + + If :data:`~flask.current_app` is available, it will use its + :meth:`app.json.load() ` + method, otherwise it will use :func:`json.load`. + + :param fp: A file opened for reading text or UTF-8 bytes. + :param kwargs: Arguments passed to the ``load`` implementation. + + .. versionchanged:: 2.3 + The ``app`` parameter was removed. + + .. versionchanged:: 2.2 + Calls ``current_app.json.load``, allowing an app to override + the behavior. + + .. versionchanged:: 2.2 + The ``app`` parameter will be removed in Flask 2.3. + + .. versionchanged:: 2.0 + ``encoding`` will be removed in Flask 2.1. The file must be text + mode, or binary mode with UTF-8 bytes. + """ + if current_app: + return current_app.json.load(fp, **kwargs) + + return _json.load(fp, **kwargs) + + +def jsonify(*args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with the ``application/json`` + mimetype. A dict or list returned from a view will be converted to a + JSON response automatically without needing to call this. + + This requires an active request or application context, and calls + :meth:`app.json.response() `. + + In debug mode, the output is formatted with indentation to make it + easier to read. This may also be controlled by the provider. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + + .. versionchanged:: 2.2 + Calls ``current_app.json.response``, allowing an app to override + the behavior. + + .. versionchanged:: 2.0.2 + :class:`decimal.Decimal` is supported by converting to a string. + + .. versionchanged:: 0.11 + Added support for serializing top-level arrays. This was a + security risk in ancient browsers. See :ref:`security-json`. + + .. versionadded:: 0.2 + """ + return current_app.json.response(*args, **kwargs) # type: ignore[return-value] diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/provider.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/provider.py new file mode 100644 index 000000000..f9b2e8ff5 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/provider.py @@ -0,0 +1,215 @@ +from __future__ import annotations + +import dataclasses +import decimal +import json +import typing as t +import uuid +import weakref +from datetime import date + +from werkzeug.http import http_date + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.sansio.response import Response + + from ..sansio.app import App + + +class JSONProvider: + """A standard set of JSON operations for an application. Subclasses + of this can be used to customize JSON behavior or use different + JSON libraries. + + To implement a provider for a specific library, subclass this base + class and implement at least :meth:`dumps` and :meth:`loads`. All + other methods have default implementations. + + To use a different provider, either subclass ``Flask`` and set + :attr:`~flask.Flask.json_provider_class` to a provider class, or set + :attr:`app.json ` to an instance of the class. + + :param app: An application instance. This will be stored as a + :class:`weakref.proxy` on the :attr:`_app` attribute. + + .. versionadded:: 2.2 + """ + + def __init__(self, app: App) -> None: + self._app: App = weakref.proxy(app) + + def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON. + + :param obj: The data to serialize. + :param kwargs: May be passed to the underlying JSON library. + """ + raise NotImplementedError + + def dump(self, obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: + """Serialize data as JSON and write to a file. + + :param obj: The data to serialize. + :param fp: A file opened for writing text. Should use the UTF-8 + encoding to be valid JSON. + :param kwargs: May be passed to the underlying JSON library. + """ + fp.write(self.dumps(obj, **kwargs)) + + def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON. + + :param s: Text or UTF-8 bytes. + :param kwargs: May be passed to the underlying JSON library. + """ + raise NotImplementedError + + def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON read from a file. + + :param fp: A file opened for reading text or UTF-8 bytes. + :param kwargs: May be passed to the underlying JSON library. + """ + return self.loads(fp.read(), **kwargs) + + def _prepare_response_obj( + self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] + ) -> t.Any: + if args and kwargs: + raise TypeError("app.json.response() takes either args or kwargs, not both") + + if not args and not kwargs: + return None + + if len(args) == 1: + return args[0] + + return args or kwargs + + def response(self, *args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with the ``application/json`` + mimetype. + + The :func:`~flask.json.jsonify` function calls this method for + the current application. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + """ + obj = self._prepare_response_obj(args, kwargs) + return self._app.response_class(self.dumps(obj), mimetype="application/json") + + +def _default(o: t.Any) -> t.Any: + if isinstance(o, date): + return http_date(o) + + if isinstance(o, (decimal.Decimal, uuid.UUID)): + return str(o) + + if dataclasses and dataclasses.is_dataclass(o): + return dataclasses.asdict(o) + + if hasattr(o, "__html__"): + return str(o.__html__()) + + raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable") + + +class DefaultJSONProvider(JSONProvider): + """Provide JSON operations using Python's built-in :mod:`json` + library. Serializes the following additional data types: + + - :class:`datetime.datetime` and :class:`datetime.date` are + serialized to :rfc:`822` strings. This is the same as the HTTP + date format. + - :class:`uuid.UUID` is serialized to a string. + - :class:`dataclasses.dataclass` is passed to + :func:`dataclasses.asdict`. + - :class:`~markupsafe.Markup` (or any object with a ``__html__`` + method) will call the ``__html__`` method to get a string. + """ + + default: t.Callable[[t.Any], t.Any] = staticmethod(_default) # type: ignore[assignment] + """Apply this function to any object that :meth:`json.dumps` does + not know how to serialize. It should return a valid JSON type or + raise a ``TypeError``. + """ + + ensure_ascii = True + """Replace non-ASCII characters with escape sequences. This may be + more compatible with some clients, but can be disabled for better + performance and size. + """ + + sort_keys = True + """Sort the keys in any serialized dicts. This may be useful for + some caching situations, but can be disabled for better performance. + When enabled, keys must all be strings, they are not converted + before sorting. + """ + + compact: bool | None = None + """If ``True``, or ``None`` out of debug mode, the :meth:`response` + output will not add indentation, newlines, or spaces. If ``False``, + or ``None`` in debug mode, it will use a non-compact representation. + """ + + mimetype = "application/json" + """The mimetype set in :meth:`response`.""" + + def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: + """Serialize data as JSON to a string. + + Keyword arguments are passed to :func:`json.dumps`. Sets some + parameter defaults from the :attr:`default`, + :attr:`ensure_ascii`, and :attr:`sort_keys` attributes. + + :param obj: The data to serialize. + :param kwargs: Passed to :func:`json.dumps`. + """ + kwargs.setdefault("default", self.default) + kwargs.setdefault("ensure_ascii", self.ensure_ascii) + kwargs.setdefault("sort_keys", self.sort_keys) + return json.dumps(obj, **kwargs) + + def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: + """Deserialize data as JSON from a string or bytes. + + :param s: Text or UTF-8 bytes. + :param kwargs: Passed to :func:`json.loads`. + """ + return json.loads(s, **kwargs) + + def response(self, *args: t.Any, **kwargs: t.Any) -> Response: + """Serialize the given arguments as JSON, and return a + :class:`~flask.Response` object with it. The response mimetype + will be "application/json" and can be changed with + :attr:`mimetype`. + + If :attr:`compact` is ``False`` or debug mode is enabled, the + output will be formatted to be easier to read. + + Either positional or keyword arguments can be given, not both. + If no arguments are given, ``None`` is serialized. + + :param args: A single value to serialize, or multiple values to + treat as a list to serialize. + :param kwargs: Treat as a dict to serialize. + """ + obj = self._prepare_response_obj(args, kwargs) + dump_args: dict[str, t.Any] = {} + + if (self.compact is None and self._app.debug) or self.compact is False: + dump_args.setdefault("indent", 2) + else: + dump_args.setdefault("separators", (",", ":")) + + return self._app.response_class( + f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype + ) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/tag.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/tag.py new file mode 100644 index 000000000..8dc3629bf --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/json/tag.py @@ -0,0 +1,327 @@ +""" +Tagged JSON +~~~~~~~~~~~ + +A compact representation for lossless serialization of non-standard JSON +types. :class:`~flask.sessions.SecureCookieSessionInterface` uses this +to serialize the session data, but it may be useful in other places. It +can be extended to support other types. + +.. autoclass:: TaggedJSONSerializer + :members: + +.. autoclass:: JSONTag + :members: + +Let's see an example that adds support for +:class:`~collections.OrderedDict`. Dicts don't have an order in JSON, so +to handle this we will dump the items as a list of ``[key, value]`` +pairs. Subclass :class:`JSONTag` and give it the new key ``' od'`` to +identify the type. The session serializer processes dicts first, so +insert the new tag at the front of the order since ``OrderedDict`` must +be processed before ``dict``. + +.. code-block:: python + + from flask.json.tag import JSONTag + + class TagOrderedDict(JSONTag): + __slots__ = ('serializer',) + key = ' od' + + def check(self, value): + return isinstance(value, OrderedDict) + + def to_json(self, value): + return [[k, self.serializer.tag(v)] for k, v in iteritems(value)] + + def to_python(self, value): + return OrderedDict(value) + + app.session_interface.serializer.register(TagOrderedDict, index=0) +""" + +from __future__ import annotations + +import typing as t +from base64 import b64decode +from base64 import b64encode +from datetime import datetime +from uuid import UUID + +from markupsafe import Markup +from werkzeug.http import http_date +from werkzeug.http import parse_date + +from ..json import dumps +from ..json import loads + + +class JSONTag: + """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" + + __slots__ = ("serializer",) + + #: The tag to mark the serialized object with. If empty, this tag is + #: only used as an intermediate step during tagging. + key: str = "" + + def __init__(self, serializer: TaggedJSONSerializer) -> None: + """Create a tagger for the given serializer.""" + self.serializer = serializer + + def check(self, value: t.Any) -> bool: + """Check if the given value should be tagged by this tag.""" + raise NotImplementedError + + def to_json(self, value: t.Any) -> t.Any: + """Convert the Python object to an object that is a valid JSON type. + The tag will be added later.""" + raise NotImplementedError + + def to_python(self, value: t.Any) -> t.Any: + """Convert the JSON representation back to the correct type. The tag + will already be removed.""" + raise NotImplementedError + + def tag(self, value: t.Any) -> dict[str, t.Any]: + """Convert the value to a valid JSON type and add the tag structure + around it.""" + return {self.key: self.to_json(value)} + + +class TagDict(JSONTag): + """Tag for 1-item dicts whose only key matches a registered tag. + + Internally, the dict key is suffixed with `__`, and the suffix is removed + when deserializing. + """ + + __slots__ = () + key = " di" + + def check(self, value: t.Any) -> bool: + return ( + isinstance(value, dict) + and len(value) == 1 + and next(iter(value)) in self.serializer.tags + ) + + def to_json(self, value: t.Any) -> t.Any: + key = next(iter(value)) + return {f"{key}__": self.serializer.tag(value[key])} + + def to_python(self, value: t.Any) -> t.Any: + key = next(iter(value)) + return {key[:-2]: value[key]} + + +class PassDict(JSONTag): + __slots__ = () + + def check(self, value: t.Any) -> bool: + return isinstance(value, dict) + + def to_json(self, value: t.Any) -> t.Any: + # JSON objects may only have string keys, so don't bother tagging the + # key here. + return {k: self.serializer.tag(v) for k, v in value.items()} + + tag = to_json + + +class TagTuple(JSONTag): + __slots__ = () + key = " t" + + def check(self, value: t.Any) -> bool: + return isinstance(value, tuple) + + def to_json(self, value: t.Any) -> t.Any: + return [self.serializer.tag(item) for item in value] + + def to_python(self, value: t.Any) -> t.Any: + return tuple(value) + + +class PassList(JSONTag): + __slots__ = () + + def check(self, value: t.Any) -> bool: + return isinstance(value, list) + + def to_json(self, value: t.Any) -> t.Any: + return [self.serializer.tag(item) for item in value] + + tag = to_json + + +class TagBytes(JSONTag): + __slots__ = () + key = " b" + + def check(self, value: t.Any) -> bool: + return isinstance(value, bytes) + + def to_json(self, value: t.Any) -> t.Any: + return b64encode(value).decode("ascii") + + def to_python(self, value: t.Any) -> t.Any: + return b64decode(value) + + +class TagMarkup(JSONTag): + """Serialize anything matching the :class:`~markupsafe.Markup` API by + having a ``__html__`` method to the result of that method. Always + deserializes to an instance of :class:`~markupsafe.Markup`.""" + + __slots__ = () + key = " m" + + def check(self, value: t.Any) -> bool: + return callable(getattr(value, "__html__", None)) + + def to_json(self, value: t.Any) -> t.Any: + return str(value.__html__()) + + def to_python(self, value: t.Any) -> t.Any: + return Markup(value) + + +class TagUUID(JSONTag): + __slots__ = () + key = " u" + + def check(self, value: t.Any) -> bool: + return isinstance(value, UUID) + + def to_json(self, value: t.Any) -> t.Any: + return value.hex + + def to_python(self, value: t.Any) -> t.Any: + return UUID(value) + + +class TagDateTime(JSONTag): + __slots__ = () + key = " d" + + def check(self, value: t.Any) -> bool: + return isinstance(value, datetime) + + def to_json(self, value: t.Any) -> t.Any: + return http_date(value) + + def to_python(self, value: t.Any) -> t.Any: + return parse_date(value) + + +class TaggedJSONSerializer: + """Serializer that uses a tag system to compactly represent objects that + are not JSON types. Passed as the intermediate serializer to + :class:`itsdangerous.Serializer`. + + The following extra types are supported: + + * :class:`dict` + * :class:`tuple` + * :class:`bytes` + * :class:`~markupsafe.Markup` + * :class:`~uuid.UUID` + * :class:`~datetime.datetime` + """ + + __slots__ = ("tags", "order") + + #: Tag classes to bind when creating the serializer. Other tags can be + #: added later using :meth:`~register`. + default_tags = [ + TagDict, + PassDict, + TagTuple, + PassList, + TagBytes, + TagMarkup, + TagUUID, + TagDateTime, + ] + + def __init__(self) -> None: + self.tags: dict[str, JSONTag] = {} + self.order: list[JSONTag] = [] + + for cls in self.default_tags: + self.register(cls) + + def register( + self, + tag_class: type[JSONTag], + force: bool = False, + index: int | None = None, + ) -> None: + """Register a new tag with this serializer. + + :param tag_class: tag class to register. Will be instantiated with this + serializer instance. + :param force: overwrite an existing tag. If false (default), a + :exc:`KeyError` is raised. + :param index: index to insert the new tag in the tag order. Useful when + the new tag is a special case of an existing tag. If ``None`` + (default), the tag is appended to the end of the order. + + :raise KeyError: if the tag key is already registered and ``force`` is + not true. + """ + tag = tag_class(self) + key = tag.key + + if key: + if not force and key in self.tags: + raise KeyError(f"Tag '{key}' is already registered.") + + self.tags[key] = tag + + if index is None: + self.order.append(tag) + else: + self.order.insert(index, tag) + + def tag(self, value: t.Any) -> t.Any: + """Convert a value to a tagged representation if necessary.""" + for tag in self.order: + if tag.check(value): + return tag.tag(value) + + return value + + def untag(self, value: dict[str, t.Any]) -> t.Any: + """Convert a tagged representation back to the original type.""" + if len(value) != 1: + return value + + key = next(iter(value)) + + if key not in self.tags: + return value + + return self.tags[key].to_python(value[key]) + + def _untag_scan(self, value: t.Any) -> t.Any: + if isinstance(value, dict): + # untag each item recursively + value = {k: self._untag_scan(v) for k, v in value.items()} + # untag the dict itself + value = self.untag(value) + elif isinstance(value, list): + # untag each item recursively + value = [self._untag_scan(item) for item in value] + + return value + + def dumps(self, value: t.Any) -> str: + """Tag the value and dump it to a compact JSON string.""" + return dumps(self.tag(value), separators=(",", ":")) + + def loads(self, value: str) -> t.Any: + """Load data from a JSON string and deserialized any tagged objects.""" + return self._untag_scan(loads(value)) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/logging.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/logging.py new file mode 100644 index 000000000..0cb8f4374 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/logging.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import logging +import sys +import typing as t + +from werkzeug.local import LocalProxy + +from .globals import request + +if t.TYPE_CHECKING: # pragma: no cover + from .sansio.app import App + + +@LocalProxy +def wsgi_errors_stream() -> t.TextIO: + """Find the most appropriate error stream for the application. If a request + is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. + + If you configure your own :class:`logging.StreamHandler`, you may want to + use this for the stream. If you are using file or dict configuration and + can't import this directly, you can refer to it as + ``ext://flask.logging.wsgi_errors_stream``. + """ + if request: + return request.environ["wsgi.errors"] # type: ignore[no-any-return] + + return sys.stderr + + +def has_level_handler(logger: logging.Logger) -> bool: + """Check if there is a handler in the logging chain that will handle the + given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. + """ + level = logger.getEffectiveLevel() + current = logger + + while current: + if any(handler.level <= level for handler in current.handlers): + return True + + if not current.propagate: + break + + current = current.parent # type: ignore + + return False + + +#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format +#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. +default_handler = logging.StreamHandler(wsgi_errors_stream) # type: ignore +default_handler.setFormatter( + logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s") +) + + +def create_logger(app: App) -> logging.Logger: + """Get the Flask app's logger and configure it if needed. + + The logger name will be the same as + :attr:`app.import_name `. + + When :attr:`~flask.Flask.debug` is enabled, set the logger level to + :data:`logging.DEBUG` if it is not set. + + If there is no handler for the logger's effective level, add a + :class:`~logging.StreamHandler` for + :func:`~flask.logging.wsgi_errors_stream` with a basic format. + """ + logger = logging.getLogger(app.name) + + if app.debug and not logger.level: + logger.setLevel(logging.DEBUG) + + if not has_level_handler(logger): + logger.addHandler(default_handler) + + return logger diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/py.typed b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/README.md b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/README.md new file mode 100644 index 000000000..623ac1982 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/README.md @@ -0,0 +1,6 @@ +# Sansio + +This folder contains code that can be used by alternative Flask +implementations, for example Quart. The code therefore cannot do any +IO, nor be part of a likely IO path. Finally this code cannot use the +Flask globals. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/app.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/app.py new file mode 100644 index 000000000..01fd5dbfa --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/app.py @@ -0,0 +1,964 @@ +from __future__ import annotations + +import logging +import os +import sys +import typing as t +from datetime import timedelta +from itertools import chain + +from werkzeug.exceptions import Aborter +from werkzeug.exceptions import BadRequest +from werkzeug.exceptions import BadRequestKeyError +from werkzeug.routing import BuildError +from werkzeug.routing import Map +from werkzeug.routing import Rule +from werkzeug.sansio.response import Response +from werkzeug.utils import cached_property +from werkzeug.utils import redirect as _wz_redirect + +from .. import typing as ft +from ..config import Config +from ..config import ConfigAttribute +from ..ctx import _AppCtxGlobals +from ..helpers import _split_blueprint_path +from ..helpers import get_debug_flag +from ..json.provider import DefaultJSONProvider +from ..json.provider import JSONProvider +from ..logging import create_logger +from ..templating import DispatchingJinjaLoader +from ..templating import Environment +from .scaffold import _endpoint_from_view_func +from .scaffold import find_package +from .scaffold import Scaffold +from .scaffold import setupmethod + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.wrappers import Response as BaseResponse + + from ..testing import FlaskClient + from ..testing import FlaskCliRunner + from .blueprints import Blueprint + +T_shell_context_processor = t.TypeVar( + "T_shell_context_processor", bound=ft.ShellContextProcessorCallable +) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) +T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) +T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) + + +def _make_timedelta(value: timedelta | int | None) -> timedelta | None: + if value is None or isinstance(value, timedelta): + return value + + return timedelta(seconds=value) + + +class App(Scaffold): + """The flask object implements a WSGI application and acts as the central + object. It is passed the name of the module or package of the + application. Once it is created it will act as a central registry for + the view functions, the URL rules, template configuration and much more. + + The name of the package is used to resolve resources from inside the + package or the folder the module is contained in depending on if the + package parameter resolves to an actual python package (a folder with + an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). + + For more information about resource loading, see :func:`open_resource`. + + Usually you create a :class:`Flask` instance in your main module or + in the :file:`__init__.py` file of your package like this:: + + from flask import Flask + app = Flask(__name__) + + .. admonition:: About the First Parameter + + The idea of the first parameter is to give Flask an idea of what + belongs to your application. This name is used to find resources + on the filesystem, can be used by extensions to improve debugging + information and a lot more. + + So it's important what you provide there. If you are using a single + module, `__name__` is always the correct value. If you however are + using a package, it's usually recommended to hardcode the name of + your package there. + + For example if your application is defined in :file:`yourapplication/app.py` + you should create it with one of the two versions below:: + + app = Flask('yourapplication') + app = Flask(__name__.split('.')[0]) + + Why is that? The application will work even with `__name__`, thanks + to how resources are looked up. However it will make debugging more + painful. Certain extensions can make assumptions based on the + import name of your application. For example the Flask-SQLAlchemy + extension will look for the code in your application that triggered + an SQL query in debug mode. If the import name is not properly set + up, that debugging information is lost. (For example it would only + pick up SQL queries in `yourapplication.app` and not + `yourapplication.views.frontend`) + + .. versionadded:: 0.7 + The `static_url_path`, `static_folder`, and `template_folder` + parameters were added. + + .. versionadded:: 0.8 + The `instance_path` and `instance_relative_config` parameters were + added. + + .. versionadded:: 0.11 + The `root_path` parameter was added. + + .. versionadded:: 1.0 + The ``host_matching`` and ``static_host`` parameters were added. + + .. versionadded:: 1.0 + The ``subdomain_matching`` parameter was added. Subdomain + matching needs to be enabled manually now. Setting + :data:`SERVER_NAME` does not implicitly enable it. + + :param import_name: the name of the application package + :param static_url_path: can be used to specify a different path for the + static files on the web. Defaults to the name + of the `static_folder` folder. + :param static_folder: The folder with static files that is served at + ``static_url_path``. Relative to the application ``root_path`` + or an absolute path. Defaults to ``'static'``. + :param static_host: the host to use when adding the static route. + Defaults to None. Required when using ``host_matching=True`` + with a ``static_folder`` configured. + :param host_matching: set ``url_map.host_matching`` attribute. + Defaults to False. + :param subdomain_matching: consider the subdomain relative to + :data:`SERVER_NAME` when matching routes. Defaults to False. + :param template_folder: the folder that contains the templates that should + be used by the application. Defaults to + ``'templates'`` folder in the root path of the + application. + :param instance_path: An alternative instance path for the application. + By default the folder ``'instance'`` next to the + package or module is assumed to be the instance + path. + :param instance_relative_config: if set to ``True`` relative filenames + for loading the config are assumed to + be relative to the instance path instead + of the application root. + :param root_path: The path to the root of the application files. + This should only be set manually when it can't be detected + automatically, such as for namespace packages. + """ + + #: The class of the object assigned to :attr:`aborter`, created by + #: :meth:`create_aborter`. That object is called by + #: :func:`flask.abort` to raise HTTP errors, and can be + #: called directly as well. + #: + #: Defaults to :class:`werkzeug.exceptions.Aborter`. + #: + #: .. versionadded:: 2.2 + aborter_class = Aborter + + #: The class that is used for the Jinja environment. + #: + #: .. versionadded:: 0.11 + jinja_environment = Environment + + #: The class that is used for the :data:`~flask.g` instance. + #: + #: Example use cases for a custom class: + #: + #: 1. Store arbitrary attributes on flask.g. + #: 2. Add a property for lazy per-request database connectors. + #: 3. Return None instead of AttributeError on unexpected attributes. + #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. + #: + #: In Flask 0.9 this property was called `request_globals_class` but it + #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the + #: flask.g object is now application context scoped. + #: + #: .. versionadded:: 0.10 + app_ctx_globals_class = _AppCtxGlobals + + #: The class that is used for the ``config`` attribute of this app. + #: Defaults to :class:`~flask.Config`. + #: + #: Example use cases for a custom class: + #: + #: 1. Default values for certain config options. + #: 2. Access to config values through attributes in addition to keys. + #: + #: .. versionadded:: 0.11 + config_class = Config + + #: The testing flag. Set this to ``True`` to enable the test mode of + #: Flask extensions (and in the future probably also Flask itself). + #: For example this might activate test helpers that have an + #: additional runtime cost which should not be enabled by default. + #: + #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the + #: default it's implicitly enabled. + #: + #: This attribute can also be configured from the config with the + #: ``TESTING`` configuration key. Defaults to ``False``. + testing = ConfigAttribute[bool]("TESTING") + + #: If a secret key is set, cryptographic components can use this to + #: sign cookies and other things. Set this to a complex random value + #: when you want to use the secure cookie for instance. + #: + #: This attribute can also be configured from the config with the + #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. + secret_key = ConfigAttribute[t.Union[str, bytes, None]]("SECRET_KEY") + + #: A :class:`~datetime.timedelta` which is used to set the expiration + #: date of a permanent session. The default is 31 days which makes a + #: permanent session survive for roughly one month. + #: + #: This attribute can also be configured from the config with the + #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to + #: ``timedelta(days=31)`` + permanent_session_lifetime = ConfigAttribute[timedelta]( + "PERMANENT_SESSION_LIFETIME", + get_converter=_make_timedelta, # type: ignore[arg-type] + ) + + json_provider_class: type[JSONProvider] = DefaultJSONProvider + """A subclass of :class:`~flask.json.provider.JSONProvider`. An + instance is created and assigned to :attr:`app.json` when creating + the app. + + The default, :class:`~flask.json.provider.DefaultJSONProvider`, uses + Python's built-in :mod:`json` library. A different provider can use + a different JSON library. + + .. versionadded:: 2.2 + """ + + #: Options that are passed to the Jinja environment in + #: :meth:`create_jinja_environment`. Changing these options after + #: the environment is created (accessing :attr:`jinja_env`) will + #: have no effect. + #: + #: .. versionchanged:: 1.1.0 + #: This is a ``dict`` instead of an ``ImmutableDict`` to allow + #: easier configuration. + #: + jinja_options: dict[str, t.Any] = {} + + #: The rule object to use for URL rules created. This is used by + #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. + #: + #: .. versionadded:: 0.7 + url_rule_class = Rule + + #: The map object to use for storing the URL rules and routing + #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`. + #: + #: .. versionadded:: 1.1.0 + url_map_class = Map + + #: The :meth:`test_client` method creates an instance of this test + #: client class. Defaults to :class:`~flask.testing.FlaskClient`. + #: + #: .. versionadded:: 0.7 + test_client_class: type[FlaskClient] | None = None + + #: The :class:`~click.testing.CliRunner` subclass, by default + #: :class:`~flask.testing.FlaskCliRunner` that is used by + #: :meth:`test_cli_runner`. Its ``__init__`` method should take a + #: Flask app object as the first argument. + #: + #: .. versionadded:: 1.0 + test_cli_runner_class: type[FlaskCliRunner] | None = None + + default_config: dict[str, t.Any] + response_class: type[Response] + + def __init__( + self, + import_name: str, + static_url_path: str | None = None, + static_folder: str | os.PathLike[str] | None = "static", + static_host: str | None = None, + host_matching: bool = False, + subdomain_matching: bool = False, + template_folder: str | os.PathLike[str] | None = "templates", + instance_path: str | None = None, + instance_relative_config: bool = False, + root_path: str | None = None, + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if instance_path is None: + instance_path = self.auto_find_instance_path() + elif not os.path.isabs(instance_path): + raise ValueError( + "If an instance path is provided it must be absolute." + " A relative path was given instead." + ) + + #: Holds the path to the instance folder. + #: + #: .. versionadded:: 0.8 + self.instance_path = instance_path + + #: The configuration dictionary as :class:`Config`. This behaves + #: exactly like a regular dictionary but supports additional methods + #: to load a config from files. + self.config = self.make_config(instance_relative_config) + + #: An instance of :attr:`aborter_class` created by + #: :meth:`make_aborter`. This is called by :func:`flask.abort` + #: to raise HTTP errors, and can be called directly as well. + #: + #: .. versionadded:: 2.2 + #: Moved from ``flask.abort``, which calls this object. + self.aborter = self.make_aborter() + + self.json: JSONProvider = self.json_provider_class(self) + """Provides access to JSON methods. Functions in ``flask.json`` + will call methods on this provider when the application context + is active. Used for handling JSON requests and responses. + + An instance of :attr:`json_provider_class`. Can be customized by + changing that attribute on a subclass, or by assigning to this + attribute afterwards. + + The default, :class:`~flask.json.provider.DefaultJSONProvider`, + uses Python's built-in :mod:`json` library. A different provider + can use a different JSON library. + + .. versionadded:: 2.2 + """ + + #: A list of functions that are called by + #: :meth:`handle_url_build_error` when :meth:`.url_for` raises a + #: :exc:`~werkzeug.routing.BuildError`. Each function is called + #: with ``error``, ``endpoint`` and ``values``. If a function + #: returns ``None`` or raises a ``BuildError``, it is skipped. + #: Otherwise, its return value is returned by ``url_for``. + #: + #: .. versionadded:: 0.9 + self.url_build_error_handlers: list[ + t.Callable[[Exception, str, dict[str, t.Any]], str] + ] = [] + + #: A list of functions that are called when the application context + #: is destroyed. Since the application context is also torn down + #: if the request ends this is the place to store code that disconnects + #: from databases. + #: + #: .. versionadded:: 0.9 + self.teardown_appcontext_funcs: list[ft.TeardownCallable] = [] + + #: A list of shell context processor functions that should be run + #: when a shell context is created. + #: + #: .. versionadded:: 0.11 + self.shell_context_processors: list[ft.ShellContextProcessorCallable] = [] + + #: Maps registered blueprint names to blueprint objects. The + #: dict retains the order the blueprints were registered in. + #: Blueprints can be registered multiple times, this dict does + #: not track how often they were attached. + #: + #: .. versionadded:: 0.7 + self.blueprints: dict[str, Blueprint] = {} + + #: a place where extensions can store application specific state. For + #: example this is where an extension could store database engines and + #: similar things. + #: + #: The key must match the name of the extension module. For example in + #: case of a "Flask-Foo" extension in `flask_foo`, the key would be + #: ``'foo'``. + #: + #: .. versionadded:: 0.7 + self.extensions: dict[str, t.Any] = {} + + #: The :class:`~werkzeug.routing.Map` for this instance. You can use + #: this to change the routing converters after the class was created + #: but before any routes are connected. Example:: + #: + #: from werkzeug.routing import BaseConverter + #: + #: class ListConverter(BaseConverter): + #: def to_python(self, value): + #: return value.split(',') + #: def to_url(self, values): + #: return ','.join(super(ListConverter, self).to_url(value) + #: for value in values) + #: + #: app = Flask(__name__) + #: app.url_map.converters['list'] = ListConverter + self.url_map = self.url_map_class(host_matching=host_matching) + + self.subdomain_matching = subdomain_matching + + # tracks internally if the application already handled at least one + # request. + self._got_first_request = False + + def _check_setup_finished(self, f_name: str) -> None: + if self._got_first_request: + raise AssertionError( + f"The setup method '{f_name}' can no longer be called" + " on the application. It has already handled its first" + " request, any changes will not be applied" + " consistently.\n" + "Make sure all imports, decorators, functions, etc." + " needed to set up the application are done before" + " running it." + ) + + @cached_property + def name(self) -> str: # type: ignore + """The name of the application. This is usually the import name + with the difference that it's guessed from the run file if the + import name is main. This name is used as a display name when + Flask needs the name of the application. It can be set and overridden + to change the value. + + .. versionadded:: 0.8 + """ + if self.import_name == "__main__": + fn: str | None = getattr(sys.modules["__main__"], "__file__", None) + if fn is None: + return "__main__" + return os.path.splitext(os.path.basename(fn))[0] + return self.import_name + + @cached_property + def logger(self) -> logging.Logger: + """A standard Python :class:`~logging.Logger` for the app, with + the same name as :attr:`name`. + + In debug mode, the logger's :attr:`~logging.Logger.level` will + be set to :data:`~logging.DEBUG`. + + If there are no handlers configured, a default handler will be + added. See :doc:`/logging` for more information. + + .. versionchanged:: 1.1.0 + The logger takes the same name as :attr:`name` rather than + hard-coding ``"flask.app"``. + + .. versionchanged:: 1.0.0 + Behavior was simplified. The logger is always named + ``"flask.app"``. The level is only set during configuration, + it doesn't check ``app.debug`` each time. Only one format is + used, not different ones depending on ``app.debug``. No + handlers are removed, and a handler is only added if no + handlers are already configured. + + .. versionadded:: 0.3 + """ + return create_logger(self) + + @cached_property + def jinja_env(self) -> Environment: + """The Jinja environment used to load templates. + + The environment is created the first time this property is + accessed. Changing :attr:`jinja_options` after that will have no + effect. + """ + return self.create_jinja_environment() + + def create_jinja_environment(self) -> Environment: + raise NotImplementedError() + + def make_config(self, instance_relative: bool = False) -> Config: + """Used to create the config attribute by the Flask constructor. + The `instance_relative` parameter is passed in from the constructor + of Flask (there named `instance_relative_config`) and indicates if + the config should be relative to the instance path or the root path + of the application. + + .. versionadded:: 0.8 + """ + root_path = self.root_path + if instance_relative: + root_path = self.instance_path + defaults = dict(self.default_config) + defaults["DEBUG"] = get_debug_flag() + return self.config_class(root_path, defaults) + + def make_aborter(self) -> Aborter: + """Create the object to assign to :attr:`aborter`. That object + is called by :func:`flask.abort` to raise HTTP errors, and can + be called directly as well. + + By default, this creates an instance of :attr:`aborter_class`, + which defaults to :class:`werkzeug.exceptions.Aborter`. + + .. versionadded:: 2.2 + """ + return self.aborter_class() + + def auto_find_instance_path(self) -> str: + """Tries to locate the instance path if it was not provided to the + constructor of the application class. It will basically calculate + the path to a folder named ``instance`` next to your main file or + the package. + + .. versionadded:: 0.8 + """ + prefix, package_path = find_package(self.import_name) + if prefix is None: + return os.path.join(package_path, "instance") + return os.path.join(prefix, "var", f"{self.name}-instance") + + def create_global_jinja_loader(self) -> DispatchingJinjaLoader: + """Creates the loader for the Jinja2 environment. Can be used to + override just the loader and keeping the rest unchanged. It's + discouraged to override this function. Instead one should override + the :meth:`jinja_loader` function instead. + + The global loader dispatches between the loaders of the application + and the individual blueprints. + + .. versionadded:: 0.7 + """ + return DispatchingJinjaLoader(self) + + def select_jinja_autoescape(self, filename: str) -> bool: + """Returns ``True`` if autoescaping should be active for the given + template name. If no template name is given, returns `True`. + + .. versionchanged:: 2.2 + Autoescaping is now enabled by default for ``.svg`` files. + + .. versionadded:: 0.5 + """ + if filename is None: + return True + return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg")) + + @property + def debug(self) -> bool: + """Whether debug mode is enabled. When using ``flask run`` to start the + development server, an interactive debugger will be shown for unhandled + exceptions, and the server will be reloaded when code changes. This maps to the + :data:`DEBUG` config key. It may not behave as expected if set late. + + **Do not enable debug mode when deploying in production.** + + Default: ``False`` + """ + return self.config["DEBUG"] # type: ignore[no-any-return] + + @debug.setter + def debug(self, value: bool) -> None: + self.config["DEBUG"] = value + + if self.config["TEMPLATES_AUTO_RELOAD"] is None: + self.jinja_env.auto_reload = value + + @setupmethod + def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on the application. Keyword + arguments passed to this method will override the defaults set on the + blueprint. + + Calls the blueprint's :meth:`~flask.Blueprint.register` method after + recording the blueprint in the application's :attr:`blueprints`. + + :param blueprint: The blueprint to register. + :param url_prefix: Blueprint routes will be prefixed with this. + :param subdomain: Blueprint routes will match on this subdomain. + :param url_defaults: Blueprint routes will use these default values for + view arguments. + :param options: Additional keyword arguments are passed to + :class:`~flask.blueprints.BlueprintSetupState`. They can be + accessed in :meth:`~flask.Blueprint.record` callbacks. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 0.7 + """ + blueprint.register(self, options) + + def iter_blueprints(self) -> t.ValuesView[Blueprint]: + """Iterates over all blueprints by the order they were registered. + + .. versionadded:: 0.11 + """ + return self.blueprints.values() + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = None, + **options: t.Any, + ) -> None: + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + options["endpoint"] = endpoint + methods = options.pop("methods", None) + + # if the methods are not given and the view_func object knows its + # methods we can use that instead. If neither exists, we go with + # a tuple of only ``GET`` as default. + if methods is None: + methods = getattr(view_func, "methods", None) or ("GET",) + if isinstance(methods, str): + raise TypeError( + "Allowed methods must be a list of strings, for" + ' example: @app.route(..., methods=["POST"])' + ) + methods = {item.upper() for item in methods} + + # Methods that should always be added + required_methods = set(getattr(view_func, "required_methods", ())) + + # starting with Flask 0.8 the view_func object can disable and + # force-enable the automatic options handling. + if provide_automatic_options is None: + provide_automatic_options = getattr( + view_func, "provide_automatic_options", None + ) + + if provide_automatic_options is None: + if "OPTIONS" not in methods: + provide_automatic_options = True + required_methods.add("OPTIONS") + else: + provide_automatic_options = False + + # Add the required methods now. + methods |= required_methods + + rule_obj = self.url_rule_class(rule, methods=methods, **options) + rule_obj.provide_automatic_options = provide_automatic_options # type: ignore[attr-defined] + + self.url_map.add(rule_obj) + if view_func is not None: + old_func = self.view_functions.get(endpoint) + if old_func is not None and old_func != view_func: + raise AssertionError( + "View function mapping is overwriting an existing" + f" endpoint function: {endpoint}" + ) + self.view_functions[endpoint] = view_func + + @setupmethod + def template_filter( + self, name: str | None = None + ) -> t.Callable[[T_template_filter], T_template_filter]: + """A decorator that is used to register custom template filter. + You can specify a name for the filter, otherwise the function + name will be used. Example:: + + @app.template_filter() + def reverse(s): + return s[::-1] + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: T_template_filter) -> T_template_filter: + self.add_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_filter( + self, f: ft.TemplateFilterCallable, name: str | None = None + ) -> None: + """Register a custom template filter. Works exactly like the + :meth:`template_filter` decorator. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + self.jinja_env.filters[name or f.__name__] = f + + @setupmethod + def template_test( + self, name: str | None = None + ) -> t.Callable[[T_template_test], T_template_test]: + """A decorator that is used to register custom template test. + You can specify a name for the test, otherwise the function + name will be used. Example:: + + @app.template_test() + def is_prime(n): + if n == 2: + return True + for i in range(2, int(math.ceil(math.sqrt(n))) + 1): + if n % i == 0: + return False + return True + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: T_template_test) -> T_template_test: + self.add_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_test( + self, f: ft.TemplateTestCallable, name: str | None = None + ) -> None: + """Register a custom template test. Works exactly like the + :meth:`template_test` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + self.jinja_env.tests[name or f.__name__] = f + + @setupmethod + def template_global( + self, name: str | None = None + ) -> t.Callable[[T_template_global], T_template_global]: + """A decorator that is used to register a custom template global function. + You can specify a name for the global function, otherwise the function + name will be used. Example:: + + @app.template_global() + def double(n): + return 2 * n + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + + def decorator(f: T_template_global) -> T_template_global: + self.add_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_template_global( + self, f: ft.TemplateGlobalCallable, name: str | None = None + ) -> None: + """Register a custom template global function. Works exactly like the + :meth:`template_global` decorator. + + .. versionadded:: 0.10 + + :param name: the optional name of the global function, otherwise the + function name will be used. + """ + self.jinja_env.globals[name or f.__name__] = f + + @setupmethod + def teardown_appcontext(self, f: T_teardown) -> T_teardown: + """Registers a function to be called when the application + context is popped. The application context is typically popped + after the request context for each request, at the end of CLI + commands, or after a manually pushed context ends. + + .. code-block:: python + + with app.app_context(): + ... + + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the app context is + made inactive. Since a request context typically also manages an + application context it would also be called when you pop a + request context. + + When a teardown function was called because of an unhandled + exception it will be passed an error object. If an + :meth:`errorhandler` is registered, it will handle the exception + and the teardown will not receive it. + + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. + + The return values of teardown functions are ignored. + + .. versionadded:: 0.9 + """ + self.teardown_appcontext_funcs.append(f) + return f + + @setupmethod + def shell_context_processor( + self, f: T_shell_context_processor + ) -> T_shell_context_processor: + """Registers a shell context processor function. + + .. versionadded:: 0.11 + """ + self.shell_context_processors.append(f) + return f + + def _find_error_handler( + self, e: Exception, blueprints: list[str] + ) -> ft.ErrorHandlerCallable | None: + """Return a registered error handler for an exception in this order: + blueprint handler for a specific code, app handler for a specific code, + blueprint handler for an exception class, app handler for an exception + class, or ``None`` if a suitable handler is not found. + """ + exc_class, code = self._get_exc_class_and_code(type(e)) + names = (*blueprints, None) + + for c in (code, None) if code is not None else (None,): + for name in names: + handler_map = self.error_handler_spec[name][c] + + if not handler_map: + continue + + for cls in exc_class.__mro__: + handler = handler_map.get(cls) + + if handler is not None: + return handler + return None + + def trap_http_exception(self, e: Exception) -> bool: + """Checks if an HTTP exception should be trapped or not. By default + this will return ``False`` for all exceptions except for a bad request + key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It + also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``. + + This is called for all HTTP exceptions raised by a view function. + If it returns ``True`` for any exception the error handler for this + exception is not called and it shows up as regular exception in the + traceback. This is helpful for debugging implicitly raised HTTP + exceptions. + + .. versionchanged:: 1.0 + Bad request errors are not trapped by default in debug mode. + + .. versionadded:: 0.8 + """ + if self.config["TRAP_HTTP_EXCEPTIONS"]: + return True + + trap_bad_request = self.config["TRAP_BAD_REQUEST_ERRORS"] + + # if unset, trap key errors in debug mode + if ( + trap_bad_request is None + and self.debug + and isinstance(e, BadRequestKeyError) + ): + return True + + if trap_bad_request: + return isinstance(e, BadRequest) + + return False + + def should_ignore_error(self, error: BaseException | None) -> bool: + """This is called to figure out if an error should be ignored + or not as far as the teardown system is concerned. If this + function returns ``True`` then the teardown handlers will not be + passed the error. + + .. versionadded:: 0.10 + """ + return False + + def redirect(self, location: str, code: int = 302) -> BaseResponse: + """Create a redirect response object. + + This is called by :func:`flask.redirect`, and can be called + directly as well. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + + .. versionadded:: 2.2 + Moved from ``flask.redirect``, which calls this method. + """ + return _wz_redirect( + location, + code=code, + Response=self.response_class, # type: ignore[arg-type] + ) + + def inject_url_defaults(self, endpoint: str, values: dict[str, t.Any]) -> None: + """Injects the URL defaults for the given endpoint directly into + the values dictionary passed. This is used internally and + automatically called on URL building. + + .. versionadded:: 0.7 + """ + names: t.Iterable[str | None] = (None,) + + # url_for may be called outside a request context, parse the + # passed endpoint instead of using request.blueprints. + if "." in endpoint: + names = chain( + names, reversed(_split_blueprint_path(endpoint.rpartition(".")[0])) + ) + + for name in names: + if name in self.url_default_functions: + for func in self.url_default_functions[name]: + func(endpoint, values) + + def handle_url_build_error( + self, error: BuildError, endpoint: str, values: dict[str, t.Any] + ) -> str: + """Called by :meth:`.url_for` if a + :exc:`~werkzeug.routing.BuildError` was raised. If this returns + a value, it will be returned by ``url_for``, otherwise the error + will be re-raised. + + Each function in :attr:`url_build_error_handlers` is called with + ``error``, ``endpoint`` and ``values``. If a function returns + ``None`` or raises a ``BuildError``, it is skipped. Otherwise, + its return value is returned by ``url_for``. + + :param error: The active ``BuildError`` being handled. + :param endpoint: The endpoint being built. + :param values: The keyword arguments passed to ``url_for``. + """ + for handler in self.url_build_error_handlers: + try: + rv = handler(error, endpoint, values) + except BuildError as e: + # make error available outside except block + error = e + else: + if rv is not None: + return rv + + # Re-raise if called with an active exception, otherwise raise + # the passed in exception. + if error is sys.exc_info()[1]: + raise + + raise error diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/blueprints.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/blueprints.py new file mode 100644 index 000000000..4f912cca0 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/blueprints.py @@ -0,0 +1,632 @@ +from __future__ import annotations + +import os +import typing as t +from collections import defaultdict +from functools import update_wrapper + +from .. import typing as ft +from .scaffold import _endpoint_from_view_func +from .scaffold import _sentinel +from .scaffold import Scaffold +from .scaffold import setupmethod + +if t.TYPE_CHECKING: # pragma: no cover + from .app import App + +DeferredSetupFunction = t.Callable[["BlueprintSetupState"], None] +T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any]) +T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) +T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_context_processor = t.TypeVar( + "T_template_context_processor", bound=ft.TemplateContextProcessorCallable +) +T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) +T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) +T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) +T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) +T_url_value_preprocessor = t.TypeVar( + "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable +) + + +class BlueprintSetupState: + """Temporary holder object for registering a blueprint with the + application. An instance of this class is created by the + :meth:`~flask.Blueprint.make_setup_state` method and later passed + to all register callback functions. + """ + + def __init__( + self, + blueprint: Blueprint, + app: App, + options: t.Any, + first_registration: bool, + ) -> None: + #: a reference to the current application + self.app = app + + #: a reference to the blueprint that created this setup state. + self.blueprint = blueprint + + #: a dictionary with all options that were passed to the + #: :meth:`~flask.Flask.register_blueprint` method. + self.options = options + + #: as blueprints can be registered multiple times with the + #: application and not everything wants to be registered + #: multiple times on it, this attribute can be used to figure + #: out if the blueprint was registered in the past already. + self.first_registration = first_registration + + subdomain = self.options.get("subdomain") + if subdomain is None: + subdomain = self.blueprint.subdomain + + #: The subdomain that the blueprint should be active for, ``None`` + #: otherwise. + self.subdomain = subdomain + + url_prefix = self.options.get("url_prefix") + if url_prefix is None: + url_prefix = self.blueprint.url_prefix + #: The prefix that should be used for all URLs defined on the + #: blueprint. + self.url_prefix = url_prefix + + self.name = self.options.get("name", blueprint.name) + self.name_prefix = self.options.get("name_prefix", "") + + #: A dictionary with URL defaults that is added to each and every + #: URL that was defined with the blueprint. + self.url_defaults = dict(self.blueprint.url_values_defaults) + self.url_defaults.update(self.options.get("url_defaults", ())) + + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + **options: t.Any, + ) -> None: + """A helper method to register a rule (and optionally a view function) + to the application. The endpoint is automatically prefixed with the + blueprint's name. + """ + if self.url_prefix is not None: + if rule: + rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/"))) + else: + rule = self.url_prefix + options.setdefault("subdomain", self.subdomain) + if endpoint is None: + endpoint = _endpoint_from_view_func(view_func) # type: ignore + defaults = self.url_defaults + if "defaults" in options: + defaults = dict(defaults, **options.pop("defaults")) + + self.app.add_url_rule( + rule, + f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."), + view_func, + defaults=defaults, + **options, + ) + + +class Blueprint(Scaffold): + """Represents a blueprint, a collection of routes and other + app-related functions that can be registered on a real application + later. + + A blueprint is an object that allows defining application functions + without requiring an application object ahead of time. It uses the + same decorators as :class:`~flask.Flask`, but defers the need for an + application by recording them for later registration. + + Decorating a function with a blueprint creates a deferred function + that is called with :class:`~flask.blueprints.BlueprintSetupState` + when the blueprint is registered on an application. + + See :doc:`/blueprints` for more information. + + :param name: The name of the blueprint. Will be prepended to each + endpoint name. + :param import_name: The name of the blueprint package, usually + ``__name__``. This helps locate the ``root_path`` for the + blueprint. + :param static_folder: A folder with static files that should be + served by the blueprint's static route. The path is relative to + the blueprint's root path. Blueprint static files are disabled + by default. + :param static_url_path: The url to serve static files from. + Defaults to ``static_folder``. If the blueprint does not have + a ``url_prefix``, the app's static route will take precedence, + and the blueprint's static files won't be accessible. + :param template_folder: A folder with templates that should be added + to the app's template search path. The path is relative to the + blueprint's root path. Blueprint templates are disabled by + default. Blueprint templates have a lower precedence than those + in the app's templates folder. + :param url_prefix: A path to prepend to all of the blueprint's URLs, + to make them distinct from the rest of the app's routes. + :param subdomain: A subdomain that blueprint routes will match on by + default. + :param url_defaults: A dict of default values that blueprint routes + will receive by default. + :param root_path: By default, the blueprint will automatically set + this based on ``import_name``. In certain situations this + automatic detection can fail, so the path can be specified + manually instead. + + .. versionchanged:: 1.1.0 + Blueprints have a ``cli`` group to register nested CLI commands. + The ``cli_group`` parameter controls the name of the group under + the ``flask`` command. + + .. versionadded:: 0.7 + """ + + _got_registered_once = False + + def __init__( + self, + name: str, + import_name: str, + static_folder: str | os.PathLike[str] | None = None, + static_url_path: str | None = None, + template_folder: str | os.PathLike[str] | None = None, + url_prefix: str | None = None, + subdomain: str | None = None, + url_defaults: dict[str, t.Any] | None = None, + root_path: str | None = None, + cli_group: str | None = _sentinel, # type: ignore[assignment] + ): + super().__init__( + import_name=import_name, + static_folder=static_folder, + static_url_path=static_url_path, + template_folder=template_folder, + root_path=root_path, + ) + + if not name: + raise ValueError("'name' may not be empty.") + + if "." in name: + raise ValueError("'name' may not contain a dot '.' character.") + + self.name = name + self.url_prefix = url_prefix + self.subdomain = subdomain + self.deferred_functions: list[DeferredSetupFunction] = [] + + if url_defaults is None: + url_defaults = {} + + self.url_values_defaults = url_defaults + self.cli_group = cli_group + self._blueprints: list[tuple[Blueprint, dict[str, t.Any]]] = [] + + def _check_setup_finished(self, f_name: str) -> None: + if self._got_registered_once: + raise AssertionError( + f"The setup method '{f_name}' can no longer be called on the blueprint" + f" '{self.name}'. It has already been registered at least once, any" + " changes will not be applied consistently.\n" + "Make sure all imports, decorators, functions, etc. needed to set up" + " the blueprint are done before registering it." + ) + + @setupmethod + def record(self, func: DeferredSetupFunction) -> None: + """Registers a function that is called when the blueprint is + registered on the application. This function is called with the + state as argument as returned by the :meth:`make_setup_state` + method. + """ + self.deferred_functions.append(func) + + @setupmethod + def record_once(self, func: DeferredSetupFunction) -> None: + """Works like :meth:`record` but wraps the function in another + function that will ensure the function is only called once. If the + blueprint is registered a second time on the application, the + function passed is not called. + """ + + def wrapper(state: BlueprintSetupState) -> None: + if state.first_registration: + func(state) + + self.record(update_wrapper(wrapper, func)) + + def make_setup_state( + self, app: App, options: dict[str, t.Any], first_registration: bool = False + ) -> BlueprintSetupState: + """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` + object that is later passed to the register callback functions. + Subclasses can override this to return a subclass of the setup state. + """ + return BlueprintSetupState(self, app, options, first_registration) + + @setupmethod + def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None: + """Register a :class:`~flask.Blueprint` on this blueprint. Keyword + arguments passed to this method will override the defaults set + on the blueprint. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + + .. versionadded:: 2.0 + """ + if blueprint is self: + raise ValueError("Cannot register a blueprint on itself") + self._blueprints.append((blueprint, options)) + + def register(self, app: App, options: dict[str, t.Any]) -> None: + """Called by :meth:`Flask.register_blueprint` to register all + views and callbacks registered on the blueprint with the + application. Creates a :class:`.BlueprintSetupState` and calls + each :meth:`record` callback with it. + + :param app: The application this blueprint is being registered + with. + :param options: Keyword arguments forwarded from + :meth:`~Flask.register_blueprint`. + + .. versionchanged:: 2.3 + Nested blueprints now correctly apply subdomains. + + .. versionchanged:: 2.1 + Registering the same blueprint with the same name multiple + times is an error. + + .. versionchanged:: 2.0.1 + Nested blueprints are registered with their dotted name. + This allows different blueprints with the same name to be + nested at different locations. + + .. versionchanged:: 2.0.1 + The ``name`` option can be used to change the (pre-dotted) + name the blueprint is registered with. This allows the same + blueprint to be registered multiple times with unique names + for ``url_for``. + """ + name_prefix = options.get("name_prefix", "") + self_name = options.get("name", self.name) + name = f"{name_prefix}.{self_name}".lstrip(".") + + if name in app.blueprints: + bp_desc = "this" if app.blueprints[name] is self else "a different" + existing_at = f" '{name}'" if self_name != name else "" + + raise ValueError( + f"The name '{self_name}' is already registered for" + f" {bp_desc} blueprint{existing_at}. Use 'name=' to" + f" provide a unique name." + ) + + first_bp_registration = not any(bp is self for bp in app.blueprints.values()) + first_name_registration = name not in app.blueprints + + app.blueprints[name] = self + self._got_registered_once = True + state = self.make_setup_state(app, options, first_bp_registration) + + if self.has_static_folder: + state.add_url_rule( + f"{self.static_url_path}/", + view_func=self.send_static_file, # type: ignore[attr-defined] + endpoint="static", + ) + + # Merge blueprint data into parent. + if first_bp_registration or first_name_registration: + self._merge_blueprint_funcs(app, name) + + for deferred in self.deferred_functions: + deferred(state) + + cli_resolved_group = options.get("cli_group", self.cli_group) + + if self.cli.commands: + if cli_resolved_group is None: + app.cli.commands.update(self.cli.commands) + elif cli_resolved_group is _sentinel: + self.cli.name = name + app.cli.add_command(self.cli) + else: + self.cli.name = cli_resolved_group + app.cli.add_command(self.cli) + + for blueprint, bp_options in self._blueprints: + bp_options = bp_options.copy() + bp_url_prefix = bp_options.get("url_prefix") + bp_subdomain = bp_options.get("subdomain") + + if bp_subdomain is None: + bp_subdomain = blueprint.subdomain + + if state.subdomain is not None and bp_subdomain is not None: + bp_options["subdomain"] = bp_subdomain + "." + state.subdomain + elif bp_subdomain is not None: + bp_options["subdomain"] = bp_subdomain + elif state.subdomain is not None: + bp_options["subdomain"] = state.subdomain + + if bp_url_prefix is None: + bp_url_prefix = blueprint.url_prefix + + if state.url_prefix is not None and bp_url_prefix is not None: + bp_options["url_prefix"] = ( + state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/") + ) + elif bp_url_prefix is not None: + bp_options["url_prefix"] = bp_url_prefix + elif state.url_prefix is not None: + bp_options["url_prefix"] = state.url_prefix + + bp_options["name_prefix"] = name + blueprint.register(app, bp_options) + + def _merge_blueprint_funcs(self, app: App, name: str) -> None: + def extend( + bp_dict: dict[ft.AppOrBlueprintKey, list[t.Any]], + parent_dict: dict[ft.AppOrBlueprintKey, list[t.Any]], + ) -> None: + for key, values in bp_dict.items(): + key = name if key is None else f"{name}.{key}" + parent_dict[key].extend(values) + + for key, value in self.error_handler_spec.items(): + key = name if key is None else f"{name}.{key}" + value = defaultdict( + dict, + { + code: {exc_class: func for exc_class, func in code_values.items()} + for code, code_values in value.items() + }, + ) + app.error_handler_spec[key] = value + + for endpoint, func in self.view_functions.items(): + app.view_functions[endpoint] = func + + extend(self.before_request_funcs, app.before_request_funcs) + extend(self.after_request_funcs, app.after_request_funcs) + extend( + self.teardown_request_funcs, + app.teardown_request_funcs, + ) + extend(self.url_default_functions, app.url_default_functions) + extend(self.url_value_preprocessors, app.url_value_preprocessors) + extend(self.template_context_processors, app.template_context_processors) + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = None, + **options: t.Any, + ) -> None: + """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for + full documentation. + + The URL rule is prefixed with the blueprint's URL prefix. The endpoint name, + used with :func:`url_for`, is prefixed with the blueprint's name. + """ + if endpoint and "." in endpoint: + raise ValueError("'endpoint' may not contain a dot '.' character.") + + if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__: + raise ValueError("'view_func' name may not contain a dot '.' character.") + + self.record( + lambda s: s.add_url_rule( + rule, + endpoint, + view_func, + provide_automatic_options=provide_automatic_options, + **options, + ) + ) + + @setupmethod + def app_template_filter( + self, name: str | None = None + ) -> t.Callable[[T_template_filter], T_template_filter]: + """Register a template filter, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_filter`. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def decorator(f: T_template_filter) -> T_template_filter: + self.add_app_template_filter(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_filter( + self, f: ft.TemplateFilterCallable, name: str | None = None + ) -> None: + """Register a template filter, available in any template rendered by the + application. Works like the :meth:`app_template_filter` decorator. Equivalent to + :meth:`.Flask.add_template_filter`. + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.filters[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def app_template_test( + self, name: str | None = None + ) -> t.Callable[[T_template_test], T_template_test]: + """Register a template test, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_test`. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def decorator(f: T_template_test) -> T_template_test: + self.add_app_template_test(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_test( + self, f: ft.TemplateTestCallable, name: str | None = None + ) -> None: + """Register a template test, available in any template rendered by the + application. Works like the :meth:`app_template_test` decorator. Equivalent to + :meth:`.Flask.add_template_test`. + + .. versionadded:: 0.10 + + :param name: the optional name of the test, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.tests[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def app_template_global( + self, name: str | None = None + ) -> t.Callable[[T_template_global], T_template_global]: + """Register a template global, available in any template rendered by the + application. Equivalent to :meth:`.Flask.template_global`. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def decorator(f: T_template_global) -> T_template_global: + self.add_app_template_global(f, name=name) + return f + + return decorator + + @setupmethod + def add_app_template_global( + self, f: ft.TemplateGlobalCallable, name: str | None = None + ) -> None: + """Register a template global, available in any template rendered by the + application. Works like the :meth:`app_template_global` decorator. Equivalent to + :meth:`.Flask.add_template_global`. + + .. versionadded:: 0.10 + + :param name: the optional name of the global, otherwise the + function name will be used. + """ + + def register_template(state: BlueprintSetupState) -> None: + state.app.jinja_env.globals[name or f.__name__] = f + + self.record_once(register_template) + + @setupmethod + def before_app_request(self, f: T_before_request) -> T_before_request: + """Like :meth:`before_request`, but before every request, not only those handled + by the blueprint. Equivalent to :meth:`.Flask.before_request`. + """ + self.record_once( + lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def after_app_request(self, f: T_after_request) -> T_after_request: + """Like :meth:`after_request`, but after every request, not only those handled + by the blueprint. Equivalent to :meth:`.Flask.after_request`. + """ + self.record_once( + lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def teardown_app_request(self, f: T_teardown) -> T_teardown: + """Like :meth:`teardown_request`, but after every request, not only those + handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`. + """ + self.record_once( + lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_context_processor( + self, f: T_template_context_processor + ) -> T_template_context_processor: + """Like :meth:`context_processor`, but for templates rendered by every view, not + only by the blueprint. Equivalent to :meth:`.Flask.context_processor`. + """ + self.record_once( + lambda s: s.app.template_context_processors.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_errorhandler( + self, code: type[Exception] | int + ) -> t.Callable[[T_error_handler], T_error_handler]: + """Like :meth:`errorhandler`, but for every request, not only those handled by + the blueprint. Equivalent to :meth:`.Flask.errorhandler`. + """ + + def decorator(f: T_error_handler) -> T_error_handler: + def from_blueprint(state: BlueprintSetupState) -> None: + state.app.errorhandler(code)(f) + + self.record_once(from_blueprint) + return f + + return decorator + + @setupmethod + def app_url_value_preprocessor( + self, f: T_url_value_preprocessor + ) -> T_url_value_preprocessor: + """Like :meth:`url_value_preprocessor`, but for every request, not only those + handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`. + """ + self.record_once( + lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) + ) + return f + + @setupmethod + def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults: + """Like :meth:`url_defaults`, but for every request, not only those handled by + the blueprint. Equivalent to :meth:`.Flask.url_defaults`. + """ + self.record_once( + lambda s: s.app.url_default_functions.setdefault(None, []).append(f) + ) + return f diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/scaffold.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/scaffold.py new file mode 100644 index 000000000..69e33a095 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sansio/scaffold.py @@ -0,0 +1,801 @@ +from __future__ import annotations + +import importlib.util +import os +import pathlib +import sys +import typing as t +from collections import defaultdict +from functools import update_wrapper + +from jinja2 import BaseLoader +from jinja2 import FileSystemLoader +from werkzeug.exceptions import default_exceptions +from werkzeug.exceptions import HTTPException +from werkzeug.utils import cached_property + +from .. import typing as ft +from ..helpers import get_root_path +from ..templating import _default_template_ctx_processor + +if t.TYPE_CHECKING: # pragma: no cover + from click import Group + +# a singleton sentinel value for parameter defaults +_sentinel = object() + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any]) +T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) +T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) +T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) +T_template_context_processor = t.TypeVar( + "T_template_context_processor", bound=ft.TemplateContextProcessorCallable +) +T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) +T_url_value_preprocessor = t.TypeVar( + "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable +) +T_route = t.TypeVar("T_route", bound=ft.RouteCallable) + + +def setupmethod(f: F) -> F: + f_name = f.__name__ + + def wrapper_func(self: Scaffold, *args: t.Any, **kwargs: t.Any) -> t.Any: + self._check_setup_finished(f_name) + return f(self, *args, **kwargs) + + return t.cast(F, update_wrapper(wrapper_func, f)) + + +class Scaffold: + """Common behavior shared between :class:`~flask.Flask` and + :class:`~flask.blueprints.Blueprint`. + + :param import_name: The import name of the module where this object + is defined. Usually :attr:`__name__` should be used. + :param static_folder: Path to a folder of static files to serve. + If this is set, a static route will be added. + :param static_url_path: URL prefix for the static route. + :param template_folder: Path to a folder containing template files. + for rendering. If this is set, a Jinja loader will be added. + :param root_path: The path that static, template, and resource files + are relative to. Typically not set, it is discovered based on + the ``import_name``. + + .. versionadded:: 2.0 + """ + + cli: Group + name: str + _static_folder: str | None = None + _static_url_path: str | None = None + + def __init__( + self, + import_name: str, + static_folder: str | os.PathLike[str] | None = None, + static_url_path: str | None = None, + template_folder: str | os.PathLike[str] | None = None, + root_path: str | None = None, + ): + #: The name of the package or module that this object belongs + #: to. Do not change this once it is set by the constructor. + self.import_name = import_name + + self.static_folder = static_folder # type: ignore + self.static_url_path = static_url_path + + #: The path to the templates folder, relative to + #: :attr:`root_path`, to add to the template loader. ``None`` if + #: templates should not be added. + self.template_folder = template_folder + + if root_path is None: + root_path = get_root_path(self.import_name) + + #: Absolute path to the package on the filesystem. Used to look + #: up resources contained in the package. + self.root_path = root_path + + #: A dictionary mapping endpoint names to view functions. + #: + #: To register a view function, use the :meth:`route` decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.view_functions: dict[str, ft.RouteCallable] = {} + + #: A data structure of registered error handlers, in the format + #: ``{scope: {code: {class: handler}}}``. The ``scope`` key is + #: the name of a blueprint the handlers are active for, or + #: ``None`` for all requests. The ``code`` key is the HTTP + #: status code for ``HTTPException``, or ``None`` for + #: other exceptions. The innermost dictionary maps exception + #: classes to handler functions. + #: + #: To register an error handler, use the :meth:`errorhandler` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.error_handler_spec: dict[ + ft.AppOrBlueprintKey, + dict[int | None, dict[type[Exception], ft.ErrorHandlerCallable]], + ] = defaultdict(lambda: defaultdict(dict)) + + #: A data structure of functions to call at the beginning of + #: each request, in the format ``{scope: [functions]}``. The + #: ``scope`` key is the name of a blueprint the functions are + #: active for, or ``None`` for all requests. + #: + #: To register a function, use the :meth:`before_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.before_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.BeforeRequestCallable] + ] = defaultdict(list) + + #: A data structure of functions to call at the end of each + #: request, in the format ``{scope: [functions]}``. The + #: ``scope`` key is the name of a blueprint the functions are + #: active for, or ``None`` for all requests. + #: + #: To register a function, use the :meth:`after_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.after_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.AfterRequestCallable[t.Any]] + ] = defaultdict(list) + + #: A data structure of functions to call at the end of each + #: request even if an exception is raised, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`teardown_request` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.teardown_request_funcs: dict[ + ft.AppOrBlueprintKey, list[ft.TeardownCallable] + ] = defaultdict(list) + + #: A data structure of functions to call to pass extra context + #: values when rendering templates, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`context_processor` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.template_context_processors: dict[ + ft.AppOrBlueprintKey, list[ft.TemplateContextProcessorCallable] + ] = defaultdict(list, {None: [_default_template_ctx_processor]}) + + #: A data structure of functions to call to modify the keyword + #: arguments passed to the view function, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the + #: :meth:`url_value_preprocessor` decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.url_value_preprocessors: dict[ + ft.AppOrBlueprintKey, + list[ft.URLValuePreprocessorCallable], + ] = defaultdict(list) + + #: A data structure of functions to call to modify the keyword + #: arguments when generating URLs, in the format + #: ``{scope: [functions]}``. The ``scope`` key is the name of a + #: blueprint the functions are active for, or ``None`` for all + #: requests. + #: + #: To register a function, use the :meth:`url_defaults` + #: decorator. + #: + #: This data structure is internal. It should not be modified + #: directly and its format may change at any time. + self.url_default_functions: dict[ + ft.AppOrBlueprintKey, list[ft.URLDefaultCallable] + ] = defaultdict(list) + + def __repr__(self) -> str: + return f"<{type(self).__name__} {self.name!r}>" + + def _check_setup_finished(self, f_name: str) -> None: + raise NotImplementedError + + @property + def static_folder(self) -> str | None: + """The absolute path to the configured static folder. ``None`` + if no static folder is set. + """ + if self._static_folder is not None: + return os.path.join(self.root_path, self._static_folder) + else: + return None + + @static_folder.setter + def static_folder(self, value: str | os.PathLike[str] | None) -> None: + if value is not None: + value = os.fspath(value).rstrip(r"\/") + + self._static_folder = value + + @property + def has_static_folder(self) -> bool: + """``True`` if :attr:`static_folder` is set. + + .. versionadded:: 0.5 + """ + return self.static_folder is not None + + @property + def static_url_path(self) -> str | None: + """The URL prefix that the static route will be accessible from. + + If it was not configured during init, it is derived from + :attr:`static_folder`. + """ + if self._static_url_path is not None: + return self._static_url_path + + if self.static_folder is not None: + basename = os.path.basename(self.static_folder) + return f"/{basename}".rstrip("/") + + return None + + @static_url_path.setter + def static_url_path(self, value: str | None) -> None: + if value is not None: + value = value.rstrip("/") + + self._static_url_path = value + + @cached_property + def jinja_loader(self) -> BaseLoader | None: + """The Jinja loader for this object's templates. By default this + is a class :class:`jinja2.loaders.FileSystemLoader` to + :attr:`template_folder` if it is set. + + .. versionadded:: 0.5 + """ + if self.template_folder is not None: + return FileSystemLoader(os.path.join(self.root_path, self.template_folder)) + else: + return None + + def _method_route( + self, + method: str, + rule: str, + options: dict[str, t.Any], + ) -> t.Callable[[T_route], T_route]: + if "methods" in options: + raise TypeError("Use the 'route' decorator to use the 'methods' argument.") + + return self.route(rule, methods=[method], **options) + + @setupmethod + def get(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["GET"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("GET", rule, options) + + @setupmethod + def post(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["POST"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("POST", rule, options) + + @setupmethod + def put(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["PUT"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("PUT", rule, options) + + @setupmethod + def delete(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["DELETE"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("DELETE", rule, options) + + @setupmethod + def patch(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Shortcut for :meth:`route` with ``methods=["PATCH"]``. + + .. versionadded:: 2.0 + """ + return self._method_route("PATCH", rule, options) + + @setupmethod + def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: + """Decorate a view function to register it with the given URL + rule and options. Calls :meth:`add_url_rule`, which has more + details about the implementation. + + .. code-block:: python + + @app.route("/") + def index(): + return "Hello, World!" + + See :ref:`url-route-registrations`. + + The endpoint name for the route defaults to the name of the view + function if the ``endpoint`` parameter isn't passed. + + The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` and + ``OPTIONS`` are added automatically. + + :param rule: The URL rule string. + :param options: Extra options passed to the + :class:`~werkzeug.routing.Rule` object. + """ + + def decorator(f: T_route) -> T_route: + endpoint = options.pop("endpoint", None) + self.add_url_rule(rule, endpoint, f, **options) + return f + + return decorator + + @setupmethod + def add_url_rule( + self, + rule: str, + endpoint: str | None = None, + view_func: ft.RouteCallable | None = None, + provide_automatic_options: bool | None = None, + **options: t.Any, + ) -> None: + """Register a rule for routing incoming requests and building + URLs. The :meth:`route` decorator is a shortcut to call this + with the ``view_func`` argument. These are equivalent: + + .. code-block:: python + + @app.route("/") + def index(): + ... + + .. code-block:: python + + def index(): + ... + + app.add_url_rule("/", view_func=index) + + See :ref:`url-route-registrations`. + + The endpoint name for the route defaults to the name of the view + function if the ``endpoint`` parameter isn't passed. An error + will be raised if a function has already been registered for the + endpoint. + + The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` is + always added automatically, and ``OPTIONS`` is added + automatically by default. + + ``view_func`` does not necessarily need to be passed, but if the + rule should participate in routing an endpoint name must be + associated with a view function at some point with the + :meth:`endpoint` decorator. + + .. code-block:: python + + app.add_url_rule("/", endpoint="index") + + @app.endpoint("index") + def index(): + ... + + If ``view_func`` has a ``required_methods`` attribute, those + methods are added to the passed and automatic methods. If it + has a ``provide_automatic_methods`` attribute, it is used as the + default if the parameter is not passed. + + :param rule: The URL rule string. + :param endpoint: The endpoint name to associate with the rule + and view function. Used when routing and building URLs. + Defaults to ``view_func.__name__``. + :param view_func: The view function to associate with the + endpoint name. + :param provide_automatic_options: Add the ``OPTIONS`` method and + respond to ``OPTIONS`` requests automatically. + :param options: Extra options passed to the + :class:`~werkzeug.routing.Rule` object. + """ + raise NotImplementedError + + @setupmethod + def endpoint(self, endpoint: str) -> t.Callable[[F], F]: + """Decorate a view function to register it for the given + endpoint. Used if a rule is added without a ``view_func`` with + :meth:`add_url_rule`. + + .. code-block:: python + + app.add_url_rule("/ex", endpoint="example") + + @app.endpoint("example") + def example(): + ... + + :param endpoint: The endpoint name to associate with the view + function. + """ + + def decorator(f: F) -> F: + self.view_functions[endpoint] = f + return f + + return decorator + + @setupmethod + def before_request(self, f: T_before_request) -> T_before_request: + """Register a function to run before each request. + + For example, this can be used to open a database connection, or + to load the logged in user from the session. + + .. code-block:: python + + @app.before_request + def load_user(): + if "user_id" in session: + g.user = db.session.get(session["user_id"]) + + The function will be called without any arguments. If it returns + a non-``None`` value, the value is handled as if it was the + return value from the view, and further request handling is + stopped. + + This is available on both app and blueprint objects. When used on an app, this + executes before every request. When used on a blueprint, this executes before + every request that the blueprint handles. To register with a blueprint and + execute before every request, use :meth:`.Blueprint.before_app_request`. + """ + self.before_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def after_request(self, f: T_after_request) -> T_after_request: + """Register a function to run after each request to this object. + + The function is called with the response object, and must return + a response object. This allows the functions to modify or + replace the response before it is sent. + + If a function raises an exception, any remaining + ``after_request`` functions will not be called. Therefore, this + should not be used for actions that must execute, such as to + close resources. Use :meth:`teardown_request` for that. + + This is available on both app and blueprint objects. When used on an app, this + executes after every request. When used on a blueprint, this executes after + every request that the blueprint handles. To register with a blueprint and + execute after every request, use :meth:`.Blueprint.after_app_request`. + """ + self.after_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def teardown_request(self, f: T_teardown) -> T_teardown: + """Register a function to be called when the request context is + popped. Typically this happens at the end of each request, but + contexts may be pushed manually as well during testing. + + .. code-block:: python + + with app.test_request_context(): + ... + + When the ``with`` block exits (or ``ctx.pop()`` is called), the + teardown functions are called just before the request context is + made inactive. + + When a teardown function was called because of an unhandled + exception it will be passed an error object. If an + :meth:`errorhandler` is registered, it will handle the exception + and the teardown will not receive it. + + Teardown functions must avoid raising exceptions. If they + execute code that might fail they must surround that code with a + ``try``/``except`` block and log any errors. + + The return values of teardown functions are ignored. + + This is available on both app and blueprint objects. When used on an app, this + executes after every request. When used on a blueprint, this executes after + every request that the blueprint handles. To register with a blueprint and + execute after every request, use :meth:`.Blueprint.teardown_app_request`. + """ + self.teardown_request_funcs.setdefault(None, []).append(f) + return f + + @setupmethod + def context_processor( + self, + f: T_template_context_processor, + ) -> T_template_context_processor: + """Registers a template context processor function. These functions run before + rendering a template. The keys of the returned dict are added as variables + available in the template. + + This is available on both app and blueprint objects. When used on an app, this + is called for every rendered template. When used on a blueprint, this is called + for templates rendered from the blueprint's views. To register with a blueprint + and affect every template, use :meth:`.Blueprint.app_context_processor`. + """ + self.template_context_processors[None].append(f) + return f + + @setupmethod + def url_value_preprocessor( + self, + f: T_url_value_preprocessor, + ) -> T_url_value_preprocessor: + """Register a URL value preprocessor function for all view + functions in the application. These functions will be called before the + :meth:`before_request` functions. + + The function can modify the values captured from the matched url before + they are passed to the view. For example, this can be used to pop a + common language code value and place it in ``g`` rather than pass it to + every view. + + The function is passed the endpoint name and values dict. The return + value is ignored. + + This is available on both app and blueprint objects. When used on an app, this + is called for every request. When used on a blueprint, this is called for + requests that the blueprint handles. To register with a blueprint and affect + every request, use :meth:`.Blueprint.app_url_value_preprocessor`. + """ + self.url_value_preprocessors[None].append(f) + return f + + @setupmethod + def url_defaults(self, f: T_url_defaults) -> T_url_defaults: + """Callback function for URL defaults for all view functions of the + application. It's called with the endpoint and values and should + update the values passed in place. + + This is available on both app and blueprint objects. When used on an app, this + is called for every request. When used on a blueprint, this is called for + requests that the blueprint handles. To register with a blueprint and affect + every request, use :meth:`.Blueprint.app_url_defaults`. + """ + self.url_default_functions[None].append(f) + return f + + @setupmethod + def errorhandler( + self, code_or_exception: type[Exception] | int + ) -> t.Callable[[T_error_handler], T_error_handler]: + """Register a function to handle errors by code or exception class. + + A decorator that is used to register a function given an + error code. Example:: + + @app.errorhandler(404) + def page_not_found(error): + return 'This page does not exist', 404 + + You can also register handlers for arbitrary exceptions:: + + @app.errorhandler(DatabaseError) + def special_exception_handler(error): + return 'Database connection failed', 500 + + This is available on both app and blueprint objects. When used on an app, this + can handle errors from every request. When used on a blueprint, this can handle + errors from requests that the blueprint handles. To register with a blueprint + and affect every request, use :meth:`.Blueprint.app_errorhandler`. + + .. versionadded:: 0.7 + Use :meth:`register_error_handler` instead of modifying + :attr:`error_handler_spec` directly, for application wide error + handlers. + + .. versionadded:: 0.7 + One can now additionally also register custom exception types + that do not necessarily have to be a subclass of the + :class:`~werkzeug.exceptions.HTTPException` class. + + :param code_or_exception: the code as integer for the handler, or + an arbitrary exception + """ + + def decorator(f: T_error_handler) -> T_error_handler: + self.register_error_handler(code_or_exception, f) + return f + + return decorator + + @setupmethod + def register_error_handler( + self, + code_or_exception: type[Exception] | int, + f: ft.ErrorHandlerCallable, + ) -> None: + """Alternative error attach function to the :meth:`errorhandler` + decorator that is more straightforward to use for non decorator + usage. + + .. versionadded:: 0.7 + """ + exc_class, code = self._get_exc_class_and_code(code_or_exception) + self.error_handler_spec[None][code][exc_class] = f + + @staticmethod + def _get_exc_class_and_code( + exc_class_or_code: type[Exception] | int, + ) -> tuple[type[Exception], int | None]: + """Get the exception class being handled. For HTTP status codes + or ``HTTPException`` subclasses, return both the exception and + status code. + + :param exc_class_or_code: Any exception class, or an HTTP status + code as an integer. + """ + exc_class: type[Exception] + + if isinstance(exc_class_or_code, int): + try: + exc_class = default_exceptions[exc_class_or_code] + except KeyError: + raise ValueError( + f"'{exc_class_or_code}' is not a recognized HTTP" + " error code. Use a subclass of HTTPException with" + " that code instead." + ) from None + else: + exc_class = exc_class_or_code + + if isinstance(exc_class, Exception): + raise TypeError( + f"{exc_class!r} is an instance, not a class. Handlers" + " can only be registered for Exception classes or HTTP" + " error codes." + ) + + if not issubclass(exc_class, Exception): + raise ValueError( + f"'{exc_class.__name__}' is not a subclass of Exception." + " Handlers can only be registered for Exception classes" + " or HTTP error codes." + ) + + if issubclass(exc_class, HTTPException): + return exc_class, exc_class.code + else: + return exc_class, None + + +def _endpoint_from_view_func(view_func: ft.RouteCallable) -> str: + """Internal helper that returns the default endpoint for a given + function. This always is the function name. + """ + assert view_func is not None, "expected view func if endpoint is not provided." + return view_func.__name__ + + +def _path_is_relative_to(path: pathlib.PurePath, base: str) -> bool: + # Path.is_relative_to doesn't exist until Python 3.9 + try: + path.relative_to(base) + return True + except ValueError: + return False + + +def _find_package_path(import_name: str) -> str: + """Find the path that contains the package or module.""" + root_mod_name, _, _ = import_name.partition(".") + + try: + root_spec = importlib.util.find_spec(root_mod_name) + + if root_spec is None: + raise ValueError("not found") + except (ImportError, ValueError): + # ImportError: the machinery told us it does not exist + # ValueError: + # - the module name was invalid + # - the module name is __main__ + # - we raised `ValueError` due to `root_spec` being `None` + return os.getcwd() + + if root_spec.submodule_search_locations: + if root_spec.origin is None or root_spec.origin == "namespace": + # namespace package + package_spec = importlib.util.find_spec(import_name) + + if package_spec is not None and package_spec.submodule_search_locations: + # Pick the path in the namespace that contains the submodule. + package_path = pathlib.Path( + os.path.commonpath(package_spec.submodule_search_locations) + ) + search_location = next( + location + for location in root_spec.submodule_search_locations + if _path_is_relative_to(package_path, location) + ) + else: + # Pick the first path. + search_location = root_spec.submodule_search_locations[0] + + return os.path.dirname(search_location) + else: + # package with __init__.py + return os.path.dirname(os.path.dirname(root_spec.origin)) + else: + # module + return os.path.dirname(root_spec.origin) # type: ignore[type-var, return-value] + + +def find_package(import_name: str) -> tuple[str | None, str]: + """Find the prefix that a package is installed under, and the path + that it would be imported from. + + The prefix is the directory containing the standard directory + hierarchy (lib, bin, etc.). If the package is not installed to the + system (:attr:`sys.prefix`) or a virtualenv (``site-packages``), + ``None`` is returned. + + The path is the entry in :attr:`sys.path` that contains the package + for import. If the package is not installed, it's assumed that the + package was imported from the current working directory. + """ + package_path = _find_package_path(import_name) + py_prefix = os.path.abspath(sys.prefix) + + # installed to the system + if _path_is_relative_to(pathlib.PurePath(package_path), py_prefix): + return py_prefix, package_path + + site_parent, site_folder = os.path.split(package_path) + + # installed to a virtualenv + if site_folder.lower() == "site-packages": + parent, folder = os.path.split(site_parent) + + # Windows (prefix/lib/site-packages) + if folder.lower() == "lib": + return parent, package_path + + # Unix (prefix/lib/pythonX.Y/site-packages) + if os.path.basename(parent).lower() == "lib": + return os.path.dirname(parent), package_path + + # something else (prefix/site-packages) + return site_parent, package_path + + # not installed + return None, package_path diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sessions.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sessions.py new file mode 100644 index 000000000..ee19ad638 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/sessions.py @@ -0,0 +1,379 @@ +from __future__ import annotations + +import hashlib +import typing as t +from collections.abc import MutableMapping +from datetime import datetime +from datetime import timezone + +from itsdangerous import BadSignature +from itsdangerous import URLSafeTimedSerializer +from werkzeug.datastructures import CallbackDict + +from .json.tag import TaggedJSONSerializer + +if t.TYPE_CHECKING: # pragma: no cover + import typing_extensions as te + + from .app import Flask + from .wrappers import Request + from .wrappers import Response + + +# TODO generic when Python > 3.8 +class SessionMixin(MutableMapping): # type: ignore[type-arg] + """Expands a basic dictionary with session attributes.""" + + @property + def permanent(self) -> bool: + """This reflects the ``'_permanent'`` key in the dict.""" + return self.get("_permanent", False) + + @permanent.setter + def permanent(self, value: bool) -> None: + self["_permanent"] = bool(value) + + #: Some implementations can detect whether a session is newly + #: created, but that is not guaranteed. Use with caution. The mixin + # default is hard-coded ``False``. + new = False + + #: Some implementations can detect changes to the session and set + #: this when that happens. The mixin default is hard coded to + #: ``True``. + modified = True + + #: Some implementations can detect when session data is read or + #: written and set this when that happens. The mixin default is hard + #: coded to ``True``. + accessed = True + + +# TODO generic when Python > 3.8 +class SecureCookieSession(CallbackDict, SessionMixin): # type: ignore[type-arg] + """Base class for sessions based on signed cookies. + + This session backend will set the :attr:`modified` and + :attr:`accessed` attributes. It cannot reliably track whether a + session is new (vs. empty), so :attr:`new` remains hard coded to + ``False``. + """ + + #: When data is changed, this is set to ``True``. Only the session + #: dictionary itself is tracked; if the session contains mutable + #: data (for example a nested dict) then this must be set to + #: ``True`` manually when modifying that data. The session cookie + #: will only be written to the response if this is ``True``. + modified = False + + #: When data is read or written, this is set to ``True``. Used by + # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie`` + #: header, which allows caching proxies to cache different pages for + #: different users. + accessed = False + + def __init__(self, initial: t.Any = None) -> None: + def on_update(self: te.Self) -> None: + self.modified = True + self.accessed = True + + super().__init__(initial, on_update) + + def __getitem__(self, key: str) -> t.Any: + self.accessed = True + return super().__getitem__(key) + + def get(self, key: str, default: t.Any = None) -> t.Any: + self.accessed = True + return super().get(key, default) + + def setdefault(self, key: str, default: t.Any = None) -> t.Any: + self.accessed = True + return super().setdefault(key, default) + + +class NullSession(SecureCookieSession): + """Class used to generate nicer error messages if sessions are not + available. Will still allow read-only access to the empty session + but fail on setting. + """ + + def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: + raise RuntimeError( + "The session is unavailable because no secret " + "key was set. Set the secret_key on the " + "application to something unique and secret." + ) + + __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail # type: ignore # noqa: B950 + del _fail + + +class SessionInterface: + """The basic interface you have to implement in order to replace the + default session interface which uses werkzeug's securecookie + implementation. The only methods you have to implement are + :meth:`open_session` and :meth:`save_session`, the others have + useful defaults which you don't need to change. + + The session object returned by the :meth:`open_session` method has to + provide a dictionary like interface plus the properties and methods + from the :class:`SessionMixin`. We recommend just subclassing a dict + and adding that mixin:: + + class Session(dict, SessionMixin): + pass + + If :meth:`open_session` returns ``None`` Flask will call into + :meth:`make_null_session` to create a session that acts as replacement + if the session support cannot work because some requirement is not + fulfilled. The default :class:`NullSession` class that is created + will complain that the secret key was not set. + + To replace the session interface on an application all you have to do + is to assign :attr:`flask.Flask.session_interface`:: + + app = Flask(__name__) + app.session_interface = MySessionInterface() + + Multiple requests with the same session may be sent and handled + concurrently. When implementing a new session interface, consider + whether reads or writes to the backing store must be synchronized. + There is no guarantee on the order in which the session for each + request is opened or saved, it will occur in the order that requests + begin and end processing. + + .. versionadded:: 0.8 + """ + + #: :meth:`make_null_session` will look here for the class that should + #: be created when a null session is requested. Likewise the + #: :meth:`is_null_session` method will perform a typecheck against + #: this type. + null_session_class = NullSession + + #: A flag that indicates if the session interface is pickle based. + #: This can be used by Flask extensions to make a decision in regards + #: to how to deal with the session object. + #: + #: .. versionadded:: 0.10 + pickle_based = False + + def make_null_session(self, app: Flask) -> NullSession: + """Creates a null session which acts as a replacement object if the + real session support could not be loaded due to a configuration + error. This mainly aids the user experience because the job of the + null session is to still support lookup without complaining but + modifications are answered with a helpful error message of what + failed. + + This creates an instance of :attr:`null_session_class` by default. + """ + return self.null_session_class() + + def is_null_session(self, obj: object) -> bool: + """Checks if a given object is a null session. Null sessions are + not asked to be saved. + + This checks if the object is an instance of :attr:`null_session_class` + by default. + """ + return isinstance(obj, self.null_session_class) + + def get_cookie_name(self, app: Flask) -> str: + """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``.""" + return app.config["SESSION_COOKIE_NAME"] # type: ignore[no-any-return] + + def get_cookie_domain(self, app: Flask) -> str | None: + """The value of the ``Domain`` parameter on the session cookie. If not set, + browsers will only send the cookie to the exact domain it was set from. + Otherwise, they will send it to any subdomain of the given value as well. + + Uses the :data:`SESSION_COOKIE_DOMAIN` config. + + .. versionchanged:: 2.3 + Not set by default, does not fall back to ``SERVER_NAME``. + """ + return app.config["SESSION_COOKIE_DOMAIN"] # type: ignore[no-any-return] + + def get_cookie_path(self, app: Flask) -> str: + """Returns the path for which the cookie should be valid. The + default implementation uses the value from the ``SESSION_COOKIE_PATH`` + config var if it's set, and falls back to ``APPLICATION_ROOT`` or + uses ``/`` if it's ``None``. + """ + return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"] # type: ignore[no-any-return] + + def get_cookie_httponly(self, app: Flask) -> bool: + """Returns True if the session cookie should be httponly. This + currently just returns the value of the ``SESSION_COOKIE_HTTPONLY`` + config var. + """ + return app.config["SESSION_COOKIE_HTTPONLY"] # type: ignore[no-any-return] + + def get_cookie_secure(self, app: Flask) -> bool: + """Returns True if the cookie should be secure. This currently + just returns the value of the ``SESSION_COOKIE_SECURE`` setting. + """ + return app.config["SESSION_COOKIE_SECURE"] # type: ignore[no-any-return] + + def get_cookie_samesite(self, app: Flask) -> str | None: + """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the + ``SameSite`` attribute. This currently just returns the value of + the :data:`SESSION_COOKIE_SAMESITE` setting. + """ + return app.config["SESSION_COOKIE_SAMESITE"] # type: ignore[no-any-return] + + def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None: + """A helper method that returns an expiration date for the session + or ``None`` if the session is linked to the browser session. The + default implementation returns now + the permanent session + lifetime configured on the application. + """ + if session.permanent: + return datetime.now(timezone.utc) + app.permanent_session_lifetime + return None + + def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool: + """Used by session backends to determine if a ``Set-Cookie`` header + should be set for this session cookie for this response. If the session + has been modified, the cookie is set. If the session is permanent and + the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is + always set. + + This check is usually skipped if the session was deleted. + + .. versionadded:: 0.11 + """ + + return session.modified or ( + session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"] + ) + + def open_session(self, app: Flask, request: Request) -> SessionMixin | None: + """This is called at the beginning of each request, after + pushing the request context, before matching the URL. + + This must return an object which implements a dictionary-like + interface as well as the :class:`SessionMixin` interface. + + This will return ``None`` to indicate that loading failed in + some way that is not immediately an error. The request + context will fall back to using :meth:`make_null_session` + in this case. + """ + raise NotImplementedError() + + def save_session( + self, app: Flask, session: SessionMixin, response: Response + ) -> None: + """This is called at the end of each request, after generating + a response, before removing the request context. It is skipped + if :meth:`is_null_session` returns ``True``. + """ + raise NotImplementedError() + + +session_json_serializer = TaggedJSONSerializer() + + +def _lazy_sha1(string: bytes = b"") -> t.Any: + """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include + SHA-1, in which case the import and use as a default would fail before the + developer can configure something else. + """ + return hashlib.sha1(string) + + +class SecureCookieSessionInterface(SessionInterface): + """The default session interface that stores sessions in signed cookies + through the :mod:`itsdangerous` module. + """ + + #: the salt that should be applied on top of the secret key for the + #: signing of cookie based sessions. + salt = "cookie-session" + #: the hash function to use for the signature. The default is sha1 + digest_method = staticmethod(_lazy_sha1) + #: the name of the itsdangerous supported key derivation. The default + #: is hmac. + key_derivation = "hmac" + #: A python serializer for the payload. The default is a compact + #: JSON derived serializer with support for some extra Python types + #: such as datetime objects or tuples. + serializer = session_json_serializer + session_class = SecureCookieSession + + def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None: + if not app.secret_key: + return None + signer_kwargs = dict( + key_derivation=self.key_derivation, digest_method=self.digest_method + ) + return URLSafeTimedSerializer( + app.secret_key, + salt=self.salt, + serializer=self.serializer, + signer_kwargs=signer_kwargs, + ) + + def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None: + s = self.get_signing_serializer(app) + if s is None: + return None + val = request.cookies.get(self.get_cookie_name(app)) + if not val: + return self.session_class() + max_age = int(app.permanent_session_lifetime.total_seconds()) + try: + data = s.loads(val, max_age=max_age) + return self.session_class(data) + except BadSignature: + return self.session_class() + + def save_session( + self, app: Flask, session: SessionMixin, response: Response + ) -> None: + name = self.get_cookie_name(app) + domain = self.get_cookie_domain(app) + path = self.get_cookie_path(app) + secure = self.get_cookie_secure(app) + samesite = self.get_cookie_samesite(app) + httponly = self.get_cookie_httponly(app) + + # Add a "Vary: Cookie" header if the session was accessed at all. + if session.accessed: + response.vary.add("Cookie") + + # If the session is modified to be empty, remove the cookie. + # If the session is empty, return without setting the cookie. + if not session: + if session.modified: + response.delete_cookie( + name, + domain=domain, + path=path, + secure=secure, + samesite=samesite, + httponly=httponly, + ) + response.vary.add("Cookie") + + return + + if not self.should_set_cookie(app, session): + return + + expires = self.get_expiration_time(app, session) + val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore + response.set_cookie( + name, + val, # type: ignore + expires=expires, + httponly=httponly, + domain=domain, + path=path, + secure=secure, + samesite=samesite, + ) + response.vary.add("Cookie") diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/signals.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/signals.py new file mode 100644 index 000000000..444fda998 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/signals.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from blinker import Namespace + +# This namespace is only for signals provided by Flask itself. +_signals = Namespace() + +template_rendered = _signals.signal("template-rendered") +before_render_template = _signals.signal("before-render-template") +request_started = _signals.signal("request-started") +request_finished = _signals.signal("request-finished") +request_tearing_down = _signals.signal("request-tearing-down") +got_request_exception = _signals.signal("got-request-exception") +appcontext_tearing_down = _signals.signal("appcontext-tearing-down") +appcontext_pushed = _signals.signal("appcontext-pushed") +appcontext_popped = _signals.signal("appcontext-popped") +message_flashed = _signals.signal("message-flashed") diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/templating.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/templating.py new file mode 100644 index 000000000..618a3b35d --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/templating.py @@ -0,0 +1,219 @@ +from __future__ import annotations + +import typing as t + +from jinja2 import BaseLoader +from jinja2 import Environment as BaseEnvironment +from jinja2 import Template +from jinja2 import TemplateNotFound + +from .globals import _cv_app +from .globals import _cv_request +from .globals import current_app +from .globals import request +from .helpers import stream_with_context +from .signals import before_render_template +from .signals import template_rendered + +if t.TYPE_CHECKING: # pragma: no cover + from .app import Flask + from .sansio.app import App + from .sansio.scaffold import Scaffold + + +def _default_template_ctx_processor() -> dict[str, t.Any]: + """Default template context processor. Injects `request`, + `session` and `g`. + """ + appctx = _cv_app.get(None) + reqctx = _cv_request.get(None) + rv: dict[str, t.Any] = {} + if appctx is not None: + rv["g"] = appctx.g + if reqctx is not None: + rv["request"] = reqctx.request + rv["session"] = reqctx.session + return rv + + +class Environment(BaseEnvironment): + """Works like a regular Jinja2 environment but has some additional + knowledge of how Flask's blueprint works so that it can prepend the + name of the blueprint to referenced templates if necessary. + """ + + def __init__(self, app: App, **options: t.Any) -> None: + if "loader" not in options: + options["loader"] = app.create_global_jinja_loader() + BaseEnvironment.__init__(self, **options) + self.app = app + + +class DispatchingJinjaLoader(BaseLoader): + """A loader that looks for templates in the application and all + the blueprint folders. + """ + + def __init__(self, app: App) -> None: + self.app = app + + def get_source( + self, environment: BaseEnvironment, template: str + ) -> tuple[str, str | None, t.Callable[[], bool] | None]: + if self.app.config["EXPLAIN_TEMPLATE_LOADING"]: + return self._get_source_explained(environment, template) + return self._get_source_fast(environment, template) + + def _get_source_explained( + self, environment: BaseEnvironment, template: str + ) -> tuple[str, str | None, t.Callable[[], bool] | None]: + attempts = [] + rv: tuple[str, str | None, t.Callable[[], bool] | None] | None + trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None + + for srcobj, loader in self._iter_loaders(template): + try: + rv = loader.get_source(environment, template) + if trv is None: + trv = rv + except TemplateNotFound: + rv = None + attempts.append((loader, srcobj, rv)) + + from .debughelpers import explain_template_loading_attempts + + explain_template_loading_attempts(self.app, template, attempts) + + if trv is not None: + return trv + raise TemplateNotFound(template) + + def _get_source_fast( + self, environment: BaseEnvironment, template: str + ) -> tuple[str, str | None, t.Callable[[], bool] | None]: + for _srcobj, loader in self._iter_loaders(template): + try: + return loader.get_source(environment, template) + except TemplateNotFound: + continue + raise TemplateNotFound(template) + + def _iter_loaders(self, template: str) -> t.Iterator[tuple[Scaffold, BaseLoader]]: + loader = self.app.jinja_loader + if loader is not None: + yield self.app, loader + + for blueprint in self.app.iter_blueprints(): + loader = blueprint.jinja_loader + if loader is not None: + yield blueprint, loader + + def list_templates(self) -> list[str]: + result = set() + loader = self.app.jinja_loader + if loader is not None: + result.update(loader.list_templates()) + + for blueprint in self.app.iter_blueprints(): + loader = blueprint.jinja_loader + if loader is not None: + for template in loader.list_templates(): + result.add(template) + + return list(result) + + +def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str: + app.update_template_context(context) + before_render_template.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + rv = template.render(context) + template_rendered.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + return rv + + +def render_template( + template_name_or_list: str | Template | list[str | Template], + **context: t.Any, +) -> str: + """Render a template by name with the given context. + + :param template_name_or_list: The name of the template to render. If + a list is given, the first name to exist will be rendered. + :param context: The variables to make available in the template. + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.get_or_select_template(template_name_or_list) + return _render(app, template, context) + + +def render_template_string(source: str, **context: t.Any) -> str: + """Render a template from the given source string with the given + context. + + :param source: The source code of the template to render. + :param context: The variables to make available in the template. + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.from_string(source) + return _render(app, template, context) + + +def _stream( + app: Flask, template: Template, context: dict[str, t.Any] +) -> t.Iterator[str]: + app.update_template_context(context) + before_render_template.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + + def generate() -> t.Iterator[str]: + yield from template.generate(context) + template_rendered.send( + app, _async_wrapper=app.ensure_sync, template=template, context=context + ) + + rv = generate() + + # If a request context is active, keep it while generating. + if request: + rv = stream_with_context(rv) + + return rv + + +def stream_template( + template_name_or_list: str | Template | list[str | Template], + **context: t.Any, +) -> t.Iterator[str]: + """Render a template by name with the given context as a stream. + This returns an iterator of strings, which can be used as a + streaming response from a view. + + :param template_name_or_list: The name of the template to render. If + a list is given, the first name to exist will be rendered. + :param context: The variables to make available in the template. + + .. versionadded:: 2.2 + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.get_or_select_template(template_name_or_list) + return _stream(app, template, context) + + +def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]: + """Render a template from the given source string with the given + context as a stream. This returns an iterator of strings, which can + be used as a streaming response from a view. + + :param source: The source code of the template to render. + :param context: The variables to make available in the template. + + .. versionadded:: 2.2 + """ + app = current_app._get_current_object() # type: ignore[attr-defined] + template = app.jinja_env.from_string(source) + return _stream(app, template, context) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/testing.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/testing.py new file mode 100644 index 000000000..a27b7c8fe --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/testing.py @@ -0,0 +1,298 @@ +from __future__ import annotations + +import importlib.metadata +import typing as t +from contextlib import contextmanager +from contextlib import ExitStack +from copy import copy +from types import TracebackType +from urllib.parse import urlsplit + +import werkzeug.test +from click.testing import CliRunner +from werkzeug.test import Client +from werkzeug.wrappers import Request as BaseRequest + +from .cli import ScriptInfo +from .sessions import SessionMixin + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import WSGIEnvironment + from werkzeug.test import TestResponse + + from .app import Flask + + +class EnvironBuilder(werkzeug.test.EnvironBuilder): + """An :class:`~werkzeug.test.EnvironBuilder`, that takes defaults from the + application. + + :param app: The Flask application to configure the environment from. + :param path: URL path being requested. + :param base_url: Base URL where the app is being served, which + ``path`` is relative to. If not given, built from + :data:`PREFERRED_URL_SCHEME`, ``subdomain``, + :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. + :param subdomain: Subdomain name to append to :data:`SERVER_NAME`. + :param url_scheme: Scheme to use instead of + :data:`PREFERRED_URL_SCHEME`. + :param json: If given, this is serialized as JSON and passed as + ``data``. Also defaults ``content_type`` to + ``application/json``. + :param args: other positional arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + :param kwargs: other keyword arguments passed to + :class:`~werkzeug.test.EnvironBuilder`. + """ + + def __init__( + self, + app: Flask, + path: str = "/", + base_url: str | None = None, + subdomain: str | None = None, + url_scheme: str | None = None, + *args: t.Any, + **kwargs: t.Any, + ) -> None: + assert not (base_url or subdomain or url_scheme) or ( + base_url is not None + ) != bool( + subdomain or url_scheme + ), 'Cannot pass "subdomain" or "url_scheme" with "base_url".' + + if base_url is None: + http_host = app.config.get("SERVER_NAME") or "localhost" + app_root = app.config["APPLICATION_ROOT"] + + if subdomain: + http_host = f"{subdomain}.{http_host}" + + if url_scheme is None: + url_scheme = app.config["PREFERRED_URL_SCHEME"] + + url = urlsplit(path) + base_url = ( + f"{url.scheme or url_scheme}://{url.netloc or http_host}" + f"/{app_root.lstrip('/')}" + ) + path = url.path + + if url.query: + sep = b"?" if isinstance(url.query, bytes) else "?" + path += sep + url.query + + self.app = app + super().__init__(path, base_url, *args, **kwargs) + + def json_dumps(self, obj: t.Any, **kwargs: t.Any) -> str: # type: ignore + """Serialize ``obj`` to a JSON-formatted string. + + The serialization will be configured according to the config associated + with this EnvironBuilder's ``app``. + """ + return self.app.json.dumps(obj, **kwargs) + + +_werkzeug_version = "" + + +def _get_werkzeug_version() -> str: + global _werkzeug_version + + if not _werkzeug_version: + _werkzeug_version = importlib.metadata.version("werkzeug") + + return _werkzeug_version + + +class FlaskClient(Client): + """Works like a regular Werkzeug test client but has knowledge about + Flask's contexts to defer the cleanup of the request context until + the end of a ``with`` block. For general information about how to + use this class refer to :class:`werkzeug.test.Client`. + + .. versionchanged:: 0.12 + `app.test_client()` includes preset default environment, which can be + set after instantiation of the `app.test_client()` object in + `client.environ_base`. + + Basic usage is outlined in the :doc:`/testing` chapter. + """ + + application: Flask + + def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: + super().__init__(*args, **kwargs) + self.preserve_context = False + self._new_contexts: list[t.ContextManager[t.Any]] = [] + self._context_stack = ExitStack() + self.environ_base = { + "REMOTE_ADDR": "127.0.0.1", + "HTTP_USER_AGENT": f"Werkzeug/{_get_werkzeug_version()}", + } + + @contextmanager + def session_transaction( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Iterator[SessionMixin]: + """When used in combination with a ``with`` statement this opens a + session transaction. This can be used to modify the session that + the test client uses. Once the ``with`` block is left the session is + stored back. + + :: + + with client.session_transaction() as session: + session['value'] = 42 + + Internally this is implemented by going through a temporary test + request context and since session handling could depend on + request variables this function accepts the same arguments as + :meth:`~flask.Flask.test_request_context` which are directly + passed through. + """ + if self._cookies is None: + raise TypeError( + "Cookies are disabled. Create a client with 'use_cookies=True'." + ) + + app = self.application + ctx = app.test_request_context(*args, **kwargs) + self._add_cookies_to_wsgi(ctx.request.environ) + + with ctx: + sess = app.session_interface.open_session(app, ctx.request) + + if sess is None: + raise RuntimeError("Session backend did not open a session.") + + yield sess + resp = app.response_class() + + if app.session_interface.is_null_session(sess): + return + + with ctx: + app.session_interface.save_session(app, sess, resp) + + self._update_cookies_from_response( + ctx.request.host.partition(":")[0], + ctx.request.path, + resp.headers.getlist("Set-Cookie"), + ) + + def _copy_environ(self, other: WSGIEnvironment) -> WSGIEnvironment: + out = {**self.environ_base, **other} + + if self.preserve_context: + out["werkzeug.debug.preserve_context"] = self._new_contexts.append + + return out + + def _request_from_builder_args( + self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] + ) -> BaseRequest: + kwargs["environ_base"] = self._copy_environ(kwargs.get("environ_base", {})) + builder = EnvironBuilder(self.application, *args, **kwargs) + + try: + return builder.get_request() + finally: + builder.close() + + def open( + self, + *args: t.Any, + buffered: bool = False, + follow_redirects: bool = False, + **kwargs: t.Any, + ) -> TestResponse: + if args and isinstance( + args[0], (werkzeug.test.EnvironBuilder, dict, BaseRequest) + ): + if isinstance(args[0], werkzeug.test.EnvironBuilder): + builder = copy(args[0]) + builder.environ_base = self._copy_environ(builder.environ_base or {}) # type: ignore[arg-type] + request = builder.get_request() + elif isinstance(args[0], dict): + request = EnvironBuilder.from_environ( + args[0], app=self.application, environ_base=self._copy_environ({}) + ).get_request() + else: + # isinstance(args[0], BaseRequest) + request = copy(args[0]) + request.environ = self._copy_environ(request.environ) + else: + # request is None + request = self._request_from_builder_args(args, kwargs) + + # Pop any previously preserved contexts. This prevents contexts + # from being preserved across redirects or multiple requests + # within a single block. + self._context_stack.close() + + response = super().open( + request, + buffered=buffered, + follow_redirects=follow_redirects, + ) + response.json_module = self.application.json # type: ignore[assignment] + + # Re-push contexts that were preserved during the request. + while self._new_contexts: + cm = self._new_contexts.pop() + self._context_stack.enter_context(cm) + + return response + + def __enter__(self) -> FlaskClient: + if self.preserve_context: + raise RuntimeError("Cannot nest client invocations") + self.preserve_context = True + return self + + def __exit__( + self, + exc_type: type | None, + exc_value: BaseException | None, + tb: TracebackType | None, + ) -> None: + self.preserve_context = False + self._context_stack.close() + + +class FlaskCliRunner(CliRunner): + """A :class:`~click.testing.CliRunner` for testing a Flask app's + CLI commands. Typically created using + :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`. + """ + + def __init__(self, app: Flask, **kwargs: t.Any) -> None: + self.app = app + super().__init__(**kwargs) + + def invoke( # type: ignore + self, cli: t.Any = None, args: t.Any = None, **kwargs: t.Any + ) -> t.Any: + """Invokes a CLI command in an isolated environment. See + :meth:`CliRunner.invoke ` for + full method documentation. See :ref:`testing-cli` for examples. + + If the ``obj`` argument is not given, passes an instance of + :class:`~flask.cli.ScriptInfo` that knows how to load the Flask + app being tested. + + :param cli: Command object to invoke. Default is the app's + :attr:`~flask.app.Flask.cli` group. + :param args: List of strings to invoke the command with. + + :return: a :class:`~click.testing.Result` object. + """ + if cli is None: + cli = self.app.cli + + if "obj" not in kwargs: + kwargs["obj"] = ScriptInfo(create_app=lambda: self.app) + + return super().invoke(cli, args, **kwargs) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/typing.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/typing.py new file mode 100644 index 000000000..cf6d4ae6d --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/typing.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +import typing as t + +if t.TYPE_CHECKING: # pragma: no cover + from _typeshed.wsgi import WSGIApplication # noqa: F401 + from werkzeug.datastructures import Headers # noqa: F401 + from werkzeug.sansio.response import Response # noqa: F401 + +# The possible types that are directly convertible or are a Response object. +ResponseValue = t.Union[ + "Response", + str, + bytes, + t.List[t.Any], + # Only dict is actually accepted, but Mapping allows for TypedDict. + t.Mapping[str, t.Any], + t.Iterator[str], + t.Iterator[bytes], +] + +# the possible types for an individual HTTP header +# This should be a Union, but mypy doesn't pass unless it's a TypeVar. +HeaderValue = t.Union[str, t.List[str], t.Tuple[str, ...]] + +# the possible types for HTTP headers +HeadersValue = t.Union[ + "Headers", + t.Mapping[str, HeaderValue], + t.Sequence[t.Tuple[str, HeaderValue]], +] + +# The possible types returned by a route function. +ResponseReturnValue = t.Union[ + ResponseValue, + t.Tuple[ResponseValue, HeadersValue], + t.Tuple[ResponseValue, int], + t.Tuple[ResponseValue, int, HeadersValue], + "WSGIApplication", +] + +# Allow any subclass of werkzeug.Response, such as the one from Flask, +# as a callback argument. Using werkzeug.Response directly makes a +# callback annotated with flask.Response fail type checking. +ResponseClass = t.TypeVar("ResponseClass", bound="Response") + +AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named +AfterRequestCallable = t.Union[ + t.Callable[[ResponseClass], ResponseClass], + t.Callable[[ResponseClass], t.Awaitable[ResponseClass]], +] +BeforeFirstRequestCallable = t.Union[ + t.Callable[[], None], t.Callable[[], t.Awaitable[None]] +] +BeforeRequestCallable = t.Union[ + t.Callable[[], t.Optional[ResponseReturnValue]], + t.Callable[[], t.Awaitable[t.Optional[ResponseReturnValue]]], +] +ShellContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]] +TeardownCallable = t.Union[ + t.Callable[[t.Optional[BaseException]], None], + t.Callable[[t.Optional[BaseException]], t.Awaitable[None]], +] +TemplateContextProcessorCallable = t.Union[ + t.Callable[[], t.Dict[str, t.Any]], + t.Callable[[], t.Awaitable[t.Dict[str, t.Any]]], +] +TemplateFilterCallable = t.Callable[..., t.Any] +TemplateGlobalCallable = t.Callable[..., t.Any] +TemplateTestCallable = t.Callable[..., bool] +URLDefaultCallable = t.Callable[[str, t.Dict[str, t.Any]], None] +URLValuePreprocessorCallable = t.Callable[ + [t.Optional[str], t.Optional[t.Dict[str, t.Any]]], None +] + +# This should take Exception, but that either breaks typing the argument +# with a specific exception, or decorating multiple times with different +# exceptions (and using a union type on the argument). +# https://github.com/pallets/flask/issues/4095 +# https://github.com/pallets/flask/issues/4295 +# https://github.com/pallets/flask/issues/4297 +ErrorHandlerCallable = t.Union[ + t.Callable[[t.Any], ResponseReturnValue], + t.Callable[[t.Any], t.Awaitable[ResponseReturnValue]], +] + +RouteCallable = t.Union[ + t.Callable[..., ResponseReturnValue], + t.Callable[..., t.Awaitable[ResponseReturnValue]], +] diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/views.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/views.py new file mode 100644 index 000000000..794fdc06c --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/views.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +import typing as t + +from . import typing as ft +from .globals import current_app +from .globals import request + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +http_method_funcs = frozenset( + ["get", "post", "head", "options", "delete", "put", "trace", "patch"] +) + + +class View: + """Subclass this class and override :meth:`dispatch_request` to + create a generic class-based view. Call :meth:`as_view` to create a + view function that creates an instance of the class with the given + arguments and calls its ``dispatch_request`` method with any URL + variables. + + See :doc:`views` for a detailed guide. + + .. code-block:: python + + class Hello(View): + init_every_request = False + + def dispatch_request(self, name): + return f"Hello, {name}!" + + app.add_url_rule( + "/hello/", view_func=Hello.as_view("hello") + ) + + Set :attr:`methods` on the class to change what methods the view + accepts. + + Set :attr:`decorators` on the class to apply a list of decorators to + the generated view function. Decorators applied to the class itself + will not be applied to the generated view function! + + Set :attr:`init_every_request` to ``False`` for efficiency, unless + you need to store request-global data on ``self``. + """ + + #: The methods this view is registered for. Uses the same default + #: (``["GET", "HEAD", "OPTIONS"]``) as ``route`` and + #: ``add_url_rule`` by default. + methods: t.ClassVar[t.Collection[str] | None] = None + + #: Control whether the ``OPTIONS`` method is handled automatically. + #: Uses the same default (``True``) as ``route`` and + #: ``add_url_rule`` by default. + provide_automatic_options: t.ClassVar[bool | None] = None + + #: A list of decorators to apply, in order, to the generated view + #: function. Remember that ``@decorator`` syntax is applied bottom + #: to top, so the first decorator in the list would be the bottom + #: decorator. + #: + #: .. versionadded:: 0.8 + decorators: t.ClassVar[list[t.Callable[[F], F]]] = [] + + #: Create a new instance of this view class for every request by + #: default. If a view subclass sets this to ``False``, the same + #: instance is used for every request. + #: + #: A single instance is more efficient, especially if complex setup + #: is done during init. However, storing data on ``self`` is no + #: longer safe across requests, and :data:`~flask.g` should be used + #: instead. + #: + #: .. versionadded:: 2.2 + init_every_request: t.ClassVar[bool] = True + + def dispatch_request(self) -> ft.ResponseReturnValue: + """The actual view function behavior. Subclasses must override + this and return a valid response. Any variables from the URL + rule are passed as keyword arguments. + """ + raise NotImplementedError() + + @classmethod + def as_view( + cls, name: str, *class_args: t.Any, **class_kwargs: t.Any + ) -> ft.RouteCallable: + """Convert the class into a view function that can be registered + for a route. + + By default, the generated view will create a new instance of the + view class for every request and call its + :meth:`dispatch_request` method. If the view class sets + :attr:`init_every_request` to ``False``, the same instance will + be used for every request. + + Except for ``name``, all other arguments passed to this method + are forwarded to the view class ``__init__`` method. + + .. versionchanged:: 2.2 + Added the ``init_every_request`` class attribute. + """ + if cls.init_every_request: + + def view(**kwargs: t.Any) -> ft.ResponseReturnValue: + self = view.view_class( # type: ignore[attr-defined] + *class_args, **class_kwargs + ) + return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] + + else: + self = cls(*class_args, **class_kwargs) + + def view(**kwargs: t.Any) -> ft.ResponseReturnValue: + return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] + + if cls.decorators: + view.__name__ = name + view.__module__ = cls.__module__ + for decorator in cls.decorators: + view = decorator(view) + + # We attach the view class to the view function for two reasons: + # first of all it allows us to easily figure out what class-based + # view this thing came from, secondly it's also used for instantiating + # the view class so you can actually replace it with something else + # for testing purposes and debugging. + view.view_class = cls # type: ignore + view.__name__ = name + view.__doc__ = cls.__doc__ + view.__module__ = cls.__module__ + view.methods = cls.methods # type: ignore + view.provide_automatic_options = cls.provide_automatic_options # type: ignore + return view + + +class MethodView(View): + """Dispatches request methods to the corresponding instance methods. + For example, if you implement a ``get`` method, it will be used to + handle ``GET`` requests. + + This can be useful for defining a REST API. + + :attr:`methods` is automatically set based on the methods defined on + the class. + + See :doc:`views` for a detailed guide. + + .. code-block:: python + + class CounterAPI(MethodView): + def get(self): + return str(session.get("counter", 0)) + + def post(self): + session["counter"] = session.get("counter", 0) + 1 + return redirect(url_for("counter")) + + app.add_url_rule( + "/counter", view_func=CounterAPI.as_view("counter") + ) + """ + + def __init_subclass__(cls, **kwargs: t.Any) -> None: + super().__init_subclass__(**kwargs) + + if "methods" not in cls.__dict__: + methods = set() + + for base in cls.__bases__: + if getattr(base, "methods", None): + methods.update(base.methods) # type: ignore[attr-defined] + + for key in http_method_funcs: + if hasattr(cls, key): + methods.add(key.upper()) + + if methods: + cls.methods = methods + + def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue: + meth = getattr(self, request.method.lower(), None) + + # If the request method is HEAD and we don't have a handler for it + # retry with GET. + if meth is None and request.method == "HEAD": + meth = getattr(self, "get", None) + + assert meth is not None, f"Unimplemented method {request.method!r}" + return current_app.ensure_sync(meth)(**kwargs) # type: ignore[no-any-return] diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/wrappers.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/wrappers.py new file mode 100644 index 000000000..c1eca8078 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask/wrappers.py @@ -0,0 +1,174 @@ +from __future__ import annotations + +import typing as t + +from werkzeug.exceptions import BadRequest +from werkzeug.exceptions import HTTPException +from werkzeug.wrappers import Request as RequestBase +from werkzeug.wrappers import Response as ResponseBase + +from . import json +from .globals import current_app +from .helpers import _split_blueprint_path + +if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.routing import Rule + + +class Request(RequestBase): + """The request object used by default in Flask. Remembers the + matched endpoint and view arguments. + + It is what ends up as :class:`~flask.request`. If you want to replace + the request object used you can subclass this and set + :attr:`~flask.Flask.request_class` to your subclass. + + The request object is a :class:`~werkzeug.wrappers.Request` subclass and + provides all of the attributes Werkzeug defines plus a few Flask + specific ones. + """ + + json_module: t.Any = json + + #: The internal URL rule that matched the request. This can be + #: useful to inspect which methods are allowed for the URL from + #: a before/after handler (``request.url_rule.methods``) etc. + #: Though if the request's method was invalid for the URL rule, + #: the valid list is available in ``routing_exception.valid_methods`` + #: instead (an attribute of the Werkzeug exception + #: :exc:`~werkzeug.exceptions.MethodNotAllowed`) + #: because the request was never internally bound. + #: + #: .. versionadded:: 0.6 + url_rule: Rule | None = None + + #: A dict of view arguments that matched the request. If an exception + #: happened when matching, this will be ``None``. + view_args: dict[str, t.Any] | None = None + + #: If matching the URL failed, this is the exception that will be + #: raised / was raised as part of the request handling. This is + #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or + #: something similar. + routing_exception: HTTPException | None = None + + @property + def max_content_length(self) -> int | None: # type: ignore[override] + """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" + if current_app: + return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return] + else: + return None + + @property + def endpoint(self) -> str | None: + """The endpoint that matched the request URL. + + This will be ``None`` if matching failed or has not been + performed yet. + + This in combination with :attr:`view_args` can be used to + reconstruct the same URL or a modified URL. + """ + if self.url_rule is not None: + return self.url_rule.endpoint + + return None + + @property + def blueprint(self) -> str | None: + """The registered name of the current blueprint. + + This will be ``None`` if the endpoint is not part of a + blueprint, or if URL matching failed or has not been performed + yet. + + This does not necessarily match the name the blueprint was + created with. It may have been nested, or registered with a + different name. + """ + endpoint = self.endpoint + + if endpoint is not None and "." in endpoint: + return endpoint.rpartition(".")[0] + + return None + + @property + def blueprints(self) -> list[str]: + """The registered names of the current blueprint upwards through + parent blueprints. + + This will be an empty list if there is no current blueprint, or + if URL matching failed. + + .. versionadded:: 2.0.1 + """ + name = self.blueprint + + if name is None: + return [] + + return _split_blueprint_path(name) + + def _load_form_data(self) -> None: + super()._load_form_data() + + # In debug mode we're replacing the files multidict with an ad-hoc + # subclass that raises a different error for key errors. + if ( + current_app + and current_app.debug + and self.mimetype != "multipart/form-data" + and not self.files + ): + from .debughelpers import attach_enctype_error_multidict + + attach_enctype_error_multidict(self) + + def on_json_loading_failed(self, e: ValueError | None) -> t.Any: + try: + return super().on_json_loading_failed(e) + except BadRequest as e: + if current_app and current_app.debug: + raise + + raise BadRequest() from e + + +class Response(ResponseBase): + """The response object that is used by default in Flask. Works like the + response object from Werkzeug but is set to have an HTML mimetype by + default. Quite often you don't have to create this object yourself because + :meth:`~flask.Flask.make_response` will take care of that for you. + + If you want to replace the response object used you can subclass this and + set :attr:`~flask.Flask.response_class` to your subclass. + + .. versionchanged:: 1.0 + JSON support is added to the response, like the request. This is useful + when testing to get the test client response data as JSON. + + .. versionchanged:: 1.0 + + Added :attr:`max_cookie_size`. + """ + + default_mimetype: str | None = "text/html" + + json_module = json + + autocorrect_location_header = False + + @property + def max_cookie_size(self) -> int: # type: ignore + """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. + + See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in + Werkzeug's docs. + """ + if current_app: + return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return] + + # return Werkzeug's default when not in an app context + return super().max_cookie_size diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/__init__.py new file mode 100644 index 000000000..458150efb --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +""" + flask_cors + ~~~~ + Flask-CORS is a simple extension to Flask allowing you to support cross + origin resource sharing (CORS) using a simple decorator. + + :copyright: (c) 2016 by Cory Dolphin. + :license: MIT, see LICENSE for more details. +""" +from .decorator import cross_origin +from .extension import CORS +from .version import __version__ + +__all__ = ['CORS', 'cross_origin'] + +# Set default logging handler to avoid "No handler found" warnings. +import logging +from logging import NullHandler + +# Set initial level to WARN. Users must manually enable logging for +# flask_cors to see our logging. +rootlogger = logging.getLogger(__name__) +rootlogger.addHandler(NullHandler()) + +if rootlogger.level == logging.NOTSET: + rootlogger.setLevel(logging.WARN) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/core.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/core.py new file mode 100644 index 000000000..5358036c2 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/core.py @@ -0,0 +1,382 @@ +# -*- coding: utf-8 -*- +""" + core + ~~~~ + Core functionality shared between the extension and the decorator. + + :copyright: (c) 2016 by Cory Dolphin. + :license: MIT, see LICENSE for more details. +""" +import re +import logging +from collections.abc import Iterable +from datetime import timedelta +from flask import request, current_app +from werkzeug.datastructures import Headers, MultiDict + +LOG = logging.getLogger(__name__) + +# Response Headers +ACL_ORIGIN = 'Access-Control-Allow-Origin' +ACL_METHODS = 'Access-Control-Allow-Methods' +ACL_ALLOW_HEADERS = 'Access-Control-Allow-Headers' +ACL_EXPOSE_HEADERS = 'Access-Control-Expose-Headers' +ACL_CREDENTIALS = 'Access-Control-Allow-Credentials' +ACL_MAX_AGE = 'Access-Control-Max-Age' +ACL_RESPONSE_PRIVATE_NETWORK = 'Access-Control-Allow-Private-Network' + +# Request Header +ACL_REQUEST_METHOD = 'Access-Control-Request-Method' +ACL_REQUEST_HEADERS = 'Access-Control-Request-Headers' +ACL_REQUEST_HEADER_PRIVATE_NETWORK = 'Access-Control-Request-Private-Network' + +ALL_METHODS = ['GET', 'HEAD', 'POST', 'OPTIONS', 'PUT', 'PATCH', 'DELETE'] +CONFIG_OPTIONS = ['CORS_ORIGINS', 'CORS_METHODS', 'CORS_ALLOW_HEADERS', + 'CORS_EXPOSE_HEADERS', 'CORS_SUPPORTS_CREDENTIALS', + 'CORS_MAX_AGE', 'CORS_SEND_WILDCARD', + 'CORS_AUTOMATIC_OPTIONS', 'CORS_VARY_HEADER', + 'CORS_RESOURCES', 'CORS_INTERCEPT_EXCEPTIONS', + 'CORS_ALWAYS_SEND'] +# Attribute added to request object by decorator to indicate that CORS +# was evaluated, in case the decorator and extension are both applied +# to a view. +FLASK_CORS_EVALUATED = '_FLASK_CORS_EVALUATED' + +# Strange, but this gets the type of a compiled regex, which is otherwise not +# exposed in a public API. +RegexObject = type(re.compile('')) +DEFAULT_OPTIONS = dict(origins='*', + methods=ALL_METHODS, + allow_headers='*', + expose_headers=None, + supports_credentials=False, + max_age=None, + send_wildcard=False, + automatic_options=True, + vary_header=True, + resources=r'/*', + intercept_exceptions=True, + always_send=True) + + +def parse_resources(resources): + if isinstance(resources, dict): + # To make the API more consistent with the decorator, allow a + # resource of '*', which is not actually a valid regexp. + resources = [(re_fix(k), v) for k, v in resources.items()] + + # Sort by regex length to provide consistency of matching and + # to provide a proxy for specificity of match. E.G. longer + # regular expressions are tried first. + def pattern_length(pair): + maybe_regex, _ = pair + return len(get_regexp_pattern(maybe_regex)) + + return sorted(resources, + key=pattern_length, + reverse=True) + + elif isinstance(resources, str): + return [(re_fix(resources), {})] + + elif isinstance(resources, Iterable): + return [(re_fix(r), {}) for r in resources] + + # Type of compiled regex is not part of the public API. Test for this + # at runtime. + elif isinstance(resources, RegexObject): + return [(re_fix(resources), {})] + + else: + raise ValueError("Unexpected value for resources argument.") + + +def get_regexp_pattern(regexp): + """ + Helper that returns regexp pattern from given value. + + :param regexp: regular expression to stringify + :type regexp: _sre.SRE_Pattern or str + :returns: string representation of given regexp pattern + :rtype: str + """ + try: + return regexp.pattern + except AttributeError: + return str(regexp) + + +def get_cors_origins(options, request_origin): + origins = options.get('origins') + wildcard = r'.*' in origins + + # If the Origin header is not present terminate this set of steps. + # The request is outside the scope of this specification.-- W3Spec + if request_origin: + LOG.debug("CORS request received with 'Origin' %s", request_origin) + + # If the allowed origins is an asterisk or 'wildcard', always match + if wildcard and options.get('send_wildcard'): + LOG.debug("Allowed origins are set to '*'. Sending wildcard CORS header.") + return ['*'] + # If the value of the Origin header is a case-sensitive match + # for any of the values in list of origins + elif try_match_any(request_origin, origins): + LOG.debug("The request's Origin header matches. Sending CORS headers.", ) + # Add a single Access-Control-Allow-Origin header, with either + # the value of the Origin header or the string "*" as value. + # -- W3Spec + return [request_origin] + else: + LOG.debug("The request's Origin header does not match any of allowed origins.") + return None + + + elif options.get('always_send'): + if wildcard: + # If wildcard is in the origins, even if 'send_wildcard' is False, + # simply send the wildcard. Unless supports_credentials is True, + # since that is forbidded by the spec.. + # It is the most-likely to be correct thing to do (the only other + # option is to return nothing, which almost certainly not what + # the developer wants if the '*' origin was specified. + if options.get('supports_credentials'): + return None + else: + return ['*'] + else: + # Return all origins that are not regexes. + return sorted([o for o in origins if not probably_regex(o)]) + + # Terminate these steps, return the original request untouched. + else: + LOG.debug("The request did not contain an 'Origin' header. This means the browser or client did not request CORS, ensure the Origin Header is set.") + return None + + +def get_allow_headers(options, acl_request_headers): + if acl_request_headers: + request_headers = [h.strip() for h in acl_request_headers.split(',')] + + # any header that matches in the allow_headers + matching_headers = filter( + lambda h: try_match_any(h, options.get('allow_headers')), + request_headers + ) + + return ', '.join(sorted(matching_headers)) + + return None + + +def get_cors_headers(options, request_headers, request_method): + origins_to_set = get_cors_origins(options, request_headers.get('Origin')) + headers = MultiDict() + + if not origins_to_set: # CORS is not enabled for this route + return headers + + for origin in origins_to_set: + headers.add(ACL_ORIGIN, origin) + + headers[ACL_EXPOSE_HEADERS] = options.get('expose_headers') + + if options.get('supports_credentials'): + headers[ACL_CREDENTIALS] = 'true' # case sensitive + + if ACL_REQUEST_HEADER_PRIVATE_NETWORK in request_headers \ + and request_headers.get(ACL_REQUEST_HEADER_PRIVATE_NETWORK) == 'true': + headers[ACL_RESPONSE_PRIVATE_NETWORK] = 'true' + + # This is a preflight request + # http://www.w3.org/TR/cors/#resource-preflight-requests + if request_method == 'OPTIONS': + acl_request_method = request_headers.get(ACL_REQUEST_METHOD, '').upper() + + # If there is no Access-Control-Request-Method header or if parsing + # failed, do not set any additional headers + if acl_request_method and acl_request_method in options.get('methods'): + + # If method is not a case-sensitive match for any of the values in + # list of methods do not set any additional headers and terminate + # this set of steps. + headers[ACL_ALLOW_HEADERS] = get_allow_headers(options, request_headers.get(ACL_REQUEST_HEADERS)) + headers[ACL_MAX_AGE] = options.get('max_age') + headers[ACL_METHODS] = options.get('methods') + else: + LOG.info("The request's Access-Control-Request-Method header does not match allowed methods. CORS headers will not be applied.") + + # http://www.w3.org/TR/cors/#resource-implementation + if options.get('vary_header'): + # Only set header if the origin returned will vary dynamically, + # i.e. if we are not returning an asterisk, and there are multiple + # origins that can be matched. + if headers[ACL_ORIGIN] == '*': + pass + elif (len(options.get('origins')) > 1 or + len(origins_to_set) > 1 or + any(map(probably_regex, options.get('origins')))): + headers.add('Vary', 'Origin') + + return MultiDict((k, v) for k, v in headers.items() if v) + + +def set_cors_headers(resp, options): + """ + Performs the actual evaluation of Flask-CORS options and actually + modifies the response object. + + This function is used both in the decorator and the after_request + callback + """ + + # If CORS has already been evaluated via the decorator, skip + if hasattr(resp, FLASK_CORS_EVALUATED): + LOG.debug('CORS have been already evaluated, skipping') + return resp + + # Some libraries, like OAuthlib, set resp.headers to non Multidict + # objects (Werkzeug Headers work as well). This is a problem because + # headers allow repeated values. + if (not isinstance(resp.headers, Headers) + and not isinstance(resp.headers, MultiDict)): + resp.headers = MultiDict(resp.headers) + + headers_to_set = get_cors_headers(options, request.headers, request.method) + + LOG.debug('Settings CORS headers: %s', str(headers_to_set)) + + for k, v in headers_to_set.items(): + resp.headers.add(k, v) + + return resp + +def probably_regex(maybe_regex): + if isinstance(maybe_regex, RegexObject): + return True + else: + common_regex_chars = ['*', '\\', ']', '?', '$', '^', '[', ']', '(', ')'] + # Use common characters used in regular expressions as a proxy + # for if this string is in fact a regex. + return any((c in maybe_regex for c in common_regex_chars)) + +def re_fix(reg): + """ + Replace the invalid regex r'*' with the valid, wildcard regex r'/.*' to + enable the CORS app extension to have a more user friendly api. + """ + return r'.*' if reg == r'*' else reg + + +def try_match_any(inst, patterns): + return any(try_match(inst, pattern) for pattern in patterns) + + +def try_match(request_origin, maybe_regex): + """Safely attempts to match a pattern or string to a request origin.""" + if isinstance(maybe_regex, RegexObject): + return re.match(maybe_regex, request_origin) + elif probably_regex(maybe_regex): + return re.match(maybe_regex, request_origin, flags=re.IGNORECASE) + else: + try: + return request_origin.lower() == maybe_regex.lower() + except AttributeError: + return request_origin == maybe_regex + + +def get_cors_options(appInstance, *dicts): + """ + Compute CORS options for an application by combining the DEFAULT_OPTIONS, + the app's configuration-specified options and any dictionaries passed. The + last specified option wins. + """ + options = DEFAULT_OPTIONS.copy() + options.update(get_app_kwarg_dict(appInstance)) + if dicts: + for d in dicts: + options.update(d) + + return serialize_options(options) + + +def get_app_kwarg_dict(appInstance=None): + """Returns the dictionary of CORS specific app configurations.""" + app = (appInstance or current_app) + + # In order to support blueprints which do not have a config attribute + app_config = getattr(app, 'config', {}) + + return { + k.lower().replace('cors_', ''): app_config.get(k) + for k in CONFIG_OPTIONS + if app_config.get(k) is not None + } + + +def flexible_str(obj): + """ + A more flexible str function which intelligently handles stringifying + strings, lists and other iterables. The results are lexographically sorted + to ensure generated responses are consistent when iterables such as Set + are used. + """ + if obj is None: + return None + elif not isinstance(obj, str) and isinstance(obj, Iterable): + return ", ".join(str(item) for item in sorted(obj)) + else: + return str(obj) + + +def serialize_option(options_dict, key, upper=False): + if key in options_dict: + value = flexible_str(options_dict[key]) + options_dict[key] = value.upper() if upper else value + + +def ensure_iterable(inst): + """ + Wraps scalars or string types as a list, or returns the iterable instance. + """ + if isinstance(inst, str): + return [inst] + elif not isinstance(inst, Iterable): + return [inst] + else: + return inst + +def sanitize_regex_param(param): + return [re_fix(x) for x in ensure_iterable(param)] + + +def serialize_options(opts): + """ + A helper method to serialize and processes the options dictionary. + """ + options = (opts or {}).copy() + + for key in opts.keys(): + if key not in DEFAULT_OPTIONS: + LOG.warning("Unknown option passed to Flask-CORS: %s", key) + + # Ensure origins is a list of allowed origins with at least one entry. + options['origins'] = sanitize_regex_param(options.get('origins')) + options['allow_headers'] = sanitize_regex_param(options.get('allow_headers')) + + # This is expressly forbidden by the spec. Raise a value error so people + # don't get burned in production. + if r'.*' in options['origins'] and options['supports_credentials'] and options['send_wildcard']: + raise ValueError("Cannot use supports_credentials in conjunction with" + "an origin string of '*'. See: " + "http://www.w3.org/TR/cors/#resource-requests") + + + + serialize_option(options, 'expose_headers') + serialize_option(options, 'methods', upper=True) + + if isinstance(options.get('max_age'), timedelta): + options['max_age'] = str(int(options['max_age'].total_seconds())) + + return options diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/decorator.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/decorator.py new file mode 100644 index 000000000..b61d22f80 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/decorator.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +""" + decorator + ~~~~ + This unit exposes a single decorator which should be used to wrap a + Flask route with. It accepts all parameters and options as + the CORS extension. + + :copyright: (c) 2016 by Cory Dolphin. + :license: MIT, see LICENSE for more details. +""" +import logging +from functools import update_wrapper +from flask import make_response, request, current_app + +from .core import get_cors_options, set_cors_headers, FLASK_CORS_EVALUATED + +LOG = logging.getLogger(__name__) + +def cross_origin(*args, **kwargs): + """ + This function is the decorator which is used to wrap a Flask route with. + In the simplest case, simply use the default parameters to allow all + origins in what is the most permissive configuration. If this method + modifies state or performs authentication which may be brute-forced, you + should add some degree of protection, such as Cross Site Request Forgery + protection. + + :param origins: + The origin, or list of origins to allow requests from. + The origin(s) may be regular expressions, case-sensitive strings, + or else an asterisk + + Default : '*' + :type origins: list, string or regex + + :param methods: + The method or list of methods which the allowed origins are allowed to + access for non-simple requests. + + Default : [GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE] + :type methods: list or string + + :param expose_headers: + The header or list which are safe to expose to the API of a CORS API + specification. + + Default : None + :type expose_headers: list or string + + :param allow_headers: + The header or list of header field names which can be used when this + resource is accessed by allowed origins. The header(s) may be regular + expressions, case-sensitive strings, or else an asterisk. + + Default : '*', allow all headers + :type allow_headers: list, string or regex + + :param supports_credentials: + Allows users to make authenticated requests. If true, injects the + `Access-Control-Allow-Credentials` header in responses. This allows + cookies and credentials to be submitted across domains. + + :note: This option cannot be used in conjunction with a '*' origin + + Default : False + :type supports_credentials: bool + + :param max_age: + The maximum time for which this CORS request maybe cached. This value + is set as the `Access-Control-Max-Age` header. + + Default : None + :type max_age: timedelta, integer, string or None + + :param send_wildcard: If True, and the origins parameter is `*`, a wildcard + `Access-Control-Allow-Origin` header is sent, rather than the + request's `Origin` header. + + Default : False + :type send_wildcard: bool + + :param vary_header: + If True, the header Vary: Origin will be returned as per the W3 + implementation guidelines. + + Setting this header when the `Access-Control-Allow-Origin` is + dynamically generated (e.g. when there is more than one allowed + origin, and an Origin than '*' is returned) informs CDNs and other + caches that the CORS headers are dynamic, and cannot be cached. + + If False, the Vary header will never be injected or altered. + + Default : True + :type vary_header: bool + + :param automatic_options: + Only applies to the `cross_origin` decorator. If True, Flask-CORS will + override Flask's default OPTIONS handling to return CORS headers for + OPTIONS requests. + + Default : True + :type automatic_options: bool + + """ + _options = kwargs + + def decorator(f): + LOG.debug("Enabling %s for cross_origin using options:%s", f, _options) + + # If True, intercept OPTIONS requests by modifying the view function, + # replicating Flask's default behavior, and wrapping the response with + # CORS headers. + # + # If f.provide_automatic_options is unset or True, Flask's route + # decorator (which is actually wraps the function object we return) + # intercepts OPTIONS handling, and requests will not have CORS headers + if _options.get('automatic_options', True): + f.required_methods = getattr(f, 'required_methods', set()) + f.required_methods.add('OPTIONS') + f.provide_automatic_options = False + + def wrapped_function(*args, **kwargs): + # Handle setting of Flask-Cors parameters + options = get_cors_options(current_app, _options) + + if options.get('automatic_options') and request.method == 'OPTIONS': + resp = current_app.make_default_options_response() + else: + resp = make_response(f(*args, **kwargs)) + + set_cors_headers(resp, options) + setattr(resp, FLASK_CORS_EVALUATED, True) + return resp + + return update_wrapper(wrapped_function, f) + return decorator diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/extension.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/extension.py new file mode 100644 index 000000000..6361dccfd --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/extension.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +""" + extension + ~~~~ + Flask-CORS is a simple extension to Flask allowing you to support cross + origin resource sharing (CORS) using a simple decorator. + + :copyright: (c) 2016 by Cory Dolphin. + :license: MIT, see LICENSE for more details. +""" +import logging +from urllib.parse import unquote_plus +from flask import request + +from .core import ( + parse_resources, + get_cors_options, + get_regexp_pattern, + ACL_ORIGIN, + try_match, + set_cors_headers +) + + +LOG = logging.getLogger(__name__) + +class CORS(object): + """ + Initializes Cross Origin Resource sharing for the application. The + arguments are identical to :py:func:`cross_origin`, with the addition of a + `resources` parameter. The resources parameter defines a series of regular + expressions for resource paths to match and optionally, the associated + options to be applied to the particular resource. These options are + identical to the arguments to :py:func:`cross_origin`. + + The settings for CORS are determined in the following order + + 1. Resource level settings (e.g when passed as a dictionary) + 2. Keyword argument settings + 3. App level configuration settings (e.g. CORS_*) + 4. Default settings + + Note: as it is possible for multiple regular expressions to match a + resource path, the regular expressions are first sorted by length, + from longest to shortest, in order to attempt to match the most + specific regular expression. This allows the definition of a + number of specific resource options, with a wildcard fallback + for all other resources. + + :param resources: + The series of regular expression and (optionally) associated CORS + options to be applied to the given resource path. + + If the argument is a dictionary, it's keys must be regular expressions, + and the values must be a dictionary of kwargs, identical to the kwargs + of this function. + + If the argument is a list, it is expected to be a list of regular + expressions, for which the app-wide configured options are applied. + + If the argument is a string, it is expected to be a regular expression + for which the app-wide configured options are applied. + + Default : Match all and apply app-level configuration + + :type resources: dict, iterable or string + + :param origins: + The origin, or list of origins to allow requests from. + The origin(s) may be regular expressions, case-sensitive strings, + or else an asterisk. + + .. note:: + + origins must include the schema and the port (if not port 80), + e.g., + `CORS(app, origins=["http://localhost:8000", "https://example.com"])`. + + Default : '*' + :type origins: list, string or regex + + :param methods: + The method or list of methods which the allowed origins are allowed to + access for non-simple requests. + + Default : [GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE] + :type methods: list or string + + :param expose_headers: + The header or list which are safe to expose to the API of a CORS API + specification. + + Default : None + :type expose_headers: list or string + + :param allow_headers: + The header or list of header field names which can be used when this + resource is accessed by allowed origins. The header(s) may be regular + expressions, case-sensitive strings, or else an asterisk. + + Default : '*', allow all headers + :type allow_headers: list, string or regex + + :param supports_credentials: + Allows users to make authenticated requests. If true, injects the + `Access-Control-Allow-Credentials` header in responses. This allows + cookies and credentials to be submitted across domains. + + :note: This option cannot be used in conjunction with a '*' origin + + Default : False + :type supports_credentials: bool + + :param max_age: + The maximum time for which this CORS request maybe cached. This value + is set as the `Access-Control-Max-Age` header. + + Default : None + :type max_age: timedelta, integer, string or None + + :param send_wildcard: If True, and the origins parameter is `*`, a wildcard + `Access-Control-Allow-Origin` header is sent, rather than the + request's `Origin` header. + + Default : False + :type send_wildcard: bool + + :param vary_header: + If True, the header Vary: Origin will be returned as per the W3 + implementation guidelines. + + Setting this header when the `Access-Control-Allow-Origin` is + dynamically generated (e.g. when there is more than one allowed + origin, and an Origin than '*' is returned) informs CDNs and other + caches that the CORS headers are dynamic, and cannot be cached. + + If False, the Vary header will never be injected or altered. + + Default : True + :type vary_header: bool + """ + + def __init__(self, app=None, **kwargs): + self._options = kwargs + if app is not None: + self.init_app(app, **kwargs) + + def init_app(self, app, **kwargs): + # The resources and options may be specified in the App Config, the CORS constructor + # or the kwargs to the call to init_app. + options = get_cors_options(app, self._options, kwargs) + + # Flatten our resources into a list of the form + # (pattern_or_regexp, dictionary_of_options) + resources = parse_resources(options.get('resources')) + + # Compute the options for each resource by combining the options from + # the app's configuration, the constructor, the kwargs to init_app, and + # finally the options specified in the resources dictionary. + resources = [ + (pattern, get_cors_options(app, options, opts)) + for (pattern, opts) in resources + ] + + # Create a human-readable form of these resources by converting the compiled + # regular expressions into strings. + resources_human = {get_regexp_pattern(pattern): opts for (pattern,opts) in resources} + LOG.debug("Configuring CORS with resources: %s", resources_human) + + cors_after_request = make_after_request_function(resources) + app.after_request(cors_after_request) + + # Wrap exception handlers with cross_origin + # These error handlers will still respect the behavior of the route + if options.get('intercept_exceptions', True): + def _after_request_decorator(f): + def wrapped_function(*args, **kwargs): + return cors_after_request(app.make_response(f(*args, **kwargs))) + return wrapped_function + + if hasattr(app, 'handle_exception'): + app.handle_exception = _after_request_decorator( + app.handle_exception) + app.handle_user_exception = _after_request_decorator( + app.handle_user_exception) + +def make_after_request_function(resources): + def cors_after_request(resp): + # If CORS headers are set in a view decorator, pass + if resp.headers is not None and resp.headers.get(ACL_ORIGIN): + LOG.debug('CORS have been already evaluated, skipping') + return resp + normalized_path = unquote_plus(request.path) + for res_regex, res_options in resources: + if try_match(normalized_path, res_regex): + LOG.debug("Request to '%r' matches CORS resource '%s'. Using options: %s", + request.path, get_regexp_pattern(res_regex), res_options) + set_cors_headers(resp, res_options) + break + else: + LOG.debug('No CORS rule matches') + return resp + return cors_after_request diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/version.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/version.py new file mode 100644 index 000000000..1a3bef532 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/flask_cors/version.py @@ -0,0 +1 @@ +__version__ = '4.0.1' diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt new file mode 100644 index 000000000..7b190ca67 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2011 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/METADATA b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/METADATA new file mode 100644 index 000000000..ddf546484 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/METADATA @@ -0,0 +1,60 @@ +Metadata-Version: 2.1 +Name: itsdangerous +Version: 2.2.0 +Summary: Safely pass data to untrusted environments and back. +Maintainer-email: Pallets +Requires-Python: >=3.8 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Typing :: Typed +Project-URL: Changes, https://itsdangerous.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://itsdangerous.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/itsdangerous/ + +# ItsDangerous + +... so better sign this + +Various helpers to pass data to untrusted environments and to get it +back safe and sound. Data is cryptographically signed to ensure that a +token has not been tampered with. + +It's possible to customize how data is serialized. Data is compressed as +needed. A timestamp can be added and verified automatically while +loading a token. + + +## A Simple Example + +Here's how you could generate a token for transmitting a user's id and +name between web requests. + +```python +from itsdangerous import URLSafeSerializer +auth_s = URLSafeSerializer("secret key", "auth") +token = auth_s.dumps({"id": 5, "name": "itsdangerous"}) + +print(token) +# eyJpZCI6NSwibmFtZSI6Iml0c2Rhbmdlcm91cyJ9.6YP6T0BaO67XP--9UzTrmurXSmg + +data = auth_s.loads(token) +print(data["name"]) +# itsdangerous +``` + + +## Donate + +The Pallets organization develops and supports ItsDangerous and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +[please donate today][]. + +[please donate today]: https://palletsprojects.com/donate + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/RECORD b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/RECORD new file mode 100644 index 000000000..13948767d --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/RECORD @@ -0,0 +1,22 @@ +itsdangerous-2.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +itsdangerous-2.2.0.dist-info/LICENSE.txt,sha256=Y68JiRtr6K0aQlLtQ68PTvun_JSOIoNnvtfzxa4LCdc,1475 +itsdangerous-2.2.0.dist-info/METADATA,sha256=0rk0-1ZwihuU5DnwJVwPWoEI4yWOyCexih3JyZHblhE,1924 +itsdangerous-2.2.0.dist-info/RECORD,, +itsdangerous-2.2.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 +itsdangerous/__init__.py,sha256=4SK75sCe29xbRgQE1ZQtMHnKUuZYAf3bSpZOrff1IAY,1427 +itsdangerous/__pycache__/__init__.cpython-312.pyc,, +itsdangerous/__pycache__/_json.cpython-312.pyc,, +itsdangerous/__pycache__/encoding.cpython-312.pyc,, +itsdangerous/__pycache__/exc.cpython-312.pyc,, +itsdangerous/__pycache__/serializer.cpython-312.pyc,, +itsdangerous/__pycache__/signer.cpython-312.pyc,, +itsdangerous/__pycache__/timed.cpython-312.pyc,, +itsdangerous/__pycache__/url_safe.cpython-312.pyc,, +itsdangerous/_json.py,sha256=wPQGmge2yZ9328EHKF6gadGeyGYCJQKxtU-iLKE6UnA,473 +itsdangerous/encoding.py,sha256=wwTz5q_3zLcaAdunk6_vSoStwGqYWe307Zl_U87aRFM,1409 +itsdangerous/exc.py,sha256=Rr3exo0MRFEcPZltwecyK16VV1bE2K9_F1-d-ljcUn4,3201 +itsdangerous/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +itsdangerous/serializer.py,sha256=PmdwADLqkSyQLZ0jOKAgDsAW4k_H0TlA71Ei3z0C5aI,15601 +itsdangerous/signer.py,sha256=YO0CV7NBvHA6j549REHJFUjUojw2pHqwcUpQnU7yNYQ,9647 +itsdangerous/timed.py,sha256=6RvDMqNumGMxf0-HlpaZdN9PUQQmRvrQGplKhxuivUs,8083 +itsdangerous/url_safe.py,sha256=az4e5fXi_vs-YbWj8YZwn4wiVKfeD--GEKRT5Ueu4P4,2505 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/WHEEL b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/WHEEL new file mode 100644 index 000000000..3b5e64b5e --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous-2.2.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.9.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/__init__.py new file mode 100644 index 000000000..ea55256eb --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/__init__.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import typing as t + +from .encoding import base64_decode as base64_decode +from .encoding import base64_encode as base64_encode +from .encoding import want_bytes as want_bytes +from .exc import BadData as BadData +from .exc import BadHeader as BadHeader +from .exc import BadPayload as BadPayload +from .exc import BadSignature as BadSignature +from .exc import BadTimeSignature as BadTimeSignature +from .exc import SignatureExpired as SignatureExpired +from .serializer import Serializer as Serializer +from .signer import HMACAlgorithm as HMACAlgorithm +from .signer import NoneAlgorithm as NoneAlgorithm +from .signer import Signer as Signer +from .timed import TimedSerializer as TimedSerializer +from .timed import TimestampSigner as TimestampSigner +from .url_safe import URLSafeSerializer as URLSafeSerializer +from .url_safe import URLSafeTimedSerializer as URLSafeTimedSerializer + + +def __getattr__(name: str) -> t.Any: + if name == "__version__": + import importlib.metadata + import warnings + + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " ItsDangerous 2.3. Use feature detection or" + " 'importlib.metadata.version(\"itsdangerous\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("itsdangerous") + + raise AttributeError(name) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/_json.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/_json.py new file mode 100644 index 000000000..fc23feaaf --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/_json.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +import json as _json +import typing as t + + +class _CompactJSON: + """Wrapper around json module that strips whitespace.""" + + @staticmethod + def loads(payload: str | bytes) -> t.Any: + return _json.loads(payload) + + @staticmethod + def dumps(obj: t.Any, **kwargs: t.Any) -> str: + kwargs.setdefault("ensure_ascii", False) + kwargs.setdefault("separators", (",", ":")) + return _json.dumps(obj, **kwargs) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/encoding.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/encoding.py new file mode 100644 index 000000000..f5ca80f90 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/encoding.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import base64 +import string +import struct +import typing as t + +from .exc import BadData + + +def want_bytes( + s: str | bytes, encoding: str = "utf-8", errors: str = "strict" +) -> bytes: + if isinstance(s, str): + s = s.encode(encoding, errors) + + return s + + +def base64_encode(string: str | bytes) -> bytes: + """Base64 encode a string of bytes or text. The resulting bytes are + safe to use in URLs. + """ + string = want_bytes(string) + return base64.urlsafe_b64encode(string).rstrip(b"=") + + +def base64_decode(string: str | bytes) -> bytes: + """Base64 decode a URL-safe string of bytes or text. The result is + bytes. + """ + string = want_bytes(string, encoding="ascii", errors="ignore") + string += b"=" * (-len(string) % 4) + + try: + return base64.urlsafe_b64decode(string) + except (TypeError, ValueError) as e: + raise BadData("Invalid base64-encoded data") from e + + +# The alphabet used by base64.urlsafe_* +_base64_alphabet = f"{string.ascii_letters}{string.digits}-_=".encode("ascii") + +_int64_struct = struct.Struct(">Q") +_int_to_bytes = _int64_struct.pack +_bytes_to_int = t.cast("t.Callable[[bytes], tuple[int]]", _int64_struct.unpack) + + +def int_to_bytes(num: int) -> bytes: + return _int_to_bytes(num).lstrip(b"\x00") + + +def bytes_to_int(bytestr: bytes) -> int: + return _bytes_to_int(bytestr.rjust(8, b"\x00"))[0] diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/exc.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/exc.py new file mode 100644 index 000000000..a75adcd52 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/exc.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +import typing as t +from datetime import datetime + + +class BadData(Exception): + """Raised if bad data of any sort was encountered. This is the base + for all exceptions that ItsDangerous defines. + + .. versionadded:: 0.15 + """ + + def __init__(self, message: str): + super().__init__(message) + self.message = message + + def __str__(self) -> str: + return self.message + + +class BadSignature(BadData): + """Raised if a signature does not match.""" + + def __init__(self, message: str, payload: t.Any | None = None): + super().__init__(message) + + #: The payload that failed the signature test. In some + #: situations you might still want to inspect this, even if + #: you know it was tampered with. + #: + #: .. versionadded:: 0.14 + self.payload: t.Any | None = payload + + +class BadTimeSignature(BadSignature): + """Raised if a time-based signature is invalid. This is a subclass + of :class:`BadSignature`. + """ + + def __init__( + self, + message: str, + payload: t.Any | None = None, + date_signed: datetime | None = None, + ): + super().__init__(message, payload) + + #: If the signature expired this exposes the date of when the + #: signature was created. This can be helpful in order to + #: tell the user how long a link has been gone stale. + #: + #: .. versionchanged:: 2.0 + #: The datetime value is timezone-aware rather than naive. + #: + #: .. versionadded:: 0.14 + self.date_signed = date_signed + + +class SignatureExpired(BadTimeSignature): + """Raised if a signature timestamp is older than ``max_age``. This + is a subclass of :exc:`BadTimeSignature`. + """ + + +class BadHeader(BadSignature): + """Raised if a signed header is invalid in some form. This only + happens for serializers that have a header that goes with the + signature. + + .. versionadded:: 0.24 + """ + + def __init__( + self, + message: str, + payload: t.Any | None = None, + header: t.Any | None = None, + original_error: Exception | None = None, + ): + super().__init__(message, payload) + + #: If the header is actually available but just malformed it + #: might be stored here. + self.header: t.Any | None = header + + #: If available, the error that indicates why the payload was + #: not valid. This might be ``None``. + self.original_error: Exception | None = original_error + + +class BadPayload(BadData): + """Raised if a payload is invalid. This could happen if the payload + is loaded despite an invalid signature, or if there is a mismatch + between the serializer and deserializer. The original exception + that occurred during loading is stored on as :attr:`original_error`. + + .. versionadded:: 0.15 + """ + + def __init__(self, message: str, original_error: Exception | None = None): + super().__init__(message) + + #: If available, the error that indicates why the payload was + #: not valid. This might be ``None``. + self.original_error: Exception | None = original_error diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/py.typed b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/serializer.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/serializer.py new file mode 100644 index 000000000..5ddf3871d --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/serializer.py @@ -0,0 +1,406 @@ +from __future__ import annotations + +import collections.abc as cabc +import json +import typing as t + +from .encoding import want_bytes +from .exc import BadPayload +from .exc import BadSignature +from .signer import _make_keys_list +from .signer import Signer + +if t.TYPE_CHECKING: + import typing_extensions as te + + # This should be either be str or bytes. To avoid having to specify the + # bound type, it falls back to a union if structural matching fails. + _TSerialized = te.TypeVar( + "_TSerialized", bound=t.Union[str, bytes], default=t.Union[str, bytes] + ) +else: + # Still available at runtime on Python < 3.13, but without the default. + _TSerialized = t.TypeVar("_TSerialized", bound=t.Union[str, bytes]) + + +class _PDataSerializer(t.Protocol[_TSerialized]): + def loads(self, payload: _TSerialized, /) -> t.Any: ... + # A signature with additional arguments is not handled correctly by type + # checkers right now, so an overload is used below for serializers that + # don't match this strict protocol. + def dumps(self, obj: t.Any, /) -> _TSerialized: ... + + +# Use TypeIs once it's available in typing_extensions or 3.13. +def is_text_serializer( + serializer: _PDataSerializer[t.Any], +) -> te.TypeGuard[_PDataSerializer[str]]: + """Checks whether a serializer generates text or binary.""" + return isinstance(serializer.dumps({}), str) + + +class Serializer(t.Generic[_TSerialized]): + """A serializer wraps a :class:`~itsdangerous.signer.Signer` to + enable serializing and securely signing data other than bytes. It + can unsign to verify that the data hasn't been changed. + + The serializer provides :meth:`dumps` and :meth:`loads`, similar to + :mod:`json`, and by default uses :mod:`json` internally to serialize + the data to bytes. + + The secret key should be a random string of ``bytes`` and should not + be saved to code or version control. Different salts should be used + to distinguish signing in different contexts. See :doc:`/concepts` + for information about the security of the secret key and salt. + + :param secret_key: The secret key to sign and verify with. Can be a + list of keys, oldest to newest, to support key rotation. + :param salt: Extra key to combine with ``secret_key`` to distinguish + signatures in different contexts. + :param serializer: An object that provides ``dumps`` and ``loads`` + methods for serializing data to a string. Defaults to + :attr:`default_serializer`, which defaults to :mod:`json`. + :param serializer_kwargs: Keyword arguments to pass when calling + ``serializer.dumps``. + :param signer: A ``Signer`` class to instantiate when signing data. + Defaults to :attr:`default_signer`, which defaults to + :class:`~itsdangerous.signer.Signer`. + :param signer_kwargs: Keyword arguments to pass when instantiating + the ``Signer`` class. + :param fallback_signers: List of signer parameters to try when + unsigning with the default signer fails. Each item can be a dict + of ``signer_kwargs``, a ``Signer`` class, or a tuple of + ``(signer, signer_kwargs)``. Defaults to + :attr:`default_fallback_signers`. + + .. versionchanged:: 2.0 + Added support for key rotation by passing a list to + ``secret_key``. + + .. versionchanged:: 2.0 + Removed the default SHA-512 fallback signer from + ``default_fallback_signers``. + + .. versionchanged:: 1.1 + Added support for ``fallback_signers`` and configured a default + SHA-512 fallback. This fallback is for users who used the yanked + 1.0.0 release which defaulted to SHA-512. + + .. versionchanged:: 0.14 + The ``signer`` and ``signer_kwargs`` parameters were added to + the constructor. + """ + + #: The default serialization module to use to serialize data to a + #: string internally. The default is :mod:`json`, but can be changed + #: to any object that provides ``dumps`` and ``loads`` methods. + default_serializer: _PDataSerializer[t.Any] = json + + #: The default ``Signer`` class to instantiate when signing data. + #: The default is :class:`itsdangerous.signer.Signer`. + default_signer: type[Signer] = Signer + + #: The default fallback signers to try when unsigning fails. + default_fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] = [] + + # Serializer[str] if no data serializer is provided, or if it returns str. + @t.overload + def __init__( + self: Serializer[str], + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous", + serializer: None | _PDataSerializer[str] = None, + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + # Serializer[bytes] with a bytes data serializer positional argument. + @t.overload + def __init__( + self: Serializer[bytes], + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None, + serializer: _PDataSerializer[bytes], + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + # Serializer[bytes] with a bytes data serializer keyword argument. + @t.overload + def __init__( + self: Serializer[bytes], + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous", + *, + serializer: _PDataSerializer[bytes], + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + # Fall back with a positional argument. If the strict signature of + # _PDataSerializer doesn't match, fall back to a union, requiring the user + # to specify the type. + @t.overload + def __init__( + self, + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None, + serializer: t.Any, + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + # Fall back with a keyword argument. + @t.overload + def __init__( + self, + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous", + *, + serializer: t.Any, + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): ... + + def __init__( + self, + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous", + serializer: t.Any | None = None, + serializer_kwargs: dict[str, t.Any] | None = None, + signer: type[Signer] | None = None, + signer_kwargs: dict[str, t.Any] | None = None, + fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] + | None = None, + ): + #: The list of secret keys to try for verifying signatures, from + #: oldest to newest. The newest (last) key is used for signing. + #: + #: This allows a key rotation system to keep a list of allowed + #: keys and remove expired ones. + self.secret_keys: list[bytes] = _make_keys_list(secret_key) + + if salt is not None: + salt = want_bytes(salt) + # if salt is None then the signer's default is used + + self.salt = salt + + if serializer is None: + serializer = self.default_serializer + + self.serializer: _PDataSerializer[_TSerialized] = serializer + self.is_text_serializer: bool = is_text_serializer(serializer) + + if signer is None: + signer = self.default_signer + + self.signer: type[Signer] = signer + self.signer_kwargs: dict[str, t.Any] = signer_kwargs or {} + + if fallback_signers is None: + fallback_signers = list(self.default_fallback_signers) + + self.fallback_signers: list[ + dict[str, t.Any] | tuple[type[Signer], dict[str, t.Any]] | type[Signer] + ] = fallback_signers + self.serializer_kwargs: dict[str, t.Any] = serializer_kwargs or {} + + @property + def secret_key(self) -> bytes: + """The newest (last) entry in the :attr:`secret_keys` list. This + is for compatibility from before key rotation support was added. + """ + return self.secret_keys[-1] + + def load_payload( + self, payload: bytes, serializer: _PDataSerializer[t.Any] | None = None + ) -> t.Any: + """Loads the encoded object. This function raises + :class:`.BadPayload` if the payload is not valid. The + ``serializer`` parameter can be used to override the serializer + stored on the class. The encoded ``payload`` should always be + bytes. + """ + if serializer is None: + use_serializer = self.serializer + is_text = self.is_text_serializer + else: + use_serializer = serializer + is_text = is_text_serializer(serializer) + + try: + if is_text: + return use_serializer.loads(payload.decode("utf-8")) # type: ignore[arg-type] + + return use_serializer.loads(payload) # type: ignore[arg-type] + except Exception as e: + raise BadPayload( + "Could not load the payload because an exception" + " occurred on unserializing the data.", + original_error=e, + ) from e + + def dump_payload(self, obj: t.Any) -> bytes: + """Dumps the encoded object. The return value is always bytes. + If the internal serializer returns text, the value will be + encoded as UTF-8. + """ + return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs)) + + def make_signer(self, salt: str | bytes | None = None) -> Signer: + """Creates a new instance of the signer to be used. The default + implementation uses the :class:`.Signer` base class. + """ + if salt is None: + salt = self.salt + + return self.signer(self.secret_keys, salt=salt, **self.signer_kwargs) + + def iter_unsigners(self, salt: str | bytes | None = None) -> cabc.Iterator[Signer]: + """Iterates over all signers to be tried for unsigning. Starts + with the configured signer, then constructs each signer + specified in ``fallback_signers``. + """ + if salt is None: + salt = self.salt + + yield self.make_signer(salt) + + for fallback in self.fallback_signers: + if isinstance(fallback, dict): + kwargs = fallback + fallback = self.signer + elif isinstance(fallback, tuple): + fallback, kwargs = fallback + else: + kwargs = self.signer_kwargs + + for secret_key in self.secret_keys: + yield fallback(secret_key, salt=salt, **kwargs) + + def dumps(self, obj: t.Any, salt: str | bytes | None = None) -> _TSerialized: + """Returns a signed string serialized with the internal + serializer. The return value can be either a byte or unicode + string depending on the format of the internal serializer. + """ + payload = want_bytes(self.dump_payload(obj)) + rv = self.make_signer(salt).sign(payload) + + if self.is_text_serializer: + return rv.decode("utf-8") # type: ignore[return-value] + + return rv # type: ignore[return-value] + + def dump(self, obj: t.Any, f: t.IO[t.Any], salt: str | bytes | None = None) -> None: + """Like :meth:`dumps` but dumps into a file. The file handle has + to be compatible with what the internal serializer expects. + """ + f.write(self.dumps(obj, salt)) + + def loads( + self, s: str | bytes, salt: str | bytes | None = None, **kwargs: t.Any + ) -> t.Any: + """Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the + signature validation fails. + """ + s = want_bytes(s) + last_exception = None + + for signer in self.iter_unsigners(salt): + try: + return self.load_payload(signer.unsign(s)) + except BadSignature as err: + last_exception = err + + raise t.cast(BadSignature, last_exception) + + def load(self, f: t.IO[t.Any], salt: str | bytes | None = None) -> t.Any: + """Like :meth:`loads` but loads from a file.""" + return self.loads(f.read(), salt) + + def loads_unsafe( + self, s: str | bytes, salt: str | bytes | None = None + ) -> tuple[bool, t.Any]: + """Like :meth:`loads` but without verifying the signature. This + is potentially very dangerous to use depending on how your + serializer works. The return value is ``(signature_valid, + payload)`` instead of just the payload. The first item will be a + boolean that indicates if the signature is valid. This function + never fails. + + Use it for debugging only and if you know that your serializer + module is not exploitable (for example, do not use it with a + pickle serializer). + + .. versionadded:: 0.15 + """ + return self._loads_unsafe_impl(s, salt) + + def _loads_unsafe_impl( + self, + s: str | bytes, + salt: str | bytes | None, + load_kwargs: dict[str, t.Any] | None = None, + load_payload_kwargs: dict[str, t.Any] | None = None, + ) -> tuple[bool, t.Any]: + """Low level helper function to implement :meth:`loads_unsafe` + in serializer subclasses. + """ + if load_kwargs is None: + load_kwargs = {} + + try: + return True, self.loads(s, salt=salt, **load_kwargs) + except BadSignature as e: + if e.payload is None: + return False, None + + if load_payload_kwargs is None: + load_payload_kwargs = {} + + try: + return ( + False, + self.load_payload(e.payload, **load_payload_kwargs), + ) + except BadPayload: + return False, None + + def load_unsafe( + self, f: t.IO[t.Any], salt: str | bytes | None = None + ) -> tuple[bool, t.Any]: + """Like :meth:`loads_unsafe` but loads from a file. + + .. versionadded:: 0.15 + """ + return self.loads_unsafe(f.read(), salt=salt) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/signer.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/signer.py new file mode 100644 index 000000000..e324dc03d --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/signer.py @@ -0,0 +1,266 @@ +from __future__ import annotations + +import collections.abc as cabc +import hashlib +import hmac +import typing as t + +from .encoding import _base64_alphabet +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import want_bytes +from .exc import BadSignature + + +class SigningAlgorithm: + """Subclasses must implement :meth:`get_signature` to provide + signature generation functionality. + """ + + def get_signature(self, key: bytes, value: bytes) -> bytes: + """Returns the signature for the given key and value.""" + raise NotImplementedError() + + def verify_signature(self, key: bytes, value: bytes, sig: bytes) -> bool: + """Verifies the given signature matches the expected + signature. + """ + return hmac.compare_digest(sig, self.get_signature(key, value)) + + +class NoneAlgorithm(SigningAlgorithm): + """Provides an algorithm that does not perform any signing and + returns an empty signature. + """ + + def get_signature(self, key: bytes, value: bytes) -> bytes: + return b"" + + +def _lazy_sha1(string: bytes = b"") -> t.Any: + """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include + SHA-1, in which case the import and use as a default would fail before the + developer can configure something else. + """ + return hashlib.sha1(string) + + +class HMACAlgorithm(SigningAlgorithm): + """Provides signature generation using HMACs.""" + + #: The digest method to use with the MAC algorithm. This defaults to + #: SHA1, but can be changed to any other function in the hashlib + #: module. + default_digest_method: t.Any = staticmethod(_lazy_sha1) + + def __init__(self, digest_method: t.Any = None): + if digest_method is None: + digest_method = self.default_digest_method + + self.digest_method: t.Any = digest_method + + def get_signature(self, key: bytes, value: bytes) -> bytes: + mac = hmac.new(key, msg=value, digestmod=self.digest_method) + return mac.digest() + + +def _make_keys_list( + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], +) -> list[bytes]: + if isinstance(secret_key, (str, bytes)): + return [want_bytes(secret_key)] + + return [want_bytes(s) for s in secret_key] # pyright: ignore + + +class Signer: + """A signer securely signs bytes, then unsigns them to verify that + the value hasn't been changed. + + The secret key should be a random string of ``bytes`` and should not + be saved to code or version control. Different salts should be used + to distinguish signing in different contexts. See :doc:`/concepts` + for information about the security of the secret key and salt. + + :param secret_key: The secret key to sign and verify with. Can be a + list of keys, oldest to newest, to support key rotation. + :param salt: Extra key to combine with ``secret_key`` to distinguish + signatures in different contexts. + :param sep: Separator between the signature and value. + :param key_derivation: How to derive the signing key from the secret + key and salt. Possible values are ``concat``, ``django-concat``, + or ``hmac``. Defaults to :attr:`default_key_derivation`, which + defaults to ``django-concat``. + :param digest_method: Hash function to use when generating the HMAC + signature. Defaults to :attr:`default_digest_method`, which + defaults to :func:`hashlib.sha1`. Note that the security of the + hash alone doesn't apply when used intermediately in HMAC. + :param algorithm: A :class:`SigningAlgorithm` instance to use + instead of building a default :class:`HMACAlgorithm` with the + ``digest_method``. + + .. versionchanged:: 2.0 + Added support for key rotation by passing a list to + ``secret_key``. + + .. versionchanged:: 0.18 + ``algorithm`` was added as an argument to the class constructor. + + .. versionchanged:: 0.14 + ``key_derivation`` and ``digest_method`` were added as arguments + to the class constructor. + """ + + #: The default digest method to use for the signer. The default is + #: :func:`hashlib.sha1`, but can be changed to any :mod:`hashlib` or + #: compatible object. Note that the security of the hash alone + #: doesn't apply when used intermediately in HMAC. + #: + #: .. versionadded:: 0.14 + default_digest_method: t.Any = staticmethod(_lazy_sha1) + + #: The default scheme to use to derive the signing key from the + #: secret key and salt. The default is ``django-concat``. Possible + #: values are ``concat``, ``django-concat``, and ``hmac``. + #: + #: .. versionadded:: 0.14 + default_key_derivation: str = "django-concat" + + def __init__( + self, + secret_key: str | bytes | cabc.Iterable[str] | cabc.Iterable[bytes], + salt: str | bytes | None = b"itsdangerous.Signer", + sep: str | bytes = b".", + key_derivation: str | None = None, + digest_method: t.Any | None = None, + algorithm: SigningAlgorithm | None = None, + ): + #: The list of secret keys to try for verifying signatures, from + #: oldest to newest. The newest (last) key is used for signing. + #: + #: This allows a key rotation system to keep a list of allowed + #: keys and remove expired ones. + self.secret_keys: list[bytes] = _make_keys_list(secret_key) + self.sep: bytes = want_bytes(sep) + + if self.sep in _base64_alphabet: + raise ValueError( + "The given separator cannot be used because it may be" + " contained in the signature itself. ASCII letters," + " digits, and '-_=' must not be used." + ) + + if salt is not None: + salt = want_bytes(salt) + else: + salt = b"itsdangerous.Signer" + + self.salt = salt + + if key_derivation is None: + key_derivation = self.default_key_derivation + + self.key_derivation: str = key_derivation + + if digest_method is None: + digest_method = self.default_digest_method + + self.digest_method: t.Any = digest_method + + if algorithm is None: + algorithm = HMACAlgorithm(self.digest_method) + + self.algorithm: SigningAlgorithm = algorithm + + @property + def secret_key(self) -> bytes: + """The newest (last) entry in the :attr:`secret_keys` list. This + is for compatibility from before key rotation support was added. + """ + return self.secret_keys[-1] + + def derive_key(self, secret_key: str | bytes | None = None) -> bytes: + """This method is called to derive the key. The default key + derivation choices can be overridden here. Key derivation is not + intended to be used as a security method to make a complex key + out of a short password. Instead you should use large random + secret keys. + + :param secret_key: A specific secret key to derive from. + Defaults to the last item in :attr:`secret_keys`. + + .. versionchanged:: 2.0 + Added the ``secret_key`` parameter. + """ + if secret_key is None: + secret_key = self.secret_keys[-1] + else: + secret_key = want_bytes(secret_key) + + if self.key_derivation == "concat": + return t.cast(bytes, self.digest_method(self.salt + secret_key).digest()) + elif self.key_derivation == "django-concat": + return t.cast( + bytes, self.digest_method(self.salt + b"signer" + secret_key).digest() + ) + elif self.key_derivation == "hmac": + mac = hmac.new(secret_key, digestmod=self.digest_method) + mac.update(self.salt) + return mac.digest() + elif self.key_derivation == "none": + return secret_key + else: + raise TypeError("Unknown key derivation method") + + def get_signature(self, value: str | bytes) -> bytes: + """Returns the signature for the given value.""" + value = want_bytes(value) + key = self.derive_key() + sig = self.algorithm.get_signature(key, value) + return base64_encode(sig) + + def sign(self, value: str | bytes) -> bytes: + """Signs the given string.""" + value = want_bytes(value) + return value + self.sep + self.get_signature(value) + + def verify_signature(self, value: str | bytes, sig: str | bytes) -> bool: + """Verifies the signature for the given value.""" + try: + sig = base64_decode(sig) + except Exception: + return False + + value = want_bytes(value) + + for secret_key in reversed(self.secret_keys): + key = self.derive_key(secret_key) + + if self.algorithm.verify_signature(key, value, sig): + return True + + return False + + def unsign(self, signed_value: str | bytes) -> bytes: + """Unsigns the given string.""" + signed_value = want_bytes(signed_value) + + if self.sep not in signed_value: + raise BadSignature(f"No {self.sep!r} found in value") + + value, sig = signed_value.rsplit(self.sep, 1) + + if self.verify_signature(value, sig): + return value + + raise BadSignature(f"Signature {sig!r} does not match", payload=value) + + def validate(self, signed_value: str | bytes) -> bool: + """Only validates the given signed value. Returns ``True`` if + the signature exists and is valid. + """ + try: + self.unsign(signed_value) + return True + except BadSignature: + return False diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/timed.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/timed.py new file mode 100644 index 000000000..73843755d --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/timed.py @@ -0,0 +1,228 @@ +from __future__ import annotations + +import collections.abc as cabc +import time +import typing as t +from datetime import datetime +from datetime import timezone + +from .encoding import base64_decode +from .encoding import base64_encode +from .encoding import bytes_to_int +from .encoding import int_to_bytes +from .encoding import want_bytes +from .exc import BadSignature +from .exc import BadTimeSignature +from .exc import SignatureExpired +from .serializer import _TSerialized +from .serializer import Serializer +from .signer import Signer + + +class TimestampSigner(Signer): + """Works like the regular :class:`.Signer` but also records the time + of the signing and can be used to expire signatures. The + :meth:`unsign` method can raise :exc:`.SignatureExpired` if the + unsigning failed because the signature is expired. + """ + + def get_timestamp(self) -> int: + """Returns the current timestamp. The function must return an + integer. + """ + return int(time.time()) + + def timestamp_to_datetime(self, ts: int) -> datetime: + """Convert the timestamp from :meth:`get_timestamp` into an + aware :class`datetime.datetime` in UTC. + + .. versionchanged:: 2.0 + The timestamp is returned as a timezone-aware ``datetime`` + in UTC rather than a naive ``datetime`` assumed to be UTC. + """ + return datetime.fromtimestamp(ts, tz=timezone.utc) + + def sign(self, value: str | bytes) -> bytes: + """Signs the given string and also attaches time information.""" + value = want_bytes(value) + timestamp = base64_encode(int_to_bytes(self.get_timestamp())) + sep = want_bytes(self.sep) + value = value + sep + timestamp + return value + sep + self.get_signature(value) + + # Ignore overlapping signatures check, return_timestamp is the only + # parameter that affects the return type. + + @t.overload + def unsign( # type: ignore[overload-overlap] + self, + signed_value: str | bytes, + max_age: int | None = None, + return_timestamp: t.Literal[False] = False, + ) -> bytes: ... + + @t.overload + def unsign( + self, + signed_value: str | bytes, + max_age: int | None = None, + return_timestamp: t.Literal[True] = True, + ) -> tuple[bytes, datetime]: ... + + def unsign( + self, + signed_value: str | bytes, + max_age: int | None = None, + return_timestamp: bool = False, + ) -> tuple[bytes, datetime] | bytes: + """Works like the regular :meth:`.Signer.unsign` but can also + validate the time. See the base docstring of the class for + the general behavior. If ``return_timestamp`` is ``True`` the + timestamp of the signature will be returned as an aware + :class:`datetime.datetime` object in UTC. + + .. versionchanged:: 2.0 + The timestamp is returned as a timezone-aware ``datetime`` + in UTC rather than a naive ``datetime`` assumed to be UTC. + """ + try: + result = super().unsign(signed_value) + sig_error = None + except BadSignature as e: + sig_error = e + result = e.payload or b"" + + sep = want_bytes(self.sep) + + # If there is no timestamp in the result there is something + # seriously wrong. In case there was a signature error, we raise + # that one directly, otherwise we have a weird situation in + # which we shouldn't have come except someone uses a time-based + # serializer on non-timestamp data, so catch that. + if sep not in result: + if sig_error: + raise sig_error + + raise BadTimeSignature("timestamp missing", payload=result) + + value, ts_bytes = result.rsplit(sep, 1) + ts_int: int | None = None + ts_dt: datetime | None = None + + try: + ts_int = bytes_to_int(base64_decode(ts_bytes)) + except Exception: + pass + + # Signature is *not* okay. Raise a proper error now that we have + # split the value and the timestamp. + if sig_error is not None: + if ts_int is not None: + try: + ts_dt = self.timestamp_to_datetime(ts_int) + except (ValueError, OSError, OverflowError) as exc: + # Windows raises OSError + # 32-bit raises OverflowError + raise BadTimeSignature( + "Malformed timestamp", payload=value + ) from exc + + raise BadTimeSignature(str(sig_error), payload=value, date_signed=ts_dt) + + # Signature was okay but the timestamp is actually not there or + # malformed. Should not happen, but we handle it anyway. + if ts_int is None: + raise BadTimeSignature("Malformed timestamp", payload=value) + + # Check timestamp is not older than max_age + if max_age is not None: + age = self.get_timestamp() - ts_int + + if age > max_age: + raise SignatureExpired( + f"Signature age {age} > {max_age} seconds", + payload=value, + date_signed=self.timestamp_to_datetime(ts_int), + ) + + if age < 0: + raise SignatureExpired( + f"Signature age {age} < 0 seconds", + payload=value, + date_signed=self.timestamp_to_datetime(ts_int), + ) + + if return_timestamp: + return value, self.timestamp_to_datetime(ts_int) + + return value + + def validate(self, signed_value: str | bytes, max_age: int | None = None) -> bool: + """Only validates the given signed value. Returns ``True`` if + the signature exists and is valid.""" + try: + self.unsign(signed_value, max_age=max_age) + return True + except BadSignature: + return False + + +class TimedSerializer(Serializer[_TSerialized]): + """Uses :class:`TimestampSigner` instead of the default + :class:`.Signer`. + """ + + default_signer: type[TimestampSigner] = TimestampSigner + + def iter_unsigners( + self, salt: str | bytes | None = None + ) -> cabc.Iterator[TimestampSigner]: + return t.cast("cabc.Iterator[TimestampSigner]", super().iter_unsigners(salt)) + + # TODO: Signature is incompatible because parameters were added + # before salt. + + def loads( # type: ignore[override] + self, + s: str | bytes, + max_age: int | None = None, + return_timestamp: bool = False, + salt: str | bytes | None = None, + ) -> t.Any: + """Reverse of :meth:`dumps`, raises :exc:`.BadSignature` if the + signature validation fails. If a ``max_age`` is provided it will + ensure the signature is not older than that time in seconds. In + case the signature is outdated, :exc:`.SignatureExpired` is + raised. All arguments are forwarded to the signer's + :meth:`~TimestampSigner.unsign` method. + """ + s = want_bytes(s) + last_exception = None + + for signer in self.iter_unsigners(salt): + try: + base64d, timestamp = signer.unsign( + s, max_age=max_age, return_timestamp=True + ) + payload = self.load_payload(base64d) + + if return_timestamp: + return payload, timestamp + + return payload + except SignatureExpired: + # The signature was unsigned successfully but was + # expired. Do not try the next signer. + raise + except BadSignature as err: + last_exception = err + + raise t.cast(BadSignature, last_exception) + + def loads_unsafe( # type: ignore[override] + self, + s: str | bytes, + max_age: int | None = None, + salt: str | bytes | None = None, + ) -> tuple[bool, t.Any]: + return self._loads_unsafe_impl(s, salt, load_kwargs={"max_age": max_age}) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/url_safe.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/url_safe.py new file mode 100644 index 000000000..56a079331 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/itsdangerous/url_safe.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import typing as t +import zlib + +from ._json import _CompactJSON +from .encoding import base64_decode +from .encoding import base64_encode +from .exc import BadPayload +from .serializer import _PDataSerializer +from .serializer import Serializer +from .timed import TimedSerializer + + +class URLSafeSerializerMixin(Serializer[str]): + """Mixed in with a regular serializer it will attempt to zlib + compress the string to make it shorter if necessary. It will also + base64 encode the string so that it can safely be placed in a URL. + """ + + default_serializer: _PDataSerializer[str] = _CompactJSON + + def load_payload( + self, + payload: bytes, + *args: t.Any, + serializer: t.Any | None = None, + **kwargs: t.Any, + ) -> t.Any: + decompress = False + + if payload.startswith(b"."): + payload = payload[1:] + decompress = True + + try: + json = base64_decode(payload) + except Exception as e: + raise BadPayload( + "Could not base64 decode the payload because of an exception", + original_error=e, + ) from e + + if decompress: + try: + json = zlib.decompress(json) + except Exception as e: + raise BadPayload( + "Could not zlib decompress the payload before decoding the payload", + original_error=e, + ) from e + + return super().load_payload(json, *args, **kwargs) + + def dump_payload(self, obj: t.Any) -> bytes: + json = super().dump_payload(obj) + is_compressed = False + compressed = zlib.compress(json) + + if len(compressed) < (len(json) - 1): + json = compressed + is_compressed = True + + base64d = base64_encode(json) + + if is_compressed: + base64d = b"." + base64d + + return base64d + + +class URLSafeSerializer(URLSafeSerializerMixin, Serializer[str]): + """Works like :class:`.Serializer` but dumps and loads into a URL + safe string consisting of the upper and lowercase character of the + alphabet as well as ``'_'``, ``'-'`` and ``'.'``. + """ + + +class URLSafeTimedSerializer(URLSafeSerializerMixin, TimedSerializer[str]): + """Works like :class:`.TimedSerializer` but dumps and loads into a + URL safe string consisting of the upper and lowercase character of + the alphabet as well as ``'_'``, ``'-'`` and ``'.'``. + """ diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA new file mode 100644 index 000000000..ffef2ff3b --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: Jinja2 +Version: 3.1.6 +Summary: A very fast and expressive template engine. +Maintainer-email: Pallets +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: MarkupSafe>=2.0 +Requires-Dist: Babel>=2.7 ; extra == "i18n" +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/jinja/ +Provides-Extra: i18n + +# Jinja + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +## In A Nutshell + +```jinja +{% extends "base.html" %} +{% block title %}Members{% endblock %} +{% block content %} + +{% endblock %} +``` + +## Donate + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD new file mode 100644 index 000000000..0ca35a749 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/RECORD @@ -0,0 +1,57 @@ +jinja2-3.1.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +jinja2-3.1.6.dist-info/METADATA,sha256=aMVUj7Z8QTKhOJjZsx7FDGvqKr3ZFdkh8hQ1XDpkmcg,2871 +jinja2-3.1.6.dist-info/RECORD,, +jinja2-3.1.6.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82 +jinja2-3.1.6.dist-info/entry_points.txt,sha256=OL85gYU1eD8cuPlikifFngXpeBjaxl6rIJ8KkC_3r-I,58 +jinja2-3.1.6.dist-info/licenses/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +jinja2/__init__.py,sha256=xxepO9i7DHsqkQrgBEduLtfoz2QCuT6_gbL4XSN1hbU,1928 +jinja2/__pycache__/__init__.cpython-312.pyc,, +jinja2/__pycache__/_identifier.cpython-312.pyc,, +jinja2/__pycache__/async_utils.cpython-312.pyc,, +jinja2/__pycache__/bccache.cpython-312.pyc,, +jinja2/__pycache__/compiler.cpython-312.pyc,, +jinja2/__pycache__/constants.cpython-312.pyc,, +jinja2/__pycache__/debug.cpython-312.pyc,, +jinja2/__pycache__/defaults.cpython-312.pyc,, +jinja2/__pycache__/environment.cpython-312.pyc,, +jinja2/__pycache__/exceptions.cpython-312.pyc,, +jinja2/__pycache__/ext.cpython-312.pyc,, +jinja2/__pycache__/filters.cpython-312.pyc,, +jinja2/__pycache__/idtracking.cpython-312.pyc,, +jinja2/__pycache__/lexer.cpython-312.pyc,, +jinja2/__pycache__/loaders.cpython-312.pyc,, +jinja2/__pycache__/meta.cpython-312.pyc,, +jinja2/__pycache__/nativetypes.cpython-312.pyc,, +jinja2/__pycache__/nodes.cpython-312.pyc,, +jinja2/__pycache__/optimizer.cpython-312.pyc,, +jinja2/__pycache__/parser.cpython-312.pyc,, +jinja2/__pycache__/runtime.cpython-312.pyc,, +jinja2/__pycache__/sandbox.cpython-312.pyc,, +jinja2/__pycache__/tests.cpython-312.pyc,, +jinja2/__pycache__/utils.cpython-312.pyc,, +jinja2/__pycache__/visitor.cpython-312.pyc,, +jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=vK-PdsuorOMnWSnEkT3iUJRIkTnYgO2T6MnGxDgHI5o,2834 +jinja2/bccache.py,sha256=gh0qs9rulnXo0PhX5jTJy2UHzI8wFnQ63o_vw7nhzRg,14061 +jinja2/compiler.py,sha256=9RpCQl5X88BHllJiPsHPh295Hh0uApvwFJNQuutULeM,74131 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=CnHqCDHd-BVGvti_8ZsTolnXNhA3ECsY-6n_2pwU8Hw,6297 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=9nhrP7Ch-NbGX00wvyr4yy-uhNHq2OCc60ggGrni_fk,61513 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=5PF5eHfh8mXAIxXHHRB2xXbXohi8pE3nHSOxa66uS7E,31875 +jinja2/filters.py,sha256=PQ_Egd9n9jSgtnGQYyF4K5j2nYwhUIulhPnyimkdr-k,55212 +jinja2/idtracking.py,sha256=-ll5lIp73pML3ErUYiIJj7tdmWxcH_IlDv3yA_hiZYo,10555 +jinja2/lexer.py,sha256=LYiYio6br-Tep9nPcupWXsPEtjluw3p1mU-lNBVRUfk,29786 +jinja2/loaders.py,sha256=wIrnxjvcbqh5VwW28NSkfotiDq8qNCxIOSFbGUiSLB4,24055 +jinja2/meta.py,sha256=OTDPkaFvU2Hgvx-6akz7154F8BIWaRmvJcBFvwopHww,4397 +jinja2/nativetypes.py,sha256=7GIGALVJgdyL80oZJdQUaUfwSt5q2lSSZbXt0dNf_M4,4210 +jinja2/nodes.py,sha256=m1Duzcr6qhZI8JQ6VyJgUNinjAf5bQzijSmDnMsvUx8,34579 +jinja2/optimizer.py,sha256=rJnCRlQ7pZsEEmMhsQDgC_pKyDHxP5TPS6zVPGsgcu8,1651 +jinja2/parser.py,sha256=lLOFy3sEmHc5IaEHRiH1sQVnId2moUQzhyeJZTtdY30,40383 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=gDk-GvdriJXqgsGbHgrcKTP0Yp6zPXzhzrIpCFH3jAU,34249 +jinja2/sandbox.py,sha256=Mw2aitlY2I8la7FYhcX2YG9BtUYcLnD0Gh3d29cDWrY,15009 +jinja2/tests.py,sha256=VLsBhVFnWg-PxSBz1MhRnNWgP1ovXk3neO1FLQMeC9Q,5926 +jinja2/utils.py,sha256=rRp3o9e7ZKS4fyrWRbELyLcpuGVTFcnooaOa1qx_FIk,24129 +jinja2/visitor.py,sha256=EcnL1PIwf_4RVCOMxsRNuR8AXHbS1qfAdMOE2ngKJz4,3557 diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL new file mode 100644 index 000000000..23d2d7e9a --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.11.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt new file mode 100644 index 000000000..abc3eae3b --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2=jinja2.ext:babel_extract[i18n] + diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt new file mode 100644 index 000000000..c37cae49e --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2-3.1.6.dist-info/licenses/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/__init__.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/__init__.py new file mode 100644 index 000000000..1a423a3ea --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/__init__.py @@ -0,0 +1,38 @@ +"""Jinja is a template engine written in pure Python. It provides a +non-XML syntax that supports inline expressions and an optional +sandboxed environment. +""" + +from .bccache import BytecodeCache as BytecodeCache +from .bccache import FileSystemBytecodeCache as FileSystemBytecodeCache +from .bccache import MemcachedBytecodeCache as MemcachedBytecodeCache +from .environment import Environment as Environment +from .environment import Template as Template +from .exceptions import TemplateAssertionError as TemplateAssertionError +from .exceptions import TemplateError as TemplateError +from .exceptions import TemplateNotFound as TemplateNotFound +from .exceptions import TemplateRuntimeError as TemplateRuntimeError +from .exceptions import TemplatesNotFound as TemplatesNotFound +from .exceptions import TemplateSyntaxError as TemplateSyntaxError +from .exceptions import UndefinedError as UndefinedError +from .loaders import BaseLoader as BaseLoader +from .loaders import ChoiceLoader as ChoiceLoader +from .loaders import DictLoader as DictLoader +from .loaders import FileSystemLoader as FileSystemLoader +from .loaders import FunctionLoader as FunctionLoader +from .loaders import ModuleLoader as ModuleLoader +from .loaders import PackageLoader as PackageLoader +from .loaders import PrefixLoader as PrefixLoader +from .runtime import ChainableUndefined as ChainableUndefined +from .runtime import DebugUndefined as DebugUndefined +from .runtime import make_logging_undefined as make_logging_undefined +from .runtime import StrictUndefined as StrictUndefined +from .runtime import Undefined as Undefined +from .utils import clear_caches as clear_caches +from .utils import is_undefined as is_undefined +from .utils import pass_context as pass_context +from .utils import pass_environment as pass_environment +from .utils import pass_eval_context as pass_eval_context +from .utils import select_autoescape as select_autoescape + +__version__ = "3.1.6" diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/_identifier.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/_identifier.py new file mode 100644 index 000000000..928c1503c --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/_identifier.py @@ -0,0 +1,6 @@ +import re + +# generated by scripts/generate_identifier_pattern.py +pattern = re.compile( + r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪽ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰℘℮⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗𑍢𑍣𑍦-𑍬𑍰-𑍴𑐵-𑑆𑑞𑒰-𑓃𑖯-𑖵𑖸-𑗀𑗜𑗝𑘰-𑙀𑚫-𑚷𑜝-𑜫𑠬-𑠺𑨁-𑨊𑨳-𑨹𑨻-𑨾𑩇𑩑-𑩛𑪊-𑪙𑰯-𑰶𑰸-𑰿𑲒-𑲧𑲩-𑲶𑴱-𑴶𑴺𑴼𑴽𑴿-𑵅𑵇𑶊-𑶎𑶐𑶑𑶓-𑶗𑻳-𑻶𖫰-𖫴𖬰-𖬶𖽑-𖽾𖾏-𖾒𛲝𛲞𝅥-𝅩𝅭-𝅲𝅻-𝆂𝆅-𝆋𝆪-𝆭𝉂-𝉄𝨀-𝨶𝨻-𝩬𝩵𝪄𝪛-𝪟𝪡-𝪯𞀀-𞀆𞀈-𞀘𞀛-𞀡𞀣𞀤𞀦-𞣐𞀪-𞣖𞥄-𞥊󠄀-󠇯]+" # noqa: B950 +) diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/async_utils.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/async_utils.py new file mode 100644 index 000000000..f0c140205 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/async_utils.py @@ -0,0 +1,99 @@ +import inspect +import typing as t +from functools import WRAPPER_ASSIGNMENTS +from functools import wraps + +from .utils import _PassArg +from .utils import pass_eval_context + +if t.TYPE_CHECKING: + import typing_extensions as te + +V = t.TypeVar("V") + + +def async_variant(normal_func): # type: ignore + def decorator(async_func): # type: ignore + pass_arg = _PassArg.from_obj(normal_func) + need_eval_context = pass_arg is None + + if pass_arg is _PassArg.environment: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].is_async) + + else: + + def is_async(args: t.Any) -> bool: + return t.cast(bool, args[0].environment.is_async) + + # Take the doc and annotations from the sync function, but the + # name from the async function. Pallets-Sphinx-Themes + # build_function_directive expects __wrapped__ to point to the + # sync function. + async_func_attrs = ("__module__", "__name__", "__qualname__") + normal_func_attrs = tuple(set(WRAPPER_ASSIGNMENTS).difference(async_func_attrs)) + + @wraps(normal_func, assigned=normal_func_attrs) + @wraps(async_func, assigned=async_func_attrs, updated=()) + def wrapper(*args, **kwargs): # type: ignore + b = is_async(args) + + if need_eval_context: + args = args[1:] + + if b: + return async_func(*args, **kwargs) + + return normal_func(*args, **kwargs) + + if need_eval_context: + wrapper = pass_eval_context(wrapper) + + wrapper.jinja_async_variant = True # type: ignore[attr-defined] + return wrapper + + return decorator + + +_common_primitives = {int, float, bool, str, list, dict, tuple, type(None)} + + +async def auto_await(value: t.Union[t.Awaitable["V"], "V"]) -> "V": + # Avoid a costly call to isawaitable + if type(value) in _common_primitives: + return t.cast("V", value) + + if inspect.isawaitable(value): + return await t.cast("t.Awaitable[V]", value) + + return value + + +class _IteratorToAsyncIterator(t.Generic[V]): + def __init__(self, iterator: "t.Iterator[V]"): + self._iterator = iterator + + def __aiter__(self) -> "te.Self": + return self + + async def __anext__(self) -> V: + try: + return next(self._iterator) + except StopIteration as e: + raise StopAsyncIteration(e.value) from e + + +def auto_aiter( + iterable: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> "t.AsyncIterator[V]": + if hasattr(iterable, "__aiter__"): + return iterable.__aiter__() + else: + return _IteratorToAsyncIterator(iter(iterable)) + + +async def auto_to_list( + value: "t.Union[t.AsyncIterable[V], t.Iterable[V]]", +) -> t.List["V"]: + return [x async for x in auto_aiter(value)] diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/bccache.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/bccache.py new file mode 100644 index 000000000..ada8b099f --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/bccache.py @@ -0,0 +1,408 @@ +"""The optional bytecode cache system. This is useful if you have very +complex template situations and the compilation of all those templates +slows down your application too much. + +Situations where this is useful are often forking web applications that +are initialized on the first request. +""" + +import errno +import fnmatch +import marshal +import os +import pickle +import stat +import sys +import tempfile +import typing as t +from hashlib import sha1 +from io import BytesIO +from types import CodeType + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + + class _MemcachedClient(te.Protocol): + def get(self, key: str) -> bytes: ... + + def set( + self, key: str, value: bytes, timeout: t.Optional[int] = None + ) -> None: ... + + +bc_version = 5 +# Magic bytes to identify Jinja bytecode cache files. Contains the +# Python major and minor version to avoid loading incompatible bytecode +# if a project upgrades its Python version. +bc_magic = ( + b"j2" + + pickle.dumps(bc_version, 2) + + pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1], 2) +) + + +class Bucket: + """Buckets are used to store the bytecode for one template. It's created + and initialized by the bytecode cache and passed to the loading functions. + + The buckets get an internal checksum from the cache assigned and use this + to automatically reject outdated cache material. Individual bytecode + cache subclasses don't have to care about cache invalidation. + """ + + def __init__(self, environment: "Environment", key: str, checksum: str) -> None: + self.environment = environment + self.key = key + self.checksum = checksum + self.reset() + + def reset(self) -> None: + """Resets the bucket (unloads the bytecode).""" + self.code: t.Optional[CodeType] = None + + def load_bytecode(self, f: t.BinaryIO) -> None: + """Loads bytecode from a file or file like object.""" + # make sure the magic header is correct + magic = f.read(len(bc_magic)) + if magic != bc_magic: + self.reset() + return + # the source code of the file changed, we need to reload + checksum = pickle.load(f) + if self.checksum != checksum: + self.reset() + return + # if marshal_load fails then we need to reload + try: + self.code = marshal.load(f) + except (EOFError, ValueError, TypeError): + self.reset() + return + + def write_bytecode(self, f: t.IO[bytes]) -> None: + """Dump the bytecode into the file or file like object passed.""" + if self.code is None: + raise TypeError("can't write empty bucket") + f.write(bc_magic) + pickle.dump(self.checksum, f, 2) + marshal.dump(self.code, f) + + def bytecode_from_string(self, string: bytes) -> None: + """Load bytecode from bytes.""" + self.load_bytecode(BytesIO(string)) + + def bytecode_to_string(self) -> bytes: + """Return the bytecode as bytes.""" + out = BytesIO() + self.write_bytecode(out) + return out.getvalue() + + +class BytecodeCache: + """To implement your own bytecode cache you have to subclass this class + and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of + these methods are passed a :class:`~jinja2.bccache.Bucket`. + + A very basic bytecode cache that saves the bytecode on the file system:: + + from os import path + + class MyCache(BytecodeCache): + + def __init__(self, directory): + self.directory = directory + + def load_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + if path.exists(filename): + with open(filename, 'rb') as f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket): + filename = path.join(self.directory, bucket.key) + with open(filename, 'wb') as f: + bucket.write_bytecode(f) + + A more advanced version of a filesystem based bytecode cache is part of + Jinja. + """ + + def load_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to load bytecode into a + bucket. If they are not able to find code in the cache for the + bucket, it must not do anything. + """ + raise NotImplementedError() + + def dump_bytecode(self, bucket: Bucket) -> None: + """Subclasses have to override this method to write the bytecode + from a bucket back to the cache. If it unable to do so it must not + fail silently but raise an exception. + """ + raise NotImplementedError() + + def clear(self) -> None: + """Clears the cache. This method is not used by Jinja but should be + implemented to allow applications to clear the bytecode cache used + by a particular environment. + """ + + def get_cache_key( + self, name: str, filename: t.Optional[t.Union[str]] = None + ) -> str: + """Returns the unique hash key for this template name.""" + hash = sha1(name.encode("utf-8")) + + if filename is not None: + hash.update(f"|{filename}".encode()) + + return hash.hexdigest() + + def get_source_checksum(self, source: str) -> str: + """Returns a checksum for the source.""" + return sha1(source.encode("utf-8")).hexdigest() + + def get_bucket( + self, + environment: "Environment", + name: str, + filename: t.Optional[str], + source: str, + ) -> Bucket: + """Return a cache bucket for the given template. All arguments are + mandatory but filename may be `None`. + """ + key = self.get_cache_key(name, filename) + checksum = self.get_source_checksum(source) + bucket = Bucket(environment, key, checksum) + self.load_bytecode(bucket) + return bucket + + def set_bucket(self, bucket: Bucket) -> None: + """Put the bucket into the cache.""" + self.dump_bytecode(bucket) + + +class FileSystemBytecodeCache(BytecodeCache): + """A bytecode cache that stores bytecode on the filesystem. It accepts + two arguments: The directory where the cache items are stored and a + pattern string that is used to build the filename. + + If no directory is specified a default cache directory is selected. On + Windows the user's temp directory is used, on UNIX systems a directory + is created for the user in the system temp directory. + + The pattern can be used to have multiple separate caches operate on the + same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s`` + is replaced with the cache key. + + >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache') + + This bytecode cache supports clearing of the cache using the clear method. + """ + + def __init__( + self, directory: t.Optional[str] = None, pattern: str = "__jinja2_%s.cache" + ) -> None: + if directory is None: + directory = self._get_default_cache_dir() + self.directory = directory + self.pattern = pattern + + def _get_default_cache_dir(self) -> str: + def _unsafe_dir() -> "te.NoReturn": + raise RuntimeError( + "Cannot determine safe temp directory. You " + "need to explicitly provide one." + ) + + tmpdir = tempfile.gettempdir() + + # On windows the temporary directory is used specific unless + # explicitly forced otherwise. We can just use that. + if os.name == "nt": + return tmpdir + if not hasattr(os, "getuid"): + _unsafe_dir() + + dirname = f"_jinja2-cache-{os.getuid()}" + actual_dir = os.path.join(tmpdir, dirname) + + try: + os.mkdir(actual_dir, stat.S_IRWXU) + except OSError as e: + if e.errno != errno.EEXIST: + raise + try: + os.chmod(actual_dir, stat.S_IRWXU) + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + except OSError as e: + if e.errno != errno.EEXIST: + raise + + actual_dir_stat = os.lstat(actual_dir) + if ( + actual_dir_stat.st_uid != os.getuid() + or not stat.S_ISDIR(actual_dir_stat.st_mode) + or stat.S_IMODE(actual_dir_stat.st_mode) != stat.S_IRWXU + ): + _unsafe_dir() + + return actual_dir + + def _get_cache_filename(self, bucket: Bucket) -> str: + return os.path.join(self.directory, self.pattern % (bucket.key,)) + + def load_bytecode(self, bucket: Bucket) -> None: + filename = self._get_cache_filename(bucket) + + # Don't test for existence before opening the file, since the + # file could disappear after the test before the open. + try: + f = open(filename, "rb") + except (FileNotFoundError, IsADirectoryError, PermissionError): + # PermissionError can occur on Windows when an operation is + # in progress, such as calling clear(). + return + + with f: + bucket.load_bytecode(f) + + def dump_bytecode(self, bucket: Bucket) -> None: + # Write to a temporary file, then rename to the real name after + # writing. This avoids another process reading the file before + # it is fully written. + name = self._get_cache_filename(bucket) + f = tempfile.NamedTemporaryFile( + mode="wb", + dir=os.path.dirname(name), + prefix=os.path.basename(name), + suffix=".tmp", + delete=False, + ) + + def remove_silent() -> None: + try: + os.remove(f.name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + pass + + try: + with f: + bucket.write_bytecode(f) + except BaseException: + remove_silent() + raise + + try: + os.replace(f.name, name) + except OSError: + # Another process may have called clear(). On Windows, + # another program may be holding the file open. + remove_silent() + except BaseException: + remove_silent() + raise + + def clear(self) -> None: + # imported lazily here because google app-engine doesn't support + # write access on the file system and the function does not exist + # normally. + from os import remove + + files = fnmatch.filter(os.listdir(self.directory), self.pattern % ("*",)) + for filename in files: + try: + remove(os.path.join(self.directory, filename)) + except OSError: + pass + + +class MemcachedBytecodeCache(BytecodeCache): + """This class implements a bytecode cache that uses a memcache cache for + storing the information. It does not enforce a specific memcache library + (tummy's memcache or cmemcache) but will accept any class that provides + the minimal interface required. + + Libraries compatible with this class: + + - `cachelib `_ + - `python-memcached `_ + + (Unfortunately the django cache interface is not compatible because it + does not support storing binary data, only text. You can however pass + the underlying cache client to the bytecode cache which is available + as `django.core.cache.cache._client`.) + + The minimal interface for the client passed to the constructor is this: + + .. class:: MinimalClientInterface + + .. method:: set(key, value[, timeout]) + + Stores the bytecode in the cache. `value` is a string and + `timeout` the timeout of the key. If timeout is not provided + a default timeout or no timeout should be assumed, if it's + provided it's an integer with the number of seconds the cache + item should exist. + + .. method:: get(key) + + Returns the value for the cache key. If the item does not + exist in the cache the return value must be `None`. + + The other arguments to the constructor are the prefix for all keys that + is added before the actual cache key and the timeout for the bytecode in + the cache system. We recommend a high (or no) timeout. + + This bytecode cache does not support clearing of used items in the cache. + The clear method is a no-operation function. + + .. versionadded:: 2.7 + Added support for ignoring memcache errors through the + `ignore_memcache_errors` parameter. + """ + + def __init__( + self, + client: "_MemcachedClient", + prefix: str = "jinja2/bytecode/", + timeout: t.Optional[int] = None, + ignore_memcache_errors: bool = True, + ): + self.client = client + self.prefix = prefix + self.timeout = timeout + self.ignore_memcache_errors = ignore_memcache_errors + + def load_bytecode(self, bucket: Bucket) -> None: + try: + code = self.client.get(self.prefix + bucket.key) + except Exception: + if not self.ignore_memcache_errors: + raise + else: + bucket.bytecode_from_string(code) + + def dump_bytecode(self, bucket: Bucket) -> None: + key = self.prefix + bucket.key + value = bucket.bytecode_to_string() + + try: + if self.timeout is not None: + self.client.set(key, value, self.timeout) + else: + self.client.set(key, value) + except Exception: + if not self.ignore_memcache_errors: + raise diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/compiler.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/compiler.py new file mode 100644 index 000000000..a4ff6a1b1 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/compiler.py @@ -0,0 +1,1998 @@ +"""Compiles nodes from the parser into Python code.""" + +import typing as t +from contextlib import contextmanager +from functools import update_wrapper +from io import StringIO +from itertools import chain +from keyword import iskeyword as is_python_keyword + +from markupsafe import escape +from markupsafe import Markup + +from . import nodes +from .exceptions import TemplateAssertionError +from .idtracking import Symbols +from .idtracking import VAR_LOAD_ALIAS +from .idtracking import VAR_LOAD_PARAMETER +from .idtracking import VAR_LOAD_RESOLVE +from .idtracking import VAR_LOAD_UNDEFINED +from .nodes import EvalContext +from .optimizer import Optimizer +from .utils import _PassArg +from .utils import concat +from .visitor import NodeVisitor + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .environment import Environment + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + +operators = { + "eq": "==", + "ne": "!=", + "gt": ">", + "gteq": ">=", + "lt": "<", + "lteq": "<=", + "in": "in", + "notin": "not in", +} + + +def optimizeconst(f: F) -> F: + def new_func( + self: "CodeGenerator", node: nodes.Expr, frame: "Frame", **kwargs: t.Any + ) -> t.Any: + # Only optimize if the frame is not volatile + if self.optimizer is not None and not frame.eval_ctx.volatile: + new_node = self.optimizer.visit(node, frame.eval_ctx) + + if new_node != node: + return self.visit(new_node, frame) + + return f(self, node, frame, **kwargs) + + return update_wrapper(new_func, f) # type: ignore[return-value] + + +def _make_binop(op: str) -> t.Callable[["CodeGenerator", nodes.BinExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.BinExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_binops # type: ignore + ): + self.write(f"environment.call_binop(context, {op!r}, ") + self.visit(node.left, frame) + self.write(", ") + self.visit(node.right, frame) + else: + self.write("(") + self.visit(node.left, frame) + self.write(f" {op} ") + self.visit(node.right, frame) + + self.write(")") + + return visitor + + +def _make_unop( + op: str, +) -> t.Callable[["CodeGenerator", nodes.UnaryExpr, "Frame"], None]: + @optimizeconst + def visitor(self: "CodeGenerator", node: nodes.UnaryExpr, frame: Frame) -> None: + if ( + self.environment.sandboxed and op in self.environment.intercepted_unops # type: ignore + ): + self.write(f"environment.call_unop(context, {op!r}, ") + self.visit(node.node, frame) + else: + self.write("(" + op) + self.visit(node.node, frame) + + self.write(")") + + return visitor + + +def generate( + node: nodes.Template, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, +) -> t.Optional[str]: + """Generate the python source for a node tree.""" + if not isinstance(node, nodes.Template): + raise TypeError("Can't compile non template nodes") + + generator = environment.code_generator_class( + environment, name, filename, stream, defer_init, optimized + ) + generator.visit(node) + + if stream is None: + return generator.stream.getvalue() # type: ignore + + return None + + +def has_safe_repr(value: t.Any) -> bool: + """Does the node have a safe representation?""" + if value is None or value is NotImplemented or value is Ellipsis: + return True + + if type(value) in {bool, int, float, complex, range, str, Markup}: + return True + + if type(value) in {tuple, list, set, frozenset}: + return all(has_safe_repr(v) for v in value) + + if type(value) is dict: # noqa E721 + return all(has_safe_repr(k) and has_safe_repr(v) for k, v in value.items()) + + return False + + +def find_undeclared( + nodes: t.Iterable[nodes.Node], names: t.Iterable[str] +) -> t.Set[str]: + """Check if the names passed are accessed undeclared. The return value + is a set of all the undeclared names from the sequence of names found. + """ + visitor = UndeclaredNameVisitor(names) + try: + for node in nodes: + visitor.visit(node) + except VisitorExit: + pass + return visitor.undeclared + + +class MacroRef: + def __init__(self, node: t.Union[nodes.Macro, nodes.CallBlock]) -> None: + self.node = node + self.accesses_caller = False + self.accesses_kwargs = False + self.accesses_varargs = False + + +class Frame: + """Holds compile time information for us.""" + + def __init__( + self, + eval_ctx: EvalContext, + parent: t.Optional["Frame"] = None, + level: t.Optional[int] = None, + ) -> None: + self.eval_ctx = eval_ctx + + # the parent of this frame + self.parent = parent + + if parent is None: + self.symbols = Symbols(level=level) + + # in some dynamic inheritance situations the compiler needs to add + # write tests around output statements. + self.require_output_check = False + + # inside some tags we are using a buffer rather than yield statements. + # this for example affects {% filter %} or {% macro %}. If a frame + # is buffered this variable points to the name of the list used as + # buffer. + self.buffer: t.Optional[str] = None + + # the name of the block we're in, otherwise None. + self.block: t.Optional[str] = None + + else: + self.symbols = Symbols(parent.symbols, level=level) + self.require_output_check = parent.require_output_check + self.buffer = parent.buffer + self.block = parent.block + + # a toplevel frame is the root + soft frames such as if conditions. + self.toplevel = False + + # the root frame is basically just the outermost frame, so no if + # conditions. This information is used to optimize inheritance + # situations. + self.rootlevel = False + + # variables set inside of loops and blocks should not affect outer frames, + # but they still needs to be kept track of as part of the active context. + self.loop_frame = False + self.block_frame = False + + # track whether the frame is being used in an if-statement or conditional + # expression as it determines which errors should be raised during runtime + # or compile time. + self.soft_frame = False + + def copy(self) -> "te.Self": + """Create a copy of the current one.""" + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.symbols = self.symbols.copy() + return rv + + def inner(self, isolated: bool = False) -> "Frame": + """Return an inner frame.""" + if isolated: + return Frame(self.eval_ctx, level=self.symbols.level + 1) + return Frame(self.eval_ctx, self) + + def soft(self) -> "te.Self": + """Return a soft frame. A soft frame may not be modified as + standalone thing as it shares the resources with the frame it + was created of, but it's not a rootlevel frame any longer. + + This is only used to implement if-statements and conditional + expressions. + """ + rv = self.copy() + rv.rootlevel = False + rv.soft_frame = True + return rv + + __copy__ = copy + + +class VisitorExit(RuntimeError): + """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" + + +class DependencyFinderVisitor(NodeVisitor): + """A visitor that collects filter and test calls.""" + + def __init__(self) -> None: + self.filters: t.Set[str] = set() + self.tests: t.Set[str] = set() + + def visit_Filter(self, node: nodes.Filter) -> None: + self.generic_visit(node) + self.filters.add(node.name) + + def visit_Test(self, node: nodes.Test) -> None: + self.generic_visit(node) + self.tests.add(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting at blocks.""" + + +class UndeclaredNameVisitor(NodeVisitor): + """A visitor that checks if a name is accessed without being + declared. This is different from the frame visitor as it will + not stop at closure frames. + """ + + def __init__(self, names: t.Iterable[str]) -> None: + self.names = set(names) + self.undeclared: t.Set[str] = set() + + def visit_Name(self, node: nodes.Name) -> None: + if node.ctx == "load" and node.name in self.names: + self.undeclared.add(node.name) + if self.undeclared == self.names: + raise VisitorExit() + else: + self.names.discard(node.name) + + def visit_Block(self, node: nodes.Block) -> None: + """Stop visiting a blocks.""" + + +class CompilerExit(Exception): + """Raised if the compiler encountered a situation where it just + doesn't make sense to further process the code. Any block that + raises such an exception is not further processed. + """ + + +class CodeGenerator(NodeVisitor): + def __init__( + self, + environment: "Environment", + name: t.Optional[str], + filename: t.Optional[str], + stream: t.Optional[t.TextIO] = None, + defer_init: bool = False, + optimized: bool = True, + ) -> None: + if stream is None: + stream = StringIO() + self.environment = environment + self.name = name + self.filename = filename + self.stream = stream + self.created_block_context = False + self.defer_init = defer_init + self.optimizer: t.Optional[Optimizer] = None + + if optimized: + self.optimizer = Optimizer(environment) + + # aliases for imports + self.import_aliases: t.Dict[str, str] = {} + + # a registry for all blocks. Because blocks are moved out + # into the global python scope they are registered here + self.blocks: t.Dict[str, nodes.Block] = {} + + # the number of extends statements so far + self.extends_so_far = 0 + + # some templates have a rootlevel extends. In this case we + # can safely assume that we're a child template and do some + # more optimizations. + self.has_known_extends = False + + # the current line number + self.code_lineno = 1 + + # registry of all filters and tests (global, not block local) + self.tests: t.Dict[str, str] = {} + self.filters: t.Dict[str, str] = {} + + # the debug information + self.debug_info: t.List[t.Tuple[int, int]] = [] + self._write_debug_info: t.Optional[int] = None + + # the number of new lines before the next write() + self._new_lines = 0 + + # the line number of the last written statement + self._last_line = 0 + + # true if nothing was written so far. + self._first_write = True + + # used by the `temporary_identifier` method to get new + # unique, temporary identifier + self._last_identifier = 0 + + # the current indentation + self._indentation = 0 + + # Tracks toplevel assignments + self._assign_stack: t.List[t.Set[str]] = [] + + # Tracks parameter definition blocks + self._param_def_block: t.List[t.Set[str]] = [] + + # Tracks the current context. + self._context_reference_stack = ["context"] + + @property + def optimized(self) -> bool: + return self.optimizer is not None + + # -- Various compilation helpers + + def fail(self, msg: str, lineno: int) -> "te.NoReturn": + """Fail with a :exc:`TemplateAssertionError`.""" + raise TemplateAssertionError(msg, lineno, self.name, self.filename) + + def temporary_identifier(self) -> str: + """Get a new unique identifier.""" + self._last_identifier += 1 + return f"t_{self._last_identifier}" + + def buffer(self, frame: Frame) -> None: + """Enable buffering for the frame from that point onwards.""" + frame.buffer = self.temporary_identifier() + self.writeline(f"{frame.buffer} = []") + + def return_buffer_contents( + self, frame: Frame, force_unescaped: bool = False + ) -> None: + """Return the buffer contents of the frame.""" + if not force_unescaped: + if frame.eval_ctx.volatile: + self.writeline("if context.eval_ctx.autoescape:") + self.indent() + self.writeline(f"return Markup(concat({frame.buffer}))") + self.outdent() + self.writeline("else:") + self.indent() + self.writeline(f"return concat({frame.buffer})") + self.outdent() + return + elif frame.eval_ctx.autoescape: + self.writeline(f"return Markup(concat({frame.buffer}))") + return + self.writeline(f"return concat({frame.buffer})") + + def indent(self) -> None: + """Indent by one.""" + self._indentation += 1 + + def outdent(self, step: int = 1) -> None: + """Outdent by step.""" + self._indentation -= step + + def start_write(self, frame: Frame, node: t.Optional[nodes.Node] = None) -> None: + """Yield or write into the frame buffer.""" + if frame.buffer is None: + self.writeline("yield ", node) + else: + self.writeline(f"{frame.buffer}.append(", node) + + def end_write(self, frame: Frame) -> None: + """End the writing process started by `start_write`.""" + if frame.buffer is not None: + self.write(")") + + def simple_write( + self, s: str, frame: Frame, node: t.Optional[nodes.Node] = None + ) -> None: + """Simple shortcut for start_write + write + end_write.""" + self.start_write(frame, node) + self.write(s) + self.end_write(frame) + + def blockvisit(self, nodes: t.Iterable[nodes.Node], frame: Frame) -> None: + """Visit a list of nodes as block in a frame. If the current frame + is no buffer a dummy ``if 0: yield None`` is written automatically. + """ + try: + self.writeline("pass") + for node in nodes: + self.visit(node, frame) + except CompilerExit: + pass + + def write(self, x: str) -> None: + """Write a string into the output stream.""" + if self._new_lines: + if not self._first_write: + self.stream.write("\n" * self._new_lines) + self.code_lineno += self._new_lines + if self._write_debug_info is not None: + self.debug_info.append((self._write_debug_info, self.code_lineno)) + self._write_debug_info = None + self._first_write = False + self.stream.write(" " * self._indentation) + self._new_lines = 0 + self.stream.write(x) + + def writeline( + self, x: str, node: t.Optional[nodes.Node] = None, extra: int = 0 + ) -> None: + """Combination of newline and write.""" + self.newline(node, extra) + self.write(x) + + def newline(self, node: t.Optional[nodes.Node] = None, extra: int = 0) -> None: + """Add one or more newlines before the next write.""" + self._new_lines = max(self._new_lines, 1 + extra) + if node is not None and node.lineno != self._last_line: + self._write_debug_info = node.lineno + self._last_line = node.lineno + + def signature( + self, + node: t.Union[nodes.Call, nodes.Filter, nodes.Test], + frame: Frame, + extra_kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + ) -> None: + """Writes a function call to the stream for the current node. + A leading comma is added automatically. The extra keyword + arguments may not include python keywords otherwise a syntax + error could occur. The extra keyword arguments should be given + as python dict. + """ + # if any of the given keyword arguments is a python keyword + # we have to make sure that no invalid call is created. + kwarg_workaround = any( + is_python_keyword(t.cast(str, k)) + for k in chain((x.key for x in node.kwargs), extra_kwargs or ()) + ) + + for arg in node.args: + self.write(", ") + self.visit(arg, frame) + + if not kwarg_workaround: + for kwarg in node.kwargs: + self.write(", ") + self.visit(kwarg, frame) + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f", {key}={value}") + if node.dyn_args: + self.write(", *") + self.visit(node.dyn_args, frame) + + if kwarg_workaround: + if node.dyn_kwargs is not None: + self.write(", **dict({") + else: + self.write(", **{") + for kwarg in node.kwargs: + self.write(f"{kwarg.key!r}: ") + self.visit(kwarg.value, frame) + self.write(", ") + if extra_kwargs is not None: + for key, value in extra_kwargs.items(): + self.write(f"{key!r}: {value}, ") + if node.dyn_kwargs is not None: + self.write("}, **") + self.visit(node.dyn_kwargs, frame) + self.write(")") + else: + self.write("}") + + elif node.dyn_kwargs is not None: + self.write(", **") + self.visit(node.dyn_kwargs, frame) + + def pull_dependencies(self, nodes: t.Iterable[nodes.Node]) -> None: + """Find all filter and test names used in the template and + assign them to variables in the compiled namespace. Checking + that the names are registered with the environment is done when + compiling the Filter and Test nodes. If the node is in an If or + CondExpr node, the check is done at runtime instead. + + .. versionchanged:: 3.0 + Filters and tests in If and CondExpr nodes are checked at + runtime instead of compile time. + """ + visitor = DependencyFinderVisitor() + + for node in nodes: + visitor.visit(node) + + for id_map, names, dependency in ( + (self.filters, visitor.filters, "filters"), + ( + self.tests, + visitor.tests, + "tests", + ), + ): + for name in sorted(names): + if name not in id_map: + id_map[name] = self.temporary_identifier() + + # add check during runtime that dependencies used inside of executed + # blocks are defined, as this step may be skipped during compile time + self.writeline("try:") + self.indent() + self.writeline(f"{id_map[name]} = environment.{dependency}[{name!r}]") + self.outdent() + self.writeline("except KeyError:") + self.indent() + self.writeline("@internalcode") + self.writeline(f"def {id_map[name]}(*unused):") + self.indent() + self.writeline( + f'raise TemplateRuntimeError("No {dependency[:-1]}' + f' named {name!r} found.")' + ) + self.outdent() + self.outdent() + + def enter_frame(self, frame: Frame) -> None: + undefs = [] + for target, (action, param) in frame.symbols.loads.items(): + if action == VAR_LOAD_PARAMETER: + pass + elif action == VAR_LOAD_RESOLVE: + self.writeline(f"{target} = {self.get_resolve_func()}({param!r})") + elif action == VAR_LOAD_ALIAS: + self.writeline(f"{target} = {param}") + elif action == VAR_LOAD_UNDEFINED: + undefs.append(target) + else: + raise NotImplementedError("unknown load instruction") + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def leave_frame(self, frame: Frame, with_python_scope: bool = False) -> None: + if not with_python_scope: + undefs = [] + for target in frame.symbols.loads: + undefs.append(target) + if undefs: + self.writeline(f"{' = '.join(undefs)} = missing") + + def choose_async(self, async_value: str = "async ", sync_value: str = "") -> str: + return async_value if self.environment.is_async else sync_value + + def func(self, name: str) -> str: + return f"{self.choose_async()}def {name}" + + def macro_body( + self, node: t.Union[nodes.Macro, nodes.CallBlock], frame: Frame + ) -> t.Tuple[Frame, MacroRef]: + """Dump the function def of a macro or call block.""" + frame = frame.inner() + frame.symbols.analyze_node(node) + macro_ref = MacroRef(node) + + explicit_caller = None + skip_special_params = set() + args = [] + + for idx, arg in enumerate(node.args): + if arg.name == "caller": + explicit_caller = idx + if arg.name in ("kwargs", "varargs"): + skip_special_params.add(arg.name) + args.append(frame.symbols.ref(arg.name)) + + undeclared = find_undeclared(node.body, ("caller", "kwargs", "varargs")) + + if "caller" in undeclared: + # In older Jinja versions there was a bug that allowed caller + # to retain the special behavior even if it was mentioned in + # the argument list. However thankfully this was only really + # working if it was the last argument. So we are explicitly + # checking this now and error out if it is anywhere else in + # the argument list. + if explicit_caller is not None: + try: + node.defaults[explicit_caller - len(node.args)] + except IndexError: + self.fail( + "When defining macros or call blocks the " + 'special "caller" argument must be omitted ' + "or be given a default.", + node.lineno, + ) + else: + args.append(frame.symbols.declare_parameter("caller")) + macro_ref.accesses_caller = True + if "kwargs" in undeclared and "kwargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("kwargs")) + macro_ref.accesses_kwargs = True + if "varargs" in undeclared and "varargs" not in skip_special_params: + args.append(frame.symbols.declare_parameter("varargs")) + macro_ref.accesses_varargs = True + + # macros are delayed, they never require output checks + frame.require_output_check = False + frame.symbols.analyze_node(node) + self.writeline(f"{self.func('macro')}({', '.join(args)}):", node) + self.indent() + + self.buffer(frame) + self.enter_frame(frame) + + self.push_parameter_definitions(frame) + for idx, arg in enumerate(node.args): + ref = frame.symbols.ref(arg.name) + self.writeline(f"if {ref} is missing:") + self.indent() + try: + default = node.defaults[idx - len(node.args)] + except IndexError: + self.writeline( + f'{ref} = undefined("parameter {arg.name!r} was not provided",' + f" name={arg.name!r})" + ) + else: + self.writeline(f"{ref} = ") + self.visit(default, frame) + self.mark_parameter_stored(ref) + self.outdent() + self.pop_parameter_definitions() + + self.blockvisit(node.body, frame) + self.return_buffer_contents(frame, force_unescaped=True) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + return frame, macro_ref + + def macro_def(self, macro_ref: MacroRef, frame: Frame) -> None: + """Dump the macro definition for the def created by macro_body.""" + arg_tuple = ", ".join(repr(x.name) for x in macro_ref.node.args) + name = getattr(macro_ref.node, "name", None) + if len(macro_ref.node.args) == 1: + arg_tuple += "," + self.write( + f"Macro(environment, macro, {name!r}, ({arg_tuple})," + f" {macro_ref.accesses_kwargs!r}, {macro_ref.accesses_varargs!r}," + f" {macro_ref.accesses_caller!r}, context.eval_ctx.autoescape)" + ) + + def position(self, node: nodes.Node) -> str: + """Return a human readable position for the node.""" + rv = f"line {node.lineno}" + if self.name is not None: + rv = f"{rv} in {self.name!r}" + return rv + + def dump_local_context(self, frame: Frame) -> str: + items_kv = ", ".join( + f"{name!r}: {target}" + for name, target in frame.symbols.dump_stores().items() + ) + return f"{{{items_kv}}}" + + def write_commons(self) -> None: + """Writes a common preamble that is used by root and block functions. + Primarily this sets up common local helpers and enforces a generator + through a dead branch. + """ + self.writeline("resolve = context.resolve_or_missing") + self.writeline("undefined = environment.undefined") + self.writeline("concat = environment.concat") + # always use the standard Undefined class for the implicit else of + # conditional expressions + self.writeline("cond_expr_undefined = Undefined") + self.writeline("if 0: yield None") + + def push_parameter_definitions(self, frame: Frame) -> None: + """Pushes all parameter targets from the given frame into a local + stack that permits tracking of yet to be assigned parameters. In + particular this enables the optimization from `visit_Name` to skip + undefined expressions for parameters in macros as macros can reference + otherwise unbound parameters. + """ + self._param_def_block.append(frame.symbols.dump_param_targets()) + + def pop_parameter_definitions(self) -> None: + """Pops the current parameter definitions set.""" + self._param_def_block.pop() + + def mark_parameter_stored(self, target: str) -> None: + """Marks a parameter in the current parameter definitions as stored. + This will skip the enforced undefined checks. + """ + if self._param_def_block: + self._param_def_block[-1].discard(target) + + def push_context_reference(self, target: str) -> None: + self._context_reference_stack.append(target) + + def pop_context_reference(self) -> None: + self._context_reference_stack.pop() + + def get_context_ref(self) -> str: + return self._context_reference_stack[-1] + + def get_resolve_func(self) -> str: + target = self._context_reference_stack[-1] + if target == "context": + return "resolve" + return f"{target}.resolve" + + def derive_context(self, frame: Frame) -> str: + return f"{self.get_context_ref()}.derived({self.dump_local_context(frame)})" + + def parameter_is_undeclared(self, target: str) -> bool: + """Checks if a given target is an undeclared parameter.""" + if not self._param_def_block: + return False + return target in self._param_def_block[-1] + + def push_assign_tracking(self) -> None: + """Pushes a new layer for assignment tracking.""" + self._assign_stack.append(set()) + + def pop_assign_tracking(self, frame: Frame) -> None: + """Pops the topmost level for assignment tracking and updates the + context variables if necessary. + """ + vars = self._assign_stack.pop() + if ( + not frame.block_frame + and not frame.loop_frame + and not frame.toplevel + or not vars + ): + return + public_names = [x for x in vars if x[:1] != "_"] + if len(vars) == 1: + name = next(iter(vars)) + ref = frame.symbols.ref(name) + if frame.loop_frame: + self.writeline(f"_loop_vars[{name!r}] = {ref}") + return + if frame.block_frame: + self.writeline(f"_block_vars[{name!r}] = {ref}") + return + self.writeline(f"context.vars[{name!r}] = {ref}") + else: + if frame.loop_frame: + self.writeline("_loop_vars.update({") + elif frame.block_frame: + self.writeline("_block_vars.update({") + else: + self.writeline("context.vars.update({") + for idx, name in enumerate(sorted(vars)): + if idx: + self.write(", ") + ref = frame.symbols.ref(name) + self.write(f"{name!r}: {ref}") + self.write("})") + if not frame.block_frame and not frame.loop_frame and public_names: + if len(public_names) == 1: + self.writeline(f"context.exported_vars.add({public_names[0]!r})") + else: + names_str = ", ".join(map(repr, sorted(public_names))) + self.writeline(f"context.exported_vars.update(({names_str}))") + + # -- Statement Visitors + + def visit_Template( + self, node: nodes.Template, frame: t.Optional[Frame] = None + ) -> None: + assert frame is None, "no root frame allowed" + eval_ctx = EvalContext(self.environment, self.name) + + from .runtime import async_exported + from .runtime import exported + + if self.environment.is_async: + exported_names = sorted(exported + async_exported) + else: + exported_names = sorted(exported) + + self.writeline("from jinja2.runtime import " + ", ".join(exported_names)) + + # if we want a deferred initialization we cannot move the + # environment into a local name + envenv = "" if self.defer_init else ", environment=environment" + + # do we have an extends tag at all? If not, we can save some + # overhead by just not processing any inheritance code. + have_extends = node.find(nodes.Extends) is not None + + # find all blocks + for block in node.find_all(nodes.Block): + if block.name in self.blocks: + self.fail(f"block {block.name!r} defined twice", block.lineno) + self.blocks[block.name] = block + + # find all imports and import them + for import_ in node.find_all(nodes.ImportedName): + if import_.importname not in self.import_aliases: + imp = import_.importname + self.import_aliases[imp] = alias = self.temporary_identifier() + if "." in imp: + module, obj = imp.rsplit(".", 1) + self.writeline(f"from {module} import {obj} as {alias}") + else: + self.writeline(f"import {imp} as {alias}") + + # add the load name + self.writeline(f"name = {self.name!r}") + + # generate the root render function. + self.writeline( + f"{self.func('root')}(context, missing=missing{envenv}):", extra=1 + ) + self.indent() + self.write_commons() + + # process the root + frame = Frame(eval_ctx) + if "self" in find_undeclared(node.body, ("self",)): + ref = frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + frame.symbols.analyze_node(node) + frame.toplevel = frame.rootlevel = True + frame.require_output_check = have_extends and not self.has_known_extends + if have_extends: + self.writeline("parent_template = None") + self.enter_frame(frame) + self.pull_dependencies(node.body) + self.blockvisit(node.body, frame) + self.leave_frame(frame, with_python_scope=True) + self.outdent() + + # make sure that the parent root is called. + if have_extends: + if not self.has_known_extends: + self.indent() + self.writeline("if parent_template is not None:") + self.indent() + if not self.environment.is_async: + self.writeline("yield from parent_template.root_render_func(context)") + else: + self.writeline("agen = parent_template.root_render_func(context)") + self.writeline("try:") + self.indent() + self.writeline("async for event in agen:") + self.indent() + self.writeline("yield event") + self.outdent() + self.outdent() + self.writeline("finally: await agen.aclose()") + self.outdent(1 + (not self.has_known_extends)) + + # at this point we now have the blocks collected and can visit them too. + for name, block in self.blocks.items(): + self.writeline( + f"{self.func('block_' + name)}(context, missing=missing{envenv}):", + block, + 1, + ) + self.indent() + self.write_commons() + # It's important that we do not make this frame a child of the + # toplevel template. This would cause a variety of + # interesting issues with identifier tracking. + block_frame = Frame(eval_ctx) + block_frame.block_frame = True + undeclared = find_undeclared(block.body, ("self", "super")) + if "self" in undeclared: + ref = block_frame.symbols.declare_parameter("self") + self.writeline(f"{ref} = TemplateReference(context)") + if "super" in undeclared: + ref = block_frame.symbols.declare_parameter("super") + self.writeline(f"{ref} = context.super({name!r}, block_{name})") + block_frame.symbols.analyze_node(block) + block_frame.block = name + self.writeline("_block_vars = {}") + self.enter_frame(block_frame) + self.pull_dependencies(block.body) + self.blockvisit(block.body, block_frame) + self.leave_frame(block_frame, with_python_scope=True) + self.outdent() + + blocks_kv_str = ", ".join(f"{x!r}: block_{x}" for x in self.blocks) + self.writeline(f"blocks = {{{blocks_kv_str}}}", extra=1) + debug_kv_str = "&".join(f"{k}={v}" for k, v in self.debug_info) + self.writeline(f"debug_info = {debug_kv_str!r}") + + def visit_Block(self, node: nodes.Block, frame: Frame) -> None: + """Call a block and register it for the template.""" + level = 0 + if frame.toplevel: + # if we know that we are a child template, there is no need to + # check if we are one + if self.has_known_extends: + return + if self.extends_so_far > 0: + self.writeline("if parent_template is None:") + self.indent() + level += 1 + + if node.scoped: + context = self.derive_context(frame) + else: + context = self.get_context_ref() + + if node.required: + self.writeline(f"if len(context.blocks[{node.name!r}]) <= 1:", node) + self.indent() + self.writeline( + f'raise TemplateRuntimeError("Required block {node.name!r} not found")', + node, + ) + self.outdent() + + if not self.environment.is_async and frame.buffer is None: + self.writeline( + f"yield from context.blocks[{node.name!r}][0]({context})", node + ) + else: + self.writeline(f"gen = context.blocks[{node.name!r}][0]({context})") + self.writeline("try:") + self.indent() + self.writeline( + f"{self.choose_async()}for event in gen:", + node, + ) + self.indent() + self.simple_write("event", frame) + self.outdent() + self.outdent() + self.writeline( + f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}" + ) + + self.outdent(level) + + def visit_Extends(self, node: nodes.Extends, frame: Frame) -> None: + """Calls the extender.""" + if not frame.toplevel: + self.fail("cannot use extend from a non top-level scope", node.lineno) + + # if the number of extends statements in general is zero so + # far, we don't have to add a check if something extended + # the template before this one. + if self.extends_so_far > 0: + # if we have a known extends we just add a template runtime + # error into the generated code. We could catch that at compile + # time too, but i welcome it not to confuse users by throwing the + # same error at different times just "because we can". + if not self.has_known_extends: + self.writeline("if parent_template is not None:") + self.indent() + self.writeline('raise TemplateRuntimeError("extended multiple times")') + + # if we have a known extends already we don't need that code here + # as we know that the template execution will end here. + if self.has_known_extends: + raise CompilerExit() + else: + self.outdent() + + self.writeline("parent_template = environment.get_template(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + self.writeline("for name, parent_block in parent_template.blocks.items():") + self.indent() + self.writeline("context.blocks.setdefault(name, []).append(parent_block)") + self.outdent() + + # if this extends statement was in the root level we can take + # advantage of that information and simplify the generated code + # in the top level from this point onwards + if frame.rootlevel: + self.has_known_extends = True + + # and now we have one more + self.extends_so_far += 1 + + def visit_Include(self, node: nodes.Include, frame: Frame) -> None: + """Handles includes.""" + if node.ignore_missing: + self.writeline("try:") + self.indent() + + func_name = "get_or_select_template" + if isinstance(node.template, nodes.Const): + if isinstance(node.template.value, str): + func_name = "get_template" + elif isinstance(node.template.value, (tuple, list)): + func_name = "select_template" + elif isinstance(node.template, (nodes.Tuple, nodes.List)): + func_name = "select_template" + + self.writeline(f"template = environment.{func_name}(", node) + self.visit(node.template, frame) + self.write(f", {self.name!r})") + if node.ignore_missing: + self.outdent() + self.writeline("except TemplateNotFound:") + self.indent() + self.writeline("pass") + self.outdent() + self.writeline("else:") + self.indent() + + def loop_body() -> None: + self.indent() + self.simple_write("event", frame) + self.outdent() + + if node.with_context: + self.writeline( + f"gen = template.root_render_func(" + "template.new_context(context.get_all(), True," + f" {self.dump_local_context(frame)}))" + ) + self.writeline("try:") + self.indent() + self.writeline(f"{self.choose_async()}for event in gen:") + loop_body() + self.outdent() + self.writeline( + f"finally: {self.choose_async('await gen.aclose()', 'gen.close()')}" + ) + elif self.environment.is_async: + self.writeline( + "for event in (await template._get_default_module_async())" + "._body_stream:" + ) + loop_body() + else: + self.writeline("yield from template._get_default_module()._body_stream") + + if node.ignore_missing: + self.outdent() + + def _import_common( + self, node: t.Union[nodes.Import, nodes.FromImport], frame: Frame + ) -> None: + self.write(f"{self.choose_async('await ')}environment.get_template(") + self.visit(node.template, frame) + self.write(f", {self.name!r}).") + + if node.with_context: + f_name = f"make_module{self.choose_async('_async')}" + self.write( + f"{f_name}(context.get_all(), True, {self.dump_local_context(frame)})" + ) + else: + self.write(f"_get_default_module{self.choose_async('_async')}(context)") + + def visit_Import(self, node: nodes.Import, frame: Frame) -> None: + """Visit regular imports.""" + self.writeline(f"{frame.symbols.ref(node.target)} = ", node) + if frame.toplevel: + self.write(f"context.vars[{node.target!r}] = ") + + self._import_common(node, frame) + + if frame.toplevel and not node.target.startswith("_"): + self.writeline(f"context.exported_vars.discard({node.target!r})") + + def visit_FromImport(self, node: nodes.FromImport, frame: Frame) -> None: + """Visit named imports.""" + self.newline(node) + self.write("included_template = ") + self._import_common(node, frame) + var_names = [] + discarded_names = [] + for name in node.names: + if isinstance(name, tuple): + name, alias = name + else: + alias = name + self.writeline( + f"{frame.symbols.ref(alias)} =" + f" getattr(included_template, {name!r}, missing)" + ) + self.writeline(f"if {frame.symbols.ref(alias)} is missing:") + self.indent() + # The position will contain the template name, and will be formatted + # into a string that will be compiled into an f-string. Curly braces + # in the name must be replaced with escapes so that they will not be + # executed as part of the f-string. + position = self.position(node).replace("{", "{{").replace("}", "}}") + message = ( + "the template {included_template.__name__!r}" + f" (imported on {position})" + f" does not export the requested name {name!r}" + ) + self.writeline( + f"{frame.symbols.ref(alias)} = undefined(f{message!r}, name={name!r})" + ) + self.outdent() + if frame.toplevel: + var_names.append(alias) + if not alias.startswith("_"): + discarded_names.append(alias) + + if var_names: + if len(var_names) == 1: + name = var_names[0] + self.writeline(f"context.vars[{name!r}] = {frame.symbols.ref(name)}") + else: + names_kv = ", ".join( + f"{name!r}: {frame.symbols.ref(name)}" for name in var_names + ) + self.writeline(f"context.vars.update({{{names_kv}}})") + if discarded_names: + if len(discarded_names) == 1: + self.writeline(f"context.exported_vars.discard({discarded_names[0]!r})") + else: + names_str = ", ".join(map(repr, discarded_names)) + self.writeline( + f"context.exported_vars.difference_update(({names_str}))" + ) + + def visit_For(self, node: nodes.For, frame: Frame) -> None: + loop_frame = frame.inner() + loop_frame.loop_frame = True + test_frame = frame.inner() + else_frame = frame.inner() + + # try to figure out if we have an extended loop. An extended loop + # is necessary if the loop is in recursive mode if the special loop + # variable is accessed in the body if the body is a scoped block. + extended_loop = ( + node.recursive + or "loop" + in find_undeclared(node.iter_child_nodes(only=("body",)), ("loop",)) + or any(block.scoped for block in node.find_all(nodes.Block)) + ) + + loop_ref = None + if extended_loop: + loop_ref = loop_frame.symbols.declare_parameter("loop") + + loop_frame.symbols.analyze_node(node, for_branch="body") + if node.else_: + else_frame.symbols.analyze_node(node, for_branch="else") + + if node.test: + loop_filter_func = self.temporary_identifier() + test_frame.symbols.analyze_node(node, for_branch="test") + self.writeline(f"{self.func(loop_filter_func)}(fiter):", node.test) + self.indent() + self.enter_frame(test_frame) + self.writeline(self.choose_async("async for ", "for ")) + self.visit(node.target, loop_frame) + self.write(" in ") + self.write(self.choose_async("auto_aiter(fiter)", "fiter")) + self.write(":") + self.indent() + self.writeline("if ", node.test) + self.visit(node.test, test_frame) + self.write(":") + self.indent() + self.writeline("yield ") + self.visit(node.target, loop_frame) + self.outdent(3) + self.leave_frame(test_frame, with_python_scope=True) + + # if we don't have an recursive loop we have to find the shadowed + # variables at that point. Because loops can be nested but the loop + # variable is a special one we have to enforce aliasing for it. + if node.recursive: + self.writeline( + f"{self.func('loop')}(reciter, loop_render_func, depth=0):", node + ) + self.indent() + self.buffer(loop_frame) + + # Use the same buffer for the else frame + else_frame.buffer = loop_frame.buffer + + # make sure the loop variable is a special one and raise a template + # assertion error if a loop tries to write to loop + if extended_loop: + self.writeline(f"{loop_ref} = missing") + + for name in node.find_all(nodes.Name): + if name.ctx == "store" and name.name == "loop": + self.fail( + "Can't assign to special loop variable in for-loop target", + name.lineno, + ) + + if node.else_: + iteration_indicator = self.temporary_identifier() + self.writeline(f"{iteration_indicator} = 1") + + self.writeline(self.choose_async("async for ", "for "), node) + self.visit(node.target, loop_frame) + if extended_loop: + self.write(f", {loop_ref} in {self.choose_async('Async')}LoopContext(") + else: + self.write(" in ") + + if node.test: + self.write(f"{loop_filter_func}(") + if node.recursive: + self.write("reciter") + else: + if self.environment.is_async and not extended_loop: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async and not extended_loop: + self.write(")") + if node.test: + self.write(")") + + if node.recursive: + self.write(", undefined, loop_render_func, depth):") + else: + self.write(", undefined):" if extended_loop else ":") + + self.indent() + self.enter_frame(loop_frame) + + self.writeline("_loop_vars = {}") + self.blockvisit(node.body, loop_frame) + if node.else_: + self.writeline(f"{iteration_indicator} = 0") + self.outdent() + self.leave_frame( + loop_frame, with_python_scope=node.recursive and not node.else_ + ) + + if node.else_: + self.writeline(f"if {iteration_indicator}:") + self.indent() + self.enter_frame(else_frame) + self.blockvisit(node.else_, else_frame) + self.leave_frame(else_frame) + self.outdent() + + # if the node was recursive we have to return the buffer contents + # and start the iteration code + if node.recursive: + self.return_buffer_contents(loop_frame) + self.outdent() + self.start_write(frame, node) + self.write(f"{self.choose_async('await ')}loop(") + if self.environment.is_async: + self.write("auto_aiter(") + self.visit(node.iter, frame) + if self.environment.is_async: + self.write(")") + self.write(", loop)") + self.end_write(frame) + + # at the end of the iteration, clear any assignments made in the + # loop from the top level + if self._assign_stack: + self._assign_stack[-1].difference_update(loop_frame.symbols.stores) + + def visit_If(self, node: nodes.If, frame: Frame) -> None: + if_frame = frame.soft() + self.writeline("if ", node) + self.visit(node.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(node.body, if_frame) + self.outdent() + for elif_ in node.elif_: + self.writeline("elif ", elif_) + self.visit(elif_.test, if_frame) + self.write(":") + self.indent() + self.blockvisit(elif_.body, if_frame) + self.outdent() + if node.else_: + self.writeline("else:") + self.indent() + self.blockvisit(node.else_, if_frame) + self.outdent() + + def visit_Macro(self, node: nodes.Macro, frame: Frame) -> None: + macro_frame, macro_ref = self.macro_body(node, frame) + self.newline() + if frame.toplevel: + if not node.name.startswith("_"): + self.write(f"context.exported_vars.add({node.name!r})") + self.writeline(f"context.vars[{node.name!r}] = ") + self.write(f"{frame.symbols.ref(node.name)} = ") + self.macro_def(macro_ref, macro_frame) + + def visit_CallBlock(self, node: nodes.CallBlock, frame: Frame) -> None: + call_frame, macro_ref = self.macro_body(node, frame) + self.writeline("caller = ") + self.macro_def(macro_ref, call_frame) + self.start_write(frame, node) + self.visit_Call(node.call, frame, forward_caller=True) + self.end_write(frame) + + def visit_FilterBlock(self, node: nodes.FilterBlock, frame: Frame) -> None: + filter_frame = frame.inner() + filter_frame.symbols.analyze_node(node) + self.enter_frame(filter_frame) + self.buffer(filter_frame) + self.blockvisit(node.body, filter_frame) + self.start_write(frame, node) + self.visit_Filter(node.filter, filter_frame) + self.end_write(frame) + self.leave_frame(filter_frame) + + def visit_With(self, node: nodes.With, frame: Frame) -> None: + with_frame = frame.inner() + with_frame.symbols.analyze_node(node) + self.enter_frame(with_frame) + for target, expr in zip(node.targets, node.values): + self.newline() + self.visit(target, with_frame) + self.write(" = ") + self.visit(expr, frame) + self.blockvisit(node.body, with_frame) + self.leave_frame(with_frame) + + def visit_ExprStmt(self, node: nodes.ExprStmt, frame: Frame) -> None: + self.newline(node) + self.visit(node.node, frame) + + class _FinalizeInfo(t.NamedTuple): + const: t.Optional[t.Callable[..., str]] + src: t.Optional[str] + + @staticmethod + def _default_finalize(value: t.Any) -> t.Any: + """The default finalize function if the environment isn't + configured with one. Or, if the environment has one, this is + called on that function's output for constants. + """ + return str(value) + + _finalize: t.Optional[_FinalizeInfo] = None + + def _make_finalize(self) -> _FinalizeInfo: + """Build the finalize function to be used on constants and at + runtime. Cached so it's only created once for all output nodes. + + Returns a ``namedtuple`` with the following attributes: + + ``const`` + A function to finalize constant data at compile time. + + ``src`` + Source code to output around nodes to be evaluated at + runtime. + """ + if self._finalize is not None: + return self._finalize + + finalize: t.Optional[t.Callable[..., t.Any]] + finalize = default = self._default_finalize + src = None + + if self.environment.finalize: + src = "environment.finalize(" + env_finalize = self.environment.finalize + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(env_finalize) # type: ignore + ) + finalize = None + + if pass_arg is None: + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(value)) + + else: + src = f"{src}{pass_arg}, " + + if pass_arg == "environment": + + def finalize(value: t.Any) -> t.Any: # noqa: F811 + return default(env_finalize(self.environment, value)) + + self._finalize = self._FinalizeInfo(finalize, src) + return self._finalize + + def _output_const_repr(self, group: t.Iterable[t.Any]) -> str: + """Given a group of constant values converted from ``Output`` + child nodes, produce a string to write to the template module + source. + """ + return repr(concat(group)) + + def _output_child_to_const( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> str: + """Try to optimize a child of an ``Output`` node by trying to + convert it to constant, finalized data at compile time. + + If :exc:`Impossible` is raised, the node is not constant and + will be evaluated at runtime. Any other exception will also be + evaluated at runtime for easier debugging. + """ + const = node.as_const(frame.eval_ctx) + + if frame.eval_ctx.autoescape: + const = escape(const) + + # Template data doesn't go through finalize. + if isinstance(node, nodes.TemplateData): + return str(const) + + return finalize.const(const) # type: ignore + + def _output_child_pre( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code before visiting a child of an + ``Output`` node. + """ + if frame.eval_ctx.volatile: + self.write("(escape if context.eval_ctx.autoescape else str)(") + elif frame.eval_ctx.autoescape: + self.write("escape(") + else: + self.write("str(") + + if finalize.src is not None: + self.write(finalize.src) + + def _output_child_post( + self, node: nodes.Expr, frame: Frame, finalize: _FinalizeInfo + ) -> None: + """Output extra source code after visiting a child of an + ``Output`` node. + """ + self.write(")") + + if finalize.src is not None: + self.write(")") + + def visit_Output(self, node: nodes.Output, frame: Frame) -> None: + # If an extends is active, don't render outside a block. + if frame.require_output_check: + # A top-level extends is known to exist at compile time. + if self.has_known_extends: + return + + self.writeline("if parent_template is None:") + self.indent() + + finalize = self._make_finalize() + body: t.List[t.Union[t.List[t.Any], nodes.Expr]] = [] + + # Evaluate constants at compile time if possible. Each item in + # body will be either a list of static data or a node to be + # evaluated at runtime. + for child in node.nodes: + try: + if not ( + # If the finalize function requires runtime context, + # constants can't be evaluated at compile time. + finalize.const + # Unless it's basic template data that won't be + # finalized anyway. + or isinstance(child, nodes.TemplateData) + ): + raise nodes.Impossible() + + const = self._output_child_to_const(child, frame, finalize) + except (nodes.Impossible, Exception): + # The node was not constant and needs to be evaluated at + # runtime. Or another error was raised, which is easier + # to debug at runtime. + body.append(child) + continue + + if body and isinstance(body[-1], list): + body[-1].append(const) + else: + body.append([const]) + + if frame.buffer is not None: + if len(body) == 1: + self.writeline(f"{frame.buffer}.append(") + else: + self.writeline(f"{frame.buffer}.extend((") + + self.indent() + + for item in body: + if isinstance(item, list): + # A group of constant data to join and output. + val = self._output_const_repr(item) + + if frame.buffer is None: + self.writeline("yield " + val) + else: + self.writeline(val + ",") + else: + if frame.buffer is None: + self.writeline("yield ", item) + else: + self.newline(item) + + # A node to be evaluated at runtime. + self._output_child_pre(item, frame, finalize) + self.visit(item, frame) + self._output_child_post(item, frame, finalize) + + if frame.buffer is not None: + self.write(",") + + if frame.buffer is not None: + self.outdent() + self.writeline(")" if len(body) == 1 else "))") + + if frame.require_output_check: + self.outdent() + + def visit_Assign(self, node: nodes.Assign, frame: Frame) -> None: + self.push_assign_tracking() + + # ``a.b`` is allowed for assignment, and is parsed as an NSRef. However, + # it is only valid if it references a Namespace object. Emit a check for + # that for each ref here, before assignment code is emitted. This can't + # be done in visit_NSRef as the ref could be in the middle of a tuple. + seen_refs: t.Set[str] = set() + + for nsref in node.find_all(nodes.NSRef): + if nsref.name in seen_refs: + # Only emit the check for each reference once, in case the same + # ref is used multiple times in a tuple, `ns.a, ns.b = c, d`. + continue + + seen_refs.add(nsref.name) + ref = frame.symbols.ref(nsref.name) + self.writeline(f"if not isinstance({ref}, Namespace):") + self.indent() + self.writeline( + "raise TemplateRuntimeError" + '("cannot assign attribute on non-namespace object")' + ) + self.outdent() + + self.newline(node) + self.visit(node.target, frame) + self.write(" = ") + self.visit(node.node, frame) + self.pop_assign_tracking(frame) + + def visit_AssignBlock(self, node: nodes.AssignBlock, frame: Frame) -> None: + self.push_assign_tracking() + block_frame = frame.inner() + # This is a special case. Since a set block always captures we + # will disable output checks. This way one can use set blocks + # toplevel even in extended templates. + block_frame.require_output_check = False + block_frame.symbols.analyze_node(node) + self.enter_frame(block_frame) + self.buffer(block_frame) + self.blockvisit(node.body, block_frame) + self.newline(node) + self.visit(node.target, frame) + self.write(" = (Markup if context.eval_ctx.autoescape else identity)(") + if node.filter is not None: + self.visit_Filter(node.filter, block_frame) + else: + self.write(f"concat({block_frame.buffer})") + self.write(")") + self.pop_assign_tracking(frame) + self.leave_frame(block_frame) + + # -- Expression Visitors + + def visit_Name(self, node: nodes.Name, frame: Frame) -> None: + if node.ctx == "store" and ( + frame.toplevel or frame.loop_frame or frame.block_frame + ): + if self._assign_stack: + self._assign_stack[-1].add(node.name) + ref = frame.symbols.ref(node.name) + + # If we are looking up a variable we might have to deal with the + # case where it's undefined. We can skip that case if the load + # instruction indicates a parameter which are always defined. + if node.ctx == "load": + load = frame.symbols.find_load(ref) + if not ( + load is not None + and load[0] == VAR_LOAD_PARAMETER + and not self.parameter_is_undeclared(ref) + ): + self.write( + f"(undefined(name={node.name!r}) if {ref} is missing else {ref})" + ) + return + + self.write(ref) + + def visit_NSRef(self, node: nodes.NSRef, frame: Frame) -> None: + # NSRef is a dotted assignment target a.b=c, but uses a[b]=c internally. + # visit_Assign emits code to validate that each ref is to a Namespace + # object only. That can't be emitted here as the ref could be in the + # middle of a tuple assignment. + ref = frame.symbols.ref(node.name) + self.writeline(f"{ref}[{node.attr!r}]") + + def visit_Const(self, node: nodes.Const, frame: Frame) -> None: + val = node.as_const(frame.eval_ctx) + if isinstance(val, float): + self.write(str(val)) + else: + self.write(repr(val)) + + def visit_TemplateData(self, node: nodes.TemplateData, frame: Frame) -> None: + try: + self.write(repr(node.as_const(frame.eval_ctx))) + except nodes.Impossible: + self.write( + f"(Markup if context.eval_ctx.autoescape else identity)({node.data!r})" + ) + + def visit_Tuple(self, node: nodes.Tuple, frame: Frame) -> None: + self.write("(") + idx = -1 + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write(",)" if idx == 0 else ")") + + def visit_List(self, node: nodes.List, frame: Frame) -> None: + self.write("[") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item, frame) + self.write("]") + + def visit_Dict(self, node: nodes.Dict, frame: Frame) -> None: + self.write("{") + for idx, item in enumerate(node.items): + if idx: + self.write(", ") + self.visit(item.key, frame) + self.write(": ") + self.visit(item.value, frame) + self.write("}") + + visit_Add = _make_binop("+") + visit_Sub = _make_binop("-") + visit_Mul = _make_binop("*") + visit_Div = _make_binop("/") + visit_FloorDiv = _make_binop("//") + visit_Pow = _make_binop("**") + visit_Mod = _make_binop("%") + visit_And = _make_binop("and") + visit_Or = _make_binop("or") + visit_Pos = _make_unop("+") + visit_Neg = _make_unop("-") + visit_Not = _make_unop("not ") + + @optimizeconst + def visit_Concat(self, node: nodes.Concat, frame: Frame) -> None: + if frame.eval_ctx.volatile: + func_name = "(markup_join if context.eval_ctx.volatile else str_join)" + elif frame.eval_ctx.autoescape: + func_name = "markup_join" + else: + func_name = "str_join" + self.write(f"{func_name}((") + for arg in node.nodes: + self.visit(arg, frame) + self.write(", ") + self.write("))") + + @optimizeconst + def visit_Compare(self, node: nodes.Compare, frame: Frame) -> None: + self.write("(") + self.visit(node.expr, frame) + for op in node.ops: + self.visit(op, frame) + self.write(")") + + def visit_Operand(self, node: nodes.Operand, frame: Frame) -> None: + self.write(f" {operators[node.op]} ") + self.visit(node.expr, frame) + + @optimizeconst + def visit_Getattr(self, node: nodes.Getattr, frame: Frame) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getattr(") + self.visit(node.node, frame) + self.write(f", {node.attr!r})") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Getitem(self, node: nodes.Getitem, frame: Frame) -> None: + # slices bypass the environment getitem method. + if isinstance(node.arg, nodes.Slice): + self.visit(node.node, frame) + self.write("[") + self.visit(node.arg, frame) + self.write("]") + else: + if self.environment.is_async: + self.write("(await auto_await(") + + self.write("environment.getitem(") + self.visit(node.node, frame) + self.write(", ") + self.visit(node.arg, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + def visit_Slice(self, node: nodes.Slice, frame: Frame) -> None: + if node.start is not None: + self.visit(node.start, frame) + self.write(":") + if node.stop is not None: + self.visit(node.stop, frame) + if node.step is not None: + self.write(":") + self.visit(node.step, frame) + + @contextmanager + def _filter_test_common( + self, node: t.Union[nodes.Filter, nodes.Test], frame: Frame, is_filter: bool + ) -> t.Iterator[None]: + if self.environment.is_async: + self.write("(await auto_await(") + + if is_filter: + self.write(f"{self.filters[node.name]}(") + func = self.environment.filters.get(node.name) + else: + self.write(f"{self.tests[node.name]}(") + func = self.environment.tests.get(node.name) + + # When inside an If or CondExpr frame, allow the filter to be + # undefined at compile time and only raise an error if it's + # actually called at runtime. See pull_dependencies. + if func is None and not frame.soft_frame: + type_name = "filter" if is_filter else "test" + self.fail(f"No {type_name} named {node.name!r}.", node.lineno) + + pass_arg = { + _PassArg.context: "context", + _PassArg.eval_context: "context.eval_ctx", + _PassArg.environment: "environment", + }.get( + _PassArg.from_obj(func) # type: ignore + ) + + if pass_arg is not None: + self.write(f"{pass_arg}, ") + + # Back to the visitor function to handle visiting the target of + # the filter or test. + yield + + self.signature(node, frame) + self.write(")") + + if self.environment.is_async: + self.write("))") + + @optimizeconst + def visit_Filter(self, node: nodes.Filter, frame: Frame) -> None: + with self._filter_test_common(node, frame, True): + # if the filter node is None we are inside a filter block + # and want to write to the current buffer + if node.node is not None: + self.visit(node.node, frame) + elif frame.eval_ctx.volatile: + self.write( + f"(Markup(concat({frame.buffer}))" + f" if context.eval_ctx.autoescape else concat({frame.buffer}))" + ) + elif frame.eval_ctx.autoescape: + self.write(f"Markup(concat({frame.buffer}))") + else: + self.write(f"concat({frame.buffer})") + + @optimizeconst + def visit_Test(self, node: nodes.Test, frame: Frame) -> None: + with self._filter_test_common(node, frame, False): + self.visit(node.node, frame) + + @optimizeconst + def visit_CondExpr(self, node: nodes.CondExpr, frame: Frame) -> None: + frame = frame.soft() + + def write_expr2() -> None: + if node.expr2 is not None: + self.visit(node.expr2, frame) + return + + self.write( + f'cond_expr_undefined("the inline if-expression on' + f" {self.position(node)} evaluated to false and no else" + f' section was defined.")' + ) + + self.write("(") + self.visit(node.expr1, frame) + self.write(" if ") + self.visit(node.test, frame) + self.write(" else ") + write_expr2() + self.write(")") + + @optimizeconst + def visit_Call( + self, node: nodes.Call, frame: Frame, forward_caller: bool = False + ) -> None: + if self.environment.is_async: + self.write("(await auto_await(") + if self.environment.sandboxed: + self.write("environment.call(context, ") + else: + self.write("context.call(") + self.visit(node.node, frame) + extra_kwargs = {"caller": "caller"} if forward_caller else None + loop_kwargs = {"_loop_vars": "_loop_vars"} if frame.loop_frame else {} + block_kwargs = {"_block_vars": "_block_vars"} if frame.block_frame else {} + if extra_kwargs: + extra_kwargs.update(loop_kwargs, **block_kwargs) + elif loop_kwargs or block_kwargs: + extra_kwargs = dict(loop_kwargs, **block_kwargs) + self.signature(node, frame, extra_kwargs) + self.write(")") + if self.environment.is_async: + self.write("))") + + def visit_Keyword(self, node: nodes.Keyword, frame: Frame) -> None: + self.write(node.key + "=") + self.visit(node.value, frame) + + # -- Unused nodes for extensions + + def visit_MarkSafe(self, node: nodes.MarkSafe, frame: Frame) -> None: + self.write("Markup(") + self.visit(node.expr, frame) + self.write(")") + + def visit_MarkSafeIfAutoescape( + self, node: nodes.MarkSafeIfAutoescape, frame: Frame + ) -> None: + self.write("(Markup if context.eval_ctx.autoescape else identity)(") + self.visit(node.expr, frame) + self.write(")") + + def visit_EnvironmentAttribute( + self, node: nodes.EnvironmentAttribute, frame: Frame + ) -> None: + self.write("environment." + node.name) + + def visit_ExtensionAttribute( + self, node: nodes.ExtensionAttribute, frame: Frame + ) -> None: + self.write(f"environment.extensions[{node.identifier!r}].{node.name}") + + def visit_ImportedName(self, node: nodes.ImportedName, frame: Frame) -> None: + self.write(self.import_aliases[node.importname]) + + def visit_InternalName(self, node: nodes.InternalName, frame: Frame) -> None: + self.write(node.name) + + def visit_ContextReference( + self, node: nodes.ContextReference, frame: Frame + ) -> None: + self.write("context") + + def visit_DerivedContextReference( + self, node: nodes.DerivedContextReference, frame: Frame + ) -> None: + self.write(self.derive_context(frame)) + + def visit_Continue(self, node: nodes.Continue, frame: Frame) -> None: + self.writeline("continue", node) + + def visit_Break(self, node: nodes.Break, frame: Frame) -> None: + self.writeline("break", node) + + def visit_Scope(self, node: nodes.Scope, frame: Frame) -> None: + scope_frame = frame.inner() + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + + def visit_OverlayScope(self, node: nodes.OverlayScope, frame: Frame) -> None: + ctx = self.temporary_identifier() + self.writeline(f"{ctx} = {self.derive_context(frame)}") + self.writeline(f"{ctx}.vars = ") + self.visit(node.context, frame) + self.push_context_reference(ctx) + + scope_frame = frame.inner(isolated=True) + scope_frame.symbols.analyze_node(node) + self.enter_frame(scope_frame) + self.blockvisit(node.body, scope_frame) + self.leave_frame(scope_frame) + self.pop_context_reference() + + def visit_EvalContextModifier( + self, node: nodes.EvalContextModifier, frame: Frame + ) -> None: + for keyword in node.options: + self.writeline(f"context.eval_ctx.{keyword.key} = ") + self.visit(keyword.value, frame) + try: + val = keyword.value.as_const(frame.eval_ctx) + except nodes.Impossible: + frame.eval_ctx.volatile = True + else: + setattr(frame.eval_ctx, keyword.key, val) + + def visit_ScopedEvalContextModifier( + self, node: nodes.ScopedEvalContextModifier, frame: Frame + ) -> None: + old_ctx_name = self.temporary_identifier() + saved_ctx = frame.eval_ctx.save() + self.writeline(f"{old_ctx_name} = context.eval_ctx.save()") + self.visit_EvalContextModifier(node, frame) + for child in node.body: + self.visit(child, frame) + frame.eval_ctx.revert(saved_ctx) + self.writeline(f"context.eval_ctx.revert({old_ctx_name})") diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/constants.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/constants.py new file mode 100644 index 000000000..41a1c23b0 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/constants.py @@ -0,0 +1,20 @@ +#: list of lorem ipsum words used by the lipsum() helper function +LOREM_IPSUM_WORDS = """\ +a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at +auctor augue bibendum blandit class commodo condimentum congue consectetuer +consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus +diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend +elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames +faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac +hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum +justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem +luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie +mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non +nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque +penatibus per pharetra phasellus placerat platea porta porttitor posuere +potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus +ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit +sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor +tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices +ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus +viverra volutpat vulputate""" diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/debug.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/debug.py new file mode 100644 index 000000000..eeeeee78b --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/debug.py @@ -0,0 +1,191 @@ +import sys +import typing as t +from types import CodeType +from types import TracebackType + +from .exceptions import TemplateSyntaxError +from .utils import internal_code +from .utils import missing + +if t.TYPE_CHECKING: + from .runtime import Context + + +def rewrite_traceback_stack(source: t.Optional[str] = None) -> BaseException: + """Rewrite the current exception to replace any tracebacks from + within compiled template code with tracebacks that look like they + came from the template source. + + This must be called within an ``except`` block. + + :param source: For ``TemplateSyntaxError``, the original source if + known. + :return: The original exception with the rewritten traceback. + """ + _, exc_value, tb = sys.exc_info() + exc_value = t.cast(BaseException, exc_value) + tb = t.cast(TracebackType, tb) + + if isinstance(exc_value, TemplateSyntaxError) and not exc_value.translated: + exc_value.translated = True + exc_value.source = source + # Remove the old traceback, otherwise the frames from the + # compiler still show up. + exc_value.with_traceback(None) + # Outside of runtime, so the frame isn't executing template + # code, but it still needs to point at the template. + tb = fake_traceback( + exc_value, None, exc_value.filename or "", exc_value.lineno + ) + else: + # Skip the frame for the render function. + tb = tb.tb_next + + stack = [] + + # Build the stack of traceback object, replacing any in template + # code with the source file and line information. + while tb is not None: + # Skip frames decorated with @internalcode. These are internal + # calls that aren't useful in template debugging output. + if tb.tb_frame.f_code in internal_code: + tb = tb.tb_next + continue + + template = tb.tb_frame.f_globals.get("__jinja_template__") + + if template is not None: + lineno = template.get_corresponding_lineno(tb.tb_lineno) + fake_tb = fake_traceback(exc_value, tb, template.filename, lineno) + stack.append(fake_tb) + else: + stack.append(tb) + + tb = tb.tb_next + + tb_next = None + + # Assign tb_next in reverse to avoid circular references. + for tb in reversed(stack): + tb.tb_next = tb_next + tb_next = tb + + return exc_value.with_traceback(tb_next) + + +def fake_traceback( # type: ignore + exc_value: BaseException, tb: t.Optional[TracebackType], filename: str, lineno: int +) -> TracebackType: + """Produce a new traceback object that looks like it came from the + template source instead of the compiled code. The filename, line + number, and location name will point to the template, and the local + variables will be the current template context. + + :param exc_value: The original exception to be re-raised to create + the new traceback. + :param tb: The original traceback to get the local variables and + code info from. + :param filename: The template filename. + :param lineno: The line number in the template source. + """ + if tb is not None: + # Replace the real locals with the context that would be + # available at that point in the template. + locals = get_template_locals(tb.tb_frame.f_locals) + locals.pop("__jinja_exception__", None) + else: + locals = {} + + globals = { + "__name__": filename, + "__file__": filename, + "__jinja_exception__": exc_value, + } + # Raise an exception at the correct line number. + code: CodeType = compile( + "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec" + ) + + # Build a new code object that points to the template file and + # replaces the location with a block name. + location = "template" + + if tb is not None: + function = tb.tb_frame.f_code.co_name + + if function == "root": + location = "top-level template code" + elif function.startswith("block_"): + location = f"block {function[6:]!r}" + + if sys.version_info >= (3, 8): + code = code.replace(co_name=location) + else: + code = CodeType( + code.co_argcount, + code.co_kwonlyargcount, + code.co_nlocals, + code.co_stacksize, + code.co_flags, + code.co_code, + code.co_consts, + code.co_names, + code.co_varnames, + code.co_filename, + location, + code.co_firstlineno, + code.co_lnotab, + code.co_freevars, + code.co_cellvars, + ) + + # Execute the new code, which is guaranteed to raise, and return + # the new traceback without this frame. + try: + exec(code, globals, locals) + except BaseException: + return sys.exc_info()[2].tb_next # type: ignore + + +def get_template_locals(real_locals: t.Mapping[str, t.Any]) -> t.Dict[str, t.Any]: + """Based on the runtime locals, get the context that would be + available at that point in the template. + """ + # Start with the current template context. + ctx: t.Optional[Context] = real_locals.get("context") + + if ctx is not None: + data: t.Dict[str, t.Any] = ctx.get_all().copy() + else: + data = {} + + # Might be in a derived context that only sets local variables + # rather than pushing a context. Local variables follow the scheme + # l_depth_name. Find the highest-depth local that has a value for + # each name. + local_overrides: t.Dict[str, t.Tuple[int, t.Any]] = {} + + for name, value in real_locals.items(): + if not name.startswith("l_") or value is missing: + # Not a template variable, or no longer relevant. + continue + + try: + _, depth_str, name = name.split("_", 2) + depth = int(depth_str) + except ValueError: + continue + + cur_depth = local_overrides.get(name, (-1,))[0] + + if cur_depth < depth: + local_overrides[name] = (depth, value) + + # Modify the context with any derived context. + for name, (_, value) in local_overrides.items(): + if value is missing: + data.pop(name, None) + else: + data[name] = value + + return data diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/defaults.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/defaults.py new file mode 100644 index 000000000..638cad3d2 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/defaults.py @@ -0,0 +1,48 @@ +import typing as t + +from .filters import FILTERS as DEFAULT_FILTERS # noqa: F401 +from .tests import TESTS as DEFAULT_TESTS # noqa: F401 +from .utils import Cycler +from .utils import generate_lorem_ipsum +from .utils import Joiner +from .utils import Namespace + +if t.TYPE_CHECKING: + import typing_extensions as te + +# defaults for the parser / lexer +BLOCK_START_STRING = "{%" +BLOCK_END_STRING = "%}" +VARIABLE_START_STRING = "{{" +VARIABLE_END_STRING = "}}" +COMMENT_START_STRING = "{#" +COMMENT_END_STRING = "#}" +LINE_STATEMENT_PREFIX: t.Optional[str] = None +LINE_COMMENT_PREFIX: t.Optional[str] = None +TRIM_BLOCKS = False +LSTRIP_BLOCKS = False +NEWLINE_SEQUENCE: "te.Literal['\\n', '\\r\\n', '\\r']" = "\n" +KEEP_TRAILING_NEWLINE = False + +# default filters, tests and namespace + +DEFAULT_NAMESPACE = { + "range": range, + "dict": dict, + "lipsum": generate_lorem_ipsum, + "cycler": Cycler, + "joiner": Joiner, + "namespace": Namespace, +} + +# default policies +DEFAULT_POLICIES: t.Dict[str, t.Any] = { + "compiler.ascii_str": True, + "urlize.rel": "noopener", + "urlize.target": None, + "urlize.extra_schemes": None, + "truncate.leeway": 5, + "json.dumps_function": None, + "json.dumps_kwargs": {"sort_keys": True}, + "ext.i18n.trimmed": False, +} diff --git a/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/environment.py b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/environment.py new file mode 100644 index 000000000..0fc6e5be8 --- /dev/null +++ b/src/TrackerTool/admin-dashboard/backend/venv/Lib/site-packages/jinja2/environment.py @@ -0,0 +1,1672 @@ +"""Classes for managing templates and their runtime and compile time +options. +""" + +import os +import typing +import typing as t +import weakref +from collections import ChainMap +from functools import lru_cache +from functools import partial +from functools import reduce +from types import CodeType + +from markupsafe import Markup + +from . import nodes +from .compiler import CodeGenerator +from .compiler import generate +from .defaults import BLOCK_END_STRING +from .defaults import BLOCK_START_STRING +from .defaults import COMMENT_END_STRING +from .defaults import COMMENT_START_STRING +from .defaults import DEFAULT_FILTERS # type: ignore[attr-defined] +from .defaults import DEFAULT_NAMESPACE +from .defaults import DEFAULT_POLICIES +from .defaults import DEFAULT_TESTS # type: ignore[attr-defined] +from .defaults import KEEP_TRAILING_NEWLINE +from .defaults import LINE_COMMENT_PREFIX +from .defaults import LINE_STATEMENT_PREFIX +from .defaults import LSTRIP_BLOCKS +from .defaults import NEWLINE_SEQUENCE +from .defaults import TRIM_BLOCKS +from .defaults import VARIABLE_END_STRING +from .defaults import VARIABLE_START_STRING +from .exceptions import TemplateNotFound +from .exceptions import TemplateRuntimeError +from .exceptions import TemplatesNotFound +from .exceptions import TemplateSyntaxError +from .exceptions import UndefinedError +from .lexer import get_lexer +from .lexer import Lexer +from .lexer import TokenStream +from .nodes import EvalContext +from .parser import Parser +from .runtime import Context +from .runtime import new_context +from .runtime import Undefined +from .utils import _PassArg +from .utils import concat +from .utils import consume +from .utils import import_string +from .utils import internalcode +from .utils import LRUCache +from .utils import missing + +if t.TYPE_CHECKING: + import typing_extensions as te + + from .bccache import BytecodeCache + from .ext import Extension + from .loaders import BaseLoader + +_env_bound = t.TypeVar("_env_bound", bound="Environment") + + +# for direct template usage we have up to ten living environments +@lru_cache(maxsize=10) +def get_spontaneous_environment(cls: t.Type[_env_bound], *args: t.Any) -> _env_bound: + """Return a new spontaneous environment. A spontaneous environment + is used for templates created directly rather than through an + existing environment. + + :param cls: Environment class to create. + :param args: Positional arguments passed to environment. + """ + env = cls(*args) + env.shared = True + return env + + +def create_cache( + size: int, +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Return the cache class for the given size.""" + if size == 0: + return None + + if size < 0: + return {} + + return LRUCache(size) # type: ignore + + +def copy_cache( + cache: t.Optional[t.MutableMapping[t.Any, t.Any]], +) -> t.Optional[t.MutableMapping[t.Tuple["weakref.ref[t.Any]", str], "Template"]]: + """Create an empty copy of the given cache.""" + if cache is None: + return None + + if type(cache) is dict: # noqa E721 + return {} + + return LRUCache(cache.capacity) # type: ignore + + +def load_extensions( + environment: "Environment", + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]], +) -> t.Dict[str, "Extension"]: + """Load the extensions from the list and bind it to the environment. + Returns a dict of instantiated extensions. + """ + result = {} + + for extension in extensions: + if isinstance(extension, str): + extension = t.cast(t.Type["Extension"], import_string(extension)) + + result[extension.identifier] = extension(environment) + + return result + + +def _environment_config_check(environment: _env_bound) -> _env_bound: + """Perform a sanity check on the environment.""" + assert issubclass( + environment.undefined, Undefined + ), "'undefined' must be a subclass of 'jinja2.Undefined'." + assert ( + environment.block_start_string + != environment.variable_start_string + != environment.comment_start_string + ), "block, variable and comment start strings must be different." + assert environment.newline_sequence in { + "\r", + "\r\n", + "\n", + }, "'newline_sequence' must be one of '\\n', '\\r\\n', or '\\r'." + return environment + + +class Environment: + r"""The core component of Jinja is the `Environment`. It contains + important shared variables like configuration, filters, tests, + globals and others. Instances of this class may be modified if + they are not shared and if no template was loaded so far. + Modifications on environments after the first template was loaded + will lead to surprising effects and undefined behavior. + + Here are the possible initialization parameters: + + `block_start_string` + The string marking the beginning of a block. Defaults to ``'{%'``. + + `block_end_string` + The string marking the end of a block. Defaults to ``'%}'``. + + `variable_start_string` + The string marking the beginning of a print statement. + Defaults to ``'{{'``. + + `variable_end_string` + The string marking the end of a print statement. Defaults to + ``'}}'``. + + `comment_start_string` + The string marking the beginning of a comment. Defaults to ``'{#'``. + + `comment_end_string` + The string marking the end of a comment. Defaults to ``'#}'``. + + `line_statement_prefix` + If given and a string, this will be used as prefix for line based + statements. See also :ref:`line-statements`. + + `line_comment_prefix` + If given and a string, this will be used as prefix for line based + comments. See also :ref:`line-statements`. + + .. versionadded:: 2.2 + + `trim_blocks` + If this is set to ``True`` the first newline after a block is + removed (block, not variable tag!). Defaults to `False`. + + `lstrip_blocks` + If this is set to ``True`` leading spaces and tabs are stripped + from the start of a line to a block. Defaults to `False`. + + `newline_sequence` + The sequence that starts a newline. Must be one of ``'\r'``, + ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a + useful default for Linux and OS X systems as well as web + applications. + + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + + `extensions` + List of Jinja extensions to use. This can either be import paths + as strings or extension classes. For more information have a + look at :ref:`the extensions documentation `. + + `optimized` + should the optimizer be enabled? Default is ``True``. + + `undefined` + :class:`Undefined` or a subclass of it that is used to represent + undefined values in the template. + + `finalize` + A callable that can be used to process the result of a variable + expression before it is output. For example one can convert + ``None`` implicitly into an empty string here. + + `autoescape` + If set to ``True`` the XML/HTML autoescaping feature is enabled by + default. For more details about autoescaping see + :class:`~markupsafe.Markup`. As of Jinja 2.4 this can also + be a callable that is passed the template name and has to + return ``True`` or ``False`` depending on autoescape should be + enabled by default. + + .. versionchanged:: 2.4 + `autoescape` can now be a function + + `loader` + The template loader for this environment. + + `cache_size` + The size of the cache. Per default this is ``400`` which means + that if more than 400 templates are loaded the loader will clean + out the least recently used template. If the cache size is set to + ``0`` templates are recompiled all the time, if the cache size is + ``-1`` the cache will not be cleaned. + + .. versionchanged:: 2.8 + The cache size was increased to 400 from a low 50. + + `auto_reload` + Some loaders load templates from locations where the template + sources may change (ie: file system or database). If + ``auto_reload`` is set to ``True`` (default) every time a template is + requested the loader checks if the source changed and if yes, it + will reload the template. For higher performance it's possible to + disable that. + + `bytecode_cache` + If set to a bytecode cache object, this object will provide a + cache for the internal Jinja bytecode so that templates don't + have to be parsed if they were not changed. + + See :ref:`bytecode-cache` for more information. + + `enable_async` + If set to true this enables async template execution which + allows using async functions and generators. + """ + + #: if this environment is sandboxed. Modifying this variable won't make + #: the environment sandboxed though. For a real sandboxed environment + #: have a look at jinja2.sandbox. This flag alone controls the code + #: generation by the compiler. + sandboxed = False + + #: True if the environment is just an overlay + overlayed = False + + #: the environment this environment is linked to if it is an overlay + linked_to: t.Optional["Environment"] = None + + #: shared environments have this set to `True`. A shared environment + #: must not be modified + shared = False + + #: the class that is used for code generation. See + #: :class:`~jinja2.compiler.CodeGenerator` for more information. + code_generator_class: t.Type["CodeGenerator"] = CodeGenerator + + concat = "".join + + #: the context class that is used for templates. See + #: :class:`~jinja2.runtime.Context` for more information. + context_class: t.Type[Context] = Context + + template_class: t.Type["Template"] + + def __init__( + self, + block_start_string: str = BLOCK_START_STRING, + block_end_string: str = BLOCK_END_STRING, + variable_start_string: str = VARIABLE_START_STRING, + variable_end_string: str = VARIABLE_END_STRING, + comment_start_string: str = COMMENT_START_STRING, + comment_end_string: str = COMMENT_END_STRING, + line_statement_prefix: t.Optional[str] = LINE_STATEMENT_PREFIX, + line_comment_prefix: t.Optional[str] = LINE_COMMENT_PREFIX, + trim_blocks: bool = TRIM_BLOCKS, + lstrip_blocks: bool = LSTRIP_BLOCKS, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = NEWLINE_SEQUENCE, + keep_trailing_newline: bool = KEEP_TRAILING_NEWLINE, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = (), + optimized: bool = True, + undefined: t.Type[Undefined] = Undefined, + finalize: t.Optional[t.Callable[..., t.Any]] = None, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = False, + loader: t.Optional["BaseLoader"] = None, + cache_size: int = 400, + auto_reload: bool = True, + bytecode_cache: t.Optional["BytecodeCache"] = None, + enable_async: bool = False, + ): + # !!Important notice!! + # The constructor accepts quite a few arguments that should be + # passed by keyword rather than position. However it's important to + # not change the order of arguments because it's used at least + # internally in those cases: + # - spontaneous environments (i18n extension and Template) + # - unittests + # If parameter changes are required only add parameters at the end + # and don't change the arguments (or the defaults!) of the arguments + # existing already. + + # lexer / parser information + self.block_start_string = block_start_string + self.block_end_string = block_end_string + self.variable_start_string = variable_start_string + self.variable_end_string = variable_end_string + self.comment_start_string = comment_start_string + self.comment_end_string = comment_end_string + self.line_statement_prefix = line_statement_prefix + self.line_comment_prefix = line_comment_prefix + self.trim_blocks = trim_blocks + self.lstrip_blocks = lstrip_blocks + self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline + + # runtime information + self.undefined: t.Type[Undefined] = undefined + self.optimized = optimized + self.finalize = finalize + self.autoescape = autoescape + + # defaults + self.filters = DEFAULT_FILTERS.copy() + self.tests = DEFAULT_TESTS.copy() + self.globals = DEFAULT_NAMESPACE.copy() + + # set the loader provided + self.loader = loader + self.cache = create_cache(cache_size) + self.bytecode_cache = bytecode_cache + self.auto_reload = auto_reload + + # configurable policies + self.policies = DEFAULT_POLICIES.copy() + + # load extensions + self.extensions = load_extensions(self, extensions) + + self.is_async = enable_async + _environment_config_check(self) + + def add_extension(self, extension: t.Union[str, t.Type["Extension"]]) -> None: + """Adds an extension after the environment was created. + + .. versionadded:: 2.5 + """ + self.extensions.update(load_extensions(self, [extension])) + + def extend(self, **attributes: t.Any) -> None: + """Add the items to the instance of the environment if they do not exist + yet. This is used by :ref:`extensions ` to register + callbacks and configuration values without breaking inheritance. + """ + for key, value in attributes.items(): + if not hasattr(self, key): + setattr(self, key, value) + + def overlay( + self, + block_start_string: str = missing, + block_end_string: str = missing, + variable_start_string: str = missing, + variable_end_string: str = missing, + comment_start_string: str = missing, + comment_end_string: str = missing, + line_statement_prefix: t.Optional[str] = missing, + line_comment_prefix: t.Optional[str] = missing, + trim_blocks: bool = missing, + lstrip_blocks: bool = missing, + newline_sequence: "te.Literal['\\n', '\\r\\n', '\\r']" = missing, + keep_trailing_newline: bool = missing, + extensions: t.Sequence[t.Union[str, t.Type["Extension"]]] = missing, + optimized: bool = missing, + undefined: t.Type[Undefined] = missing, + finalize: t.Optional[t.Callable[..., t.Any]] = missing, + autoescape: t.Union[bool, t.Callable[[t.Optional[str]], bool]] = missing, + loader: t.Optional["BaseLoader"] = missing, + cache_size: int = missing, + auto_reload: bool = missing, + bytecode_cache: t.Optional["BytecodeCache"] = missing, + enable_async: bool = missing, + ) -> "te.Self": + """Create a new overlay environment that shares all the data with the + current environment except for cache and the overridden attributes. + Extensions cannot be removed for an overlayed environment. An overlayed + environment automatically gets all the extensions of the environment it + is linked to plus optional extra extensions. + + Creating overlays should happen after the initial environment was set + up completely. Not all attributes are truly linked, some are just + copied over so modifications on the original environment may not shine + through. + + .. versionchanged:: 3.1.5 + ``enable_async`` is applied correctly. + + .. versionchanged:: 3.1.2 + Added the ``newline_sequence``, ``keep_trailing_newline``, + and ``enable_async`` parameters to match ``__init__``. + """ + args = dict(locals()) + del args["self"], args["cache_size"], args["extensions"], args["enable_async"] + + rv = object.__new__(self.__class__) + rv.__dict__.update(self.__dict__) + rv.overlayed = True + rv.linked_to = self + + for key, value in args.items(): + if value is not missing: + setattr(rv, key, value) + + if cache_size is not missing: + rv.cache = create_cache(cache_size) + else: + rv.cache = copy_cache(self.cache) + + rv.extensions = {} + for key, value in self.extensions.items(): + rv.extensions[key] = value.bind(rv) + if extensions is not missing: + rv.extensions.update(load_extensions(rv, extensions)) + + if enable_async is not missing: + rv.is_async = enable_async + + return _environment_config_check(rv) + + @property + def lexer(self) -> Lexer: + """The lexer for this environment.""" + return get_lexer(self) + + def iter_extensions(self) -> t.Iterator["Extension"]: + """Iterates over the extensions by priority.""" + return iter(sorted(self.extensions.values(), key=lambda x: x.priority)) + + def getitem( + self, obj: t.Any, argument: t.Union[str, t.Any] + ) -> t.Union[t.Any, Undefined]: + """Get an item or attribute of an object but prefer the item.""" + try: + return obj[argument] + except (AttributeError, TypeError, LookupError): + if isinstance(argument, str): + try: + attr = str(argument) + except Exception: + pass + else: + try: + return getattr(obj, attr) + except AttributeError: + pass + return self.undefined(obj=obj, name=argument) + + def getattr(self, obj: t.Any, attribute: str) -> t.Any: + """Get an item or attribute of an object but prefer the attribute. + Unlike :meth:`getitem` the attribute *must* be a string. + """ + try: + return getattr(obj, attribute) + except AttributeError: + pass + try: + return obj[attribute] + except (TypeError, LookupError, AttributeError): + return self.undefined(obj=obj, name=attribute) + + def _filter_test_common( + self, + name: t.Union[str, Undefined], + value: t.Any, + args: t.Optional[t.Sequence[t.Any]], + kwargs: t.Optional[t.Mapping[str, t.Any]], + context: t.Optional[Context], + eval_ctx: t.Optional[EvalContext], + is_filter: bool, + ) -> t.Any: + if is_filter: + env_map = self.filters + type_name = "filter" + else: + env_map = self.tests + type_name = "test" + + func = env_map.get(name) # type: ignore + + if func is None: + msg = f"No {type_name} named {name!r}." + + if isinstance(name, Undefined): + try: + name._fail_with_undefined_error() + except Exception as e: + msg = f"{msg} ({e}; did you forget to quote the callable name?)" + + raise TemplateRuntimeError(msg) + + args = [value, *(args if args is not None else ())] + kwargs = kwargs if kwargs is not None else {} + pass_arg = _PassArg.from_obj(func) + + if pass_arg is _PassArg.context: + if context is None: + raise TemplateRuntimeError( + f"Attempted to invoke a context {type_name} without context." + ) + + args.insert(0, context) + elif pass_arg is _PassArg.eval_context: + if eval_ctx is None: + if context is not None: + eval_ctx = context.eval_ctx + else: + eval_ctx = EvalContext(self) + + args.insert(0, eval_ctx) + elif pass_arg is _PassArg.environment: + args.insert(0, self) + + return func(*args, **kwargs) + + def call_filter( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a filter on a value the same way the compiler does. + + This might return a coroutine if the filter is running from an + environment in async mode and the filter supports async + execution. It's your responsibility to await this if needed. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, True + ) + + def call_test( + self, + name: str, + value: t.Any, + args: t.Optional[t.Sequence[t.Any]] = None, + kwargs: t.Optional[t.Mapping[str, t.Any]] = None, + context: t.Optional[Context] = None, + eval_ctx: t.Optional[EvalContext] = None, + ) -> t.Any: + """Invoke a test on a value the same way the compiler does. + + This might return a coroutine if the test is running from an + environment in async mode and the test supports async execution. + It's your responsibility to await this if needed. + + .. versionchanged:: 3.0 + Tests support ``@pass_context``, etc. decorators. Added + the ``context`` and ``eval_ctx`` parameters. + + .. versionadded:: 2.7 + """ + return self._filter_test_common( + name, value, args, kwargs, context, eval_ctx, False + ) + + @internalcode + def parse( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> nodes.Template: + """Parse the sourcecode and return the abstract syntax tree. This + tree of nodes is used by the compiler to convert the template into + executable source- or bytecode. This is useful for debugging or to + extract information from templates. + + If you are :ref:`developing Jinja extensions ` + this gives you a good overview of the node tree generated. + """ + try: + return self._parse(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def _parse( + self, source: str, name: t.Optional[str], filename: t.Optional[str] + ) -> nodes.Template: + """Internal parsing function used by `parse` and `compile`.""" + return Parser(self, source, name, filename).parse() + + def lex( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> t.Iterator[t.Tuple[int, str, str]]: + """Lex the given sourcecode and return a generator that yields + tokens as tuples in the form ``(lineno, token_type, value)``. + This can be useful for :ref:`extension development ` + and debugging templates. + + This does not perform preprocessing. If you want the preprocessing + of the extensions to be applied you have to filter source through + the :meth:`preprocess` method. + """ + source = str(source) + try: + return self.lexer.tokeniter(source, name, filename) + except TemplateSyntaxError: + self.handle_exception(source=source) + + def preprocess( + self, + source: str, + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + ) -> str: + """Preprocesses the source with all extensions. This is automatically + called for all parsing and compiling methods but *not* for :meth:`lex` + because there you usually only want the actual source tokenized. + """ + return reduce( + lambda s, e: e.preprocess(s, name, filename), + self.iter_extensions(), + str(source), + ) + + def _tokenize( + self, + source: str, + name: t.Optional[str], + filename: t.Optional[str] = None, + state: t.Optional[str] = None, + ) -> TokenStream: + """Called by the parser to do the preprocessing and filtering + for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`. + """ + source = self.preprocess(source, name, filename) + stream = self.lexer.tokenize(source, name, filename, state) + + for ext in self.iter_extensions(): + stream = ext.filter_stream(stream) # type: ignore + + if not isinstance(stream, TokenStream): + stream = TokenStream(stream, name, filename) + + return stream + + def _generate( + self, + source: nodes.Template, + name: t.Optional[str], + filename: t.Optional[str], + defer_init: bool = False, + ) -> str: + """Internal hook that can be overridden to hook a different generate + method in. + + .. versionadded:: 2.5 + """ + return generate( # type: ignore + source, + self, + name, + filename, + defer_init=defer_init, + optimized=self.optimized, + ) + + def _compile(self, source: str, filename: str) -> CodeType: + """Internal hook that can be overridden to hook a different compile + method in. + + .. versionadded:: 2.5 + """ + return compile(source, filename, "exec") + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[False]" = False, + defer_init: bool = False, + ) -> CodeType: ... + + @typing.overload + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: "te.Literal[True]" = ..., + defer_init: bool = False, + ) -> str: ... + + @internalcode + def compile( + self, + source: t.Union[str, nodes.Template], + name: t.Optional[str] = None, + filename: t.Optional[str] = None, + raw: bool = False, + defer_init: bool = False, + ) -> t.Union[str, CodeType]: + """Compile a node or template source code. The `name` parameter is + the load name of the template after it was joined using + :meth:`join_path` if necessary, not the filename on the file system. + the `filename` parameter is the estimated filename of the template on + the file system. If the template came from a database or memory this + can be omitted. + + The return value of this method is a python code object. If the `raw` + parameter is `True` the return value will be a string with python + code equivalent to the bytecode returned otherwise. This method is + mainly used internally. + + `defer_init` is use internally to aid the module code generator. This + causes the generated code to be able to import without the global + environment variable to be set. + + .. versionadded:: 2.4 + `defer_init` parameter added. + """ + source_hint = None + try: + if isinstance(source, str): + source_hint = source + source = self._parse(source, name, filename) + source = self._generate(source, name, filename, defer_init=defer_init) + if raw: + return source + if filename is None: + filename = "