diff --git a/.gitattributes b/.gitattributes index f3bffe3c0..6107f6104 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,7 @@ *.png binary *.pptx binary *.odp binary + +# Instruct linguist not to ignore the PEPs +# https://github.com/github-linguist/linguist/blob/master/docs/overrides.md +peps/*.rst text linguist-detectable diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 286a782e1..15fc5c5bd 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,679 +12,684 @@ requirements.txt @AA-Turner infra/ @ewdurbin pep_sphinx_extensions/ @AA-Turner -AUTHOR_OVERRIDES.csv @AA-Turner build.py @AA-Turner -conf.py @AA-Turner -contents.rst @AA-Turner +peps/conf.py @AA-Turner +peps/contents.rst @AA-Turner # Linting infrastructure .codespell/ @CAM-Gerlach @hugovk .codespellrc @CAM-Gerlach @hugovk .pre-commit-config.yaml @CAM-Gerlach @hugovk +.ruff.toml @AA-Turner @CAM-Gerlach @hugovk +check-peps.py @AA-Turner @CAM-Gerlach @hugovk # Git infrastructure .gitattributes @CAM-Gerlach .gitignore @CAM-Gerlach -pep-0001.txt @warsaw @ncoghlan -pep-0001-process_flow.png @warsaw @ncoghlan -pep-0001/ @warsaw @ncoghlan -# pep-0002.txt -pep-0003.txt @jeremyhylton -pep-0004.txt @brettcannon -# pep-0005.txt -# pep-0006.txt -pep-0007.txt @gvanrossum @warsaw -pep-0008.txt @gvanrossum @warsaw @ncoghlan -pep-0009.txt @warsaw -pep-0010.txt @warsaw -pep-0011.txt @brettcannon -pep-0012.rst @brettcannon @warsaw -pep-0012/ @brettcannon -# pep-0013.rst is owned by the entire core team. +peps/pep-0001.rst @warsaw @ncoghlan +peps/pep-0001-process_flow.png @warsaw @ncoghlan +peps/pep-0001/ @warsaw @ncoghlan +# peps/pep-0002.rst +peps/pep-0003.rst @jeremyhylton +peps/pep-0004.rst @brettcannon +# peps/pep-0005.rst +# peps/pep-0006.rst +peps/pep-0007.rst @gvanrossum @warsaw +peps/pep-0008.rst @gvanrossum @warsaw @ncoghlan +peps/pep-0009.rst @warsaw +peps/pep-0010.rst @warsaw +peps/pep-0011.rst @brettcannon +peps/pep-0012.rst @brettcannon @warsaw +peps/pep-0012/ @brettcannon +# peps/pep-0013.rst is owned by the entire core team. # ... -pep-0020.txt @tim-one +peps/pep-0020.rst @tim-one # ... -pep-0042.txt @jeremyhylton +peps/pep-0042.rst @jeremyhylton # ... -pep-0100.txt @malemburg -pep-0101.txt @Yhg1s @pablogsal @ambv @ned-deily -pep-0102.txt @warsaw @gvanrossum -# pep-0103.txt +peps/pep-0100.rst @malemburg +peps/pep-0101.rst @Yhg1s @pablogsal @ambv @ned-deily +peps/pep-0102.rst @warsaw @gvanrossum +# peps/pep-0103.rst # ... -pep-0160.txt @freddrake +peps/pep-0160.rst @freddrake # ... -pep-0200.txt @jeremyhylton -pep-0201.txt @warsaw -pep-0202.txt @warsaw -pep-0203.txt @Yhg1s -pep-0204.txt @Yhg1s -pep-0205.txt @freddrake -# pep-0206.txt -pep-0207.txt @gvanrossum -pep-0208.txt @nascheme @malemburg -# pep-0209.txt -# pep-0210.txt -# pep-0211.txt -# pep-0212.txt -# pep-0213.txt -pep-0214.txt @warsaw -# pep-0215.txt -# pep-0216.txt -# pep-0217.txt -pep-0218.txt @rhettinger -# pep-0219.txt -# pep-0220.txt -pep-0221.txt @Yhg1s -# pep-0222.txt -pep-0223.txt @tim-one -pep-0224.txt @malemburg -# pep-0225.txt -pep-0226.txt @jeremyhylton -pep-0227.txt @jeremyhylton -pep-0228.txt @gvanrossum -# pep-0229.txt -pep-0230.txt @gvanrossum -pep-0231.txt @warsaw -pep-0232.txt @warsaw -# pep-0233.txt -pep-0234.txt @gvanrossum -pep-0235.txt @tim-one -pep-0236.txt @tim-one -pep-0237.txt @gvanrossum -pep-0238.txt @gvanrossum -# pep-0239.txt -# pep-0240.txt -# pep-0241.txt -# pep-0242.txt -# pep-0243.txt -# pep-0244.txt -# pep-0245.txt -pep-0246.txt @aleaxit -# pep-0247.txt -pep-0248.txt @malemburg -pep-0249.txt @malemburg -pep-0250.txt @pfmoore -pep-0251.txt @warsaw @gvanrossum -pep-0252.txt @gvanrossum -pep-0253.txt @gvanrossum -pep-0254.txt @gvanrossum -pep-0255.txt @nascheme @tim-one -# pep-0256.txt -pep-0257.txt @gvanrossum -# pep-0258.txt -pep-0259.txt @gvanrossum -pep-0260.txt @gvanrossum -# pep-0261.txt -# pep-0262.txt -pep-0263.txt @malemburg -# pep-0264.txt -# pep-0265.txt -# pep-0266.txt -pep-0267.txt @jeremyhylton -# pep-0268.txt -# pep-0269.txt -# pep-0270.txt -# pep-0271.txt -# pep-0272.txt -# pep-0273.txt -pep-0274.txt @warsaw -pep-0275.txt @malemburg -# pep-0276.txt -# pep-0277.txt -pep-0278.txt @jackjansen -pep-0279.txt @rhettinger -pep-0280.txt @gvanrossum -# pep-0281.txt -pep-0282.txt @vsajip -pep-0283.txt @gvanrossum -# pep-0284.txt -pep-0285.txt @gvanrossum -# pep-0286.txt -# pep-0287.txt -pep-0288.txt @rhettinger -pep-0289.txt @rhettinger -pep-0290.txt @rhettinger -# pep-0291.txt -pep-0292.txt @warsaw -pep-0293.txt @doerwalter -# pep-0294.txt -# pep-0295.txt -# pep-0296.txt -pep-0297.txt @malemburg -pep-0298.txt @theller -# pep-0299.txt -# pep-0301.txt -pep-0302.txt @pfmoore -# pep-0303.txt -# pep-0304.txt -# pep-0305.txt -pep-0306.txt @jackdied @ncoghlan @benjaminp -pep-0307.txt @gvanrossum @tim-one -pep-0308.txt @gvanrossum @rhettinger -# pep-0309.txt -pep-0310.txt @pfmoore -pep-0311.txt @mhammond -pep-0312.txt @aleaxit -# pep-0313.txt -# pep-0314.txt -pep-0315.txt @rhettinger -# pep-0316.txt -# pep-0317.txt -# pep-0318.txt -# pep-0319.txt -pep-0320.txt @warsaw @rhettinger -# pep-0321.txt -pep-0322.txt @rhettinger -pep-0323.txt @aleaxit -# pep-0324.txt -# pep-0325.txt -pep-0326.txt @terryjreedy -pep-0327.txt @facundobatista -# pep-0328.txt -pep-0329.txt @rhettinger -# pep-0330.txt -# pep-0331.txt -# pep-0332.txt -# pep-0333.txt -# pep-0334.txt -# pep-0335.txt -# pep-0336.txt -# pep-0337.txt -pep-0338.txt @ncoghlan -pep-0339.txt @brettcannon -pep-0340.txt @gvanrossum -pep-0341.txt @birkenfeld -pep-0342.txt @gvanrossum -pep-0343.txt @gvanrossum @ncoghlan -# pep-0344.txt -# pep-0345.txt -pep-0346.txt @ncoghlan -# pep-0347.txt -pep-0348.txt @brettcannon -pep-0349.txt @nascheme -# pep-0350.txt -pep-0351.txt @warsaw -pep-0352.txt @brettcannon @gvanrossum -# pep-0353.txt -# pep-0354.txt -# pep-0355.txt -pep-0356.txt @gvanrossum -# pep-0357.txt -pep-0358.txt @nascheme @gvanrossum -# pep-0359.txt -pep-0360.txt @brettcannon -pep-0361.txt @warsaw -pep-0362.txt @brettcannon @1st1 @larryhastings -# pep-0363.txt -pep-0364.txt @warsaw -# pep-0365.txt -pep-0366.txt @ncoghlan -# pep-0367.txt -# pep-0368.txt -pep-0369.txt @tiran -pep-0370.txt @tiran -# pep-0371.txt -pep-0372.txt @mitsuhiko @rhettinger -pep-0373.txt @benjaminp -pep-0374.txt @brettcannon @avassalotti @warsaw -pep-0375.txt @benjaminp -# pep-0376.txt -pep-0377.txt @ncoghlan -pep-0378.txt @rhettinger -# pep-0379.txt -# pep-0380.txt -# pep-0381.txt -# pep-0382.txt -# pep-0383.txt -# pep-0384.txt -pep-0385.txt @pitrou @birkenfeld -# pep-0386.txt -pep-0387.txt @benjaminp @vstinner -# pep-0389.txt -# pep-0390.txt -pep-0391.txt @vsajip -pep-0392.txt @birkenfeld -# pep-0393.txt -pep-0394.txt @ncoghlan @warsaw @encukou @willingc -pep-0395.txt @ncoghlan -pep-0396.txt @warsaw -pep-0397.txt @mhammond -pep-0398.txt @birkenfeld -pep-0399.txt @brettcannon -pep-0400.txt @vstinner -pep-0401.txt @warsaw @brettcannon -# pep-0402.txt -pep-0403.txt @ncoghlan -pep-0404.txt @warsaw -# pep-0405.txt -pep-0406.txt @ncoghlan -pep-0407.txt @pitrou @birkenfeld @warsaw -pep-0408.txt @ncoghlan @eliben -pep-0409.txt @ethanfurman -pep-0410.txt @vstinner -pep-0411.txt @ncoghlan @eliben -pep-0412.txt @markshannon -pep-0413.txt @ncoghlan -pep-0414.txt @mitsuhiko @ncoghlan -pep-0415.txt @benjaminp -pep-0416.txt @vstinner -pep-0417.txt @voidspace -pep-0418.txt @vstinner -pep-0418/ @vstinner -# pep-0419.txt -pep-0420.txt @ericvsmith -pep-0421.txt @ericsnowcurrently -pep-0422.txt @ncoghlan -# pep-0423.txt -pep-0424.txt @alex -# pep-0425.txt -pep-0426.txt @ncoghlan @dstufft -pep-0426/ @ncoghlan @dstufft -# pep-0427.txt -pep-0428.txt @pitrou -pep-0429.txt @larryhastings -pep-0430.txt @ncoghlan -# pep-0431.txt -pep-0432.txt @ncoghlan @vstinner @ericsnowcurrently -pep-0433.txt @vstinner -pep-0433/ @vstinner -pep-0434.txt @terryjreedy -pep-0435.txt @warsaw @eliben @ethanfurman -pep-0436.txt @larryhastings -# pep-0437.txt -# pep-0438.txt -# pep-0439.txt -pep-0440.txt @ncoghlan @dstufft -pep-0441.txt @pfmoore -pep-0442.txt @pitrou -pep-0443.txt @ambv -pep-0444.txt @mitsuhiko -pep-0445.txt @vstinner -pep-0446.txt @vstinner -pep-0446/ @vstinner -pep-0447.txt @ronaldoussoren -# pep-0448.txt -pep-0449.txt @dstufft -# pep-0450.txt @stevendaprano -pep-0451.txt @ericsnowcurrently -pep-0452.txt @tiran -pep-0453.txt @dstufft @ncoghlan -pep-0454.txt @vstinner -pep-0455.txt @pitrou -pep-0456.txt @tiran -pep-0457.txt @larryhastings -# pep-0458.txt, pep-0458-1.png -pep-0459.txt @ncoghlan -pep-0460.txt @pitrou -pep-0461.txt @ethanfurman -pep-0462.txt @ncoghlan -# pep-0463.txt -pep-0464.txt @dstufft -pep-0465.txt @njsmith -pep-0465/ @njsmith -pep-0466.txt @ncoghlan -pep-0467.txt @ncoghlan @ethanfurman -pep-0468.txt @ericsnowcurrently -pep-0469.txt @ncoghlan -pep-0470.txt @dstufft -# pep-0471.txt -# pep-0472.txt -# pep-0473.txt -pep-0474.txt @ncoghlan -pep-0475.txt @vstinner -pep-0476.txt @alex -pep-0477.txt @dstufft @ncoghlan -pep-0478.txt @larryhastings -pep-0479.txt @gvanrossum -# pep-0480.txt, pep-0480-1.png -pep-0481.txt @dstufft -pep-0482.txt @ambv -pep-0483.txt @gvanrossum @ilevkivskyi -pep-0484.txt @gvanrossum @ambv -# pep-0485.txt -pep-0486.txt @pfmoore -# pep-0487.txt -pep-0488.txt @brettcannon -pep-0489.txt @encukou @scoder @ncoghlan -pep-0490.txt @vstinner -# pep-0491.txt -pep-0492.txt @1st1 -pep-0493.txt @ncoghlan @malemburg -pep-0494.txt @ned-deily -pep-0495.txt @abalkin @tim-one -pep-0495-gap.png @abalkin @tim-one -pep-0495-gap.svg @abalkin @tim-one -pep-0495-fold.svg @abalkin @tim-one -pep-0495-fold-2.png @abalkin @tim-one -pep-0495-daylightsavings.png @abalkin @tim-one -# pep-0496.txt -# pep-0497.txt -pep-0498.txt @ericvsmith -# pep-0499.txt -pep-0500.txt @abalkin @tim-one -pep-0501.txt @ncoghlan -# pep-0502.txt -pep-0503.txt @dstufft -pep-0504.txt @ncoghlan -pep-0505.rst @zooba -pep-0505/ @zooba -# pep-0506.txt @stevendaprano -pep-0507.txt @warsaw -pep-0508.txt @rbtcollins -pep-0509.txt @vstinner -pep-0510.txt @vstinner -pep-0511.txt @vstinner -pep-0512.txt @brettcannon -pep-0513.txt @njsmith -pep-0514.txt @zooba -pep-0515.txt @birkenfeld @serhiy-storchaka -pep-0516.txt @rbtcollins @njsmith -pep-0517.txt @njsmith -pep-0518.txt @brettcannon @njsmith @dstufft -pep-0519.txt @brettcannon -pep-0520.txt @ericsnowcurrently -pep-0521.txt @njsmith -pep-0522.txt @ncoghlan @njsmith -pep-0523.txt @brettcannon @DinoV -pep-0524.txt @vstinner -pep-0525.txt @1st1 -pep-0525-1.png @1st1 -pep-0526.txt @ilevkivskyi @lisroach @gvanrossum -pep-0527.txt @dstufft -pep-0528.txt @zooba -pep-0529.txt @zooba -pep-0530.txt @1st1 -pep-0531.txt @ncoghlan -pep-0532.txt @ncoghlan -pep-0532/ @ncoghlan -pep-0533.txt @njsmith -pep-0534.txt @encukou @ncoghlan -pep-0535.txt @ncoghlan -# pep-0536.txt -pep-0537.txt @ned-deily -pep-0538.txt @ncoghlan -# pep-0539.txt -pep-0540.txt @vstinner -pep-0541.txt @ambv -# pep-0542.txt -pep-0543.rst @tiran -pep-0544.txt @ilevkivskyi @ambv -pep-0545.txt @JulienPalard @methane @vstinner -pep-0546.txt @vstinner -pep-0547.rst @encukou -pep-0548.rst @bitdancer -pep-0549.rst @larryhastings -pep-0550.rst @1st1 -pep-0550-lookup_hamt.png @1st1 -pep-0550-hamt_vs_dict.png @1st1 -pep-0550-hamt_vs_dict-v2.png @1st1 -pep-0551.rst @zooba -pep-0552.rst @benjaminp -pep-0553.rst @warsaw -pep-0554.rst @ericsnowcurrently -# pep-0555.rst -pep-0556.rst @pitrou -pep-0557.rst @ericvsmith -pep-0558.rst @ncoghlan -pep-0559.rst @warsaw -pep-0560.rst @ilevkivskyi -# pep-0561.rst -pep-0562.rst @ilevkivskyi -pep-0563.rst @ambv -pep-0564.rst @vstinner -pep-0565.rst @ncoghlan -# pep-0566.rst -pep-0567.rst @1st1 -pep-0568.rst @njsmith -pep-0569.rst @ambv -pep-0570.rst @larryhastings @pablogsal -# pep-0571.rst -pep-0572.rst @tim-one @gvanrossum -pep-0573.rst @encukou @ncoghlan @ericsnowcurrently -pep-0574.rst @pitrou -# pep-0575.rst -pep-0576.rst @markshannon -pep-0577.rst @ncoghlan -pep-0578.rst @zooba -# pep-0579.rst -# pep-0580.rst -pep-0581.rst @Mariatta -pep-0582.rst @kushaldas @zooba @dstufft @ncoghlan -# pep-0583.rst -pep-0584.rst @brandtbucher # @stevendaprano -pep-0585.rst @ambv -pep-0586.rst @ilevkivskyi -pep-0587.rst @vstinner @ncoghlan -pep-0588.rst @Mariatta -pep-0589.rst @gvanrossum -pep-0590.rst @markshannon -pep-0591.rst @ilevkivskyi -pep-0592.rst @dstufft -pep-0593.rst @ilevkivskyi -pep-0594.rst @tiran @brettcannon -pep-0595.rst @ezio-melotti @berkerpeksag -pep-0596.rst @ambv -pep-0597.rst @methane -pep-0598.rst @ncoghlan -pep-0599.rst @pfmoore -pep-0600.rst @njsmith -pep-0601.txt @isidentical -pep-0602.rst @ambv -pep-0602-example-release-calendar.png @ambv -pep-0602-example-release-calendar.pptx @ambv -pep-0602-overlapping-support-matrix.png @ambv -pep-0602-overlapping-support-matrix.pptx @ambv -pep-0603.rst @1st1 -pep-0603-lookup_hamt.png @1st1 -pep-0603-hamt_vs_dict.png @1st1 -# pep-0604.rst -pep-0605.rst @zooba @ncoghlan -pep-0605-example-release-calendar.png @zooba @ncoghlan -pep-0605-overlapping-support-matrix.png @zooba @ncoghlan -pep-0605/ @zooba @ncoghlan -pep-0606.rst @vstinner -pep-0607.rst @ambv @zooba @ncoghlan -pep-0608.rst @vstinner -pep-0609.rst @pganssle -pep-0610.rst @cjerdonek -pep-0611.rst @markshannon -pep-0612.rst @gvanrossum -pep-0613.rst @gvanrossum -pep-0614.rst @brandtbucher -pep-0615.rst @pganssle -pep-0616.rst @ericvsmith -pep-0617.rst @gvanrossum @pablogsal @lysnikolaou -pep-0618.rst @brandtbucher -pep-0619.rst @pablogsal -pep-0620.rst @vstinner -pep-0621.rst @brettcannon @pganssle -pep-0622.rst @brandtbucher @ilevkivskyi @gvanrossum -pep-0623.rst @methane -pep-0624.rst @methane -pep-0625.rst @pfmoore -pep-0626.rst @markshannon -pep-0627.rst @encukou -pep-0628.txt @ncoghlan -pep-0629.rst @dstufft -pep-0630.rst @encukou -pep-0631.rst @pganssle -pep-0632.rst @zooba -pep-0633.rst @brettcannon -pep-0634.rst @brandtbucher @gvanrossum -pep-0635.rst @brandtbucher @gvanrossum -pep-0636.rst @brandtbucher @gvanrossum -# pep-0637.rst @stevendaprano -pep-0638.rst @markshannon -pep-0639.rst @CAM-Gerlach -pep-0640.rst @Yhg1s -pep-0641.rst @zooba @warsaw @brettcannon -pep-0642.rst @ncoghlan -pep-0643.rst @pfmoore -pep-0644.rst @tiran -pep-0645.rst @gvanrossum -pep-0646.rst @gvanrossum -pep-0647.rst @gvanrossum -pep-0648.rst @pablogsal -pep-0649.rst @larryhastings -pep-0650.rst @brettcannon -pep-0651.rst @markshannon -pep-0652.rst @encukou -pep-0653.rst @markshannon -pep-0654.rst @1st1 @gvanrossum @iritkatriel -pep-0655.rst @gvanrossum -pep-0656.rst @brettcannon -pep-0657.rst @pablogsal @isidentical @ammaraskar -pep-0658.rst @brettcannon -pep-0659.rst @markshannon -pep-0660.rst @pfmoore -pep-0661.rst @taleinat -pep-0662.rst @brettcannon -pep-0662/ @brettcannon -pep-0663.txt @ethanfurman -pep-0664.rst @pablogsal -pep-0665.rst @brettcannon -# pep-0666.txt -pep-0667.rst @markshannon -pep-0668.rst @dstufft -pep-0669.rst @markshannon -pep-0670.rst @vstinner @erlend-aasland -pep-0671.rst @rosuav -pep-0672.rst @encukou -pep-0673.rst @jellezijlstra -pep-0674.rst @vstinner -pep-0675.rst @jellezijlstra -pep-0676.rst @AA-Turner @Mariatta -pep-0677.rst @gvanrossum -pep-0678.rst @iritkatriel -pep-0679.rst @pablogsal -pep-0680.rst @encukou -pep-0681.rst @jellezijlstra -pep-0682.rst @mdickinson -pep-0683.rst @ericsnowcurrently -pep-0684.rst @ericsnowcurrently -# pep-0684.rst -pep-0685.rst @brettcannon -pep-0686.rst @methane -pep-0687.rst @encukou @erlend-aasland -pep-0688.rst @jellezijlstra -pep-0689.rst @encukou -pep-0690.rst @warsaw -pep-0691.rst @dstufft -pep-0692.rst @jellezijlstra -pep-0693.rst @Yhg1s -pep-0694.rst @dstufft -pep-0695.rst @gvanrossum -pep-0696.rst @jellezijlstra -pep-0697.rst @encukou -pep-0698.rst @jellezijlstra -pep-0699.rst @Fidget-Spinner -pep-0700.rst @pfmoore -pep-0701.rst @pablogsal @isidentical @lysnikolaou -pep-0702.rst @jellezijlstra -pep-0703.rst @ambv -pep-0704.rst @brettcannon @pradyunsg -pep-0705.rst @pablogsal -pep-0706.rst @encukou -pep-0707.rst @iritkatriel -pep-0708.rst @dstufft -pep-0709.rst @carljm -pep-0710.rst @dstufft -pep-0711.rst @njsmith -pep-0712.rst @ericvsmith -pep-0713.rst @ambv -pep-0714.rst @dstufft -pep-0715.rst @dstufft -pep-0718.rst @gvanrossum -pep-0719.rst @Yhg1s -pep-0720.rst @FFY00 -pep-0721.rst @encukou -pep-0722.rst @pfmoore -pep-0723.rst @AA-Turner +peps/pep-0200.rst @jeremyhylton +peps/pep-0201.rst @warsaw +peps/pep-0202.rst @warsaw +peps/pep-0203.rst @Yhg1s +peps/pep-0204.rst @Yhg1s +peps/pep-0205.rst @freddrake +# peps/pep-0206.rst +peps/pep-0207.rst @gvanrossum +peps/pep-0208.rst @nascheme @malemburg +# peps/pep-0209.rst +# peps/pep-0210.rst +# peps/pep-0211.rst +# peps/pep-0212.rst +# peps/pep-0213.rst +peps/pep-0214.rst @warsaw +# peps/pep-0215.rst +# peps/pep-0216.rst +# peps/pep-0217.rst +peps/pep-0218.rst @rhettinger +# peps/pep-0219.rst +# peps/pep-0220.rst +peps/pep-0221.rst @Yhg1s +# peps/pep-0222.rst +peps/pep-0223.rst @tim-one +peps/pep-0224.rst @malemburg +# peps/pep-0225.rst +peps/pep-0226.rst @jeremyhylton +peps/pep-0227.rst @jeremyhylton +peps/pep-0228.rst @gvanrossum +# peps/pep-0229.rst +peps/pep-0230.rst @gvanrossum +peps/pep-0231.rst @warsaw +peps/pep-0232.rst @warsaw +# peps/pep-0233.rst +peps/pep-0234.rst @gvanrossum +peps/pep-0235.rst @tim-one +peps/pep-0236.rst @tim-one +peps/pep-0237.rst @gvanrossum +peps/pep-0238.rst @gvanrossum +# peps/pep-0239.rst +# peps/pep-0240.rst +# peps/pep-0241.rst +# peps/pep-0242.rst +# peps/pep-0243.rst +# peps/pep-0244.rst +# peps/pep-0245.rst +peps/pep-0246.rst @aleaxit +# peps/pep-0247.rst +peps/pep-0248.rst @malemburg +peps/pep-0249.rst @malemburg +peps/pep-0250.rst @pfmoore +peps/pep-0251.rst @warsaw @gvanrossum +peps/pep-0252.rst @gvanrossum +peps/pep-0253.rst @gvanrossum +peps/pep-0254.rst @gvanrossum +peps/pep-0255.rst @nascheme @tim-one +# peps/pep-0256.rst +peps/pep-0257.rst @gvanrossum +# peps/pep-0258.rst +peps/pep-0259.rst @gvanrossum +peps/pep-0260.rst @gvanrossum +# peps/pep-0261.rst +# peps/pep-0262.rst +peps/pep-0263.rst @malemburg +# peps/pep-0264.rst +# peps/pep-0265.rst +# peps/pep-0266.rst +peps/pep-0267.rst @jeremyhylton +# peps/pep-0268.rst +# peps/pep-0269.rst +# peps/pep-0270.rst +# peps/pep-0271.rst +# peps/pep-0272.rst +# peps/pep-0273.rst +peps/pep-0274.rst @warsaw +peps/pep-0275.rst @malemburg +# peps/pep-0276.rst +# peps/pep-0277.rst +peps/pep-0278.rst @jackjansen +peps/pep-0279.rst @rhettinger +peps/pep-0280.rst @gvanrossum +# peps/pep-0281.rst +peps/pep-0282.rst @vsajip +peps/pep-0283.rst @gvanrossum +# peps/pep-0284.rst +peps/pep-0285.rst @gvanrossum +# peps/pep-0286.rst +# peps/pep-0287.rst +peps/pep-0288.rst @rhettinger +peps/pep-0289.rst @rhettinger +peps/pep-0290.rst @rhettinger +# peps/pep-0291.rst +peps/pep-0292.rst @warsaw +peps/pep-0293.rst @doerwalter +# peps/pep-0294.rst +# peps/pep-0295.rst +# peps/pep-0296.rst +peps/pep-0297.rst @malemburg +peps/pep-0298.rst @theller +# peps/pep-0299.rst +# peps/pep-0301.rst +peps/pep-0302.rst @pfmoore +# peps/pep-0303.rst +# peps/pep-0304.rst +# peps/pep-0305.rst +peps/pep-0306.rst @jackdied @ncoghlan @benjaminp +peps/pep-0307.rst @gvanrossum @tim-one +peps/pep-0308.rst @gvanrossum @rhettinger +# peps/pep-0309.rst +peps/pep-0310.rst @pfmoore +peps/pep-0311.rst @mhammond +peps/pep-0312.rst @aleaxit +# peps/pep-0313.rst +# peps/pep-0314.rst +peps/pep-0315.rst @rhettinger +# peps/pep-0316.rst +# peps/pep-0317.rst +# peps/pep-0318.rst +# peps/pep-0319.rst +peps/pep-0320.rst @warsaw @rhettinger +# peps/pep-0321.rst +peps/pep-0322.rst @rhettinger +peps/pep-0323.rst @aleaxit +# peps/pep-0324.rst +# peps/pep-0325.rst +peps/pep-0326.rst @terryjreedy +peps/pep-0327.rst @facundobatista +# peps/pep-0328.rst +peps/pep-0329.rst @rhettinger +# peps/pep-0330.rst +# peps/pep-0331.rst +# peps/pep-0332.rst +# peps/pep-0333.rst +# peps/pep-0334.rst +# peps/pep-0335.rst +# peps/pep-0336.rst +# peps/pep-0337.rst +peps/pep-0338.rst @ncoghlan +peps/pep-0339.rst @brettcannon +peps/pep-0340.rst @gvanrossum +peps/pep-0341.rst @birkenfeld +peps/pep-0342.rst @gvanrossum +peps/pep-0343.rst @gvanrossum @ncoghlan +# peps/pep-0344.rst +# peps/pep-0345.rst +peps/pep-0346.rst @ncoghlan +# peps/pep-0347.rst +peps/pep-0348.rst @brettcannon +peps/pep-0349.rst @nascheme +# peps/pep-0350.rst +peps/pep-0351.rst @warsaw +peps/pep-0352.rst @brettcannon @gvanrossum +# peps/pep-0353.rst +# peps/pep-0354.rst +# peps/pep-0355.rst +peps/pep-0356.rst @gvanrossum +# peps/pep-0357.rst +peps/pep-0358.rst @nascheme @gvanrossum +# peps/pep-0359.rst +peps/pep-0360.rst @brettcannon +peps/pep-0361.rst @warsaw +peps/pep-0362.rst @brettcannon @1st1 @larryhastings +# peps/pep-0363.rst +peps/pep-0364.rst @warsaw +# peps/pep-0365.rst +peps/pep-0366.rst @ncoghlan +# peps/pep-0367.rst +# peps/pep-0368.rst +peps/pep-0369.rst @tiran +peps/pep-0370.rst @tiran +# peps/pep-0371.rst +peps/pep-0372.rst @mitsuhiko @rhettinger +peps/pep-0373.rst @benjaminp +peps/pep-0374.rst @brettcannon @avassalotti @warsaw +peps/pep-0375.rst @benjaminp +# peps/pep-0376.rst +peps/pep-0377.rst @ncoghlan +peps/pep-0378.rst @rhettinger +# peps/pep-0379.rst +# peps/pep-0380.rst +# peps/pep-0381.rst +# peps/pep-0382.rst +# peps/pep-0383.rst +# peps/pep-0384.rst +peps/pep-0385.rst @pitrou @birkenfeld +# peps/pep-0386.rst +peps/pep-0387.rst @benjaminp @vstinner +# peps/pep-0389.rst +# peps/pep-0390.rst +peps/pep-0391.rst @vsajip +peps/pep-0392.rst @birkenfeld +# peps/pep-0393.rst +peps/pep-0394.rst @ncoghlan @warsaw @encukou @willingc +peps/pep-0395.rst @ncoghlan +peps/pep-0396.rst @warsaw +peps/pep-0397.rst @mhammond +peps/pep-0398.rst @birkenfeld +peps/pep-0399.rst @brettcannon +peps/pep-0400.rst @vstinner +peps/pep-0401.rst @warsaw @brettcannon +# peps/pep-0402.rst +peps/pep-0403.rst @ncoghlan +peps/pep-0404.rst @warsaw +# peps/pep-0405.rst +peps/pep-0406.rst @ncoghlan +peps/pep-0407.rst @pitrou @birkenfeld @warsaw +peps/pep-0408.rst @ncoghlan @eliben +peps/pep-0409.rst @ethanfurman +peps/pep-0410.rst @vstinner +peps/pep-0411.rst @ncoghlan @eliben +peps/pep-0412.rst @markshannon +peps/pep-0413.rst @ncoghlan +peps/pep-0414.rst @mitsuhiko @ncoghlan +peps/pep-0415.rst @benjaminp +peps/pep-0416.rst @vstinner +peps/pep-0417.rst @voidspace +peps/pep-0418.rst @vstinner +peps/pep-0418/ @vstinner +# peps/pep-0419.rst +peps/pep-0420.rst @ericvsmith +peps/pep-0421.rst @ericsnowcurrently +peps/pep-0422.rst @ncoghlan +# peps/pep-0423.rst +peps/pep-0424.rst @alex +# peps/pep-0425.rst +peps/pep-0426.rst @ncoghlan @dstufft +peps/pep-0426/ @ncoghlan @dstufft +# peps/pep-0427.rst +peps/pep-0428.rst @pitrou +peps/pep-0429.rst @larryhastings +peps/pep-0430.rst @ncoghlan +# peps/pep-0431.rst +peps/pep-0432.rst @ncoghlan @vstinner @ericsnowcurrently +peps/pep-0433.rst @vstinner +peps/pep-0433/ @vstinner +peps/pep-0434.rst @terryjreedy +peps/pep-0435.rst @warsaw @eliben @ethanfurman +peps/pep-0436.rst @larryhastings +# peps/pep-0437.rst +# peps/pep-0438.rst +# peps/pep-0439.rst +peps/pep-0440.rst @ncoghlan @dstufft +peps/pep-0441.rst @pfmoore +peps/pep-0442.rst @pitrou +peps/pep-0443.rst @ambv +peps/pep-0444.rst @mitsuhiko +peps/pep-0445.rst @vstinner +peps/pep-0446.rst @vstinner +peps/pep-0446/ @vstinner +peps/pep-0447.rst @ronaldoussoren +# peps/pep-0448.rst +peps/pep-0449.rst @dstufft +# peps/pep-0450.rst @stevendaprano +peps/pep-0451.rst @ericsnowcurrently +peps/pep-0452.rst @tiran +peps/pep-0453.rst @dstufft @ncoghlan +peps/pep-0454.rst @vstinner +peps/pep-0455.rst @pitrou +peps/pep-0456.rst @tiran +peps/pep-0457.rst @larryhastings +# peps/pep-0458.rst, peps/pep-0458-1.png +peps/pep-0459.rst @ncoghlan +peps/pep-0460.rst @pitrou +peps/pep-0461.rst @ethanfurman +peps/pep-0462.rst @ncoghlan +# peps/pep-0463.rst +peps/pep-0464.rst @dstufft +peps/pep-0465.rst @njsmith +peps/pep-0465/ @njsmith +peps/pep-0466.rst @ncoghlan +peps/pep-0467.rst @ncoghlan @ethanfurman +peps/pep-0468.rst @ericsnowcurrently +peps/pep-0469.rst @ncoghlan +peps/pep-0470.rst @dstufft +# peps/pep-0471.rst +# peps/pep-0472.rst +# peps/pep-0473.rst +peps/pep-0474.rst @ncoghlan +peps/pep-0475.rst @vstinner +peps/pep-0476.rst @alex +peps/pep-0477.rst @dstufft @ncoghlan +peps/pep-0478.rst @larryhastings +peps/pep-0479.rst @gvanrossum +# peps/pep-0480.rst, peps/pep-0480-1.png +peps/pep-0481.rst @dstufft +peps/pep-0482.rst @ambv +peps/pep-0483.rst @gvanrossum @ilevkivskyi +peps/pep-0484.rst @gvanrossum @ambv +# peps/pep-0485.rst +peps/pep-0486.rst @pfmoore +# peps/pep-0487.rst +peps/pep-0488.rst @brettcannon +peps/pep-0489.rst @encukou @scoder @ncoghlan +peps/pep-0490.rst @vstinner +# peps/pep-0491.rst +peps/pep-0492.rst @1st1 +peps/pep-0493.rst @ncoghlan @malemburg +peps/pep-0494.rst @ned-deily +peps/pep-0495.rst @abalkin @tim-one +peps/pep-0495-gap.png @abalkin @tim-one +peps/pep-0495-gap.svg @abalkin @tim-one +peps/pep-0495-fold.svg @abalkin @tim-one +peps/pep-0495-fold-2.png @abalkin @tim-one +peps/pep-0495-daylightsavings.png @abalkin @tim-one +# peps/pep-0496.rst +# peps/pep-0497.rst +peps/pep-0498.rst @ericvsmith +# peps/pep-0499.rst +peps/pep-0500.rst @abalkin @tim-one +peps/pep-0501.rst @ncoghlan +# peps/pep-0502.rst +peps/pep-0503.rst @dstufft +peps/pep-0504.rst @ncoghlan +peps/pep-0505.rst @zooba +peps/pep-0505/ @zooba +# peps/pep-0506.rst @stevendaprano +peps/pep-0507.rst @warsaw +peps/pep-0508.rst @rbtcollins +peps/pep-0509.rst @vstinner +peps/pep-0510.rst @vstinner +peps/pep-0511.rst @vstinner +peps/pep-0512.rst @brettcannon +peps/pep-0513.rst @njsmith +peps/pep-0514.rst @zooba +peps/pep-0515.rst @birkenfeld @serhiy-storchaka +peps/pep-0516.rst @rbtcollins @njsmith +peps/pep-0517.rst @njsmith +peps/pep-0518.rst @brettcannon @njsmith @dstufft +peps/pep-0519.rst @brettcannon +peps/pep-0520.rst @ericsnowcurrently +peps/pep-0521.rst @njsmith +peps/pep-0522.rst @ncoghlan @njsmith +peps/pep-0523.rst @brettcannon @DinoV +peps/pep-0524.rst @vstinner +peps/pep-0525.rst @1st1 +peps/pep-0525-1.png @1st1 +peps/pep-0526.rst @ilevkivskyi @lisroach @gvanrossum +peps/pep-0527.rst @dstufft +peps/pep-0528.rst @zooba +peps/pep-0529.rst @zooba +peps/pep-0530.rst @1st1 +peps/pep-0531.rst @ncoghlan +peps/pep-0532.rst @ncoghlan +peps/pep-0532/ @ncoghlan +peps/pep-0533.rst @njsmith +peps/pep-0534.rst @encukou @ncoghlan +peps/pep-0535.rst @ncoghlan +# peps/pep-0536.rst +peps/pep-0537.rst @ned-deily +peps/pep-0538.rst @ncoghlan +# peps/pep-0539.rst +peps/pep-0540.rst @vstinner +peps/pep-0541.rst @ambv +# peps/pep-0542.rst +peps/pep-0543.rst @tiran +peps/pep-0544.rst @ilevkivskyi @ambv +peps/pep-0545.rst @JulienPalard @methane @vstinner +peps/pep-0546.rst @vstinner +peps/pep-0547.rst @encukou +peps/pep-0548.rst @bitdancer +peps/pep-0549.rst @larryhastings +peps/pep-0550.rst @1st1 +peps/pep-0550-lookup_hamt.png @1st1 +peps/pep-0550-hamt_vs_dict.png @1st1 +peps/pep-0550-hamt_vs_dict-v2.png @1st1 +peps/pep-0551.rst @zooba +peps/pep-0552.rst @benjaminp +peps/pep-0553.rst @warsaw +peps/pep-0554.rst @ericsnowcurrently +# peps/pep-0555.rst +peps/pep-0556.rst @pitrou +peps/pep-0557.rst @ericvsmith +peps/pep-0558.rst @ncoghlan +peps/pep-0559.rst @warsaw +peps/pep-0560.rst @ilevkivskyi +# peps/pep-0561.rst +peps/pep-0562.rst @ilevkivskyi +peps/pep-0563.rst @ambv +peps/pep-0564.rst @vstinner +peps/pep-0565.rst @ncoghlan +# peps/pep-0566.rst +peps/pep-0567.rst @1st1 +peps/pep-0568.rst @njsmith +peps/pep-0569.rst @ambv +peps/pep-0570.rst @larryhastings @pablogsal +# peps/pep-0571.rst +peps/pep-0572.rst @tim-one @gvanrossum +peps/pep-0573.rst @encukou @ncoghlan @ericsnowcurrently +peps/pep-0574.rst @pitrou +# peps/pep-0575.rst +peps/pep-0576.rst @markshannon +peps/pep-0577.rst @ncoghlan +peps/pep-0578.rst @zooba +# peps/pep-0579.rst +# peps/pep-0580.rst +peps/pep-0581.rst @Mariatta +peps/pep-0582.rst @kushaldas @zooba @dstufft @ncoghlan +# peps/pep-0583.rst +peps/pep-0584.rst @brandtbucher # @stevendaprano +peps/pep-0585.rst @ambv +peps/pep-0586.rst @ilevkivskyi +peps/pep-0587.rst @vstinner @ncoghlan +peps/pep-0588.rst @Mariatta +peps/pep-0589.rst @gvanrossum +peps/pep-0590.rst @markshannon +peps/pep-0591.rst @ilevkivskyi +peps/pep-0592.rst @dstufft +peps/pep-0593.rst @ilevkivskyi +peps/pep-0594.rst @tiran @brettcannon +peps/pep-0595.rst @ezio-melotti @berkerpeksag +peps/pep-0596.rst @ambv +peps/pep-0597.rst @methane +peps/pep-0598.rst @ncoghlan +peps/pep-0599.rst @pfmoore +peps/pep-0600.rst @njsmith +peps/pep-0601.rst @isidentical +peps/pep-0602.rst @ambv +peps/pep-0602-example-release-calendar.png @ambv +peps/pep-0602-example-release-calendar.pptx @ambv +peps/pep-0602-overlapping-support-matrix.png @ambv +peps/pep-0602-overlapping-support-matrix.pptx @ambv +peps/pep-0603.rst @1st1 +peps/pep-0603-lookup_hamt.png @1st1 +peps/pep-0603-hamt_vs_dict.png @1st1 +# peps/pep-0604.rst +peps/pep-0605.rst @zooba @ncoghlan +peps/pep-0605-example-release-calendar.png @zooba @ncoghlan +peps/pep-0605-overlapping-support-matrix.png @zooba @ncoghlan +peps/pep-0605/ @zooba @ncoghlan +peps/pep-0606.rst @vstinner +peps/pep-0607.rst @ambv @zooba @ncoghlan +peps/pep-0608.rst @vstinner +peps/pep-0609.rst @pganssle +peps/pep-0610.rst @cjerdonek +peps/pep-0611.rst @markshannon +peps/pep-0612.rst @gvanrossum +peps/pep-0613.rst @gvanrossum +peps/pep-0614.rst @brandtbucher +peps/pep-0615.rst @pganssle +peps/pep-0616.rst @ericvsmith +peps/pep-0617.rst @gvanrossum @pablogsal @lysnikolaou +peps/pep-0618.rst @brandtbucher +peps/pep-0619.rst @pablogsal +peps/pep-0620.rst @vstinner +peps/pep-0621.rst @brettcannon @pganssle +peps/pep-0622.rst @brandtbucher @ilevkivskyi @gvanrossum +peps/pep-0623.rst @methane +peps/pep-0624.rst @methane +peps/pep-0625.rst @pfmoore +peps/pep-0626.rst @markshannon +peps/pep-0627.rst @encukou +peps/pep-0628.rst @ncoghlan +peps/pep-0629.rst @dstufft +peps/pep-0630.rst @encukou +peps/pep-0631.rst @pganssle +peps/pep-0632.rst @zooba +peps/pep-0633.rst @brettcannon +peps/pep-0634.rst @brandtbucher @gvanrossum +peps/pep-0635.rst @brandtbucher @gvanrossum +peps/pep-0636.rst @brandtbucher @gvanrossum +# peps/pep-0637.rst @stevendaprano +peps/pep-0638.rst @markshannon +peps/pep-0639.rst @CAM-Gerlach +peps/pep-0640.rst @Yhg1s +peps/pep-0641.rst @zooba @warsaw @brettcannon +peps/pep-0642.rst @ncoghlan +peps/pep-0643.rst @pfmoore +peps/pep-0644.rst @tiran +peps/pep-0645.rst @gvanrossum +peps/pep-0646.rst @gvanrossum +peps/pep-0647.rst @gvanrossum +peps/pep-0648.rst @pablogsal +peps/pep-0649.rst @larryhastings +peps/pep-0650.rst @brettcannon +peps/pep-0651.rst @markshannon +peps/pep-0652.rst @encukou +peps/pep-0653.rst @markshannon +peps/pep-0654.rst @1st1 @gvanrossum @iritkatriel +peps/pep-0655.rst @gvanrossum +peps/pep-0656.rst @brettcannon +peps/pep-0657.rst @pablogsal @isidentical @ammaraskar +peps/pep-0658.rst @brettcannon +peps/pep-0659.rst @markshannon +peps/pep-0660.rst @pfmoore +peps/pep-0661.rst @taleinat +peps/pep-0662.rst @brettcannon +peps/pep-0662/ @brettcannon +peps/pep-0663.rst @ethanfurman +peps/pep-0664.rst @pablogsal +peps/pep-0665.rst @brettcannon +# peps/pep-0666.rst +peps/pep-0667.rst @markshannon +peps/pep-0668.rst @dstufft +peps/pep-0669.rst @markshannon +peps/pep-0670.rst @vstinner @erlend-aasland +peps/pep-0671.rst @rosuav +peps/pep-0672.rst @encukou +peps/pep-0673.rst @jellezijlstra +peps/pep-0674.rst @vstinner +peps/pep-0675.rst @jellezijlstra +peps/pep-0676.rst @AA-Turner @Mariatta +peps/pep-0677.rst @gvanrossum +peps/pep-0678.rst @iritkatriel +peps/pep-0679.rst @pablogsal +peps/pep-0680.rst @encukou +peps/pep-0681.rst @jellezijlstra +peps/pep-0682.rst @mdickinson +peps/pep-0683.rst @ericsnowcurrently +peps/pep-0684.rst @ericsnowcurrently +# peps/pep-0684.rst +peps/pep-0685.rst @brettcannon +peps/pep-0686.rst @methane +peps/pep-0687.rst @encukou @erlend-aasland +peps/pep-0688.rst @jellezijlstra +peps/pep-0689.rst @encukou +peps/pep-0690.rst @warsaw +peps/pep-0691.rst @dstufft +peps/pep-0692.rst @jellezijlstra +peps/pep-0693.rst @Yhg1s +peps/pep-0694.rst @dstufft +peps/pep-0695.rst @gvanrossum +peps/pep-0696.rst @jellezijlstra +peps/pep-0697.rst @encukou +peps/pep-0698.rst @jellezijlstra +peps/pep-0699.rst @Fidget-Spinner +peps/pep-0700.rst @pfmoore +peps/pep-0701.rst @pablogsal @isidentical @lysnikolaou +peps/pep-0702.rst @jellezijlstra +peps/pep-0703.rst @ambv +peps/pep-0704.rst @brettcannon @pradyunsg +peps/pep-0705.rst @pablogsal +peps/pep-0706.rst @encukou +peps/pep-0707.rst @iritkatriel +peps/pep-0708.rst @dstufft +peps/pep-0709.rst @carljm +peps/pep-0710.rst @dstufft +peps/pep-0711.rst @njsmith +peps/pep-0712.rst @ericvsmith +peps/pep-0713.rst @ambv +peps/pep-0714.rst @dstufft +peps/pep-0715.rst @dstufft +peps/pep-0718.rst @gvanrossum +peps/pep-0719.rst @Yhg1s +peps/pep-0720.rst @FFY00 +peps/pep-0721.rst @encukou +peps/pep-0722.rst @pfmoore +peps/pep-0723.rst @AA-Turner +peps/pep-0724.rst @jellezijlstra +peps/pep-0725.rst @pradyunsg +peps/pep-0726.rst @AA-Turner +peps/pep-0727.rst @JelleZijlstra # ... -# pep-0754.txt +# peps/pep-0754.rst # ... -pep-0801.rst @warsaw +peps/pep-0801.rst @warsaw # ... -pep-3000.txt @gvanrossum -pep-3001.txt @birkenfeld -# pep-3002.txt -pep-3003.txt @brettcannon @gvanrossum +peps/pep-3000.rst @gvanrossum +peps/pep-3001.rst @birkenfeld +# peps/pep-3002.rst +peps/pep-3003.rst @brettcannon @gvanrossum # ... -pep-3099.txt @birkenfeld -pep-3100.txt @brettcannon -# pep-3101.txt -# pep-3102.txt -pep-3103.txt @gvanrossum -# pep-3104.txt -pep-3105.txt @birkenfeld -pep-3106.txt @gvanrossum -# pep-3107.txt -pep-3108.txt @brettcannon -# pep-3109.txt -# pep-3110.txt -# pep-3111.txt -# pep-3112.txt -pep-3113.txt @brettcannon -# pep-3114.txt -# pep-3115.txt -pep-3116.txt @gvanrossum -pep-3117.txt @birkenfeld -# pep-3118.txt -pep-3119.txt @gvanrossum -# pep-3120.txt -# pep-3121.txt -pep-3122.txt @brettcannon -# pep-3123.txt -# pep-3124.txt -# pep-3125.txt -pep-3126.txt @rhettinger -# pep-3127.txt -# pep-3128.txt -# pep-3129.txt -# pep-3130.txt -# pep-3131.txt -pep-3132.txt @birkenfeld -# pep-3133.txt -# pep-3134.txt -# pep-3135.txt -# pep-3136.txt -pep-3137.txt @gvanrossum -# pep-3138.txt -pep-3139.txt @benjaminp -# pep-3140.txt -# pep-3141.txt -# pep-3142.txt -# pep-3143.txt -# pep-3144.txt -# pep-3145.txt -# pep-3146.txt -pep-3147.txt @warsaw -pep-3147-1.dia @warsaw -pep-3147-1.png @warsaw -pep-3148.txt @brianquinlan -pep-3149.txt @warsaw -pep-3150.txt @ncoghlan -pep-3151.txt @pitrou -# pep-3152.txt -# pep-3153.txt -pep-3154.txt @pitrou -pep-3155.txt @pitrou -pep-3156.txt @gvanrossum +peps/pep-3099.rst @birkenfeld +peps/pep-3100.rst @brettcannon +# peps/pep-3101.rst +# peps/pep-3102.rst +peps/pep-3103.rst @gvanrossum +# peps/pep-3104.rst +peps/pep-3105.rst @birkenfeld +peps/pep-3106.rst @gvanrossum +# peps/pep-3107.rst +peps/pep-3108.rst @brettcannon +# peps/pep-3109.rst +# peps/pep-3110.rst +# peps/pep-3111.rst +# peps/pep-3112.rst +peps/pep-3113.rst @brettcannon +# peps/pep-3114.rst +# peps/pep-3115.rst +peps/pep-3116.rst @gvanrossum +peps/pep-3117.rst @birkenfeld +# peps/pep-3118.rst +peps/pep-3119.rst @gvanrossum +# peps/pep-3120.rst +# peps/pep-3121.rst +peps/pep-3122.rst @brettcannon +# peps/pep-3123.rst +# peps/pep-3124.rst +# peps/pep-3125.rst +peps/pep-3126.rst @rhettinger +# peps/pep-3127.rst +# peps/pep-3128.rst +# peps/pep-3129.rst +# peps/pep-3130.rst +# peps/pep-3131.rst +peps/pep-3132.rst @birkenfeld +# peps/pep-3133.rst +# peps/pep-3134.rst +# peps/pep-3135.rst +# peps/pep-3136.rst +peps/pep-3137.rst @gvanrossum +# peps/pep-3138.rst +peps/pep-3139.rst @benjaminp +# peps/pep-3140.rst +# peps/pep-3141.rst +# peps/pep-3142.rst +# peps/pep-3143.rst +# peps/pep-3144.rst +# peps/pep-3145.rst +# peps/pep-3146.rst +peps/pep-3147.rst @warsaw +peps/pep-3147-1.dia @warsaw +peps/pep-3147-1.png @warsaw +peps/pep-3148.rst @brianquinlan +peps/pep-3149.rst @warsaw +peps/pep-3150.rst @ncoghlan +peps/pep-3151.rst @pitrou +# peps/pep-3152.rst +# peps/pep-3153.rst +peps/pep-3154.rst @pitrou +peps/pep-3155.rst @pitrou +peps/pep-3156.rst @gvanrossum # ... -# pep-3333.txt +# peps/pep-3333.rst # ... -pep-8000.rst @warsaw -pep-8001.rst @brettcannon @tiran @dstufft @ericsnowcurrently @gpshead @ambv @Mariatta @njsmith @pablogsal @rhettinger @taleinat @tim-one @zware -pep-8002.rst @warsaw @ambv @pitrou @dhellmann @willingc -pep-8010.rst @warsaw -pep-8011.rst @Mariatta @warsaw -pep-8012.rst @ambv -pep-8013.rst @zooba -pep-8014.rst @jackjansen -pep-8015.rst @vstinner -pep-8016.rst @njsmith @dstufft +peps/pep-8000.rst @warsaw +peps/pep-8001.rst @brettcannon @tiran @dstufft @ericsnowcurrently @gpshead @ambv @Mariatta @njsmith @pablogsal @rhettinger @taleinat @tim-one @zware +peps/pep-8002.rst @warsaw @ambv @pitrou @dhellmann @willingc +peps/pep-8010.rst @warsaw +peps/pep-8011.rst @Mariatta @warsaw +peps/pep-8012.rst @ambv +peps/pep-8013.rst @zooba +peps/pep-8014.rst @jackjansen +peps/pep-8015.rst @vstinner +peps/pep-8016.rst @njsmith @dstufft # ... -pep-8100.rst @njsmith -# pep-8101.rst -# pep-8102.rst +peps/pep-8100.rst @njsmith +# peps/pep-8101.rst +# peps/pep-8102.rst diff --git a/.github/PULL_REQUEST_TEMPLATE/Add a new PEP.md b/.github/PULL_REQUEST_TEMPLATE/Add a new PEP.md index bcc29bf74..48aaa3072 100644 --- a/.github/PULL_REQUEST_TEMPLATE/Add a new PEP.md +++ b/.github/PULL_REQUEST_TEMPLATE/Add a new PEP.md @@ -10,7 +10,7 @@ If your PEP is not Standards Track, remove the corresponding section. ## Basic requirements (all PEP Types) * [ ] Read and followed [PEP 1](https://peps.python.org/1) & [PEP 12](https://peps.python.org/12) -* [ ] File created from the [latest PEP template](https://github.com/python/peps/blob/main/pep-0012/pep-NNNN.rst?plain=1) +* [ ] File created from the [latest PEP template](https://github.com/python/peps/blob/main/peps/pep-0012/pep-NNNN.rst?plain=1) * [ ] PEP has next available number, & set in filename (``pep-NNNN.rst``), PR title (``PEP 123: ``) and ``PEP`` header * [ ] Title clearly, accurately and concisely describes the content in 79 characters or less * [ ] Core dev/PEP editor listed as ``Author`` or ``Sponsor``, and formally confirmed their approval diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f5afc0740..fc096a0cd 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,6 +14,7 @@ concurrency: env: FORCE_COLOR: 1 + RUFF_FORMAT: github jobs: pre-commit: @@ -21,7 +22,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3 uses: actions/setup-python@v4 with: @@ -35,3 +36,17 @@ jobs: uses: pre-commit/action@v3.0.0 with: extra_args: --all-files --hook-stage manual codespell || true + + check-peps: + name: Run check-peps + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3 + uses: actions/setup-python@v4 + with: + python-version: "3" + + - name: Run check-peps + run: python check-peps.py --detailed diff --git a/.github/workflows/render.yml b/.github/workflows/render.yml index 01afd33f3..98dcbf730 100644 --- a/.github/workflows/render.yml +++ b/.github/workflows/render.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 # fetch all history so that last modified date-times are accurate diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f80254024..e71847944 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: - "ubuntu-latest" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: diff --git a/.gitignore b/.gitignore index ae1196cb1..6beae9d8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,24 @@ -coverage.xml -pep-0000.txt +# PEPs pep-0000.rst -pep-????.html peps.rss +topic +/build + +# Bytecode __pycache__ -*.pyc -*.pyo +*.py[co] + +# Editors *~ -*env -.coverage -.tox +.idea .vscode *.swp -/build -/package -/topic + +# Tests +coverage.xml +.coverage +.tox + +# Virtual environments +*env /venv diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5012191ca..b1c1602fb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: name: "Check YAML" - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.7.0 hooks: - id: black name: "Format with Black" @@ -52,22 +52,23 @@ repos: - '--target-version=py310' files: 'pep_sphinx_extensions/tests/.*' - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.287 hooks: - - id: isort - name: "Sort imports with isort" - args: ['--profile=black', '--atomic'] - files: 'pep_sphinx_extensions/tests/.*' + - id: ruff + name: "Lint with Ruff" + args: + - '--exit-non-zero-on-fix' + files: '^pep_sphinx_extensions/tests/' - repo: https://github.com/tox-dev/tox-ini-fmt - rev: 0.6.1 + rev: 1.3.1 hooks: - id: tox-ini-fmt name: "Format tox.ini" - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v0.6.7 + rev: v0.6.8 hooks: - id: sphinx-lint name: "Sphinx lint" @@ -79,20 +80,16 @@ repos: hooks: - id: rst-backticks name: "Check RST: No single backticks" - files: '^pep-\d\.txt|\.rst$' - types: [text] + - id: rst-inline-touching-normal name: "Check RST: No backticks touching text" - files: '^pep-\d+\.txt|\.rst$' - types: [text] + - id: rst-directive-colons name: "Check RST: 2 colons after directives" - files: '^pep-\d+\.txt|\.rst$' - types: [text] # Manual codespell check - repo: https://github.com/codespell-project/codespell - rev: v2.2.2 + rev: v2.2.5 hooks: - id: codespell name: "Check for common misspellings in text files" @@ -101,152 +98,134 @@ repos: # Local checks for PEP headers and more - repo: local hooks: - - id: check-no-tabs - name: "Check tabs not used in PEPs" - language: pygrep - entry: '\t' - files: '^pep-\d+\.(rst|txt)$' - types: [text] +# # Hook to run "check-peps.py" +# - id: "check-peps" +# name: "Check PEPs for metadata and content enforcement" +# entry: "python check-peps.py" +# language: "system" +# files: "^pep-\d{4}\.(rst|txt)$" +# require_serial: true - id: check-required-headers name: "PEPs must have all required headers" language: pygrep entry: '(?-m:^PEP:(?=[\s\S]*\nTitle:)(?=[\s\S]*\nAuthor:)(?=[\s\S]*\nStatus:)(?=[\s\S]*\nType:)(?=[\s\S]*\nContent-Type:)(?=[\s\S]*\nCreated:))' args: ['--negate', '--multiline'] - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: check-header-order name: "PEP header order must follow PEP 12" language: pygrep entry: '^PEP:[^\n]+\nTitle:[^\n]+\n(Version:[^\n]+\n)?(Last-Modified:[^\n]+\n)?Author:[^\n]+\n( +\S[^\n]+\n)*(Sponsor:[^\n]+\n)?((PEP|BDFL)-Delegate:[^\n]*\n)?(Discussions-To:[^\n]*\n)?Status:[^\n]+\nType:[^\n]+\n(Topic:[^\n]+\n)?Content-Type:[^\n]+\n(Requires:[^\n]+\n)?Created:[^\n]+\n(Python-Version:[^\n]*\n)?(Post-History:[^\n]*\n( +\S[^\n]*\n)*)?(Replaces:[^\n]+\n)?(Superseded-By:[^\n]+\n)?(Resolution:[^\n]*\n)?\n' args: ['--negate', '--multiline'] - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-pep-number name: "'PEP' header must be a number 1-9999" language: pygrep entry: '(?-m:^PEP:(?:(?! +(0|[1-9][0-9]{0,3})\n)))' args: ['--multiline'] - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-title name: "'Title' must be 1-79 characters" language: pygrep entry: '(?<=\n)Title:(?:(?! +\S.{1,78}\n(?=[A-Z])))' args: ['--multiline'] - files: '^pep-\d+\.(rst|txt)$' - exclude: '^pep-(0499)\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' + exclude: '^peps/pep-(0499)\.rst$' - id: validate-author name: "'Author' must be list of 'Name <email@example.com>, ...'" language: pygrep entry: '(?<=\n)Author:(?:(?!((( +|\n {1,8})[^!#$%&()*+,/:;<=>?@\[\\\]\^_`{|}~]+( <[\w!#$%&''*+\-/=?^_{|}~.]+(@| at )[\w\-.]+\.[A-Za-z0-9]+>)?)(,|(?=\n[^ ])))+\n(?=[A-Z])))' - args: [--multiline] - files: '^pep-\d+\.(rst|txt)$' - types: [text] + args: ["--multiline"] + files: '^peps/pep-\d+\.rst$' - id: validate-sponsor name: "'Sponsor' must have format 'Name <email@example.com>'" language: pygrep entry: '^Sponsor:(?: (?! *[^!#$%&()*+,/:;<=>?@\[\\\]\^_`{|}~]+( <[\w!#$%&''*+\-/=?^_{|}~.]+(@| at )[\w\-.]+\.[A-Za-z0-9]+>)?$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-delegate name: "'Delegate' must have format 'Name <email@example.com>'" language: pygrep entry: '^(PEP|BDFL)-Delegate: (?:(?! *[^!#$%&()*+,/:;<=>?@\[\\\]\^_`{|}~]+( <[\w!#$%&''*+\-/=?^_{|}~.]+(@| at )[\w\-.]+\.[A-Za-z0-9]+>)?$))' - files: '^pep-\d+\.(rst|txt)$' - exclude: '^pep-(0451)\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' + exclude: '^peps/pep-(0451)\.rst$' - id: validate-discussions-to name: "'Discussions-To' must be a thread URL" language: pygrep entry: '^Discussions-To: (?:(?!([\w\-]+@(python\.org|googlegroups\.com))|https://((discuss\.python\.org/t/([\w\-]+/)?\d+/?)|(mail\.python\.org/pipermail/[\w\-]+/\d{4}-[A-Za-z]+/[A-Za-z0-9]+\.html)|(mail\.python\.org/archives/list/[\w\-]+@python\.org/thread/[A-Za-z0-9]+/?))$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-status name: "'Status' must be a valid PEP status" language: pygrep entry: '^Status:(?:(?! +(Draft|Withdrawn|Rejected|Accepted|Final|Active|Provisional|Deferred|Superseded|April Fool!)$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-type name: "'Type' must be a valid PEP type" language: pygrep entry: '^Type:(?:(?! +(Standards Track|Informational|Process)$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-topic name: "'Topic' must be for a valid sub-index" language: pygrep entry: '^Topic:(?:(?! +(Governance|Packaging|Typing|Release)(, (Governance|Packaging|Typing|Release))*$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-content-type name: "'Content-Type' must be 'text/x-rst'" language: pygrep entry: '^Content-Type:(?:(?! +text/x-rst$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-pep-references name: "`Requires`/`Replaces`/`Superseded-By` must be 'NNN' PEP IDs" language: pygrep entry: '^(Requires|Replaces|Superseded-By):(?:(?! *( (0|[1-9][0-9]{0,3})(,|$))+$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-created name: "'Created' must be a 'DD-mmm-YYYY' date" language: pygrep entry: '^Created:(?:(?! +([0-2][0-9]|(3[01]))-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(199[0-9]|20[0-9][0-9])$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-python-version name: "'Python-Version' must be a 'X.Y[.Z]` version" language: pygrep entry: '^Python-Version:(?:(?! *( [1-9]\.([0-9][0-9]?|x)(\.[1-9][0-9]?)?(,|$))+$))' - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-post-history name: "'Post-History' must be '`DD-mmm-YYYY <Thread URL>`__, ...'" language: pygrep entry: '(?<=\n)Post-History:(?:(?! ?\n|((( +|\n {1,14})(([0-2][0-9]|(3[01]))-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(199[0-9]|20[0-9][0-9])|`([0-2][0-9]|(3[01]))-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(199[0-9]|20[0-9][0-9]) <https://((discuss\.python\.org/t/([\w\-]+/)?\d+(?:/\d+/|/?))|(mail\.python\.org/pipermail/[\w\-]+/\d{4}-[A-Za-z]+/[A-Za-z0-9]+\.html)|(mail\.python\.org/archives/list/[\w\-]+@python\.org/thread/[A-Za-z0-9]+/?(#[A-Za-z0-9]+)?))>`__)(,|(?=\n[^ ])))+\n(?=[A-Z\n]))))' args: [--multiline] - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: validate-resolution name: "'Resolution' must be a direct thread/message URL" language: pygrep entry: '(?<!\n\n)(?<=\n)Resolution: (?:(?!https://((discuss\.python\.org/t/([\w\-]+/)?\d+(/\d+)?/?)|(mail\.python\.org/pipermail/[\w\-]+/\d{4}-[A-Za-z]+/[A-Za-z0-9]+\.html)|(mail\.python\.org/archives/list/[\w\-]+@python\.org/(message|thread)/[A-Za-z0-9]+/?(#[A-Za-z0-9]+)?))\n))' args: ['--multiline'] - files: '^pep-\d+\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' - id: check-direct-pep-links name: "Check that PEPs aren't linked directly" language: pygrep entry: '(dev/peps|peps\.python\.org)/pep-\d+' - files: '^pep-\d+\.(rst|txt)$' - exclude: '^pep-(0009|0287|0676|0684|8001)\.(rst|txt)$' - types: [text] + files: '^peps/pep-\d+\.rst$' + exclude: '^peps/pep-(0009|0287|0676|0684|8001)\.rst$' - id: check-direct-rfc-links name: "Check that RFCs aren't linked directly" language: pygrep entry: '(rfc-editor\.org|ietf\.org)/[\.\-_\?\&\#\w/]*[Rr][Ff][Cc][\-_]?\d+' - files: '\.(rst|txt)$' - types: [text] + types: ['rst'] diff --git a/.ruff.toml b/.ruff.toml new file mode 100644 index 000000000..5ab2337f9 --- /dev/null +++ b/.ruff.toml @@ -0,0 +1,15 @@ +ignore = [ + "E501", # Line too long +] + +select = [ + "E", # pycodestyle errors + "F", # pyflakes + "I", # isort + "PT", # flake8-pytest-style + "W", # pycodestyle warnings +] + +show-source = true + +target-version = "py39" diff --git a/AUTHOR_OVERRIDES.csv b/AUTHOR_OVERRIDES.csv deleted file mode 100644 index ba4e681a2..000000000 --- a/AUTHOR_OVERRIDES.csv +++ /dev/null @@ -1,13 +0,0 @@ -Overridden Name,Surname First,Name Reference -The Python core team and community,"The Python core team and community",python-dev -Erik De Bonte,"De Bonte, Erik",De Bonte -Greg Ewing,"Ewing, Gregory",Ewing -Guido van Rossum,"van Rossum, Guido (GvR)",GvR -Inada Naoki,"Inada, Naoki",Inada -Jim Jewett,"Jewett, Jim J.",Jewett -Just van Rossum,"van Rossum, Just (JvR)",JvR -Martin v. Löwis,"von Löwis, Martin",von Löwis -Nathaniel Smith,"Smith, Nathaniel J.",Smith -P.J. Eby,"Eby, Phillip J.",Eby -Germán Méndez Bravo,"Méndez Bravo, Germán",Méndez Bravo -Amethyst Reese,"Reese, Amethyst",Amethyst diff --git a/Makefile b/Makefile index dcbf43a19..8f973be2c 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ OUTPUT_DIR = build SPHINXERRORHANDLING = -W --keep-going -w sphinx-warnings.txt ALLSPHINXOPTS = -b $(BUILDER) -j $(JOBS) \ - $(SPHINXOPTS) $(SPHINXERRORHANDLING) . $(OUTPUT_DIR) $(SOURCES) + $(SPHINXOPTS) $(SPHINXERRORHANDLING) peps $(OUTPUT_DIR) $(SOURCES) ## html to render PEPs to "pep-NNNN.html" files .PHONY: html diff --git a/build.py b/build.py index 8f0ead024..04f0b5fdb 100755 --- a/build.py +++ b/build.py @@ -5,6 +5,7 @@ """Build script for Sphinx documentation""" import argparse +import os from pathlib import Path from sphinx.application import Sphinx @@ -27,15 +28,6 @@ def create_parser(): help='Render PEPs to "index.html" files within "pep-NNNN" directories. ' 'Cannot be used with "-f" or "-l".') - # flags / options - parser.add_argument("-w", "--fail-on-warning", action="store_true", - help="Fail the Sphinx build on any warning.") - parser.add_argument("-n", "--nitpicky", action="store_true", - help="Run Sphinx in 'nitpicky' mode, " - "warning on every missing reference target.") - parser.add_argument("-j", "--jobs", type=int, default=1, - help="How many parallel jobs to run (if supported). " - "Integer, default 1.") parser.add_argument( "-o", "--output-dir", @@ -61,33 +53,23 @@ def create_index_file(html_root: Path, builder: str) -> None: if __name__ == "__main__": args = create_parser() - root_directory = Path(".").absolute() - source_directory = root_directory + root_directory = Path(__file__).resolve().parent + source_directory = root_directory / "peps" build_directory = root_directory / args.output_dir - doctree_directory = build_directory / ".doctrees" # builder configuration - if args.builder is not None: - sphinx_builder = args.builder - else: - # default builder - sphinx_builder = "html" - - # other configuration - config_overrides = {} - if args.nitpicky: - config_overrides["nitpicky"] = True + sphinx_builder = args.builder or "html" app = Sphinx( source_directory, confdir=source_directory, - outdir=build_directory, - doctreedir=doctree_directory, + outdir=build_directory / sphinx_builder, + doctreedir=build_directory / "doctrees", buildername=sphinx_builder, - confoverrides=config_overrides, - warningiserror=args.fail_on_warning, - parallel=args.jobs, + warningiserror=True, + parallel=os.cpu_count() or 1, tags=["internal_builder"], + keep_going=True, ) app.build() diff --git a/check-peps.py b/check-peps.py new file mode 100755 index 000000000..ea45cd161 --- /dev/null +++ b/check-peps.py @@ -0,0 +1,605 @@ +#!/usr/bin/env python3 + +# This file is placed in the public domain or under the +# CC0-1.0-Universal license, whichever is more permissive. + +"""check-peps: Check PEPs for common mistakes. + +Usage: check-peps [-d | --detailed] <PEP files...> + +Only the PEPs specified are checked. +If none are specified, all PEPs are checked. + +Use "--detailed" to show the contents of lines where errors were found. +""" + +from __future__ import annotations + +import datetime as dt +import re +import sys +from pathlib import Path + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Iterable, Iterator, KeysView, Sequence + from typing import TypeAlias + + # (line number, warning message) + Message: TypeAlias = tuple[int, str] + MessageIterator: TypeAlias = Iterator[Message] + + +# get the directory with the PEP sources +ROOT_DIR = Path(__file__).resolve().parent +PEP_ROOT = ROOT_DIR / "peps" + +# See PEP 12 for the order +# Note we retain "BDFL-Delegate" +ALL_HEADERS = ( + "PEP", + "Title", + "Version", + "Last-Modified", + "Author", + "Sponsor", + "BDFL-Delegate", "PEP-Delegate", + "Discussions-To", + "Status", + "Type", + "Topic", + "Content-Type", + "Requires", + "Created", + "Python-Version", + "Post-History", + "Replaces", + "Superseded-By", + "Resolution", +) +REQUIRED_HEADERS = frozenset({"PEP", "Title", "Author", "Status", "Type", "Created"}) + +# See PEP 1 for the full list +ALL_STATUSES = frozenset({ + "Accepted", + "Active", + "April Fool!", + "Deferred", + "Draft", + "Final", + "Provisional", + "Rejected", + "Superseded", + "Withdrawn", +}) + +# PEPs that are allowed to link directly to PEPs +SKIP_DIRECT_PEP_LINK_CHECK = frozenset({"0009", "0287", "0676", "0684", "8001"}) + +DEFAULT_FLAGS = re.ASCII | re.IGNORECASE # Insensitive latin + +# any sequence of letters or '-', followed by a single ':' and a space or end of line +HEADER_PATTERN = re.compile(r"^([a-z\-]+):(?: |$)", DEFAULT_FLAGS) +# any sequence of unicode letters or legal special characters +NAME_PATTERN = re.compile(r"(?:[^\W\d_]|[ ',\-.])+(?: |$)") +# any sequence of ASCII letters, digits, or legal special characters +EMAIL_LOCAL_PART_PATTERN = re.compile(r"[\w!#$%&'*+\-/=?^{|}~.]+", DEFAULT_FLAGS) + +DISCOURSE_THREAD_PATTERN = re.compile(r"([\w\-]+/)?\d+", DEFAULT_FLAGS) +DISCOURSE_POST_PATTERN = re.compile(r"([\w\-]+/)?\d+(/\d+)?", DEFAULT_FLAGS) + +MAILMAN_2_PATTERN = re.compile(r"[\w\-]+/\d{4}-[a-z]+/\d+\.html", DEFAULT_FLAGS) +MAILMAN_3_THREAD_PATTERN = re.compile(r"[\w\-]+@python\.org/thread/[a-z0-9]+/?", DEFAULT_FLAGS) +MAILMAN_3_MESSAGE_PATTERN = re.compile(r"[\w\-]+@python\.org/message/[a-z0-9]+/?(#[a-z0-9]+)?", DEFAULT_FLAGS) + +# Controlled by the "--detailed" flag +DETAILED_ERRORS = False + + +def check(filenames: Sequence[str] = (), /) -> int: + """The main entry-point.""" + if filenames: + filenames = map(Path, filenames) + else: + filenames = PEP_ROOT.glob("pep-????.rst") + if (count := sum(map(check_file, filenames))) > 0: + s = "s" * (count != 1) + print(f"check-peps failed: {count} error{s}", file=sys.stderr) + return 1 + return 0 + + +def check_file(filename: Path, /) -> int: + filename = filename.resolve() + try: + content = filename.read_text(encoding="utf-8") + except FileNotFoundError: + return _output_error(filename, [""], [(0, "Could not read PEP!")]) + else: + lines = content.splitlines() + return _output_error(filename, lines, check_peps(filename, lines)) + + +def check_peps(filename: Path, lines: Sequence[str], /) -> MessageIterator: + yield from check_headers(lines) + for line_num, line in enumerate(lines, start=1): + if filename.stem.removeprefix("pep-") in SKIP_DIRECT_PEP_LINK_CHECK: + continue + yield from check_direct_links(line_num, line.lstrip()) + + +def check_headers(lines: Sequence[str], /) -> MessageIterator: + yield from _validate_pep_number(next(iter(lines), "")) + + found_headers = {} + line_num = 0 + for line_num, line in enumerate(lines, start=1): + if line.strip() == "": + headers_end_line_num = line_num + break + if match := HEADER_PATTERN.match(line): + header = match[1] + if header in ALL_HEADERS: + if header not in found_headers: + found_headers[match[1]] = line_num + else: + yield line_num, f"Must not have duplicate header: {header} " + else: + yield line_num, f"Must not have invalid header: {header}" + else: + headers_end_line_num = line_num + + yield from _validate_required_headers(found_headers.keys()) + + shifted_line_nums = list(found_headers.values())[1:] + for i, (header, line_num) in enumerate(found_headers.items()): + start = line_num - 1 + end = headers_end_line_num - 1 + if i < len(found_headers) - 1: + end = shifted_line_nums[i] - 1 + remainder = "\n".join(lines[start:end]).removeprefix(f"{header}:") + if remainder != "": + if remainder[0] not in {" ", "\n"}: + yield line_num, f"Headers must have a space after the colon: {header}" + remainder = remainder.lstrip() + yield from _validate_header(header, line_num, remainder) + + +def _validate_header(header: str, line_num: int, content: str) -> MessageIterator: + if header == "Title": + yield from _validate_title(line_num, content) + elif header == "Author": + yield from _validate_author(line_num, content) + elif header == "Sponsor": + yield from _validate_sponsor(line_num, content) + elif header in {"BDFL-Delegate", "PEP-Delegate"}: + yield from _validate_delegate(line_num, content) + elif header == "Discussions-To": + yield from _validate_discussions_to(line_num, content) + elif header == "Status": + yield from _validate_status(line_num, content) + elif header == "Type": + yield from _validate_type(line_num, content) + elif header == "Topic": + yield from _validate_topic(line_num, content) + elif header == "Content-Type": + yield from _validate_content_type(line_num, content) + elif header in {"Requires", "Replaces", "Superseded-By"}: + yield from _validate_pep_references(line_num, content) + elif header == "Created": + yield from _validate_created(line_num, content) + elif header == "Python-Version": + yield from _validate_python_version(line_num, content) + elif header == "Post-History": + yield from _validate_post_history(line_num, content) + elif header == "Resolution": + yield from _validate_resolution(line_num, content) + + +def check_direct_links(line_num: int, line: str) -> MessageIterator: + """Check that PEPs and RFCs aren't linked directly""" + + line = line.lower() + if "dev/peps/pep-" in line or "peps.python.org/pep-" in line: + yield line_num, "Use the :pep:`NNN` role to refer to PEPs" + if "rfc-editor.org/rfc/" in line or "ietf.org/doc/html/rfc" in line: + yield line_num, "Use the :rfc:`NNN` role to refer to RFCs" + + +def _output_error(filename: Path, lines: Sequence[str], errors: Iterable[Message]) -> int: + relative_filename = filename.relative_to(ROOT_DIR) + err_count = 0 + for line_num, msg in errors: + err_count += 1 + + print(f"{relative_filename}:{line_num}: {msg}") + if not DETAILED_ERRORS: + continue + + line = lines[line_num - 1] + print(" |") + print(f"{line_num: >4} | '{line}'") + print(" |") + + return err_count + + +########################### +# PEP Header Validators # +########################### + + +def _validate_required_headers(found_headers: KeysView[str]) -> MessageIterator: + """PEPs must have all required headers, in the PEP 12 order""" + + if missing := REQUIRED_HEADERS.difference(found_headers): + for missing_header in sorted(missing, key=ALL_HEADERS.index): + yield 1, f"Must have required header: {missing_header}" + + ordered_headers = sorted(found_headers, key=ALL_HEADERS.index) + if list(found_headers) != ordered_headers: + order_str = ", ".join(ordered_headers) + yield 1, "Headers must be in PEP 12 order. Correct order: " + order_str + + +def _validate_pep_number(line: str) -> MessageIterator: + """'PEP' header must be a number 1-9999""" + + if not line.startswith("PEP: "): + yield 1, "PEP must begin with the 'PEP:' header" + return + + pep_number = line.removeprefix("PEP: ").lstrip() + yield from _pep_num(1, pep_number, "'PEP:' header") + + +def _validate_title(line_num: int, line: str) -> MessageIterator: + """'Title' must be 1-79 characters""" + + if len(line) == 0: + yield line_num, "PEP must have a title" + elif len(line) > 79: + yield line_num, "PEP title must be less than 80 characters" + + +def _validate_author(line_num: int, body: str) -> MessageIterator: + """'Author' must be list of 'Name <email@example.com>, …'""" + + lines = body.split("\n") + for offset, line in enumerate(lines): + if offset >= 1 and line[:9].isspace(): + # Checks for: + # Author: Alice + # Bob + # ^^^^ + # Note that len("Author: ") == 8 + yield line_num + offset, "Author line must not be over-indented" + if offset < len(lines) - 1: + if not line.endswith(","): + yield line_num + offset, "Author continuation lines must end with a comma" + for part in line.removesuffix(",").split(", "): + yield from _email(line_num + offset, part, "Author") + + +def _validate_sponsor(line_num: int, line: str) -> MessageIterator: + """'Sponsor' must have format 'Name <email@example.com>'""" + + yield from _email(line_num, line, "Sponsor") + + +def _validate_delegate(line_num: int, line: str) -> MessageIterator: + """'Delegate' must have format 'Name <email@example.com>'""" + + if line == "": + return + + # PEP 451 + if ", " in line: + for part in line.removesuffix(",").split(", "): + yield from _email(line_num, part, "Delegate") + return + + yield from _email(line_num, line, "Delegate") + + +def _validate_discussions_to(line_num: int, line: str) -> MessageIterator: + """'Discussions-To' must be a thread URL""" + + yield from _thread(line_num, line, "Discussions-To", discussions_to=True) + if line.startswith("https://"): + return + for suffix in "@python.org", "@googlegroups.com": + if line.endswith(suffix): + remainder = line.removesuffix(suffix) + if re.fullmatch(r"[\w\-]+", remainder) is None: + yield line_num, "Discussions-To must be a valid mailing list" + return + yield line_num, "Discussions-To must be a valid thread URL or mailing list" + + +def _validate_status(line_num: int, line: str) -> MessageIterator: + """'Status' must be a valid PEP status""" + + if line not in ALL_STATUSES: + yield line_num, "Status must be a valid PEP status" + + +def _validate_type(line_num: int, line: str) -> MessageIterator: + """'Type' must be a valid PEP type""" + + if line not in {"Standards Track", "Informational", "Process"}: + yield line_num, "Type must be a valid PEP type" + + +def _validate_topic(line_num: int, line: str) -> MessageIterator: + """'Topic' must be for a valid sub-index""" + + topics = line.split(", ") + unique_topics = set(topics) + if len(topics) > len(unique_topics): + yield line_num, "Topic must not contain duplicates" + + if unique_topics - {"Governance", "Packaging", "Typing", "Release"}: + if not all(map(str.istitle, unique_topics)): + yield line_num, "Topic must be properly capitalised (Title Case)" + if unique_topics - {"governance", "packaging", "typing", "release"}: + yield line_num, "Topic must be for a valid sub-index" + if sorted(topics) != topics: + yield line_num, "Topic must be sorted lexicographically" + + +def _validate_content_type(line_num: int, line: str) -> MessageIterator: + """'Content-Type' must be 'text/x-rst'""" + + if line != "text/x-rst": + yield line_num, "Content-Type must be 'text/x-rst'" + + +def _validate_pep_references(line_num: int, line: str) -> MessageIterator: + """`Requires`/`Replaces`/`Superseded-By` must be 'NNN' PEP IDs""" + + line = line.removesuffix(",").rstrip() + if line.count(", ") != line.count(","): + yield line_num, "PEP references must be separated by comma-spaces (', ')" + return + + references = line.split(", ") + for reference in references: + yield from _pep_num(line_num, reference, "PEP reference") + + +def _validate_created(line_num: int, line: str) -> MessageIterator: + """'Created' must be a 'DD-mmm-YYYY' date""" + + yield from _date(line_num, line, "Created") + + +def _validate_python_version(line_num: int, line: str) -> MessageIterator: + """'Python-Version' must be an ``X.Y[.Z]`` version""" + + versions = line.split(", ") + for version in versions: + if version.count(".") not in {1, 2}: + yield line_num, f"Python-Version must have two or three segments: {version}" + continue + + try: + major, minor, micro = version.split(".", 2) + except ValueError: + major, minor = version.split(".", 1) + micro = "" + + if major not in "123": + yield line_num, f"Python-Version major part must be 1, 2, or 3: {version}" + if not _is_digits(minor) and minor != "x": + yield line_num, f"Python-Version minor part must be numeric: {version}" + elif minor != "0" and minor[0] == "0": + yield line_num, f"Python-Version minor part must not have leading zeros: {version}" + + if micro == "": + return + if minor == "x": + yield line_num, f"Python-Version micro part must be empty if minor part is 'x': {version}" + elif micro[0] == "0": + yield line_num, f"Python-Version micro part must not have leading zeros: {version}" + elif not _is_digits(micro): + yield line_num, f"Python-Version micro part must be numeric: {version}" + + +def _validate_post_history(line_num: int, body: str) -> MessageIterator: + """'Post-History' must be '`DD-mmm-YYYY <Thread URL>`__, …'""" + + if body == "": + return + + for offset, line in enumerate(body.removesuffix(",").split("\n"), start=line_num): + for post in line.removesuffix(",").strip().split(", "): + if not post.startswith("`") and not post.endswith(">`__"): + yield from _date(offset, post, "Post-History") + else: + post_date, post_url = post[1:-4].split(" <") + yield from _date(offset, post_date, "Post-History") + yield from _thread(offset, post_url, "Post-History") + + +def _validate_resolution(line_num: int, line: str) -> MessageIterator: + """'Resolution' must be a direct thread/message URL""" + + yield from _thread(line_num, line, "Resolution", allow_message=True) + + +######################## +# Validation Helpers # +######################## + +def _pep_num(line_num: int, pep_number: str, prefix: str) -> MessageIterator: + if pep_number == "": + yield line_num, f"{prefix} must not be blank: {pep_number!r}" + return + if pep_number.startswith("0") and pep_number != "0": + yield line_num, f"{prefix} must not contain leading zeros: {pep_number!r}" + if not _is_digits(pep_number): + yield line_num, f"{prefix} must be numeric: {pep_number!r}" + elif not 0 <= int(pep_number) <= 9999: + yield line_num, f"{prefix} must be between 0 and 9999: {pep_number!r}" + + +def _is_digits(string: str) -> bool: + """Match a string of ASCII digits ([0-9]+).""" + return string.isascii() and string.isdigit() + + +def _email(line_num: int, author_email: str, prefix: str) -> MessageIterator: + author_email = author_email.strip() + + if author_email.count("<") > 1: + msg = f"{prefix} entries must not contain multiple '<': {author_email!r}" + yield line_num, msg + if author_email.count(">") > 1: + msg = f"{prefix} entries must not contain multiple '>': {author_email!r}" + yield line_num, msg + if author_email.count("@") > 1: + msg = f"{prefix} entries must not contain multiple '@': {author_email!r}" + yield line_num, msg + + author = author_email.split("<", 1)[0].rstrip() + if NAME_PATTERN.fullmatch(author) is None: + msg = f"{prefix} entries must begin with a valid 'Name': {author_email!r}" + yield line_num, msg + return + + email_text = author_email.removeprefix(author) + if not email_text: + # Does not have the optional email part + return + + if not email_text.startswith(" <") or not email_text.endswith(">"): + msg = f"{prefix} entries must be formatted as 'Name <email@example.com>': {author_email!r}" + yield line_num, msg + email_text = email_text.removeprefix(" <").removesuffix(">") + + if "@" in email_text: + local, domain = email_text.rsplit("@", 1) + elif " at " in email_text: + local, domain = email_text.rsplit(" at ", 1) + else: + yield line_num, f"{prefix} entries must contain a valid email address: {author_email!r}" + return + if EMAIL_LOCAL_PART_PATTERN.fullmatch(local) is None or _invalid_domain(domain): + yield line_num, f"{prefix} entries must contain a valid email address: {author_email!r}" + + +def _invalid_domain(domain_part: str) -> bool: + *labels, root = domain_part.split(".") + for label in labels: + if not label.replace("-", "").isalnum(): + return True + return not root.isalnum() or not root.isascii() + + +def _thread(line_num: int, url: str, prefix: str, *, allow_message: bool = False, discussions_to: bool = False) -> MessageIterator: + if allow_message and discussions_to: + msg = "allow_message and discussions_to cannot both be True" + raise ValueError(msg) + + msg = f"{prefix} must be a valid thread URL" + + if not url.startswith("https://"): + if not discussions_to: + yield line_num, msg + return + + if url.startswith("https://discuss.python.org/t/"): + remainder = url.removeprefix("https://discuss.python.org/t/").removesuffix("/") + + # Discussions-To links must be the thread itself, not a post + if discussions_to: + # The equivalent pattern is similar to '([\w\-]+/)?\d+', + # but the topic name must contain a non-numeric character + + # We use ``str.rpartition`` as the topic name is optional + topic_name, _, topic_id = remainder.rpartition("/") + if topic_name == '' and _is_digits(topic_id): + return + topic_name = topic_name.replace("-", "0").replace("_", "0") + # the topic name must not be entirely numeric + valid_topic_name = not _is_digits(topic_name) and topic_name.isalnum() + if valid_topic_name and _is_digits(topic_id): + return + else: + # The equivalent pattern is similar to '([\w\-]+/)?\d+(/\d+)?', + # but the topic name must contain a non-numeric character + if remainder.count("/") == 2: + # When there are three parts, the URL must be "topic-name/topic-id/post-id". + topic_name, topic_id, post_id = remainder.rsplit("/", 2) + topic_name = topic_name.replace("-", "0").replace("_", "0") + valid_topic_name = not _is_digits(topic_name) and topic_name.isalnum() + if valid_topic_name and _is_digits(topic_id) and _is_digits(post_id): + # the topic name must not be entirely numeric + return + elif remainder.count("/") == 1: + # When there are only two parts, there's an ambiguity between + # "topic-name/topic-id" and "topic-id/post-id". + # We disambiguate by checking if the LHS is a valid name and + # the RHS is a valid topic ID (for the former), + # and then if both the LHS and RHS are valid IDs (for the latter). + left, right = remainder.rsplit("/") + left = left.replace("-", "0").replace("_", "0") + # the topic name must not be entirely numeric + left_is_name = not _is_digits(left) and left.isalnum() + if left_is_name and _is_digits(right): + return + elif _is_digits(left) and _is_digits(right): + return + else: + # When there's only one part, it must be a valid topic ID. + if _is_digits(remainder): + return + + if url.startswith("https://mail.python.org/pipermail/"): + remainder = url.removeprefix("https://mail.python.org/pipermail/") + if MAILMAN_2_PATTERN.fullmatch(remainder) is not None: + return + + if url.startswith("https://mail.python.org/archives/list/"): + remainder = url.removeprefix("https://mail.python.org/archives/list/") + if allow_message and MAILMAN_3_MESSAGE_PATTERN.fullmatch(remainder) is not None: + return + if MAILMAN_3_THREAD_PATTERN.fullmatch(remainder) is not None: + return + + yield line_num, msg + + +def _date(line_num: int, date_str: str, prefix: str) -> MessageIterator: + try: + parsed_date = dt.datetime.strptime(date_str, "%d-%b-%Y") + except ValueError: + yield line_num, f"{prefix} must be a 'DD-mmm-YYYY' date: {date_str!r}" + return + else: + if date_str[1] == "-": # Date must be zero-padded + yield line_num, f"{prefix} must be a 'DD-mmm-YYYY' date: {date_str!r}" + return + + if parsed_date.year < 1990: + yield line_num, f"{prefix} must not be before Python was invented: {date_str!r}" + if parsed_date > (dt.datetime.now() + dt.timedelta(days=14)): + yield line_num, f"{prefix} must not be in the future: {date_str!r}" + + +if __name__ == "__main__": + if {"-h", "--help", "-?"}.intersection(sys.argv[1:]): + print(__doc__, file=sys.stderr) + raise SystemExit(0) + + files = {} + for arg in sys.argv[1:]: + if not arg.startswith("-"): + files[arg] = None + elif arg in {"-d", "--detailed"}: + DETAILED_ERRORS = True + else: + print(f"Unknown option: {arg!r}", file=sys.stderr) + raise SystemExit(1) + raise SystemExit(check(files)) diff --git a/docs/build.rst b/docs/build.rst index ae4ac1848..d59b2f804 100644 --- a/docs/build.rst +++ b/docs/build.rst @@ -1,5 +1,4 @@ -.. - Author: Adam Turner +:author: Adam Turner Building PEPs Locally @@ -10,8 +9,8 @@ This can also be used to check that the PEP is valid reStructuredText before submission to the PEP editors. The rest of this document assumes you are working from a local clone of the -`PEPs repository <https://github.com/python/peps>`__, with -**Python 3.9 or later** installed. +`PEPs repository <https://github.com/python/peps>`__, +with **Python 3.9 or later** installed. Render PEPs locally @@ -51,11 +50,6 @@ Render PEPs locally (venv) PS> python build.py - .. note:: - - There may be a series of warnings about unreferenced citations or labels. - Whilst these are valid warnings, they do not impact the build process. - 4. Navigate to the ``build`` directory of your PEPs repo to find the HTML pages. PEP 0 provides a formatted index, and may be a useful reference. @@ -87,28 +81,8 @@ Check the validity of links within PEP sources (runs the `Sphinx linkchecker .. code-block:: shell - python build.py --check-links - make check-links - - -Stricter rendering -'''''''''''''''''' - -Run in `nit-picky <https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-nitpicky>`__ -mode. -This generates warnings for all missing references. - -.. code-block:: shell - - python build.py --nitpicky - -Fail the build on any warning. -As of January 2022, there are around 250 warnings when building the PEPs. - -.. code-block:: shell - - python build.py --fail-on-warning - make fail-warning + python build.py --check-links + make check-links ``build.py`` usage @@ -118,4 +92,4 @@ For details on the command-line options to the ``build.py`` script, run: .. code-block:: shell - python build.py --help + python build.py --help diff --git a/docs/rendering_system.rst b/docs/rendering_system.rst index 9a298e0e8..fc20d00df 100644 --- a/docs/rendering_system.rst +++ b/docs/rendering_system.rst @@ -1,6 +1,6 @@ -.. - Author: Adam Turner +:author: Adam Turner +.. We can't use :pep:`N` references in this document, as they use links relative to the current file, which doesn't work in a subdirectory like this one. @@ -9,7 +9,7 @@ An Overview of the PEP Rendering System ======================================= This document provides an overview of the PEP rendering system, as a companion -to :doc:`PEP 676 <../pep-0676>`. +to `PEP 676 <https://peps.python.org/pep-0676/>`__. 1. Configuration @@ -17,14 +17,14 @@ to :doc:`PEP 676 <../pep-0676>`. Configuration is stored in three files: -- ``conf.py`` contains the majority of the Sphinx configuration -- ``contents.rst`` creates the Sphinx-mandated table of contents directive +- ``peps/conf.py`` contains the majority of the Sphinx configuration +- ``peps/contents.rst`` contains the compulsory table of contents directive - ``pep_sphinx_extensions/pep_theme/theme.conf`` sets the Pygments themes The configuration: - registers the custom Sphinx extension -- sets both ``.txt`` and ``.rst`` suffixes to be parsed as PEPs +- sets the ``.rst`` suffix to be parsed as PEPs - tells Sphinx which source files to use - registers the PEP theme, maths renderer, and template - disables some default settings that are covered in the extension @@ -35,7 +35,7 @@ The configuration: ---------------- ``build.py`` manages the rendering process. -Usage is covered in :doc:`build`. +Usage is covered in `Building PEPs Locally <./build.rst>`_. 3. Extension @@ -110,7 +110,8 @@ This overrides the built-in ``:pep:`` role to return the correct URL. 3.4.2 ``PEPHeaders`` transform ****************************** -PEPs start with a set of :rfc:`2822` headers, per :doc:`PEP 1 <../pep-0001>`. +PEPs start with a set of :rfc:`2822` headers, +per `PEP 1 <https://peps.python.org/pep-0001/>`__. This transform validates that the required headers are present and of the correct data type, and removes headers not for display. It must run before the ``PEPTitle`` transform. @@ -122,7 +123,7 @@ It must run before the ``PEPTitle`` transform. We generate the title node from the parsed title in the PEP headers, and make all nodes in the document children of the new title node. This transform must also handle parsing reStructuredText markup within PEP -titles, such as :doc:`PEP 604 <../pep-0604>`. +titles, such as `PEP 604 <https://peps.python.org/pep-0604/>`__. 3.4.4 ``PEPContents`` transform @@ -216,12 +217,9 @@ parse and validate that metadata. After collecting and validating all the PEP data, the index itself is created in three steps: - 1. Output the header text - 2. Output the category and numerical indices - 3. Output the author index - -The ``AUTHOR_OVERRIDES.csv`` file can be used to override an author's name in -the PEP 0 output. +1. Output the header text +2. Output the category and numerical indices +3. Output the author index We then add the newly created PEP 0 file to two Sphinx variables so that it will be processed as a normal source document. diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 672a6a452..8538b838a 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -28,7 +28,7 @@ def _update_config_for_builder(app: Sphinx) -> None: app.env.document_ids = {} # For PEPReferenceRoleTitleText app.env.settings["builder"] = app.builder.name if app.builder.name == "dirhtml": - app.env.settings["pep_url"] = "pep-{:0>4}" + app.env.settings["pep_url"] = "pep-{:0>4}/" app.connect("build-finished", _post_build) # Post-build tasks diff --git a/pep_sphinx_extensions/generate_rss.py b/pep_sphinx_extensions/generate_rss.py index a7120c9d6..5e5e0b8bc 100644 --- a/pep_sphinx_extensions/generate_rss.py +++ b/pep_sphinx_extensions/generate_rss.py @@ -17,9 +17,6 @@ RSS_DESCRIPTION = ( "and some meta-information like release procedure and schedules." ) -# get the directory with the PEP sources -PEP_ROOT = Path(__file__).parent - def _format_rfc_2822(datetime: dt.datetime) -> str: datetime = datetime.replace(tzinfo=dt.timezone.utc) diff --git a/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py b/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py index f95d90411..7349f712f 100644 --- a/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py +++ b/pep_sphinx_extensions/pep_processor/html/pep_html_builder.py @@ -1,5 +1,3 @@ -from pathlib import Path - from docutils import nodes from docutils.frontend import OptionParser from sphinx.builders.html import StandaloneHTMLBuilder @@ -22,6 +20,7 @@ class FileBuilder(StandaloneHTMLBuilder): self.docwriter = HTMLWriter(self) _opt_parser = OptionParser([self.docwriter], defaults=self.env.settings, read_config_files=True) self.docsettings = _opt_parser.get_default_values() + self._orig_css_files = self._orig_js_files = [] def get_doc_context(self, docname: str, body: str, _metatags: str) -> dict: """Collect items for the template context of a page.""" @@ -30,10 +29,6 @@ class FileBuilder(StandaloneHTMLBuilder): except KeyError: title = "" - # source filename - file_is_rst = Path(self.env.srcdir, docname + ".rst").exists() - source_name = f"{docname}.rst" if file_is_rst else f"{docname}.txt" - # local table of contents toc_tree = self.env.tocs[docname].deepcopy() if len(toc_tree) and len(toc_tree[0]) > 1: @@ -45,7 +40,7 @@ class FileBuilder(StandaloneHTMLBuilder): else: toc = "" # PEPs with no sections -- 9, 210 - return {"title": title, "sourcename": source_name, "toc": toc, "body": body} + return {"title": title, "toc": toc, "body": body} class DirectoryBuilder(FileBuilder): diff --git a/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py b/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py index f3a55270c..d0f1cbee2 100644 --- a/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py +++ b/pep_sphinx_extensions/pep_processor/parsing/pep_banner_directive.py @@ -5,7 +5,6 @@ from __future__ import annotations from docutils import nodes from docutils.parsers import rst - PYPA_SPEC_BASE_URL = "https://packaging.python.org/en/latest/specifications/" diff --git a/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py b/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py index 7b9c29d51..c49355fd1 100644 --- a/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py +++ b/pep_sphinx_extensions/pep_processor/transforms/pep_footer.py @@ -1,4 +1,4 @@ -import datetime as dt +import time from pathlib import Path import subprocess @@ -23,7 +23,7 @@ class PEPFooter(transforms.Transform): def apply(self) -> None: pep_source_path = Path(self.document["source"]) - if not pep_source_path.match("pep-*"): + if not pep_source_path.match("pep-????.???"): return # not a PEP file, exit early # Iterate through sections from the end of the document @@ -54,7 +54,7 @@ class PEPFooter(transforms.Transform): def _add_source_link(pep_source_path: Path) -> nodes.paragraph: """Add link to source text on VCS (GitHub)""" - source_link = f"https://github.com/python/peps/blob/main/{pep_source_path.name}" + source_link = f"https://github.com/python/peps/blob/main/peps/{pep_source_path.name}" link_node = nodes.reference("", source_link, refuri=source_link) return nodes.paragraph("", "Source: ", link_node) @@ -62,12 +62,10 @@ def _add_source_link(pep_source_path: Path) -> nodes.paragraph: def _add_commit_history_info(pep_source_path: Path) -> nodes.paragraph: """Use local git history to find last modified date.""" try: - since_epoch = LAST_MODIFIED_TIMES[pep_source_path.name] + iso_time = _LAST_MODIFIED_TIMES[pep_source_path.stem] except KeyError: return nodes.paragraph() - epoch_dt = dt.datetime.fromtimestamp(since_epoch, dt.timezone.utc) - iso_time = epoch_dt.isoformat(sep=" ") commit_link = f"https://github.com/python/peps/commits/main/{pep_source_path.name}" link_node = nodes.reference("", f"{iso_time} GMT", refuri=commit_link) return nodes.paragraph("", "Last modified: ", link_node) @@ -75,29 +73,36 @@ def _add_commit_history_info(pep_source_path: Path) -> nodes.paragraph: def _get_last_modified_timestamps(): # get timestamps and changed files from all commits (without paging results) - args = ["git", "--no-pager", "log", "--format=#%at", "--name-only"] - with subprocess.Popen(args, stdout=subprocess.PIPE) as process: - all_modified = process.stdout.read().decode("utf-8") - process.stdout.close() - if process.wait(): # non-zero return code - return {} + args = ("git", "--no-pager", "log", "--format=#%at", "--name-only") + ret = subprocess.run(args, stdout=subprocess.PIPE, text=True, encoding="utf-8") + if ret.returncode: # non-zero return code + return {} + all_modified = ret.stdout + + # remove "peps/" prefix from file names + all_modified = all_modified.replace("\npeps/", "\n") # set up the dictionary with the *current* files - last_modified = {path.name: 0 for path in Path().glob("pep-*") if path.suffix in {".txt", ".rst"}} + peps_dir = Path(__file__, "..", "..", "..", "..", "peps").resolve() + last_modified = {path.stem: "" for path in peps_dir.glob("pep-????.rst")} # iterate through newest to oldest, updating per file timestamps change_sets = all_modified.removeprefix("#").split("#") for change_set in change_sets: timestamp, files = change_set.split("\n", 1) for file in files.strip().split("\n"): - if file.startswith("pep-") and file[-3:] in {"txt", "rst"}: - if last_modified.get(file) == 0: - try: - last_modified[file] = float(timestamp) - except ValueError: - pass # if float conversion fails + if not file.startswith("pep-") or not file.endswith((".rst", ".txt")): + continue # not a PEP + file = file[:-4] + if last_modified.get(file) != "": + continue # most recent modified date already found + try: + y, m, d, hh, mm, ss, *_ = time.gmtime(float(timestamp)) + except ValueError: + continue # if float conversion fails + last_modified[file] = f"{y:04}-{m:02}-{d:02} {hh:02}:{mm:02}:{ss:02}" return last_modified -LAST_MODIFIED_TIMES = _get_last_modified_timestamps() +_LAST_MODIFIED_TIMES = _get_last_modified_timestamps() diff --git a/pep_sphinx_extensions/pep_theme/static/style.css b/pep_sphinx_extensions/pep_theme/static/style.css index f3c7cb16d..70e1cbf08 100644 --- a/pep_sphinx_extensions/pep_theme/static/style.css +++ b/pep_sphinx_extensions/pep_theme/static/style.css @@ -230,6 +230,22 @@ table th + th, table td + td { border-left: 1px solid var(--colour-background-accent-strong); } +/* Common column widths for PEP status tables */ +table.pep-zero-table tr td:nth-child(1) { + width: 5.5%; +} +table.pep-zero-table tr td:nth-child(2) { + width: 6.5%; +} +table.pep-zero-table tr td:nth-child(3), +table.pep-zero-table tr td:nth-child(4){ + width: 44%; +} +/* Authors & Sponsors table */ +#authors-owners table td, +#authors-owners table th { + width: 50%; +} /* Breadcrumbs rules */ section#pep-page-section > header { diff --git a/pep_sphinx_extensions/pep_theme/templates/page.html b/pep_sphinx_extensions/pep_theme/templates/page.html index 2831fce0c..46be8c5bb 100644 --- a/pep_sphinx_extensions/pep_theme/templates/page.html +++ b/pep_sphinx_extensions/pep_theme/templates/page.html @@ -43,8 +43,8 @@ <h2>Contents</h2> {{ toc }} <br> - {%- if not (sourcename.startswith("pep-0000") or sourcename.startswith("topic")) %} - <a id="source" href="https://github.com/python/peps/blob/main/{{sourcename}}">Page Source (GitHub)</a> + {%- if not pagename.startswith(("pep-0000", "topic")) %} + <a id="source" href="https://github.com/python/peps/blob/main/peps/{{pagename}}.rst">Page Source (GitHub)</a> {%- endif %} </nav> </section> diff --git a/pep_sphinx_extensions/pep_zero_generator/author.py b/pep_sphinx_extensions/pep_zero_generator/author.py deleted file mode 100644 index 4425c6b3c..000000000 --- a/pep_sphinx_extensions/pep_zero_generator/author.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import annotations - -from typing import NamedTuple - - -class _Name(NamedTuple): - mononym: str = None - forename: str = None - surname: str = None - suffix: str = None - - -class Author(NamedTuple): - """Represent PEP authors.""" - last_first: str # The author's name in Surname, Forename, Suffix order. - nick: str # Author's nickname for PEP tables. Defaults to surname. - email: str # The author's email address. - - -def parse_author_email(author_email_tuple: tuple[str, str], authors_overrides: dict[str, dict[str, str]]) -> Author: - """Parse the name and email address of an author.""" - name, email = author_email_tuple - _first_last = name.strip() - email = email.lower() - - if _first_last in authors_overrides: - name_dict = authors_overrides[_first_last] - last_first = name_dict["Surname First"] - nick = name_dict["Name Reference"] - return Author(last_first, nick, email) - - name_parts = _parse_name(_first_last) - if name_parts.mononym is not None: - return Author(name_parts.mononym, name_parts.mononym, email) - - if name_parts.suffix: - last_first = f"{name_parts.surname}, {name_parts.forename}, {name_parts.suffix}" - return Author(last_first, name_parts.surname, email) - - last_first = f"{name_parts.surname}, {name_parts.forename}" - return Author(last_first, name_parts.surname, email) - - -def _parse_name(full_name: str) -> _Name: - """Decompose a full name into parts. - - If a mononym (e.g, 'Aahz') then return the full name. If there are - suffixes in the name (e.g. ', Jr.' or 'II'), then find and extract - them. If there is a middle initial followed by a full stop, then - combine the following words into a surname (e.g. N. Vander Weele). If - there is a leading, lowercase portion to the last name (e.g. 'van' or - 'von') then include it in the surname. - - """ - possible_suffixes = {"Jr", "Jr.", "II", "III"} - - pre_suffix, _, raw_suffix = full_name.partition(",") - name_parts = pre_suffix.strip().split(" ") - num_parts = len(name_parts) - suffix = raw_suffix.strip() - - if name_parts == [""]: - raise ValueError("Name is empty!") - elif num_parts == 1: - return _Name(mononym=name_parts[0], suffix=suffix) - elif num_parts == 2: - return _Name(forename=name_parts[0].strip(), surname=name_parts[1], suffix=suffix) - - # handles rogue uncaught suffixes - if name_parts[-1] in possible_suffixes: - suffix = f"{name_parts.pop(-1)} {suffix}".strip() - - # handles von, van, v. etc. - if name_parts[-2].islower(): - forename = " ".join(name_parts[:-2]).strip() - surname = " ".join(name_parts[-2:]) - return _Name(forename=forename, surname=surname, suffix=suffix) - - # handles double surnames after a middle initial (e.g. N. Vander Weele) - elif any(s.endswith(".") for s in name_parts): - split_position = [i for i, x in enumerate(name_parts) if x.endswith(".")][-1] + 1 - forename = " ".join(name_parts[:split_position]).strip() - surname = " ".join(name_parts[split_position:]) - return _Name(forename=forename, surname=surname, suffix=suffix) - - # default to using the last item as the surname - else: - forename = " ".join(name_parts[:-1]).strip() - return _Name(forename=forename, surname=name_parts[-1], suffix=suffix) diff --git a/pep_sphinx_extensions/pep_zero_generator/parser.py b/pep_sphinx_extensions/pep_zero_generator/parser.py index 220ef308c..193186c74 100644 --- a/pep_sphinx_extensions/pep_zero_generator/parser.py +++ b/pep_sphinx_extensions/pep_zero_generator/parser.py @@ -2,13 +2,10 @@ from __future__ import annotations -import csv +import dataclasses from email.parser import HeaderParser from pathlib import Path -import re -from typing import TYPE_CHECKING -from pep_sphinx_extensions.pep_zero_generator.author import parse_author_email from pep_sphinx_extensions.pep_zero_generator.constants import ACTIVE_ALLOWED from pep_sphinx_extensions.pep_zero_generator.constants import HIDE_STATUS from pep_sphinx_extensions.pep_zero_generator.constants import SPECIAL_STATUSES @@ -19,16 +16,12 @@ from pep_sphinx_extensions.pep_zero_generator.constants import TYPE_STANDARDS from pep_sphinx_extensions.pep_zero_generator.constants import TYPE_VALUES from pep_sphinx_extensions.pep_zero_generator.errors import PEPError -if TYPE_CHECKING: - from pep_sphinx_extensions.pep_zero_generator.author import Author - -# AUTHOR_OVERRIDES.csv is an exception file for PEP 0 name parsing -AUTHOR_OVERRIDES: dict[str, dict[str, str]] = {} -with open("AUTHOR_OVERRIDES.csv", "r", encoding="utf-8") as f: - for line in csv.DictReader(f): - full_name = line.pop("Overridden Name") - AUTHOR_OVERRIDES[full_name] = line +@dataclasses.dataclass(order=True, frozen=True) +class _Author: + """Represent PEP authors.""" + full_name: str # The author's name. + email: str # The author's email address. class PEP: @@ -97,7 +90,9 @@ class PEP: self.status: str = status # Parse PEP authors - self.authors: list[Author] = _parse_authors(self, metadata["Author"], AUTHOR_OVERRIDES) + self.authors: list[_Author] = _parse_author(metadata["Author"]) + if not self.authors: + raise _raise_pep_error(self, "no authors found", pep_num=True) # Topic (for sub-indices) _topic = metadata.get("Topic", "").lower().split(",") @@ -144,7 +139,7 @@ class PEP: # a tooltip representing the type and status "shorthand": self.shorthand, # the author list as a comma-separated with only last names - "authors": ", ".join(author.nick for author in self.authors), + "authors": ", ".join(author.full_name for author in self.authors), } @property @@ -153,7 +148,7 @@ class PEP: return { "number": self.number, "title": self.title, - "authors": ", ".join(author.nick for author in self.authors), + "authors": ", ".join(author.full_name for author in self.authors), "discussions_to": self.discussions_to, "status": self.status, "type": self.pep_type, @@ -175,41 +170,27 @@ def _raise_pep_error(pep: PEP, msg: str, pep_num: bool = False) -> None: raise PEPError(msg, pep.filename) -def _parse_authors(pep: PEP, author_header: str, authors_overrides: dict) -> list[Author]: - """Parse Author header line""" - authors_and_emails = _parse_author(author_header) - if not authors_and_emails: - raise _raise_pep_error(pep, "no authors found", pep_num=True) - return [parse_author_email(author_tuple, authors_overrides) for author_tuple in authors_and_emails] +jr_placeholder = ",Jr" -author_angled = re.compile(r"(?P<author>.+?) <(?P<email>.+?)>(,\s*)?") -author_paren = re.compile(r"(?P<email>.+?) \((?P<author>.+?)\)(,\s*)?") -author_simple = re.compile(r"(?P<author>[^,]+)(,\s*)?") - - -def _parse_author(data: str) -> list[tuple[str, str]]: +def _parse_author(data: str) -> list[_Author]: """Return a list of author names and emails.""" author_list = [] - for regex in (author_angled, author_paren, author_simple): - for match in regex.finditer(data): - # Watch out for suffixes like 'Jr.' when they are comma-separated - # from the name and thus cause issues when *all* names are only - # separated by commas. - match_dict = match.groupdict() - author = match_dict["author"] - if not author.partition(" ")[1] and author.endswith("."): - prev_author = author_list.pop() - author = ", ".join([prev_author, author]) - if "email" not in match_dict: - email = "" - else: - email = match_dict["email"] - author_list.append((author, email)) + data = (data.replace("\n", " ") + .replace(", Jr", jr_placeholder) + .rstrip().removesuffix(",")) + for author_email in data.split(", "): + if ' <' in author_email: + author, email = author_email.removesuffix(">").split(" <") + else: + author, email = author_email, "" - # If authors were found then stop searching as only expect one - # style of author citation. - if author_list: - break + author = author.strip() + if author == "": + raise ValueError("Name is empty!") + + author = author.replace(jr_placeholder, ", Jr") + email = email.lower() + author_list.append(_Author(author, email)) return author_list diff --git a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py index e2c1a7963..8fbf5cc7e 100644 --- a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py +++ b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py @@ -18,22 +18,22 @@ to allow it to be processed as normal. from __future__ import annotations import json +import os from pathlib import Path from typing import TYPE_CHECKING -from pep_sphinx_extensions.pep_zero_generator.constants import SUBINDICES_BY_TOPIC from pep_sphinx_extensions.pep_zero_generator import parser from pep_sphinx_extensions.pep_zero_generator import subindices from pep_sphinx_extensions.pep_zero_generator import writer +from pep_sphinx_extensions.pep_zero_generator.constants import SUBINDICES_BY_TOPIC if TYPE_CHECKING: from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment -def _parse_peps() -> list[parser.PEP]: +def _parse_peps(path: Path) -> list[parser.PEP]: # Read from root directory - path = Path(".") peps: list[parser.PEP] = [] for file_path in path.iterdir(): @@ -41,7 +41,7 @@ def _parse_peps() -> list[parser.PEP]: continue # Skip directories etc. if file_path.match("pep-0000*"): continue # Skip pre-existing PEP 0 files - if file_path.match("pep-????.???") and file_path.suffix in {".txt", ".rst"}: + if file_path.match("pep-????.rst"): pep = parser.PEP(path.joinpath(file_path).absolute()) peps.append(pep) @@ -52,8 +52,16 @@ def create_pep_json(peps: list[parser.PEP]) -> str: return json.dumps({pep.number: pep.full_details for pep in peps}, indent=1) +def write_peps_json(peps: list[parser.PEP], path: Path) -> None: + # Create peps.json + json_peps = create_pep_json(peps) + Path(path, "peps.json").write_text(json_peps, encoding="utf-8") + os.makedirs(os.path.join(path, "api"), exist_ok=True) + Path(path, "api", "peps.json").write_text(json_peps, encoding="utf-8") + + def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None: - peps = _parse_peps() + peps = _parse_peps(Path(app.srcdir)) pep0_text = writer.PEPZeroWriter().write_pep0(peps, builder=env.settings["builder"]) pep0_path = subindices.update_sphinx("pep-0000", pep0_text, docnames, env) @@ -61,7 +69,4 @@ def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> subindices.generate_subindices(SUBINDICES_BY_TOPIC, peps, docnames, env) - # Create peps.json - json_path = Path(app.outdir, "api", "peps.json").resolve() - json_path.parent.mkdir(exist_ok=True) - json_path.write_text(create_pep_json(peps), encoding="utf-8") + write_peps_json(peps, Path(app.outdir)) diff --git a/pep_sphinx_extensions/pep_zero_generator/subindices.py b/pep_sphinx_extensions/pep_zero_generator/subindices.py index 455a49dd8..3f61b3dd4 100644 --- a/pep_sphinx_extensions/pep_zero_generator/subindices.py +++ b/pep_sphinx_extensions/pep_zero_generator/subindices.py @@ -2,6 +2,7 @@ from __future__ import annotations +import os from pathlib import Path from typing import TYPE_CHECKING @@ -14,8 +15,7 @@ if TYPE_CHECKING: def update_sphinx(filename: str, text: str, docnames: list[str], env: BuildEnvironment) -> Path: - file_path = Path(f"{filename}.rst").resolve() - file_path.parent.mkdir(parents=True, exist_ok=True) + file_path = Path(env.srcdir, f"{filename}.rst") file_path.write_text(text, encoding="utf-8") # Add to files for builder @@ -32,6 +32,9 @@ def generate_subindices( docnames: list[str], env: BuildEnvironment, ) -> None: + # create topic directory + os.makedirs(os.path.join(env.srcdir, "topic"), exist_ok=True) + # Create sub index page generate_topic_contents(docnames, env) diff --git a/pep_sphinx_extensions/pep_zero_generator/writer.py b/pep_sphinx_extensions/pep_zero_generator/writer.py index b6171f731..69a5fe4bc 100644 --- a/pep_sphinx_extensions/pep_zero_generator/writer.py +++ b/pep_sphinx_extensions/pep_zero_generator/writer.py @@ -2,14 +2,11 @@ from __future__ import annotations -import datetime as dt from typing import TYPE_CHECKING import unicodedata -from pep_sphinx_extensions.pep_processor.transforms.pep_headers import ( - ABBREVIATED_STATUSES, - ABBREVIATED_TYPES, -) +from pep_sphinx_extensions.pep_processor.transforms.pep_headers import ABBREVIATED_STATUSES +from pep_sphinx_extensions.pep_processor.transforms.pep_headers import ABBREVIATED_TYPES from pep_sphinx_extensions.pep_zero_generator.constants import DEAD_STATUSES from pep_sphinx_extensions.pep_zero_generator.constants import STATUS_ACCEPTED from pep_sphinx_extensions.pep_zero_generator.constants import STATUS_ACTIVE @@ -29,11 +26,10 @@ from pep_sphinx_extensions.pep_zero_generator.errors import PEPError if TYPE_CHECKING: from pep_sphinx_extensions.pep_zero_generator.parser import PEP -HEADER = f"""\ +HEADER = """\ PEP: 0 Title: Index of Python Enhancement Proposals (PEPs) -Last-Modified: {dt.date.today()} -Author: python-dev <python-dev@python.org> +Author: The PEP Editors Status: Active Type: Informational Content-Type: text/x-rst @@ -149,7 +145,7 @@ class PEPZeroWriter: target = ( f"topic/{subindex}.html" if builder == "html" - else f"../topic/{subindex}" + else f"../topic/{subindex}/" ) self.emit_text(f"* `{subindex.title()} PEPs <{target}>`_") self.emit_newline() @@ -241,7 +237,7 @@ class PEPZeroWriter: self.emit_newline() self.emit_newline() - pep0_string = "\n".join([str(s) for s in self.output]) + pep0_string = "\n".join(map(str, self.output)) return pep0_string @@ -297,22 +293,22 @@ def _verify_email_addresses(peps: list[PEP]) -> dict[str, str]: for pep in peps: for author in pep.authors: # If this is the first time we have come across an author, add them. - if author.last_first not in authors_dict: - authors_dict[author.last_first] = set() + if author.full_name not in authors_dict: + authors_dict[author.full_name] = set() # If the new email is an empty string, move on. if not author.email: continue # If the email has not been seen, add it to the list. - authors_dict[author.last_first].add(author.email) + authors_dict[author.full_name].add(author.email) valid_authors_dict: dict[str, str] = {} too_many_emails: list[tuple[str, set[str]]] = [] - for last_first, emails in authors_dict.items(): + for full_name, emails in authors_dict.items(): if len(emails) > 1: - too_many_emails.append((last_first, emails)) + too_many_emails.append((full_name, emails)) else: - valid_authors_dict[last_first] = next(iter(emails), "") + valid_authors_dict[full_name] = next(iter(emails), "") if too_many_emails: err_output = [] for author, emails in too_many_emails: diff --git a/pep_sphinx_extensions/tests/conftest.py b/pep_sphinx_extensions/tests/conftest.py new file mode 100644 index 000000000..15f3c907b --- /dev/null +++ b/pep_sphinx_extensions/tests/conftest.py @@ -0,0 +1,12 @@ +import importlib.util +import sys +from pathlib import Path + +_ROOT_PATH = Path(__file__, "..", "..", "..").resolve() +PEP_ROOT = _ROOT_PATH / "peps" + +# Import "check-peps.py" as "check_peps" +CHECK_PEPS_PATH = _ROOT_PATH / "check-peps.py" +spec = importlib.util.spec_from_file_location("check_peps", CHECK_PEPS_PATH) +sys.modules["check_peps"] = check_peps = importlib.util.module_from_spec(spec) +spec.loader.exec_module(check_peps) diff --git a/pep_sphinx_extensions/tests/pep_lint/__init__.py b/pep_sphinx_extensions/tests/pep_lint/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pep_sphinx_extensions/tests/pep_lint/test_date.py b/pep_sphinx_extensions/tests/pep_lint/test_date.py new file mode 100644 index 000000000..2218fc2a0 --- /dev/null +++ b/pep_sphinx_extensions/tests/pep_lint/test_date.py @@ -0,0 +1,105 @@ +import datetime as dt + +import check_peps # NoQA: inserted into sys.modules in conftest.py +import pytest + + +@pytest.mark.parametrize( + "line", + [ + # valid entries + "01-Jan-2000", + "29-Feb-2016", + "31-Dec-2000", + "01-Apr-2003", + "01-Apr-2007", + "01-Apr-2009", + "01-Jan-1990", + ], +) +def test_validate_created(line: str): + warnings = [warning for (_, warning) in check_peps._validate_created(1, line)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "date_str", + [ + # valid entries + "01-Jan-2000", + "29-Feb-2016", + "31-Dec-2000", + "01-Apr-2003", + "01-Apr-2007", + "01-Apr-2009", + "01-Jan-1990", + ], +) +def test_date_checker_valid(date_str: str): + warnings = [warning for (_, warning) in check_peps._date(1, date_str, "<Prefix>")] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "date_str", + [ + # malformed + "2000-01-01", + "01 January 2000", + "1 Jan 2000", + "1-Jan-2000", + "1-January-2000", + "Jan-1-2000", + "January 1 2000", + "January 01 2000", + "01/01/2000", + "01/Jan/2000", # 🇬🇧, 🇦🇺, 🇨🇦, 🇳🇿, 🇮🇪 , ... + "Jan/01/2000", # 🇺🇸 + "1st January 2000", + "The First day of January in the year of Our Lord Two Thousand", + "Jan, 1, 2000", + "2000-Jan-1", + "2000-Jan-01", + "2000-January-1", + "2000-January-01", + "00 Jan 2000", + "00-Jan-2000", + ], +) +def test_date_checker_malformed(date_str: str): + warnings = [warning for (_, warning) in check_peps._date(1, date_str, "<Prefix>")] + expected = f"<Prefix> must be a 'DD-mmm-YYYY' date: {date_str!r}" + assert warnings == [expected], warnings + + +@pytest.mark.parametrize( + "date_str", + [ + # too early + "31-Dec-1989", + "01-Apr-1916", + "01-Jan-0020", + "01-Jan-0023", + ], +) +def test_date_checker_too_early(date_str: str): + warnings = [warning for (_, warning) in check_peps._date(1, date_str, "<Prefix>")] + expected = f"<Prefix> must not be before Python was invented: {date_str!r}" + assert warnings == [expected], warnings + + +@pytest.mark.parametrize( + "date_str", + [ + # the future + "31-Dec-2999", + "01-Jan-2042", + "01-Jan-2100", + (dt.datetime.now() + dt.timedelta(days=15)).strftime("%d-%b-%Y"), + (dt.datetime.now() + dt.timedelta(days=100)).strftime("%d-%b-%Y"), + ], +) +def test_date_checker_too_late(date_str: str): + warnings = [warning for (_, warning) in check_peps._date(1, date_str, "<Prefix>")] + expected = f"<Prefix> must not be in the future: {date_str!r}" + assert warnings == [expected], warnings diff --git a/pep_sphinx_extensions/tests/pep_lint/test_direct_links.py b/pep_sphinx_extensions/tests/pep_lint/test_direct_links.py new file mode 100644 index 000000000..66edc0552 --- /dev/null +++ b/pep_sphinx_extensions/tests/pep_lint/test_direct_links.py @@ -0,0 +1,30 @@ +import check_peps # NoQA: inserted into sys.modules in conftest.py +import pytest + + +@pytest.mark.parametrize( + "line", + [ + "http://www.python.org/dev/peps/pep-0000/", + "https://www.python.org/dev/peps/pep-0000/", + "http://peps.python.org/pep-0000/", + "https://peps.python.org/pep-0000/", + ], +) +def test_check_direct_links_pep(line: str): + warnings = [warning for (_, warning) in check_peps.check_direct_links(1, line)] + assert warnings == ["Use the :pep:`NNN` role to refer to PEPs"], warnings + + +@pytest.mark.parametrize( + "line", + [ + "http://www.rfc-editor.org/rfc/rfc2324", + "https://www.rfc-editor.org/rfc/rfc2324", + "http://datatracker.ietf.org/doc/html/rfc2324", + "https://datatracker.ietf.org/doc/html/rfc2324", + ], +) +def test_check_direct_links_rfc(line: str): + warnings = [warning for (_, warning) in check_peps.check_direct_links(1, line)] + assert warnings == ["Use the :rfc:`NNN` role to refer to RFCs"], warnings diff --git a/pep_sphinx_extensions/tests/pep_lint/test_email.py b/pep_sphinx_extensions/tests/pep_lint/test_email.py new file mode 100644 index 000000000..d9f19592d --- /dev/null +++ b/pep_sphinx_extensions/tests/pep_lint/test_email.py @@ -0,0 +1,238 @@ +import check_peps # NoQA: inserted into sys.modules in conftest.py +import pytest + + +@pytest.mark.parametrize( + "line", + [ + "Alice", + "Alice,", + "Alice, Bob, Charlie", + "Alice,\nBob,\nCharlie", + "Alice,\n Bob,\n Charlie", + "Alice,\n Bob,\n Charlie", + "Cardinal Ximénez", + "Alice <alice@domain.example>", + "Cardinal Ximénez <Cardinal.Ximenez@spanish.inquisition>", + ], + ids=repr, # the default calls str and renders newlines. +) +def test_validate_author(line: str): + warnings = [warning for (_, warning) in check_peps._validate_author(1, line)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "Alice,\n Bob,\n Charlie", + "Alice,\n Bob,\n Charlie", + "Alice,\n Bob,\n Charlie", + "Alice,\n Bob", + ], + ids=repr, # the default calls str and renders newlines. +) +def test_validate_author_over__indented(line: str): + warnings = [warning for (_, warning) in check_peps._validate_author(1, line)] + assert {*warnings} == {"Author line must not be over-indented"}, warnings + + +@pytest.mark.parametrize( + "line", + [ + "Cardinal Ximénez\nCardinal Biggles\nCardinal Fang", + "Cardinal Ximénez,\nCardinal Biggles\nCardinal Fang", + "Cardinal Ximénez\nCardinal Biggles,\nCardinal Fang", + ], + ids=repr, # the default calls str and renders newlines. +) +def test_validate_author_continuation(line: str): + warnings = [warning for (_, warning) in check_peps._validate_author(1, line)] + assert {*warnings} == {"Author continuation lines must end with a comma"}, warnings + + +@pytest.mark.parametrize( + "line", + [ + "Alice", + "Cardinal Ximénez", + "Alice <alice@domain.example>", + "Cardinal Ximénez <Cardinal.Ximenez@spanish.inquisition>", + ], +) +def test_validate_sponsor(line: str): + warnings = [warning for (_, warning) in check_peps._validate_sponsor(1, line)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "", + "Alice, Bob, Charlie", + "Alice, Bob, Charlie,", + "Alice <alice@domain.example>", + "Cardinal Ximénez <Cardinal.Ximenez@spanish.inquisition>", + ], +) +def test_validate_delegate(line: str): + warnings = [warning for (_, warning) in check_peps._validate_delegate(1, line)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + ("email", "expected_warnings"), + [ + # ... entries must not contain multiple '...' + ("Cardinal Ximénez <<", {"multiple <"}), + ("Cardinal Ximénez <<<", {"multiple <"}), + ("Cardinal Ximénez >>", {"multiple >"}), + ("Cardinal Ximénez >>>", {"multiple >"}), + ("Cardinal Ximénez <<<>>>", {"multiple <", "multiple >"}), + ("Cardinal Ximénez @@", {"multiple @"}), + ("Cardinal Ximénez <<@@@>", {"multiple <", "multiple @"}), + ("Cardinal Ximénez <@@@>>", {"multiple >", "multiple @"}), + ("Cardinal Ximénez <<@@>>", {"multiple <", "multiple >", "multiple @"}), + # valid names + ("Cardinal Ximénez", set()), + (" Cardinal Ximénez", set()), + ("\t\tCardinal Ximénez", set()), + ("Cardinal Ximénez ", set()), + ("Cardinal Ximénez\t\t", set()), + ("Cardinal O'Ximénez", set()), + ("Cardinal Ximénez, Inquisitor", set()), + ("Cardinal Ximénez-Biggles", set()), + ("Cardinal Ximénez-Biggles, Inquisitor", set()), + ("Cardinal T. S. I. Ximénez", set()), + # ... entries must have a valid 'Name' + ("Cardinal_Ximénez", {"valid name"}), + ("Cardinal Ximénez 3", {"valid name"}), + ("~ Cardinal Ximénez ~", {"valid name"}), + ("Cardinal Ximénez!", {"valid name"}), + ("@Cardinal Ximénez", {"valid name"}), + ("Cardinal_Ximénez <>", {"valid name"}), + ("Cardinal Ximénez 3 <>", {"valid name"}), + ("~ Cardinal Ximénez ~ <>", {"valid name"}), + ("Cardinal Ximénez! <>", {"valid name"}), + ("@Cardinal Ximénez <>", {"valid name"}), + # ... entries must be formatted as 'Name <email@example.com>' + ("Cardinal Ximénez<>", {"name <email>"}), + ("Cardinal Ximénez<", {"name <email>"}), + ("Cardinal Ximénez <", {"name <email>"}), + ("Cardinal Ximénez <", {"name <email>"}), + ("Cardinal Ximénez <>", {"name <email>"}), + # ... entries must contain a valid email address (missing) + ("Cardinal Ximénez <>", {"valid email"}), + ("Cardinal Ximénez <> ", {"valid email"}), + ("Cardinal Ximénez <@> ", {"valid email"}), + ("Cardinal Ximénez <at> ", {"valid email"}), + ("Cardinal Ximénez < at > ", {"valid email"}), + # ... entries must contain a valid email address (local) + ("Cardinal Ximénez <Cardinal.Ximénez@spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal.Ximénez at spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal.Ximenez AT spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal.Ximenez @spanish.inquisition> ", {"valid email"}), + ("Cardinal Ximénez <Cardinal Ximenez@spanish.inquisition> ", {"valid email"}), + ("Cardinal Ximénez < Cardinal Ximenez @spanish.inquisition> ", {"valid email"}), + ("Cardinal Ximénez <(Cardinal.Ximenez)@spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal,Ximenez@spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal:Ximenez@spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal;Ximenez@spanish.inquisition>", {"valid email"}), + ( + "Cardinal Ximénez <Cardinal><Ximenez@spanish.inquisition>", + {"multiple <", "multiple >", "valid email"}, + ), + ( + "Cardinal Ximénez <Cardinal@Ximenez@spanish.inquisition>", + {"multiple @", "valid email"}, + ), + (r"Cardinal Ximénez <Cardinal\Ximenez@spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <[Cardinal.Ximenez]@spanish.inquisition>", {"valid email"}), + ('Cardinal Ximénez <"Cardinal"Ximenez"@spanish.inquisition>', {"valid email"}), + ("Cardinal Ximenez <Cardinal;Ximenez@spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal£Ximénez@spanish.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal§Ximenez@spanish.inquisition>", {"valid email"}), + # ... entries must contain a valid email address (domain) + ( + "Cardinal Ximénez <Cardinal.Ximenez@spanish+american.inquisition>", + {"valid email"}, + ), + ("Cardinal Ximénez <Cardinal.Ximenez@spani$h.inquisition>", {"valid email"}), + ("Cardinal Ximénez <Cardinal.Ximenez@spanish.inquisitioñ>", {"valid email"}), + ( + "Cardinal Ximénez <Cardinal.Ximenez@th£.spanish.inquisition>", + {"valid email"}, + ), + # valid name-emails + ("Cardinal Ximénez <Cardinal.Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal.Ximenez at spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal_Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal-Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal!Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal#Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal$Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal%Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal&Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal'Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal*Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal+Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal/Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal=Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal?Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal^Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <{Cardinal.Ximenez}@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal|Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal~Ximenez@spanish.inquisition>", set()), + ("Cardinal Ximénez <Cardinal.Ximenez@español.inquisition>", set()), + ("Cardinal Ximénez <Cardinal.Ximenez at español.inquisition>", set()), + ("Cardinal Ximénez <Cardinal.Ximenez@spanish-american.inquisition>", set()), + ], + # call str() on each parameterised value in the test ID. + ids=str, +) +def test_email_checker(email: str, expected_warnings: set): + warnings = [warning for (_, warning) in check_peps._email(1, email, "<Prefix>")] + + found_warnings = set() + email = email.strip() + + if "multiple <" in expected_warnings: + found_warnings.add("multiple <") + expected = f"<Prefix> entries must not contain multiple '<': {email!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "multiple >" in expected_warnings: + found_warnings.add("multiple >") + expected = f"<Prefix> entries must not contain multiple '>': {email!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "multiple @" in expected_warnings: + found_warnings.add("multiple @") + expected = f"<Prefix> entries must not contain multiple '@': {email!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "valid name" in expected_warnings: + found_warnings.add("valid name") + expected = f"<Prefix> entries must begin with a valid 'Name': {email!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "name <email>" in expected_warnings: + found_warnings.add("name <email>") + expected = f"<Prefix> entries must be formatted as 'Name <email@example.com>': {email!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "valid email" in expected_warnings: + found_warnings.add("valid email") + expected = f"<Prefix> entries must contain a valid email address: {email!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if expected_warnings == set(): + assert warnings == [], warnings + + assert found_warnings == expected_warnings diff --git a/pep_sphinx_extensions/tests/pep_lint/test_headers.py b/pep_sphinx_extensions/tests/pep_lint/test_headers.py new file mode 100644 index 000000000..227eb5397 --- /dev/null +++ b/pep_sphinx_extensions/tests/pep_lint/test_headers.py @@ -0,0 +1,408 @@ +import check_peps # NoQA: inserted into sys.modules in conftest.py +import pytest + + +@pytest.mark.parametrize( + ("test_input", "expected"), + [ + # capitalisation + ("Header:", "Header"), + ("header:", "header"), + ("hEADER:", "hEADER"), + ("hEaDeR:", "hEaDeR"), + # trailing spaces + ("Header: ", "Header"), + ("Header: ", "Header"), + ("Header: \t", "Header"), + # trailing content + ("Header: Text", "Header"), + ("Header: 123", "Header"), + ("Header: !", "Header"), + # separators + ("Hyphenated-Header:", "Hyphenated-Header"), + ], +) +def test_header_pattern(test_input, expected): + assert check_peps.HEADER_PATTERN.match(test_input)[1] == expected + + +@pytest.mark.parametrize( + "test_input", + [ + # trailing content + "Header:Text", + "Header:123", + "Header:!", + # colon position + "Header", + "Header : ", + "Header :", + "SemiColonHeader;", + # separators + "Underscored_Header:", + "Spaced Header:", + "Plus+Header:", + ], +) +def test_header_pattern_no_match(test_input): + assert check_peps.HEADER_PATTERN.match(test_input) is None + + +def test_validate_required_headers(): + found_headers = dict.fromkeys( + ("PEP", "Title", "Author", "Status", "Type", "Created") + ) + warnings = [ + warning for (_, warning) in check_peps._validate_required_headers(found_headers) + ] + assert warnings == [], warnings + + +def test_validate_required_headers_missing(): + found_headers = dict.fromkeys(("PEP", "Title", "Author", "Type")) + warnings = [ + warning for (_, warning) in check_peps._validate_required_headers(found_headers) + ] + assert warnings == [ + "Must have required header: Status", + "Must have required header: Created", + ], warnings + + +def test_validate_required_headers_order(): + found_headers = dict.fromkeys( + ("PEP", "Title", "Sponsor", "Author", "Type", "Status", "Replaces", "Created") + ) + warnings = [ + warning for (_, warning) in check_peps._validate_required_headers(found_headers) + ] + assert warnings == [ + "Headers must be in PEP 12 order. Correct order: PEP, Title, Author, Sponsor, Status, Type, Created, Replaces" + ], warnings + + +@pytest.mark.parametrize( + "line", + [ + "!", + "The Zen of Python", + "A title that is exactly 79 characters long, but shorter than 80 characters long", + ], +) +def test_validate_title(line: str): + warnings = [warning for (_, warning) in check_peps._validate_title(1, line)] + assert warnings == [], warnings + + +def test_validate_title_blank(): + warnings = [warning for (_, warning) in check_peps._validate_title(1, "-" * 80)] + assert warnings == ["PEP title must be less than 80 characters"], warnings + + +def test_validate_title_too_long(): + warnings = [warning for (_, warning) in check_peps._validate_title(1, "")] + assert warnings == ["PEP must have a title"], warnings + + +@pytest.mark.parametrize( + "line", + [ + "Accepted", + "Active", + "April Fool!", + "Deferred", + "Draft", + "Final", + "Provisional", + "Rejected", + "Superseded", + "Withdrawn", + ], +) +def test_validate_status_valid(line: str): + warnings = [warning for (_, warning) in check_peps._validate_status(1, line)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "Standards Track", + "Informational", + "Process", + "accepted", + "active", + "april fool!", + "deferred", + "draft", + "final", + "provisional", + "rejected", + "superseded", + "withdrawn", + ], +) +def test_validate_status_invalid(line: str): + warnings = [warning for (_, warning) in check_peps._validate_status(1, line)] + assert warnings == ["Status must be a valid PEP status"], warnings + + +@pytest.mark.parametrize( + "line", + [ + "Standards Track", + "Informational", + "Process", + ], +) +def test_validate_type_valid(line: str): + warnings = [warning for (_, warning) in check_peps._validate_type(1, line)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "standards track", + "informational", + "process", + "Accepted", + "Active", + "April Fool!", + "Deferred", + "Draft", + "Final", + "Provisional", + "Rejected", + "Superseded", + "Withdrawn", + ], +) +def test_validate_type_invalid(line: str): + warnings = [warning for (_, warning) in check_peps._validate_type(1, line)] + assert warnings == ["Type must be a valid PEP type"], warnings + + +@pytest.mark.parametrize( + ("line", "expected_warnings"), + [ + # valid entries + ("Governance", set()), + ("Packaging", set()), + ("Typing", set()), + ("Release", set()), + ("Governance, Packaging", set()), + ("Packaging, Typing", set()), + # duplicates + ("Governance, Governance", {"duplicates"}), + ("Release, Release", {"duplicates"}), + ("Packaging, Packaging", {"duplicates"}), + ("Spam, Spam", {"duplicates", "valid"}), + ("lobster, lobster", {"duplicates", "capitalisation", "valid"}), + ("governance, governance", {"duplicates", "capitalisation"}), + # capitalisation + ("governance", {"capitalisation"}), + ("packaging", {"capitalisation"}), + ("typing", {"capitalisation"}), + ("release", {"capitalisation"}), + ("Governance, release", {"capitalisation"}), + # validity + ("Spam", {"valid"}), + ("lobster", {"capitalisation", "valid"}), + # sorted + ("Packaging, Governance", {"sorted"}), + ("Typing, Release", {"sorted"}), + ("Release, Governance", {"sorted"}), + ("spam, packaging", {"capitalisation", "valid", "sorted"}), + ], + # call str() on each parameterised value in the test ID. + ids=str, +) +def test_validate_topic(line: str, expected_warnings: set): + warnings = [warning for (_, warning) in check_peps._validate_topic(1, line)] + + found_warnings = set() + + if "duplicates" in expected_warnings: + found_warnings.add("duplicates") + expected = "Topic must not contain duplicates" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "capitalisation" in expected_warnings: + found_warnings.add("capitalisation") + expected = "Topic must be properly capitalised (Title Case)" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "valid" in expected_warnings: + found_warnings.add("valid") + expected = "Topic must be for a valid sub-index" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "sorted" in expected_warnings: + found_warnings.add("sorted") + expected = "Topic must be sorted lexicographically" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if expected_warnings == set(): + assert warnings == [], warnings + + assert found_warnings == expected_warnings + + +def test_validate_content_type_valid(): + warnings = [ + warning for (_, warning) in check_peps._validate_content_type(1, "text/x-rst") + ] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "text/plain", + "text/markdown", + "text/csv", + "text/rtf", + "text/javascript", + "text/html", + "text/xml", + ], +) +def test_validate_content_type_invalid(line: str): + warnings = [warning for (_, warning) in check_peps._validate_content_type(1, line)] + assert warnings == ["Content-Type must be 'text/x-rst'"], warnings + + +@pytest.mark.parametrize( + "line", + [ + "0, 1, 8, 12, 20,", + "101, 801,", + "3099, 9999", + ], +) +def test_validate_pep_references(line: str): + warnings = [ + warning for (_, warning) in check_peps._validate_pep_references(1, line) + ] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "0,1,8, 12, 20,", + "101,801,", + "3099, 9998,9999", + ], +) +def test_validate_pep_references_separators(line: str): + warnings = [ + warning for (_, warning) in check_peps._validate_pep_references(1, line) + ] + assert warnings == [ + "PEP references must be separated by comma-spaces (', ')" + ], warnings + + +@pytest.mark.parametrize( + ("line", "expected_warnings"), + [ + # valid entries + ("1.0, 2.4, 2.7, 2.8, 3.0, 3.1, 3.4, 3.7, 3.11, 3.14", set()), + ("2.x", set()), + ("3.x", set()), + ("3.0.1", set()), + # segments + ("", {"segments"}), + ("1", {"segments"}), + ("1.2.3.4", {"segments"}), + # major + ("0.0", {"major"}), + ("4.0", {"major"}), + ("9.0", {"major"}), + # minor number + ("3.a", {"minor numeric"}), + ("3.spam", {"minor numeric"}), + ("3.0+", {"minor numeric"}), + ("3.0-9", {"minor numeric"}), + ("9.Z", {"major", "minor numeric"}), + # minor leading zero + ("3.01", {"minor zero"}), + ("0.00", {"major", "minor zero"}), + # micro empty + ("3.x.1", {"micro empty"}), + ("9.x.1", {"major", "micro empty"}), + # micro leading zero + ("3.3.0", {"micro zero"}), + ("3.3.00", {"micro zero"}), + ("3.3.01", {"micro zero"}), + ("3.0.0", {"micro zero"}), + ("3.00.0", {"minor zero", "micro zero"}), + ("0.00.0", {"major", "minor zero", "micro zero"}), + # micro number + ("3.0.a", {"micro numeric"}), + ("0.3.a", {"major", "micro numeric"}), + ], + # call str() on each parameterised value in the test ID. + ids=str, +) +def test_validate_python_version(line: str, expected_warnings: set): + warnings = [ + warning for (_, warning) in check_peps._validate_python_version(1, line) + ] + + found_warnings = set() + + if "segments" in expected_warnings: + found_warnings.add("segments") + expected = f"Python-Version must have two or three segments: {line}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "major" in expected_warnings: + found_warnings.add("major") + expected = f"Python-Version major part must be 1, 2, or 3: {line}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "minor numeric" in expected_warnings: + found_warnings.add("minor numeric") + expected = f"Python-Version minor part must be numeric: {line}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "minor zero" in expected_warnings: + found_warnings.add("minor zero") + expected = f"Python-Version minor part must not have leading zeros: {line}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "micro empty" in expected_warnings: + found_warnings.add("micro empty") + expected = ( + f"Python-Version micro part must be empty if minor part is 'x': {line}" + ) + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "micro zero" in expected_warnings: + found_warnings.add("micro zero") + expected = f"Python-Version micro part must not have leading zeros: {line}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "micro numeric" in expected_warnings: + found_warnings.add("micro numeric") + expected = f"Python-Version micro part must be numeric: {line}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if expected_warnings == set(): + assert warnings == [], warnings + + assert found_warnings == expected_warnings diff --git a/pep_sphinx_extensions/tests/pep_lint/test_pep_lint.py b/pep_sphinx_extensions/tests/pep_lint/test_pep_lint.py new file mode 100644 index 000000000..2c52fa397 --- /dev/null +++ b/pep_sphinx_extensions/tests/pep_lint/test_pep_lint.py @@ -0,0 +1,48 @@ +from pathlib import Path + +import check_peps # NoQA: inserted into sys.modules in conftest.py + +PEP_9002 = Path(__file__).parent.parent / "peps" / "pep-9002.rst" + + +def test_with_fake_pep(): + content = PEP_9002.read_text(encoding="utf-8").splitlines() + warnings = list(check_peps.check_peps(PEP_9002, content)) + assert warnings == [ + (1, "PEP must begin with the 'PEP:' header"), + (9, "Must not have duplicate header: Sponsor "), + (10, "Must not have invalid header: Horse-Guards"), + (1, "Must have required header: PEP"), + (1, "Must have required header: Type"), + ( + 1, + "Headers must be in PEP 12 order. Correct order: Title, Version, " + "Author, Sponsor, BDFL-Delegate, Discussions-To, Status, Topic, " + "Content-Type, Requires, Created, Python-Version, Post-History, " + "Resolution", + ), + (4, "Author continuation lines must end with a comma"), + (5, "Author line must not be over-indented"), + (7, "Python-Version major part must be 1, 2, or 3: 4.0"), + ( + 8, + "Sponsor entries must begin with a valid 'Name': " + r"'Sponsor:\nHorse-Guards: Parade'", + ), + (11, "Created must be a 'DD-mmm-YYYY' date: '1-Jan-1989'"), + (12, "Delegate entries must begin with a valid 'Name': 'Barry!'"), + (13, "Status must be a valid PEP status"), + (14, "Topic must not contain duplicates"), + (14, "Topic must be properly capitalised (Title Case)"), + (14, "Topic must be for a valid sub-index"), + (14, "Topic must be sorted lexicographically"), + (15, "Content-Type must be 'text/x-rst'"), + (16, "PEP references must be separated by comma-spaces (', ')"), + (17, "Discussions-To must be a valid thread URL or mailing list"), + (18, "Post-History must be a 'DD-mmm-YYYY' date: '2-Feb-2000'"), + (18, "Post-History must be a valid thread URL"), + (19, "Post-History must be a 'DD-mmm-YYYY' date: '3-Mar-2001'"), + (19, "Post-History must be a valid thread URL"), + (20, "Resolution must be a valid thread URL"), + (23, "Use the :pep:`NNN` role to refer to PEPs"), + ] diff --git a/pep_sphinx_extensions/tests/pep_lint/test_pep_number.py b/pep_sphinx_extensions/tests/pep_lint/test_pep_number.py new file mode 100644 index 000000000..3443f7144 --- /dev/null +++ b/pep_sphinx_extensions/tests/pep_lint/test_pep_number.py @@ -0,0 +1,108 @@ +import check_peps # NoQA: inserted into sys.modules in conftest.py +import pytest + + +@pytest.mark.parametrize( + "line", + [ + "PEP: 0", + "PEP: 12", + ], +) +def test_validate_pep_number(line: str): + warnings = [warning for (_, warning) in check_peps._validate_pep_number(line)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "0", + "PEP:12", + "PEP 0", + "PEP 12", + "PEP:0", + ], +) +def test_validate_pep_number_invalid_header(line: str): + warnings = [warning for (_, warning) in check_peps._validate_pep_number(line)] + assert warnings == ["PEP must begin with the 'PEP:' header"], warnings + + +@pytest.mark.parametrize( + ("pep_number", "expected_warnings"), + [ + # valid entries + ("0", set()), + ("1", set()), + ("12", set()), + ("20", set()), + ("101", set()), + ("801", set()), + ("3099", set()), + ("9999", set()), + # empty + ("", {"not blank"}), + # leading zeros + ("01", {"leading zeros"}), + ("001", {"leading zeros"}), + ("0001", {"leading zeros"}), + ("00001", {"leading zeros"}), + # non-numeric + ("a", {"non-numeric"}), + ("123abc", {"non-numeric"}), + ("0123A", {"leading zeros", "non-numeric"}), + ("0", {"non-numeric"}), + ("101", {"non-numeric"}), + ("9999", {"non-numeric"}), + ("𝟎", {"non-numeric"}), + ("𝟘", {"non-numeric"}), + ("𝟏𝟚", {"non-numeric"}), + ("𝟸𝟬", {"non-numeric"}), + ("-1", {"non-numeric"}), + ("+1", {"non-numeric"}), + # out of bounds + ("10000", {"range"}), + ("54321", {"range"}), + ("99999", {"range"}), + ("32768", {"range"}), + ], + # call str() on each parameterised value in the test ID. + ids=str, +) +def test_pep_num_checker(pep_number: str, expected_warnings: set): + warnings = [ + warning for (_, warning) in check_peps._pep_num(1, pep_number, "<Prefix>") + ] + + found_warnings = set() + pep_number = pep_number.strip() + + if "not blank" in expected_warnings: + found_warnings.add("not blank") + expected = f"<Prefix> must not be blank: {pep_number!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "leading zeros" in expected_warnings: + found_warnings.add("leading zeros") + expected = f"<Prefix> must not contain leading zeros: {pep_number!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "non-numeric" in expected_warnings: + found_warnings.add("non-numeric") + expected = f"<Prefix> must be numeric: {pep_number!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if "range" in expected_warnings: + found_warnings.add("range") + expected = f"<Prefix> must be between 0 and 9999: {pep_number!r}" + matching = [w for w in warnings if w == expected] + assert matching == [expected], warnings + + if expected_warnings == set(): + assert warnings == [], warnings + + assert found_warnings == expected_warnings diff --git a/pep_sphinx_extensions/tests/pep_lint/test_post_url.py b/pep_sphinx_extensions/tests/pep_lint/test_post_url.py new file mode 100644 index 000000000..982f4612e --- /dev/null +++ b/pep_sphinx_extensions/tests/pep_lint/test_post_url.py @@ -0,0 +1,305 @@ +import check_peps # NoQA: inserted into sys.modules in conftest.py +import pytest + + +@pytest.mark.parametrize( + "line", + [ + "list-name@python.org", + "distutils-sig@python.org", + "csv@python.org", + "python-3000@python.org", + "ipaddr-py-dev@googlegroups.com", + "python-tulip@googlegroups.com", + "https://discuss.python.org/t/thread-name/123456", + "https://discuss.python.org/t/thread-name/123456/", + "https://discuss.python.org/t/thread_name/123456", + "https://discuss.python.org/t/thread_name/123456/", + "https://discuss.python.org/t/123456/", + "https://discuss.python.org/t/123456", + ], +) +def test_validate_discussions_to_valid(line: str): + warnings = [ + warning for (_, warning) in check_peps._validate_discussions_to(1, line) + ] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "$pecial+chars@python.org", + "a-discussions-to-list!@googlegroups.com", + ], +) +def test_validate_discussions_to_list_name(line: str): + warnings = [ + warning for (_, warning) in check_peps._validate_discussions_to(1, line) + ] + assert warnings == ["Discussions-To must be a valid mailing list"], warnings + + +@pytest.mark.parametrize( + "line", + [ + "list-name@python.org.uk", + "distutils-sig@mail-server.example", + ], +) +def test_validate_discussions_to_invalid_list_domain(line: str): + warnings = [ + warning for (_, warning) in check_peps._validate_discussions_to(1, line) + ] + assert warnings == [ + "Discussions-To must be a valid thread URL or mailing list" + ], warnings + + +@pytest.mark.parametrize( + "body", + [ + "", + ( + "01-Jan-2001, 02-Feb-2002,\n " + "03-Mar-2003, 04-Apr-2004,\n " + "05-May-2005," + ), + ( + "`01-Jan-2000 <https://mail.python.org/pipermail/list-name/0000-Month/0123456.html>`__,\n " + "`11-Mar-2005 <https://mail.python.org/archives/list/list-name@python.org/thread/abcdef0123456789/>`__,\n " + "`21-May-2010 <https://discuss.python.org/t/thread-name/123456/654321>`__,\n " + "`31-Jul-2015 <https://discuss.python.org/t/123456>`__," + ), + "01-Jan-2001, `02-Feb-2002 <https://discuss.python.org/t/123456>`__,\n03-Mar-2003", + ], +) +def test_validate_post_history_valid(body: str): + warnings = [warning for (_, warning) in check_peps._validate_post_history(1, body)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123/", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123/", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123#Anchor", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123/#Anchor", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123#Anchor123", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123/#Anchor123", + ], +) +def test_validate_resolution_valid(line: str): + warnings = [warning for (_, warning) in check_peps._validate_resolution(1, line)] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "line", + [ + "https://mail.python.org/archives/list/list-name@python.org/thread", + "https://mail.python.org/archives/list/list-name@python.org/message", + "https://mail.python.org/archives/list/list-name@python.org/thread/", + "https://mail.python.org/archives/list/list-name@python.org/message/", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123#anchor", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123/#anchor", + "https://mail.python.org/archives/list/list-name@python.org/message/#abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/message/#abcXYZ123/", + "https://mail.python.org/archives/list/list-name@python.org/spam/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/spam/abcXYZ123/", + ], +) +def test_validate_resolution_invalid(line: str): + warnings = [warning for (_, warning) in check_peps._validate_resolution(1, line)] + assert warnings == ["Resolution must be a valid thread URL"], warnings + + +@pytest.mark.parametrize( + "thread_url", + [ + "https://discuss.python.org/t/thread-name/123456", + "https://discuss.python.org/t/thread-name/123456/", + "https://discuss.python.org/t/thread_name/123456", + "https://discuss.python.org/t/thread_name/123456/", + "https://discuss.python.org/t/thread-name/123456/654321/", + "https://discuss.python.org/t/thread-name/123456/654321", + "https://discuss.python.org/t/123456", + "https://discuss.python.org/t/123456/", + "https://discuss.python.org/t/123456/654321/", + "https://discuss.python.org/t/123456/654321", + "https://discuss.python.org/t/1", + "https://discuss.python.org/t/1/", + "https://mail.python.org/pipermail/list-name/0000-Month/0123456.html", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123/", + ], +) +def test_thread_checker_valid(thread_url: str): + warnings = [ + warning for (_, warning) in check_peps._thread(1, thread_url, "<Prefix>") + ] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "thread_url", + [ + "http://link.example", + "list-name@python.org", + "distutils-sig@python.org", + "csv@python.org", + "python-3000@python.org", + "ipaddr-py-dev@googlegroups.com", + "python-tulip@googlegroups.com", + "https://link.example", + "https://discuss.python.org", + "https://discuss.python.org/", + "https://discuss.python.org/c/category", + "https://discuss.python.org/t/thread_name/123456//", + "https://discuss.python.org/t/thread+name/123456", + "https://discuss.python.org/t/thread+name/123456#", + "https://discuss.python.org/t/thread+name/123456/#", + "https://discuss.python.org/t/thread+name/123456/#anchor", + "https://discuss.python.org/t/thread+name/", + "https://discuss.python.org/t/thread+name", + "https://discuss.python.org/t/thread-name/123abc", + "https://discuss.python.org/t/thread-name/123abc/", + "https://discuss.python.org/t/thread-name/123456/123abc", + "https://discuss.python.org/t/thread-name/123456/123abc/", + "https://discuss.python.org/t/123/456/789", + "https://discuss.python.org/t/123/456/789/", + "https://discuss.python.org/t/#/", + "https://discuss.python.org/t/#", + "https://mail.python.org/pipermail/list+name/0000-Month/0123456.html", + "https://mail.python.org/pipermail/list-name/YYYY-Month/0123456.html", + "https://mail.python.org/pipermail/list-name/0123456/0123456.html", + "https://mail.python.org/pipermail/list-name/0000-Month/0123456", + "https://mail.python.org/pipermail/list-name/0000-Month/0123456/", + "https://mail.python.org/pipermail/list-name/0000-Month/", + "https://mail.python.org/pipermail/list-name/0000-Month", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123#anchor", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123/#anchor", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123/", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123#anchor", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123/#anchor", + "https://mail.python.org/archives/list/list-name@python.org/spam/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/spam/abcXYZ123/", + ], +) +def test_thread_checker_invalid(thread_url: str): + warnings = [ + warning for (_, warning) in check_peps._thread(1, thread_url, "<Prefix>") + ] + assert warnings == ["<Prefix> must be a valid thread URL"], warnings + + +@pytest.mark.parametrize( + "thread_url", + [ + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123/", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123/", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123#Anchor", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123/#Anchor", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123#Anchor123", + "https://mail.python.org/archives/list/list-name@python.org/message/abcXYZ123/#Anchor123", + ], +) +def test_thread_checker_valid_allow_message(thread_url: str): + warnings = [ + warning + for (_, warning) in check_peps._thread( + 1, thread_url, "<Prefix>", allow_message=True + ) + ] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "thread_url", + [ + "https://mail.python.org/archives/list/list-name@python.org/thread", + "https://mail.python.org/archives/list/list-name@python.org/message", + "https://mail.python.org/archives/list/list-name@python.org/thread/", + "https://mail.python.org/archives/list/list-name@python.org/message/", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123#anchor", + "https://mail.python.org/archives/list/list-name@python.org/thread/abcXYZ123/#anchor", + "https://mail.python.org/archives/list/list-name@python.org/message/#abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/message/#abcXYZ123/", + "https://mail.python.org/archives/list/list-name@python.org/spam/abcXYZ123", + "https://mail.python.org/archives/list/list-name@python.org/spam/abcXYZ123/", + ], +) +def test_thread_checker_invalid_allow_message(thread_url: str): + warnings = [ + warning + for (_, warning) in check_peps._thread( + 1, thread_url, "<Prefix>", allow_message=True + ) + ] + assert warnings == ["<Prefix> must be a valid thread URL"], warnings + + +@pytest.mark.parametrize( + "thread_url", + [ + "list-name@python.org", + "distutils-sig@python.org", + "csv@python.org", + "python-3000@python.org", + "ipaddr-py-dev@googlegroups.com", + "python-tulip@googlegroups.com", + "https://discuss.python.org/t/thread-name/123456", + "https://discuss.python.org/t/thread-name/123456/", + "https://discuss.python.org/t/thread_name/123456", + "https://discuss.python.org/t/thread_name/123456/", + "https://discuss.python.org/t/123456/", + "https://discuss.python.org/t/123456", + ], +) +def test_thread_checker_valid_discussions_to(thread_url: str): + warnings = [ + warning + for (_, warning) in check_peps._thread( + 1, thread_url, "<Prefix>", discussions_to=True + ) + ] + assert warnings == [], warnings + + +@pytest.mark.parametrize( + "thread_url", + [ + "https://discuss.python.org/t/thread-name/123456/000", + "https://discuss.python.org/t/thread-name/123456/000/", + "https://discuss.python.org/t/thread_name/123456/000", + "https://discuss.python.org/t/thread_name/123456/000/", + "https://discuss.python.org/t/123456/000/", + "https://discuss.python.org/t/12345656/000", + "https://discuss.python.org/t/thread-name", + "https://discuss.python.org/t/thread_name", + "https://discuss.python.org/t/thread+name", + ], +) +def test_thread_checker_invalid_discussions_to(thread_url: str): + warnings = [ + warning + for (_, warning) in check_peps._thread( + 1, thread_url, "<Prefix>", discussions_to=True + ) + ] + assert warnings == ["<Prefix> must be a valid thread URL"], warnings + + +def test_thread_checker_allow_message_discussions_to(): + with pytest.raises(ValueError, match="cannot both be True"): + list( + check_peps._thread( + 1, "", "<Prefix>", allow_message=True, discussions_to=True + ) + ) diff --git a/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_footer.py b/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_footer.py index ad8cf2782..9f040c5e1 100644 --- a/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_footer.py +++ b/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_footer.py @@ -1,27 +1,29 @@ -from pathlib import Path +import datetime as dt from pep_sphinx_extensions.pep_processor.transforms import pep_footer +from ...conftest import PEP_ROOT + def test_add_source_link(): - out = pep_footer._add_source_link(Path("pep-0008.txt")) + out = pep_footer._add_source_link(PEP_ROOT / "pep-0008.rst") - assert "https://github.com/python/peps/blob/main/pep-0008.txt" in str(out) + assert "https://github.com/python/peps/blob/main/peps/pep-0008.rst" in str(out) def test_add_commit_history_info(): - out = pep_footer._add_commit_history_info(Path("pep-0008.txt")) + out = pep_footer._add_commit_history_info(PEP_ROOT / "pep-0008.rst") assert str(out).startswith( "<paragraph>Last modified: " - '<reference refuri="https://github.com/python/peps/commits/main/pep-0008.txt">' + '<reference refuri="https://github.com/python/peps/commits/main/pep-0008.rst">' ) # A variable timestamp comes next, don't test that assert str(out).endswith("</reference></paragraph>") def test_add_commit_history_info_invalid(): - out = pep_footer._add_commit_history_info(Path("pep-not-found.txt")) + out = pep_footer._add_commit_history_info(PEP_ROOT / "pep-not-found.rst") assert str(out) == "<paragraph/>" @@ -31,4 +33,4 @@ def test_get_last_modified_timestamps(): assert len(out) >= 585 # Should be a Unix timestamp and at least this - assert out["pep-0008.txt"] >= 1643124055 + assert dt.datetime.fromisoformat(out["pep-0008"]).timestamp() >= 1643124055 diff --git a/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_headers.py b/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_headers.py index 4cc45423e..772c24158 100644 --- a/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_headers.py +++ b/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_headers.py @@ -18,7 +18,7 @@ from pep_sphinx_extensions.pep_zero_generator.constants import ( @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ ("my-mailing-list@example.com", "my-mailing-list@example.com"), ("python-tulip@googlegroups.com", "https://groups.google.com/g/python-tulip"), @@ -37,7 +37,7 @@ def test_generate_list_url(test_input, expected): @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ ( "https://mail.python.org/pipermail/python-3000/2006-November/004190.html", @@ -72,7 +72,7 @@ def test_process_pretty_url(test_input, expected): @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ ( "https://example.com/", @@ -94,7 +94,7 @@ def test_process_pretty_url_invalid(test_input, expected): @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ ( "https://mail.python.org/pipermail/python-3000/2006-November/004190.html", @@ -129,7 +129,7 @@ def test_make_link_pretty(test_input, expected): @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ (STATUS_ACCEPTED, "Normative proposal accepted for implementation"), (STATUS_ACTIVE, "Currently valid informational guidance, or an in-use process"), @@ -155,7 +155,7 @@ def test_abbreviate_status_unknown(): @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ ( TYPE_INFO, diff --git a/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_zero.py b/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_zero.py index 09b2effea..e25d37fd2 100644 --- a/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_zero.py +++ b/pep_sphinx_extensions/tests/pep_processor/transform/test_pep_zero.py @@ -5,7 +5,7 @@ from pep_sphinx_extensions.pep_processor.transforms import pep_zero @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ ( nodes.reference( diff --git a/pep_sphinx_extensions/tests/pep_zero_generator/test_author.py b/pep_sphinx_extensions/tests/pep_zero_generator/test_author.py deleted file mode 100644 index 8334b1c5f..000000000 --- a/pep_sphinx_extensions/tests/pep_zero_generator/test_author.py +++ /dev/null @@ -1,69 +0,0 @@ -import pytest - -from pep_sphinx_extensions.pep_zero_generator import author -from pep_sphinx_extensions.tests.utils import AUTHORS_OVERRIDES - - -@pytest.mark.parametrize( - "test_input, expected", - [ - ( - ("First Last", "first@example.com"), - author.Author( - last_first="Last, First", nick="Last", email="first@example.com" - ), - ), - ( - ("Guido van Rossum", "guido@example.com"), - author.Author( - last_first="van Rossum, Guido (GvR)", - nick="GvR", - email="guido@example.com", - ), - ), - ( - ("Hugo van Kemenade", "hugo@example.com"), - author.Author( - last_first="van Kemenade, Hugo", - nick="van Kemenade", - email="hugo@example.com", - ), - ), - ( - ("Eric N. Vander Weele", "eric@example.com"), - author.Author( - last_first="Vander Weele, Eric N.", - nick="Vander Weele", - email="eric@example.com", - ), - ), - ( - ("Mariatta", "mariatta@example.com"), - author.Author( - last_first="Mariatta", nick="Mariatta", email="mariatta@example.com" - ), - ), - ( - ("First Last Jr.", "first@example.com"), - author.Author( - last_first="Last, First, Jr.", nick="Last", email="first@example.com" - ), - ), - pytest.param( - ("First Last", "first at example.com"), - author.Author( - last_first="Last, First", nick="Last", email="first@example.com" - ), - marks=pytest.mark.xfail, - ), - ], -) -def test_parse_author_email(test_input, expected): - out = author.parse_author_email(test_input, AUTHORS_OVERRIDES) - - assert out == expected - - -def test_parse_author_email_empty_name(): - with pytest.raises(ValueError, match="Name is empty!"): - author.parse_author_email(("", "user@example.com"), AUTHORS_OVERRIDES) diff --git a/pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py b/pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py index 4b94a945c..daeb77c70 100644 --- a/pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py +++ b/pep_sphinx_extensions/tests/pep_zero_generator/test_parser.py @@ -1,9 +1,6 @@ -from pathlib import Path - import pytest from pep_sphinx_extensions.pep_zero_generator import parser -from pep_sphinx_extensions.pep_zero_generator.author import Author from pep_sphinx_extensions.pep_zero_generator.constants import ( STATUS_ACCEPTED, STATUS_ACTIVE, @@ -18,35 +15,36 @@ from pep_sphinx_extensions.pep_zero_generator.constants import ( TYPE_PROCESS, TYPE_STANDARDS, ) -from pep_sphinx_extensions.pep_zero_generator.errors import PEPError -from pep_sphinx_extensions.tests.utils import AUTHORS_OVERRIDES +from pep_sphinx_extensions.pep_zero_generator.parser import _Author + +from ..conftest import PEP_ROOT def test_pep_repr(): - pep8 = parser.PEP(Path("pep-0008.txt")) + pep8 = parser.PEP(PEP_ROOT / "pep-0008.rst") assert repr(pep8) == "<PEP 0008 - Style Guide for Python Code>" def test_pep_less_than(): - pep8 = parser.PEP(Path("pep-0008.txt")) - pep3333 = parser.PEP(Path("pep-3333.txt")) + pep8 = parser.PEP(PEP_ROOT / "pep-0008.rst") + pep3333 = parser.PEP(PEP_ROOT / "pep-3333.rst") assert pep8 < pep3333 def test_pep_equal(): - pep_a = parser.PEP(Path("pep-0008.txt")) - pep_b = parser.PEP(Path("pep-0008.txt")) + pep_a = parser.PEP(PEP_ROOT / "pep-0008.rst") + pep_b = parser.PEP(PEP_ROOT / "pep-0008.rst") assert pep_a == pep_b def test_pep_details(monkeypatch): - pep8 = parser.PEP(Path("pep-0008.txt")) + pep8 = parser.PEP(PEP_ROOT / "pep-0008.rst") assert pep8.details == { - "authors": "GvR, Warsaw, Coghlan", + "authors": "Guido van Rossum, Barry Warsaw, Nick Coghlan", "number": 8, "shorthand": ":abbr:`PA (Process, Active)`", "title": "Style Guide for Python Code", @@ -54,48 +52,43 @@ def test_pep_details(monkeypatch): @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ ( "First Last <user@example.com>", - [Author(last_first="Last, First", nick="Last", email="user@example.com")], + [_Author(full_name="First Last", email="user@example.com")], ), ( "First Last", - [Author(last_first="Last, First", nick="Last", email="")], - ), - ( - "user@example.com (First Last)", - [Author(last_first="Last, First", nick="Last", email="user@example.com")], + [_Author(full_name="First Last", email="")], ), pytest.param( "First Last <user at example.com>", - [Author(last_first="Last, First", nick="Last", email="user@example.com")], + [_Author(full_name="First Last", email="user@example.com")], marks=pytest.mark.xfail, ), + pytest.param( + " , First Last,", + {"First Last": ""}, + marks=pytest.mark.xfail(raises=ValueError), + ), ], ) def test_parse_authors(test_input, expected): - # Arrange - dummy_object = parser.PEP(Path("pep-0160.txt")) - # Act - out = parser._parse_authors(dummy_object, test_input, AUTHORS_OVERRIDES) + out = parser._parse_author(test_input) # Assert assert out == expected def test_parse_authors_invalid(): - - pep = parser.PEP(Path("pep-0008.txt")) - - with pytest.raises(PEPError, match="no authors found"): - parser._parse_authors(pep, "", AUTHORS_OVERRIDES) + with pytest.raises(ValueError, match="Name is empty!"): + assert parser._parse_author("") @pytest.mark.parametrize( - "test_type, test_status, expected", + ("test_type", "test_status", "expected"), [ (TYPE_INFO, STATUS_DRAFT, ":abbr:`I (Informational, Draft)`"), (TYPE_INFO, STATUS_ACTIVE, ":abbr:`IA (Informational, Active)`"), @@ -113,7 +106,7 @@ def test_parse_authors_invalid(): ) def test_abbreviate_type_status(test_type, test_status, expected): # set up dummy PEP object and monkeypatch attributes - pep = parser.PEP(Path("pep-0008.txt")) + pep = parser.PEP(PEP_ROOT / "pep-0008.rst") pep.pep_type = test_type pep.status = test_status diff --git a/pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py b/pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py index c2e15844f..75c16f624 100644 --- a/pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py +++ b/pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py @@ -1,10 +1,10 @@ -from pathlib import Path - from pep_sphinx_extensions.pep_zero_generator import parser, pep_index_generator +from ..conftest import PEP_ROOT + def test_create_pep_json(): - peps = [parser.PEP(Path("pep-0008.txt"))] + peps = [parser.PEP(PEP_ROOT / "pep-0008.rst")] out = pep_index_generator.create_pep_json(peps) diff --git a/pep_sphinx_extensions/tests/pep_zero_generator/test_writer.py b/pep_sphinx_extensions/tests/pep_zero_generator/test_writer.py index 19eeca2d9..1ccf8c418 100644 --- a/pep_sphinx_extensions/tests/pep_zero_generator/test_writer.py +++ b/pep_sphinx_extensions/tests/pep_zero_generator/test_writer.py @@ -30,18 +30,18 @@ def test_pep_zero_writer_emit_title(): @pytest.mark.parametrize( - "test_input, expected", + ("test_input", "expected"), [ ( "pep-9000.rst", { - "Fussyreverend, Francis": "one@example.com", - "Soulfulcommodore, Javier": "two@example.com", + "Francis Fussyreverend": "one@example.com", + "Javier Soulfulcommodore": "two@example.com", }, ), ( "pep-9001.rst", - {"Fussyreverend, Francis": "", "Soulfulcommodore, Javier": ""}, + {"Francis Fussyreverend": "", "Javier Soulfulcommodore": ""}, ), ], ) diff --git a/pep_sphinx_extensions/tests/peps/pep-9002.rst b/pep_sphinx_extensions/tests/peps/pep-9002.rst new file mode 100644 index 000000000..208569e03 --- /dev/null +++ b/pep_sphinx_extensions/tests/peps/pep-9002.rst @@ -0,0 +1,23 @@ +PEP:9002 +Title: Nobody expects the example PEP! +Author: Cardinal Ximénez <Cardinal.Ximenez@spanish.inquisition>, + Cardinal Biggles + Cardinal Fang +Version: 4.0 +Python-Version: 4.0 +Sponsor: +Sponsor: +Horse-Guards: Parade +Created: 1-Jan-1989 +BDFL-Delegate: Barry! +Status: Draught +Topic: Inquisiting, Governance, Governance, packaging +Content-Type: video/quicktime +Requires: 0020,1,2,3, 7, 8 +Discussions-To: MR ALBERT SPIM, I,OOO,OO8 LONDON ROAD, OXFORD +Post-History: `2-Feb-2000 <FLIGHT LT. & PREBENDARY ETHEL MORRIS; THE DIMPLES; THAXTED; NR BUENOS AIRES>`__ + `3-Mar-2001 <The Royal Frog Trampling Institute; 16 Rayners Lane; London>`__ +Resolution: + + +https://peps.python.org/pep-9002.html diff --git a/pep_sphinx_extensions/tests/utils.py b/pep_sphinx_extensions/tests/utils.py deleted file mode 100644 index 19167d552..000000000 --- a/pep_sphinx_extensions/tests/utils.py +++ /dev/null @@ -1,6 +0,0 @@ -AUTHORS_OVERRIDES = { - "Guido van Rossum": { - "Surname First": "van Rossum, Guido (GvR)", - "Name Reference": "GvR", - }, -} diff --git a/conf.py b/peps/conf.py similarity index 87% rename from conf.py rename to peps/conf.py index 95a1debd4..09b9b3ed3 100644 --- a/conf.py +++ b/peps/conf.py @@ -3,10 +3,12 @@ """Configuration for building PEPs using Sphinx.""" +import os from pathlib import Path import sys -sys.path.append(str(Path(".").absolute())) +_ROOT = Path(__file__).resolve().parent.parent +sys.path.append(os.fspath(_ROOT)) # -- Project information ----------------------------------------------------- @@ -25,7 +27,6 @@ extensions = [ # The file extensions of source files. Sphinx uses these suffixes as sources. source_suffix = { ".rst": "pep", - ".txt": "pep", } # List of patterns (relative to source dir) to ignore when looking for source files. @@ -34,7 +35,6 @@ include_patterns = [ "contents.rst", # PEP files "pep-????.rst", - "pep-????.txt", # PEP ancillary files "pep-????/*.rst", # Documentation @@ -60,11 +60,13 @@ intersphinx_disabled_reftypes = [] # -- Options for HTML output ------------------------------------------------- +_PSE_PATH = _ROOT / "pep_sphinx_extensions" + # HTML output settings html_math_renderer = "maths_to_html" # Maths rendering # Theme settings -html_theme_path = ["pep_sphinx_extensions"] +html_theme_path = [os.fspath(_PSE_PATH)] html_theme = "pep_theme" # The actual theme directory (child of html_theme_path) html_use_index = False # Disable index (we use PEP 0) html_style = "" # must be defined here or in theme.conf, but is unused @@ -72,4 +74,4 @@ html_permalinks = False # handled in the PEPContents transform html_baseurl = "https://peps.python.org" # to create the CNAME file gettext_auto_build = False # speed-ups -templates_path = ["pep_sphinx_extensions/pep_theme/templates"] # Theme template relative paths from `confdir` +templates_path = [os.fspath(_PSE_PATH / "pep_theme" / "templates")] # Theme template relative paths from `confdir` diff --git a/contents.rst b/peps/contents.rst similarity index 97% rename from contents.rst rename to peps/contents.rst index 3aa0d788d..d791f08f8 100644 --- a/contents.rst +++ b/peps/contents.rst @@ -14,6 +14,5 @@ This is an internal Sphinx page; please go to the :doc:`PEP Index <pep-0000>`. :glob: :caption: PEP Table of Contents (needed for Sphinx): - docs/* pep-* topic/* diff --git a/pep-0001.txt b/peps/pep-0001.rst similarity index 99% rename from pep-0001.txt rename to peps/pep-0001.rst index 82ff18ea0..66da707dc 100644 --- a/pep-0001.txt +++ b/peps/pep-0001.rst @@ -207,7 +207,7 @@ The standard PEP workflow is: It also provides a complete introduction to reST markup that is used in PEPs. Approval criteria are: - * It sound and complete. The ideas must make technical sense. The + * It is sound and complete. The ideas must make technical sense. The editors do not consider whether they seem likely to be accepted. * The title accurately describes the content. * The PEP's language (spelling, grammar, sentence structure, etc.) @@ -296,7 +296,7 @@ pointing to this new thread. If it is not chosen as the discussion venue, a brief announcement post should be made to the `PEPs category`_ -with at least a link to the rendered PEP and the `Discussions-To` thread +with at least a link to the rendered PEP and the ``Discussions-To`` thread when the draft PEP is committed to the repository and if a major-enough change is made to trigger a new thread. diff --git a/pep-0001/process_flow.svg b/peps/pep-0001/process_flow.svg similarity index 100% rename from pep-0001/process_flow.svg rename to peps/pep-0001/process_flow.svg diff --git a/pep-0002.txt b/peps/pep-0002.rst similarity index 100% rename from pep-0002.txt rename to peps/pep-0002.rst diff --git a/pep-0003.txt b/peps/pep-0003.rst similarity index 100% rename from pep-0003.txt rename to peps/pep-0003.rst diff --git a/pep-0004.txt b/peps/pep-0004.rst similarity index 100% rename from pep-0004.txt rename to peps/pep-0004.rst diff --git a/pep-0005.txt b/peps/pep-0005.rst similarity index 100% rename from pep-0005.txt rename to peps/pep-0005.rst diff --git a/pep-0006.txt b/peps/pep-0006.rst similarity index 99% rename from pep-0006.txt rename to peps/pep-0006.rst index ca6a6e74c..fd5389929 100644 --- a/pep-0006.txt +++ b/peps/pep-0006.rst @@ -45,7 +45,7 @@ Prohibitions Bug fix releases are required to adhere to the following restrictions: -1. There must be zero syntax changes. All `.pyc` and `.pyo` files must +1. There must be zero syntax changes. All ``.pyc`` and ``.pyo`` files must work (no regeneration needed) with all bugfix releases forked off from a major release. diff --git a/pep-0007.txt b/peps/pep-0007.rst similarity index 100% rename from pep-0007.txt rename to peps/pep-0007.rst diff --git a/pep-0008.txt b/peps/pep-0008.rst similarity index 100% rename from pep-0008.txt rename to peps/pep-0008.rst diff --git a/pep-0009.txt b/peps/pep-0009.rst similarity index 100% rename from pep-0009.txt rename to peps/pep-0009.rst diff --git a/pep-0010.txt b/peps/pep-0010.rst similarity index 100% rename from pep-0010.txt rename to peps/pep-0010.rst diff --git a/pep-0011.txt b/peps/pep-0011.rst similarity index 99% rename from pep-0011.txt rename to peps/pep-0011.rst index ffbd2e766..63131c3a2 100644 --- a/pep-0011.txt +++ b/peps/pep-0011.rst @@ -8,7 +8,7 @@ Content-Type: text/x-rst Created: 07-Jul-2002 Post-History: `18-Aug-2007 <https://mail.python.org/archives/list/python-dev@python.org/thread/DSSGXU5LBCMKYMZBRVB6RF3YAB6ST5AV/>`__, `14-May-2014 <https://mail.python.org/archives/list/python-dev@python.org/thread/T7WTUJ6TD3IGYGWV3M4PHJWNLM2WPZAW/>`__, - `20-Feb-2015 <https://mail.python.org/archives/list/python-dev@python.org/thread/OEQHRR2COYZDL6LZ42RBZOMIUB32WI34/#L3K7IKGVT4ND45SKAJPJ3Q2ADVK5KP52>`__, + `20-Feb-2015 <https://mail.python.org/archives/list/python-dev@python.org/thread/OEQHRR2COYZDL6LZ42RBZOMIUB32WI34/>`__, `10-Mar-2022 <https://mail.python.org/archives/list/python-committers@python.org/thread/K757345KX6W5ZLTWYBUXOXQTJJTL7GW5/>`__, diff --git a/pep-0012.rst b/peps/pep-0012.rst similarity index 100% rename from pep-0012.rst rename to peps/pep-0012.rst diff --git a/pep-0012/pep-NNNN.rst b/peps/pep-0012/pep-NNNN.rst similarity index 100% rename from pep-0012/pep-NNNN.rst rename to peps/pep-0012/pep-NNNN.rst diff --git a/pep-0013.rst b/peps/pep-0013.rst similarity index 100% rename from pep-0013.rst rename to peps/pep-0013.rst diff --git a/pep-0020.txt b/peps/pep-0020.rst similarity index 100% rename from pep-0020.txt rename to peps/pep-0020.rst diff --git a/pep-0042.txt b/peps/pep-0042.rst similarity index 100% rename from pep-0042.txt rename to peps/pep-0042.rst diff --git a/pep-0100.txt b/peps/pep-0100.rst similarity index 100% rename from pep-0100.txt rename to peps/pep-0100.rst diff --git a/pep-0101.txt b/peps/pep-0101.rst similarity index 98% rename from pep-0101.txt rename to peps/pep-0101.rst index b2bbfde93..e877f8958 100644 --- a/pep-0101.txt +++ b/peps/pep-0101.rst @@ -212,7 +212,7 @@ to perform some manual editing steps. within it (called the "release clone" from now on). You can use the same GitHub fork you use for cpython development. Using the standard setup recommended in the Python Developer's Guide, your fork would be referred - to as `origin` and the standard cpython repo as `upstream`. You will + to as ``origin`` and the standard cpython repo as ``upstream``. You will use the branch on your fork to do the release engineering work, including tagging the release, and you will use it to share with the other experts for making the binaries. @@ -302,7 +302,7 @@ to perform some manual editing steps. $ .../release-tools/release.py --tag X.Y.ZaN - This executes a `git tag` command with the `-s` option so that the + This executes a ``git tag`` command with the ``-s`` option so that the release tag in the repo is signed with your gpg key. When prompted choose the private key you use for signing release tarballs etc. @@ -538,7 +538,7 @@ the main repo. do some post-merge cleanup. Check the top-level ``README.rst`` and ``include/patchlevel.h`` files to ensure they now reflect the desired post-release values for on-going development. - The patchlevel should be the release tag with a `+`. + The patchlevel should be the release tag with a ``+``. Also, if you cherry-picked changes from the standard release branch into the release engineering branch for this release, you will now need to manual remove each blurb entry from @@ -546,8 +546,8 @@ the main repo. into the release you are working on since that blurb entry is now captured in the merged x.y.z.rst file for the new release. Otherwise, the blurb entry will appear twice in - the `changelog.html` file, once under `Python next` and again - under `x.y.z`. + the ``changelog.html`` file, once under ``Python next`` and again + under ``x.y.z``. - Review and commit these changes:: @@ -712,19 +712,19 @@ with RevSys.) - If this is a **final** release: - Add the new version to the *Python Documentation by Version* - page `https://www.python.org/doc/versions/` and + page ``https://www.python.org/doc/versions/`` and remove the current version from any 'in development' section. - For X.Y.Z, edit all the previous X.Y releases' page(s) to point to the new release. This includes the content field of the - `Downloads -> Releases` entry for the release:: + ``Downloads -> Releases`` entry for the release:: Note: Python x.y.m has been superseded by `Python x.y.n </downloads/release/python-xyn/>`_. And, for those releases having separate release page entries (phasing these out?), update those pages as well, - e.g. `download/releases/x.y.z`:: + e.g. ``download/releases/x.y.z``:: Note: Python x.y.m has been superseded by `Python x.y.n </download/releases/x.y.n/>`_. @@ -908,8 +908,8 @@ else does them. Some of those tasks include: - Remove the release from the list of "Active Python Releases" on the Downloads page. To do this, log in to the admin page for python.org, navigate to Boxes, - and edit the `downloads-active-releases` entry. Simply strip out the relevant - paragraph of HTML for your release. (You'll probably have to do the `curl -X PURGE` + and edit the ``downloads-active-releases`` entry. Simply strip out the relevant + paragraph of HTML for your release. (You'll probably have to do the ``curl -X PURGE`` trick to purge the cache if you want to confirm you made the change correctly.) - Add retired notice to each release page on python.org for the retired branch. diff --git a/pep-0102.txt b/peps/pep-0102.rst similarity index 100% rename from pep-0102.txt rename to peps/pep-0102.rst diff --git a/pep-0103.txt b/peps/pep-0103.rst similarity index 100% rename from pep-0103.txt rename to peps/pep-0103.rst diff --git a/pep-0160.txt b/peps/pep-0160.rst similarity index 100% rename from pep-0160.txt rename to peps/pep-0160.rst diff --git a/pep-0200.txt b/peps/pep-0200.rst similarity index 100% rename from pep-0200.txt rename to peps/pep-0200.rst diff --git a/pep-0201.txt b/peps/pep-0201.rst similarity index 99% rename from pep-0201.txt rename to peps/pep-0201.rst index 71454d9ae..7b1c77af3 100644 --- a/pep-0201.txt +++ b/peps/pep-0201.rst @@ -46,8 +46,8 @@ Lockstep For-Loops Lockstep for-loops are non-nested iterations over two or more sequences, such that at each pass through the loop, one element from each sequence is taken to compose the target. This behavior can -already be accomplished in Python through the use of the map() built- -in function:: +already be accomplished in Python through the use of the map() built-in +function:: >>> a = (1, 2, 3) >>> b = (4, 5, 6) diff --git a/pep-0202.txt b/peps/pep-0202.rst similarity index 100% rename from pep-0202.txt rename to peps/pep-0202.rst diff --git a/pep-0203.txt b/peps/pep-0203.rst similarity index 100% rename from pep-0203.txt rename to peps/pep-0203.rst diff --git a/pep-0204.txt b/peps/pep-0204.rst similarity index 100% rename from pep-0204.txt rename to peps/pep-0204.rst diff --git a/pep-0205.txt b/peps/pep-0205.rst similarity index 99% rename from pep-0205.txt rename to peps/pep-0205.rst index d746d7b02..e16c2d3dc 100644 --- a/pep-0205.txt +++ b/peps/pep-0205.rst @@ -185,8 +185,8 @@ Implementation Strategy ======================= The implementation of weak references will include a list of -reference containers that must be cleared for each weakly- -referencable object. If the reference is from a weak dictionary, +reference containers that must be cleared for each weakly-referencable +object. If the reference is from a weak dictionary, the dictionary entry is cleared first. Then, any associated callback is called with the object passed as a parameter. Once all callbacks have been called, the object is finalized and diff --git a/pep-0206.txt b/peps/pep-0206.rst similarity index 100% rename from pep-0206.txt rename to peps/pep-0206.rst diff --git a/pep-0207.txt b/peps/pep-0207.rst similarity index 100% rename from pep-0207.txt rename to peps/pep-0207.rst diff --git a/pep-0208.txt b/peps/pep-0208.rst similarity index 100% rename from pep-0208.txt rename to peps/pep-0208.rst diff --git a/pep-0209.txt b/peps/pep-0209.rst similarity index 98% rename from pep-0209.txt rename to peps/pep-0209.rst index f3ffd2f93..c91a15f0a 100644 --- a/pep-0209.txt +++ b/peps/pep-0209.rst @@ -12,9 +12,9 @@ Post-History: Abstract ======== -This PEP proposes a redesign and re-implementation of the multi- -dimensional array module, Numeric, to make it easier to add new -features and functionality to the module. Aspects of Numeric 2 +This PEP proposes a redesign and re-implementation of the +multi-dimensional array module, Numeric, to make it easier to add +new features and functionality to the module. Aspects of Numeric 2 that will receive special attention are efficient access to arrays exceeding a gigabyte in size and composed of inhomogeneous data structures or records. The proposed design uses four Python @@ -128,8 +128,8 @@ Some planned features are: automatically handle alignment and representational issues when data is accessed or operated on. There are two approaches to implementing records; as either a derived array - class or a special array type, depending on your point-of- - view. We defer this discussion to the Open Issues section. + class or a special array type, depending on your point-of-view. + We defer this discussion to the Open Issues section. 2. Additional array types @@ -265,8 +265,8 @@ The design of Numeric 2 has four primary classes: _ufunc.compute(slice, data, func, swap, conv) The 'func' argument is a CFuncObject, while the 'swap' and 'conv' - arguments are lists of CFuncObjects for those arrays needing pre- - or post-processing, otherwise None is used. The data argument is + arguments are lists of CFuncObjects for those arrays needing pre- or + post-processing, otherwise None is used. The data argument is a list of buffer objects, and the slice argument gives the number of iterations for each dimension along with the buffer offset and step size for each array and each dimension. diff --git a/pep-0210.txt b/peps/pep-0210.rst similarity index 100% rename from pep-0210.txt rename to peps/pep-0210.rst diff --git a/pep-0211.txt b/peps/pep-0211.rst similarity index 100% rename from pep-0211.txt rename to peps/pep-0211.rst diff --git a/pep-0212.txt b/peps/pep-0212.rst similarity index 100% rename from pep-0212.txt rename to peps/pep-0212.rst diff --git a/pep-0213.txt b/peps/pep-0213.rst similarity index 100% rename from pep-0213.txt rename to peps/pep-0213.rst diff --git a/pep-0214.txt b/peps/pep-0214.rst similarity index 100% rename from pep-0214.txt rename to peps/pep-0214.rst diff --git a/pep-0215.txt b/peps/pep-0215.rst similarity index 100% rename from pep-0215.txt rename to peps/pep-0215.rst diff --git a/pep-0216.txt b/peps/pep-0216.rst similarity index 100% rename from pep-0216.txt rename to peps/pep-0216.rst diff --git a/pep-0217.txt b/peps/pep-0217.rst similarity index 100% rename from pep-0217.txt rename to peps/pep-0217.rst diff --git a/pep-0218.txt b/peps/pep-0218.rst similarity index 100% rename from pep-0218.txt rename to peps/pep-0218.rst diff --git a/pep-0219.txt b/peps/pep-0219.rst similarity index 100% rename from pep-0219.txt rename to peps/pep-0219.rst diff --git a/pep-0220.txt b/peps/pep-0220.rst similarity index 100% rename from pep-0220.txt rename to peps/pep-0220.rst diff --git a/pep-0221.txt b/peps/pep-0221.rst similarity index 100% rename from pep-0221.txt rename to peps/pep-0221.rst diff --git a/pep-0222.txt b/peps/pep-0222.rst similarity index 100% rename from pep-0222.txt rename to peps/pep-0222.rst diff --git a/pep-0223.txt b/peps/pep-0223.rst similarity index 100% rename from pep-0223.txt rename to peps/pep-0223.rst diff --git a/pep-0224.txt b/peps/pep-0224.rst similarity index 100% rename from pep-0224.txt rename to peps/pep-0224.rst diff --git a/pep-0225.txt b/peps/pep-0225.rst similarity index 100% rename from pep-0225.txt rename to peps/pep-0225.rst diff --git a/pep-0226.txt b/peps/pep-0226.rst similarity index 100% rename from pep-0226.txt rename to peps/pep-0226.rst diff --git a/pep-0227.txt b/peps/pep-0227.rst similarity index 100% rename from pep-0227.txt rename to peps/pep-0227.rst diff --git a/pep-0228.txt b/peps/pep-0228.rst similarity index 100% rename from pep-0228.txt rename to peps/pep-0228.rst diff --git a/pep-0229.txt b/peps/pep-0229.rst similarity index 100% rename from pep-0229.txt rename to peps/pep-0229.rst diff --git a/pep-0230.txt b/peps/pep-0230.rst similarity index 100% rename from pep-0230.txt rename to peps/pep-0230.rst diff --git a/pep-0231.txt b/peps/pep-0231.rst similarity index 100% rename from pep-0231.txt rename to peps/pep-0231.rst diff --git a/pep-0232.txt b/peps/pep-0232.rst similarity index 100% rename from pep-0232.txt rename to peps/pep-0232.rst diff --git a/pep-0233.txt b/peps/pep-0233.rst similarity index 100% rename from pep-0233.txt rename to peps/pep-0233.rst diff --git a/pep-0234.txt b/peps/pep-0234.rst similarity index 100% rename from pep-0234.txt rename to peps/pep-0234.rst diff --git a/pep-0235.txt b/peps/pep-0235.rst similarity index 97% rename from pep-0235.txt rename to peps/pep-0235.rst index f152caa9c..d5a374afa 100644 --- a/pep-0235.txt +++ b/peps/pep-0235.rst @@ -125,8 +125,8 @@ A. If the ``PYTHONCASEOK`` environment variable exists, same as B. Else search ``sys.path`` for the first case-sensitive match; raise ``ImportError`` if none found. -#B is the same rule as is used on Unix, so this will improve cross- -platform portability. That's good. #B is also the rule the Mac +#B is the same rule as is used on Unix, so this will improve +cross-platform portability. That's good. #B is also the rule the Mac and Cygwin folks want (and wanted enough to implement themselves, multiple times, which is a powerful argument in PythonLand). It can't cause any existing non-exceptional Windows import to fail, diff --git a/pep-0236.txt b/peps/pep-0236.rst similarity index 100% rename from pep-0236.txt rename to peps/pep-0236.rst diff --git a/pep-0237.txt b/peps/pep-0237.rst similarity index 100% rename from pep-0237.txt rename to peps/pep-0237.rst diff --git a/pep-0238.txt b/peps/pep-0238.rst similarity index 100% rename from pep-0238.txt rename to peps/pep-0238.rst diff --git a/pep-0239.txt b/peps/pep-0239.rst similarity index 100% rename from pep-0239.txt rename to peps/pep-0239.rst diff --git a/pep-0240.txt b/peps/pep-0240.rst similarity index 100% rename from pep-0240.txt rename to peps/pep-0240.rst diff --git a/pep-0241.txt b/peps/pep-0241.rst similarity index 100% rename from pep-0241.txt rename to peps/pep-0241.rst diff --git a/pep-0242.txt b/peps/pep-0242.rst similarity index 100% rename from pep-0242.txt rename to peps/pep-0242.rst diff --git a/pep-0243.txt b/peps/pep-0243.rst similarity index 100% rename from pep-0243.txt rename to peps/pep-0243.rst diff --git a/pep-0244.txt b/peps/pep-0244.rst similarity index 100% rename from pep-0244.txt rename to peps/pep-0244.rst diff --git a/pep-0245.txt b/peps/pep-0245.rst similarity index 100% rename from pep-0245.txt rename to peps/pep-0245.rst diff --git a/pep-0246.txt b/peps/pep-0246.rst similarity index 100% rename from pep-0246.txt rename to peps/pep-0246.rst diff --git a/pep-0247.txt b/peps/pep-0247.rst similarity index 100% rename from pep-0247.txt rename to peps/pep-0247.rst diff --git a/pep-0248.txt b/peps/pep-0248.rst similarity index 100% rename from pep-0248.txt rename to peps/pep-0248.rst diff --git a/pep-0249.txt b/peps/pep-0249.rst similarity index 99% rename from pep-0249.txt rename to peps/pep-0249.rst index 629a8ba89..d8a409cc5 100644 --- a/pep-0249.txt +++ b/peps/pep-0249.rst @@ -938,8 +938,8 @@ Cursor\ `.lastrowid`_ Connection\ `.autocommit`_ Attribute to query and set the autocommit mode of the connection. - Return ``True`` if the connection is operating in autocommit (non- - transactional) mode. Return ``False`` if the connection is + Return ``True`` if the connection is operating in autocommit + (non-transactional) mode. Return ``False`` if the connection is operating in manual commit (transactional) mode. Setting the attribute to ``True`` or ``False`` adjusts the diff --git a/pep-0250.txt b/peps/pep-0250.rst similarity index 100% rename from pep-0250.txt rename to peps/pep-0250.rst diff --git a/pep-0251.txt b/peps/pep-0251.rst similarity index 100% rename from pep-0251.txt rename to peps/pep-0251.rst diff --git a/pep-0252.txt b/peps/pep-0252.rst similarity index 100% rename from pep-0252.txt rename to peps/pep-0252.rst diff --git a/pep-0253.txt b/peps/pep-0253.rst similarity index 100% rename from pep-0253.txt rename to peps/pep-0253.rst diff --git a/pep-0254.txt b/peps/pep-0254.rst similarity index 100% rename from pep-0254.txt rename to peps/pep-0254.rst diff --git a/pep-0255.txt b/peps/pep-0255.rst similarity index 100% rename from pep-0255.txt rename to peps/pep-0255.rst diff --git a/pep-0256.txt b/peps/pep-0256.rst similarity index 100% rename from pep-0256.txt rename to peps/pep-0256.rst diff --git a/pep-0257.txt b/peps/pep-0257.rst similarity index 100% rename from pep-0257.txt rename to peps/pep-0257.rst diff --git a/pep-0258.txt b/peps/pep-0258.rst similarity index 100% rename from pep-0258.txt rename to peps/pep-0258.rst diff --git a/pep-0259.txt b/peps/pep-0259.rst similarity index 100% rename from pep-0259.txt rename to peps/pep-0259.rst diff --git a/pep-0260.txt b/peps/pep-0260.rst similarity index 100% rename from pep-0260.txt rename to peps/pep-0260.rst diff --git a/pep-0261.txt b/peps/pep-0261.rst similarity index 100% rename from pep-0261.txt rename to peps/pep-0261.rst diff --git a/pep-0262.txt b/peps/pep-0262.rst similarity index 100% rename from pep-0262.txt rename to peps/pep-0262.rst diff --git a/pep-0263.txt b/peps/pep-0263.rst similarity index 100% rename from pep-0263.txt rename to peps/pep-0263.rst diff --git a/pep-0264.txt b/peps/pep-0264.rst similarity index 100% rename from pep-0264.txt rename to peps/pep-0264.rst diff --git a/pep-0265.txt b/peps/pep-0265.rst similarity index 100% rename from pep-0265.txt rename to peps/pep-0265.rst diff --git a/pep-0266.txt b/peps/pep-0266.rst similarity index 100% rename from pep-0266.txt rename to peps/pep-0266.rst diff --git a/pep-0267.txt b/peps/pep-0267.rst similarity index 100% rename from pep-0267.txt rename to peps/pep-0267.rst diff --git a/pep-0268.txt b/peps/pep-0268.rst similarity index 100% rename from pep-0268.txt rename to peps/pep-0268.rst diff --git a/pep-0269.txt b/peps/pep-0269.rst similarity index 100% rename from pep-0269.txt rename to peps/pep-0269.rst diff --git a/pep-0270.txt b/peps/pep-0270.rst similarity index 100% rename from pep-0270.txt rename to peps/pep-0270.rst diff --git a/pep-0271.txt b/peps/pep-0271.rst similarity index 100% rename from pep-0271.txt rename to peps/pep-0271.rst diff --git a/pep-0272.txt b/peps/pep-0272.rst similarity index 100% rename from pep-0272.txt rename to peps/pep-0272.rst diff --git a/pep-0273.txt b/peps/pep-0273.rst similarity index 100% rename from pep-0273.txt rename to peps/pep-0273.rst diff --git a/pep-0274.txt b/peps/pep-0274.rst similarity index 100% rename from pep-0274.txt rename to peps/pep-0274.rst diff --git a/pep-0275.txt b/peps/pep-0275.rst similarity index 100% rename from pep-0275.txt rename to peps/pep-0275.rst diff --git a/pep-0276.txt b/peps/pep-0276.rst similarity index 100% rename from pep-0276.txt rename to peps/pep-0276.rst diff --git a/pep-0277.txt b/peps/pep-0277.rst similarity index 100% rename from pep-0277.txt rename to peps/pep-0277.rst diff --git a/pep-0278.txt b/peps/pep-0278.rst similarity index 100% rename from pep-0278.txt rename to peps/pep-0278.rst diff --git a/pep-0279.txt b/peps/pep-0279.rst similarity index 100% rename from pep-0279.txt rename to peps/pep-0279.rst diff --git a/pep-0280.txt b/peps/pep-0280.rst similarity index 100% rename from pep-0280.txt rename to peps/pep-0280.rst diff --git a/pep-0281.txt b/peps/pep-0281.rst similarity index 100% rename from pep-0281.txt rename to peps/pep-0281.rst diff --git a/pep-0282.txt b/peps/pep-0282.rst similarity index 100% rename from pep-0282.txt rename to peps/pep-0282.rst diff --git a/pep-0283.txt b/peps/pep-0283.rst similarity index 100% rename from pep-0283.txt rename to peps/pep-0283.rst diff --git a/pep-0284.txt b/peps/pep-0284.rst similarity index 98% rename from pep-0284.txt rename to peps/pep-0284.rst index 48c476f23..08fedf5ab 100644 --- a/pep-0284.txt +++ b/peps/pep-0284.rst @@ -1,9 +1,7 @@ PEP: 284 Title: Integer for-loops -Version: $Revision$ -Last-Modified: $Date$ Author: David Eppstein <eppstein@ics.uci.edu>, - Greg Ewing <greg.ewing@canterbury.ac.nz> + Gregory Ewing <greg.ewing@canterbury.ac.nz> Status: Rejected Type: Standards Track Content-Type: text/x-rst @@ -264,12 +262,3 @@ Copyright ========= This document has been placed in the public domain. - - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - fill-column: 70 - End: diff --git a/pep-0285.txt b/peps/pep-0285.rst similarity index 100% rename from pep-0285.txt rename to peps/pep-0285.rst diff --git a/pep-0286.txt b/peps/pep-0286.rst similarity index 100% rename from pep-0286.txt rename to peps/pep-0286.rst diff --git a/pep-0287.txt b/peps/pep-0287.rst similarity index 100% rename from pep-0287.txt rename to peps/pep-0287.rst diff --git a/pep-0288.txt b/peps/pep-0288.rst similarity index 100% rename from pep-0288.txt rename to peps/pep-0288.rst diff --git a/pep-0289.txt b/peps/pep-0289.rst similarity index 100% rename from pep-0289.txt rename to peps/pep-0289.rst diff --git a/pep-0290.txt b/peps/pep-0290.rst similarity index 100% rename from pep-0290.txt rename to peps/pep-0290.rst diff --git a/pep-0291.txt b/peps/pep-0291.rst similarity index 100% rename from pep-0291.txt rename to peps/pep-0291.rst diff --git a/pep-0292.txt b/peps/pep-0292.rst similarity index 100% rename from pep-0292.txt rename to peps/pep-0292.rst diff --git a/pep-0293.txt b/peps/pep-0293.rst similarity index 100% rename from pep-0293.txt rename to peps/pep-0293.rst diff --git a/pep-0294.txt b/peps/pep-0294.rst similarity index 100% rename from pep-0294.txt rename to peps/pep-0294.rst diff --git a/pep-0295.txt b/peps/pep-0295.rst similarity index 100% rename from pep-0295.txt rename to peps/pep-0295.rst diff --git a/pep-0296.txt b/peps/pep-0296.rst similarity index 100% rename from pep-0296.txt rename to peps/pep-0296.rst diff --git a/pep-0297.txt b/peps/pep-0297.rst similarity index 100% rename from pep-0297.txt rename to peps/pep-0297.rst diff --git a/pep-0298.txt b/peps/pep-0298.rst similarity index 100% rename from pep-0298.txt rename to peps/pep-0298.rst diff --git a/pep-0299.txt b/peps/pep-0299.rst similarity index 100% rename from pep-0299.txt rename to peps/pep-0299.rst diff --git a/pep-0301.txt b/peps/pep-0301.rst similarity index 100% rename from pep-0301.txt rename to peps/pep-0301.rst diff --git a/pep-0302.txt b/peps/pep-0302.rst similarity index 99% rename from pep-0302.txt rename to peps/pep-0302.rst index bdea0a363..29f9f799d 100644 --- a/pep-0302.txt +++ b/peps/pep-0302.rst @@ -428,7 +428,7 @@ built-in import mechanism". They simply won't invoke any import hooks. A new if loader is not None: loader.load_module(fullname) -In the case of a "basic" import, one the `imp.find_module()` function would +In the case of a "basic" import, one the ``imp.find_module()`` function would handle, the loader object would be a wrapper for the current output of ``imp.find_module()``, and ``loader.load_module()`` would call ``imp.load_module()`` with that output. diff --git a/pep-0303.txt b/peps/pep-0303.rst similarity index 100% rename from pep-0303.txt rename to peps/pep-0303.rst diff --git a/pep-0304.txt b/peps/pep-0304.rst similarity index 100% rename from pep-0304.txt rename to peps/pep-0304.rst diff --git a/pep-0305.txt b/peps/pep-0305.rst similarity index 100% rename from pep-0305.txt rename to peps/pep-0305.rst diff --git a/pep-0306.txt b/peps/pep-0306.rst similarity index 100% rename from pep-0306.txt rename to peps/pep-0306.rst diff --git a/pep-0307.txt b/peps/pep-0307.rst similarity index 100% rename from pep-0307.txt rename to peps/pep-0307.rst diff --git a/pep-0308.txt b/peps/pep-0308.rst similarity index 100% rename from pep-0308.txt rename to peps/pep-0308.rst diff --git a/pep-0309.txt b/peps/pep-0309.rst similarity index 100% rename from pep-0309.txt rename to peps/pep-0309.rst diff --git a/pep-0310.txt b/peps/pep-0310.rst similarity index 99% rename from pep-0310.txt rename to peps/pep-0310.rst index 09a389711..a327def99 100644 --- a/pep-0310.txt +++ b/peps/pep-0310.rst @@ -238,7 +238,7 @@ could be mentioned here. https://mail.python.org/pipermail/python-dev/2003-August/037795.html .. [3] Thread on python-dev with subject - `[Python-Dev] pre-PEP: Resource-Release Support for Generators` + ``[Python-Dev] pre-PEP: Resource-Release Support for Generators`` starting at https://mail.python.org/pipermail/python-dev/2003-August/037803.html diff --git a/pep-0311.txt b/peps/pep-0311.rst similarity index 100% rename from pep-0311.txt rename to peps/pep-0311.rst diff --git a/pep-0312.txt b/peps/pep-0312.rst similarity index 100% rename from pep-0312.txt rename to peps/pep-0312.rst diff --git a/pep-0313.txt b/peps/pep-0313.rst similarity index 100% rename from pep-0313.txt rename to peps/pep-0313.rst diff --git a/pep-0314.txt b/peps/pep-0314.rst similarity index 100% rename from pep-0314.txt rename to peps/pep-0314.rst diff --git a/pep-0315.txt b/peps/pep-0315.rst similarity index 100% rename from pep-0315.txt rename to peps/pep-0315.rst diff --git a/pep-0316.txt b/peps/pep-0316.rst similarity index 100% rename from pep-0316.txt rename to peps/pep-0316.rst diff --git a/pep-0317.txt b/peps/pep-0317.rst similarity index 100% rename from pep-0317.txt rename to peps/pep-0317.rst diff --git a/pep-0318.txt b/peps/pep-0318.rst similarity index 100% rename from pep-0318.txt rename to peps/pep-0318.rst diff --git a/pep-0319.txt b/peps/pep-0319.rst similarity index 100% rename from pep-0319.txt rename to peps/pep-0319.rst diff --git a/pep-0320.txt b/peps/pep-0320.rst similarity index 100% rename from pep-0320.txt rename to peps/pep-0320.rst diff --git a/pep-0321.txt b/peps/pep-0321.rst similarity index 100% rename from pep-0321.txt rename to peps/pep-0321.rst diff --git a/pep-0322.txt b/peps/pep-0322.rst similarity index 100% rename from pep-0322.txt rename to peps/pep-0322.rst diff --git a/pep-0323.txt b/peps/pep-0323.rst similarity index 100% rename from pep-0323.txt rename to peps/pep-0323.rst diff --git a/pep-0324.txt b/peps/pep-0324.rst similarity index 100% rename from pep-0324.txt rename to peps/pep-0324.rst diff --git a/pep-0325.txt b/peps/pep-0325.rst similarity index 100% rename from pep-0325.txt rename to peps/pep-0325.rst diff --git a/pep-0326.txt b/peps/pep-0326.rst similarity index 99% rename from pep-0326.txt rename to peps/pep-0326.rst index bf5959a8f..20ddc6f12 100644 --- a/pep-0326.txt +++ b/peps/pep-0326.rst @@ -268,7 +268,7 @@ following problem [5]_: and ``B``. Such an algorithm is a 7 line modification to the `DijkstraSP_table`_ -algorithm given above (modified lines prefixed with `*`):: +algorithm given above (modified lines prefixed with ``*``):: def DijkstraSP_table(graph, S, T): table = {} #3 diff --git a/pep-0327.txt b/peps/pep-0327.rst similarity index 100% rename from pep-0327.txt rename to peps/pep-0327.rst diff --git a/pep-0328.txt b/peps/pep-0328.rst similarity index 100% rename from pep-0328.txt rename to peps/pep-0328.rst diff --git a/pep-0329.txt b/peps/pep-0329.rst similarity index 91% rename from pep-0329.txt rename to peps/pep-0329.rst index 7b9f99b3c..b59e9c588 100644 --- a/pep-0329.txt +++ b/peps/pep-0329.rst @@ -49,14 +49,14 @@ of performance. There are currently over a hundred instances of ``while 1`` in the library. They were not replaced with the more readable ``while True`` because of performance reasons (the compiler cannot eliminate the test -because `True` is not known to always be a constant). Conversion of +because ``True`` is not known to always be a constant). Conversion of True to a constant will clarify the code while retaining performance. Many other basic Python operations run much slower because of global lookups. In try/except statements, the trapped exceptions are dynamically looked up before testing whether they match. Similarly, simple identity tests such as ``while x is not None`` -require the `None` variable to be re-looked up on every pass. +require the ``None`` variable to be re-looked up on every pass. Builtin lookups are especially egregious because the enclosing global scope must be checked first. These lookup chains devour cache space that is best used elsewhere. @@ -69,7 +69,7 @@ Proposal ======== Add a module called codetweaks.py which contains two functions, -`bind_constants()` and `bind_all()`. The first function performs +``bind_constants()`` and ``bind_all()``. The first function performs constant binding and the second recursively applies it to every function and class in a target module. @@ -80,7 +80,7 @@ the end of the script:: codetweaks.bind_all(sys.modules[__name__]) In addition to binding builtins, there are some modules (like -`sre_compile`) where it also makes sense to bind module variables +``sre_compile``) where it also makes sense to bind module variables as well as builtins into constants. @@ -97,17 +97,17 @@ Questions and Answers Every function has attributes with its bytecodes (the language of the Python virtual machine) and a table of constants. The bind - function scans the bytecodes for a `LOAD_GLOBAL` instruction and + function scans the bytecodes for a ``LOAD_GLOBAL`` instruction and checks to see whether the value is already known. If so, it adds that value to the constants table and replaces the opcode with - `LOAD_CONSTANT`. + ``LOAD_CONSTANT``. 3. When does it work? When a module is imported for the first time, python compiles the bytecode and runs the binding optimization. Subsequent imports just re-use the previous work. Each session repeats this process - (the results are not saved in `pyc` files). + (the results are not saved in ``pyc`` files). 4. How do you know this works? @@ -117,7 +117,7 @@ Questions and Answers 5. What if the module defines a variable shadowing a builtin? This does happen. For instance, True can be redefined at the module - level as `True = (1==1)`. The sample implementation below detects the + level as ``True = (1==1)``. The sample implementation below detects the shadowing and leaves the global lookup unchanged. 6. Are you the first person to recognize that most global lookups are for @@ -130,22 +130,22 @@ Questions and Answers implementations? Either do this before importing a module, or just reload the - module, or disable `codetweaks.py` (it will have a disable flag). + module, or disable ``codetweaks.py`` (it will have a disable flag). 8. How susceptible is this module to changes in Python's byte coding? - It imports `opcode.py` to protect against renumbering. Also, it - uses `LOAD_CONST` and `LOAD_GLOBAL` which are fundamental and have + It imports ``opcode.py`` to protect against renumbering. Also, it + uses ``LOAD_CONST`` and ``LOAD_GLOBAL`` which are fundamental and have been around forever. That notwithstanding, the coding scheme could change and this implementation would have to change along with - modules like `dis` which also rely on the current coding scheme. + modules like ``dis`` which also rely on the current coding scheme. 9. What is the effect on startup time? I could not measure a difference. None of the startup modules are bound except for warnings.py. Also, the binding function is very fast, making just a single pass over the code string in search of - the `LOAD_GLOBAL` opcode. + the ``LOAD_GLOBAL`` opcode. Sample Implementation diff --git a/pep-0330.txt b/peps/pep-0330.rst similarity index 100% rename from pep-0330.txt rename to peps/pep-0330.rst diff --git a/pep-0331.txt b/peps/pep-0331.rst similarity index 100% rename from pep-0331.txt rename to peps/pep-0331.rst diff --git a/pep-0332.txt b/peps/pep-0332.rst similarity index 100% rename from pep-0332.txt rename to peps/pep-0332.rst diff --git a/pep-0333.txt b/peps/pep-0333.rst similarity index 99% rename from pep-0333.txt rename to peps/pep-0333.rst index d0c6e8377..e57f89254 100644 --- a/pep-0333.txt +++ b/peps/pep-0333.rst @@ -596,9 +596,9 @@ Variable Value ``wsgi.input`` An input stream (file-like object) from which the HTTP request body can be read. (The server or gateway may perform reads on-demand as - requested by the application, or it may pre- - read the client's request body and buffer it - in-memory or on disk, or use any other + requested by the application, or it may + pre-read the client's request body and buffer + it in-memory or on disk, or use any other technique for providing such an input stream, according to its preference.) @@ -1341,8 +1341,8 @@ use only language features available in the target version, use Optional Platform-Specific File Handling ---------------------------------------- -Some operating environments provide special high-performance file- -transmission facilities, such as the Unix ``sendfile()`` call. +Some operating environments provide special high-performance +file-transmission facilities, such as the Unix ``sendfile()`` call. Servers and gateways **may** expose this functionality via an optional ``wsgi.file_wrapper`` key in the ``environ``. An application **may** use this "file wrapper" to convert a file or file-like object diff --git a/pep-0334.txt b/peps/pep-0334.rst similarity index 100% rename from pep-0334.txt rename to peps/pep-0334.rst diff --git a/pep-0335.txt b/peps/pep-0335.rst similarity index 100% rename from pep-0335.txt rename to peps/pep-0335.rst diff --git a/pep-0336.txt b/peps/pep-0336.rst similarity index 100% rename from pep-0336.txt rename to peps/pep-0336.rst diff --git a/pep-0337.txt b/peps/pep-0337.rst similarity index 100% rename from pep-0337.txt rename to peps/pep-0337.rst diff --git a/pep-0338.txt b/peps/pep-0338.rst similarity index 100% rename from pep-0338.txt rename to peps/pep-0338.rst diff --git a/pep-0339.txt b/peps/pep-0339.rst similarity index 99% rename from pep-0339.txt rename to peps/pep-0339.rst index 18e97d023..434a159c4 100644 --- a/pep-0339.txt +++ b/peps/pep-0339.rst @@ -550,7 +550,7 @@ References ---------- .. [Aho86] Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman. - `Compilers: Principles, Techniques, and Tools`, + ``Compilers: Principles, Techniques, and Tools``, http://www.amazon.com/exec/obidos/tg/detail/-/0201100886/104-0162389-6419108 .. [Wang97] Daniel C. Wang, Andrew W. Appel, Jeff L. Korn, and Chris diff --git a/pep-0340.txt b/peps/pep-0340.rst similarity index 100% rename from pep-0340.txt rename to peps/pep-0340.rst diff --git a/pep-0341.txt b/peps/pep-0341.rst similarity index 100% rename from pep-0341.txt rename to peps/pep-0341.rst diff --git a/pep-0342.txt b/peps/pep-0342.rst similarity index 100% rename from pep-0342.txt rename to peps/pep-0342.rst diff --git a/pep-0343.txt b/peps/pep-0343.rst similarity index 100% rename from pep-0343.txt rename to peps/pep-0343.rst diff --git a/pep-0344.txt b/peps/pep-0344.rst similarity index 99% rename from pep-0344.txt rename to peps/pep-0344.rst index c2c4f0efb..de5fc298d 100644 --- a/pep-0344.txt +++ b/peps/pep-0344.rst @@ -85,8 +85,8 @@ exception, the exception must be retained implicitly. To support intentional translation of an exception, there must be a way to chain exceptions explicitly. This PEP addresses both. -Several attribute names for chained exceptions have been suggested on Python- -Dev [2]_, including ``cause``, ``antecedent``, ``reason``, ``original``, +Several attribute names for chained exceptions have been suggested on +Python-Dev [2]_, including ``cause``, ``antecedent``, ``reason``, ``original``, ``chain``, ``chainedexc``, ``xc_chain``, ``excprev``, ``previous`` and ``precursor``. For an explicitly chained exception, this PEP suggests ``__cause__`` because of its specific meaning. For an implicitly chained diff --git a/pep-0345.txt b/peps/pep-0345.rst similarity index 99% rename from pep-0345.txt rename to peps/pep-0345.rst index 876f5c0f2..a9a9f359b 100644 --- a/pep-0345.txt +++ b/peps/pep-0345.rst @@ -32,7 +32,7 @@ These fields are "Requires-Python", "Requires-External", "Requires-Dist", "Platform" field. Three new fields were also added: "Maintainer", "Maintainer-email" and "Project-URL". -Last, this new version also adds `environment markers`. +Last, this new version also adds ``environment markers``. Fields ====== @@ -137,7 +137,7 @@ parser. Example:: Description: This project provides powerful math functions - |For example, you can use `sum()` to sum numbers: + |For example, you can use ``sum()`` to sum numbers: | |Example:: | diff --git a/pep-0346.txt b/peps/pep-0346.rst similarity index 100% rename from pep-0346.txt rename to peps/pep-0346.rst diff --git a/pep-0347.txt b/peps/pep-0347.rst similarity index 100% rename from pep-0347.txt rename to peps/pep-0347.rst diff --git a/pep-0348.txt b/peps/pep-0348.rst similarity index 100% rename from pep-0348.txt rename to peps/pep-0348.rst diff --git a/pep-0349.txt b/peps/pep-0349.rst similarity index 100% rename from pep-0349.txt rename to peps/pep-0349.rst diff --git a/pep-0350.txt b/peps/pep-0350.rst similarity index 100% rename from pep-0350.txt rename to peps/pep-0350.rst diff --git a/pep-0351.txt b/peps/pep-0351.rst similarity index 100% rename from pep-0351.txt rename to peps/pep-0351.rst diff --git a/pep-0352.txt b/peps/pep-0352.rst similarity index 100% rename from pep-0352.txt rename to peps/pep-0352.rst diff --git a/pep-0353.txt b/peps/pep-0353.rst similarity index 100% rename from pep-0353.txt rename to peps/pep-0353.rst diff --git a/pep-0354.txt b/peps/pep-0354.rst similarity index 100% rename from pep-0354.txt rename to peps/pep-0354.rst diff --git a/pep-0355.txt b/peps/pep-0355.rst similarity index 100% rename from pep-0355.txt rename to peps/pep-0355.rst diff --git a/pep-0356.txt b/peps/pep-0356.rst similarity index 100% rename from pep-0356.txt rename to peps/pep-0356.rst diff --git a/pep-0357.txt b/peps/pep-0357.rst similarity index 100% rename from pep-0357.txt rename to peps/pep-0357.rst diff --git a/pep-0358.txt b/peps/pep-0358.rst similarity index 100% rename from pep-0358.txt rename to peps/pep-0358.rst diff --git a/pep-0359.txt b/peps/pep-0359.rst similarity index 100% rename from pep-0359.txt rename to peps/pep-0359.rst diff --git a/pep-0360.txt b/peps/pep-0360.rst similarity index 100% rename from pep-0360.txt rename to peps/pep-0360.rst diff --git a/pep-0361.txt b/peps/pep-0361.rst similarity index 100% rename from pep-0361.txt rename to peps/pep-0361.rst diff --git a/pep-0362.txt b/peps/pep-0362.rst similarity index 100% rename from pep-0362.txt rename to peps/pep-0362.rst diff --git a/pep-0363.txt b/peps/pep-0363.rst similarity index 100% rename from pep-0363.txt rename to peps/pep-0363.rst diff --git a/pep-0364.txt b/peps/pep-0364.rst similarity index 100% rename from pep-0364.txt rename to peps/pep-0364.rst diff --git a/pep-0365.txt b/peps/pep-0365.rst similarity index 100% rename from pep-0365.txt rename to peps/pep-0365.rst diff --git a/pep-0366.txt b/peps/pep-0366.rst similarity index 100% rename from pep-0366.txt rename to peps/pep-0366.rst diff --git a/pep-0367.txt b/peps/pep-0367.rst similarity index 97% rename from pep-0367.txt rename to peps/pep-0367.rst index 89ca58778..168db7a1e 100644 --- a/pep-0367.txt +++ b/peps/pep-0367.rst @@ -122,10 +122,10 @@ Should ``super`` actually become a keyword? With this proposal, ``super`` would become a keyword to the same extent that ``None`` is a keyword. It is possible that further restricting the ``super`` -name may simplify implementation, however some are against the actual keyword- -ization of super. The simplest solution is often the correct solution and the -simplest solution may well not be adding additional keywords to the language -when they are not needed. Still, it may solve other open issues. +name may simplify implementation, however some are against the actual +keyword-ization of super. The simplest solution is often the correct solution +and the simplest solution may well not be adding additional keywords to the +language when they are not needed. Still, it may solve other open issues. Closed Issues @@ -513,10 +513,10 @@ super(self, \*args) or __super__(self, \*args) This solution only solves the problem of the type indication, does not handle differently named super methods, and is explicit about the name of the instance. It is less flexible without being able to enacted on other method -names, in cases where that is needed. One use case this fails is where a base- -class has a factory classmethod and a subclass has two factory classmethods, -both of which needing to properly make super calls to the one in the base- -class. +names, in cases where that is needed. One use case this fails is where a +base-class has a factory classmethod and a subclass has two factory +classmethods, both of which needing to properly make super calls to the one +in the base-class. super.foo(self, \*args) ----------------------- diff --git a/pep-0368.txt b/peps/pep-0368.rst similarity index 100% rename from pep-0368.txt rename to peps/pep-0368.rst diff --git a/pep-0369.txt b/peps/pep-0369.rst similarity index 100% rename from pep-0369.txt rename to peps/pep-0369.rst diff --git a/pep-0370.txt b/peps/pep-0370.rst similarity index 100% rename from pep-0370.txt rename to peps/pep-0370.rst diff --git a/pep-0371.txt b/peps/pep-0371.rst similarity index 100% rename from pep-0371.txt rename to peps/pep-0371.rst diff --git a/pep-0372.txt b/peps/pep-0372.rst similarity index 100% rename from pep-0372.txt rename to peps/pep-0372.rst diff --git a/pep-0373.txt b/peps/pep-0373.rst similarity index 100% rename from pep-0373.txt rename to peps/pep-0373.rst diff --git a/pep-0374.txt b/peps/pep-0374.rst similarity index 99% rename from pep-0374.txt rename to peps/pep-0374.rst index f6228d857..49c944e8f 100644 --- a/pep-0374.txt +++ b/peps/pep-0374.rst @@ -274,8 +274,8 @@ This identity may be associated with a full name. All of the DVCSs will query the system to get some approximation to this information, but that may not be what you want. They also -support setting this information on a per-user basis, and on a per- -project basis. Convenience commands to set these attributes vary, +support setting this information on a per-user basis, and on a +per-project basis. Convenience commands to set these attributes vary, but all allow direct editing of configuration files. Some VCSs support end-of-line (EOL) conversions on checkout/checkin. diff --git a/pep-0375.txt b/peps/pep-0375.rst similarity index 100% rename from pep-0375.txt rename to peps/pep-0375.rst diff --git a/pep-0376.txt b/peps/pep-0376.rst similarity index 99% rename from pep-0376.txt rename to peps/pep-0376.rst index 5cc2bfc8f..60119c69e 100644 --- a/pep-0376.txt +++ b/peps/pep-0376.rst @@ -7,7 +7,7 @@ Topic: Packaging Content-Type: text/x-rst Created: 22-Feb-2009 Python-Version: 2.7, 3.2 -Post-History: `22-Jun-2009 <https://mail.python.org/archives/list/python-dev@python.org/thread/ILLTIOZAULMDY5CAS6GOITEYJ4HNFATQ/#ILLTIOZAULMDY5CAS6GOITEYJ4HNFATQ>`__ +Post-History: `22-Jun-2009 <https://mail.python.org/archives/list/python-dev@python.org/thread/ILLTIOZAULMDY5CAS6GOITEYJ4HNFATQ/>`__ .. canonical-pypa-spec:: :ref:`packaging:core-metadata` diff --git a/pep-0377.txt b/peps/pep-0377.rst similarity index 100% rename from pep-0377.txt rename to peps/pep-0377.rst diff --git a/pep-0378.txt b/peps/pep-0378.rst similarity index 100% rename from pep-0378.txt rename to peps/pep-0378.rst diff --git a/pep-0379.txt b/peps/pep-0379.rst similarity index 100% rename from pep-0379.txt rename to peps/pep-0379.rst diff --git a/pep-0380.txt b/peps/pep-0380.rst similarity index 100% rename from pep-0380.txt rename to peps/pep-0380.rst diff --git a/pep-0381.txt b/peps/pep-0381.rst similarity index 89% rename from pep-0381.txt rename to peps/pep-0381.rst index 2bfad7c1e..6aac3a78c 100644 --- a/pep-0381.txt +++ b/peps/pep-0381.rst @@ -1,8 +1,6 @@ PEP: 381 Title: Mirroring infrastructure for PyPI -Version: $Revision$ -Last-Modified: $Date$ -Author: Tarek Ziadé <tarek@ziade.org>, Martin v. Löwis <martin@v.loewis.de> +Author: Tarek Ziadé <tarek@ziade.org>, Martin von Löwis <martin@v.loewis.de> Status: Withdrawn Type: Standards Track Topic: Packaging @@ -39,8 +37,8 @@ Rationale ========= PyPI is hosting over 6000 projects and is used on a daily basis -by people to build applications. Especially systems like `easy_install` -and `zc.buildout` make intensive usage of PyPI. +by people to build applications. Especially systems like ``easy_install`` +and ``zc.buildout`` make intensive usage of PyPI. For people making intensive use of PyPI, it can act as a single point of failure. People have started to set up some mirrors, both private @@ -76,19 +74,19 @@ last host name. Mirror operators should use a static address, and report planned changes to that address in advance to distutils-sig. -The new mirror also appears at `http://pypi.python.org/mirrors` +The new mirror also appears at ``http://pypi.python.org/mirrors`` which is a human-readable page that gives the list of mirrors. This page also explains how to register a new mirror. Statistics page ::::::::::::::: -PyPI provides statistics on downloads at `/stats`. This page is +PyPI provides statistics on downloads at ``/stats``. This page is calculated daily by PyPI, by reading all mirrors' local stats and summing them. -The stats are presented in daily or monthly files, under `/stats/days` -and `/stats/months`. Each file is a `bzip2` file with these formats: +The stats are presented in daily or monthly files, under ``/stats/days`` +and ``/stats/months``. Each file is a ``bzip2`` file with these formats: - YYYY-MM-DD.bz2 for daily files - YYYY-MM.bz2 for monthly files @@ -182,7 +180,7 @@ that represents the last synchronisation date the mirror maintains. The date is provided in GMT time, using the ISO 8601 format [#iso8601]_. Each mirror will be responsible to maintain its last modified date. -This page must be located at : `/last-modified` and must be a +This page must be located at : ``/last-modified`` and must be a text/plain page. Local statistics @@ -194,7 +192,7 @@ display the grand total. These statistics are in CSV-like form, with a header in the first line. It needs to obey :pep:`305`. Basically, it should be -readable by Python's `csv` module. +readable by Python's ``csv`` module. The fields in this file are: @@ -211,26 +209,26 @@ The content will look like this:: ... The counting starts the day the mirror is launched, and there is one -file per day, compressed using the `bzip2` format. Each file is named -like the day. For example, `2008-11-06.bz2` is the file for the 6th of +file per day, compressed using the ``bzip2`` format. Each file is named +like the day. For example, ``2008-11-06.bz2`` is the file for the 6th of November 2008. -They are then provided in a folder called `days`. For example: +They are then provided in a folder called ``days``. For example: - /local-stats/days/2008-11-06.bz2 - /local-stats/days/2008-11-07.bz2 - /local-stats/days/2008-11-08.bz2 -This page must be located at `/local-stats`. +This page must be located at ``/local-stats``. How a mirror should synchronize with PyPI ========================================= -A mirroring protocol called `Simple Index` was described and +A mirroring protocol called ``Simple Index`` was described and implemented by Martin v. Loewis and Jim Fulton, based on how -`easy_install` works. This section synthesizes it and gives a few -relevant links, plus a small part about `User-Agent`. +``easy_install`` works. This section synthesizes it and gives a few +relevant links, plus a small part about ``User-Agent``. The mirroring protocol :::::::::::::::::::::: @@ -269,7 +267,7 @@ How a client can use PyPI and its mirrors ::::::::::::::::::::::::::::::::::::::::: Clients that are browsing PyPI should be able to use alternative -mirrors, by getting the list of the mirrors using `last.pypi.python.org`. +mirrors, by getting the list of the mirrors using ``last.pypi.python.org``. Code example:: @@ -311,7 +309,7 @@ runs their own server where people might get the project package. However, it is strongly encouraged that a public package index follows PyPI and Distutils protocols. -In other words, the `register` and `upload` command should be +In other words, the ``register`` and ``upload`` command should be compatible with any package index server out there. Software that are compatible with PyPI and Distutils so far: @@ -376,13 +374,3 @@ Copyright ========= This document has been placed in the public domain. - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0382.txt b/peps/pep-0382.rst similarity index 97% rename from pep-0382.txt rename to peps/pep-0382.rst index 520c33c01..ecbbaeacc 100644 --- a/pep-0382.txt +++ b/peps/pep-0382.rst @@ -1,8 +1,6 @@ PEP: 382 Title: Namespace Packages -Version: $Revision$ -Last-Modified: $Date$ -Author: Martin v. Löwis <martin@v.loewis.de> +Author: Martin von Löwis <martin@v.loewis.de> Status: Rejected Type: Standards Track Content-Type: text/x-rst @@ -215,13 +213,3 @@ Copyright ========= This document has been placed in the public domain. - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0383.txt b/peps/pep-0383.rst similarity index 99% rename from pep-0383.txt rename to peps/pep-0383.rst index e13c7b953..cce198367 100644 --- a/pep-0383.txt +++ b/peps/pep-0383.rst @@ -1,6 +1,6 @@ PEP: 383 Title: Non-decodable Bytes in System Character Interfaces -Author: Martin v. Löwis <martin@v.loewis.de> +Author: Martin von Löwis <martin@v.loewis.de> Status: Final Type: Standards Track Content-Type: text/x-rst diff --git a/pep-0384.txt b/peps/pep-0384.rst similarity index 98% rename from pep-0384.txt rename to peps/pep-0384.rst index 7c7b4e495..867173da9 100644 --- a/pep-0384.txt +++ b/peps/pep-0384.rst @@ -1,8 +1,6 @@ PEP: 384 Title: Defining a Stable ABI -Version: $Revision$ -Last-Modified: $Date$ -Author: Martin v. Löwis <martin@v.loewis.de> +Author: Martin von Löwis <martin@v.loewis.de> Status: Final Type: Standards Track Content-Type: text/x-rst @@ -375,13 +373,3 @@ Copyright ========= This document has been placed in the public domain. - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0385.txt b/peps/pep-0385.rst similarity index 100% rename from pep-0385.txt rename to peps/pep-0385.rst diff --git a/pep-0386.txt b/peps/pep-0386.rst similarity index 97% rename from pep-0386.txt rename to peps/pep-0386.rst index aa7f964cb..6197dbc8a 100644 --- a/pep-0386.txt +++ b/peps/pep-0386.rst @@ -25,9 +25,9 @@ Motivation In Python there are no real restrictions yet on how a project should manage its versions, and how they should be incremented. -Distutils provides a `version` distribution meta-data field but it is freeform and +Distutils provides a ``version`` distribution meta-data field but it is freeform and current users, such as PyPI usually consider the latest version pushed as the -`latest` one, regardless of the expected semantics. +``latest`` one, regardless of the expected semantics. Distutils will soon extend its capabilities to allow distributions to express a dependency on other distributions through the ``Requires-Dist`` metadata field @@ -271,8 +271,8 @@ a particular package was using and to provide tools on top of PyPI. Distutils classes are not really used in Python projects, but the Setuptools function is quite widespread because it's used by tools like -`easy_install` [#ezinstall]_, `pip` [#pip]_ or `zc.buildout` [#zc.buildout]_ -to install dependencies of a given project. +``easy_install`` [#ezinstall]_, ``pip`` [#pip]_ or ``zc.buildout`` +[#zc.buildout]_ to install dependencies of a given project. While Setuptools *does* provide a mechanism for comparing/sorting versions, it is much preferable if the versioning spec is such that a human can make a @@ -292,7 +292,7 @@ The new versioning algorithm During Pycon, members of the Python, Ubuntu and Fedora community worked on a version standard that would be acceptable for everyone. -It's currently called `verlib` and a prototype lives at [#prototype]_. +It's currently called ``verlib`` and a prototype lives at [#prototype]_. The pseudo-format supported is:: @@ -362,9 +362,9 @@ Note that ``c`` is the preferred marker for third party projects. NormalizedVersion ----------------- -The `NormalizedVersion` class is used to hold a version and to compare it with -others. It takes a string as an argument, that contains the representation of -the version:: +The ``NormalizedVersion`` class is used to hold a version and to compare it +with others. It takes a string as an argument, that contains the representation +of the version:: >>> from verlib import NormalizedVersion >>> version = NormalizedVersion('1.0') diff --git a/pep-0387.txt b/peps/pep-0387.rst similarity index 100% rename from pep-0387.txt rename to peps/pep-0387.rst diff --git a/pep-0389.txt b/peps/pep-0389.rst similarity index 100% rename from pep-0389.txt rename to peps/pep-0389.rst diff --git a/pep-0390.txt b/peps/pep-0390.rst similarity index 99% rename from pep-0390.txt rename to peps/pep-0390.rst index f10d4fd53..502e43a4f 100644 --- a/pep-0390.txt +++ b/peps/pep-0390.rst @@ -166,8 +166,8 @@ Impact on PKG-INFO generation and PEP 314 ========================================= When ``PKG-INFO`` is generated by Distutils, every field that relies on a -condition will have that condition written at the end of the line, after a `;` -separator:: +condition will have that condition written at the end of the line, after a +``;`` separator:: Metadata-Version: 1.2 Name: distribute diff --git a/pep-0391.txt b/peps/pep-0391.rst similarity index 97% rename from pep-0391.txt rename to peps/pep-0391.rst index d6050e30f..407ecb4b4 100644 --- a/pep-0391.txt +++ b/peps/pep-0391.rst @@ -383,7 +383,7 @@ Dictionary Schema - Detail The dictionary passed to ``dictConfig()`` must contain the following keys: -* `version` - to be set to an integer value representing the schema +* ``version`` - to be set to an integer value representing the schema version. The only valid value at present is 1, but having this key allows the schema to evolve while still preserving backwards compatibility. @@ -395,7 +395,7 @@ custom instantiation is required. If so, the mechanism described above is used to instantiate; otherwise, the context is used to determine how to instantiate. -* `formatters` - the corresponding value will be a dict in which each +* ``formatters`` - the corresponding value will be a dict in which each key is a formatter id and each value is a dict describing how to configure the corresponding Formatter instance. @@ -403,7 +403,7 @@ determine how to instantiate. (with defaults of ``None``) and these are used to construct a ``logging.Formatter`` instance. -* `filters` - the corresponding value will be a dict in which each key +* ``filters`` - the corresponding value will be a dict in which each key is a filter id and each value is a dict describing how to configure the corresponding Filter instance. @@ -411,7 +411,7 @@ determine how to instantiate. empty string) and this is used to construct a ``logging.Filter`` instance. -* `handlers` - the corresponding value will be a dict in which each +* ``handlers`` - the corresponding value will be a dict in which each key is a handler id and each value is a dict describing how to configure the corresponding Handler instance. @@ -451,7 +451,7 @@ determine how to instantiate. ``logging.handlers.RotatingFileHandler`` with the keyword arguments ``filename='logconfig.log', maxBytes=1024, backupCount=3``. -* `loggers` - the corresponding value will be a dict in which each key +* ``loggers`` - the corresponding value will be a dict in which each key is a logger name and each value is a dict describing how to configure the corresponding Logger instance. @@ -470,11 +470,11 @@ determine how to instantiate. The specified loggers will be configured according to the level, propagation, filters and handlers specified. -* `root` - this will be the configuration for the root logger. +* ``root`` - this will be the configuration for the root logger. Processing of the configuration will be as for any logger, except that the ``propagate`` setting will not be applicable. -* `incremental` - whether the configuration is to be interpreted as +* ``incremental`` - whether the configuration is to be interpreted as incremental to the existing configuration. This value defaults to ``False``, which means that the specified configuration replaces the existing configuration with the same semantics as used by the @@ -483,10 +483,10 @@ determine how to instantiate. If the specified value is ``True``, the configuration is processed as described in the section on `Incremental Configuration`_, below. -* `disable_existing_loggers` - whether any existing loggers are to be +* ``disable_existing_loggers`` - whether any existing loggers are to be disabled. This setting mirrors the parameter of the same name in ``fileConfig()``. If absent, this parameter defaults to ``True``. - This value is ignored if `incremental` is ``True``. + This value is ignored if ``incremental`` is ``True``. A Working Example ----------------- diff --git a/pep-0392.txt b/peps/pep-0392.rst similarity index 100% rename from pep-0392.txt rename to peps/pep-0392.rst diff --git a/pep-0393.txt b/peps/pep-0393.rst similarity index 98% rename from pep-0393.txt rename to peps/pep-0393.rst index 2bd4d83db..bee0319fd 100644 --- a/pep-0393.txt +++ b/peps/pep-0393.rst @@ -1,8 +1,6 @@ PEP: 393 Title: Flexible String Representation -Version: $Revision$ -Last-Modified: $Date$ -Author: Martin v. Löwis <martin@v.loewis.de> +Author: Martin von Löwis <martin@v.loewis.de> Status: Final Type: Standards Track Content-Type: text/x-rst @@ -463,13 +461,3 @@ Copyright ========= This document has been placed in the public domain. - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0394.txt b/peps/pep-0394.rst similarity index 99% rename from pep-0394.txt rename to peps/pep-0394.rst index 7e6110112..c3438657f 100644 --- a/pep-0394.txt +++ b/peps/pep-0394.rst @@ -361,7 +361,7 @@ References .. [6] PEP 394 - Clarification of what "python" command should invoke (https://mail.python.org/pipermail/python-dev/2014-September/136374.html) -.. [7] PEP 394: Allow the `python` command to not be installed, and other +.. [7] PEP 394: Allow the ``python`` command to not be installed, and other minor edits (https://github.com/python/peps/pull/630) diff --git a/pep-0395.txt b/peps/pep-0395.rst similarity index 100% rename from pep-0395.txt rename to peps/pep-0395.rst diff --git a/pep-0396.txt b/peps/pep-0396.rst similarity index 100% rename from pep-0396.txt rename to peps/pep-0396.rst diff --git a/pep-0397.txt b/peps/pep-0397.rst similarity index 98% rename from pep-0397.txt rename to peps/pep-0397.rst index b0b170a56..bec6ac504 100644 --- a/pep-0397.txt +++ b/peps/pep-0397.rst @@ -1,9 +1,7 @@ PEP: 397 Title: Python launcher for Windows -Version: $Revision: a57419aee37d $ -Last-Modified: $Date: 2012/06/19 15:13:49 $ Author: Mark Hammond <mhammond@skippinet.com.au>, - Martin v. Löwis <martin@v.loewis.de> + Martin von Löwis <martin@v.loewis.de> Status: Final Type: Standards Track Content-Type: text/x-rst @@ -420,13 +418,3 @@ Copyright ========= This document has been placed in the public domain. - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0398.txt b/peps/pep-0398.rst similarity index 100% rename from pep-0398.txt rename to peps/pep-0398.rst diff --git a/pep-0399.txt b/peps/pep-0399.rst similarity index 97% rename from pep-0399.txt rename to peps/pep-0399.rst index 3fe730c98..a87ee2a10 100644 --- a/pep-0399.txt +++ b/peps/pep-0399.rst @@ -161,9 +161,9 @@ could be added. To also help with compatibility, C code should use abstract APIs on objects to prevent accidental dependence on specific types. For instance, if a function accepts a sequence then the C code should -default to using `PyObject_GetItem()` instead of something like -`PyList_GetItem()`. C code is allowed to have a fast path if the -proper `PyList_CheckExact()` is used, but otherwise APIs should work +default to using ``PyObject_GetItem()`` instead of something like +``PyList_GetItem()``. C code is allowed to have a fast path if the +proper ``PyList_CheckExact()`` is used, but otherwise APIs should work with any object that duck types to the proper interface instead of a specific type. diff --git a/pep-0400.txt b/peps/pep-0400.rst similarity index 100% rename from pep-0400.txt rename to peps/pep-0400.rst diff --git a/pep-0401.txt b/peps/pep-0401.rst similarity index 100% rename from pep-0401.txt rename to peps/pep-0401.rst diff --git a/pep-0402.txt b/peps/pep-0402.rst similarity index 98% rename from pep-0402.txt rename to peps/pep-0402.rst index 0d8a95440..318726599 100644 --- a/pep-0402.txt +++ b/peps/pep-0402.rst @@ -1,8 +1,6 @@ PEP: 402 Title: Simplified Package Layout and Partitioning -Version: $Revision$ -Last-Modified: $Date$ -Author: P.J. Eby +Author: Phillip J. Eby Status: Rejected Type: Standards Track Topic: Packaging @@ -534,7 +532,7 @@ Specifically the proposed changes and additions to ``pkgutil`` are: The implementation of this function does a simple top-down traversal of ``sys.virtual_package_paths``, and performs any necessary ``get_subpath()`` calls to identify what path entries need to be - added to the virtual path for that package, given that `path_entry` + added to the virtual path for that package, given that ``path_entry`` has been added to ``sys.path``. (Or, in the case of sub-packages, adding a derived subpath entry, based on their parent package's virtual path.) @@ -547,7 +545,7 @@ Specifically the proposed changes and additions to ``pkgutil`` are: * A new ``iter_virtual_packages(parent='')`` function to allow top-down traversal of virtual packages from ``sys.virtual_package_paths``, by yielding the child virtual - packages of `parent`. For example, calling + packages of ``parent``. For example, calling ``iter_virtual_packages("zope")`` might yield ``zope.app`` and ``zope.products`` (if they are virtual packages listed in ``sys.virtual_package_paths``), but **not** ``zope.foo.bar``. @@ -664,13 +662,3 @@ Copyright ========= This document has been placed in the public domain. - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0403.txt b/peps/pep-0403.rst similarity index 100% rename from pep-0403.txt rename to peps/pep-0403.rst diff --git a/pep-0404.txt b/peps/pep-0404.rst similarity index 100% rename from pep-0404.txt rename to peps/pep-0404.rst diff --git a/pep-0405.txt b/peps/pep-0405.rst similarity index 100% rename from pep-0405.txt rename to peps/pep-0405.rst diff --git a/pep-0406.txt b/peps/pep-0406.rst similarity index 100% rename from pep-0406.txt rename to peps/pep-0406.rst diff --git a/pep-0407.txt b/peps/pep-0407.rst similarity index 100% rename from pep-0407.txt rename to peps/pep-0407.rst diff --git a/pep-0408.txt b/peps/pep-0408.rst similarity index 100% rename from pep-0408.txt rename to peps/pep-0408.rst diff --git a/pep-0409.txt b/peps/pep-0409.rst similarity index 100% rename from pep-0409.txt rename to peps/pep-0409.rst diff --git a/pep-0410.txt b/peps/pep-0410.rst similarity index 100% rename from pep-0410.txt rename to peps/pep-0410.rst diff --git a/pep-0411.txt b/peps/pep-0411.rst similarity index 100% rename from pep-0411.txt rename to peps/pep-0411.rst diff --git a/pep-0412.txt b/peps/pep-0412.rst similarity index 100% rename from pep-0412.txt rename to peps/pep-0412.rst diff --git a/pep-0413.txt b/peps/pep-0413.rst similarity index 100% rename from pep-0413.txt rename to peps/pep-0413.rst diff --git a/pep-0414.txt b/peps/pep-0414.rst similarity index 100% rename from pep-0414.txt rename to peps/pep-0414.rst diff --git a/pep-0415.txt b/peps/pep-0415.rst similarity index 100% rename from pep-0415.txt rename to peps/pep-0415.rst diff --git a/pep-0416.txt b/peps/pep-0416.rst similarity index 100% rename from pep-0416.txt rename to peps/pep-0416.rst diff --git a/pep-0417.txt b/peps/pep-0417.rst similarity index 100% rename from pep-0417.txt rename to peps/pep-0417.rst diff --git a/pep-0418.txt b/peps/pep-0418.rst similarity index 99% rename from pep-0418.txt rename to peps/pep-0418.rst index bcf698ec8..b728a3fb3 100644 --- a/pep-0418.txt +++ b/peps/pep-0418.rst @@ -1,8 +1,9 @@ PEP: 418 Title: Add monotonic time, performance counter, and process time functions -Version: $Revision$ -Last-Modified: $Date$ -Author: Cameron Simpson <cs@cskk.id.au>, Jim Jewett <jimjjewett@gmail.com>, Stephen J. Turnbull <stephen@xemacs.org>, Victor Stinner <vstinner@python.org> +Author: Cameron Simpson <cs@cskk.id.au>, + Jim J. Jewett <jimjjewett@gmail.com>, + Stephen J. Turnbull <stephen@xemacs.org>, + Victor Stinner <vstinner@python.org> Status: Final Type: Standards Track Content-Type: text/x-rst @@ -1640,14 +1641,3 @@ Copyright ========= This document has been placed in the public domain. - - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0418/bench_time.c b/peps/pep-0418/bench_time.c similarity index 100% rename from pep-0418/bench_time.c rename to peps/pep-0418/bench_time.c diff --git a/pep-0418/clock_resolution.py b/peps/pep-0418/clock_resolution.py similarity index 100% rename from pep-0418/clock_resolution.py rename to peps/pep-0418/clock_resolution.py diff --git a/pep-0418/clockutils.py b/peps/pep-0418/clockutils.py similarity index 100% rename from pep-0418/clockutils.py rename to peps/pep-0418/clockutils.py diff --git a/pep-0419.txt b/peps/pep-0419.rst similarity index 100% rename from pep-0419.txt rename to peps/pep-0419.rst diff --git a/pep-0420.txt b/peps/pep-0420.rst similarity index 100% rename from pep-0420.txt rename to peps/pep-0420.rst diff --git a/pep-0421.txt b/peps/pep-0421.rst similarity index 100% rename from pep-0421.txt rename to peps/pep-0421.rst diff --git a/pep-0422.txt b/peps/pep-0422.rst similarity index 100% rename from pep-0422.txt rename to peps/pep-0422.rst diff --git a/pep-0423.txt b/peps/pep-0423.rst similarity index 99% rename from pep-0423.txt rename to peps/pep-0423.rst index 094fef505..0eba9486c 100644 --- a/pep-0423.txt +++ b/peps/pep-0423.rst @@ -631,7 +631,7 @@ already been registered: * in the `Python Standard Library`_. -* inside projects at `PyPI`. There is currently no helper for that. +* inside projects at ``PyPI``. There is currently no helper for that. Notice that the more projects follow the `use a single name`_ rule, the easier is the verification. diff --git a/pep-0424.txt b/peps/pep-0424.rst similarity index 100% rename from pep-0424.txt rename to peps/pep-0424.rst diff --git a/pep-0425.txt b/peps/pep-0425.rst similarity index 87% rename from pep-0425.txt rename to peps/pep-0425.rst index e627d5c98..5037b576a 100644 --- a/pep-0425.txt +++ b/peps/pep-0425.rst @@ -71,7 +71,7 @@ For example, the tag py27-none-any indicates compatible with Python 2.7 Use === -The `wheel` built package format includes these tags in its filenames, +The ``wheel`` built package format includes these tags in its filenames, of the form ``{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl``. Other package formats may have their own conventions. @@ -91,31 +91,31 @@ a distribution. Major implementations have abbreviated codes, initially: * pp: PyPy * jy: Jython -Other Python implementations should use `sys.implementation.name`. +Other Python implementations should use ``sys.implementation.name``. -The version is `py_version_nodot`. CPython gets away with no dot, -but if one is needed the underscore `_` is used instead. PyPy should -probably use its own versions here `pp18`, `pp19`. +The version is ``py_version_nodot``. CPython gets away with no dot, +but if one is needed the underscore ``_`` is used instead. PyPy should +probably use its own versions here ``pp18``, ``pp19``. -The version can be just the major version `2` or `3` `py2`, `py3` for +The version can be just the major version ``2`` or ``3`` ``py2``, ``py3`` for many pure-Python distributions. -Importantly, major-version-only tags like `py2` and `py3` are not -shorthand for `py20` and `py30`. Instead, these tags mean the packager +Importantly, major-version-only tags like ``py2`` and ``py3`` are not +shorthand for ``py20`` and ``py30``. Instead, these tags mean the packager intentionally released a cross-version-compatible distribution. A single-source Python 2/3 compatible distribution can use the compound -tag `py2.py3`. See `Compressed Tag Sets`, below. +tag ``py2.py3``. See ``Compressed Tag Sets``, below. ABI Tag ------- The ABI tag indicates which Python ABI is required by any included extension modules. For implementation-specific ABIs, the implementation -is abbreviated in the same way as the Python Tag, e.g. `cp33d` would be +is abbreviated in the same way as the Python Tag, e.g. ``cp33d`` would be the CPython 3.3 ABI with debugging. -The CPython stable ABI is `abi3` as in the shared library suffix. +The CPython stable ABI is ``abi3`` as in the shared library suffix. Implementations with a very unstable ABI may use the first 6 bytes (as 8 base64-encoded characters) of the SHA-256 hash of their source code @@ -126,8 +126,8 @@ decide how to best use the ABI tag. Platform Tag ------------ -The platform tag is simply `distutils.util.get_platform()` with all -hyphens `-` and periods `.` replaced with underscore `_`. +The platform tag is simply ``distutils.util.get_platform()`` with all +hyphens ``-`` and periods ``.`` replaced with underscore ``_``. * win32 * linux_i386 @@ -139,7 +139,7 @@ Use The tags are used by installers to decide which built distribution (if any) to download from a list of potential built distributions. The installer maintains a list of (pyver, abi, arch) tuples that it -will support. If the built distribution's tag is `in` the list, then +will support. If the built distribution's tag is ``in`` the list, then it can be installed. It is recommended that installers try to choose the most feature complete @@ -147,7 +147,7 @@ built distribution available (the one most specific to the installation environment) by default before falling back to pure Python versions published for older Python releases. Installers are also recommended to provide a way to configure and re-order the list of allowed compatibility -tags; for example, a user might accept only the `*-none-any` tags to only +tags; for example, a user might accept only the ``*-none-any`` tags to only download built packages that advertise themselves as being pure Python. Another desirable installer feature might be to include "re-compile from @@ -181,8 +181,8 @@ older version of Python): Sometimes there will be more than one supported built distribution for a particular version of a package. For example, a packager could release -a package tagged `cp33-abi3-linux_x86_64` that contains an optional C -extension and the same distribution tagged `py3-none-any` that does not. +a package tagged ``cp33-abi3-linux_x86_64`` that contains an optional C +extension and the same distribution tagged ``py3-none-any`` that does not. The index of the tag in the supported tags list breaks the tie, and the package with the C extension is installed in preference to the package without because that tag appears first in the list. @@ -194,7 +194,7 @@ To allow for compact filenames of bdists that work with more than one compatibility tag triple, each tag in a filename can instead be a '.'-separated, sorted, set of tags. For example, pip, a pure-Python package that is written to run under Python 2 and 3 with the same source -code, could distribute a bdist with the tag `py2.py3-none-any`. +code, could distribute a bdist with the tag ``py2.py3-none-any``. The full list of simple tags is:: for x in pytag.split('.'): @@ -212,8 +212,8 @@ FAQ What tags are used by default? Tools should use the most-preferred architecture dependent tag - e.g. `cp33-cp33m-win32` or the most-preferred pure python tag - e.g. `py33-none-any` by default. If the packager overrides the + e.g. ``cp33-cp33m-win32`` or the most-preferred pure python tag + e.g. ``py33-none-any`` by default. If the packager overrides the default it indicates that they intended to provide cross-Python compatibility. @@ -227,7 +227,7 @@ What tag do I use if my distribution uses a feature exclusive to the newest vers older release ``beaglevote-1.1.0`` that does not use the new feature, to get a compatible build. -Why isn't there a `.` in the Python version number? +Why isn't there a ``.`` in the Python version number? CPython has lasted 20+ years without a 3-digit major release. This should continue for some time. Other implementations may use _ as a delimiter, since both - and . delimit the surrounding filename. diff --git a/pep-0426.txt b/peps/pep-0426.rst similarity index 100% rename from pep-0426.txt rename to peps/pep-0426.rst diff --git a/pep-0426/pepsort.py b/peps/pep-0426/pepsort.py similarity index 100% rename from pep-0426/pepsort.py rename to peps/pep-0426/pepsort.py diff --git a/pep-0426/pydist-schema.json b/peps/pep-0426/pydist-schema.json similarity index 100% rename from pep-0426/pydist-schema.json rename to peps/pep-0426/pydist-schema.json diff --git a/pep-0427.txt b/peps/pep-0427.rst similarity index 100% rename from pep-0427.txt rename to peps/pep-0427.rst diff --git a/pep-0428.txt b/peps/pep-0428.rst similarity index 100% rename from pep-0428.txt rename to peps/pep-0428.rst diff --git a/pep-0429.txt b/peps/pep-0429.rst similarity index 100% rename from pep-0429.txt rename to peps/pep-0429.rst diff --git a/pep-0430.txt b/peps/pep-0430.rst similarity index 100% rename from pep-0430.txt rename to peps/pep-0430.rst diff --git a/pep-0431.txt b/peps/pep-0431.rst similarity index 99% rename from pep-0431.txt rename to peps/pep-0431.rst index 070e41eda..1b313bd14 100644 --- a/pep-0431.txt +++ b/peps/pep-0431.rst @@ -188,13 +188,13 @@ This function takes a name string that must be a string specifying a valid zoneinfo time zone, i.e. "US/Eastern", "Europe/Warsaw" or "Etc/GMT". If not given, the local time zone will be looked up. If an invalid zone name is given, or the local time zone can not be retrieved, the function raises -`UnknownTimeZoneError`. +``UnknownTimeZoneError``. The function also takes an optional path to the location of the zoneinfo database which should be used. If not specified, the function will look for databases in the following order: -1. Check if the `tzdata-update` module is installed, and then use that +1. Check if the ``tzdata-update`` module is installed, and then use that database. 2. Use the database in ``/usr/share/zoneinfo``, if it exists. diff --git a/pep-0432.txt b/peps/pep-0432.rst similarity index 100% rename from pep-0432.txt rename to peps/pep-0432.rst diff --git a/pep-0433.txt b/peps/pep-0433.rst similarity index 100% rename from pep-0433.txt rename to peps/pep-0433.rst diff --git a/pep-0433/bench_cloexec.py b/peps/pep-0433/bench_cloexec.py similarity index 100% rename from pep-0433/bench_cloexec.py rename to peps/pep-0433/bench_cloexec.py diff --git a/pep-0433/openbsd_bug.py b/peps/pep-0433/openbsd_bug.py similarity index 100% rename from pep-0433/openbsd_bug.py rename to peps/pep-0433/openbsd_bug.py diff --git a/pep-0434.txt b/peps/pep-0434.rst similarity index 100% rename from pep-0434.txt rename to peps/pep-0434.rst diff --git a/pep-0435.txt b/peps/pep-0435.rst similarity index 100% rename from pep-0435.txt rename to peps/pep-0435.rst diff --git a/pep-0436.txt b/peps/pep-0436.rst similarity index 100% rename from pep-0436.txt rename to peps/pep-0436.rst diff --git a/pep-0437.txt b/peps/pep-0437.rst similarity index 100% rename from pep-0437.txt rename to peps/pep-0437.rst diff --git a/pep-0438.txt b/peps/pep-0438.rst similarity index 100% rename from pep-0438.txt rename to peps/pep-0438.rst diff --git a/pep-0439.txt b/peps/pep-0439.rst similarity index 99% rename from pep-0439.txt rename to peps/pep-0439.rst index d354c3da4..b07a72043 100644 --- a/pep-0439.txt +++ b/peps/pep-0439.rst @@ -189,8 +189,8 @@ Implementation ============== The changes to pip required by this PEP are being tracked in that project's -issue tracker [2]_. Most notably, the addition of --bootstrap and --bootstrap- -to-system to the pip command-line. +issue tracker [2]_. Most notably, the addition of --bootstrap and +--bootstrap-to-system to the pip command-line. It would be preferable that the pip and setuptools projects distribute a wheel format download. diff --git a/pep-0440.txt b/peps/pep-0440.rst similarity index 98% rename from pep-0440.txt rename to peps/pep-0440.rst index 271a2268c..c098ddafa 100644 --- a/pep-0440.txt +++ b/peps/pep-0440.rst @@ -93,10 +93,10 @@ this scheme but MUST also include the normalizations specified below. Installation tools MAY warn the user when non-compliant or ambiguous versions are detected. -See also `Appendix B : Parsing version strings with regular expressions` which -provides a regular expression to check strict conformance with the canonical -format, as well as a more permissive regular expression accepting inputs that -may require subsequent normalization. +See also ``Appendix B : Parsing version strings with regular expressions`` +which provides a regular expression to check strict conformance with the +canonical format, as well as a more permissive regular expression accepting +inputs that may require subsequent normalization. Public version identifiers are separated into up to five segments: @@ -650,10 +650,10 @@ are permitted and MUST be ordered as shown:: .devN, aN, bN, rcN, <no suffix>, .postN -Note that `c` is considered to be semantically equivalent to `rc` and must be -sorted as if it were `rc`. Tools MAY reject the case of having the same ``N`` -for both a ``c`` and a ``rc`` in the same release segment as ambiguous and -remain in compliance with the PEP. +Note that ``c`` is considered to be semantically equivalent to ``rc`` and must +be sorted as if it were ``rc``. Tools MAY reject the case of having the same +``N`` for both a ``c`` and a ``rc`` in the same release segment as ambiguous +and remain in compliance with the PEP. Within an alpha (``1.0a1``), beta (``1.0b1``), or release candidate (``1.0rc1``, ``1.0c1``), the following suffixes are permitted and MUST be @@ -1296,7 +1296,7 @@ trailing ``\n`` character were found on PyPI. Various other normalisation rules were also added as described in the separate section on version normalisation below. -`Appendix A` shows detailed results of an analysis of PyPI distribution +``Appendix A`` shows detailed results of an analysis of PyPI distribution version information, as collected on 8th August, 2014. This analysis compares the behavior of the explicitly ordered version scheme defined in this PEP with the de facto standard defined by the behavior of setuptools. @@ -1599,7 +1599,7 @@ Metadata v2.0 guidelines versus setuptools:: Appendix B : Parsing version strings with regular expressions ============================================================= -As noted earlier in the `Public version identifiers` section, published +As noted earlier in the ``Public version identifiers`` section, published version identifiers SHOULD use the canonical format. This section provides regular expressions that can be used to test whether a version is already in that form, and if it's not, extract the various components for subsequent diff --git a/pep-0441.txt b/peps/pep-0441.rst similarity index 100% rename from pep-0441.txt rename to peps/pep-0441.rst diff --git a/pep-0442.txt b/peps/pep-0442.rst similarity index 100% rename from pep-0442.txt rename to peps/pep-0442.rst diff --git a/pep-0443.txt b/peps/pep-0443.rst similarity index 100% rename from pep-0443.txt rename to peps/pep-0443.rst diff --git a/pep-0444.txt b/peps/pep-0444.rst similarity index 100% rename from pep-0444.txt rename to peps/pep-0444.rst diff --git a/pep-0445.txt b/peps/pep-0445.rst similarity index 100% rename from pep-0445.txt rename to peps/pep-0445.rst diff --git a/pep-0446.txt b/peps/pep-0446.rst similarity index 100% rename from pep-0446.txt rename to peps/pep-0446.rst diff --git a/pep-0446/test_cloexec.py b/peps/pep-0446/test_cloexec.py similarity index 100% rename from pep-0446/test_cloexec.py rename to peps/pep-0446/test_cloexec.py diff --git a/pep-0447.txt b/peps/pep-0447.rst similarity index 99% rename from pep-0447.txt rename to peps/pep-0447.rst index ba04ecb31..626a54816 100644 --- a/pep-0447.txt +++ b/peps/pep-0447.rst @@ -297,7 +297,7 @@ to ``tp_flags`` to indicate that new slot must be used. Use of this hook by the interpreter ----------------------------------- -The new method is required for metatypes and as such is defined on `type_`. +The new method is required for metatypes and as such is defined on ``type_``. Both ``super.__getattribute__`` and ``object.__getattribute__``/`PyObject_GenericGetAttr`_ (through ``_PyType_Lookup``) use the this ``__getdescriptor__`` method when diff --git a/pep-0448.txt b/peps/pep-0448.rst similarity index 98% rename from pep-0448.txt rename to peps/pep-0448.rst index cd17a1a73..38721e8a3 100644 --- a/pep-0448.txt +++ b/peps/pep-0448.rst @@ -198,7 +198,7 @@ are already valid. These could be extended to:: f(**x for x in it) == f({**x for x in it}) However, it wasn't clear if this was the best behaviour or if it should -unpack into the arguments of the call to `f`. Since this is likely to be +unpack into the arguments of the call to ``f``. Since this is likely to be confusing and is of only very marginal utility, it is not included in this PEP. Instead, these will throw a ``SyntaxError`` and comprehensions with explicit brackets should be used instead. @@ -224,7 +224,7 @@ References .. [1] PEP accepted, "PEP 448 review", Guido van Rossum (https://mail.python.org/pipermail/python-dev/2015-February/138564.html) -.. [2] Issue 2292, "Missing `*`-unpacking generalizations", Thomas Wouters +.. [2] Issue 2292, "Missing ``*``-unpacking generalizations", Thomas Wouters (https://github.com/python/cpython/issues/46545) [3] Discussion on Python-ideas list, diff --git a/pep-0449.txt b/peps/pep-0449.rst similarity index 100% rename from pep-0449.txt rename to peps/pep-0449.rst diff --git a/pep-0450.txt b/peps/pep-0450.rst similarity index 100% rename from pep-0450.txt rename to peps/pep-0450.rst diff --git a/pep-0451.txt b/peps/pep-0451.rst similarity index 99% rename from pep-0451.txt rename to peps/pep-0451.rst index f4373b3e7..6e673e92a 100644 --- a/pep-0451.txt +++ b/peps/pep-0451.rst @@ -678,9 +678,9 @@ Other than adding ``__spec__``, none of the import-related module attributes will be changed or deprecated, though some of them could be; any such deprecation can wait until Python 4. -A module's spec will not be kept in sync with the corresponding import- -related attributes. Though they may differ, in practice they will -typically be the same. +A module's spec will not be kept in sync with the corresponding +import-related attributes. Though they may differ, in practice they +will typically be the same. One notable exception is that case where a module is run as a script by using the ``-m`` flag. In that case ``module.__spec__.name`` will @@ -716,8 +716,8 @@ Adding yet another similar method to loaders is a case of practicality. find_module() could be changed to return specs instead of loaders. This is tempting because the import APIs have suffered enough, especially considering PathEntryFinder.find_loader() was just -added in Python 3.3. However, the extra complexity and a less-than- -explicit method name aren't worth it. +added in Python 3.3. However, the extra complexity and a +less-than-explicit method name aren't worth it. The "target" parameter of find_spec() ------------------------------------- diff --git a/pep-0452.txt b/peps/pep-0452.rst similarity index 100% rename from pep-0452.txt rename to peps/pep-0452.rst diff --git a/pep-0453.txt b/peps/pep-0453.rst similarity index 100% rename from pep-0453.txt rename to peps/pep-0453.rst diff --git a/pep-0454.txt b/peps/pep-0454.rst similarity index 100% rename from pep-0454.txt rename to peps/pep-0454.rst diff --git a/pep-0455.txt b/peps/pep-0455.rst similarity index 100% rename from pep-0455.txt rename to peps/pep-0455.rst diff --git a/pep-0456.txt b/peps/pep-0456.rst similarity index 100% rename from pep-0456.txt rename to peps/pep-0456.rst diff --git a/pep-0457.txt b/peps/pep-0457.rst similarity index 99% rename from pep-0457.txt rename to peps/pep-0457.rst index 7ce06c782..bc9730ec8 100644 --- a/pep-0457.txt +++ b/peps/pep-0457.rst @@ -22,7 +22,7 @@ their position. This PEP is an Informational PEP describing the notation for use when describing APIs that use positional-only parameters (e.g. in Argument -Clinic, or in the string representation of `inspect.Signature` +Clinic, or in the string representation of ``inspect.Signature`` objects). A separate PEP, :pep:`570`, proposes elevation of this notation to full Python syntax. diff --git a/pep-0458-1.png b/peps/pep-0458-1.png similarity index 100% rename from pep-0458-1.png rename to peps/pep-0458-1.png diff --git a/pep-0458.txt b/peps/pep-0458.rst similarity index 99% rename from pep-0458.txt rename to peps/pep-0458.rst index b15d6a5aa..f16faab27 100644 --- a/pep-0458.txt +++ b/peps/pep-0458.rst @@ -339,7 +339,7 @@ of pip going forward SHOULD use TUF by default to download and verify distributi from PyPI before installing them. However, there may be unforeseen issues that might prevent users from installing or updating distributions, including pip itself, via TUF. Therefore, pip SHOULD provide an option e.g., -`--unsafely-disable-package-verification`, in order to work around such issues +``--unsafely-disable-package-verification``, in order to work around such issues until they are resolved. Note, the proposed option name is purposefully long, because a user must be helped to understand that the action is unsafe and not generally recommended. @@ -722,9 +722,9 @@ overhead for returning and new users would be around 50-54% and 114% respectively, assuming that the number of bins stay fixed. If the number of bins is increased, then the cost for all users would effectively be the cost for new users, because their cost would be dominated by the (once-in-a-while) -cost of downloading the large number of delegations in the `bins` metadata. +cost of downloading the large number of delegations in the ``bins`` metadata. If the cost for new users should prove to be too much, primarily due to the -overhead of downloading the `bins` metadata, then this subject SHOULD be +overhead of downloading the ``bins`` metadata, then this subject SHOULD be revisited before that happens. Note that changes to the number of bins on the server are transparent to the diff --git a/pep-0459.txt b/peps/pep-0459.rst similarity index 99% rename from pep-0459.txt rename to peps/pep-0459.rst index 98399fa9d..74920f90d 100644 --- a/pep-0459.txt +++ b/peps/pep-0459.rst @@ -22,7 +22,7 @@ PEP Withdrawal section in that PEP for details. In the meantime, metadata extensions will continue to be handled as they have been for past examples like ``entry_points.txt``: as additional files -installed into metadata directories alongside the main `METADATA` file. +installed into metadata directories alongside the main ``METADATA`` file. Abstract diff --git a/pep-0460.txt b/peps/pep-0460.rst similarity index 100% rename from pep-0460.txt rename to peps/pep-0460.rst diff --git a/pep-0461.txt b/peps/pep-0461.rst similarity index 99% rename from pep-0461.txt rename to peps/pep-0461.rst index 5f4f4a1c9..09036768d 100644 --- a/pep-0461.txt +++ b/peps/pep-0461.rst @@ -60,7 +60,7 @@ synonym for b). For the numeric codes, the only difference between ``str`` and ``bytes`` (or ``bytearray``) interpolation is that the results from these codes will be ASCII-encoded text, not unicode. In other words, for any numeric formatting -code `%x`:: +code ``%x``:: b"%x" % val diff --git a/pep-0462.txt b/peps/pep-0462.rst similarity index 100% rename from pep-0462.txt rename to peps/pep-0462.rst diff --git a/pep-0463.txt b/peps/pep-0463.rst similarity index 100% rename from pep-0463.txt rename to peps/pep-0463.rst diff --git a/pep-0464.txt b/peps/pep-0464.rst similarity index 100% rename from pep-0464.txt rename to peps/pep-0464.rst diff --git a/pep-0465.txt b/peps/pep-0465.rst similarity index 99% rename from pep-0465.txt rename to peps/pep-0465.rst index 5b38dd3c3..79f3ec705 100644 --- a/pep-0465.txt +++ b/peps/pep-0465.rst @@ -388,8 +388,8 @@ real-world code (i.e., all the code on Github). We checked for imports of several popular stdlib modules, a variety of numerically oriented modules, and various other extremely high-profile modules like django and lxml (the latter of which is the #1 most downloaded -package on PyPI). Starred lines indicate packages which export array- -or matrix-like objects which will adopt ``@`` if this PEP is +package on PyPI). Starred lines indicate packages which export +array- or matrix-like objects which will adopt ``@`` if this PEP is approved:: Count of Python source files on Github matching given search terms diff --git a/pep-0465/scan-ops.py b/peps/pep-0465/scan-ops.py similarity index 100% rename from pep-0465/scan-ops.py rename to peps/pep-0465/scan-ops.py diff --git a/pep-0466.txt b/peps/pep-0466.rst similarity index 100% rename from pep-0466.txt rename to peps/pep-0466.rst diff --git a/pep-0467.txt b/peps/pep-0467.rst similarity index 100% rename from pep-0467.txt rename to peps/pep-0467.rst diff --git a/pep-0468.txt b/peps/pep-0468.rst similarity index 97% rename from pep-0468.txt rename to peps/pep-0468.rst index ea74c98d6..fec816e7f 100644 --- a/pep-0468.txt +++ b/peps/pep-0468.rst @@ -201,7 +201,7 @@ idea regardless. (There is a reason those discussions were brief.) Relationship to inspect.Signature --------------------------------- -Signature objects should need no changes. The `kwargs` parameter of +Signature objects should need no changes. The ``kwargs`` parameter of inspect.BoundArguments (returned by Signature.bind() and Signature.bind_partial()) will change from a dict to an OrderedDict. @@ -321,7 +321,7 @@ application-level use of annotations. dict.__order__ -------------- -dict objects would have a new attribute, `__order__` that would default +dict objects would have a new attribute, ``__order__`` that would default to None and that in the kwargs case the interpreter would use in the same way as described above for __kworder__. @@ -329,13 +329,13 @@ Prognosis: It would mean zero impact on kwargs performance but the change would be pretty intrusive (Python uses dict a lot). Also, for the wrapper case -the interpreter would have to be careful to preserve `__order__`. +the interpreter would have to be careful to preserve ``__order__``. KWArgsDict.__order__ -------------------- -This is the same as the `dict.__order__` idea, but kwargs would be an -instance of a new minimal dict subclass that provides the `__order__` +This is the same as the ``dict.__order__`` idea, but kwargs would be an +instance of a new minimal dict subclass that provides the ``__order__`` attribute. dict would instead be unchanged. Prognosis: diff --git a/pep-0469.txt b/peps/pep-0469.rst similarity index 100% rename from pep-0469.txt rename to peps/pep-0469.rst diff --git a/pep-0470.txt b/peps/pep-0470.rst similarity index 100% rename from pep-0470.txt rename to peps/pep-0470.rst diff --git a/pep-0471.txt b/peps/pep-0471.rst similarity index 99% rename from pep-0471.txt rename to peps/pep-0471.rst index e48961f49..3a02fda57 100644 --- a/pep-0471.txt +++ b/peps/pep-0471.rst @@ -46,8 +46,8 @@ better than this.) In practice, removing all those extra system calls makes ``os.walk()`` about **8-9 times as fast on Windows**, and about **2-3 times as fast -on POSIX systems**. So we're not talking about micro- -optimizations. See more `benchmarks here`_. +on POSIX systems**. So we're not talking about +micro-optimizations. See more `benchmarks here`_. .. _`benchmarks here`: https://github.com/benhoyt/scandir#benchmarks diff --git a/pep-0472.txt b/peps/pep-0472.rst similarity index 100% rename from pep-0472.txt rename to peps/pep-0472.rst diff --git a/pep-0473.txt b/peps/pep-0473.rst similarity index 100% rename from pep-0473.txt rename to peps/pep-0473.rst diff --git a/pep-0474.txt b/peps/pep-0474.rst similarity index 100% rename from pep-0474.txt rename to peps/pep-0474.rst diff --git a/pep-0475.txt b/peps/pep-0475.rst similarity index 100% rename from pep-0475.txt rename to peps/pep-0475.rst diff --git a/pep-0476.txt b/peps/pep-0476.rst similarity index 100% rename from pep-0476.txt rename to peps/pep-0476.rst diff --git a/pep-0477.txt b/peps/pep-0477.rst similarity index 98% rename from pep-0477.txt rename to peps/pep-0477.rst index 4e8d81747..721bae141 100644 --- a/pep-0477.txt +++ b/peps/pep-0477.rst @@ -105,7 +105,7 @@ module it is explicitly allowed for downstream distributors to patch the If a downstream distributor wishes to disable ``ensurepip`` completely in Python 2.7, they should still at least provide the module and allow -`python -m ensurepip` style invocation. However it should raise errors or +``python -m ensurepip`` style invocation. However it should raise errors or otherwise exit with a non-zero exit code and print out an error on stderr directing users to what they can/should use instead of ``ensurepip``. diff --git a/pep-0478.txt b/peps/pep-0478.rst similarity index 100% rename from pep-0478.txt rename to peps/pep-0478.rst diff --git a/pep-0479.txt b/peps/pep-0479.rst similarity index 99% rename from pep-0479.txt rename to peps/pep-0479.rst index 44f4d75ae..e5cbe823e 100644 --- a/pep-0479.txt +++ b/peps/pep-0479.rst @@ -57,9 +57,9 @@ problem.) This is particularly pernicious in combination with the ``yield from`` construct of :pep:`380`, as it breaks the abstraction that a subgenerator may be factored out of a generator. That PEP notes this -limitation, but notes that "use cases for these [are] rare to non- -existent". Unfortunately while intentional use is rare, it is easy to -stumble on these cases by accident:: +limitation, but notes that "use cases for these [are] rare to +non-existent". Unfortunately while intentional use is rare, it is +easy to stumble on these cases by accident:: import contextlib diff --git a/pep-0480-1.png b/peps/pep-0480-1.png similarity index 100% rename from pep-0480-1.png rename to peps/pep-0480-1.png diff --git a/pep-0480.txt b/peps/pep-0480.rst similarity index 100% rename from pep-0480.txt rename to peps/pep-0480.rst diff --git a/pep-0481.txt b/peps/pep-0481.rst similarity index 100% rename from pep-0481.txt rename to peps/pep-0481.rst diff --git a/pep-0482.txt b/peps/pep-0482.rst similarity index 100% rename from pep-0482.txt rename to peps/pep-0482.rst diff --git a/pep-0483.txt b/peps/pep-0483.rst similarity index 100% rename from pep-0483.txt rename to peps/pep-0483.rst diff --git a/pep-0484.txt b/peps/pep-0484.rst similarity index 100% rename from pep-0484.txt rename to peps/pep-0484.rst diff --git a/pep-0485.txt b/peps/pep-0485.rst similarity index 99% rename from pep-0485.txt rename to peps/pep-0485.rst index 44caf0438..dd2bc3451 100644 --- a/pep-0485.txt +++ b/peps/pep-0485.rst @@ -590,7 +590,7 @@ No absolute tolerance Given the issues with comparing to zero, another possibility would have been to only provide a relative tolerance, and let comparison to zero fail. In this case, the user would need to do a simple absolute -test: `abs(val) < zero_tol` in the case where the comparison involved +test: ``abs(val) < zero_tol`` in the case where the comparison involved zero. However, this would not allow the same call to be used for a sequence diff --git a/pep-0486.txt b/peps/pep-0486.rst similarity index 100% rename from pep-0486.txt rename to peps/pep-0486.rst diff --git a/pep-0487.txt b/peps/pep-0487.rst similarity index 100% rename from pep-0487.txt rename to peps/pep-0487.rst diff --git a/pep-0488.txt b/peps/pep-0488.rst similarity index 99% rename from pep-0488.txt rename to peps/pep-0488.rst index 4e5b876b7..4015d6721 100644 --- a/pep-0488.txt +++ b/peps/pep-0488.rst @@ -48,7 +48,7 @@ of reading PYO files, this can lead to an interpreter using a mixture of optimization levels with its code if the user was not careful to make sure all PYO files were generated using the same optimization level (typically done by blindly deleting all PYO files and then -using the `compileall` module to compile all-new PYO files [1]_). +using the ``compileall`` module to compile all-new PYO files [1]_). This issue is only compounded when people optimize Python code beyond what the interpreter natively supports, e.g., using the astoptimizer project [2]_. @@ -76,7 +76,7 @@ To eliminate the ambiguity that PYO files present, this PEP proposes eliminating the concept of PYO files and their accompanying ``.pyo`` file extension. To allow for the optimization level to be unambiguous as well as to avoid having to regenerate optimized bytecode files -needlessly in the `__pycache__` directory, the optimization level +needlessly in the ``__pycache__`` directory, the optimization level used to generate the bytecode file will be incorporated into the bytecode file name. When no optimization level is specified, the pre-PEP ``.pyc`` file name will be used (i.e., no optimization level diff --git a/pep-0489.txt b/peps/pep-0489.rst similarity index 100% rename from pep-0489.txt rename to peps/pep-0489.rst diff --git a/pep-0490.txt b/peps/pep-0490.rst similarity index 100% rename from pep-0490.txt rename to peps/pep-0490.rst diff --git a/pep-0491.txt b/peps/pep-0491.rst similarity index 100% rename from pep-0491.txt rename to peps/pep-0491.rst diff --git a/pep-0492.txt b/peps/pep-0492.rst similarity index 99% rename from pep-0492.txt rename to peps/pep-0492.rst index 2905a2576..2bab371de 100644 --- a/pep-0492.txt +++ b/peps/pep-0492.rst @@ -173,8 +173,8 @@ in asyncio and *native coroutines* introduced by this PEP:: data = yield from read_data(db) ... -The function applies ``CO_ITERABLE_COROUTINE`` flag to generator- -function's code object, making it return a *coroutine* object. +The function applies ``CO_ITERABLE_COROUTINE`` flag to +generator-function's code object, making it return a *coroutine* object. If ``fn`` is not a *generator function*, it is wrapped. If it returns a *generator*, it will be wrapped in an *awaitable* proxy object @@ -996,8 +996,8 @@ Differences from this proposal: coroutines with ``yield`` or ``async yield`` expressions -- we wouldn't need a ``cocall`` keyword to call them. So we'll end up having ``__cocall__`` and no ``__call__`` for regular coroutines, - and having ``__call__`` and no ``__cocall__`` for coroutine- - generators. + and having ``__call__`` and no ``__cocall__`` for + coroutine-generators. 6. Requiring parentheses grammatically also introduces a whole lot of new problems. @@ -1316,8 +1316,8 @@ List of high-level changes and new protocols associated protocol with ``__aenter__`` and ``__aexit__`` methods. 4. New syntax for asynchronous iteration: ``async for``. And - associated protocol with ``__aiter__``, ``__aexit__`` and new built- - in exception ``StopAsyncIteration``. New ``tp_as_async.am_aiter`` + associated protocol with ``__aiter__``, ``__aexit__`` and new built-in + exception ``StopAsyncIteration``. New ``tp_as_async.am_aiter`` and ``tp_as_async.am_anext`` slots in ``PyTypeObject``. 5. New AST nodes: ``AsyncFunctionDef``, ``AsyncFor``, ``AsyncWith``, diff --git a/pep-0493.txt b/peps/pep-0493.rst similarity index 99% rename from pep-0493.txt rename to peps/pep-0493.rst index a286091fa..0cf4c5341 100644 --- a/pep-0493.txt +++ b/peps/pep-0493.rst @@ -52,7 +52,7 @@ offer options to switch off certificate checking entirely (by way of ``curl --insecure`` and ``wget --no-check-certificate``, respectively). At a different layer of the technology stack, Linux security modules like -`SELinux` and `AppArmor`, while enabled by default by distribution vendors, +``SELinux`` and ``AppArmor``, while enabled by default by distribution vendors, offer relatively straightforward mechanisms for turning them off. At the moment, no such convenient mechanisms exist to disable Python's @@ -121,8 +121,8 @@ or absence of the feature to be determined using the following technique:: python -c "import ssl; ssl.<_relevant_attribute>" -This will fail with `AttributeError` (and hence a non-zero return code) if the -relevant capability is not available. +This will fail with ``AttributeError`` (and hence a non-zero return code) if +the relevant capability is not available. The feature detection attributes defined by this PEP are: diff --git a/pep-0494.txt b/peps/pep-0494.rst similarity index 100% rename from pep-0494.txt rename to peps/pep-0494.rst diff --git a/pep-0495-daylightsavings.png b/peps/pep-0495-daylightsavings.png similarity index 100% rename from pep-0495-daylightsavings.png rename to peps/pep-0495-daylightsavings.png diff --git a/pep-0495-fold.svg b/peps/pep-0495-fold.svg similarity index 100% rename from pep-0495-fold.svg rename to peps/pep-0495-fold.svg diff --git a/pep-0495-gap.svg b/peps/pep-0495-gap.svg similarity index 100% rename from pep-0495-gap.svg rename to peps/pep-0495-gap.svg diff --git a/pep-0495.txt b/peps/pep-0495.rst similarity index 99% rename from pep-0495.txt rename to peps/pep-0495.rst index d096cee35..dc88ca603 100644 --- a/pep-0495.txt +++ b/peps/pep-0495.rst @@ -689,7 +689,7 @@ The following alternative names have also been considered: the original time is invalid. **which** - The `original`_ placeholder name for the `localtime` function + The `original`_ placeholder name for the ``localtime`` function branch index was `independently proposed`_ for the name of the disambiguation attribute and received `some support`_. diff --git a/pep-0496.txt b/peps/pep-0496.rst similarity index 100% rename from pep-0496.txt rename to peps/pep-0496.rst diff --git a/pep-0497.txt b/peps/pep-0497.rst similarity index 99% rename from pep-0497.txt rename to peps/pep-0497.rst index cfb857d41..18d352eb7 100644 --- a/pep-0497.txt +++ b/peps/pep-0497.rst @@ -12,7 +12,7 @@ Created: 04-Aug-2015 Rejection Notice ================ -The steering council decided that the `__past__` aspect of this proposal +The steering council decided that the ``__past__`` aspect of this proposal was too complicated for the potential benefit. The other aspect of stronger requirements for backwards-compatibility should be addressed by :pep:`387`. diff --git a/pep-0498.txt b/peps/pep-0498.rst similarity index 100% rename from pep-0498.txt rename to peps/pep-0498.rst diff --git a/pep-0499.txt b/peps/pep-0499.rst similarity index 87% rename from pep-0499.txt rename to peps/pep-0499.rst index 9cbf6f8c3..8a4862af7 100644 --- a/pep-0499.txt +++ b/peps/pep-0499.rst @@ -1,7 +1,5 @@ PEP: 499 -Title: ``python -m foo`` should bind ``sys.modules['foo']`` in addition to ``sys.modules['__main__']`` -Version: $Revision$ -Last-Modified: $Date$ +Title: ``python -m foo`` should also bind ``'foo'`` in ``sys.modules`` Author: Cameron Simpson <cs@cskk.id.au>, Chris Angelico <rosuav@gmail.com>, Joseph Jevnik <joejev@gmail.com> BDFL-Delegate: Nick Coghlan Status: Deferred @@ -182,29 +180,30 @@ Pickle compatibility If no changes are made to the pickle module, then pickles that were previously being written with the correct module name (due to a dual import) may start -being written with `__main__` as their module name instead, and hence fail to be -loaded correctly by other projects. +being written with ``__main__`` as their module name instead, and hence fail +to be loaded correctly by other projects. Scenarios to be checked: -* `python script.py` writing, `python -m script` reading -* `python -m script` writing, `python script.py` reading -* `python -m script` writing, `python some_other_app.py` reading -* `old_python -m script` writing, `new_python -m script` reading -* `new_python -m script` writing, `old_python -m script` reading +* ``python script.py`` writing, ``python -m script`` reading +* ``python -m script`` writing, ``python script.py`` reading +* ``python -m script`` writing, ``python some_other_app.py`` reading +* ``old_python -m script`` writing, ``new_python -m script`` reading +* ``new_python -m script`` writing, ``old_python -m script`` reading -Projects that special-case `__main__` -------------------------------------- +Projects that special-case ``__main__`` +--------------------------------------- In order to get the regression test suite to pass, the current reference -implementation had to patch `pdb` to avoid destroying its own global namespace. +implementation had to patch ``pdb`` to avoid destroying its own global +namespace. This suggests there may be a broader compatibility issue where some scripts are relying on direct execution and import giving different namespaces (just as -package execution keeps the two separate by executing the `__main__` submodule -in the `__main__` namespace, while the package name references the `__init__` -file as usual. +package execution keeps the two separate by executing the ``__main__`` +submodule in the ``__main__`` namespace, while the package name references +the ``__init__`` file as usual. Background @@ -219,7 +218,7 @@ module instance, not the running module instance. However, the problem has been around as long as the ``-m`` command line option and is encountered regularly, if infrequently, by others. -In addition to `issue 19702`_, the discrepancy around `__main__` +In addition to `issue 19702`_, the discrepancy around ``__main__`` is alluded to in :pep:`451` and a similar proposal (predating :pep:`451`) is described in :pep:`395` under :pep:`Fixing dual imports of the main module <395#fixing-dual-imports-of-the-main-module>`. @@ -237,13 +236,3 @@ Copyright ========= This document has been placed in the public domain. - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0500.txt b/peps/pep-0500.rst similarity index 100% rename from pep-0500.txt rename to peps/pep-0500.rst diff --git a/pep-0501.txt b/peps/pep-0501.rst similarity index 100% rename from pep-0501.txt rename to peps/pep-0501.rst diff --git a/pep-0502.txt b/peps/pep-0502.rst similarity index 100% rename from pep-0502.txt rename to peps/pep-0502.rst diff --git a/pep-0503.txt b/peps/pep-0503.rst similarity index 100% rename from pep-0503.txt rename to peps/pep-0503.rst diff --git a/pep-0504.txt b/peps/pep-0504.rst similarity index 100% rename from pep-0504.txt rename to peps/pep-0504.rst diff --git a/pep-0505.rst b/peps/pep-0505.rst similarity index 100% rename from pep-0505.rst rename to peps/pep-0505.rst diff --git a/pep-0505/find-pep505.out b/peps/pep-0505/find-pep505.out similarity index 100% rename from pep-0505/find-pep505.out rename to peps/pep-0505/find-pep505.out diff --git a/pep-0505/find-pep505.py b/peps/pep-0505/find-pep505.py similarity index 100% rename from pep-0505/find-pep505.py rename to peps/pep-0505/find-pep505.py diff --git a/pep-0505/test.py b/peps/pep-0505/test.py similarity index 100% rename from pep-0505/test.py rename to peps/pep-0505/test.py diff --git a/pep-0506.txt b/peps/pep-0506.rst similarity index 100% rename from pep-0506.txt rename to peps/pep-0506.rst diff --git a/pep-0507.txt b/peps/pep-0507.rst similarity index 100% rename from pep-0507.txt rename to peps/pep-0507.rst diff --git a/pep-0508.txt b/peps/pep-0508.rst similarity index 100% rename from pep-0508.txt rename to peps/pep-0508.rst diff --git a/pep-0509.txt b/peps/pep-0509.rst similarity index 100% rename from pep-0509.txt rename to peps/pep-0509.rst diff --git a/pep-0510.txt b/peps/pep-0510.rst similarity index 98% rename from pep-0510.txt rename to peps/pep-0510.rst index 70746c9ef..e96ba19e3 100644 --- a/pep-0510.txt +++ b/peps/pep-0510.rst @@ -125,11 +125,11 @@ Hypothetical myoptimizer module Examples in this PEP uses a hypothetical ``myoptimizer`` module which provides the following functions and types: -* ``specialize(func, code, guards)``: add the specialized code `code` - with guards `guards` to the function `func` +* ``specialize(func, code, guards)``: add the specialized code ``code`` + with guards ``guards`` to the function ``func`` * ``get_specialized(func)``: get the list of specialized codes as a list - of ``(code, guards)`` tuples where `code` is a callable or code object - and `guards` is a list of a guards + of ``(code, guards)`` tuples where ``code`` is a callable or code object + and ``guards`` is a list of a guards * ``GuardBuiltins(name)``: guard watching for ``builtins.__dict__[name]`` and ``globals()[name]``. The guard fails if ``builtins.__dict__[name]`` is replaced, or if ``globals()[name]`` @@ -323,7 +323,7 @@ The ``check()`` function checks a guard: *stack* is an array of arguments: indexed arguments followed by (*key*, *value*) pairs of keyword arguments. *na* is the number of indexed arguments. *nk* is the number of keyword arguments: the number of (*key*, -*value*) pairs. `stack` contains ``na + nk * 2`` objects. +*value*) pairs. ``stack`` contains ``na + nk * 2`` objects. Specialized code diff --git a/pep-0511.txt b/peps/pep-0511.rst similarity index 100% rename from pep-0511.txt rename to peps/pep-0511.rst diff --git a/pep-0512.txt b/peps/pep-0512.rst similarity index 99% rename from pep-0512.txt rename to peps/pep-0512.rst index e5c5917f2..4284f089c 100644 --- a/pep-0512.txt +++ b/peps/pep-0512.rst @@ -44,7 +44,7 @@ the basic steps were: Rietveld code review tool [#rietveld]_. 6. Download the patch to make sure it still applies cleanly. 7. Run the test suite manually. -8. Update the `NEWS`, `ACKS`, and "What's New" document as necessary +8. Update the ``NEWS``, ``ACKS``, and "What's New" document as necessary 9. Pull changes to avoid a merge race. 10. Commit the change manually. 11. If the change was for a bugfix release, merge into the @@ -240,7 +240,7 @@ in the Knights Who Say Ni project [#ni]_. Make old repository read-only ''''''''''''''''''''''''''''' -Updating `.hg/hgrc` in the now-old Mercurial repository in the `[hooks]` +Updating ``.hg/hgrc`` in the now-old Mercurial repository in the ``[hooks]`` section with:: pretxnchangegroup.reject = echo " * This repo has been migrated to github.com/python/peps and does not accept new commits in Mercurial!" 2>&1; exit 1 @@ -262,8 +262,8 @@ Requirements for the cpython Repository Obviously the most active and important repository currently hosted at hg.python.org [#h.p.o]_ is the cpython -repository [#cpython-repo]_. Because of its importance and high- -frequency use, it requires more tooling before being moved to GitHub +repository [#cpython-repo]_. Because of its importance and +high-frequency use, it requires more tooling before being moved to GitHub compared to the other repositories mentioned in this PEP. @@ -435,7 +435,7 @@ http://docs.openstack.org/developer/reno/. Discussions at the Sep 2016 Python core-dev sprints led to this decision compared to the rejected approaches outlined in the -`Rejected Ideas` section of this PEP. The separate files approach +``Rejected Ideas`` section of this PEP. The separate files approach seems to have the right balance of flexibility and potential tooling out of the various options while solving the motivating problem. @@ -772,7 +772,7 @@ Optional features: - `Link web content back to files that it is generated from`_ - `Handling Misc/NEWS`_ - `Bot to generate cherry-pick pull requests`_ - - Write `.github/CONTRIBUTING.md` + - Write ``.github/CONTRIBUTING.md`` (to prevent PRs that are inappropriate from even showing up and pointing to the devguide) diff --git a/pep-0513.txt b/peps/pep-0513.rst similarity index 100% rename from pep-0513.txt rename to peps/pep-0513.rst diff --git a/pep-0514.txt b/peps/pep-0514.rst similarity index 100% rename from pep-0514.txt rename to peps/pep-0514.rst diff --git a/pep-0515.txt b/peps/pep-0515.rst similarity index 100% rename from pep-0515.txt rename to peps/pep-0515.rst diff --git a/pep-0516.txt b/peps/pep-0516.rst similarity index 95% rename from pep-0516.txt rename to peps/pep-0516.rst index de209ada0..1f66518e1 100644 --- a/pep-0516.txt +++ b/peps/pep-0516.rst @@ -1,9 +1,7 @@ PEP: 516 Title: Build system abstraction for pip/conda etc -Version: $Revision$ -Last-Modified: $Date$ Author: Robert Collins <rbtcollins@hp.com>, - Nathaniel Smith <njs@pobox.com> + Nathaniel J. Smith <njs@pobox.com> BDFL-Delegate: Nick Coghlan <ncoghlan@gmail.com> Discussions-To: distutils-sig@python.org Status: Rejected @@ -218,9 +216,10 @@ develop [--prefix PREFIX] flit develop --root /tmp/ --prefix /usr/local - Should install scripts within `/tmp/usr/local/bin`, even if the Python - environment in use reports that the sys.prefix is `/usr/` which would lead - to using `/tmp/usr/bin/`. Similar logic applies for package files etc. + Should install scripts within ``/tmp/usr/local/bin``, even if the Python + environment in use reports that the sys.prefix is ``/usr/`` which would + lead to using ``/tmp/usr/bin/``. + Similar logic applies for package files etc. The build environment --------------------- @@ -309,15 +308,15 @@ When 'pip' reads this it would prepare an environment with flit in it before trying to use flit. Because flit doesn't have setup-requires support today, -`flit build_requires` would just output a constant string:: +``flit build_requires`` would just output a constant string:: {"build_requires": []} -`flit metadata` would interrogate `flit.ini` and marshal the metadata into +``flit metadata`` would interrogate ``flit.ini`` and marshal the metadata into a wheel METADATA file and output that on stdout. -`flit wheel` would need to accept a `-d` parameter that tells it where to output the -wheel (pip needs this). +``flit wheel`` would need to accept a ``-d`` parameter that tells it where to +output the wheel (pip needs this). Backwards Compatibility ======================= @@ -351,7 +350,7 @@ run by pip, and that will try and fail to install A. As such we recommend that tools which are currently used as setup-requires either ensure that they keep a `setuptools shim`_ or find their consumers and -get them all to upgrade to the use of a `pypa.json` in advance of moving +get them all to upgrade to the use of a ``pypa.json`` in advance of moving themselves. Pragmatically that is impossible, so the advice is to keep a setuptools shim indefinitely - both for projects like pbr, setuptools_scm and also projects like numpy. @@ -473,14 +472,3 @@ Copyright ========= This document has been placed in the public domain. - - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0517.txt b/peps/pep-0517.rst similarity index 100% rename from pep-0517.txt rename to peps/pep-0517.rst diff --git a/pep-0518.txt b/peps/pep-0518.rst similarity index 98% rename from pep-0518.txt rename to peps/pep-0518.rst index 744a56657..a2ce91924 100644 --- a/pep-0518.txt +++ b/peps/pep-0518.rst @@ -1,9 +1,7 @@ PEP: 518 Title: Specifying Minimum Build System Requirements for Python Projects -Version: $Revision$ -Last-Modified: $Date$ Author: Brett Cannon <brett@python.org>, - Nathaniel Smith <njs@pobox.com>, + Nathaniel J. Smith <njs@pobox.com>, Donald Stufft <donald@stufft.io> BDFL-Delegate: Nick Coghlan Discussions-To: distutils-sig@python.org @@ -559,14 +557,3 @@ Copyright ========= This document has been placed in the public domain. - - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - coding: utf-8 - End: diff --git a/pep-0519.txt b/peps/pep-0519.rst similarity index 100% rename from pep-0519.txt rename to peps/pep-0519.rst diff --git a/pep-0520.txt b/peps/pep-0520.rst similarity index 97% rename from pep-0520.txt rename to peps/pep-0520.rst index d3090f0ef..978456b90 100644 --- a/pep-0520.txt +++ b/peps/pep-0520.rst @@ -37,8 +37,8 @@ This allows introspection of the original definition order, e.g. by class decorators. Additionally, this PEP requires that the default class definition -namespace be ordered (e.g. ``OrderedDict``) by default. The long- -lived class namespace (``__dict__``) will remain a ``dict``. +namespace be ordered (e.g. ``OrderedDict``) by default. The +long-lived class namespace (``__dict__``) will remain a ``dict``. Motivation @@ -185,10 +185,10 @@ the well-worn precedent found in Python. Per Guido:: Also, note that a writeable ``__definition_order__`` allows dynamically created classes (e.g. by Cython) to still have ``__definition_order__`` -properly set. That could certainly be handled through specific class- -creation tools, such as ``type()`` or the C-API, without the need to -lose the semantics of a read-only attribute. However, with a writeable -attribute it's a moot point. +properly set. That could certainly be handled through specific +class-creation tools, such as ``type()`` or the C-API, without the need +to lose the semantics of a read-only attribute. However, with a +writeable attribute it's a moot point. Why not "__attribute_order__"? diff --git a/pep-0521.txt b/peps/pep-0521.rst similarity index 100% rename from pep-0521.txt rename to peps/pep-0521.rst diff --git a/pep-0522.txt b/peps/pep-0522.rst similarity index 100% rename from pep-0522.txt rename to peps/pep-0522.rst diff --git a/pep-0523.txt b/peps/pep-0523.rst similarity index 99% rename from pep-0523.txt rename to peps/pep-0523.rst index 2afecd3f0..4ba6fb4b8 100644 --- a/pep-0523.txt +++ b/peps/pep-0523.rst @@ -323,7 +323,7 @@ mutable. The thinking seemed to be that having a field that was mutated after the creation of the code object made the object seem mutable, even though no other aspect of code objects changed. -The view of this PEP is that the `co_extra` field doesn't change the +The view of this PEP is that the ``co_extra`` field doesn't change the fact that code objects are immutable. The field is specified in this PEP to not contain information required to make the code object usable, making it more of a caching field. It could be viewed as diff --git a/pep-0524.txt b/peps/pep-0524.rst similarity index 100% rename from pep-0524.txt rename to peps/pep-0524.rst diff --git a/pep-0525-1.png b/peps/pep-0525-1.png similarity index 100% rename from pep-0525-1.png rename to peps/pep-0525-1.png diff --git a/pep-0525.txt b/peps/pep-0525.rst similarity index 100% rename from pep-0525.txt rename to peps/pep-0525.rst diff --git a/pep-0526.txt b/peps/pep-0526.rst similarity index 100% rename from pep-0526.txt rename to peps/pep-0526.rst diff --git a/pep-0527.txt b/peps/pep-0527.rst similarity index 100% rename from pep-0527.txt rename to peps/pep-0527.rst diff --git a/pep-0528.txt b/peps/pep-0528.rst similarity index 100% rename from pep-0528.txt rename to peps/pep-0528.rst diff --git a/pep-0529.txt b/peps/pep-0529.rst similarity index 100% rename from pep-0529.txt rename to peps/pep-0529.rst diff --git a/pep-0530.txt b/peps/pep-0530.rst similarity index 100% rename from pep-0530.txt rename to peps/pep-0530.rst diff --git a/pep-0531.txt b/peps/pep-0531.rst similarity index 99% rename from pep-0531.txt rename to peps/pep-0531.rst index 0f8c76cd6..13be86600 100644 --- a/pep-0531.txt +++ b/peps/pep-0531.rst @@ -572,7 +572,7 @@ protocol definition significantly more complex, both to define and to use: a singleton tuple containing a reference to the object to be used as the result of the existence check * at the C layer, ``tp_exists`` implementations would return NULL to indicate - non-existence, and otherwise return a `PyObject *` pointer as the + non-existence, and otherwise return a ``PyObject *`` pointer as the result of the existence check Given that change, the sentinel object idiom could be rewritten as:: diff --git a/pep-0532.txt b/peps/pep-0532.rst similarity index 100% rename from pep-0532.txt rename to peps/pep-0532.rst diff --git a/pep-0532/circuit-breaking-protocol.svg b/peps/pep-0532/circuit-breaking-protocol.svg similarity index 100% rename from pep-0532/circuit-breaking-protocol.svg rename to peps/pep-0532/circuit-breaking-protocol.svg diff --git a/pep-0533.txt b/peps/pep-0533.rst similarity index 100% rename from pep-0533.txt rename to peps/pep-0533.rst diff --git a/pep-0534.txt b/peps/pep-0534.rst similarity index 99% rename from pep-0534.txt rename to peps/pep-0534.rst index 4959943d5..a572180fd 100644 --- a/pep-0534.txt +++ b/peps/pep-0534.rst @@ -326,7 +326,7 @@ a proper, distro-specific way to install missing standard library modules upon encountering a `ModuleNotFoundError`_. Some downstream distributors are already using this method of patching -`sys.excepthook` to integrate with platform crash reporting mechanisms. +``sys.excepthook`` to integrate with platform crash reporting mechanisms. .. _`site.py`: https://docs.python.org/3.7/library/site.html .. _`sitecustomize.py`: `site.py`_ diff --git a/pep-0535.txt b/peps/pep-0535.rst similarity index 100% rename from pep-0535.txt rename to peps/pep-0535.rst diff --git a/pep-0536.txt b/peps/pep-0536.rst similarity index 100% rename from pep-0536.txt rename to peps/pep-0536.rst diff --git a/pep-0537.txt b/peps/pep-0537.rst similarity index 100% rename from pep-0537.txt rename to peps/pep-0537.rst diff --git a/pep-0538.txt b/peps/pep-0538.rst similarity index 100% rename from pep-0538.txt rename to peps/pep-0538.rst diff --git a/pep-0539.txt b/peps/pep-0539.rst similarity index 100% rename from pep-0539.txt rename to peps/pep-0539.rst diff --git a/pep-0540.txt b/peps/pep-0540.rst similarity index 100% rename from pep-0540.txt rename to peps/pep-0540.rst diff --git a/pep-0541.txt b/peps/pep-0541.rst similarity index 100% rename from pep-0541.txt rename to peps/pep-0541.rst diff --git a/pep-0542.txt b/peps/pep-0542.rst similarity index 96% rename from pep-0542.txt rename to peps/pep-0542.rst index b7a7216a3..3359e8e45 100644 --- a/pep-0542.txt +++ b/peps/pep-0542.rst @@ -134,7 +134,7 @@ Similar to how decorators are syntastic sugar:: Implementation ============== -The `__name__` would follow the principles of a normal function:: +The ``__name__`` would follow the principles of a normal function:: class MyClass: def my_function1(self): @@ -146,7 +146,7 @@ The `__name__` would follow the principles of a normal function:: assert my_function1.__name__ == 'my_function1' assert my_function2.__name__ == 'my_function2' -The grammar would use `dotted_name` to support chaining of attributes:: +The grammar would use ``dotted_name`` to support chaining of attributes:: def Person.name.fset(self, value): self._name = value diff --git a/pep-0543.rst b/peps/pep-0543.rst similarity index 100% rename from pep-0543.rst rename to peps/pep-0543.rst diff --git a/pep-0544.txt b/peps/pep-0544.rst similarity index 100% rename from pep-0544.txt rename to peps/pep-0544.rst diff --git a/pep-0545.txt b/peps/pep-0545.rst similarity index 99% rename from pep-0545.txt rename to peps/pep-0545.rst index e1890acb2..d5b475436 100644 --- a/pep-0545.txt +++ b/peps/pep-0545.rst @@ -134,9 +134,9 @@ sometimes almost impossible when already registered, this solution should be avoided. Using subdomains like "es.docs.python.org" or "docs.es.python.org" is -possible but confusing ("is it `es.docs.python.org` or -`docs.es.python.org`?"). Hyphens in subdomains like -`pt-br.doc.python.org` is uncommon and SEOMoz [23]_ correlated the +possible but confusing ("is it ``es.docs.python.org`` or +``docs.es.python.org``?"). Hyphens in subdomains like +``pt-br.doc.python.org`` is uncommon and SEOMoz [23]_ correlated the presence of hyphens as a negative factor. Usage of underscores in subdomain is prohibited by the :rfc:`1123`, section 2.1. Finally, using subdomains means creating TLS certificates for each diff --git a/pep-0546.txt b/peps/pep-0546.rst similarity index 100% rename from pep-0546.txt rename to peps/pep-0546.rst diff --git a/pep-0547.rst b/peps/pep-0547.rst similarity index 100% rename from pep-0547.rst rename to peps/pep-0547.rst diff --git a/pep-0548.rst b/peps/pep-0548.rst similarity index 100% rename from pep-0548.rst rename to peps/pep-0548.rst diff --git a/pep-0549.rst b/peps/pep-0549.rst similarity index 100% rename from pep-0549.rst rename to peps/pep-0549.rst diff --git a/pep-0550-hamt_vs_dict-v2.png b/peps/pep-0550-hamt_vs_dict-v2.png similarity index 100% rename from pep-0550-hamt_vs_dict-v2.png rename to peps/pep-0550-hamt_vs_dict-v2.png diff --git a/pep-0550-hamt_vs_dict.png b/peps/pep-0550-hamt_vs_dict.png similarity index 100% rename from pep-0550-hamt_vs_dict.png rename to peps/pep-0550-hamt_vs_dict.png diff --git a/pep-0550-lookup_hamt.png b/peps/pep-0550-lookup_hamt.png similarity index 100% rename from pep-0550-lookup_hamt.png rename to peps/pep-0550-lookup_hamt.png diff --git a/pep-0550.rst b/peps/pep-0550.rst similarity index 100% rename from pep-0550.rst rename to peps/pep-0550.rst diff --git a/pep-0551.rst b/peps/pep-0551.rst similarity index 100% rename from pep-0551.rst rename to peps/pep-0551.rst diff --git a/pep-0552.rst b/peps/pep-0552.rst similarity index 100% rename from pep-0552.rst rename to peps/pep-0552.rst diff --git a/pep-0553.rst b/peps/pep-0553.rst similarity index 100% rename from pep-0553.rst rename to peps/pep-0553.rst diff --git a/pep-0554.rst b/peps/pep-0554.rst similarity index 100% rename from pep-0554.rst rename to peps/pep-0554.rst diff --git a/pep-0555.rst b/peps/pep-0555.rst similarity index 100% rename from pep-0555.rst rename to peps/pep-0555.rst diff --git a/pep-0556.rst b/peps/pep-0556.rst similarity index 100% rename from pep-0556.rst rename to peps/pep-0556.rst diff --git a/pep-0557.rst b/peps/pep-0557.rst similarity index 100% rename from pep-0557.rst rename to peps/pep-0557.rst diff --git a/pep-0558.rst b/peps/pep-0558.rst similarity index 100% rename from pep-0558.rst rename to peps/pep-0558.rst diff --git a/pep-0559.rst b/peps/pep-0559.rst similarity index 100% rename from pep-0559.rst rename to peps/pep-0559.rst diff --git a/pep-0560.rst b/peps/pep-0560.rst similarity index 100% rename from pep-0560.rst rename to peps/pep-0560.rst diff --git a/pep-0561.rst b/peps/pep-0561.rst similarity index 100% rename from pep-0561.rst rename to peps/pep-0561.rst diff --git a/pep-0562.rst b/peps/pep-0562.rst similarity index 100% rename from pep-0562.rst rename to peps/pep-0562.rst diff --git a/pep-0563.rst b/peps/pep-0563.rst similarity index 100% rename from pep-0563.rst rename to peps/pep-0563.rst diff --git a/pep-0564.rst b/peps/pep-0564.rst similarity index 100% rename from pep-0564.rst rename to peps/pep-0564.rst diff --git a/pep-0565.rst b/peps/pep-0565.rst similarity index 100% rename from pep-0565.rst rename to peps/pep-0565.rst diff --git a/pep-0566.rst b/peps/pep-0566.rst similarity index 100% rename from pep-0566.rst rename to peps/pep-0566.rst diff --git a/pep-0567.rst b/peps/pep-0567.rst similarity index 100% rename from pep-0567.rst rename to peps/pep-0567.rst diff --git a/pep-0568.rst b/peps/pep-0568.rst similarity index 100% rename from pep-0568.rst rename to peps/pep-0568.rst diff --git a/pep-0569.rst b/peps/pep-0569.rst similarity index 99% rename from pep-0569.rst rename to peps/pep-0569.rst index c4cfc6247..caa9593e2 100644 --- a/pep-0569.rst +++ b/peps/pep-0569.rst @@ -96,6 +96,7 @@ Provided irregularly on an "as-needed" basis until October 2024. - 3.8.15: Tuesday, 2022-10-11 - 3.8.16: Tuesday, 2022-12-06 - 3.8.17: Tuesday, 2023-06-06 +- 3.8.18: Thursday, 2023-08-24 Features for 3.8 diff --git a/pep-0570.rst b/peps/pep-0570.rst similarity index 100% rename from pep-0570.rst rename to peps/pep-0570.rst diff --git a/pep-0571.rst b/peps/pep-0571.rst similarity index 100% rename from pep-0571.rst rename to peps/pep-0571.rst diff --git a/pep-0572.rst b/peps/pep-0572.rst similarity index 100% rename from pep-0572.rst rename to peps/pep-0572.rst diff --git a/pep-0573.rst b/peps/pep-0573.rst similarity index 100% rename from pep-0573.rst rename to peps/pep-0573.rst diff --git a/pep-0574.rst b/peps/pep-0574.rst similarity index 100% rename from pep-0574.rst rename to peps/pep-0574.rst diff --git a/pep-0575.rst b/peps/pep-0575.rst similarity index 100% rename from pep-0575.rst rename to peps/pep-0575.rst diff --git a/pep-0576.rst b/peps/pep-0576.rst similarity index 100% rename from pep-0576.rst rename to peps/pep-0576.rst diff --git a/pep-0577.rst b/peps/pep-0577.rst similarity index 100% rename from pep-0577.rst rename to peps/pep-0577.rst diff --git a/pep-0578.rst b/peps/pep-0578.rst similarity index 100% rename from pep-0578.rst rename to peps/pep-0578.rst diff --git a/pep-0579.rst b/peps/pep-0579.rst similarity index 100% rename from pep-0579.rst rename to peps/pep-0579.rst diff --git a/pep-0580.rst b/peps/pep-0580.rst similarity index 100% rename from pep-0580.rst rename to peps/pep-0580.rst diff --git a/pep-0581.rst b/peps/pep-0581.rst similarity index 100% rename from pep-0581.rst rename to peps/pep-0581.rst diff --git a/pep-0582.rst b/peps/pep-0582.rst similarity index 100% rename from pep-0582.rst rename to peps/pep-0582.rst diff --git a/pep-0583.rst b/peps/pep-0583.rst similarity index 100% rename from pep-0583.rst rename to peps/pep-0583.rst diff --git a/pep-0584.rst b/peps/pep-0584.rst similarity index 100% rename from pep-0584.rst rename to peps/pep-0584.rst diff --git a/pep-0585.rst b/peps/pep-0585.rst similarity index 100% rename from pep-0585.rst rename to peps/pep-0585.rst diff --git a/pep-0586.rst b/peps/pep-0586.rst similarity index 100% rename from pep-0586.rst rename to peps/pep-0586.rst diff --git a/pep-0587.rst b/peps/pep-0587.rst similarity index 100% rename from pep-0587.rst rename to peps/pep-0587.rst diff --git a/pep-0588.rst b/peps/pep-0588.rst similarity index 100% rename from pep-0588.rst rename to peps/pep-0588.rst diff --git a/pep-0589.rst b/peps/pep-0589.rst similarity index 100% rename from pep-0589.rst rename to peps/pep-0589.rst diff --git a/pep-0590.rst b/peps/pep-0590.rst similarity index 100% rename from pep-0590.rst rename to peps/pep-0590.rst diff --git a/pep-0591.rst b/peps/pep-0591.rst similarity index 100% rename from pep-0591.rst rename to peps/pep-0591.rst diff --git a/pep-0592.rst b/peps/pep-0592.rst similarity index 100% rename from pep-0592.rst rename to peps/pep-0592.rst diff --git a/pep-0593.rst b/peps/pep-0593.rst similarity index 100% rename from pep-0593.rst rename to peps/pep-0593.rst diff --git a/pep-0594.rst b/peps/pep-0594.rst similarity index 100% rename from pep-0594.rst rename to peps/pep-0594.rst diff --git a/pep-0595.rst b/peps/pep-0595.rst similarity index 100% rename from pep-0595.rst rename to peps/pep-0595.rst diff --git a/pep-0596.rst b/peps/pep-0596.rst similarity index 99% rename from pep-0596.rst rename to peps/pep-0596.rst index 1196233e0..62b4d10d1 100644 --- a/pep-0596.rst +++ b/peps/pep-0596.rst @@ -93,6 +93,7 @@ Provided irregularly on an "as-needed" basis until October 2025. - 3.9.15: Tuesday, 2022-10-11 - 3.9.16: Tuesday, 2022-12-06 - 3.9.17: Tuesday, 2023-06-06 +- 3.9.18: Thursday, 2023-08-24 3.9 Lifespan diff --git a/pep-0597.rst b/peps/pep-0597.rst similarity index 100% rename from pep-0597.rst rename to peps/pep-0597.rst diff --git a/pep-0598.rst b/peps/pep-0598.rst similarity index 100% rename from pep-0598.rst rename to peps/pep-0598.rst diff --git a/pep-0599.rst b/peps/pep-0599.rst similarity index 100% rename from pep-0599.rst rename to peps/pep-0599.rst diff --git a/pep-0600.rst b/peps/pep-0600.rst similarity index 100% rename from pep-0600.rst rename to peps/pep-0600.rst diff --git a/pep-0601.txt b/peps/pep-0601.rst similarity index 100% rename from pep-0601.txt rename to peps/pep-0601.rst diff --git a/pep-0602-example-release-calendar.png b/peps/pep-0602-example-release-calendar.png similarity index 100% rename from pep-0602-example-release-calendar.png rename to peps/pep-0602-example-release-calendar.png diff --git a/pep-0602-example-release-calendar.pptx b/peps/pep-0602-example-release-calendar.pptx similarity index 100% rename from pep-0602-example-release-calendar.pptx rename to peps/pep-0602-example-release-calendar.pptx diff --git a/pep-0602-overlapping-support-matrix.png b/peps/pep-0602-overlapping-support-matrix.png similarity index 100% rename from pep-0602-overlapping-support-matrix.png rename to peps/pep-0602-overlapping-support-matrix.png diff --git a/pep-0602-overlapping-support-matrix.pptx b/peps/pep-0602-overlapping-support-matrix.pptx similarity index 100% rename from pep-0602-overlapping-support-matrix.pptx rename to peps/pep-0602-overlapping-support-matrix.pptx diff --git a/pep-0602.rst b/peps/pep-0602.rst similarity index 100% rename from pep-0602.rst rename to peps/pep-0602.rst diff --git a/pep-0603-hamt_vs_dict.png b/peps/pep-0603-hamt_vs_dict.png similarity index 100% rename from pep-0603-hamt_vs_dict.png rename to peps/pep-0603-hamt_vs_dict.png diff --git a/pep-0603-lookup_hamt.png b/peps/pep-0603-lookup_hamt.png similarity index 100% rename from pep-0603-lookup_hamt.png rename to peps/pep-0603-lookup_hamt.png diff --git a/pep-0603.rst b/peps/pep-0603.rst similarity index 100% rename from pep-0603.rst rename to peps/pep-0603.rst diff --git a/pep-0604.rst b/peps/pep-0604.rst similarity index 100% rename from pep-0604.rst rename to peps/pep-0604.rst diff --git a/pep-0605-example-release-calendar.png b/peps/pep-0605-example-release-calendar.png similarity index 100% rename from pep-0605-example-release-calendar.png rename to peps/pep-0605-example-release-calendar.png diff --git a/pep-0605-overlapping-support-matrix.png b/peps/pep-0605-overlapping-support-matrix.png similarity index 100% rename from pep-0605-overlapping-support-matrix.png rename to peps/pep-0605-overlapping-support-matrix.png diff --git a/pep-0605.rst b/peps/pep-0605.rst similarity index 100% rename from pep-0605.rst rename to peps/pep-0605.rst diff --git a/pep-0605/example-release-calendar.odp b/peps/pep-0605/example-release-calendar.odp similarity index 100% rename from pep-0605/example-release-calendar.odp rename to peps/pep-0605/example-release-calendar.odp diff --git a/pep-0605/overlapping-support-matrix.odp b/peps/pep-0605/overlapping-support-matrix.odp similarity index 100% rename from pep-0605/overlapping-support-matrix.odp rename to peps/pep-0605/overlapping-support-matrix.odp diff --git a/pep-0606.rst b/peps/pep-0606.rst similarity index 100% rename from pep-0606.rst rename to peps/pep-0606.rst diff --git a/pep-0607.rst b/peps/pep-0607.rst similarity index 100% rename from pep-0607.rst rename to peps/pep-0607.rst diff --git a/pep-0608.rst b/peps/pep-0608.rst similarity index 100% rename from pep-0608.rst rename to peps/pep-0608.rst diff --git a/pep-0609.rst b/peps/pep-0609.rst similarity index 100% rename from pep-0609.rst rename to peps/pep-0609.rst diff --git a/pep-0610.rst b/peps/pep-0610.rst similarity index 100% rename from pep-0610.rst rename to peps/pep-0610.rst diff --git a/pep-0611.rst b/peps/pep-0611.rst similarity index 100% rename from pep-0611.rst rename to peps/pep-0611.rst diff --git a/pep-0612.rst b/peps/pep-0612.rst similarity index 100% rename from pep-0612.rst rename to peps/pep-0612.rst diff --git a/pep-0613.rst b/peps/pep-0613.rst similarity index 100% rename from pep-0613.rst rename to peps/pep-0613.rst diff --git a/pep-0614.rst b/peps/pep-0614.rst similarity index 100% rename from pep-0614.rst rename to peps/pep-0614.rst diff --git a/pep-0615.rst b/peps/pep-0615.rst similarity index 100% rename from pep-0615.rst rename to peps/pep-0615.rst diff --git a/pep-0616.rst b/peps/pep-0616.rst similarity index 100% rename from pep-0616.rst rename to peps/pep-0616.rst diff --git a/pep-0617.rst b/peps/pep-0617.rst similarity index 100% rename from pep-0617.rst rename to peps/pep-0617.rst diff --git a/pep-0618.rst b/peps/pep-0618.rst similarity index 100% rename from pep-0618.rst rename to peps/pep-0618.rst diff --git a/pep-0619.rst b/peps/pep-0619.rst similarity index 94% rename from pep-0619.rst rename to peps/pep-0619.rst index 71b70ae0b..e60052486 100644 --- a/pep-0619.rst +++ b/peps/pep-0619.rst @@ -74,8 +74,14 @@ Actual: - 3.10.10: Wednesday, 2023-02-08 - 3.10.11: Wednesday, 2023-04-05 (final regular bugfix release with binary installers) -- 3.10.12: Tuesday, 2023-06-06 +Source-only security fix releases +--------------------------------- + +Provided irregularly on an "as-needed" basis until October 2026. + +- 3.10.12: Tuesday, 2023-06-06 +- 3.10.13: Thursday, 2023-08-24 3.10 Lifespan ------------- diff --git a/pep-0620.rst b/peps/pep-0620.rst similarity index 100% rename from pep-0620.rst rename to peps/pep-0620.rst diff --git a/pep-0621.rst b/peps/pep-0621.rst similarity index 100% rename from pep-0621.rst rename to peps/pep-0621.rst diff --git a/pep-0622.rst b/peps/pep-0622.rst similarity index 100% rename from pep-0622.rst rename to peps/pep-0622.rst diff --git a/pep-0623.rst b/peps/pep-0623.rst similarity index 98% rename from pep-0623.rst rename to peps/pep-0623.rst index ce0227d55..0d182add1 100644 --- a/pep-0623.rst +++ b/peps/pep-0623.rst @@ -8,7 +8,7 @@ Type: Standards Track Content-Type: text/x-rst Created: 25-Jun-2020 Python-Version: 3.10 -Resolution: https://mail.python.org/archives/list/python-dev@python.org/thread/VQKDIZLZ6HF2MLTNCUFURK2IFTXVQEYA/#VQKDIZLZ6HF2MLTNCUFURK2IFTXVQEYA +Resolution: https://mail.python.org/archives/list/python-dev@python.org/thread/VQKDIZLZ6HF2MLTNCUFURK2IFTXVQEYA/ Abstract diff --git a/pep-0624.rst b/peps/pep-0624.rst similarity index 100% rename from pep-0624.rst rename to peps/pep-0624.rst diff --git a/pep-0625.rst b/peps/pep-0625.rst similarity index 100% rename from pep-0625.rst rename to peps/pep-0625.rst diff --git a/pep-0626.rst b/peps/pep-0626.rst similarity index 100% rename from pep-0626.rst rename to peps/pep-0626.rst diff --git a/pep-0627.rst b/peps/pep-0627.rst similarity index 100% rename from pep-0627.rst rename to peps/pep-0627.rst diff --git a/pep-0628.txt b/peps/pep-0628.rst similarity index 100% rename from pep-0628.txt rename to peps/pep-0628.rst diff --git a/pep-0629.rst b/peps/pep-0629.rst similarity index 100% rename from pep-0629.rst rename to peps/pep-0629.rst diff --git a/pep-0630.rst b/peps/pep-0630.rst similarity index 100% rename from pep-0630.rst rename to peps/pep-0630.rst diff --git a/pep-0631.rst b/peps/pep-0631.rst similarity index 100% rename from pep-0631.rst rename to peps/pep-0631.rst diff --git a/pep-0632.rst b/peps/pep-0632.rst similarity index 100% rename from pep-0632.rst rename to peps/pep-0632.rst diff --git a/pep-0633.rst b/peps/pep-0633.rst similarity index 100% rename from pep-0633.rst rename to peps/pep-0633.rst diff --git a/pep-0634.rst b/peps/pep-0634.rst similarity index 100% rename from pep-0634.rst rename to peps/pep-0634.rst diff --git a/pep-0635.rst b/peps/pep-0635.rst similarity index 100% rename from pep-0635.rst rename to peps/pep-0635.rst diff --git a/pep-0636.rst b/peps/pep-0636.rst similarity index 100% rename from pep-0636.rst rename to peps/pep-0636.rst diff --git a/pep-0637.rst b/peps/pep-0637.rst similarity index 100% rename from pep-0637.rst rename to peps/pep-0637.rst diff --git a/pep-0638.rst b/peps/pep-0638.rst similarity index 100% rename from pep-0638.rst rename to peps/pep-0638.rst diff --git a/pep-0639.rst b/peps/pep-0639.rst similarity index 100% rename from pep-0639.rst rename to peps/pep-0639.rst diff --git a/pep-0640.rst b/peps/pep-0640.rst similarity index 100% rename from pep-0640.rst rename to peps/pep-0640.rst diff --git a/pep-0641.rst b/peps/pep-0641.rst similarity index 100% rename from pep-0641.rst rename to peps/pep-0641.rst diff --git a/pep-0642.rst b/peps/pep-0642.rst similarity index 100% rename from pep-0642.rst rename to peps/pep-0642.rst diff --git a/pep-0643.rst b/peps/pep-0643.rst similarity index 100% rename from pep-0643.rst rename to peps/pep-0643.rst diff --git a/pep-0644.rst b/peps/pep-0644.rst similarity index 100% rename from pep-0644.rst rename to peps/pep-0644.rst diff --git a/pep-0645.rst b/peps/pep-0645.rst similarity index 100% rename from pep-0645.rst rename to peps/pep-0645.rst diff --git a/pep-0646.rst b/peps/pep-0646.rst similarity index 100% rename from pep-0646.rst rename to peps/pep-0646.rst diff --git a/pep-0647.rst b/peps/pep-0647.rst similarity index 100% rename from pep-0647.rst rename to peps/pep-0647.rst diff --git a/pep-0648.rst b/peps/pep-0648.rst similarity index 100% rename from pep-0648.rst rename to peps/pep-0648.rst diff --git a/pep-0649.rst b/peps/pep-0649.rst similarity index 99% rename from pep-0649.rst rename to peps/pep-0649.rst index ad6c64afb..e91eb99ff 100644 --- a/pep-0649.rst +++ b/peps/pep-0649.rst @@ -932,7 +932,7 @@ will also require constructing a matching "fake locals" dictionary, which for ``FORWARDREF`` format will be pre-populated with the relevant locals dict. The "fake globals" environment will also have to create -a fake "closure", a tuple of ``FowardRef`` objects +a fake "closure", a tuple of ``ForwardRef`` objects pre-created with the names of the free variables referenced by the ``__annotate__`` method. @@ -1330,7 +1330,7 @@ These codebases examined the annotation strings *without evaluating them,* instead relying on identity checks or simple lexical analysis on the strings. -This PEP supports these technqiues too. But users will need +This PEP supports these techniques too. But users will need to port their code to it. First, user code will need to use ``inspect.get_annotations`` or ``typing.get_type_hints`` to access the annotations; they won't be able to simply get the diff --git a/pep-0650.rst b/peps/pep-0650.rst similarity index 100% rename from pep-0650.rst rename to peps/pep-0650.rst diff --git a/pep-0651.rst b/peps/pep-0651.rst similarity index 100% rename from pep-0651.rst rename to peps/pep-0651.rst diff --git a/pep-0652.rst b/peps/pep-0652.rst similarity index 99% rename from pep-0652.rst rename to peps/pep-0652.rst index 720f41e15..19bdff979 100644 --- a/pep-0652.rst +++ b/peps/pep-0652.rst @@ -210,7 +210,7 @@ The following will be generated from the ABI manifest: * Source for the Windows shared library, ``PC/python3dll.c``. * Input for documentation (see below). -* Test case that checks the runtime availablility of symbols (see below). +* Test case that checks the runtime availability of symbols (see below). The following will be checked against the Stable ABI manifest as part of continuous integration: diff --git a/pep-0653.rst b/peps/pep-0653.rst similarity index 99% rename from pep-0653.rst rename to peps/pep-0653.rst index 7e81a306b..bfb251122 100644 --- a/pep-0653.rst +++ b/peps/pep-0653.rst @@ -156,7 +156,7 @@ Semantics of the matching process In the following, all variables of the form ``$var`` are temporary variables and are not visible to the Python program. They may be visible via introspection, but that is an implementation detail and should not be relied on. -The psuedo-statement ``FAIL`` is used to signify that matching failed for this pattern and that matching should move to the next pattern. +The pseudo-statement ``FAIL`` is used to signify that matching failed for this pattern and that matching should move to the next pattern. If control reaches the end of the translation without reaching a ``FAIL``, then it has matched, and following patterns are ignored. Variables of the form ``$ALL_CAPS`` are meta-variables holding a syntactic element, they are not normal variables. @@ -165,7 +165,7 @@ but an unpacking of ``$items`` into the variables that ``$VARS`` holds. For example, with the abstract syntax ``case [$VARS]:``, and the concrete syntax ``case[a, b]:`` then ``$VARS`` would hold the variables ``(a, b)``, not the values of those variables. -The psuedo-function ``QUOTE`` takes a variable and returns the name of that variable. +The pseudo-function ``QUOTE`` takes a variable and returns the name of that variable. For example, if the meta-variable ``$VAR`` held the variable ``foo`` then ``QUOTE($VAR) == "foo"``. All additional code listed below that is not present in the original source will not trigger line events, conforming to :pep:`626`. diff --git a/pep-0654.rst b/peps/pep-0654.rst similarity index 100% rename from pep-0654.rst rename to peps/pep-0654.rst diff --git a/pep-0655.rst b/peps/pep-0655.rst similarity index 100% rename from pep-0655.rst rename to peps/pep-0655.rst diff --git a/pep-0656.rst b/peps/pep-0656.rst similarity index 100% rename from pep-0656.rst rename to peps/pep-0656.rst diff --git a/pep-0657.rst b/peps/pep-0657.rst similarity index 100% rename from pep-0657.rst rename to peps/pep-0657.rst diff --git a/pep-0658.rst b/peps/pep-0658.rst similarity index 100% rename from pep-0658.rst rename to peps/pep-0658.rst diff --git a/pep-0659.rst b/peps/pep-0659.rst similarity index 100% rename from pep-0659.rst rename to peps/pep-0659.rst diff --git a/pep-0660.rst b/peps/pep-0660.rst similarity index 100% rename from pep-0660.rst rename to peps/pep-0660.rst diff --git a/pep-0661.rst b/peps/pep-0661.rst similarity index 100% rename from pep-0661.rst rename to peps/pep-0661.rst diff --git a/pep-0662.rst b/peps/pep-0662.rst similarity index 100% rename from pep-0662.rst rename to peps/pep-0662.rst diff --git a/pep-0662/pep-0662-editable.json b/peps/pep-0662/pep-0662-editable.json similarity index 100% rename from pep-0662/pep-0662-editable.json rename to peps/pep-0662/pep-0662-editable.json diff --git a/pep-0663.txt b/peps/pep-0663.rst similarity index 100% rename from pep-0663.txt rename to peps/pep-0663.rst diff --git a/pep-0664.rst b/peps/pep-0664.rst similarity index 99% rename from pep-0664.rst rename to peps/pep-0664.rst index 27ab755b8..6530677bc 100644 --- a/pep-0664.rst +++ b/peps/pep-0664.rst @@ -70,10 +70,10 @@ Actual: - 3.11.2: Wednesday, 2023-02-08 - 3.11.3: Wednesday, 2023-04-05 - 3.11.4: Tuesday, 2023-06-06 +- 3.11.5: Thursday, 2023-08-24 Expected: -- 3.11.5: Monday, 2023-08-07 - 3.11.6: Monday, 2023-10-02 - 3.11.7: Monday, 2023-12-04 - 3.11.8: Monday, 2024-02-05 diff --git a/pep-0665.rst b/peps/pep-0665.rst similarity index 99% rename from pep-0665.rst rename to peps/pep-0665.rst index ec999f54f..923dc83d9 100644 --- a/pep-0665.rst +++ b/peps/pep-0665.rst @@ -740,7 +740,7 @@ Second, consumers of requirements files like cloud providers would also accept lock files. At this point the PEP would have permeated out far enough to be on -par with requirements files in terms of general accpetance and +par with requirements files in terms of general acceptance and potentially more if projects had dropped their own lock files for this PEP. diff --git a/pep-0666.txt b/peps/pep-0666.rst similarity index 100% rename from pep-0666.txt rename to peps/pep-0666.rst diff --git a/pep-0667.rst b/peps/pep-0667.rst similarity index 100% rename from pep-0667.rst rename to peps/pep-0667.rst diff --git a/pep-0668.rst b/peps/pep-0668.rst similarity index 100% rename from pep-0668.rst rename to peps/pep-0668.rst diff --git a/pep-0669.rst b/peps/pep-0669.rst similarity index 100% rename from pep-0669.rst rename to peps/pep-0669.rst diff --git a/pep-0670.rst b/peps/pep-0670.rst similarity index 100% rename from pep-0670.rst rename to peps/pep-0670.rst diff --git a/pep-0671.rst b/peps/pep-0671.rst similarity index 100% rename from pep-0671.rst rename to peps/pep-0671.rst diff --git a/pep-0672.rst b/peps/pep-0672.rst similarity index 100% rename from pep-0672.rst rename to peps/pep-0672.rst diff --git a/pep-0673.rst b/peps/pep-0673.rst similarity index 100% rename from pep-0673.rst rename to peps/pep-0673.rst diff --git a/pep-0674.rst b/peps/pep-0674.rst similarity index 100% rename from pep-0674.rst rename to peps/pep-0674.rst diff --git a/pep-0675.rst b/peps/pep-0675.rst similarity index 100% rename from pep-0675.rst rename to peps/pep-0675.rst diff --git a/pep-0676.rst b/peps/pep-0676.rst similarity index 100% rename from pep-0676.rst rename to peps/pep-0676.rst diff --git a/pep-0677.rst b/peps/pep-0677.rst similarity index 100% rename from pep-0677.rst rename to peps/pep-0677.rst diff --git a/pep-0678.rst b/peps/pep-0678.rst similarity index 100% rename from pep-0678.rst rename to peps/pep-0678.rst diff --git a/pep-0679.rst b/peps/pep-0679.rst similarity index 100% rename from pep-0679.rst rename to peps/pep-0679.rst diff --git a/pep-0680.rst b/peps/pep-0680.rst similarity index 100% rename from pep-0680.rst rename to peps/pep-0680.rst diff --git a/pep-0681.rst b/peps/pep-0681.rst similarity index 100% rename from pep-0681.rst rename to peps/pep-0681.rst diff --git a/pep-0682.rst b/peps/pep-0682.rst similarity index 100% rename from pep-0682.rst rename to peps/pep-0682.rst diff --git a/pep-0683.rst b/peps/pep-0683.rst similarity index 100% rename from pep-0683.rst rename to peps/pep-0683.rst diff --git a/pep-0684.rst b/peps/pep-0684.rst similarity index 99% rename from pep-0684.rst rename to peps/pep-0684.rst index c8964309c..65c9e5fa1 100644 --- a/pep-0684.rst +++ b/peps/pep-0684.rst @@ -682,7 +682,7 @@ Open Issues (isolation) but doesn't work under a per-interpreter GIL? (See `Extension Module Thread Safety`_.) * If it is likely enough, what can we do to help extension maintainers - mitigate the problem and enjoy use under a per-intepreter GIL? + mitigate the problem and enjoy use under a per-interpreter GIL? * What would be a better (scarier-sounding) name for ``allow_all_extensions``? diff --git a/pep-0685.rst b/peps/pep-0685.rst similarity index 100% rename from pep-0685.rst rename to peps/pep-0685.rst diff --git a/pep-0686.rst b/peps/pep-0686.rst similarity index 100% rename from pep-0686.rst rename to peps/pep-0686.rst diff --git a/pep-0687.rst b/peps/pep-0687.rst similarity index 100% rename from pep-0687.rst rename to peps/pep-0687.rst diff --git a/pep-0688.rst b/peps/pep-0688.rst similarity index 100% rename from pep-0688.rst rename to peps/pep-0688.rst diff --git a/pep-0689.rst b/peps/pep-0689.rst similarity index 100% rename from pep-0689.rst rename to peps/pep-0689.rst diff --git a/pep-0690.rst b/peps/pep-0690.rst similarity index 99% rename from pep-0690.rst rename to peps/pep-0690.rst index 3df31c961..593f117cd 100644 --- a/pep-0690.rst +++ b/peps/pep-0690.rst @@ -507,7 +507,7 @@ For authors of C extension modules, the proposed public C API is as follows: * ``PyDict_NextWithError()``, works the same way as ``PyDict_Next()``, with the exception it propagates any errors to the caller by returning ``0`` and - setting an exception. The caller should use ``PyErr_Ocurred()`` to check for any + setting an exception. The caller should use ``PyErr_Occurred()`` to check for any errors. diff --git a/pep-0691.rst b/peps/pep-0691.rst similarity index 100% rename from pep-0691.rst rename to peps/pep-0691.rst diff --git a/pep-0692.rst b/peps/pep-0692.rst similarity index 100% rename from pep-0692.rst rename to peps/pep-0692.rst diff --git a/pep-0693.rst b/peps/pep-0693.rst similarity index 96% rename from pep-0693.rst rename to peps/pep-0693.rst index dba00cf3a..092c798e2 100644 --- a/pep-0693.rst +++ b/peps/pep-0693.rst @@ -56,10 +56,11 @@ Actual: - 3.12.0 beta 3: Monday, 2023-06-19 - 3.12.0 beta 4: Tuesday, 2023-07-11 - 3.12.0 candidate 1: Sunday, 2023-08-06 +- 3.12.0 candidate 2: Wednesday, 2023-09-06 +- 3.12.0 candidate 3: Tuesday, 2023-09-19 Expected: -- 3.12.0 candidate 2: Monday, 2023-09-04 - 3.12.0 final: Monday, 2023-10-02 Subsequent bugfix releases every two months. diff --git a/pep-0694.rst b/peps/pep-0694.rst similarity index 100% rename from pep-0694.rst rename to peps/pep-0694.rst diff --git a/pep-0695.rst b/peps/pep-0695.rst similarity index 99% rename from pep-0695.rst rename to peps/pep-0695.rst index 4ecada5a0..182a3eb52 100644 --- a/pep-0695.rst +++ b/peps/pep-0695.rst @@ -1495,16 +1495,22 @@ upper and lower bounds on a type. .. code-block:: julia - // Generic struct; type parameter with upper and lower bounds - struct StructA{T} where Int <: T <: Number + # Generic struct; type parameter with upper and lower bounds + # Valid for T in (Int64, Signed, Integer, Real, Number) + struct Container{Int <: T <: Number} x::T end - // Generic function - function func1{T <: Real}(v::Container{T}) + # Generic function + function func1(v::Container{T}) where T <: Real end - // Alternate form of generic function - function func2(v::Container{T} where T <: Real) + # Alternate forms of generic function + function func2(v::Container{T} where T <: Real) end + function func3(v::Container{<: Real}) end + + # Tuple types are covariant + # Valid for func4((2//3, 3.5)) + function func4(t::Tuple{Real,Real}) end Dart ---- diff --git a/pep-0696.rst b/peps/pep-0696.rst similarity index 98% rename from pep-0696.rst rename to peps/pep-0696.rst index ce1678adc..7e8f7f1e1 100644 --- a/pep-0696.rst +++ b/peps/pep-0696.rst @@ -382,6 +382,21 @@ Function Defaults functions as ensuring the ``default`` is returned in every code path where the ``TypeVarLike`` can go unsolved is too hard to implement. +Binding rules +------------- + +``TypeVarLikes`` defaults should be bound by attribute access +(including call and subscript). + +.. code-block:: python + + class Foo[T = int]: + def meth(self) -> Self: + return self + + reveal_type(Foo.meth) # type is (self: Foo[int]) -> Foo[int] + + Implementation -------------- diff --git a/pep-0697.rst b/peps/pep-0697.rst similarity index 100% rename from pep-0697.rst rename to peps/pep-0697.rst diff --git a/pep-0698.rst b/peps/pep-0698.rst similarity index 100% rename from pep-0698.rst rename to peps/pep-0698.rst diff --git a/pep-0699.rst b/peps/pep-0699.rst similarity index 100% rename from pep-0699.rst rename to peps/pep-0699.rst diff --git a/pep-0700.rst b/peps/pep-0700.rst similarity index 100% rename from pep-0700.rst rename to peps/pep-0700.rst diff --git a/pep-0701.rst b/peps/pep-0701.rst similarity index 100% rename from pep-0701.rst rename to peps/pep-0701.rst diff --git a/pep-0702.rst b/peps/pep-0702.rst similarity index 84% rename from pep-0702.rst rename to peps/pep-0702.rst index d1c4c1c2c..d81d42ad2 100644 --- a/pep-0702.rst +++ b/peps/pep-0702.rst @@ -7,7 +7,7 @@ Type: Standards Track Topic: Typing Content-Type: text/x-rst Created: 30-Dec-2022 -Python-Version: 3.12 +Python-Version: 3.13 Post-History: `01-Jan-2023 <https://mail.python.org/archives/list/typing-sig@python.org/thread/AKTFUYW3WDT7R7PGRIJQZMYHMDJNE4QH/>`__, `22-Jan-2023 <https://discuss.python.org/t/pep-702-marking-deprecations-using-the-type-system/23036>`__ @@ -15,7 +15,7 @@ Post-History: `01-Jan-2023 <https://mail.python.org/archives/list/typing-sig@pyt Abstract ======== -This PEP adds an ``@typing.deprecated()`` decorator that marks a class or function +This PEP adds an ``@warnings.deprecated()`` decorator that marks a class or function as deprecated, enabling static checkers to warn when it is used. By default, this decorator will also raise a runtime ``DeprecationWarning``. @@ -99,7 +99,7 @@ There are similar existing third-party tools: Specification ============= -A new decorator ``@deprecated()`` is added to the :mod:`typing` module. This +A new decorator ``@deprecated()`` is added to the :mod:`warnings` module. This decorator can be used on a class, function or method to mark it as deprecated. This includes :class:`typing.TypedDict` and :class:`typing.NamedTuple` definitions. With overloaded functions, the decorator may be applied to individual overloads, @@ -132,16 +132,20 @@ For deprecated classes and functions, this includes: * If ``import *`` is used, usage of deprecated objects from the module (``from module import *; x = deprecated_object()``) * ``from`` imports (``from module import deprecated_object``) +* Any syntax that indirectly triggers a call to the function. For example, + if the ``__add__`` method of a class ``C`` is deprecated, then + the code ``C() + C()`` should trigger a diagnostic. Similarly, if the + setter of a property is marked deprecated, attempts to set the property + should trigger a diagnostic. -There are some additional scenarios where deprecations could come into play: +If a method is marked with the :func:`typing.override` decorator from :pep:`698` +and the base class method it overrides is deprecated, the type checker should +produce a diagnostic. -* An object implements a :class:`typing.Protocol`, but one of the methods - required for protocol compliance is deprecated. -* A class uses the ``@override`` decorator from :pep:`698` to assert that - its method overrides a base class method, but the base class method is - deprecated. - -As these scenarios appear complex and relatively unlikely to come up in practice, +There are additional scenarios where deprecations could come into play. +For example, an object may implement a :class:`typing.Protocol`, but one +of the methods required for protocol compliance is deprecated. +As scenarios such as this one appear complex and relatively unlikely to come up in practice, this PEP does not mandate that type checkers detect them. Example @@ -151,7 +155,7 @@ As an example, consider this library stub named ``library.pyi``: .. code-block:: python - from typing import deprecated + from warnings import deprecated @deprecated("Use Spam instead") class Ham: ... @@ -165,6 +169,20 @@ As an example, consider this library stub named ``library.pyi``: @overload def foo(x: str) -> str: ... + class Spam: + @deprecated("There is enough spam in the world") + def __add__(self, other: object) -> object: ... + + @property + @deprecated("All spam will be equally greasy") + def greasy(self) -> float: ... + + @property + def shape(self) -> str: ... + @shape.setter + @deprecated("Shapes are becoming immutable") + def shape(self, value: str) -> None: ... + Here is how type checkers should handle usage of this library: .. code-block:: python @@ -181,6 +199,15 @@ Here is how type checkers should handle usage of this library: ham = Ham() # no error (already reported above) + spam = library.Spam() + spam + 1 # error: Use of deprecated method Spam.__add__. There is enough spam in the world. + spam.greasy # error: Use of deprecated property Spam.greasy. All spam will be equally greasy. + spam.shape # no error + spam.shape = "cube" # error: Use of deprecated property setter Spam.shape. Shapes are becoming immutable. + +The exact wording of the diagnostics is up to the type checker and is not part +of the specification. + Runtime behavior ---------------- @@ -209,6 +236,7 @@ To accommodate runtime introspection, the decorator sets an attribute ``__deprecated__`` on the object it is passed, as well as on the wrapper callables it generates for deprecated classes and functions. The value of the attribute is the message passed to the decorator. +Decorating objecs that do not allow setting this attribute is not supported. If a ``Protocol`` with the ``@runtime_checkable`` decorator is marked as deprecated, the ``__deprecated__`` attribute should not be considered a member of the protocol, @@ -263,7 +291,8 @@ Reference implementation ======================== A runtime implementation of the ``@deprecated`` decorator is -`available <https://github.com/python/typing_extensions/pull/105>`__. +available in the `typing-extensions <https://pypi.org/project/typing-extensions/>`_ +library since version 4.5.0. The ``pyanalyze`` type checker has `prototype support <https://github.com/quora/pyanalyze/pull/578>`__ for emitting deprecation errors, as does @@ -309,6 +338,15 @@ show that this feature is not commonly needed. Features for deprecating more kinds of objects could be added in a future PEP. +Placing the decorator in the ``typing`` module +---------------------------------------------- + +An earlier version of this PEP proposed placing the ``@deprecated`` +decorator in the :mod:`typing` module. However, there was feedback +that it would be unexpected for a decorator in the :mod:`typing` module +to have runtime behavior. Therefore, the PEP now proposes adding the +decorator the :mod:`warnings` module instead. + Acknowledgments =============== diff --git a/pep-0703.rst b/peps/pep-0703.rst similarity index 99% rename from pep-0703.rst rename to peps/pep-0703.rst index be2a8e994..61fd12d66 100644 --- a/pep-0703.rst +++ b/peps/pep-0703.rst @@ -10,7 +10,6 @@ Created: 09-Jan-2023 Python-Version: 3.13 Post-History: `09-Jan-2023 <https://discuss.python.org/t/22606>`__, `04-May-2023 <https://discuss.python.org/t/26503>`__ -Resolution: Abstract @@ -331,8 +330,8 @@ and python.org downloads. A new build configuration flag, CPython with support for running without the global interpreter lock. When built with ``--disable-gil``, CPython will define the ``Py_NOGIL`` -macro in Python/patchlevel.h. The ABI tag will include the letter "n" -(for "nogil"). +macro in Python/patchlevel.h. The ABI tag will include the letter "t" +(for "threading"). The ``--disable-gil`` builds of CPython will still support optionally running with the GIL enabled at runtime (see `PYTHONGIL Environment @@ -474,7 +473,7 @@ field. Default (``0b00``) """""""""""""""""" -Objects are intitially created in the default state. This is the only +Objects are initially created in the default state. This is the only state that allows for the quick deallocation code path. Otherwise, the thread must merge the local and shared reference count fields, which requires an atomic compare-and-swap. @@ -1311,7 +1310,7 @@ The ``PYTHONGIL=0`` override is important because extensions that are not thread-safe can still be useful in multi-threaded applications. For example, one may want to use the extension from only a single thread or guard access by locks. For context, there are already some extensions -that aren not thread-safe even with the GIL, and users already have to +that are not thread-safe even with the GIL, and users already have to take these sorts of steps. The ``PYTHONGIL=1`` override is sometimes useful for debugging. diff --git a/pep-0704.rst b/peps/pep-0704.rst similarity index 100% rename from pep-0704.rst rename to peps/pep-0704.rst diff --git a/pep-0705.rst b/peps/pep-0705.rst similarity index 100% rename from pep-0705.rst rename to peps/pep-0705.rst diff --git a/pep-0706.rst b/peps/pep-0706.rst similarity index 100% rename from pep-0706.rst rename to peps/pep-0706.rst diff --git a/pep-0707.rst b/peps/pep-0707.rst similarity index 100% rename from pep-0707.rst rename to peps/pep-0707.rst diff --git a/pep-0708.rst b/peps/pep-0708.rst similarity index 100% rename from pep-0708.rst rename to peps/pep-0708.rst diff --git a/pep-0709.rst b/peps/pep-0709.rst similarity index 100% rename from pep-0709.rst rename to peps/pep-0709.rst diff --git a/pep-0710.rst b/peps/pep-0710.rst similarity index 100% rename from pep-0710.rst rename to peps/pep-0710.rst diff --git a/pep-0711.rst b/peps/pep-0711.rst similarity index 100% rename from pep-0711.rst rename to peps/pep-0711.rst diff --git a/pep-0712.rst b/peps/pep-0712.rst similarity index 65% rename from pep-0712.rst rename to peps/pep-0712.rst index 768c7d6fa..cbf9f51b5 100644 --- a/pep-0712.rst +++ b/peps/pep-0712.rst @@ -20,7 +20,7 @@ Abstract several common dataclass-like libraries, such as attrs, Pydantic, and object relational mapper (ORM) packages such as SQLAlchemy and Django. -A common feature these libraries provide over the standard library +A common feature other libraries provide over the standard library implementation is the ability for the library to convert arguments given at initialization time into the types expected for each field using a user-provided conversion function. @@ -71,12 +71,16 @@ New ``converter`` parameter --------------------------- This specification introduces a new parameter named ``converter`` to the -:func:`dataclasses.field` function. When an ``__init__`` method is synthesized -by ``dataclass``-like semantics, if an argument is provided for the field, the -``dataclass`` object's attribute will be assigned the result of calling the -converter on the provided argument. If no argument is given and the field was -constructed with a default value, the ``dataclass`` object's attribute will be -assigned the result of calling the converter on the provided default. +:func:`dataclasses.field` function. If provided, it represents a single-argument +callable used to convert all values when assigning to the associated attribute. + +For frozen dataclasses, the converter is only used inside a ``dataclass``-synthesized +``__init__`` when setting the attribute. For non-frozen dataclasses, the converter +is used for all attribute assignment (E.g. ``obj.attr = value``), which includes +assignment of default values. + +The converter is not used when reading attributes, as the attributes should already +have been converted. Adding this parameter also implies the following changes: @@ -91,13 +95,14 @@ Example @dataclasses.dataclass class InventoryItem: - # `converter` as a type + # `converter` as a type (including a GenericAlias). id: int = dataclasses.field(converter=int) skus: tuple[int, ...] = dataclasses.field(converter=tuple[int, ...]) - # `converter` as a callable + # `converter` as a callable. + vendor: str | None = dataclasses.field(converter=str_or_none)) names: tuple[str, ...] = dataclasses.field( converter=lambda names: tuple(map(str.lower, names)) - ) + ) # Note that lambdas are supported, but discouraged as they are untyped. # The default value is also converted; therefore the following is not a # type error. @@ -105,12 +110,31 @@ Example converter=pathlib.PurePosixPath, default="assets/unknown.png" ) - item1 = InventoryItem("1", [234, 765], ["PYTHON PLUSHIE", "FLUFFY SNAKE"]) - # item1 would have the following values: - # id=1 - # skus=(234, 765) - # names=('python plushie', 'fluffy snake') - # stock_image_path=pathlib.PurePosixPath("assets/unknown.png") + # Default value conversion extends to `default_factory`; + # therefore the following is also not a type error. + shelves: tuple = dataclasses.field( + converter=tuple, default_factory=list + ) + + item1 = InventoryItem( + "1", + [234, 765], + None, + ["PYTHON PLUSHIE", "FLUFFY SNAKE"] + ) + # item1's repr would be (with added newlines for readability): + # InventoryItem( + # id=1, + # skus=(234, 765), + # vendor=None, + # names=('PYTHON PLUSHIE', 'FLUFFY SNAKE'), + # stock_image_path=PurePosixPath('assets/unknown.png'), + # shelves=() + # ) + + # Attribute assignment also participates in conversion. + item1.skus = [555] + # item1's skus attribute is now (555,). Impact on typing @@ -124,12 +148,13 @@ In other words, the argument provided for the converter parameter must be compatible with ``Callable[[T], X]`` where ``T`` is the input type for the converter and ``X`` is the output type of the converter. -Type-checking the default value -''''''''''''''''''''''''''''''' +Type-checking ``default`` and ``default_factory`` +''''''''''''''''''''''''''''''''''''''''''''''''' -Because the ``default`` value is unconditionally converted using ``converter``, -if arguments for both ``converter`` and ``default`` are provided to -:func:`dataclasses.field`, the ``default`` argument's type should be checked +Because default values are unconditionally converted using ``converter``, if +an argument for ``converter`` is provided alongside either ``default`` or +``default_factory``, the type of the default (the ``default`` argument if +provided, otherwise the return value of ``default_factory``) should be checked using the type of the single argument to the ``converter`` callable. Converter return type @@ -141,22 +166,17 @@ a type that's more specialized (such as a converter returning a ``list[int]`` for a field annotated as ``list``, or a converter returning an ``int`` for a field annotated as ``int | str``). -Example -''''''' +Indirection of allowable argument types +--------------------------------------- -.. code-block:: python - - @dataclasses.dataclass - class Example: - my_int: int = dataclasses.field(converter=int) - my_tuple: tuple[int, ...] = dataclasses.field(converter=tuple[int, ...]) - my_cheese: Cheese = dataclasses.field(converter=make_cheese) - - # Although the default value is of type `str` and the field is declared to - # be of type `pathlib.Path`, this is not a type error because the default - # value will be converted. - tmpdir: pathlib.Path = dataclasses.field(default="/tmp", converter=pathlib.Path) +One downside introduced by this PEP is that knowing what argument types are +allowed in the dataclass' ``__init__`` and during attribute assignment is not +immediately obvious from reading the dataclass. The allowable types are defined +by the converter. +This is true when reading code from source, however typing-related aides such +as ``typing.reveal_type`` and "IntelliSense" in an IDE should make it easy to know +exactly what types are allowed without having to read any source code. Backward Compatibility @@ -184,6 +204,10 @@ users of converters are likely to encounter. Such pitfalls include: * Needing to handle values that are already of the correct type. * Avoiding lambdas for converters, as the synthesized ``__init__`` parameter's type will become ``Any``. +* Forgetting to convert values in the bodies of user-defined ``__init__`` in + frozen dataclasses. +* Forgetting to convert values in the bodies of user-defined ``__setattr__`` in + non-frozen dataclasses. Reference Implementation ======================== @@ -213,15 +237,18 @@ Not converting default values There are pros and cons with both converting and not converting default values. Leaving default values as-is allows type-checkers and dataclass authors to expect that the type of the default matches the type of the field. However, -converting default values has two large advantages: +converting default values has three large advantages: -1. Compatibility with attrs. Attrs unconditionally uses the converter to - convert the default value. +1. Consistency. Unconditionally converting all values that are assigned to the + attribute, involves fewer "special rules" that users must remember. 2. Simpler defaults. Allowing the default value to have the same type as user-provided values means dataclass authors get the same conveniences as their callers. +3. Compatibility with attrs. Attrs unconditionally uses the converter to + convert default values. + Automatic conversion using the field's type ------------------------------------------- @@ -233,7 +260,33 @@ appear to be similar to this approach. This works well for fairly simple types, but leads to ambiguity in expected behavior for complex types such as generics. E.g. For ``tuple[int, ...]`` it is ambiguous if the converter is supposed to simply convert an iterable to a tuple, -or if it is additionally supposed to convert each element type to ``int``. +or if it is additionally supposed to convert each element type to ``int``. Or +for ``int | None``, which isn't callable. + +Deducing the attribute type from the return type of the converter +----------------------------------------------------------------- + +Another idea would be to allow the user to omit the attribute's type annotation +if providing a ``field`` with a ``converter`` argument. Although this would +reduce the common repetition this PEP introduces (e.g. ``x: str = field(converter=str)``), +it isn't clear how to best support this while maintaining the current dataclass +semantics (namely, that the attribute order is preserved for things like the +synthesized ``__init__``, or ``dataclasses.fields``). This is because there isn't +an easy way in Python (today) to get the annotation-only attributes interspersed +with un-annotated attributes in the order they were defined. + +A sentinel annotation could be applied (e.g. ``x: FromConverter = ...``), +however this breaks a fundamental assumption of type annotations. + +Lastly, this is feasible if *all* fields (including those without a converter) +were assigned to ``dataclasses.field``, which means the class' own namespace +holds the order, however this trades repetition of type+converter with +repetition of field assignment. The end result is no gain or loss of repetition, +but with the added complexity of dataclasses semantics. + +This PEP doesn't suggest it can't or shouldn't be done. Just that it isn't +included in this PEP. + References ========== diff --git a/pep-0713.rst b/peps/pep-0713.rst similarity index 93% rename from pep-0713.rst rename to peps/pep-0713.rst index eff1c283c..65d117b1d 100644 --- a/pep-0713.rst +++ b/peps/pep-0713.rst @@ -3,12 +3,23 @@ Title: Callable Modules Author: Amethyst Reese <amethyst at n7.gg> Sponsor: Łukasz Langa <lukasz at python.org> Discussions-To: https://discuss.python.org/t/pep-713-callable-modules/26127 -Status: Draft +Status: Rejected Type: Standards Track Content-Type: text/x-rst Created: 20-Apr-2023 Python-Version: 3.12 Post-History: `23-Apr-2023 <https://discuss.python.org/t/pep-713-callable-modules/26127>`__ +Resolution: https://discuss.python.org/t/26127/86 + + +Rejection Notice +================ + +The Steering Council didn't feel that there was a compelling reason to +have this PEP, even though it clearly could be done from a consistency +point of view. +If this idea comes up again in the future, this is a useful prior +discussion to refer to. Abstract @@ -61,7 +72,7 @@ without further hooks from the author, even with ``from module import member``. It also results in a "module" object that is missing all of the special module attributes, including ``__doc__``, ``__package__``, ``__path__``, etc. -Alteratively, a module author can choose to override the module's ``__class__`` +Alternatively, a module author can choose to override the module's ``__class__`` property with a custom type that provides a callable interface:: # fancy.py diff --git a/pep-0714.rst b/peps/pep-0714.rst similarity index 100% rename from pep-0714.rst rename to peps/pep-0714.rst diff --git a/pep-0715.rst b/peps/pep-0715.rst similarity index 99% rename from pep-0715.rst rename to peps/pep-0715.rst index dea766947..f84967822 100644 --- a/pep-0715.rst +++ b/peps/pep-0715.rst @@ -4,7 +4,7 @@ Author: William Woodruff <william@yossarian.net> Sponsor: Donald Stufft <donald@stufft.io> PEP-Delegate: Donald Stufft <donald@stufft.io> Discussions-To: https://discuss.python.org/t/27610 -Status: Accepted +Status: Final Type: Standards Track Topic: Packaging Content-Type: text/x-rst diff --git a/pep-0718.rst b/peps/pep-0718.rst similarity index 100% rename from pep-0718.rst rename to peps/pep-0718.rst diff --git a/pep-0719.rst b/peps/pep-0719.rst similarity index 100% rename from pep-0719.rst rename to peps/pep-0719.rst diff --git a/pep-0720.rst b/peps/pep-0720.rst similarity index 99% rename from pep-0720.rst rename to peps/pep-0720.rst index 87c00ae7c..14e0708c7 100644 --- a/pep-0720.rst +++ b/peps/pep-0720.rst @@ -7,7 +7,6 @@ Type: Informational Content-Type: text/x-rst Created: 01-Jul-2023 Python-Version: 3.12 -Resolution: Abstract diff --git a/pep-0721.rst b/peps/pep-0721.rst similarity index 100% rename from pep-0721.rst rename to peps/pep-0721.rst diff --git a/pep-0722.rst b/peps/pep-0722.rst similarity index 93% rename from pep-0722.rst rename to peps/pep-0722.rst index 23437d1ff..56599d99a 100644 --- a/pep-0722.rst +++ b/peps/pep-0722.rst @@ -391,6 +391,12 @@ Furthermore, TOML, by its nature, is a flexible format intended to support very general data structures. There are *many* ways of writing a simple list of strings in it, and it will not be clear to inexperienced users which form to use. +Another potential issue is that using a generalised TOML parser can `in some cases +<https://discuss.python.org/t/pep-722-dependency-specification-for-single-file-scripts/29905/275>`__ +result in a measurable performance overhead. Startup time is often quoted as an +issue when running small scripts, so this may be a problem for script runners that +are aiming for high performance. + And finally, there will be tools that expect to *write* dependency data into scripts -- for example, an IDE with a feature that automatically adds an import and a dependency specifier when you reference a library function. While @@ -553,6 +559,53 @@ it would be ``requires: pywin32 ; sys_platform == 'win32'``). (Thanks to Jean Abou-Samra for the clear discussion of this point) +Why not simply manage the environment at runtime? +------------------------------------------------- + +Another approach to running scripts with dependencies is simply to manage those +dependencies at runtime. This can be done by using a library that makes packages +available. There are many options for implementing such a library, for example +by installing them directly into the user's environment or by manipulating +``sys.path`` to make them available from a local cache. + +These approaches are not incompatible with this PEP. An API such as + +.. code:: python + + env_mgr.install("rich") + env_mgr.install("click") + + import rich + import click + + ... + +is certainly feasible. However, such a library could be written without the need +for any new standards, and as far as the PEP author is aware, this has not +happened. This suggests that an approach like this is not as attractive as it +first seems. There is also the bootstrapping issue of making the ``env_mgr`` +library available in the first place. And finally, this approach doesn't +actually offer any interoperability benefits, as it does not use a standard form +for the dependency list, and so other tools cannot access the data. + +In any case, such a library could still benefit from this proposal, as it could +include an API to read the packages to install from the script dependency block. +This would give the same functionality while allowing interoperability with +other tools that support this specification. + +.. code:: python + + # Script Dependencies: + # rich + # click + env_mgr.install_dependencies(__file__) + + import rich + import click + + ... + + Why not just set up a Python project with a ``pyproject.toml``? --------------------------------------------------------------- @@ -587,7 +640,7 @@ detail. So in order to make a standard, two things would be required: 1. A standardised replacement for the requirements file format. -2. A standard for how to locate the requiements file for a given script. +2. A standard for how to locate the requirements file for a given script. The first item is a significant undertaking. It has been discussed on a number of occasions, but so far no-one has attempted to actually do it. The most likely diff --git a/pep-0723.rst b/peps/pep-0723.rst similarity index 64% rename from pep-0723.rst rename to peps/pep-0723.rst index ae36e5ba3..90170af41 100644 --- a/pep-0723.rst +++ b/peps/pep-0723.rst @@ -11,6 +11,7 @@ Content-Type: text/x-rst Created: 04-Aug-2023 Post-History: `04-Aug-2023 <https://discuss.python.org/t/30979>`__, `06-Aug-2023 <https://discuss.python.org/t/31151>`__, + `23-Aug-2023 <https://discuss.python.org/t/32149>`__, Replaces: 722 @@ -55,23 +56,21 @@ and not in an external file. We choose to follow the latest developments of other modern packaging ecosystems (namely `Go`__ and provisionally `Rust`__) by embedding the existing -`metadata standard <pyproject metadata_>`_ that is used to describe -projects. +file used to describe projects, in our case ``pyproject.toml``. __ https://github.com/erning/gorun __ https://rust-lang.github.io/rfcs/3424-cargo-script.html The format is intended to bridge the gap between different types of users -of Python. Knowledge of how to write project metadata will be directly -transferable to all use cases, whether writing a script or maintaining a -project that is distributed via PyPI. Additionally, users will benefit from -seamless interoperability with tools that are already familiar with the format. +of Python. Users will benefit from seamless interoperability with tools that +already work with TOML. One of the central themes we discovered from the recent `packaging survey <https://discuss.python.org/t/22420>`__ is that users have begun getting frustrated with the lack of unification regarding both tooling -and specs. Adding yet another way to define metadata, even for a currently -unsatisfied use case, would further fragment the community. +and specs. Adding yet another metadata format (like :pep:`722` syntax for a +list of dependencies), even for a currently unsatisfied use case, would +further fragment the community. The following are some of the use cases that this PEP wishes to support: @@ -79,19 +78,15 @@ The following are some of the use cases that this PEP wishes to support: an example, the interface would be simply ``hatch run /path/to/script.py [args]`` and Hatch will manage the environment for that script. Such tools could be used as shebang lines on - non-Windows systems e.g. ``#!/usr/bin/env hatch run``. You would also be - able to enter a shell into that environment like other projects by doing - ``hatch -p /path/to/script.py shell`` since the project flag would learn - that project metadata could be read from a single file. + non-Windows systems e.g. ``#!/usr/bin/env hatch run``. * A script that desires to transition to a directory-type project. A user may be rapidly prototyping locally or in a remote REPL environment and then decide to transition to a more formal project layout if their idea works out. This intermediate script stage would be very useful to have fully - reproducible bug reports. By using the same metadata format, the user can - simply copy and paste the metadata into a ``pyproject.toml`` file and - continue working without having to learn a new format. More likely, even, is - that tooling will eventually support this transformation with a single - command. + reproducible bug reports. By using the same format, the user can simply copy + and paste the metadata into a ``pyproject.toml`` file and continue working + without having to learn a new format. More likely, even, is that tooling will + eventually support this transformation with a single command. * Users that wish to avoid manual dependency management. For example, package managers that have commands to add/remove dependencies or dependency update automation in CI that triggers based on new versions or in response to @@ -101,50 +96,66 @@ The following are some of the use cases that this PEP wishes to support: Specification ============= -Any Python script may assign a variable named ``__pyproject__`` to a multi-line -*double-quoted* string literal (``"""``) containing a valid TOML document. The -variable MUST start at the beginning of the line and the opening of the string -MUST be on the same line as the assignment. The closing of the string MUST be -on a line by itself, and MUST NOT be indented. +This PEP defines a metadata comment block format loosely inspired [2]_ by +`reStructuredText Directives`__. -When there are multiple ``__pyproject__`` variables defined, tools MUST produce -an error. +__ https://docutils.sourceforge.io/docs/ref/rst/directives.html -The TOML document MUST NOT contain multi-line double-quoted strings, as that -would conflict with the Python string containing the document. Single-quoted -multi-line TOML strings may be used instead. +Any Python script may have top-level comment blocks that start with the line +``# /// TYPE`` where ``TYPE`` determines how to process the content, and ends +with the line ``# ///``. Every line between these two lines MUST be a comment +starting with ``#``. If there are characters after the ``#`` then the first +character MUST be a space. The embedded content is formed by taking away the +first two characters of each line if the second character is a space, otherwise +just the first character (which means the line consists of only a single +``#``). -This is the canonical regular expression that MUST be used to parse the -metadata: - -.. code:: text - - (?ms)^__pyproject__ *= *"""\\?$(.+?)^"""$ - -In circumstances where there is a discrepancy between the regular expression -and the text specification, the regular expression takes precedence. +When there are multiple comment blocks of the same ``TYPE`` defined, tools MUST +produce an error. Tools reading embedded metadata MAY respect the standard Python encoding declaration. If they choose not to do so, they MUST process the file as UTF-8. -This document MAY include the ``[project]``, ``[tool]`` and ``[build-system]`` -tables. +This is the canonical regular expression that MAY be used to parse the +metadata: -The ``[project]`` table differs in the following ways: +.. code:: text -* The ``name`` and ``version`` fields are not required and MAY be defined - dynamically by tools if the user does not define them -* These fields do not need to be listed in the ``dynamic`` array + (?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\s(?P<content>(^#(| .*)$\s)+)^# ///$ -Non-script running tools MAY choose to alter their behavior based on -configuration that is stored in their expected ``[tool]`` sub-table. +In circumstances where there is a discrepancy between the text specification +and the regular expression, the text specification takes precedence. -Build frontends SHOULD NOT use the backend defined in the ``[build-system]`` -table to build scripts with embedded metadata. This requires a new PEP because -the current methods defined in :pep:`517` act upon a directory, not a file. -We use ``SHOULD NOT`` instead of ``MUST NOT`` in order to allow tools to -experiment [2]_ with such functionality before we standardize (indeed this -would be a requirement). +Tools MUST NOT read from metadata blocks with types that have not been +standardized by this PEP or future ones. + +pyproject type +-------------- + +The first type of metadata block is named ``pyproject`` which represents +content similar to [3]_ what one would see in a ``pyproject.toml`` file. + +This document MAY include the ``[run]`` and ``[tool]`` tables. + +The :pep:`tool table <518#tool-table>` MAY be used by any tool, script runner +or otherwise, to configure behavior. + +The ``[run]`` table MAY include the following optional fields: + +* ``dependencies``: A list of strings that specifies the runtime dependencies + of the script. Each entry MUST be a valid :pep:`508` dependency. +* ``requires-python``: A string that specifies the Python version(s) with which + the script is compatible. The value of this field MUST be a valid + :pep:`version specifier <440#version-specifiers>`. + +Any future PEPs that define additional fields for the ``[run]`` table when used +in a ``pyproject.toml`` file MUST include the aforementioned fields exactly as +specified. The fields defined by this PEP are equally as applicable to +full-fledged projects as they are to single-file scripts. + +Script runners MUST error if the specified ``dependencies`` cannot be provided. +Script runners SHOULD error if no version of Python that satisfies the specified +``requires-python`` can be provided. Example ------- @@ -153,14 +164,14 @@ The following is an example of a script with an embedded ``pyproject.toml``: .. code:: python - __pyproject__ = """ - [project] - requires-python = ">=3.11" - dependencies = [ - "requests<3", - "rich", - ] - """ + # /// pyproject + # [run] + # requires-python = ">=3.11" + # dependencies = [ + # "requests<3", + # "rich", + # ] + # /// import requests from rich.pretty import pprint @@ -169,9 +180,9 @@ The following is an example of a script with an embedded ``pyproject.toml``: data = resp.json() pprint([(k, v["title"]) for k, v in data.items()][:10]) -The following is an example of a proposed syntax for single-file Rust project -that embeds their equivalent of ``pyproject.toml``, -which is called ``Cargo.toml``: +The following [4]_ is an example of a proposed syntax for single-file Rust +projects that embeds their equivalent of ``pyproject.toml``, which is called +``Cargo.toml``: .. code:: rust @@ -187,26 +198,6 @@ which is called ``Cargo.toml``: println!("Did our date match? {}", re.is_match("2014-01-01")); } -One important thing to note is that the metadata is embedded in a -`doc-comment`_ (their equivalent of docstrings). -`Other syntaxes <cargo embedded manifest_>`_ are under consideration -within the Rust project, -including using attributes which are somewhat like a -syntactically recognized equivalent of dunder variables, -with the key difference between Rust's choice and this PEP being that -any valid Rust syntax will be allowed, -requiring one of the Rust syntax parsers to work with it, like `syn`__. - -__ https://crates.io/crates/syn - -We argue that our choice, in comparison to the `doc-comment`_ approach, -is easier to read and provides easier edits for humans by virtue -of the contents starting at the beginning of lines so would precisely match -the contents of a ``pyproject.toml`` file. -It is also is easier for tools to parse and modify this continuous block -of text which was `one of the concerns <cargo embedded manifest_>`_ -raised in the Rust pre-RFC. - Reference Implementation ======================== @@ -215,34 +206,53 @@ higher. .. code:: python - import re, tomllib + import re + import tomllib - REGEX = r'(?ms)^__pyproject__ *= *"""\\?$(.+?)^"""$' + REGEX = r'(?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\s(?P<content>(^#(| .*)$\s)+)^# ///$' - def read(script: str) -> dict | None: - matches = list(re.finditer(REGEX, script)) - if len(matches) > 1: - raise ValueError('Multiple __pyproject__ definitions found') - elif len(matches) == 1: - return tomllib.loads(matches[0]) - else: - return None + def read(script: str) -> dict | None: + name = 'pyproject' + matches = list( + filter(lambda m: m.group('type') == name, re.finditer(REGEX, script)) + ) + if len(matches) > 1: + raise ValueError(f'Multiple {name} blocks found') + elif len(matches) == 1: + return tomllib.loads(matches[0]) + else: + return None Often tools will edit dependencies like package managers or dependency update automation in CI. The following is a crude example of modifying the content -using the ``tomlkit`` library. +using the ``tomlkit`` library__. + +__ https://tomlkit.readthedocs.io/en/latest/ .. code:: python - import re, tomlkit + import re - def add(script: str, dependency: str) -> str: - match = re.search(r'(?ms)^__pyproject__ *= *"""\\?$(.+?)^"""$', script) - config = tomlkit.parse(match.group(1)) - config['project']['dependencies'].append(dependency) + import tomlkit - start, end = match.span(1) - return script[:start] + tomlkit.dumps(config) + script[end:] + REGEX = r'(?m)^# /// (?P<type>[a-zA-Z0-9-]+)$\s(?P<content>(^#(| .*)$\s)+)^# ///$' + + def add(script: str, dependency: str) -> str: + match = re.search(REGEX, script) + content = ''.join( + line[2:] if line.startswith('# ') else line[1:] + for line in match.group('content').splitlines(keepends=True) + ) + + config = tomlkit.parse(content) + config['project']['dependencies'].append(dependency) + new_content = ''.join( + f'# {line}' if line.strip() else f'#{line}' + for line in tomlkit.dumps(config).splitlines(keepends=True) + ) + + start, end = match.span('content') + return script[:start] + new_content + script[end:] Note that this example used a library that preserves TOML formatting. This is not a requirement for editing by any means but rather is a "nice to have" @@ -252,26 +262,11 @@ feature. Backwards Compatibility ======================= -At the time of writing, the ``__pyproject__`` variable only appears five times -`on GitHub`__ and four of those belong to a user who appears to already be -using this PEP's exact format. +At the time of writing, the ``# /// pyproject`` block comment starter does not +appear `on GitHub`__. Therefore, there is little risk of existing scripts being +broken by this PEP. -__ https://github.com/search?q=__pyproject__&type=code - -For example, `this script`__ uses ``matplotlib`` and ``pandas`` to plot a -timeseries. It is a good example of a script that you would see in the wild: -self-contained and short. - -__ https://github.com/cjolowicz/scripts/blob/31c61e7dad8d17e0070b080abee68f4f505da211/python/plot_timeseries.py - -This user's tooling invokes scripts by creating a project at runtime using the -embedded metadata and then uses an entry point that references the main -function. - -This PEP allows this user's tooling to remove that extra step of indirection. - -This PEP's author has discovered after writing a draft that this pattern is -used in the wild by others (sent private messages). +__ https://github.com/search?q=%22%23+%2F%2F%2F+pyproject%22&type=code Security Implications @@ -284,34 +279,63 @@ installed in the user's environment. The risk here is part of the functionality of the tool being used to run the script, and as such should already be addressed by the tool itself. The only additional risk introduced by this PEP is if an untrusted script with a -embedded metadata is run, when a potentially malicious dependency might be -installed. +embedded metadata is run, when a potentially malicious dependency or transitive +dependency might be installed. This risk is addressed by the normal good practice of reviewing code -before running it. Additionally, tools may be able to provide locking -functionality when configured by their ``[tool]`` sub-table to, for example, -add the resolution result as managed metadata somewhere in the script (this -is what Go's ``gorun`` can do). +before running it. Additionally, tools may be able to provide +`locking functionality <723-tool-configuration_>`__ to ameliorate this risk. How to Teach This ================= -Since the format chosen is the same as the official metadata standard, we can -have a page that describes how to embed the metadata in scripts and to learn -about metadata itself direct users to the living document that describes -`project metadata <pyproject metadata_>`_. +To embed metadata in a script, define a comment block that starts with the +line ``# /// pyproject`` and ends with the line ``# ///``. Every line between +those two lines must be a comment and the full content is derived by removing +the first two characters. The ``pyproject`` type indicates that the content +is TOML and resembles a ``pyproject.toml`` file. -We will document that the name and version fields in the ``[project]`` table -may be elided for simplicity. Additionally, we will have guidance explaining -that single-file scripts cannot (yet) be built into a wheel via standard means. +.. code:: python -We will explain that it is up to individual tools whether or not their behavior -is altered based on the embedded metadata. For example, every script runner may -not be able to provide an environment for specific Python versions as defined -by the ``requires-python`` field. + # /// pyproject + # [run] + # dependencies = [ + # "requests<3", + # "rich", + # ] + # requires-python = ">=3.11" + # /// -Finally, we may want to list some tools that support this PEP's format. +The two allowed tables are ``[run]`` and ``[tool]``. The ``[run]`` table may +contain the following fields: + +.. list-table:: + + * - Field + - Description + - Tool behavior + + * - ``dependencies`` + - A list of strings that specifies the runtime dependencies of the script. + Each entry must be a valid :pep:`508` dependency. + - Tools will error if the specified dependencies cannot be provided. + + * - ``requires-python`` + - A string that specifies the Python version(s) + with which the script is compatible. + The value of this field must be a valid + :pep:`version specifier <440#version-specifiers>`. + - Tools might error if no version of Python that satisfies + the constraint can be executed. + +It is up to individual tools whether or not their behavior is altered based on +the embedded metadata. For example, every script runner may not be able to +provide an environment for specific Python versions as defined by the +``requires-python`` field. + +The :pep:`tool table <518#tool-table>` may be used by any tool, script runner +or otherwise, to configure behavior. Recommendations @@ -321,28 +345,6 @@ Tools that support managing different versions of Python should attempt to use the highest available version of Python that is compatible with the script's ``requires-python`` metadata, if defined. -For projects that have large multi-line external metadata to embed like a -README file, it is recommended that they become directories with a -``pyproject.toml`` file. While this is technically allowed, it is strongly -discouraged to have large chunks of multi-line metadata and is indicative -of the fact that a script has graduated to a more traditional layout. - -If the content is small, for example in the case of internal packages, it is -recommended that multi-line *single-quoted* TOML strings (``'''``) be used. -For example: - -.. code:: python - - __pyproject__ = """ - [project] - readme.content-type = "text/markdown" - readme.text = ''' - # Some Project - Please refer to our corporate docs - for more information. - ''' - """ - Tooling buy-in ============== @@ -366,35 +368,6 @@ have committed to implementing support should it be accepted: Rejected Ideas ============== -Why not limit to specific metadata fields? ------------------------------------------- - -By limiting the metadata to a specific set of fields, for example just -``dependencies``, we would prevent legitimate use cases both known and unknown. -The following are examples of known use cases: - -* ``requires-python``: For tools that support managing Python installations, - this allows users to target specific versions of Python for new syntax - or standard library functionality. -* ``version``: It is quite common to version scripts for persistence even when - using a VCS like Git. When not using a VCS it is even more common to version, - for example the author has been in multiple time sensitive debugging sessions - with customers where due to the airgapped nature of the environment, the only - way to transfer the script was via email or copying and pasting it into a - chat window. In these cases, versioning is invaluable to ensure that the - customer is using the latest (or a specific) version of the script. -* ``description``: For scripts that don't need an argument parser, or if the - author has never used one, tools can treat this as help text which can be - shown to the user. - -By not allowing the ``[tool]`` section, we would prevent especially script -runners from allowing users to configure behavior. For example, a script runner -may support configuration instructing to run scripts in containers for -situations in which there is no cross-platform support for a dependency or if -the setup is too complex for the average user like when requiring Nvidia -drivers. Situations like this would allow users to proceed with what they want -to do whereas otherwise they may stop at that point altogether. - .. _723-comment-block: Why not use a comment block resembling requirements.txt? @@ -436,9 +409,9 @@ would live as single-file scripts: executable or script runner. This PEP argues that reusing our TOML-based metadata format is the best for -each category of user and that the block comment is only approachable for -those who have familiarity with ``requirements.txt``, which represents a -small subset of users. +each category of user and that the requirements-like block comment is only +approachable for those who have familiarity with ``requirements.txt``, which +represents a small subset of users. * For the average person automating a task or the data scientist, they are already starting with zero context and are unlikely to be familiar with @@ -447,9 +420,9 @@ small subset of users. of a chat bot or direct code completion software. Searching for Python metadata formatting will lead them to the TOML-based format that already exists which they can reuse. The author tested GitHub Copilot with this - PEP and it already supports auto-completion of fields and dependencies. - In contrast, a new format may take years of being trained on the Internet - for models to learn. + PEP and it already supports auto-completion of ``dependencies``. In contrast, + a new format may take years of being trained on the Internet for models to + learn. Additionally, these users are most susceptible to formatting quirks and syntax errors. TOML is a well-defined format with existing online @@ -464,7 +437,7 @@ small subset of users. with TOML since they are used to structured data formats and there would be less perceived magic in their systems. - Additionally, for maintenance of their systems ``__pyproject__`` would be + Additionally, for maintenance of their systems ``/// pyproject`` would be much easier to search for from a shell than a block comment with potentially numerous extensions over time. * For the SRE types, they are likely to be familiar with TOML already from @@ -490,11 +463,12 @@ small subset of users. Studio Code would be able to provide TOML syntax highlighting much more easily than each writing custom logic for this feature. -Additionally, since the original block comment alternative format went against -the recommendation of :pep:`8` and as a result linters and IDE auto-formatters -that respected the recommendation would +Additionally, since the original block comment alternative format (double +``#``) went against the recommendation of :pep:`8` and as a result linters +and IDE auto-formatters that respected the recommendation would `fail by default <https://discuss.python.org/t/29905/247>`__, the final -proposal uses standard comments starting with a single ``#`` character. +proposal uses standard comments starting with a single ``#`` character without +any obvious start nor end sequence. The concept of regular comments that do not appear to be intended for machines (i.e. `encoding declarations`__) affecting behavior would not be customary to @@ -517,47 +491,80 @@ would be multiple ways to achieve the same thing which goes against our foundational principle of "there should be one - and preferably only one - obvious way to do it". -Why not consider scripts as projects without wheels? ----------------------------------------------------- +Why not use a multi-line string? +-------------------------------- -There is `an ongoing discussion <pyproject without wheels_>`_ about how to -use ``pyproject.toml`` for projects that are not intended to be built as -wheels. This PEP considers the discussion only tangentially related. +A previous version of this PEP proposed that the metadata be stored as follows: -The use case described in that thread is primarily talking about projects that -represent applications like a Django app or a Flask app. These projects are -often installed on each server in a virtual environment with strict dependency -pinning e.g. a lock file with some sort of hash checking. Such projects would -never be distributed as a wheel (except for maybe a transient editable one -that is created when doing ``pip install -e .``). +.. code:: python -In contrast, scripts are managed loosely by their runners and would almost -always have relaxed dependency constraints. Additionally, there may be a future -in which there is `a standard way <723-limit-build-backend_>`_ to ship projects -that are in the form of a single file. + __pyproject__ = """ + ... + """ -.. _723-limit-build-backend: +The most significant problem with this proposal is that the embedded TOML would +be limited in the following ways: -Why not limit build backend behavior? -------------------------------------- +* It would not be possible to use multi-line double-quoted strings in the TOML + as that would conflict with the Python string containing the document. Many + TOML writers do not preserve style and may potentially produce output that + would be malformed. +* The way in which character escaping works in Python strings is not quite the + way it works in TOML strings. It would be possible to preserve a one-to-one + character mapping by enforcing raw strings, but this ``r`` prefix requirement + may be potentially confusing to users. -A previous version of this PEP proposed that the ``[build-system]`` table -mustn't be defined. The rationale was that builds would never occur so it -did not make sense to allow this section. +Why not reuse core metadata fields? +----------------------------------- -We removed that limitation based on -`feedback <https://discuss.python.org/t/31151/9>`__ stating that there -are already tools that exist in the wild that build wheels and source -distributions from single files. +A previous version of this PEP proposed to reuse the existing +`metadata standard <pyproject metadata_>`_ that is used to describe projects. -The author of the Rust RFC for embedding metadata -`mentioned to us <https://discuss.python.org/t/29905/179>`__ that they are -actively looking into that as well based on user feedback saying that there -is unnecessary friction with managing small projects, which we have also -heard in the Python community. +There are two significant problems with this proposal: -There has been `a commitment <https://discuss.python.org/t/31151/15>`__ to -support this by at least one major build system. +* The ``name`` and ``version`` fields are required and changing that would + require its own PEP +* Reusing the data is `fundamentally a misuse of it`__ + + __ https://snarky.ca/differentiating-between-writing-down-dependencies-to-use-packages-and-for-packages-themselves/ + +Why not limit to specific metadata fields? +------------------------------------------ + +By limiting the metadata to just ``dependencies``, we would prevent the known +use case of tools that support managing Python installations, which would +allows users to target specific versions of Python for new syntax or standard +library functionality. + +.. _723-tool-configuration: + +Why not limit tool configuration? +--------------------------------- + +By not allowing the ``[tool]`` table, we would prevent known functionality +that would benefit users. For example: + +* A script runner may support injecting of dependency resolution data for an + embedded lock file (this is what Go's ``gorun`` can do). +* A script runner may support configuration instructing to run scripts in + containers for situations in which there is no cross-platform support for a + dependency or if the setup is too complex for the average user like when + requiring Nvidia drivers. Situations like this would allow users to proceed + with what they want to do whereas otherwise they may stop at that point + altogether. +* Tools may wish to experiment with features to ease development burden for + users such as the building of single-file scripts into packages. We received + `feedback <https://discuss.python.org/t/31151/9>`__ stating that there are + already tools that exist in the wild that build wheels and source + distributions from single files. + + The author of the Rust RFC for embedding metadata + `mentioned to us <https://discuss.python.org/t/29905/179>`__ that they are + actively looking into that as well based on user feedback saying that there + is unnecessary friction with managing small projects. + + There has been `a commitment <https://discuss.python.org/t/31151/15>`__ to + support this by at least one major build system. Why not limit tool behavior? ---------------------------- @@ -574,25 +581,6 @@ from maintainers of tools that this would be undesirable and potentially confusing to users. Additionally, this may allow for a universally easier way to configure tools in certain circumstances and solve existing issues. -Why not accept all valid Python expression syntax? --------------------------------------------------- - -There has been a suggestion that we should not restrict how the -``__pyproject__`` variable is defined and we should parse the abstract syntax -tree. For example: - -.. code:: python - - __pyproject__ = ( - """ - [project] - dependencies = [] - """ - ) - -We will not be doing this so that every language has the possibility to read -the metadata without dependence on knowledge of every version of Python. - Why not just set up a Python project with a ``pyproject.toml``? --------------------------------------------------------------- @@ -682,7 +670,7 @@ detail. So in order to make a standard, two things would be required: 1. A standardised replacement for the requirements file format. -2. A standard for how to locate the requiements file for a given script. +2. A standard for how to locate the requirements file for a given script. The first item is a significant undertaking. It has been discussed on a number of occasions, but so far no-one has attempted to actually do it. The most @@ -705,7 +693,7 @@ script (for example, publishing it on a text file sharing service like Github's gist, or a corporate intranet) may not allow for deriving the location of an associated requirements file from the script's location (tools like ``pipx`` support running a script directly from a URL, so "download and unpack a zip of -the script and itsdependencies" may not be an appropriate requirement). +the script and its dependencies" may not be an appropriate requirement). Essentially, though, the issue here is that there is an explicitly stated requirement that the format supports storing dependency data *in the script @@ -765,8 +753,6 @@ References ========== .. _pyproject metadata: https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ -.. _doc-comment: https://doc.rust-lang.org/stable/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments -.. _cargo embedded manifest: https://github.com/epage/cargo-script-mvs/blob/main/0000-cargo-script.md#embedded-manifest-format .. _pip-run issue: https://github.com/jaraco/pip-run/issues/44 .. _pyproject without wheels: https://discuss.python.org/t/projects-that-arent-meant-to-generate-a-wheel-and-pyproject-toml/29684 @@ -779,8 +765,20 @@ Footnotes projects that require special maintenance like the `AWS CLI <https://github.com/aws/aws-cli/tree/4393dcdf044a5275000c9c193d1933c07a08fdf1/scripts>`__ or `Calibre <https://github.com/kovidgoyal/calibre/tree/master/setup>`__. -.. [2] For example, projects like Hatch and Poetry have their own backends - and may wish to support this use case only when their backend is used. +.. [2] The syntax is taken directly from the final resolution of the + `Blocks extension`__ to `Python Markdown`__. + + __ https://github.com/facelessuser/pymdown-extensions/discussions/1973 + __ https://github.com/Python-Markdown/markdown +.. [3] A future PEP that officially introduces the ``[run]`` table to + ``pyproject.toml`` files will make this PEP not just similar but a strict + subset. +.. [4] One important thing to note is that the metadata is embedded in a + `doc-comment`__ (their equivalent of docstrings). `Other syntaxes`__ are + under consideration within the Rust project. + + __ https://doc.rust-lang.org/stable/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments + __ https://github.com/epage/cargo-script-mvs/blob/main/0000-cargo-script.md#embedded-manifest-format Copyright diff --git a/peps/pep-0724.rst b/peps/pep-0724.rst new file mode 100644 index 000000000..a6ae5a1d2 --- /dev/null +++ b/peps/pep-0724.rst @@ -0,0 +1,331 @@ +PEP: 724 +Title: Stricter Type Guards +Author: Rich Chiodo <rchiodo at microsoft.com>, + Eric Traut <erictr at microsoft.com>, + Erik De Bonte <erikd at microsoft.com>, +Sponsor: Jelle Zijlstra <jelle.zijlstra@gmail.com> +Discussions-To: https://discuss.python.org/t/pep-724-stricter-type-guards/34124 +Status: Draft +Type: Standards Track +Topic: Typing +Content-Type: text/x-rst +Created: 28-Jul-2023 +Python-Version: 3.13 +Post-History: `30-Dec-2021 <https://mail.python.org/archives/list/typing-sig@python.org/thread/EMUD2D424OI53DCWQ4H5L6SJD2IXBHUL/>`__, + `19-Sep-2023 <https://discuss.python.org/t/pep-724-stricter-type-guards/34124>`__, + +Abstract +======== + +:pep:`647` introduced the concept of a user-defined type guard function which +returns ``True`` if the type of the expression passed to its first parameter +matches its return ``TypeGuard`` type. For example, a function that has a +return type of ``TypeGuard[str]`` is assumed to return ``True`` if and only if +the type of the expression passed to its first input parameter is a ``str``. +This allows type checkers to narrow types when a user-defined type guard +function returns ``True``. + +This PEP refines the ``TypeGuard`` mechanism introduced in :pep:`647`. It +allows type checkers to narrow types when a user-defined type guard function +returns ``False``. It also allows type checkers to apply additional (more +precise) type narrowing under certain circumstances when the type guard +function returns ``True``. + + +Motivation +========== + +User-defined type guard functions enable a type checker to narrow the type of +an expression when it is passed as an argument to the type guard function. The +``TypeGuard`` mechanism introduced in :pep:`647` is flexible, but this +flexibility imposes some limitations that developers have found inconvenient +for some uses. + +Limitation 1: Type checkers are not allowed to narrow a type in the case where +the type guard function returns ``False``. This means the type is not narrowed +in the negative ("else") clause. + +Limitation 2: Type checkers must use the ``TypeGuard`` return type if the type +guard function returns ``True`` regardless of whether additional narrowing can +be applied based on knowledge of the pre-narrowed type. + +The following code sample demonstrates both of these limitations. + +.. code-block:: python + + def is_iterable(val: object) -> TypeGuard[Iterable[Any]]: + return isinstance(val, Iterable) + + def func(val: int | list[int]): + if is_iterable(val): + # The type is narrowed to 'Iterable[Any]' as dictated by + # the TypeGuard return type + reveal_type(val) # Iterable[Any] + else: + # The type is not narrowed in the "False" case + reveal_type(val) # int | list[int] + + # If "isinstance" is used in place of the user-defined type guard + # function, the results differ because type checkers apply additional + # logic for "isinstance" + + if isinstance(val, Iterable): + # Type is narrowed to "list[int]" because this is + # a narrower (more precise) type than "Iterable[Any]" + reveal_type(val) # list[int] + else: + # Type is narrowed to "int" because the logic eliminates + # "list[int]" from the original union + reveal_type(val) # int + + +:pep:`647` imposed these limitations so it could support use cases where the +return ``TypeGuard`` type was not a subtype of the input type. Refer to +:pep:`647` for examples. + + +Specification +============= + +The use of a user-defined type guard function involves five types: + +* I = ``TypeGuard`` input type +* R = ``TypeGuard`` return type +* A = Type of argument passed to type guard function (pre-narrowed) +* NP = Narrowed type (positive) +* NN = Narrowed type (negative) + +.. code-block:: python + + def guard(x: I) -> TypeGuard[R]: ... + + def func1(val: A): + if guard(val): + reveal_type(val) # NP + else: + reveal_type(val) # NN + + +This PEP proposes some modifications to :pep:`647` to address the limitations +discussed above. These limitations are safe to eliminate only when a specific +condition is met. In particular, when the output type ``R`` of a user-defined +type guard function is consistent [#isconsistent]_ with the type of its first +input parameter (``I``), type checkers should apply stricter type guard +semantics. + + .. code-block:: python + + # Stricter type guard semantics are used in this case because + # "Kangaroo | Koala" is consistent with "Animal" + def is_marsupial(val: Animal) -> TypeGuard[Kangaroo | Koala]: + return isinstance(val, Kangaroo | Koala) + + # Stricter type guard semantics are not used in this case because + # "list[T]"" is not consistent with "list[T | None]" + def has_no_nones(val: list[T | None]) -> TypeGuard[list[T]]: + return None not in val + +When stricter type guard semantics are applied, the application of a +user-defined type guard function changes in two ways. + +* Type narrowing is applied in the negative ("else") case. + +.. code-block:: python + + def is_str(val: str | int) -> TypeGuard[str]: + return isinstance(val, str) + + def func(val: str | int): + if not is_str(val): + reveal_type(val) # int + +* Additional type narrowing is applied in the positive "if" case if applicable. + +.. code-block:: python + + def is_cardinal_direction(val: str) -> TypeGuard[Literal["N", "S", "E", "W"]]: + return val in ("N", "S", "E", "W") + + def func(direction: Literal["NW", "E"]): + if is_cardinal_direction(direction): + reveal_type(direction) # "Literal[E]" + else: + reveal_type(direction) # "Literal[NW]" + + +The type-theoretic rules for type narrowing are specificed in the following +table. + +============ ======================= =================== +\ Non-strict type guard Strict type guard +============ ======================= =================== +Applies when R not consistent with I R consistent with I +NP is .. :math:`R` :math:`A \land R` +NN is .. :math:`A` :math:`A \land \neg{R}` +============ ======================= =================== + +In practice, the theoretic types for strict type guards cannot be expressed +precisely in the Python type system. Type checkers should fall back on +practical approximations of these types. As a rule of thumb, a type checker +should use the same type narrowing logic -- and get results that are consistent +with -- its handling of "isinstance". This guidance allows for changes and +improvements if the type system is extended in the future. + + +Additional Examples +=================== + +``Any`` is consistent [#isconsistent]_ with any other type, which means +stricter semantics can be applied. + +.. code-block:: python + + # Stricter type guard semantics are used in this case because + # "str" is consistent with "Any" + def is_str(x: Any) -> TypeGuard[str]: + return isinstance(x, str) + + def test(x: float | str): + if is_str(x): + reveal_type(x) # str + else: + reveal_type(x) # float + + +Backwards Compatibility +======================= + +This PEP proposes to change the existing behavior of ``TypeGuard``. This has no +effect at runtime, but it does change the types evaluated by a type checker. + +.. code-block:: python + + def is_int(val: int | str) -> TypeGuard[int]: + return isinstance(val, int) + + def func(val: int | str): + if is_int(val): + reveal_type(val) # "int" + else: + reveal_type(val) # Previously "int | str", now "str" + + +This behavioral change results in different types evaluated by a type checker. +It could therefore produce new (or mask existing) type errors. + +Type checkers often improve narrowing logic or fix existing bugs in such logic, +so users of static typing will be used to this type of behavioral change. + +We also hypothesize that it is unlikely that existing typed Python code relies +on the current behavior of ``TypeGuard``. To validate our hypothesis, we +implemented the proposed change in pyright and ran this modified version on +roughly 25 typed code bases using `mypy primer`__ to see if there were any +differences in the output. As predicted, the behavioral change had minimal +impact. The only noteworthy change was that some ``# type: ignore`` comments +were no longer necessary, indicating that these code bases were already working +around the existing limitations of ``TypeGuard``. + +__ https://github.com/hauntsaninja/mypy_primer + +Breaking change +--------------- + +It is possible for a user-defined type guard function to rely on the old +behavior. Such type guard functions could break with the new behavior. + +.. code-block:: python + + def is_positive_int(val: int | str) -> TypeGuard[int]: + return isinstance(val, int) and val > 0 + + def func(val: int | str): + if is_positive_int(val): + reveal_type(val) # "int" + else: + # With the older behavior, the type of "val" is evaluated as + # "int | str"; with the new behavior, the type is narrowed to + # "str", which is perhaps not what was intended. + reveal_type(val) + +We think it is unlikley that such user-defined type guards exist in real-world +code. The mypy primer results didn't uncover any such cases. + + +How to Teach This +================= + +Users unfamiliar with ``TypeGuard`` are likely to expect the behavior outlined +in this PEP, therefore making ``TypeGuard`` easier to teach and explain. + + +Reference Implementation +======================== + +A reference `implementation`__ of this idea exists in pyright. + +__ https://github.com/microsoft/pyright/commit/9a5af798d726bd0612cebee7223676c39cf0b9b0 + +To enable the modified behavior, the configuration flag +``enableExperimentalFeatures`` must be set to true. This can be done on a +per-file basis by adding a comment: + +.. code-block:: python + + # pyright: enableExperimentalFeatures=true + + +Rejected Ideas +============== + +StrictTypeGuard +--------------- + +A new ``StrictTypeGuard`` construct was proposed. This alternative form would +be similar to a ``TypeGuard`` except it would apply stricter type guard +semantics. It would also enforce that the return type was consistent +[#isconsistent]_ with the input type. See this thread for details: +`StrictTypeGuard proposal`__ + +__ https://github.com/python/typing/discussions/1013#discussioncomment-1966238 + +This idea was rejected because it is unnecessary in most cases and added +unnecessary complexity. It would require the introduction of a new special +form, and developers would need to be educated about the subtle difference +between the two forms. + +TypeGuard with a second output type +----------------------------------- + +Another idea was proposed where ``TypeGuard`` could support a second optional +type argument that indicates the type that should be used for narrowing in the +negative ("else") case. + +.. code-block:: python + + def is_int(val: int | str) -> TypeGuard[int, str]: + return isinstance(val, int) + + +This idea was proposed `here`__. + +__ https://github.com/python/typing/issues/996 + +It was rejected because it was considered too complicated and addressed only +one of the two main limitations of ``TypeGuard``. Refer to this `thread`__ for +the full discussion. + +__ https://mail.python.org/archives/list/typing-sig@python.org/thread/EMUD2D424OI53DCWQ4H5L6SJD2IXBHUL + + +Footnotes +========= + +.. [#isconsistent] :pep:`PEP 483's discussion of is-consistent <483#summary-of-gradual-typing>` + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. + diff --git a/peps/pep-0725.rst b/peps/pep-0725.rst new file mode 100644 index 000000000..172afbd86 --- /dev/null +++ b/peps/pep-0725.rst @@ -0,0 +1,587 @@ +PEP: 725 +Title: Specifying external dependencies in pyproject.toml +Author: Pradyun Gedam <pradyunsg@gmail.com>, + Ralf Gommers <ralf.gommers@gmail.com> +Discussions-To: https://discuss.python.org/t/31888 +Status: Draft +Type: Standards Track +Topic: Packaging +Content-Type: text/x-rst +Created: 17-Aug-2023 +Post-History: `18-Aug-2023 <https://discuss.python.org/t/31888>`__ + + +Abstract +======== + +This PEP specifies how to write a project's external, or non-PyPI, build and +runtime dependencies in a ``pyproject.toml`` file for packaging-related tools +to consume. + + +Motivation +========== + +Python packages may have dependencies on build tools, libraries, command-line +tools, or other software that is not present on PyPI. Currently there is no way +to express those dependencies in standardized metadata +[#singular-vision-native-deps]_, [#pypacking-native-deps]_. Key motivators for +this PEP are to: + +- Enable tools to automatically map external dependencies to packages in other + packaging repositories, +- Make it possible to include needed dependencies in error messages emitting by + Python package installers and build frontends, +- Provide a canonical place for package authors to record this dependency + information. + +Packaging ecosystems like Linux distros, Conda, Homebrew, Spack, and Nix need +full sets of dependencies for Python packages, and have tools like pyp2rpm_ +(Fedora), Grayskull_ (Conda), and dh_python_ (Debian) which attempt to +automatically generate dependency metadata from the metadata in +upstream Python packages. External dependencies are currently handled manually, +because there is no metadata for this in ``pyproject.toml`` or any other +standard location. Enabling automating this conversion is a key benefit of +this PEP, making packaging Python easier and more reliable. In addition, the +authors envision other types of tools making use of this information, e.g., +dependency analysis tools like Repology_, Dependabot_ and libraries.io_. +Software bill of materials (SBOM) generation tools may also be able to use this +information, e.g. for flagging that external dependencies listed in +``pyproject.toml`` but not contained in wheel metadata are likely vendored +within the wheel. + +Packages with external dependencies are typically hard to build from source, +and error messages from build failures tend to be hard to decipher for end +users. Missing external dependencies on the end user's system are the most +likely cause of build failures. If installers can show the required external +dependencies as part of their error message, this may save users a lot of time. + +At the moment, information on external dependencies is only captured in +installation documentation of individual packages. It is hard to maintain for +package authors and tends to go out of date. It's also hard for users and +distro packagers to find it. Having a canonical place to record this dependency +information will improve this situation. + +This PEP is not trying to specify how the external dependencies should be used, +nor a mechanism to implement a name mapping from names of individual packages +that are canonical for Python projects published on PyPI to those of other +packaging ecosystems. Those topics should be addressed in separate PEPs. + + +Rationale +========= + +Types of external dependencies +------------------------------ + +Multiple types of external dependencies can be distinguished: + +- Concrete packages that can be identified by name and have a canonical + location in another language-specific package repository. E.g., Rust + packages on `crates.io <https://crates.io/>`__, R packages on + `CRAN <https://cran.r-project.org/>`__, JavaScript packages on the + `npm registry <https://www.npmjs.com/>`__. +- Concrete packages that can be identified by name but do not have a clear + canonical location. This is typically the case for libraries and tools + written in C, C++, Fortran, CUDA and other low-level languages. E.g., + Boost, OpenSSL, Protobuf, Intel MKL, GCC. +- "Virtual" packages, which are names for concepts, types of tools or + interfaces. These typically have multiple implementations, which *are* + concrete packages. E.g., a C++ compiler, BLAS, LAPACK, OpenMP, MPI. + +Concrete packages are straightforward to understand, and are a concept present +in virtually every package management system. Virtual packages are a concept +also present in a number of packaging systems -- but not always, and the +details of their implementation varies. + +Cross compilation +----------------- + +Cross compilation is not yet (as of August 2023) well-supported by stdlib +modules and ``pyproject.toml`` metadata. It is however important when +translating external dependencies to those of other packaging systems (with +tools like ``pyp2rpm``). Introducing support for cross compilation immediately +in this PEP is much easier than extending ``[external]`` in the future, hence +the authors choose to include this now. + +Terminology +''''''''''' + +This PEP uses the following terminology: + +- *build machine*: the machine on which the package build process is being + executed +- *host machine*: the machine on which the produced artifact will be installed + and run +- *build dependency*: dependency for building the package that needs to be + present at build time and itself was built for the build machine's OS and + architecture +- *host dependency*: dependency for building the package that needs to be + present at build time and itself was built for the host machine's OS and + architecture + +Note that this terminology is not consistent across build and packaging tools, +so care must be taken when comparing build/host dependencies in +``pyproject.toml`` to dependencies from other package managers. + +Note that "target machine" or "target dependency" is not used in this PEP. That +is typically only relevant for cross-compiling compilers or other such advanced +scenarios [#gcc-cross-terminology]_, [#meson-cross]_ - this is out of scope for +this PEP. + +Finally, note that while "dependency" is the term most widely used for packages +needed at build time, the existing key in ``pyproject.toml`` for PyPI +build-time dependencies is ``build-requires``. Hence this PEP uses the keys +``build-requires`` and ``host-requires`` under ``[external]`` for consistency. + +Build and host dependencies +''''''''''''''''''''''''''' + +Clear separation of metadata associated with the definition of build and target +platforms, rather than assuming that build and target platform will always be +the same, is important [#pypackaging-native-cross]_. + +Build dependencies are typically run during the build process - they may be +compilers, code generators, or other such tools. In case the use of a build +dependency implies a runtime dependency, that runtime dependency does not have +to be declared explicitly. For example, when compiling Fortran code with +``gfortran`` into a Python extension module, the package likely incurs a +dependency on the ``libgfortran`` runtime library. The rationale for not +explicitly listing such runtime dependencies is two-fold: (1) it may depend on +compiler/linker flags or details of the build environment whether the +dependency is present, and (2) these runtime dependencies can be detected and +handled automatically by tools like ``auditwheel``. + +Host dependencies are typically not run during the build process, but only used +for linking against. This is not a rule though -- it may be possible or +necessary to run a host dependency under an emulator, or through a custom tool +like crossenv_. When host dependencies imply a runtime dependency, that runtime +dependency also does not have to be declared, just like for build dependencies. + +When host dependencies are declared and a tool is not cross-compilation aware +and has to do something with external dependencies, the tool MAY merge the +``host-requires`` list into ``build-requires``. This may for example happen if +an installer like ``pip`` starts reporting external dependencies as a likely +cause of a build failure when a package fails to build from an sdist. + +Specifying external dependencies +-------------------------------- + +Concrete package specification through PURL +''''''''''''''''''''''''''''''''''''''''''' + +The two types of concrete packages are supported by PURL_ (Package URL), which +implements a scheme for identifying packages that is meant to be portable +across packaging ecosystems. Its design is:: + + scheme:type/namespace/name@version?qualifiers#subpath + +The ``scheme`` component is a fixed string, ``pkg``, and of the other +components only ``type`` and ``name`` are required. As an example, a package +URL for the ``requests`` package on PyPI would be:: + + pkg:pypi/requests + +Adopting PURL to specify external dependencies in ``pyproject.toml`` solves a +number of problems at once - and there are already implementations of the +specification in Python and multiple languages. PURL is also already supported +by dependency-related tooling like SPDX (see +`External Repository Identifiers in the SPDX 2.3 spec <https://spdx.github.io/spdx-spec/v2.3/external-repository-identifiers/#f35-purl>`__), +the `Open Source Vulnerability format <https://ossf.github.io/osv-schema/#affectedpackage-field>`__, +and the `Sonatype OSS Index <https://ossindex.sonatype.org/doc/coordinates>`__; +not having to wait years before support in such tooling arrives is valuable. + +For concrete packages without a canonical package manager to refer to, either +``pkg:generic/pkg-name`` can be used, or a direct reference to the VCS system +that the package is maintained in (e.g., +``pkg:github/user-or-org-name/pkg-name``). Which of these is more appropriate +is situation-dependent. This PEP recommends using ``pkg:generic`` when the +package name is unambiguous and well-known (e.g., ``pkg:generic/git`` or +``pkg:generic/openblas``), and using the VCS as the PURL type otherwise. + +Virtual package specification +''''''''''''''''''''''''''''' + +There is no ready-made support for virtual packages in PURL or another +standard. There are a relatively limited number of such dependencies though, +and adoption a scheme similar to PURL but with the ``virtual:`` rather than +``pkg:`` scheme seems like it will be understandable and map well to Linux +distros with virtual packages and the likes of Conda and Spack. + +The two known virtual package types are ``compiler`` and ``interface``. + +Versioning +'''''''''' + +Support in PURL for version expressions and ranges beyond a fixed version is +still pending, see the Open Issues section. + +Dependency specifiers +''''''''''''''''''''' + +Regular Python dependency specifiers (as originally defined in :pep:`508`) may +be used behind PURLs. PURL qualifiers, which use ``?`` followed by a package +type-specific dependency specifier component, must not be used. The reason for +this is pragmatic: dependency specifiers are already used for other metadata in +``pyproject.toml``, any tooling that is used with ``pyproject.toml`` is likely +to already have a robust implementation to parse it. And we do not expect to +need the extra possibilities that PURL qualifiers provide (e.g. to specify a +Conan or Conda channel, or a RubyGems platform). + +Usage of core metadata fields +----------------------------- + +The `core metadata`_ specification contains one relevant field, namely +``Requires-External``. This has no well-defined semantics in core metadata 2.1; +this PEP chooses to reuse the field for external runtime dependencies. The core +metadata specification does not contain fields for any metadata in +``pyproject.toml``'s ``[build-system]`` table. Therefore the ``build-requires`` +and ``host-requires`` content also does not need to be reflected in core +metadata fields. The ``optional-dependencies`` content from ``[external]`` +would need to either reuse ``Provides-Extra`` or require a new +``Provides-External-Extra`` field. Neither seems desirable. + +Differences between sdist and wheel metadata +'''''''''''''''''''''''''''''''''''''''''''' + +A wheel may vendor its external dependencies. This happens in particular when +distributing wheels on PyPI or other Python package indexes - and tools like +auditwheel_, delvewheel_ and delocate_ automate this process. As a result, a +``Requires-External`` entry in an sdist may disappear from a wheel built from +that sdist. It is also possible that a ``Requires-External`` entry remains in a +wheel, either unchanged or with narrower constraints. ``auditwheel`` does not +vendor certain allow-listed dependencies, such as OpenGL, by default. In +addition, ``auditwheel`` and ``delvewheel`` allow a user to manually exclude +dependencies via a ``--exclude`` or ``--no-dll`` command-line flag. This is +used to avoid vendoring large shared libraries, for example those from CUDA. + +``Requires-External`` entries generated from external dependencies in +``pyproject.toml`` in a wheel are therefore allowed to be narrower than those +for the corresponding sdist. They must not be wider, i.e. constraints must not +allow a version of a dependency for a wheel that isn't allowed for an sdist, +nor contain new dependencies that are not listed in the sdist's metadata at +all. + + +Specification +============= + +If metadata is improperly specified then tools MUST raise an error to notify +the user about their mistake. + +Details +------- + +Note that ``pyproject.toml`` content is in the same format as in :pep:`621`. + +Table name +'''''''''' + +Tools MUST specify fields defined by this PEP in a table named ``[external]``. +No tools may add fields to this table which are not defined by this PEP or +subsequent PEPs. The lack of an ``[external]`` table means the package either +does not have any external dependencies, or the ones it does have are assumed +to be present on the system already. + +``build-requires``/``optional-build-requires`` +'''''''''''''''''''''''''''''''''''''''''''''' + +- Format: Array of PURL_ strings (``build-requires``) and a table + with values of arrays of PURL_ strings (``optional-build-requires``) +- `Core metadata`_: N/A + +The (optional) external build requirements needed to build the project. + +For ``build-requires``, it is a key whose value is an array of strings. Each +string represents a build requirement of the project and MUST be formatted as +either a valid PURL_ string or a ``virtual:`` string. + +For ``optional-build-requires``, it is a table where each key specifies an +extra set of build requirements and whose value is an array of strings. The +strings of the arrays MUST be valid PURL_ strings. + +``host-requires``/``optional-host-requires`` +'''''''''''''''''''''''''''''''''''''''''''' + +- Format: Array of PURL_ strings (``host-requires``) and a table + with values of arrays of PURL_ strings (``optional-host-requires``) +- `Core metadata`_: N/A + +The (optional) external host requirements needed to build the project. + +For ``host-requires``, it is a key whose value is an array of strings. Each +string represents a host requirement of the project and MUST be formatted as +either a valid PURL_ string or a ``virtual:`` string. + +For ``optional-host-requires``, it is a table where each key specifies an +extra set of host requirements and whose value is an array of strings. The +strings of the arrays MUST be valid PURL_ strings. + +``dependencies``/``optional-dependencies`` +'''''''''''''''''''''''''''''''''''''''''' + +- Format: Array of PURL_ strings (``dependencies``) and a table + with values of arrays of PURL_ strings (``optional-dependencies``) +- `Core metadata`_: ``Requires-External``, N/A + +The (optional) dependencies of the project. + +For ``dependencies``, it is a key whose value is an array of strings. Each +string represents a dependency of the project and MUST be formatted as either a +valid PURL_ string or a ``virtual:`` string. Each string maps directly to a +``Requires-External`` entry in the `core metadata`_. + +For ``optional-dependencies``, it is a table where each key specifies an extra +and whose value is an array of strings. The strings of the arrays MUST be valid +PURL_ strings. Optional dependencies do not map to a core metadata field. + +Examples +-------- + +These examples show what the ``[external]`` content for a number of packages is +expected to be. + +cryptography 39.0: + +.. code:: toml + + [external] + build-requires = [ + "virtual:compiler/rust", + ] + host-requires = [ + "pkg:generic/openssl", + ] + +SciPy 1.10: + +.. code:: toml + + [external] + build-requires = [ + "virtual:compiler/c", + "virtual:compiler/cpp", + "virtual:compiler/fortran", + "pkg:generic/ninja", + ] + host-requires = [ + "virtual:interface/blas", + "virtual:interface/lapack", # >=3.7.1 (can't express version ranges with PURL yet) + ] + + [external.optional-host-requires] + dependency_detection = [ + "pkg:generic/pkg-config", + "pkg:generic/cmake", + ] + +pygraphviz 1.10: + +.. code:: toml + + [external] + build-requires = [ + "virtual:compiler/c", + ] + host-requires = [ + "pkg:generic/graphviz", + ] + +NAVis 1.4.0: + +.. code:: toml + + [project.optional-dependencies] + r = ["rpy2"] + + [external] + build-requires = [ + "pkg:generic/XCB; platform_system=='Linux'", + ] + + [external.optional-dependencies] + nat = [ + "pkg:cran/nat", + "pkg:cran/nat.nblast", + ] + +Spyder 6.0: + +.. code:: toml + + [external] + dependencies = [ + "pkg:cargo/ripgrep", + "pkg:cargo/tree-sitter-cli", + "pkg:golang/github.com/junegunn/fzf", + ] + +jupyterlab-git 0.41.0: + +.. code:: toml + + [external] + dependencies = [ + "pkg:generic/git", + ] + + [external.optional-build-requires] + dev = [ + "pkg:generic/nodejs", + ] + +PyEnchant 3.2.2: + +.. code:: toml + + [external] + dependencies = [ + # libenchant is needed on all platforms but only vendored into wheels on + # Windows, so on Windows the build backend should remove this external + # dependency from wheel metadata. + "pkg:github/AbiWord/enchant", + ] + + +Backwards Compatibility +======================= + +There is no impact on backwards compatibility, as this PEP only adds new, +optional metadata. In the absence of such metadata, nothing changes for package +authors or packaging tooling. + + +Security Implications +===================== + +There are no direct security concerns as this PEP covers how to statically +define metadata for external dependencies. Any security issues would stem from +how tools consume the metadata and choose to act upon it. + + +How to Teach This +================= + +External dependencies and if and how those external dependencies are vendored +are topics that are typically not understood in detail by Python package +authors. We intend to start from how an external dependency is defined, the +different ways it can be depended on---from runtime-only with ``ctypes`` or a +``subprocess`` call to it being a build dependency that's linked against--- +before going into how to declare external dependencies in metadata. The +documentation should make explicit what is relevant for package authors, and +what for distro packagers. + +Material on this topic will be added to the most relevant packaging tutorials, +primarily the `Python Packaging User Guide`_. In addition, we expect that any +build backend that adds support for external dependencies metadata will include +information about that in its documentation, as will tools like ``auditwheel``. + + +Reference Implementation +======================== + +There is no reference implementation at this time. + + +Rejected Ideas +============== + +Specific syntax for external dependencies which are also packaged on PyPI +------------------------------------------------------------------------- + +There are non-Python packages which are packaged on PyPI, such as Ninja, +patchelf and CMake. What is typically desired is to use the system version of +those, and if it's not present on the system then install the PyPI package for +it. The authors believe that specific support for this scenario is not +necessary (or too complex to justify such support); a dependency provider for +external dependencies can treat PyPI as one possible source for obtaining the +package. + +Using library and header names as external dependencies +------------------------------------------------------- + +A previous draft PEP (`"External dependencies" (2015) <https://github.com/pypa/interoperability-peps/pull/30>`__) +proposed using specific library and header names as external dependencies. This +is too granular; using package names is a well-established pattern across +packaging ecosystems and should be preferred. + + +Open Issues +=========== + +Version specifiers for PURLs +---------------------------- + +Support in PURL for version expressions and ranges is still pending. The pull +request at `vers implementation for PURL`_ seems close to being merged, at +which point this PEP could adopt it. + +Syntax for virtual dependencies +------------------------------- + +The current syntax this PEP uses for virtual dependencies is +``virtual:type/name``, which is analogous to but not part of the PURL spec. +This open issue discusses supporting virtual dependencies within PURL: +`purl-spec#222 <https://github.com/package-url/purl-spec/issues/222>`__. + +Should a ``host-requires`` key be added under ``[build-system]``? +----------------------------------------------------------------- + +Adding ``host-requires`` for host dependencies that are on PyPI in order to +better support name mapping to other packaging systems with support for +cross-compiling may make sense. +`This issue <https://github.com/rgommers/peps/issues/6>`__ tracks this topic +and has arguments in favor and against adding ``host-requires`` under +``[build-system]`` as part of this PEP. + + +References +========== + +.. [#singular-vision-native-deps] The "define native requirements metadata" + part of the "Wanting a singular packaging vision" thread (2022, Discourse): + https://discuss.python.org/t/wanting-a-singular-packaging-tool-vision/21141/92 + +.. [#pypacking-native-deps] pypackaging-native: "Native dependencies" + https://pypackaging-native.github.io/key-issues/native-dependencies/ + +.. [#gcc-cross-terminology] GCC documentation - Configure Terms and History, + https://gcc.gnu.org/onlinedocs/gccint/Configure-Terms.html + +.. [#meson-cross] Meson documentation - Cross compilation + https://mesonbuild.com/Cross-compilation.html + +.. [#pypackaging-native-cross] pypackaging-native: "Cross compilation" + https://pypackaging-native.github.io/key-issues/cross_compilation/ + +.. [#pkgconfig-and-ctypes-findlibrary] The "``pkgconfig`` specification as an + alternative to ``ctypes.util.find_library``" thread (2023, Discourse): + https://discuss.python.org/t/pkgconfig-specification-as-an-alternative-to-ctypes-util-find-library/31379 + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. + + +.. _PyPI: https://pypi.org +.. _core metadata: https://packaging.python.org/specifications/core-metadata/ +.. _setuptools: https://setuptools.readthedocs.io/ +.. _setuptools metadata: https://setuptools.readthedocs.io/en/latest/setuptools.html#metadata +.. _SPDX: https://spdx.dev/ +.. _PURL: https://github.com/package-url/purl-spec/ +.. _vers: https://github.com/package-url/purl-spec/blob/version-range-spec/VERSION-RANGE-SPEC.rst +.. _vers implementation for PURL: https://github.com/package-url/purl-spec/pull/139 +.. _pyp2rpm: https://github.com/fedora-python/pyp2rpm +.. _Grayskull: https://github.com/conda/grayskull +.. _dh_python: https://www.debian.org/doc/packaging-manuals/python-policy/index.html#dh-python +.. _Repology: https://repology.org/ +.. _Dependabot: https://github.com/dependabot +.. _libraries.io: https://libraries.io/ +.. _crossenv: https://github.com/benfogle/crossenv +.. _Python Packaging User Guide: https://packaging.python.org +.. _auditwheel: https://github.com/pypa/auditwheel +.. _delocate: https://github.com/matthew-brett/delocate +.. _delvewheel: https://github.com/adang1345/delvewheel diff --git a/peps/pep-0726.rst b/peps/pep-0726.rst new file mode 100644 index 000000000..44cd3d9fa --- /dev/null +++ b/peps/pep-0726.rst @@ -0,0 +1,226 @@ +PEP: 726 +Title: Module ``__setattr__`` and ``__delattr__`` +Author: Sergey B Kirpichev <skirpichev@gmail.com> +Sponsor: Adam Turner <python@quite.org.uk> +Discussions-To: https://discuss.python.org/t/32640/ +Status: Draft +Type: Standards Track +Content-Type: text/x-rst +Created: 24-Aug-2023 +Python-Version: 3.13 +Post-History: `06-Apr-2023 <https://discuss.python.org/t/25506/>`__, + `31-Aug-2023 <https://discuss.python.org/t/32640/>`__, + + +Abstract +======== + +This PEP proposes supporting user-defined ``__setattr__`` +and ``__delattr__`` methods on modules to extend customization +of module attribute access beyond :pep:`562`. + +Motivation +========== + +There are several potential uses of a module ``__setattr__``: + +1. To prevent setting an attribute at all (i.e. make it read-only) +2. To validate the value to be assigned +3. To intercept setting an attribute and update some other state + +Proper support for read-only attributes would also require adding the +``__delattr__`` function to prevent their deletion. + +A typical workaround is assigning the ``__class__`` of a module object to a +custom subclass of :py:class:`python:types.ModuleType` (see [1]_). +Unfortunately, this also brings a noticeable speed regression +(~2-3x) for attribute *access*. It would be convenient to directly +support such customization, by recognizing ``__setattr__`` and ``__delattr__`` +methods defined in a module that would act like normal +:py:meth:`python:object.__setattr__` and :py:meth:`python:object.__delattr__` +methods, except that they will be defined on module *instances*. + +For example + +.. code:: python + + # mplib.py + + CONSTANT = 3.14 + prec = 53 + dps = 15 + + def dps_to_prec(n): + """Return the number of bits required to represent n decimals accurately.""" + return max(1, int(round((int(n)+1)*3.3219280948873626))) + + def prec_to_dps(n): + """Return the number of accurate decimals that can be represented with n bits.""" + return max(1, int(round(int(n)/3.3219280948873626)-1)) + + def validate(n): + n = int(n) + if n <= 0: + raise ValueError('non-negative integer expected') + return n + + def __setattr__(name, value): + if name == 'CONSTANT': + raise AttributeError('Read-only attribute!') + if name == 'dps': + value = validate(value) + globals()['dps'] = value + globals()['prec'] = dps_to_prec(value) + return + if name == 'prec': + value = validate(value) + globals()['prec'] = value + globals()['dps'] = prec_to_dps(value) + return + globals()[name] = value + + def __delattr__(name): + if name in ('CONSTANT', 'dps', 'prec'): + raise AttributeError('Read-only attribute!') + del globals()[name] + +.. code:: pycon + + >>> import mplib + >>> mplib.foo = 'spam' + >>> mplib.CONSTANT = 42 + Traceback (most recent call last): + ... + AttributeError: Read-only attribute! + >>> del mplib.foo + >>> del mplib.CONSTANT + Traceback (most recent call last): + ... + AttributeError: Read-only attribute! + >>> mplib.prec + 53 + >>> mplib.dps + 15 + >>> mplib.dps = 5 + >>> mplib.prec + 20 + >>> mplib.dps = 0 + Traceback (most recent call last): + ... + ValueError: non-negative integer expected + + +Specification +============= + +The ``__setattr__`` function at the module level should accept two +arguments, the name of an attribute and the value to be assigned, +and return :py:obj:`None` or raise an :exc:`AttributeError`. + +.. code:: python + + def __setattr__(name: str, value: typing.Any, /) -> None: ... + +The ``__delattr__`` function should accept one argument, +the name of an attribute, and return :py:obj:`None` or raise an +:py:exc:`AttributeError`: + +.. code:: python + + def __delattr__(name: str, /) -> None: ... + +The ``__setattr__`` and ``__delattr__`` functions are looked up in the +module ``__dict__``. If present, the appropriate function is called to +customize setting the attribute or its deletion, else the normal +mechanism (storing/deleting the value in the module dictionary) will work. + +Defining module ``__setattr__`` or ``__delattr__`` only affects lookups made +using the attribute access syntax---directly accessing the module globals +(whether by ``globals()`` within the module, or via a reference to the module's +globals dictionary) is unaffected. For example: + +.. code:: pycon + + >>> import mod + >>> mod.__dict__['foo'] = 'spam' # bypasses __setattr__, defined in mod.py + +or + +.. code:: python + + # mod.py + + def __setattr__(name, value): + ... + + foo = 'spam' # bypasses __setattr__ + globals()['bar'] = 'spam' # here too + + def f(): + global x + x = 123 + + f() # and here + +To use a module global and trigger ``__setattr__`` (or ``__delattr__``), +one can access it via ``sys.modules[__name__]`` within the module's code: + +.. code:: python + + # mod.py + + sys.modules[__name__].foo = 'spam' # bypasses __setattr__ + + def __setattr__(name, value): + ... + + sys.modules[__name__].bar = 'spam' # triggers __setattr__ + + +How to Teach This +================= + +The "Customizing module attribute access" [1]_ section of the documentation +will be expanded to include new functions. + + +Reference Implementation +======================== + +The reference implementation for this PEP can be found in `CPython PR #108261 +<https://github.com/python/cpython/pull/108261>`__. + + +Backwards compatibility +======================= + +This PEP may break code that uses module level (global) names +``__setattr__`` and ``__delattr__``, but the language reference +explicitly reserves *all* undocumented dunder names, and allows +"breakage without warning" [2]_. + +The performance implications of this PEP are small, since additional +dictionary lookup is much cheaper than storing/deleting the value in +the dictionary. Also it is hard to imagine a module that expects the +user to set (and/or delete) attributes enough times to be a +performance concern. On another hand, proposed mechanism allows to +override setting/deleting of attributes without affecting speed of +attribute access, which is much more likely scenario to get a +performance penalty. + + +Footnotes +========= + +.. [1] Customizing module attribute access + (https://docs.python.org/3.11/reference/datamodel.html#customizing-module-attribute-access) + +.. [2] Reserved classes of identifiers + (https://docs.python.org/3.11/reference/lexical_analysis.html#reserved-classes-of-identifiers) + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. diff --git a/peps/pep-0727.rst b/peps/pep-0727.rst new file mode 100644 index 000000000..d8f900415 --- /dev/null +++ b/peps/pep-0727.rst @@ -0,0 +1,518 @@ +PEP: 727 +Title: Documentation Metadata in Typing +Author: Sebastián Ramírez <tiangolo@gmail.com> +Sponsor: Jelle Zijlstra <jelle.zijlstra@gmail.com> +Discussions-To: https://discuss.python.org/t/32566 +Status: Draft +Type: Standards Track +Topic: Typing +Content-Type: text/x-rst +Created: 28-Aug-2023 +Python-Version: 3.13 +Post-History: `30-Aug-2023 <https://discuss.python.org/t/32566>`__ + + +Abstract +======== + +This document proposes a way to complement docstrings to add additional documentation +to Python symbols using type annotations with :py:class:`~typing.Annotated` +(in class attributes, function and method parameters, return values, and variables). + + +Motivation +========== + +The current standard method of documenting code APIs in Python is using docstrings. +But there's no standard way to document parameters in docstrings. + +There are several pseudo-standards for the format in these docstrings, and new +pseudo-standards can appear easily: numpy, Google, Keras, reST, etc. + +All these formats are some specific syntax inside a string. Because of this, when +editing those docstrings, editors can't easily provide support for autocompletion, +inline errors for broken syntax, etc. + +Editors don't have a way to support all the possible micro languages in the docstrings +and show nice user interfaces when developers use libraries with those different +formats. They could even add support for some of the syntaxes, but probably not all, +or not completely. + +Because the docstring is in a different place in the code than the actual parameters +and it requires duplication of information (the parameter name) the information about +a parameter is easily in a place in the code quite far away from the declaration of +the actual parameter. This means it's easy to refactor a function, remove a parameter, +and forget to remove its docs. The same happens when adding a new parameter: it's easy +to forget to add the docstring for it. + +And because of this same duplication of information (the parameter name) editors and +other tools would need to have complex custom logic to check or ensure the +consistency of the parameters in the signature and in their docstring, or they +would simply not be able to support that. + +Additionally, it would be difficult to robustly parse varying existing docstring +conventions to programatically get the documentation for each individual parameter +or variable at runtime. This would be useful, for example, +for testing the contents of each parameter's documentation, to ensure consistency +across several similar functions, or to extract and expose that same parameter +documentation in some other way (e.g. an API, a CLI, etc). + +Some of these previous formats tried to account for the lack of type annotations +in older Python versions by including typing information in the docstrings, +but now that information doesn't need to be in docstrings as there is now an official +:pep:`syntax for type annotations <484>`. + + +Rationale +========= + +This proposal intends to address these shortcomings by extending and complementing the +information in docstrings, keeping backwards compatibility with existing docstrings, +and doing it in a way that leverages the Python language and structure, via type +annotations with ``Annotated``, and a new function in ``typing``. + +The reason why this would belong in the standard Python library instead of an +external package is because although the implementation would be quite trivial, +the actual power and benefit from it would come from being a standard, so that +editors and other tools could implement support for it. + +This doesn't deprecate current usage of docstrings, it's transparent to common +developers (library users), and it's only opt-in for library authors that would +like to adopt it. + + +Specification +============= + +The main proposal is to introduce a new function, ``typing.doc()``, +to be used when documenting Python objects. +This function MUST only be used within :py:class:`~typing.Annotated` annotations. +The function takes a single string argument, ``documentation``, +and returns an instance of ``typing.DocInfo``, +which stores the input string unchanged. + +Any tool processing ``typing.DocInfo`` objects SHOULD interpret the string as +a docstring, and therefore SHOULD normalize whitespace +as if ``inspect.cleandoc()`` were used. + +The string passed to ``typing.doc()`` SHOULD be of the form that would be a valid docstring. +This means that `f-strings`__ and string operations SHOULD NOT be used. +As this cannot be enforced by the Python runtime, +tools SHOULD NOT rely on this behaviour, +and SHOULD exit with an error if such a prohibited string is encountered. + +__ https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals + +Examples +-------- + +Class attributes may be documented: + +.. code:: python + + from typing import Annotated, doc + + class User: + first_name: Annotated[str, doc("The user's first name")] + last_name: Annotated[str, doc("The user's last name")] + + ... + +As can function or method parameters: + +.. code:: python + + from typing import Annotated, doc + + def create_user( + first_name: Annotated[str, doc("The user's first name")], + last_name: Annotated[str, doc("The user's last name")], + cursor: DatabaseConnection | None = None, + ) -> Annotated[User, doc("The created user after saving in the database")]: + """Create a new user in the system. + + It needs the database connection to be already initialized. + """ + pass + + +Additional Scenarios +-------------------- + +The main scenarios that this proposal intends to cover are described above, and +for implementers to be conformant to this specification, they only need to support +those scenarios described above. + +Here are some additional edge case scenarios with their respective considerations, +but implementers are not required to support them. + + +Type Alias +'''''''''' + +When creating a type alias, like: + +.. code:: python + + Username = Annotated[str, doc("The name of a user in the system")] + + +The documentation would be considered to be carried by the parameter annotated +with ``Username``. + +So, in a function like: + +.. code:: python + + def hi(to: Username) -> None: ... + + +It would be equivalent to: + +.. code:: python + + def hi(to: Annotated[str, doc("The name of a user in the system")]) -> None: ... + +Nevertheless, implementers would not be required to support type aliases outside +of the final type annotation to be conformant with this specification, as it +could require more complex dereferencing logic. + + +Annotating Type Parameters +'''''''''''''''''''''''''' + +When annotating type parameters, as in: + +.. code:: python + + def hi( + to: list[Annotated[str, doc("The name of a user in a list")]], + ) -> None: ... + +The documentation in ``doc()`` would refer to what it is annotating, in this +case, each item in the list, not the list itself. + +There are currently no practical use cases for documenting type parameters, +so implementers are not required to support this scenario to be considered +conformant, but it's included for completeness. + + +Annotating Unions +''''''''''''''''' + +If used in one of the parameters of a union, as in: + +.. code:: python + + def hi( + to: str | Annotated[list[str], doc("List of user names")], + ) -> None: ... + +Again, the documentation in ``doc()`` would refer to what it is annotating, +in this case, this documents the list itself, not its items. + +In particular, the documentation would not refer to a single string passed as a +parameter, only to a list. + +There are currently no practical use cases for documenting unions, so implementers +are not required to support this scenario to be considered conformant, but it's +included for completeness. + + +Nested ``Annotated`` +'''''''''''''''''''' + +Continuing with the same idea above, if ``Annotated`` was used nested and used +multiple times in the same parameter, ``doc()`` would refer to the type it +is annotating. + +So, in an example like: + +.. code:: python + + def hi( + to: Annotated[ + Annotated[str, doc("A user name")] + | Annotated[list, doc("A list of user names")], + doc("Who to say hi to"), + ], + ) -> None: ... + + +The documentation for the whole parameter ``to`` would be considered to be +"``Who to say hi to``". + +The documentation for the case where that parameter ``to`` is specifically a ``str`` +would be considered to be "``A user name``". + +The documentation for the case where that parameter ``to`` is specifically a +``list`` would be considered to be "``A list of user names``". + +Implementers would only be required to support the top level use case, where the +documentation for ``to`` is considered to be "``Who to say hi to``". +They could optionally support having conditional documentation for when the type +of the parameter passed is of one type or another, but they are not required to do so. + + +Duplication +''''''''''' + +If ``doc()`` is used multiple times in a single ``Annotated``, it would be +considered invalid usage from the developer, for example: + +.. code:: python + + def hi( + to: Annotated[str, doc("A user name"), doc("The current user name")], + ) -> None: ... + + +Implementers can consider this invalid and are not required to support this to be +considered conformant. + +Nevertheless, as it might be difficult to enforce it on developers, implementers +can opt to support one of the ``doc()`` declarations. + +In that case, the suggestion would be to support the last one, just because +this would support overriding, for example, in: + +.. code:: python + + User = Annotated[str, doc("A user name")] + + CurrentUser = Annotated[User, doc("The current user name")] + + +Internally, in Python, ``CurrentUser`` here is equivalent to: + +.. code:: python + + CurrentUser = Annotated[str, + doc("A user name"), + doc("The current user name")] + + +For an implementation that supports the last ``doc()`` appearance, the above +example would be equivalent to: + +.. code:: python + + def hi(to: Annotated[str, doc("The current user name")]) -> None: ... + + +.. you need to fill these in: + + Backwards Compatibility + ======================= + + [Describe potential impact and severity on pre-existing code.] + + + Security Implications + ===================== + + [How could a malicious user take advantage of this new feature?] + + + How to Teach This + ================= + + [How to teach users, new and experienced, how to apply the PEP to their work.] + + +Reference Implementation +======================== + +``typing.doc`` and ``typing.DocInfo`` are implemented as follows: + +.. code:: python + + def doc(documentation: str, /) -> DocInfo: + return DocInfo(documentation) + + class DocInfo: + def __init__(self, documentation: str, /): + self.documentation = documentation + + +These have been implemented in the `typing_extensions`__ package. + +__ https://pypi.org/project/typing-extensions/ + + +Rejected Ideas +============== + + +Standardize Current Docstrings +------------------------------ + +A possible alternative would be to support and try to push as a standard one of the +existing docstring formats. But that would only solve the standardization. + +It wouldn't solve any of the other problems, like getting editor support +(syntax checks) for library authors, the distance and duplication of information +between a parameter definition and its documentation in the docstring, etc. + + +Extra Metadata and Decorator +---------------------------- + +An earlier version of this proposal included several parameters to indicate whether +an object is discouraged from use, what exceptions it may raise, etc. +To allow also deprecating functions and classes, it was also expected +that ``doc()`` could be used as a decorator. But this functionality is covered +by ``typing.deprecated()`` in :pep:`702`, so it was dropped from this proposal. + +A way to declare additional information could still be useful in the future, +but taking early feedback on this document, all that was postponed to future +proposals. + +This also shifts the focus from an all-encompassing function ``doc()`` +with multiple parameters to multiple composable functions, having ``doc()`` +handle one single use case: additional documentation in ``Annotated``. + +This design change also allows better interoperability with other proposals +like ``typing.deprecated()``, as in the future it could be considered to +allow having ``typing.deprecated()`` also in ``Annotated`` to deprecate +individual parameters, coexisting with ``doc()``. + + +Open Issues +=========== + + +Verbosity +--------- + +The main argument against this would be the increased verbosity. + +Nevertheless, this verbosity would not affect end users as they would not see the +internal code using ``typing.doc()``. + +And the cost of dealing with the additional verbosity would only be carried +by those library maintainers that decide to opt-in into this feature. + +Any authors that decide not to adopt it, are free to continue using docstrings +with any particular format they decide, no docstrings at all, etc. + +This argument could be analogous to the argument against type annotations +in general, as they do indeed increase verbosity, in exchange for their +features. But again, as with type annotations, this would be optional and only +to be used by those that are willing to take the extra verbosity in exchange +for the benefits. + + +Documentation is not Typing +--------------------------- + +It could also be argued that documentation is not really part of typing, or that +it should live in a different module. Or that this information should not be part +of the signature but live in another place (like the docstring). + +Nevertheless, type annotations in Python could already be considered, by default, +mainly documentation: they carry additional information about variables, +parameters, return types, and by default they don't have any runtime behavior. + +It could be argued that this proposal extends the type of information that +type annotations carry, the same way as :pep:`702` extends them to include +deprecation information. + +And as described above, including this in ``typing_extensions`` to support older +versions of Python would have a very simple and practical benefit. + + +Multiple Standards +------------------ + +Another argument against this would be that it would create another standard, +and that there are already several pseudo-standards for docstrings. It could +seem better to formalize one of the currently existing standards. + +Nevertheless, as stated above, none of those standards cover the general +drawbacks of a doctsring-based approach that this proposal solves naturally. + +None of the editors have full docstring editing support (even when they have +rendering support). Again, this is solved by this proposal just by using +standard Python syntax and structures instead of a docstring microsyntax. + +The effort required to implement support for this proposal by tools would +be minimal compared to that required for alternative docstring-based +pseudo-standards, as for this proposal, editors would only need to +access an already existing value in their ASTs, instead of writing a parser +for a new string microsyntax. + +In the same way, it can be seen that, in many cases, a new standard that +takes advantage of new features and solves several problems from previous +methods can be worth having. As is the case with the new ``pyproject.toml``, +``dataclass_transform``, the new typing pipe/union (``|``) operator, and other cases. + + +Adoption +-------- + +As this is a new standard proposal, it would only make sense if it had +interest from the community. + +Fortunately there's already interest from several mainstream libraries +from several developers and teams, including FastAPI, Typer, SQLModel, +Asyncer (from the author of this proposal), Pydantic, Strawberry, and others, +from other teams. + +There's also interest and support from documentation tools, like +`mkdocstrings <https://github.com/mkdocstrings/mkdocstrings>`__, which added +support even for an earlier version of this proposal. + +All the CPython core developers contacted for early feedback (at least 4) have +shown interest and support for this proposal. + +Editor developers (VS Code and PyCharm) have shown some interest, while showing +concerns about the verbosity of the proposal, although not about the +implementation (which is what would affect them the most). And they have shown +they would consider adding support for this if it were to become an +official standard. In that case, they would only need to add support for +rendering, as support for editing, which is normally non-existing for +other standards, is already there, as they already support editing standard +Python syntax. + + +Bike Shedding +------------- + +I think ``doc()`` is a good name for the main function. But it might make sense +to consider changing the names for the other parts. + +The returned class containing info currently named ``DocInfo`` could instead +be named just ``Doc``. Although it could make verbal conversations more +confusing as it's the same word as the name of the function. + +The parameter received by ``doc()`` currently named ``documentation`` could +instead be named also ``doc``, but it would make it more ambiguous in +discussions to distinguish when talking about the function and the parameter, +although it would simplify the amount of terms, but as these terms refer to +different things closely related, it could make sense to have different names. + +The parameter received by ``doc()`` currently named ``documentation`` could +instead be named ``value``, but the word "documentation" might convey +the meaning better. + +The parameter received by ``doc()`` currently named ``documentation`` could be a +position-only parameter, in which case the name wouldn't matter much. But then +there wouldn't be a way to make it match with the ``DocInfo`` attribute. + +The ``DocInfo`` class has a single attribute ``documentation``, this name matches +the parameter passed to ``doc()``. It could be named something different, +like ``doc``, but this would mean a mismatch between the ``doc()`` parameter +``documentation`` and the equivalent attribute ``doc``, and it would mean that in +one case (in the function), the term ``doc`` refers to a function, and in the +other case (the resulting class) the term ``doc`` refers to a string value. + +This shows the logic to select the current terms, but it could all be +discussed further. + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. diff --git a/pep-0754.txt b/peps/pep-0754.rst similarity index 100% rename from pep-0754.txt rename to peps/pep-0754.rst diff --git a/pep-0801.rst b/peps/pep-0801.rst similarity index 100% rename from pep-0801.rst rename to peps/pep-0801.rst diff --git a/pep-3000.txt b/peps/pep-3000.rst similarity index 100% rename from pep-3000.txt rename to peps/pep-3000.rst diff --git a/pep-3001.txt b/peps/pep-3001.rst similarity index 96% rename from pep-3001.txt rename to peps/pep-3001.rst index d4fb893c5..08df2138f 100644 --- a/pep-3001.txt +++ b/peps/pep-3001.rst @@ -29,8 +29,8 @@ but are too widely used to be deprecated or removed. Python 3000 is the big occasion to get rid of them. There will have to be a document listing all removed modules, together -with information on possible substitutes or alternatives. This infor- -mation will also have to be provided by the python3warn.py porting +with information on possible substitutes or alternatives. This +information will also have to be provided by the python3warn.py porting helper script mentioned in PEP XXX. diff --git a/pep-3002.txt b/peps/pep-3002.rst similarity index 100% rename from pep-3002.txt rename to peps/pep-3002.rst diff --git a/pep-3003.txt b/peps/pep-3003.rst similarity index 100% rename from pep-3003.txt rename to peps/pep-3003.rst diff --git a/pep-3099.txt b/peps/pep-3099.rst similarity index 100% rename from pep-3099.txt rename to peps/pep-3099.rst diff --git a/pep-3100.txt b/peps/pep-3100.rst similarity index 100% rename from pep-3100.txt rename to peps/pep-3100.rst diff --git a/pep-3101.txt b/peps/pep-3101.rst similarity index 99% rename from pep-3101.txt rename to peps/pep-3101.rst index 6b4fcc90e..9267d17cb 100644 --- a/pep-3101.txt +++ b/peps/pep-3101.rst @@ -310,10 +310,10 @@ The available integer presentation types are:: Unicode character before printing. 'd' - Decimal Integer. Outputs the number in base 10. 'o' - Octal format. Outputs the number in base 8. - 'x' - Hex format. Outputs the number in base 16, using lower- - case letters for the digits above 9. - 'X' - Hex format. Outputs the number in base 16, using upper- - case letters for the digits above 9. + 'x' - Hex format. Outputs the number in base 16, using + lower-case letters for the digits above 9. + 'X' - Hex format. Outputs the number in base 16, using + upper-case letters for the digits above 9. 'n' - Number. This is the same as 'd', except that it uses the current locale setting to insert the appropriate number separator characters. diff --git a/pep-3102.txt b/peps/pep-3102.rst similarity index 100% rename from pep-3102.txt rename to peps/pep-3102.rst diff --git a/pep-3103.txt b/peps/pep-3103.rst similarity index 99% rename from pep-3103.txt rename to peps/pep-3103.rst index 1f4b9fe44..007b909a3 100644 --- a/pep-3103.txt +++ b/peps/pep-3103.rst @@ -232,7 +232,7 @@ like this:: case [*]EXPR, [*]EXPR, ...: -The `*` notation is similar to the use of prefix `*` already in use for +The ``*`` notation is similar to the use of prefix ``*`` already in use for variable-length parameter lists and for passing computed argument lists, and often proposed for value-unpacking (e.g. ``a, b, *c = X`` as an alternative to ``(a, b), c = X[:2], X[2:]``). diff --git a/pep-3104.txt b/peps/pep-3104.rst similarity index 100% rename from pep-3104.txt rename to peps/pep-3104.rst diff --git a/pep-3105.txt b/peps/pep-3105.rst similarity index 100% rename from pep-3105.txt rename to peps/pep-3105.rst diff --git a/pep-3106.txt b/peps/pep-3106.rst similarity index 100% rename from pep-3106.txt rename to peps/pep-3106.rst diff --git a/pep-3107.txt b/peps/pep-3107.rst similarity index 100% rename from pep-3107.txt rename to peps/pep-3107.rst diff --git a/pep-3108.txt b/peps/pep-3108.rst similarity index 100% rename from pep-3108.txt rename to peps/pep-3108.rst diff --git a/pep-3109.txt b/peps/pep-3109.rst similarity index 100% rename from pep-3109.txt rename to peps/pep-3109.rst diff --git a/pep-3110.txt b/peps/pep-3110.rst similarity index 100% rename from pep-3110.txt rename to peps/pep-3110.rst diff --git a/pep-3111.txt b/peps/pep-3111.rst similarity index 100% rename from pep-3111.txt rename to peps/pep-3111.rst diff --git a/pep-3112.txt b/peps/pep-3112.rst similarity index 100% rename from pep-3112.txt rename to peps/pep-3112.rst diff --git a/pep-3113.txt b/peps/pep-3113.rst similarity index 100% rename from pep-3113.txt rename to peps/pep-3113.rst diff --git a/pep-3114.txt b/peps/pep-3114.rst similarity index 100% rename from pep-3114.txt rename to peps/pep-3114.rst diff --git a/pep-3115.txt b/peps/pep-3115.rst similarity index 99% rename from pep-3115.txt rename to peps/pep-3115.rst index 57d5e64f4..a8c2560dc 100644 --- a/pep-3115.txt +++ b/peps/pep-3115.rst @@ -137,8 +137,8 @@ an arbitrary length list of base classes. After the base classes, there may be one or more keyword arguments, one of which can be *metaclass*. Note that the *metaclass* argument is not included in *kwargs*, since it is filtered out by the normal parameter -assignment algorithm. (Note also that *metaclass* is a keyword- -only argument as per :pep:`3102`.) +assignment algorithm. (Note also that *metaclass* is a +keyword-only argument as per :pep:`3102`.) Even though ``__prepare__`` is not required, the default metaclass ('type') implements it, for the convenience of subclasses calling diff --git a/pep-3116.txt b/peps/pep-3116.rst similarity index 100% rename from pep-3116.txt rename to peps/pep-3116.rst diff --git a/pep-3117.txt b/peps/pep-3117.rst similarity index 100% rename from pep-3117.txt rename to peps/pep-3117.rst diff --git a/pep-3118.txt b/peps/pep-3118.rst similarity index 100% rename from pep-3118.txt rename to peps/pep-3118.rst diff --git a/pep-3119.txt b/peps/pep-3119.rst similarity index 100% rename from pep-3119.txt rename to peps/pep-3119.rst diff --git a/pep-3120.txt b/peps/pep-3120.rst similarity index 100% rename from pep-3120.txt rename to peps/pep-3120.rst diff --git a/pep-3121.txt b/peps/pep-3121.rst similarity index 100% rename from pep-3121.txt rename to peps/pep-3121.rst diff --git a/pep-3122.txt b/peps/pep-3122.rst similarity index 100% rename from pep-3122.txt rename to peps/pep-3122.rst diff --git a/pep-3123.txt b/peps/pep-3123.rst similarity index 100% rename from pep-3123.txt rename to peps/pep-3123.rst diff --git a/pep-3124.txt b/peps/pep-3124.rst similarity index 99% rename from pep-3124.txt rename to peps/pep-3124.rst index a3500ba5c..e755609a2 100644 --- a/pep-3124.txt +++ b/peps/pep-3124.rst @@ -959,8 +959,8 @@ about. As a result, the vast majority of overloads can be found adjacent to either the function being overloaded, or to a newly-defined type for -which the overload is adding support. Thus, overloads are highly- -discoverable in the common case, as you are either looking at the +which the overload is adding support. Thus, overloads are +highly-discoverable in the common case, as you are either looking at the function or the type, or both. It is only in rather infrequent cases that one will have overloads in a diff --git a/pep-3125.txt b/peps/pep-3125.rst similarity index 100% rename from pep-3125.txt rename to peps/pep-3125.rst diff --git a/pep-3126.txt b/peps/pep-3126.rst similarity index 100% rename from pep-3126.txt rename to peps/pep-3126.rst diff --git a/pep-3127.txt b/peps/pep-3127.rst similarity index 100% rename from pep-3127.txt rename to peps/pep-3127.rst diff --git a/pep-3128.txt b/peps/pep-3128.rst similarity index 100% rename from pep-3128.txt rename to peps/pep-3128.rst diff --git a/pep-3129.txt b/peps/pep-3129.rst similarity index 100% rename from pep-3129.txt rename to peps/pep-3129.rst diff --git a/pep-3130.txt b/peps/pep-3130.rst similarity index 100% rename from pep-3130.txt rename to peps/pep-3130.rst diff --git a/pep-3131.txt b/peps/pep-3131.rst similarity index 100% rename from pep-3131.txt rename to peps/pep-3131.rst diff --git a/pep-3132.txt b/peps/pep-3132.rst similarity index 100% rename from pep-3132.txt rename to peps/pep-3132.rst diff --git a/pep-3133.txt b/peps/pep-3133.rst similarity index 100% rename from pep-3133.txt rename to peps/pep-3133.rst diff --git a/pep-3134.txt b/peps/pep-3134.rst similarity index 100% rename from pep-3134.txt rename to peps/pep-3134.rst diff --git a/pep-3135.txt b/peps/pep-3135.rst similarity index 97% rename from pep-3135.txt rename to peps/pep-3135.rst index da66891b1..cbd13aa8b 100644 --- a/pep-3135.txt +++ b/peps/pep-3135.rst @@ -165,10 +165,10 @@ super(self, \*args) or __super__(self, \*args) This solution only solves the problem of the type indication, does not handle differently named super methods, and is explicit about the name of the instance. It is less flexible without being able to enacted on other method -names, in cases where that is needed. One use case this fails is where a base- -class has a factory classmethod and a subclass has two factory classmethods, -both of which needing to properly make super calls to the one in the base- -class. +names, in cases where that is needed. One use case this fails is where a +base-class has a factory classmethod and a subclass has two factory +classmethods,both of which needing to properly make super calls to the one +in the base-class. super.foo(self, \*args) ----------------------- diff --git a/pep-3136.txt b/peps/pep-3136.rst similarity index 100% rename from pep-3136.txt rename to peps/pep-3136.rst diff --git a/pep-3137.txt b/peps/pep-3137.rst similarity index 100% rename from pep-3137.txt rename to peps/pep-3137.rst diff --git a/pep-3138.txt b/peps/pep-3138.rst similarity index 100% rename from pep-3138.txt rename to peps/pep-3138.rst diff --git a/pep-3139.txt b/peps/pep-3139.rst similarity index 100% rename from pep-3139.txt rename to peps/pep-3139.rst diff --git a/pep-3140.txt b/peps/pep-3140.rst similarity index 100% rename from pep-3140.txt rename to peps/pep-3140.rst diff --git a/pep-3141.txt b/peps/pep-3141.rst similarity index 100% rename from pep-3141.txt rename to peps/pep-3141.rst diff --git a/pep-3142.txt b/peps/pep-3142.rst similarity index 100% rename from pep-3142.txt rename to peps/pep-3142.rst diff --git a/pep-3143.txt b/peps/pep-3143.rst similarity index 75% rename from pep-3143.txt rename to peps/pep-3143.rst index fa0858d25..a360aa374 100644 --- a/pep-3143.txt +++ b/peps/pep-3143.rst @@ -38,7 +38,7 @@ Specification Example usage ============= -Simple example of direct `DaemonContext` usage:: +Simple example of direct ``DaemonContext`` usage:: import daemon @@ -90,24 +90,24 @@ More complex example usage:: Interface ========= -A new package, `daemon`, is added to the standard library. +A new package, ``daemon``, is added to the standard library. -A class, `DaemonContext`, is defined to represent the settings and +A class, ``DaemonContext``, is defined to represent the settings and process context for the program running as a daemon process. ``DaemonContext`` objects ========================= -A `DaemonContext` instance represents the behaviour settings and +A ``DaemonContext`` instance represents the behaviour settings and process context for the program when it becomes a daemon. The behaviour and environment is customised by setting options on the -instance, before calling the `open` method. +instance, before calling the ``open`` method. -Each option can be passed as a keyword argument to the `DaemonContext` +Each option can be passed as a keyword argument to the ``DaemonContext`` constructor, or subsequently altered by assigning to an attribute on -the instance at any time prior to calling `open`. That is, for -options named `wibble` and `wubble`, the following invocation:: +the instance at any time prior to calling ``open``. That is, for +options named ``wibble`` and ``wubble``, the following invocation:: foo = daemon.DaemonContext(wibble=bar, wubble=baz) foo.open() @@ -121,24 +121,24 @@ is equivalent to:: The following options are defined. -`files_preserve` +``files_preserve`` :Default: ``None`` List of files that should *not* be closed when starting the daemon. If ``None``, all open file descriptors will be closed. Elements of the list are file descriptors (as returned by a file - object's `fileno()` method) or Python `file` objects. Each + object's ``fileno()`` method) or Python ``file`` objects. Each specifies a file that is not to be closed during daemon start. -`chroot_directory` +``chroot_directory`` :Default: ``None`` Full path to a directory to set as the effective root directory of the process. If ``None``, specifies that the root directory is not to be changed. -`working_directory` +``working_directory`` :Default: ``'/'`` Full path of the working directory to which the process should @@ -149,7 +149,7 @@ The following options are defined. be left at default or set to a directory that is a sensible “home directory” for the daemon while it is running. -`umask` +``umask`` :Default: ``0`` File access creation mask (“umask”) to set for the process on @@ -159,13 +159,13 @@ The following options are defined. starting the daemon will reset the umask to this value so that files are created by the daemon with access modes as it expects. -`pidfile` +``pidfile`` :Default: ``None`` Context manager for a PID lock file. When the daemon context opens - and closes, it enters and exits the `pidfile` context manager. + and closes, it enters and exits the ``pidfile`` context manager. -`detach_process` +``detach_process`` :Default: ``None`` If ``True``, detach the process context when opening the daemon @@ -174,10 +174,10 @@ The following options are defined. If unspecified (``None``) during initialisation of the instance, this will be set to ``True`` by default, and ``False`` only if detaching the process is determined to be redundant; for example, - in the case when the process was started by `init`, by `initd`, or - by `inetd`. + in the case when the process was started by ``init``, by ``initd``, or + by ``inetd``. -`signal_map` +``signal_map`` :Default: system-dependent Mapping from operating system signals to callback actions. @@ -215,10 +215,10 @@ The following options are defined. on how to determine what circumstances dictate the need for signal handlers. -`uid` +``uid`` :Default: ``os.getuid()`` -`gid` +``gid`` :Default: ``os.getgid()`` The user ID (“UID”) value and group ID (“GID”) value to switch @@ -228,122 +228,122 @@ The following options are defined. relinquish any effective privilege elevation inherited by the process. -`prevent_core` +``prevent_core`` :Default: ``True`` If true, prevents the generation of core files, in order to avoid - leaking sensitive information from daemons run as `root`. + leaking sensitive information from daemons run as ``root``. -`stdin` +``stdin`` :Default: ``None`` -`stdout` +``stdout`` :Default: ``None`` -`stderr` +``stderr`` :Default: ``None`` - Each of `stdin`, `stdout`, and `stderr` is a file-like object + Each of ``stdin``, ``stdout``, and ``stderr`` is a file-like object which will be used as the new file for the standard I/O stream - `sys.stdin`, `sys.stdout`, and `sys.stderr` respectively. The file + ``sys.stdin``, ``sys.stdout``, and ``sys.stderr`` respectively. The file should therefore be open, with a minimum of mode 'r' in the case - of `stdin`, and mode 'w+' in the case of `stdout` and `stderr`. + of ``stdin``, and mode 'w+' in the case of ``stdout`` and ``stderr``. - If the object has a `fileno()` method that returns a file + If the object has a ``fileno()`` method that returns a file descriptor, the corresponding file will be excluded from being closed during daemon start (that is, it will be treated as though - it were listed in `files_preserve`). + it were listed in ``files_preserve``). If ``None``, the corresponding system stream is re-bound to the - file named by `os.devnull`. + file named by ``os.devnull``. The following methods are defined. -`open()` +``open()`` :Return: ``None`` Open the daemon context, turning the current program into a daemon process. This performs the following steps: - * If this instance's `is_open` property is true, return - immediately. This makes it safe to call `open` multiple times on + * If this instance's ``is_open`` property is true, return + immediately. This makes it safe to call ``open`` multiple times on an instance. - * If the `prevent_core` attribute is true, set the resource limits + * If the ``prevent_core`` attribute is true, set the resource limits for the process to prevent any core dump from the process. - * If the `chroot_directory` attribute is not ``None``, set the + * If the ``chroot_directory`` attribute is not ``None``, set the effective root directory of the process to that directory (via - `os.chroot`). + ``os.chroot``). This allows running the daemon process inside a “chroot gaol” as a means of limiting the system's exposure to rogue behaviour by the process. Note that the specified directory needs to already be set up for this purpose. - * Set the process UID and GID to the `uid` and `gid` attribute + * Set the process UID and GID to the ``uid`` and ``gid`` attribute values. * Close all open file descriptors. This excludes those listed in - the `files_preserve` attribute, and those that correspond to the - `stdin`, `stdout`, or `stderr` attributes. + the ``files_preserve`` attribute, and those that correspond to the + ``stdin``, ``stdout``, or ``stderr`` attributes. * Change current working directory to the path specified by the - `working_directory` attribute. + ``working_directory`` attribute. * Reset the file access creation mask to the value specified by - the `umask` attribute. + the ``umask`` attribute. - * If the `detach_process` option is true, detach the current + * If the ``detach_process`` option is true, detach the current process into its own process group, and disassociate from any controlling terminal. - * Set signal handlers as specified by the `signal_map` attribute. + * Set signal handlers as specified by the ``signal_map`` attribute. - * If any of the attributes `stdin`, `stdout`, `stderr` are not - ``None``, bind the system streams `sys.stdin`, `sys.stdout`, - and/or `sys.stderr` to the files represented by the + * If any of the attributes ``stdin``, ``stdout``, ``stderr`` are not + ``None``, bind the system streams ``sys.stdin``, ``sys.stdout``, + and/or ``sys.stderr`` to the files represented by the corresponding attributes. Where the attribute has a file descriptor, the descriptor is duplicated (instead of re-binding the name). - * If the `pidfile` attribute is not ``None``, enter its context + * If the ``pidfile`` attribute is not ``None``, enter its context manager. - * Mark this instance as open (for the purpose of future `open` and - `close` calls). + * Mark this instance as open (for the purpose of future ``open`` and + ``close`` calls). - * Register the `close` method to be called during Python's exit + * Register the ``close`` method to be called during Python's exit processing. When the function returns, the running program is a daemon process. -`close()` +``close()`` :Return: ``None`` Close the daemon context. This performs the following steps: - * If this instance's `is_open` property is false, return - immediately. This makes it safe to call `close` multiple times + * If this instance's ``is_open`` property is false, return + immediately. This makes it safe to call ``close`` multiple times on an instance. - * If the `pidfile` attribute is not ``None``, exit its context + * If the ``pidfile`` attribute is not ``None``, exit its context manager. - * Mark this instance as closed (for the purpose of future `open` - and `close` calls). + * Mark this instance as closed (for the purpose of future ``open`` + and ``close`` calls). -`is_open` +``is_open`` :Return: ``True`` if the instance is open, ``False`` otherwise. This property exposes the state indicating whether the instance is - currently open. It is ``True`` if the instance's `open` method has - been called and the `close` method has not subsequently been + currently open. It is ``True`` if the instance's ``open`` method has + been called and the ``close`` method has not subsequently been called. -`terminate(signal_number, stack_frame)` +``terminate(signal_number, stack_frame)`` :Return: ``None`` Signal handler for the ``signal.SIGTERM`` signal. Performs the @@ -354,16 +354,16 @@ The following methods are defined. The class also implements the context manager protocol via ``__enter__`` and ``__exit__`` methods. -`__enter__()` +``__enter__()`` :Return: The ``DaemonContext`` instance - Call the instance's `open()` method, then return the instance. + Call the instance's ``open()`` method, then return the instance. -`__exit__(exc_type, exc_value, exc_traceback)` +``__exit__(exc_type, exc_value, exc_traceback)`` :Return: ``True`` or ``False`` as defined by the context manager protocol - Call the instance's `close()` method, then return ``True`` if the + Call the instance's ``close()`` method, then return ``True`` if the exception was handled or ``False`` if it was not. @@ -409,13 +409,13 @@ following steps to become a Unix daemon process. * Correctly handle the following circumstances: - * Started by System V `init` process. + * Started by System V ``init`` process. * Daemon termination by ``SIGTERM`` signal. * Children generate ``SIGCLD`` signal. -The `daemon` tool [slack-daemon]_ lists (in its summary of features) +The ``daemon`` tool [slack-daemon]_ lists (in its summary of features) behaviour that should be performed when turning a program into a well-behaved Unix daemon process. It differs from this PEP's intent in that it invokes a *separate* program as a daemon process. The @@ -424,7 +424,7 @@ once the program is already running: * Sets up the correct process context for a daemon. -* Behaves sensibly when started by `initd(8)` or `inetd(8)`. +* Behaves sensibly when started by ``initd(8)`` or ``inetd(8)``. * Revokes any suid or sgid privileges to reduce security risks in case daemon is incorrectly installed with special privileges. @@ -468,7 +468,7 @@ a service. Reference Implementation ======================== -The `python-daemon` package [python-daemon]_. +The ``python-daemon`` package [python-daemon]_. Other daemon implementations ============================ @@ -482,23 +482,23 @@ following implementations: * Many good ideas were contributed by the community to Python cookbook recipes #66012 [cookbook-66012]_ and #278731 [cookbook-278731]_. -* The `bda.daemon` library [bda.daemon]_ is an implementation of +* The ``bda.daemon`` library [bda.daemon]_ is an implementation of [cookbook-66012]_. It is the predecessor of [python-daemon]_. Other Python daemon implementations that differ from this PEP: -* The `zdaemon` tool [zdaemon]_ was written for the Zope project. Like +* The ``zdaemon`` tool [zdaemon]_ was written for the Zope project. Like [slack-daemon]_, it differs from this specification because it is used to run another program as a daemon process. -* The Python library `daemon` [clapper-daemon]_ is (according to its +* The Python library ``daemon`` [clapper-daemon]_ is (according to its homepage) no longer maintained. As of version 1.0.1, it implements the basic steps from [stevens]_. -* The `daemonize` library [seutter-daemonize]_ also implements the +* The ``daemonize`` library [seutter-daemonize]_ also implements the basic steps from [stevens]_. -* Ray Burr's `daemon.py` module [burr-daemon]_ provides the [stevens]_ +* Ray Burr's ``daemon.py`` module [burr-daemon]_ provides the [stevens]_ procedure as well as PID file handling and redirection of output to syslog. @@ -507,8 +507,8 @@ Other Python daemon implementations that differ from this PEP: with the rest of the Twisted framework; it differs significantly from the API in this PEP. -* The Python `initd` library [dagitses-initd]_, which uses - [clapper-daemon]_, implements an equivalent of Unix `initd(8)` for +* The Python ``initd`` library [dagitses-initd]_, which uses + [clapper-daemon]_, implements an equivalent of Unix ``initd(8)`` for controlling a daemon process. @@ -518,17 +518,17 @@ References .. [stevens] - `Unix Network Programming`, W. Richard Stevens, 1994 Prentice + ``Unix Network Programming``, W. Richard Stevens, 1994 Prentice Hall. .. [slack-daemon] - The (non-Python) “libslack” implementation of a `daemon` tool + The (non-Python) “libslack” implementation of a ``daemon`` tool `<http://www.libslack.org/daemon/>`_ by “raf” <raf@raf.org>. .. [python-daemon] - The `python-daemon` library + The ``python-daemon`` library `<http://pypi.python.org/pypi/python-daemon/>`_ by Ben Finney et al. @@ -544,39 +544,39 @@ References .. [bda.daemon] - The `bda.daemon` library + The ``bda.daemon`` library `<http://pypi.python.org/pypi/bda.daemon/>`_ by Robert Niederreiter et al. .. [zdaemon] - The `zdaemon` tool `<http://pypi.python.org/pypi/zdaemon/>`_ by + The ``zdaemon`` tool `<http://pypi.python.org/pypi/zdaemon/>`_ by Guido van Rossum et al. .. [clapper-daemon] - The `daemon` library `<http://pypi.python.org/pypi/daemon/>`_ by + The ``daemon`` library `<http://pypi.python.org/pypi/daemon/>`_ by Brian Clapper. .. [seutter-daemonize] - The `daemonize` library `<http://daemonize.sourceforge.net/>`_ by + The ``daemonize`` library `<http://daemonize.sourceforge.net/>`_ by Jerry Seutter. .. [burr-daemon] - The `daemon.py` module + The ``daemon.py`` module `<http://www.nightmare.com/~ryb/code/daemon.py>`_ by Ray Burr. .. [twisted] - The `Twisted` application framework + The ``Twisted`` application framework `<http://pypi.python.org/pypi/Twisted/>`_ by Glyph Lefkowitz et al. .. [dagitses-initd] - The Python `initd` library `<http://pypi.python.org/pypi/initd/>`_ + The Python ``initd`` library `<http://pypi.python.org/pypi/initd/>`_ by Michael Andreas Dagitses. diff --git a/pep-3144.txt b/peps/pep-3144.rst similarity index 100% rename from pep-3144.txt rename to peps/pep-3144.rst diff --git a/pep-3145.txt b/peps/pep-3145.rst similarity index 100% rename from pep-3145.txt rename to peps/pep-3145.rst diff --git a/pep-3146.txt b/peps/pep-3146.rst similarity index 99% rename from pep-3146.txt rename to peps/pep-3146.rst index afa39136a..dcbdbfe05 100644 --- a/pep-3146.txt +++ b/peps/pep-3146.rst @@ -132,10 +132,10 @@ they are used. However, if by chance the historically-untaken branch is now taken, or some integer-optimized ``a + b`` snippet receives two strings, we must support this. We cannot change Python semantics. Each of these sections of optimized machine -code is preceded by a `guard`, which checks whether the simplifying assumptions -we made when optimizing still hold. If the assumptions are still valid, we run -the optimized machine code; if they are not, we revert back to the interpreter -and pick up where we left off. +code is preceded by a ``guard``, which checks whether the simplifying +assumptions we made when optimizing still hold. If the assumptions are still +valid, we run the optimized machine code; if they are not, we revert back to +the interpreter and pick up where we left off. We have chosen to reuse a set of existing compiler libraries called LLVM [#llvm]_ for code generation and code optimization. This has saved our small @@ -848,8 +848,8 @@ Unladen Swallow [#us-oprofile-change]_, other profiling tools should be easy as well, provided they support a similar JIT interface [#oprofile-jit-interface]_. We have documented the process for using oProfile to profile Unladen Swallow -[#oprofile-workflow]_. This document will be merged into CPython's `Doc/` tree -in the merge. +[#oprofile-workflow]_. This document will be merged into CPython's ``Doc/`` +tree in the merge. Addition of C++ to CPython diff --git a/pep-3147-1.dia b/peps/pep-3147-1.dia similarity index 100% rename from pep-3147-1.dia rename to peps/pep-3147-1.dia diff --git a/pep-3147-1.png b/peps/pep-3147-1.png similarity index 100% rename from pep-3147-1.png rename to peps/pep-3147-1.png diff --git a/pep-3147.txt b/peps/pep-3147.rst similarity index 74% rename from pep-3147.txt rename to peps/pep-3147.rst index 8b54d8a30..5f3e54fa6 100644 --- a/pep-3147.txt +++ b/peps/pep-3147.rst @@ -30,7 +30,7 @@ CPython compiles its source code into "byte code", and for performance reasons, it caches this byte code on the file system whenever the source file has changes. This makes loading of Python modules much faster because the compilation phase can be bypassed. When your -source file is `foo.py`, CPython caches the byte code in a `foo.pyc` +source file is ``foo.py``, CPython caches the byte code in a ``foo.pyc`` file right next to the source. Byte code files contain two 32-bit big-endian numbers followed by the @@ -59,11 +59,11 @@ with Python 2.6 being the default. This causes a conflict for third party Python source files installed by the system, because you cannot compile a single Python source file -for more than one Python version at a time. When Python finds a `pyc` +for more than one Python version at a time. When Python finds a ``pyc`` file with a non-matching magic number, it falls back to the slower process of recompiling the source. Thus if your system installed a -`/usr/share/python/foo.py`, two different versions of Python would -fight over the `pyc` file and rewrite it each time the source is +``/usr/share/python/foo.py``, two different versions of Python would +fight over the ``pyc`` file and rewrite it each time the source is compiled. (The standard library is unaffected by this, since multiple versions of the stdlib *are* installed on such distributions..) @@ -99,7 +99,7 @@ Proposal Python's import machinery is extended to write and search for byte code cache files in a single directory inside every Python package -directory. This directory will be called `__pycache__`. +directory. This directory will be called ``__pycache__``. Further, pyc file names will contain a magic string (called a "tag") that differentiates the Python version they were compiled for. This @@ -107,36 +107,36 @@ allows multiple byte compiled cache files to co-exist for a single Python source file. The magic tag is implementation defined, but should contain the -implementation name and a version number shorthand, e.g. `cpython-32`. +implementation name and a version number shorthand, e.g. ``cpython-32``. It must be unique among all versions of Python, and whenever the magic -number is bumped, a new magic tag must be defined. An example `pyc` -file for Python 3.2 is thus `foo.cpython-32.pyc`. +number is bumped, a new magic tag must be defined. An example ``pyc`` +file for Python 3.2 is thus ``foo.cpython-32.pyc``. -The magic tag is available in the `imp` module via the `get_tag()` -function. This is parallel to the `imp.get_magic()` function. +The magic tag is available in the ``imp`` module via the ``get_tag()`` +function. This is parallel to the ``imp.get_magic()`` function. This scheme has the added benefit of reducing the clutter in a Python package directory. When a Python source file is imported for the first time, a -`__pycache__` directory will be created in the package directory, if +``__pycache__`` directory will be created in the package directory, if one does not already exist. The pyc file for the imported source will -be written to the `__pycache__` directory, using the magic-tag -formatted name. If either the creation of the `__pycache__` directory +be written to the ``__pycache__`` directory, using the magic-tag +formatted name. If either the creation of the ``__pycache__`` directory or the pyc file inside that fails, the import will still succeed, just as it does in a pre-:pep:`3147` world. -If the py source file is missing, the pyc file inside `__pycache__` +If the py source file is missing, the pyc file inside ``__pycache__`` will be ignored. This eliminates the problem of accidental stale pyc file imports. For backward compatibility, Python will still support pyc-only distributions, however it will only do so when the pyc file lives in the directory where the py file *would* have been, i.e. not in the -`__pycache__` directory. pyc file outside of `__pycache__` will only +``__pycache__`` directory. pyc file outside of ``__pycache__`` will only be imported if the py source file is missing. -Tools such as `py_compile` [15]_ and `compileall` [16]_ will be +Tools such as ``py_compile`` [15]_ and ``compileall`` [16]_ will be extended to create :pep:`3147` formatted layouts automatically, but will have an option to create pyc-only distribution layouts. @@ -146,8 +146,8 @@ Examples What would this look like in practice? -Let's say we have a Python package named `alpha` which contains a -sub-package name `beta`. The source directory layout before byte +Let's say we have a Python package named ``alpha`` which contains a +sub-package name ``beta``. The source directory layout before byte compilation might look like this:: alpha/ @@ -218,7 +218,7 @@ As you can see, as long as the Python version identifier string is unique, any number of pyc files can co-exist. These identifier strings are described in more detail below. -A nice property of this layout is that the `__pycache__` directories +A nice property of this layout is that the ``__pycache__`` directories can generally be ignored, such that a normal directory listing would show something like this:: @@ -239,50 +239,50 @@ This is much less cluttered than even today's Python. Python behavior =============== -When Python searches for a module to import (say `foo`), it may find +When Python searches for a module to import (say ``foo``), it may find one of several situations. As per current Python rules, the term "matching pyc" means that the magic number matches the current interpreter's magic number, and the source file's timestamp matches -the timestamp in the `pyc` file exactly. +the timestamp in the ``pyc`` file exactly. Case 0: The steady state ------------------------ -When Python is asked to import module `foo`, it searches for a -`foo.py` file (or `foo` package, but that's not important for this -discussion) along its `sys.path`. If found, Python looks to see if -there is a matching `__pycache__/foo.<magic>.pyc` file, and if so, -that `pyc` file is loaded. +When Python is asked to import module ``foo``, it searches for a +``foo.py`` file (or ``foo`` package, but that's not important for this +discussion) along its ``sys.path``. If found, Python looks to see if +there is a matching ``__pycache__/foo.<magic>.pyc`` file, and if so, +that ``pyc`` file is loaded. Case 1: The first import ------------------------ -When Python locates the `foo.py`, if the `__pycache__/foo.<magic>.pyc` +When Python locates the ``foo.py``, if the ``__pycache__/foo.<magic>.pyc`` file is missing, Python will create it, also creating the -`__pycache__` directory if necessary. Python will parse and byte -compile the `foo.py` file and save the byte code in -`__pycache__/foo.<magic>.pyc`. +``__pycache__`` directory if necessary. Python will parse and byte +compile the ``foo.py`` file and save the byte code in +``__pycache__/foo.<magic>.pyc``. Case 2: The second import ------------------------- -When Python is asked to import module `foo` a second time (in a -different process of course), it will again search for the `foo.py` -file along its `sys.path`. When Python locates the `foo.py` file, it -looks for a matching `__pycache__/foo.<magic>.pyc` and finding this, +When Python is asked to import module ``foo`` a second time (in a +different process of course), it will again search for the ``foo.py`` +file along its ``sys.path``. When Python locates the ``foo.py`` file, it +looks for a matching ``__pycache__/foo.<magic>.pyc`` and finding this, it reads the byte code and continues as usual. Case 3: __pycache__/foo.<magic>.pyc with no source --------------------------------------------------- -It's possible that the `foo.py` file somehow got removed, while +It's possible that the ``foo.py`` file somehow got removed, while leaving the cached pyc file still on the file system. If the -`__pycache__/foo.<magic>.pyc` file exists, but the `foo.py` file used -to create it does not, Python will raise an `ImportError` when asked +``__pycache__/foo.<magic>.pyc`` file exists, but the ``foo.py`` file used +to create it does not, Python will raise an ``ImportError`` when asked to import foo. In other words, Python will not import a pyc file from the cache directory unless the source file exists. @@ -291,8 +291,8 @@ Case 4: legacy pyc files and source-less imports ------------------------------------------------ Python will ignore all legacy pyc files when a source file exists next -to it. In other words, if a `foo.pyc` file exists next to the -`foo.py` file, the pyc file will be ignored in all cases +to it. In other words, if a ``foo.pyc`` file exists next to the +``foo.py`` file, the pyc file will be ignored in all cases In order to continue to support source-less distributions though, if the source file is missing, Python will import a lone pyc file if it @@ -302,9 +302,9 @@ lives where the source file would have been. Case 5: read-only file systems ------------------------------ -When the source lives on a read-only file system, or the `__pycache__` +When the source lives on a read-only file system, or the ``__pycache__`` directory or pyc file cannot otherwise be written, all the same rules -apply. This is also the case when `__pycache__` happens to be written +apply. This is also the case when ``__pycache__`` happens to be written with permissions which do not allow for writing containing pyc files. @@ -324,9 +324,9 @@ Alternative Python implementations Alternative Python implementations such as Jython [11]_, IronPython [12]_, PyPy [13]_, Pynie [14]_, and Unladen Swallow can also use the -`__pycache__` directory to store whatever compilation artifacts make +``__pycache__`` directory to store whatever compilation artifacts make sense for their platforms. For example, Jython could store the class -file for the module in `__pycache__/foo.jython-32.class`. +file for the module in ``__pycache__/foo.jython-32.class``. Implementation strategy @@ -336,7 +336,7 @@ This feature is targeted for Python 3.2, solving the problem for those and all future versions. It may be back-ported to Python 2.7. Vendors are free to backport the changes to earlier distributions as they see fit. For backports of this feature to Python 2, when the -`-U` flag is used, a file such as `foo.cpython-27u.pyc` can be +``-U`` flag is used, a file such as ``foo.cpython-27u.pyc`` can be written. @@ -360,9 +360,9 @@ The easiest way to detect whether your version of Python provides PEP __file__ --------- -In Python 3, when you import a module, its `__file__` attribute points -to its source `py` file (in Python 2, it points to the `pyc` file). A -package's `__file__` points to the `py` file for its `__init__.py`. +In Python 3, when you import a module, its ``__file__`` attribute points +to its source ``py`` file (in Python 2, it points to the ``pyc`` file). A +package's ``__file__`` points to the ``py`` file for its ``__init__.py``. E.g.:: >>> import foo @@ -373,64 +373,64 @@ E.g.:: >>> baz.__file__ 'baz/__init__.py' -Nothing in this PEP would change the semantics of `__file__`. +Nothing in this PEP would change the semantics of ``__file__``. -This PEP proposes the addition of an `__cached__` attribute to -modules, which will always point to the actual `pyc` file that was +This PEP proposes the addition of an ``__cached__`` attribute to +modules, which will always point to the actual ``pyc`` file that was read or written. When the environment variable -`$PYTHONDONTWRITEBYTECODE` is set, or the `-B` option is given, or if -the source lives on a read-only filesystem, then the `__cached__` -attribute will point to the location that the `pyc` file *would* have +``$PYTHONDONTWRITEBYTECODE`` is set, or the ``-B`` option is given, or if +the source lives on a read-only filesystem, then the ``__cached__`` +attribute will point to the location that the ``pyc`` file *would* have been written to if it didn't exist. This location of course includes -the `__pycache__` subdirectory in its path. +the ``__pycache__`` subdirectory in its path. -For alternative Python implementations which do not support `pyc` -files, the `__cached__` attribute may point to whatever information -makes sense. E.g. on Jython, this might be the `.class` file for the -module: `__pycache__/foo.jython-32.class`. Some implementations may +For alternative Python implementations which do not support ``pyc`` +files, the ``__cached__`` attribute may point to whatever information +makes sense. E.g. on Jython, this might be the ``.class`` file for the +module: ``__pycache__/foo.jython-32.class``. Some implementations may use multiple compiled files to create the module, in which case -`__cached__` may be a tuple. The exact contents of `__cached__` are +``__cached__`` may be a tuple. The exact contents of ``__cached__`` are Python implementation specific. It is recommended that when nothing sensible can be calculated, -implementations should set the `__cached__` attribute to `None`. +implementations should set the ``__cached__`` attribute to ``None``. py_compile and compileall ------------------------- -Python comes with two modules, `py_compile` [15]_ and `compileall` +Python comes with two modules, ``py_compile`` [15]_ and ``compileall`` [16]_ which support compiling Python modules external to the built-in -import machinery. `py_compile` in particular has intimate knowledge +import machinery. ``py_compile`` in particular has intimate knowledge of byte compilation, so these will be updated to understand the new -layout. The `-b` flag is added to `compileall` for writing legacy -`.pyc` byte-compiled file path names. +layout. The ``-b`` flag is added to ``compileall`` for writing legacy +``.pyc`` byte-compiled file path names. bdist_wininst and the Windows installer --------------------------------------- These tools also compile modules explicitly on installation. If they -do not use `py_compile` and `compileall`, then they would also have to +do not use ``py_compile`` and ``compileall``, then they would also have to be modified to understand the new layout. File extension checks --------------------- -There exists some code which checks for files ending in `.pyc` and -simply chops off the last character to find the matching `.py` file. +There exists some code which checks for files ending in ``.pyc`` and +simply chops off the last character to find the matching ``.py`` file. This code will obviously fail once this PEP is implemented. -To support this use case, we'll add two new methods to the `imp` +To support this use case, we'll add two new methods to the ``imp`` package [17]_: -* `imp.cache_from_source(py_path)` -> `pyc_path` -* `imp.source_from_cache(pyc_path)` -> `py_path` +* ``imp.cache_from_source(py_path)`` -> ``pyc_path`` +* ``imp.source_from_cache(pyc_path)`` -> ``py_path`` Alternative implementations are free to override these functions to return reasonable values based on their own support for this PEP. -These methods are allowed to return `None` when the implementation (or +These methods are allowed to return ``None`` when the implementation (or :pep:`302` loader in effect) for whatever reason cannot calculate the appropriate file name. They should not raise exceptions. @@ -443,15 +443,15 @@ possible to backport this PEP. However, in Python 3.2 (and possibly 2.7), this behavior will be turned on by default, and in fact, it will replace the old behavior. Backports will need to support the old layout by default. We suggest supporting :pep:`3147` through the use of -an environment variable called `$PYTHONENABLECACHEDIR` or the command -line switch `-Xenablecachedir` to enable the feature. +an environment variable called ``$PYTHONENABLECACHEDIR`` or the command +line switch ``-Xenablecachedir`` to enable the feature. Makefiles and other dependency tools ------------------------------------ -Makefiles and other tools which calculate dependencies on `.pyc` files -(e.g. to byte-compile the source if the `.pyc` is missing) will have +Makefiles and other tools which calculate dependencies on ``.pyc`` files +(e.g. to byte-compile the source if the ``.pyc`` is missing) will have to be updated to check the new paths. @@ -465,7 +465,7 @@ were considered and rejected during the PEP's development. Hexadecimal magic tags ---------------------- -pyc files inside of the `__pycache__` directories contain a magic tag +pyc files inside of the ``__pycache__`` directories contain a magic tag in their file names. These are mnemonic tags for the actual magic numbers used by the importer. We could have used the hexadecimal representation [10]_ of the binary magic number as a unique @@ -485,15 +485,15 @@ PEP 304 There is some overlap between the goals of this PEP and :pep:`304`, which has been withdrawn. However :pep:`304` would allow a user to -create a shadow file system hierarchy in which to store `pyc` files. -This concept of a shadow hierarchy for `pyc` files could be used to +create a shadow file system hierarchy in which to store ``pyc`` files. +This concept of a shadow hierarchy for ``pyc`` files could be used to satisfy the aims of this PEP. Although the :pep:`304` does not indicate why it was withdrawn, shadow directories have a number of problems. -The location of the shadow `pyc` files would not be easily discovered +The location of the shadow ``pyc`` files would not be easily discovered and would depend on the proper and consistent use of the -`$PYTHONBYTECODE` environment variable both by the system and by end +``$PYTHONBYTECODE`` environment variable both by the system and by end users. There are also global implications, meaning that while the -system might want to shadow `pyc` files, users might not want to, but +system might want to shadow ``pyc`` files, users might not want to, but the PEP defines only an all-or-nothing approach. As an example of the problem, a common (though fragile) Python idiom @@ -503,9 +503,9 @@ for locating data files is to do something like this:: import foo.bar data_file = join(dirname(foo.bar.__file__), 'my.dat') -This would be problematic since `foo.bar.__file__` will give the -location of the `pyc` file in the shadow directory, and it may not be -possible to find the `my.dat` file relative to the source directory +This would be problematic since ``foo.bar.__file__`` will give the +location of the ``pyc`` file in the shadow directory, and it may not be +possible to find the ``my.dat`` file relative to the source directory from there. @@ -513,11 +513,11 @@ Fat byte compilation files -------------------------- An earlier version of this PEP described "fat" Python byte code files. -These files would contain the equivalent of multiple `pyc` files in a -single `pyf` file, with a lookup table keyed off the appropriate magic +These files would contain the equivalent of multiple ``pyc`` files in a +single ``pyf`` file, with a lookup table keyed off the appropriate magic number. This was an extensible file format so that the first 5 parallel Python implementations could be supported fairly efficiently, -but with extension lookup tables available to scale `pyf` byte code +but with extension lookup tables available to scale ``pyf`` byte code objects as large as necessary. The fat byte compilation files were fairly complex, and inherently @@ -541,7 +541,7 @@ tools that are dependent on the file extension. .pyc ---- -A proposal was floated to call the `__pycache__` directory `.pyc` or +A proposal was floated to call the ``__pycache__`` directory ``.pyc`` or some other dot-file name. This would have the effect on \*nix systems of hiding the directory. There are many reasons why this was rejected by the BDFL [20]_ including the fact that dot-files are only @@ -616,7 +616,7 @@ ACKNOWLEDGMENTS Barry Warsaw's original idea was for fat Python byte code files. Martin von Loewis reviewed an early draft of the PEP and suggested the -simplification to store traditional `pyc` and `pyo` files in a +simplification to store traditional ``pyc`` and ``pyo`` files in a directory. Many other people reviewed early versions of this PEP and provided useful feedback including but not limited to: diff --git a/pep-3148.txt b/peps/pep-3148.rst similarity index 72% rename from pep-3148.txt rename to peps/pep-3148.rst index 70c044f12..61c62616c 100644 --- a/pep-3148.txt +++ b/peps/pep-3148.rst @@ -57,21 +57,21 @@ in that module, which work across thread and process boundaries. Interface --------- -The proposed package provides two core classes: `Executor` and -`Future`. An `Executor` receives asynchronous work requests (in terms -of a callable and its arguments) and returns a `Future` to represent +The proposed package provides two core classes: ``Executor`` and +``Future``. An ``Executor`` receives asynchronous work requests (in terms +of a callable and its arguments) and returns a ``Future`` to represent the execution of that work request. Executor '''''''' -`Executor` is an abstract class that provides methods to execute calls +``Executor`` is an abstract class that provides methods to execute calls asynchronously. ``submit(fn, *args, **kwargs)`` Schedules the callable to be executed as ``fn(*args, **kwargs)`` - and returns a `Future` instance representing the execution of the + and returns a ``Future`` instance representing the execution of the callable. This is an abstract method and must be implemented by Executor @@ -81,10 +81,10 @@ asynchronously. Equivalent to ``map(func, *iterables)`` but func is executed asynchronously and several calls to func may be made concurrently. - The returned iterator raises a `TimeoutError` if `__next__()` is + The returned iterator raises a ``TimeoutError`` if ``__next__()`` is called and the result isn't available after *timeout* seconds from - the original call to `map()`. If *timeout* is not specified or - `None` then there is no limit to the wait time. If a call raises + the original call to ``map()``. If *timeout* is not specified or + ``None`` then there is no limit to the wait time. If a call raises an exception then that exception will be raised when its value is retrieved from the iterator. @@ -92,12 +92,12 @@ asynchronously. Signal the executor that it should free any resources that it is using when the currently pending futures are done executing. - Calls to `Executor.submit` and `Executor.map` and made after - shutdown will raise `RuntimeError`. + Calls to ``Executor.submit`` and ``Executor.map`` and made after + shutdown will raise ``RuntimeError``. - If wait is `True` then this method will not return until all the + If wait is ``True`` then this method will not return until all the pending futures are done executing and the resources associated - with the executor have been freed. If wait is `False` then this + with the executor have been freed. If wait is ``False`` then this method will return immediately and the resources associated with the executor will be freed when all pending futures are done executing. Regardless of the value of wait, the entire Python @@ -107,21 +107,21 @@ asynchronously. | ``__enter__()`` | ``__exit__(exc_type, exc_val, exc_tb)`` - When using an executor as a context manager, `__exit__` will call + When using an executor as a context manager, ``__exit__`` will call ``Executor.shutdown(wait=True)``. ProcessPoolExecutor ''''''''''''''''''' -The `ProcessPoolExecutor` class is an `Executor` subclass that uses a +The ``ProcessPoolExecutor`` class is an ``Executor`` subclass that uses a pool of processes to execute calls asynchronously. The callable -objects and arguments passed to `ProcessPoolExecutor.submit` must be +objects and arguments passed to ``ProcessPoolExecutor.submit`` must be pickleable according to the same limitations as the multiprocessing module. -Calling `Executor` or `Future` methods from within a callable -submitted to a `ProcessPoolExecutor` will result in deadlock. +Calling ``Executor`` or ``Future`` methods from within a callable +submitted to a ``ProcessPoolExecutor`` will result in deadlock. ``__init__(max_workers)`` @@ -132,11 +132,11 @@ submitted to a `ProcessPoolExecutor` will result in deadlock. ThreadPoolExecutor '''''''''''''''''' -The `ThreadPoolExecutor` class is an `Executor` subclass that uses a +The ``ThreadPoolExecutor`` class is an ``Executor`` subclass that uses a pool of threads to execute calls asynchronously. -Deadlock can occur when the callable associated with a `Future` waits -on the results of another `Future`. For example:: +Deadlock can occur when the callable associated with a ``Future`` waits +on the results of another ``Future``. For example:: import time def wait_on_b(): @@ -173,28 +173,28 @@ And:: Future Objects '''''''''''''' -The `Future` class encapsulates the asynchronous execution of a -callable. `Future` instances are returned by `Executor.submit`. +The ``Future`` class encapsulates the asynchronous execution of a +callable. ``Future`` instances are returned by ``Executor.submit``. ``cancel()`` Attempt to cancel the call. If the call is currently being executed then it cannot be cancelled and the method will return - `False`, otherwise the call will be cancelled and the method will - return `True`. + ``False``, otherwise the call will be cancelled and the method will + return ``True``. ``cancelled()`` - Return `True` if the call was successfully cancelled. + Return ``True`` if the call was successfully cancelled. ``running()`` - Return `True` if the call is currently being executed and cannot + Return ``True`` if the call is currently being executed and cannot be cancelled. ``done()`` - Return `True` if the call was successfully cancelled or finished + Return ``True`` if the call was successfully cancelled or finished running. ``result(timeout=None)`` @@ -202,10 +202,10 @@ callable. `Future` instances are returned by `Executor.submit`. Return the value returned by the call. If the call hasn't yet completed then this method will wait up to *timeout* seconds. If the call hasn't completed in *timeout* seconds then a - `TimeoutError` will be raised. If *timeout* is not specified or - `None` then there is no limit to the wait time. + ``TimeoutError`` will be raised. If *timeout* is not specified or + ``None`` then there is no limit to the wait time. - If the future is cancelled before completing then `CancelledError` + If the future is cancelled before completing then ``CancelledError`` will be raised. If the call raised then this method will raise the same exception. @@ -215,13 +215,13 @@ callable. `Future` instances are returned by `Executor.submit`. Return the exception raised by the call. If the call hasn't yet completed then this method will wait up to *timeout* seconds. If the call hasn't completed in *timeout* seconds then a - `TimeoutError` will be raised. If *timeout* is not specified or + ``TimeoutError`` will be raised. If *timeout* is not specified or ``None`` then there is no limit to the wait time. - If the future is cancelled before completing then `CancelledError` + If the future is cancelled before completing then ``CancelledError`` will be raised. - If the call completed without raising then `None` is returned. + If the call completed without raising then ``None`` is returned. ``add_done_callback(fn)`` @@ -231,9 +231,9 @@ callable. `Future` instances are returned by `Executor.submit`. Added callables are called in the order that they were added and are always called in a thread belonging to the process that added - them. If the callable raises an `Exception` then it will be + them. If the callable raises an ``Exception`` then it will be logged and ignored. If the callable raises another - `BaseException` then behavior is not defined. + ``BaseException`` then behavior is not defined. If the future has already completed or been cancelled then *fn* will be called immediately. @@ -241,43 +241,43 @@ callable. `Future` instances are returned by `Executor.submit`. Internal Future Methods ^^^^^^^^^^^^^^^^^^^^^^^ -The following `Future` methods are meant for use in unit tests and -`Executor` implementations. +The following ``Future`` methods are meant for use in unit tests and +``Executor`` implementations. ``set_running_or_notify_cancel()`` - Should be called by `Executor` implementations before executing - the work associated with the `Future`. + Should be called by ``Executor`` implementations before executing + the work associated with the ``Future``. - If the method returns `False` then the `Future` was cancelled, - i.e. `Future.cancel` was called and returned `True`. Any threads - waiting on the `Future` completing (i.e. through `as_completed()` - or `wait()`) will be woken up. + If the method returns ``False`` then the ``Future`` was cancelled, + i.e. ``Future.cancel`` was called and returned ``True``. Any threads + waiting on the ``Future`` completing (i.e. through ``as_completed()`` + or ``wait()``) will be woken up. - If the method returns `True` then the `Future` was not cancelled + If the method returns ``True`` then the ``Future`` was not cancelled and has been put in the running state, i.e. calls to - `Future.running()` will return `True`. + ``Future.running()`` will return ``True``. This method can only be called once and cannot be called after - `Future.set_result()` or `Future.set_exception()` have been + ``Future.set_result()`` or ``Future.set_exception()`` have been called. ``set_result(result)`` - Sets the result of the work associated with the `Future`. + Sets the result of the work associated with the ``Future``. ``set_exception(exception)`` - Sets the result of the work associated with the `Future` to the - given `Exception`. + Sets the result of the work associated with the ``Future`` to the + given ``Exception``. Module Functions '''''''''''''''' ``wait(fs, timeout=None, return_when=ALL_COMPLETED)`` - Wait for the `Future` instances (possibly created by different - `Executor` instances) given by *fs* to complete. Returns a named + Wait for the ``Future`` instances (possibly created by different + ``Executor`` instances) given by *fs* to complete. Returns a named 2-tuple of sets. The first set, named "done", contains the futures that completed (finished or were cancelled) before the wait completed. The second set, named "not_done", contains @@ -293,27 +293,27 @@ Module Functions ============================= ================================================== Constant Description ============================= ================================================== - `FIRST_COMPLETED` The method will return when any future finishes or + ``FIRST_COMPLETED`` The method will return when any future finishes or is cancelled. - `FIRST_EXCEPTION` The method will return when any future finishes by + ``FIRST_EXCEPTION`` The method will return when any future finishes by raising an exception. If not future raises an exception then it is equivalent to ALL_COMPLETED. - `ALL_COMPLETED` The method will return when all calls finish. + ``ALL_COMPLETED`` The method will return when all calls finish. ============================= ================================================== ``as_completed(fs, timeout=None)`` - Returns an iterator over the `Future` instances given by *fs* that + Returns an iterator over the ``Future`` instances given by *fs* that yields futures as they complete (finished or were cancelled). Any - futures that completed before `as_completed()` was called will be - yielded first. The returned iterator raises a `TimeoutError` if - `__next__()` is called and the result isn't available after - *timeout* seconds from the original call to `as_completed()`. If - *timeout* is not specified or `None` then there is no limit to the + futures that completed before ``as_completed()`` was called will be + yielded first. The returned iterator raises a ``TimeoutError`` if + ``__next__()`` is called and the result isn't available after + *timeout* seconds from the original call to ``as_completed()``. If + *timeout* is not specified or ``None`` then there is no limit to the wait time. - The `Future` instances can have been created by different - `Executor` instances. + The ``Future`` instances can have been created by different + ``Executor`` instances. Check Prime Example ------------------- @@ -408,7 +408,7 @@ list [3]_. The proposed design is explicit, i.e. it requires that clients be aware that they are consuming Futures. It would be possible to design -a module that would return proxy objects (in the style of `weakref`) +a module that would return proxy objects (in the style of ``weakref``) that could be used transparently. It is possible to build a proxy implementation on top of the proposed explicit mechanism. @@ -425,13 +425,13 @@ the API has been discussed in some detail on stdlib-sig [6]_. The proposed design was discussed on the Python-Dev mailing list [7]_. Following those discussions, the following changes were made: -* The `Executor` class was made into an abstract base class -* The `Future.remove_done_callback` method was removed due to a lack +* The ``Executor`` class was made into an abstract base class +* The ``Future.remove_done_callback`` method was removed due to a lack of convincing use cases -* The `Future.add_done_callback` method was modified to allow the +* The ``Future.add_done_callback`` method was modified to allow the same callable to be added many times -* The `Future` class's mutation methods were better documented to - indicate that they are private to the `Executor` that created them +* The ``Future`` class's mutation methods were better documented to + indicate that they are private to the ``Executor`` that created them ======================== Reference Implementation @@ -445,7 +445,7 @@ References ========== .. [1] - `java.util.concurrent` package documentation + ``java.util.concurrent`` package documentation http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/package-summary.html .. [2] @@ -453,15 +453,15 @@ References http://code.activestate.com/recipes/84317/ .. [3] - `Python-3000` thread, "mechanism for handling asynchronous concurrency" + ``Python-3000`` thread, "mechanism for handling asynchronous concurrency" https://mail.python.org/pipermail/python-3000/2006-April/000960.html .. [4] - `Python 3000` thread, "Futures in Python 3000 (was Re: mechanism for handling asynchronous concurrency)" + ``Python 3000`` thread, "Futures in Python 3000 (was Re: mechanism for handling asynchronous concurrency)" https://mail.python.org/pipermail/python-3000/2006-April/000970.html .. [5] - A discussion of `stream`, a similar concept proposed by Anh Hai Trinh + A discussion of ``stream``, a similar concept proposed by Anh Hai Trinh http://www.mail-archive.com/stdlib-sig@python.org/msg00480.html .. [6] @@ -473,7 +473,7 @@ References https://mail.python.org/pipermail/python-dev/2010-March/098169.html .. [8] - Reference `futures` implementation + Reference ``futures`` implementation http://code.google.com/p/pythonfutures/source/browse/#svn/branches/feedback ========= diff --git a/pep-3149.txt b/peps/pep-3149.rst similarity index 92% rename from pep-3149.txt rename to peps/pep-3149.rst index a7d64707c..9d0f3a2ad 100644 --- a/pep-3149.txt +++ b/peps/pep-3149.rst @@ -30,8 +30,8 @@ Background :pep:`3147` defined the file system layout for a pure-Python package, where multiple versions of Python are available on the system. For -example, where the `alpha` package containing source modules `one.py` -and `two.py` exist on a system with Python 3.2 and 3.3, the post-byte +example, where the ``alpha`` package containing source modules ``one.py`` +and ``two.py`` exist on a system with Python 3.2 and 3.3, the post-byte compilation file system layout would be:: alpha/ @@ -69,18 +69,18 @@ with Python 2.6 being the default. In order to share as much as possible between the available Python versions, these distributions install third party package modules -(``.pyc`` and ``.so`` files) into `/usr/share/pyshared` and symlink to -them from `/usr/lib/pythonX.Y/dist-packages`. The symlinks exist -because in a pre-:pep:`3147` world (i.e < Python 3.2), the `.pyc` files +(``.pyc`` and ``.so`` files) into ``/usr/share/pyshared`` and symlink to +them from ``/usr/lib/pythonX.Y/dist-packages``. The symlinks exist +because in a pre-:pep:`3147` world (i.e < Python 3.2), the ``.pyc`` files resulting from byte compilation by the various installed Pythons will name collide with each other. For Python versions >= 3.2, all -pure-Python packages can be shared, because the `.pyc` files will no +pure-Python packages can be shared, because the ``.pyc`` files will no longer cause file system naming conflicts. Eliminating these symlinks makes for a simpler, more robust Python distribution. A similar situation arises with shared library extensions. Because -extension modules are typically named `foo.so` for a `foo` extension -module, these would also name collide if `foo` was provided for more +extension modules are typically named ``foo.so`` for a ``foo`` extension +module, these would also name collide if ``foo`` was provided for more than one Python version. In addition, because different configuration/compilation options for @@ -93,7 +93,7 @@ module files. PyPy [5]_ can also benefit from this PEP, allowing it to avoid name collisions in extension modules built for its API, but with a -different `.so` tag. +different ``.so`` tag. Proposal @@ -139,7 +139,7 @@ Note that ``$SOABI`` contains just the tag, while ``$EXT_SUFFIX`` includes the platform extension for shared library files, and is the exact suffix added to the extension module name. -For an arbitrary package `foo`, you might see these files when the +For an arbitrary package ``foo``, you might see these files when the distribution package was installed:: /usr/lib/python/foo.cpython-32m.so @@ -159,7 +159,7 @@ This shared library tag would be used globally for all distutils-based extension modules, regardless of where on the file system they are built. Extension modules built by means other than distutils would either have to calculate the tag manually, or fallback to the -non-tagged `.so` file name. +non-tagged ``.so`` file name. Proven approach @@ -170,7 +170,7 @@ and Ubuntu system where different extensions are used for debug builds of Python and extension modules. Debug builds on Windows also already use a different file extension for dynamic libraries, and in fact encoded (in a different way than proposed in this PEP) the Python -major and minor version in the `.dll` file name. +major and minor version in the ``.dll`` file name. Windows @@ -207,7 +207,7 @@ its name. The prefix ``abi`` is reserved for Python's use. Thus, an initial implementation of :pep:`384`, when Python is configured with the default set of flags, would search for the following file -names when extension module `foo` is imported (in this order):: +names when extension module ``foo`` is imported (in this order):: foo.cpython-XYm.so foo.abi3.so @@ -271,7 +271,7 @@ If a pure-Python package is shared in one version, should it suddenly be not-shared if the next release adds an extension module for speed? Also, even though all extension shared libraries will be compiled and distributed once for every supported Python, there's a big difference -between duplicating the `.so` files and duplicating all `.py` files. +between duplicating the ``.so`` files and duplicating all ``.py`` files. The extra size increases the download time for such packages, and more immediately, increases the space pressures on already constrained distribution CD-ROMs. diff --git a/pep-3150.txt b/peps/pep-3150.rst similarity index 100% rename from pep-3150.txt rename to peps/pep-3150.rst diff --git a/pep-3151.txt b/peps/pep-3151.rst similarity index 99% rename from pep-3151.txt rename to peps/pep-3151.rst index 21af2cd5f..e1ba710e2 100644 --- a/pep-3151.txt +++ b/peps/pep-3151.rst @@ -695,7 +695,7 @@ Handling of PYTHONSTARTUP raises IOError (but the error gets discarded):: IOError: [Errno 2] No such file or directory: 'foox' ``PyObject_Print()`` raises IOError when ferror() signals an error on the -`FILE *` parameter (which, in the source tree, is always either stdout or +``FILE *`` parameter (which, in the source tree, is always either stdout or stderr). Unicode encoding and decoding using the ``mbcs`` encoding can raise diff --git a/pep-3152.txt b/peps/pep-3152.rst similarity index 100% rename from pep-3152.txt rename to peps/pep-3152.rst diff --git a/pep-3153.txt b/peps/pep-3153.rst similarity index 100% rename from pep-3153.txt rename to peps/pep-3153.rst diff --git a/pep-3154.txt b/peps/pep-3154.rst similarity index 100% rename from pep-3154.txt rename to peps/pep-3154.rst diff --git a/pep-3155.txt b/peps/pep-3155.rst similarity index 100% rename from pep-3155.txt rename to peps/pep-3155.rst diff --git a/pep-3156.txt b/peps/pep-3156.rst similarity index 100% rename from pep-3156.txt rename to peps/pep-3156.rst diff --git a/pep-3333.txt b/peps/pep-3333.rst similarity index 99% rename from pep-3333.txt rename to peps/pep-3333.rst index d6d3dfa74..4ec70d84f 100644 --- a/pep-3333.txt +++ b/peps/pep-3333.rst @@ -1,8 +1,6 @@ PEP: 3333 Title: Python Web Server Gateway Interface v1.0.1 -Version: $Revision$ -Last-Modified: $Date$ -Author: P.J. Eby <pje@telecommunity.com> +Author: Phillip J. Eby <pje@telecommunity.com> Discussions-To: web-sig@python.org Status: Final Type: Informational @@ -676,9 +674,9 @@ Variable Value ``wsgi.input`` An input stream (file-like object) from which the HTTP request body bytes can be read. (The server or gateway may perform reads on-demand as - requested by the application, or it may pre- - read the client's request body and buffer it - in-memory or on disk, or use any other + requested by the application, or it may + pre-read the client's request body and buffer + it in-memory or on disk, or use any other technique for providing such an input stream, according to its preference.) @@ -1457,8 +1455,8 @@ use only language features available in the target version, use Optional Platform-Specific File Handling ---------------------------------------- -Some operating environments provide special high-performance file- -transmission facilities, such as the Unix ``sendfile()`` call. +Some operating environments provide special high-performance +file-transmission facilities, such as the Unix ``sendfile()`` call. Servers and gateways **may** expose this functionality via an optional ``wsgi.file_wrapper`` key in the ``environ``. An application **may** use this "file wrapper" to convert a file or file-like object @@ -1780,13 +1778,3 @@ Copyright ========= This document has been placed in the public domain. - - - -.. - Local Variables: - mode: indented-text - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - End: diff --git a/pep-8000.rst b/peps/pep-8000.rst similarity index 100% rename from pep-8000.rst rename to peps/pep-8000.rst diff --git a/pep-8001.rst b/peps/pep-8001.rst similarity index 100% rename from pep-8001.rst rename to peps/pep-8001.rst diff --git a/pep-8002.rst b/peps/pep-8002.rst similarity index 100% rename from pep-8002.rst rename to peps/pep-8002.rst diff --git a/pep-8010.rst b/peps/pep-8010.rst similarity index 100% rename from pep-8010.rst rename to peps/pep-8010.rst diff --git a/pep-8011.rst b/peps/pep-8011.rst similarity index 100% rename from pep-8011.rst rename to peps/pep-8011.rst diff --git a/pep-8012.rst b/peps/pep-8012.rst similarity index 100% rename from pep-8012.rst rename to peps/pep-8012.rst diff --git a/pep-8013.rst b/peps/pep-8013.rst similarity index 100% rename from pep-8013.rst rename to peps/pep-8013.rst diff --git a/pep-8014.rst b/peps/pep-8014.rst similarity index 100% rename from pep-8014.rst rename to peps/pep-8014.rst diff --git a/pep-8015.rst b/peps/pep-8015.rst similarity index 100% rename from pep-8015.rst rename to peps/pep-8015.rst diff --git a/pep-8016.rst b/peps/pep-8016.rst similarity index 100% rename from pep-8016.rst rename to peps/pep-8016.rst diff --git a/pep-8100.rst b/peps/pep-8100.rst similarity index 100% rename from pep-8100.rst rename to peps/pep-8100.rst diff --git a/pep-8101.rst b/peps/pep-8101.rst similarity index 100% rename from pep-8101.rst rename to peps/pep-8101.rst diff --git a/pep-8102.rst b/peps/pep-8102.rst similarity index 100% rename from pep-8102.rst rename to peps/pep-8102.rst diff --git a/pep-8103.rst b/peps/pep-8103.rst similarity index 100% rename from pep-8103.rst rename to peps/pep-8103.rst diff --git a/pep-8104.rst b/peps/pep-8104.rst similarity index 100% rename from pep-8104.rst rename to peps/pep-8104.rst diff --git a/pytest.ini b/pytest.ini index 10f7034ed..10404cc0b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,13 +1,16 @@ [pytest] -addopts = -r a --strict-config --strict-markers --import-mode=importlib --cov pep_sphinx_extensions --cov-report html --cov-report xml +# https://docs.pytest.org/en/7.3.x/reference/reference.html#command-line-flags +addopts = + -r a + --strict-config + --strict-markers + --import-mode=importlib + --cov check_peps --cov pep_sphinx_extensions + --cov-report html --cov-report xml empty_parameter_set_mark = fail_at_collect filterwarnings = error - # Awaiting release of https://github.com/python-babel/babel/issues/873 - # in Babel 2.11, due 2022-08-01 https://github.com/python-babel/babel/milestone/6?closed=1 - ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning - # Awaiting https://github.com/sphinx-doc/sphinx/issues/10440 - ignore:'imghdr' is deprecated and slated for removal in Python 3.13:DeprecationWarning minversion = 6.0 testpaths = pep_sphinx_extensions xfail_strict = True +disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True diff --git a/tox.ini b/tox.ini index cc88b26bd..ac54601b6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,14 @@ [tox] -envlist = +requires = + tox>=4.2 +env_list = py{312, 311, 310, 39} -skipsdist = true +no_package = true [testenv] -passenv = - FORCE_COLOR deps = -rrequirements.txt +pass_env = + FORCE_COLOR commands = python -bb -X dev -W error -m pytest {posargs}