├── .gitignore ├── .travis.yml ├── CHANGES.rst ├── CLA.rst ├── CONTRIBUTORS.rst ├── INSTALL.rst ├── Makefile ├── README.rst ├── bootstrap_jenkins.sh ├── covers ├── 802764156_859a8f6969_o.jpg ├── Cover.xcf ├── PhoneBack.pdf ├── PhoneFront.pdf ├── README.txt ├── ScreenBack.pdf ├── ScreenFront.pdf ├── TabletBack.pdf └── TabletFront.pdf ├── cronscript.sh ├── py3example ├── README.txt ├── __init__.py ├── _sorted.py ├── bites.py ├── compare.py ├── example.py └── tests.py ├── requirements.txt ├── runtests.sh └── source ├── 2to3.rst ├── _static ├── PortingtoPython3-1.0-front-small.png ├── PortingtoPython3-2nd1.0-front-small.png ├── agogo.css ├── by-nc-sa.pdf ├── dejavusansmono-bold-webfont.eot ├── dejavusansmono-bold-webfont.svg ├── dejavusansmono-bold-webfont.ttf ├── dejavusansmono-bold-webfont.woff ├── dejavusansmono-webfont.eot ├── dejavusansmono-webfont.svg ├── dejavusansmono-webfont.ttf ├── dejavusansmono-webfont.woff ├── fluxb___-webfont.eot ├── fluxb___-webfont.svg ├── fluxb___-webfont.ttf ├── fluxb___-webfont.woff ├── texgyreschola-bold-webfont.eot ├── texgyreschola-bold-webfont.svg ├── texgyreschola-bold-webfont.ttf ├── texgyreschola-bold-webfont.woff ├── texgyreschola-bolditalic-webfont.eot ├── texgyreschola-bolditalic-webfont.svg ├── texgyreschola-bolditalic-webfont.ttf ├── texgyreschola-bolditalic-webfont.woff ├── texgyreschola-italic-webfont.eot ├── texgyreschola-italic-webfont.svg ├── texgyreschola-italic-webfont.ttf ├── texgyreschola-italic-webfont.woff ├── texgyreschola-regular-webfont.eot ├── texgyreschola-regular-webfont.svg ├── texgyreschola-regular-webfont.ttf └── texgyreschola-regular-webfont.woff ├── _templates ├── layout.html ├── sphinx.sty └── sphinxcustom.cls ├── _tests ├── UTF-8.txt ├── bites.py ├── buffer25.txt ├── buffer26.txt ├── callable26.txt ├── callable30.txt ├── constantexample.py ├── constanttarget.py ├── csv26.txt ├── csv30.txt ├── csvutil.py ├── dict26.txt ├── division26.txt ├── doctest-checker.txt ├── doctest-unicode-fail.txt ├── doctest-unicode.txt ├── exception_syntax26.txt ├── exceptions25.txt ├── exceptions26.txt ├── exec25.txt ├── exec26.txt ├── exec30.txt ├── fix_constant.py ├── fix_indent.py ├── fix_name1.py ├── fix_name2.py ├── getitem26.txt ├── input26.txt ├── intern26.txt ├── inttypes26.txt ├── long25.txt ├── long26.txt ├── makebytes.py ├── makeunicode.py ├── map2.py ├── map25.txt ├── map26.txt ├── map30.txt ├── maybe_a.gif ├── mixin.py ├── pythons.txt ├── raise25.txt ├── raise30.txt ├── renames26.txt ├── repr25.txt ├── repr26.txt ├── round24.txt ├── round26.txt ├── round30.txt ├── shouldraise.py ├── test-1.1.txt ├── test-1.10.txt ├── test-1.11.txt ├── test-1.12.txt ├── test-1.2.txt ├── test-1.3.txt ├── test-1.4.txt ├── test-1.5.txt ├── test-1.6.txt ├── test-1.7.txt ├── test-1.8.txt ├── test-1.9.txt ├── test-2.1.txt ├── test-2.1 │ ├── docs │ │ ├── CHANGES.txt │ │ └── README.txt │ └── setup.py ├── test-2.2.txt ├── test-2.2 │ ├── docs │ │ ├── CHANGES.txt │ │ └── README.txt │ ├── py3example │ │ ├── __init__.py │ │ └── tests.py │ └── setup.py ├── test-2.3.txt ├── test-2.3 │ ├── docs │ │ ├── CHANGES.txt │ │ └── README.txt │ ├── py3example │ │ ├── __init__.py │ │ └── tests.py │ └── setup.py ├── test-2.4.txt ├── test-2.4 │ ├── docs │ │ ├── CHANGES.txt │ │ └── README.txt │ ├── py3example │ │ ├── __init__.py │ │ └── tests.py │ └── setup.py ├── test-2.5.txt ├── test-2.5 │ ├── docs │ │ ├── CHANGES.txt │ │ └── README.txt │ ├── py3example │ │ ├── __init__.py │ │ └── tests.py │ └── setup.py ├── test-2.6.txt ├── test-2.6 │ ├── setup.py │ ├── setup2.py │ ├── setup3.py │ ├── src2 │ │ └── __init__.py │ └── src3 │ │ └── __init__.py ├── test-3.1.txt ├── test-3.10.txt ├── test-3.11.txt ├── test-3.11.txt.2to3.bak ├── test-3.12.txt ├── test-3.13.txt ├── test-3.14.txt ├── test-3.15.txt ├── test-3.16.txt ├── test-3.17.txt ├── test-3.18.txt ├── test-3.19.txt ├── test-3.2.txt ├── test-3.20.txt ├── test-3.21.txt ├── test-3.22.txt ├── test-3.23.txt ├── test-3.24.txt ├── test-3.25.txt ├── test-3.26.txt ├── test-3.27.txt ├── test-3.28.txt ├── test-3.29.txt ├── test-3.3.txt ├── test-3.30.txt ├── test-3.31.txt ├── test-3.4.txt ├── test-3.5.txt ├── test-3.6.txt ├── test-3.7.txt ├── test-3.8.txt ├── test-3.9.txt ├── test-4.1.txt ├── test-4.10.txt ├── test-4.11.txt ├── test-4.12.txt ├── test-4.13.txt ├── test-4.14.txt ├── test-4.15.txt ├── test-4.16.txt ├── test-4.17.txt ├── test-4.18.txt ├── test-4.19.txt ├── test-4.2.txt ├── test-4.3.txt ├── test-4.4.txt ├── test-4.5.txt ├── test-4.6.txt ├── test-4.7.txt ├── test-4.8.txt ├── test-4.9.txt ├── test-5.1.txt ├── test-5.10.txt ├── test-5.11.txt ├── test-5.12.txt ├── test-5.13.txt ├── test-5.14.txt ├── test-5.15.txt ├── test-5.16.txt ├── test-5.17.txt ├── test-5.18.txt ├── test-5.2.txt ├── test-5.3.txt ├── test-5.4.txt ├── test-5.5.txt ├── test-5.6.txt ├── test-5.7.txt ├── test-5.8.txt ├── test-5.9.txt ├── test-6.1.txt ├── test-6.2.txt ├── test-7.1.txt ├── test-7.10.txt ├── test-7.11.txt ├── test-7.12.txt ├── test-7.2.txt ├── test-7.3.txt ├── test-7.4.txt ├── test-7.5.txt ├── test-7.6.txt ├── test-7.7.txt ├── test-7.8.txt ├── test-7.9.txt ├── test_base.py ├── test_fixers.py ├── tests23.py ├── tests24.py ├── tests26.py ├── tests27.py ├── tests31.py ├── tests32.py ├── travistest.py ├── unicodesort26.txt ├── unicodesort30.txt ├── unpacking25.txt ├── unpacking26.txt ├── upper24.txt └── upper26.txt ├── about.rst ├── bookindex.rst ├── cextensions.rst ├── conf.py ├── differences.rst ├── errata.rst ├── fixers.rst ├── foreword.rst ├── improving.rst ├── index.rst ├── intro.rst ├── noconv.rst ├── preparing.rst ├── problems.rst ├── stdlib.rst ├── strategies.rst └── toc.rst /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | .eggs 4 | eggs.csv 5 | *.bak 6 | *.2to3 7 | build 8 | python?? 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.6" 4 | - "2.7" 5 | - "3.2" 6 | 7 | install: pip install -r requirements.txt 8 | before_script: cd source/_tests 9 | script: python travistest.py 10 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | CHANGES 2 | ======= 3 | 4 | 1.1 (Unreleased) 5 | ---------------- 6 | 7 | * Clarified that long() typically is not needed. 8 | 9 | * Make a sentence in the foreword read better [Brett Cannon] 10 | 11 | * The foreword byline didn't show up in HTML. 12 | 13 | 14 | 1.0 (2016-10-23) 15 | ---------------- 16 | 17 | Changes from "Porting to Python 3": 18 | 19 | * Renamed the book to "Supporting Python 3" 20 | 21 | * Removed any mentions of "porting" as it makes the process sound difficult. 22 | 23 | * Use sys.version_info instead of sys.version 24 | 25 | * De-emphasized 2to3 and other strategies in favour of supporting both 26 | languages directly in one source code. 27 | 28 | * The old % style string formatting is no longer deprecated. 29 | 30 | * Typo fixes and grammar improvements. 31 | 32 | * The UnicodeReader example didn't use it's own encoding. 33 | 34 | * A Py23DocChecker class to format unicode output in DocTests was added. 35 | 36 | * Switched from the commercial Flux font to the open source Linux Biolinum O 37 | 38 | -------------------------------------------------------------------------------- /CLA.rst: -------------------------------------------------------------------------------- 1 | Contributor License Agreement 2 | ============================= 3 | 4 | You accept and agree to the following terms and conditions for Your present 5 | and future Contributions to the Work. Except for the license granted herein 6 | to Lennart Regebro, You reserve all right, title, and interest in and to Your 7 | Contributions. 8 | 9 | 1. Definitions. 10 | 11 | "You" (or "Your") shall mean the copyright owner or legal entity 12 | authorized by the copyright owner that is making this Agreement with 13 | Lennart Regebro. For legal entities, the entity making a Contribution and 14 | all other entities that control, are controlled by, or are under common 15 | control with that entity are considered to be a single Contributor. For 16 | the purposes of this definition, "control" means (i) the power, direct or 17 | indirect, to cause the direction or management of such entity, whether by 18 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of 19 | the outstanding shares, or (iii) beneficial ownership of such entity. 20 | 21 | "Contribution" shall mean any original work of authorship, including any 22 | modifications or additions to an existing work, that is intentionally 23 | submitted by You to Lennart Regebro for inclusion in, or documentation of, 24 | the Work. For the purposes of this definition, "submitted" means any form 25 | of electronic, verbal, or written communication sent to Lennart Regebro, 26 | including but not limited to communication on electronic mailing lists, 27 | source code control systems, and issue tracking systems for the purpose of 28 | discussing and improving the Work, but excluding communication that is 29 | conspicuously marked or otherwise designated in writing by You as "Not a 30 | Contribution." 31 | 32 | "The Work" shall mean the book "Supporting Python 3" and it's code and 33 | documentation. 34 | 35 | 2. Grant of Copyright License. Subject to the terms and conditions of this 36 | Agreement, You hereby grant to Lennart Regebro a perpetual, worldwide, 37 | non-exclusive, no-charge, royalty-free, irrevocable copyright license to 38 | reproduce, prepare derivative works of, publish, publicly display, 39 | sublicense, and distribute Your Contributions and such derivative works. 40 | 41 | 3. Grant of Patent License. Subject to the terms and conditions of this 42 | Agreement, You hereby grant to Lennart Regebro and to recipients of 43 | software distributed by Lennart Regebro a perpetual, worldwide, 44 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in 45 | this section) patent license to make, have made, use, offer to sell, sell, 46 | import, and otherwise transfer the Work, where such license applies only 47 | to those patent claims licensable by You that are necessarily infringed by 48 | Your Contribution(s) alone or by combination of Your Contribution(s) with 49 | the Work to which such Contribution(s) was submitted. If any entity 50 | institutes patent litigation against You or any other entity (including a 51 | cross-claim or counterclaim in a lawsuit) alleging that your Contribution, 52 | or the Work to which you have contributed, constitutes direct or 53 | contributory patent infringement, then any patent licenses granted to that 54 | entity under this Agreement for that Contribution or Work shall terminate 55 | as of the date such litigation is filed. 56 | 57 | 4. You represent that you are legally entitled to grant the above license. If 58 | your employer(s) has rights to intellectual property that you create that 59 | includes your Contributions, you represent that you have received 60 | permission to make Contributions on behalf of that employer, that your 61 | employer has waived such rights for your Contributions to Lennart Regebro, 62 | or that your employer has executed a separate Corporate CLA with Lennart 63 | Regebro. 64 | 65 | 5. You represent that each of Your Contributions is Your original creation 66 | (see section 7 for submissions on behalf of others). You represent that 67 | Your Contribution submissions include complete details of any third-party 68 | license or other restriction (including, but not limited to, related 69 | patents and trademarks) of which you are personally aware and which are 70 | associated with any part of Your Contributions. 71 | 72 | 6. You are not expected to provide support for Your Contributions, except to 73 | the extent You desire to provide support. You may provide support for 74 | free, for a fee, or not at all. Unless required by applicable law or 75 | agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, 76 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 77 | including, without limitation, any warranties or conditions of TITLE, NON- 78 | INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 79 | 80 | 7. You agree to notify Lennart Regebro of any facts or circumstances of 81 | which you become aware that would make these representations 82 | inaccurate in any respect. 83 | -------------------------------------------------------------------------------- /CONTRIBUTORS.rst: -------------------------------------------------------------------------------- 1 | CONTRIBUTORS 2 | ============ 3 | 4 | These are the people who have contributed to Supporting Python 3. 5 | My deepest apology if I have missed one! Thank you all! 6 | 7 | * Patrick Gerken (do3cc) 8 | 9 | * Geoffrey Spear (Wooble) 10 | 11 | * Tobias Bieniek (Turbo87) 12 | 13 | * Michael Joseph (michaeljoseph) 14 | 15 | * Staffan Malmgren (staffanm) 16 | 17 | * Steven Myint (myint) 18 | 19 | * Michael J Gruber (mjg) 20 | 21 | * Marianne Corvellec (mkcor) 22 | -------------------------------------------------------------------------------- /INSTALL.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | You need a Python with Sphinx installed. It's tested with Python 3.4, but 5 | any Python with Sphinx is likely to work. 6 | 7 | To run make spell you need aspell and american english dictionaries. 8 | 9 | * Debian-based: ``apt-get install aspell-en`` 10 | * Red Hats: ``yum install aspell-en`` 11 | 12 | To make pdf's you need Texlive 2013: 13 | 14 | * Debian-based: ``apt-get install texlive-full`` 15 | * Red Hats: ``yum install texlive*`` 16 | * Other systems: http://www.tug.org/texlive/quickinstall.html 17 | 18 | You also need the following fonts: 19 | 20 | * Flux Bold (FLUXB___.ttf) 21 | This is a commercial font, so you need to buy it if you want to generate PDF's. 22 | * DejaVu Sans Mono 23 | It's included in many Linux distro's by default. 24 | * TeX Gyre Schola 25 | http://www.gust.org.pl/projects/e-foundry/tex-gyre/schola 26 | Installable as ``texlive-tex-gyre`` on RPM based linuces and 27 | ``fonts-texgyre`` on DEB based linuces. 28 | 29 | 30 | Testing setup 31 | ============= 32 | 33 | (This is all stupid and needs to change. We should probably use Spiny. 34 | We can't use Tox, because it doesn't support old Python versions.) 35 | 36 | This script tests locales, for testing purposes the locale sv_SE.UTF-8 37 | must be available. 38 | 39 | The script tries to find system wide python installations. 40 | 41 | On Ubuntu, you can get all these linux installations from the ppa:fkrull/deadsnakes 42 | 43 | If this fails, it looks for Python 2.3, 2.4, 2,5 in ``/opt/python23`` etc. 44 | 45 | Python 2.5, 2.7, 3.1, 3.2, 3.3, 3.4 must be installed in ``./python26``, 46 | ... 47 | 48 | In Python 2.6 and above also do this:: 49 | 50 | pip install zope.interface setuptools==11.2 51 | 52 | Run the tests with ``make test``. Some tests may fail on the first time, with 53 | warnings like ``warning: no previously-included files matching '*.pyc' found 54 | anywhere in distribution``. Re-running them solves that. 55 | 56 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | export PYTHONPATH = $(shell echo "$$PYTHONPATH"):. 5 | 6 | # You can set these variables from the command line. 7 | SPHINXOPTS = 8 | SPHINXBUILD = sphinx-build 9 | PAPER = 10 | BUILDDIR = build 11 | 12 | # Internal variables. 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) source 14 | 15 | LATEXPATH = $${PATH} 16 | LATEXEXE = xelatex 17 | LATEXOPTS = 18 | 19 | OUTPUTNAME=SupportingPython3 20 | 21 | SPELL = aspell -l en_US 22 | 23 | 24 | TODAY = $(shell date +%Y%m%d) 25 | 26 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 27 | 28 | help: 29 | @echo "Please use \`make ' where is one of" 30 | @echo " html to make standalone HTML files" 31 | @echo " dirhtml to make HTML files named index.html in directories" 32 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 33 | @echo " pdf to make a PDF" 34 | @echo " epub to make an ePUB book" 35 | @echo " changes to make an overview of all changed/added/deprecated items" 36 | @echo " linkcheck to check all external links for integrity" 37 | 38 | clean: 39 | -rm -rf $(BUILDDIR)/* 40 | 41 | html: 42 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 43 | @echo 44 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 45 | 46 | epub: 47 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 48 | @echo 49 | @echo "Build finished. The epub pages are in $(BUILDDIR)/epub." 50 | 51 | dirhtml: 52 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 53 | @echo 54 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 55 | 56 | latex: 57 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 58 | @echo 59 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 60 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 61 | "run these through (pdf)latex." 62 | 63 | changes: 64 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 65 | @echo 66 | @echo "The overview file is in $(BUILDDIR)/changes." 67 | 68 | linkcheck: 69 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 70 | @echo 71 | @echo "Link check complete; look for any errors in the above output " \ 72 | "or in $(BUILDDIR)/linkcheck/output.txt." 73 | 74 | pdf: latex 75 | # Copy in some custom files 76 | cp source/_templates/sphinxcustom.cls $(BUILDDIR)/latex/sphinxcustom.cls 77 | cp source/_templates/sphinx.sty $(BUILDDIR)/latex/sphinx.sty 78 | cp source/_static/by-nc-sa.pdf $(BUILDDIR)/latex/by-nc-sa.pdf 79 | # Run LaTeX three times, the first time generates the text: 80 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 81 | # Create the indexes 82 | -cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 83 | # Re run LaTex with indexes. Now we get a content listing and an index. 84 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 85 | # Recreate the index since all the page numbers changed when the content listing appeared. 86 | -cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 87 | # And run LaTex again, this time with correct page numbers 88 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 89 | 90 | pdf-screen: 91 | BOOK_SIZE=screen_form $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 92 | # Copy in some custom files 93 | cp source/_templates/sphinxcustom.cls $(BUILDDIR)/latex/sphinxcustom.cls 94 | cp source/_templates/sphinx.sty $(BUILDDIR)/latex/sphinx.sty 95 | cp source/_static/by-nc-sa.pdf $(BUILDDIR)/latex/by-nc-sa.pdf 96 | # Run LaTeX three times, the first time generates the text: 97 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 98 | # Create the indexes 99 | -cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 100 | # Re run LaTex with indexes. Now we get a content listing and an index. 101 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 102 | # Recreate the index since all the page numbers changed when the content listing appeared. 103 | -cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 104 | # And run LaTex again, this time with correct page numbers 105 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 106 | pdfunite covers/ScreenFront.pdf build/latex/SupportingPython3.pdf covers/ScreenBack.pdf build/SupportingPython3-screen-1.0-$(TODAY).pdf 107 | 108 | pdf-phones: 109 | BOOK_SIZE=phones_form $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 110 | # Copy in some custom files 111 | cp source/_templates/sphinxcustom.cls $(BUILDDIR)/latex/sphinxcustom.cls 112 | cp source/_templates/sphinx.sty $(BUILDDIR)/latex/sphinx.sty 113 | cp source/_static/by-nc-sa.pdf $(BUILDDIR)/latex/by-nc-sa.pdf 114 | # Run LaTeX three times, the first time generates the text: 115 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 116 | # Create the indexes 117 | -cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 118 | # Re run LaTex with indexes. Now we get a content listing and an index. 119 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 120 | # Recreate the index since all the page numbers changed when the content listing appeared. 121 | -cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 122 | # And run LaTex again, this time with correct page numbers 123 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 124 | pdfunite covers/PhoneFront.pdf build/latex/SupportingPython3.pdf covers/PhoneBack.pdf build/SupportingPython3-phone-1.0-$(TODAY).pdf 125 | 126 | pdf-tablets: 127 | BOOK_SIZE=tablets_form $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 128 | # Copy in some custom files 129 | cp source/_templates/sphinxcustom.cls $(BUILDDIR)/latex/sphinxcustom.cls 130 | cp source/_templates/sphinx.sty $(BUILDDIR)/latex/sphinx.sty 131 | cp source/_static/by-nc-sa.pdf $(BUILDDIR)/latex/by-nc-sa.pdf 132 | # Run LaTeX three times, the first time generates the text: 133 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 134 | # Create the indexes 135 | -cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 136 | # Re run LaTex with indexes. Now we get a content listing and an index. 137 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 138 | # Recreate the index since all the page numbers changed when the content listing appeared. 139 | -cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 140 | # And run LaTex again, this time with correct page numbers 141 | cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 142 | pdfunite covers/TabletFront.pdf build/latex/SupportingPython3.pdf covers/TabletBack.pdf build/SupportingPython3-tablet-1.0-$(TODAY).pdf 143 | 144 | # No support for book printing PDF's here. Talk to the author if you want to print books 145 | #pdf-print: 146 | #BOOK_SIZE=print_form $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 147 | ## Copy in some custom files 148 | #cp source/_templates/sphinxcustom.cls $(BUILDDIR)/latex/sphinxcustom.cls 149 | #cp source/_templates/sphinx.sty $(BUILDDIR)/latex/sphinx.sty 150 | #cp source/_static/by-nc-sa.pdf $(BUILDDIR)/latex/by-nc-sa.pdf 151 | ## Run LaTeX three times, the first time generates the text: 152 | #cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 153 | ## Create the indexes 154 | #-cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 155 | ## Re run LaTex with indexes. Now we get a content listing and an index. 156 | #cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 157 | ## Recreate the index since all the page numbers changed when the content listing appeared. 158 | #-cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) makeindex -s python.ist $(OUTPUTNAME).idx 159 | ## And run LaTex again, this time with correct page numbers 160 | #cd $(BUILDDIR)/latex; PATH=$(LATEXPATH) $(LATEXEXE) $(LATEXOPTS) $(OUTPUTNAME).tex 161 | #pdfunite covers/PrintFront.pdf build/latex/SupportingPython3.pdf covers/PrintBack.pdf build/SupportingPython3-print-1.0-$(TODAY).pdf 162 | 163 | pdf-all: pdf-screen pdf-tablets pdf-phones 164 | 165 | spell: 166 | for f in source/*.rst; do $(SPELL) -c $$f; done 167 | 168 | stats: 169 | wc source/*.rst 170 | 171 | test: 172 | ./runtests.sh 173 | 174 | check: spell test stats 175 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Supporting Python 3 2 | =================== 3 | 4 | .. image:: https://api.travis-ci.org/regebro/supporting-python-3.png 5 | 6 | This is a free book about how to make your code support Python 3. 7 | 8 | It costs nothing, and you can both publish it and contribute to it! 9 | 10 | 11 | Reading this book 12 | ----------------- 13 | 14 | Supporting Python 3 is available for free on http://python3porting.com/ . You 15 | can also download a PDF version from https://gumroad.com/l/python3 for any 16 | price you like, and you can buy a paper version from 17 | https://www.createspace.com/4312357 for $4.45, which is the printing cost of 18 | the book, and hence as close to free as you can possibly come. 19 | 20 | As of today, the name of the book in all these places are "Porting to Python 21 | 3", which is the old name of the book before I made it into a community 22 | effort. This will change sometime during 2015, and the above links will also 23 | change as a result. 24 | 25 | 26 | Contributing to this book 27 | ------------------------- 28 | 29 | To contribute to this book, fork it on 30 | `GitHub `_. 31 | 32 | If you are making any changes or additions that fall under copyright law, you 33 | must first `sign the Contributor License Agreement 34 | `_. 35 | 36 | Follow the instructions in `INSTALL.rst `_ on how to install it, and 37 | make the changes you like, and create a pull request. 38 | 39 | It's probably a good idea to talk to Lennart Regebro before making major 40 | changes or additions. 41 | 42 | 43 | License 44 | ------- 45 | 46 | This book is (c) Lennart Regebro 2011-2015, and is licensed under a Creative 47 | Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. 48 | 49 | See http://creativecommons.org/licenses/by-nc-sa/3.0/ 50 | 51 | This means you can publish this book or parts of it, but not in such a way 52 | that you make money from it. Should you want to use this book in a commercial 53 | setting, please contact Lennart Regebro, regebro@gmail.com. 54 | 55 | The book in it's current form uses Flux Bold, which is a commercial font. 56 | If you want to publish this book in any form, you need either to replace 57 | this font, or buy a license for the usage that you intend. 58 | 59 | Generating the PDFs 60 | ------------------- 61 | 62 | Making a PDF is not just a matter of doing ``make pdf``. Doing so will indeed 63 | create a PDF, but you need to create four PDF's when releasing the book. 64 | 65 | You need:: 66 | 67 | * A print PDF, 6" x 9", black only. 68 | * A screen PDF, 8.27" x 11", with syntax highlighting. 69 | * A tablet PDF, 7" x 9", with syntax highlighting. 70 | * A phone PDF, 4.8" x 7.2", with syntax highlighting. 71 | 72 | You do this by changing the conf.py. For the print PDF, the ``pygments_style`` 73 | should be ``"none"`` otherwise it should be ``"sphinx"``. 74 | 75 | To select the paper size, change the used variable in ``print_latex_elements`` 76 | between ``print_form``, ``screen_form``, ``tablets_form`` or ``phone_form``. 77 | 78 | You don't need to do anything more for the Print PDF, it's done, but the 79 | other PDF's should have the front and back covers merged. This can be done 80 | with tools like pdfunite:: 81 | 82 | $ pdfunite covers/PhoneFront.pdf build/latex/SupportingPython3.pdf \ 83 | covers/PhoneBack.pdf SupportingPython3-phone-1.0-dev.pdf 84 | 85 | An additional hitch is that each release of Sphinx or texlive will have 86 | changes that break the PDF generation, so it's highly unlikely that this book 87 | will generate cleanly on your computer. It works on Ubuntu 14.04LTS, but not 88 | on Ubuntu 16.04, for example. 89 | 90 | You have to install at least the following packages to build the book:: 91 | 92 | ``texlive texlive-xetex texlive-latex-extra lmodern fonts-texgyre 93 | fonts-linuxlibertine python-sphinx poppler-utils`` 94 | 95 | EPUB Generation 96 | --------------- 97 | It is also possible to make an EPUB format of this book with the command 98 | ``make epub`` - note that at the current time this will lack the front and 99 | back covers. 100 | 101 | The resulting eBook, in EPUB format, will be placed in the file 102 | ``build/epub/SupportingPython3.epub`` _or it's windows equivalent. 103 | 104 | HTML Generation 105 | --------------- 106 | It is also possible to make both standalone HTML and HTML directory, 107 | (directories with ``index.html`` starting points), formats of this book 108 | with the commands ``make html`` and ``make dirhtml`` - note that at the 109 | current time these will lack the front and back covers. 110 | 111 | The resulting books will be placed in the directories 112 | ``build/html`` and ``build/dirhtml`` respectively. 113 | -------------------------------------------------------------------------------- /bootstrap_jenkins.sh: -------------------------------------------------------------------------------- 1 | 2 | virtualenv -p `which python2.6` python26 3 | virtualenv -p `which python2.7` python27 4 | virtualenv -p `which python3.1` python31 5 | virtualenv -p `which python3.2` python32 6 | virtualenv -p `which python3.3` python33 7 | virtualenv -p `which python3.4` python34 8 | 9 | for i in 26 27 31 32 33 34 10 | do 11 | python$i/bin/pip install zope.interface setuptools==11.2 12 | done 13 | 14 | # initialize files 15 | make test > /dev/null || echo "Ignore failures" 16 | make test > /dev/null || echo "Ignore failures" 17 | make test > /dev/null || echo "Ignore failures" 18 | make test > /dev/null || echo "Ignore failures" 19 | make test > /dev/null || echo "Ignore failures" 20 | make test > /dev/null || echo "Ignore failures" 21 | -------------------------------------------------------------------------------- /covers/802764156_859a8f6969_o.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/covers/802764156_859a8f6969_o.jpg -------------------------------------------------------------------------------- /covers/Cover.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/covers/Cover.xcf -------------------------------------------------------------------------------- /covers/PhoneBack.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/covers/PhoneBack.pdf -------------------------------------------------------------------------------- /covers/PhoneFront.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/covers/PhoneFront.pdf -------------------------------------------------------------------------------- /covers/README.txt: -------------------------------------------------------------------------------- 1 | How to make covers 2 | ================== 3 | 4 | The cover is in the Covers.xcf file. That's a GIMP file, so you need to use 5 | GIMP to open it for editing and PDF generation. And you need to create a lot 6 | of PDF's. You will also need GhostScript installed. 7 | 8 | The Covers.xcf is designed for use with Createspace self publishing. 9 | For more information on the PDF requirements, check with createspace. 10 | 11 | The Spine of the book is the number of interior pages * 0.002252 inches. 12 | If a significant amount of pages is added to the book, you may need to 13 | adjust the design. 14 | 15 | The two top layers are: 16 | 17 | * BookCover6X9_BW_140. This is an import of the Createspace cover template. 18 | It's for 140 pages, and the book is currently 151, but that's not enough 19 | to create a problem. 20 | 21 | * Preview Mask. This is a gray mask that will allow you to see approximately 22 | the area that the printed cover will be. 23 | 24 | Both these layers must be invisible when printing the cover. 25 | 26 | 27 | Custom paper sizes 28 | ------------------ 29 | 30 | The PDF's come in three formats: Print, Phone, Tablet and Screen. OK, fine, 31 | phones and tablets are also screens, but they are also computers, and "PC" 32 | kinda implies it's not a Mac, so I choose "screen" as a name. 33 | 34 | To create the cover PDFs them you need to create several custom paper sizes. 35 | There should be creates with 0 page margins, and the following sizes 36 | (width x height): 37 | 38 | * Print size: 17" x 11" 39 | * Phone size: 4.8" x 7.2" 40 | * Tablet size: 6" x 9" 41 | * Screen size: 7.32" x 11" 42 | (This fits both in A4 and Letter and hence you can also print it.) 43 | 44 | 45 | The print cover PDF 46 | ------------------- 47 | 48 | To create the cover for printing, select the custom paper size that is 17" wide 49 | by 11" high. Go to the Image Settings tab in the print dialog, select "Ignore 50 | page margins" and then scale the image to as big as possible. 51 | 52 | Print to a file in portrait mode. Yes, that portrait mode will be wider than 53 | high, but that's not a problem. The default output for printing is typically a 54 | file called ``output.pdf`` in your home directory. 55 | 56 | The resulting PDF will be sent to the printing company to make the cover, and 57 | the PDF created by Sphinx is the inside pages. The printing companies I have 58 | used for the big conference print runs have both accepted this PDF, and it's 59 | designed for use with Createspace. Other online self-publishing companies may 60 | have other requirements. 61 | 62 | 63 | The PDF covers 64 | -------------- 65 | 66 | The PDF issues should also have covers, but unlike the print cover they 67 | should contain only the printed area. Therefore you must crop the image 68 | before printing. Turn on the preview mask, make sure that Snap to guides 69 | is enabled and crop away the gray area and the spine. The resulting image 70 | should be approximately 6" x 9". 71 | 72 | This image you then print to the Phone, Tablet and Screen papers, again 73 | while ignoring page margins and scaling the image to be as big as possible. 74 | The resulting images will be too heavy, with front covers being around 6MB. 75 | 76 | Therefore you need to optimize each PDF with Ghostscript. The command to 77 | optimize the front PDF for the Phone size would be this:: 78 | 79 | gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -sOutputFile=PhoneFront.pdf ~/output.pdf 80 | 81 | Repeat for each of the sizes, and then do the same for the back cover. 82 | 83 | Done! 84 | -------------------------------------------------------------------------------- /covers/ScreenBack.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/covers/ScreenBack.pdf -------------------------------------------------------------------------------- /covers/ScreenFront.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/covers/ScreenFront.pdf -------------------------------------------------------------------------------- /covers/TabletBack.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/covers/TabletBack.pdf -------------------------------------------------------------------------------- /covers/TabletFront.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/covers/TabletFront.pdf -------------------------------------------------------------------------------- /cronscript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /mnt/sites/supporting-python-3 4 | 5 | git fetch 6 | if [ $(git rev-parse HEAD) == $(git rev-parse @{u}) ]; then 7 | echo "NOT UPDATED" 8 | exit 0 9 | fi 10 | 11 | echo "UPDATED: REBUILD!" 12 | git pull 13 | make html 14 | make pdf-all 15 | cp -r build/html/* /var/www/python3porting.com 16 | TODAY=`date +%Y%m%d` 17 | cp build/*${TODAY}.pdf /var/www/python3porting.com/pdfs 18 | rm /var/www/python3porting.com/pdfs/SupportingPython3*latest.pdf 19 | ln -s /var/www/python3porting.com/pdfs/SupportingPython3-phone-1.0-${TODAY}.pdf /var/www/python3porting.com/pdfs/SupportingPython3-phone-1.0-latest.pdf 20 | ln -s /var/www/python3porting.com/pdfs/SupportingPython3-screen-1.0-${TODAY}.pdf /var/www/python3porting.com/pdfs/SupportingPython3-screen-1.0-latest.pdf 21 | ln -s /var/www/python3porting.com/pdfs/SupportingPython3-tablet-1.0-${TODAY}.pdf /var/www/python3porting.com/pdfs/SupportingPython3-tablet-1.0-latest.pdf 22 | -------------------------------------------------------------------------------- /py3example/README.txt: -------------------------------------------------------------------------------- 1 | This is an example of a doctest. 2 | 3 | >>> print "This should work" 4 | This should work 5 | 6 | >>> import socket 7 | >>> socket.gethostbyname("www.python.rog") 8 | Traceback (most recent call last): 9 | s...gaierror: [Errno -5] No address associated with hostname 10 | -------------------------------------------------------------------------------- /py3example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/py3example/__init__.py -------------------------------------------------------------------------------- /py3example/_sorted.py: -------------------------------------------------------------------------------- 1 | def sorted(data, key): 2 | mapping = {} 3 | for x in data: 4 | mapping[key(x)] = x 5 | keys = mapping.keys() 6 | keys.sort() 7 | return [mapping[x] for x in keys] 8 | -------------------------------------------------------------------------------- /py3example/bites.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.version < '3': 3 | class Bites(str): 4 | def __new__(cls, value): 5 | if isinstance(value[0], int): 6 | # It's a list of integers 7 | value = ''.join([chr(x) for x in value]) 8 | return super(bites, cls).__new__(cls, value) 9 | 10 | def itemint(self, index): 11 | return ord(self[index]) 12 | 13 | def iterint(self): 14 | for x in self: 15 | yield ord(x) 16 | else: 17 | 18 | class Bites(bytes): 19 | def __new__(cls, value): 20 | if isinstance(value, str): 21 | # It's a unicode string: 22 | value = value.encode('ISO-8859-1') 23 | return super(bites, cls).__new__(cls, value) 24 | 25 | def itemint(self, x): 26 | return self[x] 27 | 28 | def iterint(self): 29 | for x in self: 30 | yield x 31 | -------------------------------------------------------------------------------- /py3example/compare.py: -------------------------------------------------------------------------------- 1 | class ComparableMixin2(object): 2 | def __ne__(self, other): 3 | print (">=") 4 | return other < self or self < other 5 | def __eq__(self, other): 6 | print ("==") 7 | return not self != other 8 | def __gt__(self, other): 9 | print (">") 10 | return other < self 11 | def __ge__(self, other): 12 | print (">=") 13 | return not (self < other) 14 | def __le__(self, other): 15 | print ("<=") 16 | return not (self > other) 17 | 18 | class ComparableMixin3(object): 19 | def __lt__(self, other): 20 | try: 21 | return self.__cmp__(other) < 0 22 | except TypeError: 23 | return NotImplemented 24 | 25 | def __le__(self, other): 26 | try: 27 | return self.__cmp__(other) <= 0 28 | except TypeError: 29 | return NotImplemented 30 | 31 | def __gt__(self, other): 32 | try: 33 | return self.__cmp__(other) > 0 34 | except TypeError: 35 | return NotImplemented 36 | 37 | def __ge__(self, other): 38 | try: 39 | return self.__cmp__(other) >= 0 40 | except TypeError: 41 | return NotImplemented 42 | 43 | def __eq__(self, other): 44 | try: 45 | return self.__cmp__(other) == 0 46 | except TypeError: 47 | return NotImplemented 48 | 49 | def __ne__(self, other): 50 | try: 51 | return self.__cmp__(other) != 0 52 | except TypeError: 53 | return NotImplemented 54 | 55 | class ComparableMixin(object): 56 | def _compare(self, other, values): 57 | try: 58 | return self.__cmp__(other) in values 59 | except TypeError: 60 | return NotImplemented 61 | 62 | def __lt__(self, other): return self._compare(other, (-1,)) 63 | 64 | def __le__(self, other): return self._compare(other, (-1, 0,)) 65 | 66 | def __eq__(self, other): return self._compare(other, (0,)) 67 | 68 | def __ge__(self, other): return self._compare(other, (0, 1,)) 69 | 70 | def __gt__(self, other): return self._compare(other, (1,)) 71 | 72 | def __ne__(self, other): return self._compare(other, (-1, 1,)) 73 | 74 | 75 | class ComparableMixin4(object): 76 | def __lt__(self, other): 77 | return self._compare(other, '__lt__') 78 | 79 | def __le__(self, other): 80 | return self._compare(other, '__le__') 81 | 82 | def __gt__(self, other): 83 | return self._compare(other, '__gt__') 84 | 85 | def __ge__(self, other): 86 | return self._compare(other, '__ge__') 87 | 88 | def __eq__(self, other): 89 | return self._compare(other, '__eq__') 90 | 91 | def __ne__(self, other): 92 | return self._compare(other, '__ne__') 93 | -------------------------------------------------------------------------------- /py3example/example.py: -------------------------------------------------------------------------------- 1 | # This module contains an example of code that does not work in Python 3, 2 | # and needs to be converted with 2to3. 3 | 4 | import StringIO 5 | 6 | def print_something(): 7 | out = StringIO.StringIO() 8 | print >> out, "This should end up in the tempfile" 9 | 10 | def buffer_test(): 11 | s = buffer("Whooohah") 12 | 13 | def text_conversion(): 14 | import tempfile 15 | binout = tempfile.TemporaryFile('w+b') 16 | textout = tempfile.TemporaryFile('w+t') 17 | binout.write("It's a string!") 18 | textout.write("It's a string!") 19 | binout.close() 20 | textout.close() 21 | 22 | -------------------------------------------------------------------------------- /py3example/tests.py: -------------------------------------------------------------------------------- 1 | import unittest, doctest 2 | 3 | class TestCase1(unittest.TestCase): 4 | 5 | def test_2to3(self): 6 | from py3example import example 7 | example.print_something() 8 | example.text_conversion() 9 | 10 | def test_bites(self): 11 | from py3example.bites import bites 12 | all = bites(range(255)) 13 | self.assertEqual(all.itemint(25),25) 14 | giftag = bites('GIF89a') 15 | self.assertEqual([x for x in giftag.iterint()], [71, 73, 70, 56, 57, 97]) 16 | 17 | def test_sorting(self): 18 | def dacmp(a, b): 19 | return cmp(a, b) 20 | 21 | dalist = [5,3,8,7,1,2,0,4,6,9] 22 | dalist.sort(cmp=dacmp) 23 | self.assertEqual(dalist, [0,1,2,3,4,5,6,7,8,9]) 24 | 25 | def test_23sorted(self): 26 | dalist = ['ant', 'Aardvark', 'Dingo', 'banana'] 27 | from py3example._sorted import sorted as mysort 28 | self.assertEqual(mysort(dalist, key=str.lower), ['Aardvark', 'ant', 'banana', 'Dingo']) 29 | 30 | def test_comparisons(self): 31 | from py3example.compare import ComparableMixin 32 | 33 | class Comparable(ComparableMixin): 34 | """This type uses ducktyping to compare""" 35 | def __init__(self, v): 36 | self.v = v 37 | 38 | def cmp_value(self): 39 | return self.v 40 | 41 | def __cmp__(self, other): 42 | try: 43 | return (self.v > other.v) - (self.v < other.v) 44 | except AttributeError: 45 | raise TypeError('Can not compare %s with %s ' % (self, other)) 46 | 47 | #def _compare(self, other, method): 48 | #try: 49 | #return getattr(self.v, method)(other.v) 50 | #except AttributeError: 51 | #return NotImplemented 52 | 53 | #def __lt__(self, other): 54 | #try: 55 | #return self.v < other.v 56 | #except AttributeError: 57 | #return NotImplemented 58 | 59 | class InComparable(ComparableMixin): 60 | """These objects are not comparable with any other types""" 61 | def __init__(self, v): 62 | self.v = v 63 | def __cmp__(self, other): 64 | if not isinstance(other, InComparable): 65 | raise TypeError('Can not compare InComparable and ' + type(other)) 66 | else: 67 | return (self.v > other.v) - (self.v < other.v) 68 | 69 | #def _compare(self, other, method): 70 | #if not isinstance(other, InComparable): 71 | #return NotImplemented 72 | #return getattr(self.v, method)(other.v) 73 | 74 | #def __lt__(self, other): 75 | #print ("Incomp <") 76 | #if not isinstance(other, InComparable): 77 | #return NotImplemented 78 | #else: 79 | #return self.v < other.v 80 | 81 | incomp1 = InComparable(1) 82 | incomp2 = InComparable(2) 83 | comp1 = Comparable(1) 84 | comp2 = Comparable(2) 85 | 86 | self.assert_(incomp1 < incomp2) 87 | self.assert_(incomp2 > incomp1) 88 | self.assert_(incomp1 != incomp2) 89 | self.assert_(incomp1 == incomp1) 90 | self.assert_(incomp2 >= incomp1) 91 | self.assert_(incomp1 <= incomp2) 92 | 93 | self.assert_(comp1 < comp2) 94 | self.assert_(comp2 > comp1) 95 | self.assert_(comp1 != comp2) 96 | self.assert_(comp1 == comp1) 97 | self.assert_(comp2 >= comp1) 98 | self.assert_(comp1 <= comp2) 99 | 100 | self.assert_(comp1 < incomp2) 101 | self.assert_(comp2 > incomp1) 102 | self.assert_(comp1 != incomp2) 103 | self.assert_(comp1 == incomp1) 104 | self.assert_(comp2 >= incomp1) 105 | self.assert_(comp1 <= incomp2) 106 | 107 | self.assert_(incomp1 < comp2) 108 | self.assert_(incomp2 > comp1) 109 | self.assert_(incomp1 != comp2) 110 | self.assert_(incomp1 == comp1) 111 | self.assert_(incomp2 >= comp1) 112 | self.assert_(incomp1 <= comp2) 113 | 114 | # Make sure this is sortable: 115 | [Comparable(2), Comparable(1)].sort() 116 | self.assertEqual(Comparable(2) < Comparable(1), False) 117 | self.assertEqual(Comparable(2) > Comparable(1), True) 118 | # And this not: 119 | self.assertRaises(TypeError, [2, Comparable(1)].sort,) 120 | 121 | def test_suite(): 122 | suite = unittest.makeSuite(TestCase1) 123 | suite.addTests(doctest.DocFileSuite('README.txt', 124 | optionflags=doctest.ELLIPSIS|doctest.IGNORE_EXCEPTION_DETAIL)) 125 | return suite 126 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | zope.interface==4.1.2 2 | setuptools==11.2 3 | -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PYTHON23=`which python23` 4 | PYTHON23=${PYTHON23:-`which python2.3`} 5 | PYTHON23=${PYTHON23:-/opt/python23/bin/python} 6 | 7 | PYTHON24=`which python24` 8 | PYTHON24=${PYTHON24:-`which python2.4`} 9 | PYTHON24=${PYTHON24:-/opt/python24/bin/python} 10 | 11 | PYTHON25=`which python25` 12 | PYTHON25=${PYTHON25:-`which python2.5`} 13 | PYTHON25=${PYTHON25:-/opt/python25/bin/python} 14 | 15 | if [ -a python26/bin/python ] 16 | then 17 | PYTHON26=python26/bin/python 18 | fi 19 | PYTHON26=${PYTHON26:-`which python26`} 20 | PYTHON26=${PYTHON26:-`which python2.6`} 21 | PYTHON26=${PYTHON26:-/opt/python26/bin/python} 22 | 23 | if [ -a python27/bin/python ] 24 | then 25 | PYTHON27=python27/bin/python 26 | fi 27 | PYTHON27=${PYTHON27:-`which python27`} 28 | PYTHON27=${PYTHON27:-`which python2.7`} 29 | PYTHON27=${PYTHON27:-/opt/python27/bin/python} 30 | 31 | if [ -a python31/bin/python ] 32 | then 33 | PYTHON31=python31/bin/python 34 | fi 35 | PYTHON31=${PYTHON31:-`which python31`} 36 | PYTHON31=${PYTHON31:-`which python3.1`} 37 | PYTHON31=${PYTHON31:-/opt/python31/bin/python} 38 | 39 | if [ -a python32/bin/python ] 40 | then 41 | PYTHON32=python32/bin/python 42 | fi 43 | PYTHON32=${PYTHON32:-`which python32`} 44 | PYTHON32=${PYTHON32:-`which python3.2`} 45 | PYTHON32=${PYTHON32:-/opt/python32/bin/python} 46 | 47 | if [ -a python33/bin/python ] 48 | then 49 | PYTHON33=python33/bin/python 50 | fi 51 | PYTHON33=${PYTHON33:-`which python33`} 52 | PYTHON33=${PYTHON33:-`which python3.3`} 53 | PYTHON33=${PYTHON33:-/opt/python33/bin/python} 54 | 55 | if [ -a python34/bin/python ] 56 | then 57 | PYTHON34=python34/bin/python 58 | fi 59 | PYTHON34=${PYTHON34:-`which python34`} 60 | PYTHON34=${PYTHON34:-`which python3.4`} 61 | PYTHON34=${PYTHON34:-/opt/python34/bin/python} 62 | 63 | # Fail on error 64 | set -e 65 | 66 | cd source/_tests 67 | 68 | echo "##### PY23 #######" 69 | $PYTHON23 tests23.py 70 | 71 | echo "##### PY24 #######" 72 | $PYTHON24 tests24.py 73 | 74 | echo "##### PY25 #######" 75 | $PYTHON25 tests24.py 76 | 77 | echo "##### PY26 #######" 78 | $PYTHON26 tests26.py 79 | 80 | echo "##### PY27 #######" 81 | # Run both the Python 2.6 tests and the 2.7 specific tests: 82 | $PYTHON27 tests26.py 83 | $PYTHON27 tests27.py 84 | 85 | 86 | echo "##### PY31 #######" 87 | $PYTHON31 tests31.py 88 | 89 | echo "##### PY32 #######" 90 | $PYTHON32 tests32.py 91 | 92 | echo "##### PY33 #######" 93 | $PYTHON33 tests32.py 94 | 95 | echo "##### PY34 #######" 96 | $PYTHON34 tests32.py 97 | -------------------------------------------------------------------------------- /source/_static/PortingtoPython3-1.0-front-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/PortingtoPython3-1.0-front-small.png -------------------------------------------------------------------------------- /source/_static/PortingtoPython3-2nd1.0-front-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/PortingtoPython3-2nd1.0-front-small.png -------------------------------------------------------------------------------- /source/_static/agogo.css: -------------------------------------------------------------------------------- 1 | /* 2 | * agogo.css_t 3 | * ~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- agogo theme. 6 | * 7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | 13 | 14 | @font-face { 15 | font-family: 'FluxBold'; 16 | src: url('fluxb___-webfont.eot'); 17 | src: url('fluxb___-webfont.eot?#iefix') format('embedded-opentype'), 18 | url('fluxb___-webfont.woff') format('woff'), 19 | url('fluxb___-webfont.ttf') format('truetype'), 20 | url('fluxb___-webfont.svg#FluxBoldRegular') format('svg'); 21 | font-weight: normal; 22 | font-style: normal; 23 | 24 | } 25 | 26 | @font-face { 27 | font-family: 'TeXGyreSchola'; 28 | src: url('texgyreschola-regular-webfont.eot'); 29 | src: url('texgyreschola-regular-webfont.eot?#iefix') format('embedded-opentype'), 30 | url('texgyreschola-regular-webfont.woff') format('woff'), 31 | url('texgyreschola-regular-webfont.ttf') format('truetype'), 32 | url('texgyreschola-regular-webfont.svg#TeXGyreScholaRegular') format('svg'); 33 | font-weight: normal; 34 | font-style: normal; 35 | 36 | } 37 | 38 | @font-face { 39 | font-family: 'TeXGyreSchola'; 40 | src: url('texgyreschola-bold-webfont.eot'); 41 | src: url('texgyreschola-bold-webfont.eot?#iefix') format('embedded-opentype'), 42 | url('texgyreschola-bold-webfont.woff') format('woff'), 43 | url('texgyreschola-bold-webfont.ttf') format('truetype'), 44 | url('texgyreschola-bold-webfont.svg#TeXGyreScholaBold') format('svg'); 45 | font-weight: bold; 46 | font-style: normal; 47 | 48 | } 49 | 50 | @font-face { 51 | font-family: 'TeXGyreSchola'; 52 | src: url('texgyreschola-bolditalic-webfont.eot'); 53 | src: url('texgyreschola-bolditalic-webfont.eot?#iefix') format('embedded-opentype'), 54 | url('texgyreschola-bolditalic-webfont.woff') format('woff'), 55 | url('texgyreschola-bolditalic-webfont.ttf') format('truetype'), 56 | url('texgyreschola-bolditalic-webfont.svg#TeXGyreScholaBoldItalic') format('svg'); 57 | font-weight: bold; 58 | font-style: italic; 59 | 60 | } 61 | 62 | @font-face { 63 | font-family: 'TeXGyreSchola'; 64 | src: url('texgyreschola-italic-webfont.eot'); 65 | src: url('texgyreschola-italic-webfont.eot?#iefix') format('embedded-opentype'), 66 | url('texgyreschola-italic-webfont.woff') format('woff'), 67 | url('texgyreschola-italic-webfont.ttf') format('truetype'), 68 | url('texgyreschola-italic-webfont.svg#TeXGyreScholaItalic') format('svg'); 69 | font-weight: normal; 70 | font-style: italic; 71 | 72 | } 73 | 74 | @font-face { 75 | font-family: 'DejaVuSansMono'; 76 | src: url('dejavusansmono-webfont.eot'); 77 | src: url('dejavusansmono-webfont.eot?#iefix') format('embedded-opentype'), 78 | url('dejavusansmono-webfont.woff') format('woff'), 79 | url('dejavusansmono-webfont.ttf') format('truetype'), 80 | url('dejavusansmono-webfont.svg#DejaVuSansMonoBook') format('svg'); 81 | font-weight: normal; 82 | font-style: normal; 83 | 84 | } 85 | 86 | @font-face { 87 | font-family: 'DejaVuSansMono'; 88 | src: url('dejavusansmono-bold-webfont.eot'); 89 | src: url('dejavusansmono-bold-webfont.eot?#iefix') format('embedded-opentype'), 90 | url('dejavusansmono-bold-webfont.woff') format('woff'), 91 | url('dejavusansmono-bold-webfont.ttf') format('truetype'), 92 | url('dejavusansmono-bold-webfont.svg#DejaVuSansMonoBold') format('svg'); 93 | font-weight: bold; 94 | font-style: normal; 95 | 96 | } 97 | 98 | * { 99 | margin: 0px; 100 | padding: 0px; 101 | } 102 | 103 | body { 104 | font-family: "TeXGyreSchola", "Georgia", "Utopia", "Palatino", "Palatino Linotype", "serif"; 105 | line-height: 1.4em; 106 | color: black; 107 | background-color: #eeeeec; 108 | } 109 | 110 | blockquote { 111 | margin-top: 2em; 112 | font-size: 149%; 113 | } 114 | blockquote:before { 115 | content: open-quote; 116 | font-weight: bold; 117 | font-family: Times; 118 | font-size: 300%; 119 | color: gray; 120 | margin-right: 5px; 121 | } 122 | blockquote:after { 123 | content: close-quote; 124 | font-weight: bold; 125 | font-family: Times; 126 | font-size: 0%; 127 | color: white; 128 | } 129 | 130 | /* Page layout */ 131 | 132 | div.header, div.content, div.footer { 133 | width: 62em; 134 | margin-left: auto; 135 | margin-right: auto; 136 | } 137 | 138 | div.header-wrapper { 139 | background: #FFCC33; 140 | border-bottom: 3px solid #2e3436; 141 | } 142 | 143 | 144 | /* Default body styles */ 145 | a { 146 | color: #ce5c00; 147 | } 148 | 149 | div.bodywrapper a, div.footer a { 150 | text-decoration: underline; 151 | } 152 | 153 | .clearer { 154 | clear: both; 155 | } 156 | 157 | .left { 158 | float: left; 159 | } 160 | 161 | .right { 162 | float: right; 163 | } 164 | 165 | .line-block { 166 | display: block; 167 | margin-top: 1em; 168 | margin-bottom: 1em; 169 | } 170 | 171 | .line-block .line-block { 172 | margin-top: 0; 173 | margin-bottom: 0; 174 | margin-left: 1.5em; 175 | } 176 | 177 | h1, h2, h3, h4 { 178 | font-family: "FluxBold", "Arial", "Liberation Sans", "DejaVu Sans", "sans-serif"; 179 | font-weight: normal; 180 | text-align: left; 181 | color: #3465a4; 182 | margin-bottom: .8em; 183 | } 184 | 185 | h1 { 186 | color: #204a87; 187 | } 188 | 189 | h2 { 190 | padding-bottom: .5em; 191 | border-bottom: 1px solid #3465a4; 192 | } 193 | 194 | pre { 195 | font-family: "DejaVuSansMono", "Courier New", "Courier", "monospace"; 196 | font-size: 15px; 197 | } 198 | 199 | a.headerlink { 200 | visibility: hidden; 201 | color: #dddddd; 202 | padding-left: .3em; 203 | } 204 | 205 | h1:hover > a.headerlink, 206 | h2:hover > a.headerlink, 207 | h3:hover > a.headerlink, 208 | h4:hover > a.headerlink, 209 | h5:hover > a.headerlink, 210 | h6:hover > a.headerlink, 211 | dt:hover > a.headerlink { 212 | visibility: visible; 213 | } 214 | 215 | img { 216 | border: 0; 217 | } 218 | 219 | div.admonition { 220 | margin-top: 10px; 221 | margin-bottom: 10px; 222 | padding: 2px 7px 1px 7px; 223 | border-left: 0.2em solid black; 224 | } 225 | 226 | p.admonition-title { 227 | margin: 0px 10px 5px 0px; 228 | font-weight: bold; 229 | } 230 | 231 | dt:target, .highlighted { 232 | background-color: #fbe54e; 233 | } 234 | 235 | /* Header */ 236 | 237 | div.header { 238 | padding-top: 10px; 239 | padding-bottom: 10px; 240 | } 241 | 242 | div.header h1 { 243 | font-weight: normal; 244 | font-size: 180%; 245 | letter-spacing: .08em; 246 | } 247 | 248 | div.header h1 a { 249 | color: white; 250 | } 251 | 252 | div.header div.rel { 253 | margin-top: 1em; 254 | } 255 | 256 | div.header div.rel a { 257 | color: white; 258 | letter-spacing: .1em; 259 | text-transform: uppercase; 260 | font-family: "FluxBold", "Arial", "Liberation Sans", "DejaVu Sans", "sans-serif"; 261 | } 262 | 263 | p.logo { 264 | float: right; 265 | } 266 | 267 | img.logo { 268 | border: 0; 269 | } 270 | 271 | 272 | /* Content */ 273 | div.content-wrapper { 274 | background-color: white; 275 | padding-top: 20px; 276 | padding-bottom: 20px; 277 | } 278 | 279 | div.document { 280 | width: 40em; 281 | float: left; 282 | } 283 | 284 | div.body { 285 | padding-right: 2em; 286 | text-align: justify; 287 | } 288 | 289 | div.document ul { 290 | margin: 0 1.5em; 291 | list-style-type: square; 292 | } 293 | 294 | div.document dd { 295 | margin-left: 1.2em; 296 | margin-top: .4em; 297 | margin-bottom: 1em; 298 | } 299 | 300 | div.document .section { 301 | margin-top: 1.7em; 302 | } 303 | div.document .section:first-child { 304 | margin-top: 0px; 305 | } 306 | 307 | div.document div.highlight { 308 | padding: 3px; 309 | background-color: #eeeeec; 310 | border-top: 2px solid #dddddd; 311 | border-bottom: 2px solid #dddddd; 312 | margin-top: .8em; 313 | margin-bottom: .8em; 314 | } 315 | 316 | div.document h2 { 317 | margin-top: .7em; 318 | } 319 | 320 | div.document p { 321 | margin-bottom: .5em; 322 | } 323 | 324 | div.document li.toctree-l1 { 325 | margin-bottom: .5em; 326 | } 327 | 328 | div.document .descname { 329 | font-weight: bold; 330 | } 331 | 332 | div.document .docutils.literal { 333 | padding: 1px; 334 | font-family: "DejaVuSansMono", "Courier New", "Courier", "monospace"; 335 | font-size: 91%; 336 | } 337 | 338 | div.document h1 .docutils.literal, 339 | div.document h2 .docutils.literal, 340 | div.document h3 .docutils.literal, 341 | div.document h4 .docutils.literal, 342 | div.document h5 .docutils.literal { 343 | background-color: white; 344 | padding: 1px; 345 | font-family: "DejaVuSansMono", "Courier New", "Courier", "monospace"; 346 | font-size: 84%; 347 | } 348 | 349 | div.document .docutils.xref.literal { 350 | background-color: transparent; 351 | padding: 0px; 352 | } 353 | 354 | div.document blockquote { 355 | margin: 1em; 356 | } 357 | 358 | div.document ol { 359 | margin: 1.5em; 360 | } 361 | 362 | p.rubric { 363 | border-bottom: 1px solid black; 364 | } 365 | 366 | a.footnote-reference { 367 | vertical-align: super; 368 | font-size: 75%; 369 | } 370 | 371 | 372 | /* Sidebar */ 373 | 374 | div.sidebar { 375 | width: 22em; 376 | float: right; 377 | font-size: .9em; 378 | } 379 | 380 | div.sidebar a, div.header a { 381 | text-decoration: none; 382 | } 383 | 384 | div.sidebar a:hover, div.header a:hover { 385 | text-decoration: underline; 386 | } 387 | 388 | div.sidebar h3 { 389 | color: #2e3436; 390 | text-transform: uppercase; 391 | font-size: 130%; 392 | letter-spacing: .1em; 393 | } 394 | 395 | div.sidebar ul { 396 | list-style-type: none; 397 | } 398 | 399 | div.sidebar li.toctree-l1 a { 400 | display: block; 401 | padding: 1px; 402 | border: 1px solid #dddddd; 403 | background-color: #eeeeec; 404 | margin-bottom: .4em; 405 | padding-left: 3px; 406 | color: #2e3436; 407 | } 408 | 409 | div.sidebar li.toctree-l2 a { 410 | background-color: transparent; 411 | border: none; 412 | margin-left: 1em; 413 | border-bottom: 1px solid #dddddd; 414 | } 415 | 416 | div.sidebar li.toctree-l3 a { 417 | background-color: transparent; 418 | border: none; 419 | margin-left: 2em; 420 | border-bottom: 1px solid #dddddd; 421 | } 422 | 423 | div.sidebar li.toctree-l2:last-child a { 424 | border-bottom: none; 425 | } 426 | 427 | div.sidebar li.toctree-l1.current a { 428 | border-right: 5px solid #fcaf3e; 429 | } 430 | 431 | div.sidebar li.toctree-l1.current li.toctree-l2 a { 432 | border-right: none; 433 | } 434 | 435 | table.docutils { 436 | border-collapse: collapse; 437 | border: 2px solid; 438 | } 439 | 440 | table.docutils thead, 441 | table.docutils tbody { 442 | border-bottom: 2px solid; 443 | } 444 | 445 | table.docutils th { 446 | border: 2px solid; 447 | padding: 3px; 448 | font-family: "FluxBold", "Arial", "Liberation Sans", "DejaVu Sans", "sans-serif"; 449 | font-size: 18px; 450 | } 451 | 452 | table.docutils td { 453 | border-right: 2px solid; 454 | padding: 3px; 455 | border-bottom: none; 456 | border-top: none; 457 | } 458 | 459 | 460 | 461 | table.footnote, 462 | table.footnote thead, 463 | table.footnote tbody, 464 | table.footnote th, 465 | table.footnote td { 466 | border: none; 467 | } 468 | 469 | /* Footer */ 470 | 471 | div.footer-wrapper { 472 | background: #FFCC33; 473 | border-top: 4px solid #babdb6; 474 | padding-top: 10px; 475 | padding-bottom: 10px; 476 | min-height: 80px; 477 | } 478 | 479 | div.footer, div.footer a { 480 | color: black; 481 | } 482 | 483 | div.footer .right { 484 | text-align: right; 485 | } 486 | 487 | div.footer .left { 488 | text-transform: uppercase; 489 | font-family: "FluxBold", "Arial", "Liberation Sans", "DejaVu Sans", "sans-serif"; 490 | } 491 | 492 | 493 | /* Styles copied from basic theme */ 494 | 495 | img.align-left, .figure.align-left, object.align-left { 496 | clear: left; 497 | float: left; 498 | margin-right: 1em; 499 | } 500 | 501 | img.align-right, .figure.align-right, object.align-right { 502 | clear: right; 503 | float: right; 504 | margin-left: 1em; 505 | } 506 | 507 | img.align-center, .figure.align-center, object.align-center { 508 | display: block; 509 | margin-left: auto; 510 | margin-right: auto; 511 | } 512 | 513 | .align-left { 514 | text-align: left; 515 | } 516 | 517 | .align-center { 518 | clear: both; 519 | text-align: center; 520 | } 521 | 522 | .align-right { 523 | text-align: right; 524 | } 525 | 526 | /* -- search page ----------------------------------------------------------- */ 527 | 528 | ul.search { 529 | margin: 10px 0 0 20px; 530 | padding: 0; 531 | } 532 | 533 | ul.search li { 534 | padding: 5px 0 5px 20px; 535 | background-image: url(file.png); 536 | background-repeat: no-repeat; 537 | background-position: 0 7px; 538 | } 539 | 540 | ul.search li a { 541 | font-weight: bold; 542 | } 543 | 544 | ul.search li div.context { 545 | color: #888; 546 | margin: 2px 0 0 30px; 547 | text-align: left; 548 | } 549 | 550 | ul.keywordmatches li.goodmatch a { 551 | font-weight: bold; 552 | } 553 | 554 | /* -- index page ------------------------------------------------------------ */ 555 | 556 | table.contentstable { 557 | width: 90%; 558 | } 559 | 560 | table.contentstable p.biglink { 561 | line-height: 150%; 562 | } 563 | 564 | a.biglink { 565 | font-size: 1.3em; 566 | } 567 | 568 | span.linkdescr { 569 | font-style: italic; 570 | padding-top: 5px; 571 | font-size: 90%; 572 | } 573 | 574 | /* -- general index --------------------------------------------------------- */ 575 | 576 | table.indextable td { 577 | text-align: left; 578 | vertical-align: top; 579 | } 580 | 581 | table.indextable dl, table.indextable dd { 582 | margin-top: 0; 583 | margin-bottom: 0; 584 | } 585 | 586 | table.indextable tr.pcap { 587 | height: 10px; 588 | } 589 | 590 | table.indextable tr.cap { 591 | margin-top: 10px; 592 | background-color: #f2f2f2; 593 | } 594 | 595 | img.toggler { 596 | margin-right: 3px; 597 | margin-top: 3px; 598 | cursor: pointer; 599 | } 600 | 601 | /* -- viewcode extension ---------------------------------------------------- */ 602 | 603 | .viewcode-link { 604 | float: right; 605 | } 606 | 607 | .viewcode-back { 608 | float: right; 609 | font-family:: "Verdana", Arial, sans-serif; 610 | } 611 | 612 | div.viewcode-block:target { 613 | margin: -1px -3px; 614 | padding: 0 3px; 615 | background-color: #f4debf; 616 | border-top: 1px solid #ac9; 617 | border-bottom: 1px solid #ac9; 618 | } 619 | -------------------------------------------------------------------------------- /source/_static/by-nc-sa.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/by-nc-sa.pdf -------------------------------------------------------------------------------- /source/_static/dejavusansmono-bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/dejavusansmono-bold-webfont.eot -------------------------------------------------------------------------------- /source/_static/dejavusansmono-bold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/dejavusansmono-bold-webfont.ttf -------------------------------------------------------------------------------- /source/_static/dejavusansmono-bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/dejavusansmono-bold-webfont.woff -------------------------------------------------------------------------------- /source/_static/dejavusansmono-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/dejavusansmono-webfont.eot -------------------------------------------------------------------------------- /source/_static/dejavusansmono-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/dejavusansmono-webfont.ttf -------------------------------------------------------------------------------- /source/_static/dejavusansmono-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/dejavusansmono-webfont.woff -------------------------------------------------------------------------------- /source/_static/fluxb___-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/fluxb___-webfont.eot -------------------------------------------------------------------------------- /source/_static/fluxb___-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/fluxb___-webfont.ttf -------------------------------------------------------------------------------- /source/_static/fluxb___-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/fluxb___-webfont.woff -------------------------------------------------------------------------------- /source/_static/texgyreschola-bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-bold-webfont.eot -------------------------------------------------------------------------------- /source/_static/texgyreschola-bold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-bold-webfont.ttf -------------------------------------------------------------------------------- /source/_static/texgyreschola-bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-bold-webfont.woff -------------------------------------------------------------------------------- /source/_static/texgyreschola-bolditalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-bolditalic-webfont.eot -------------------------------------------------------------------------------- /source/_static/texgyreschola-bolditalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-bolditalic-webfont.ttf -------------------------------------------------------------------------------- /source/_static/texgyreschola-bolditalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-bolditalic-webfont.woff -------------------------------------------------------------------------------- /source/_static/texgyreschola-italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-italic-webfont.eot -------------------------------------------------------------------------------- /source/_static/texgyreschola-italic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-italic-webfont.ttf -------------------------------------------------------------------------------- /source/_static/texgyreschola-italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-italic-webfont.woff -------------------------------------------------------------------------------- /source/_static/texgyreschola-regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-regular-webfont.eot -------------------------------------------------------------------------------- /source/_static/texgyreschola-regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-regular-webfont.ttf -------------------------------------------------------------------------------- /source/_static/texgyreschola-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_static/texgyreschola-regular-webfont.woff -------------------------------------------------------------------------------- /source/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | {% block header %} 3 |
4 |
5 | {%- if logo %} 6 | 9 | {%- endif %} 10 | {%- block headertitle %} 11 |

{{ shorttitle|e }}

12 | {%- endblock %} 13 |
14 | Contents | 15 | {%- for rellink in rellinks|reverse %} 16 | {{ rellink[3] }} 18 | {%- if not loop.last %}{{ reldelim2 }}{% endif %} 19 | {%- endfor %} 20 |
21 |
22 |
23 | {% endblock %} 24 | 25 | {%- block sidebartoc %} 26 | {%- endblock %} 27 | {%- block sidebarsearch %} 28 |
The book gets right to the point, without a lot of fluff and filler
29 | Doug Hellmann 30 |
A concise, well-organised and complete reference
31 | Richard Jones 32 |
33 |

{{ _('Search the book') }}

34 | 40 |

41 | {{ _('Enter search terms or a module, class or function name.') }} 42 |

43 | {%- endblock %} 44 | {%- block extrahead %} 45 | 46 | 47 | 48 | 49 | {% endblock %} 50 | 51 | {% block footer %} 52 | 86 | {% endblock %} 87 | -------------------------------------------------------------------------------- /source/_templates/sphinxcustom.cls: -------------------------------------------------------------------------------- 1 | % 2 | % sphinxmanual.cls for Sphinx (http://sphinx.pocoo.org/) 3 | % 4 | 5 | \NeedsTeXFormat{LaTeX2e}[1995/12/01] 6 | \ProvidesClass{sphinxcustom}[2010/12/07 Document class (Colliberty Custom)] 7 | 8 | % chapters starting at odd pages (overridden by 'openany' document option) 9 | \PassOptionsToClass{openright}{\sphinxdocclass} 10 | 11 | % 'oneside' option overriding the 'twoside' default 12 | \newif\if@oneside 13 | \DeclareOption{oneside}{\@onesidetrue} 14 | % Pass remaining document options to the parent class. 15 | \DeclareOption*{\PassOptionsToClass{\CurrentOption}{\sphinxdocclass}} 16 | \ProcessOptions\relax 17 | 18 | % Defaults two-side document 19 | \if@oneside 20 | % nothing to do (oneside is the default) 21 | \else 22 | \PassOptionsToClass{twoside}{\sphinxdocclass} 23 | \fi 24 | 25 | \LoadClass{\sphinxdocclass} 26 | 27 | % Set some sane defaults for section numbering depth and TOC depth. You can 28 | % reset these counters in your preamble. 29 | % 30 | \setcounter{secnumdepth}{2} 31 | \setcounter{tocdepth}{1} 32 | 33 | % Change the title page to look a bit better, and fit in with the fncychap 34 | % ``Bjarne'' style a bit better. 35 | % 36 | \renewcommand{\maketitle}{% 37 | \begin{titlepage}% 38 | \let\footnotesize\small 39 | \let\footnoterule\relax 40 | \rule{\textwidth}{1pt}% 41 | \ifsphinxpdfoutput 42 | \begingroup 43 | % This \def is required to deal with multi-line authors; it 44 | % changes \\ to ', ' (comma-space), making it pass muster for 45 | % generating document info in the PDF file. 46 | \def\\{, } 47 | \pdfinfo{ 48 | /Author (\@author) 49 | /Title (\@title) 50 | } 51 | \endgroup 52 | \fi 53 | \begin{flushright}% 54 | \sphinxlogo% 55 | {\rm\Huge\py@HeaderFamily \@title \par}% 56 | {\LARGE\py@HeaderFamily \@author \par} 57 | \end{flushright}%\par 58 | \clearpage% 59 | \py@authoraddress \par 60 | \vfill\vfill\vfill 61 | % ISBN 978-1-490-36222-9 62 | \par 63 | First edition, \py@release\releaseinfo \\* 64 | \@date \par 65 | \vfill 66 | Copyright © 2011--2015 Lennart Regebro \par 67 | \includegraphics[width=100pt,height=35pt]{by-nc-sa.pdf}\par 68 | This work is licensed under a Creative Commons 69 | Attribution-NonCommercial-ShareAlike 3.0 Unported License. 70 | To view a copy of this license, visit 71 | http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to 72 | Creative Commons, 171 Second Street, Suite 300, 73 | San Francisco, California, 94105, USA. 74 | 75 | \@thanks 76 | \end{titlepage}% 77 | \cleardoublepage% 78 | \setcounter{footnote}{0}% 79 | \let\thanks\relax\let\maketitle\relax 80 | %\gdef\@thanks{}\gdef\@author{}\gdef\@title{} 81 | } 82 | 83 | 84 | % Catch the end of the {abstract} environment, but here make sure the abstract 85 | % is followed by a blank page if the 'openright' option is used. 86 | % 87 | \let\py@OldEndAbstract=\endabstract 88 | \renewcommand{\endabstract}{ 89 | \if@openright 90 | \ifodd\value{page} 91 | \typeout{Adding blank page after the abstract.} 92 | \vfil\pagebreak 93 | \fi 94 | \fi 95 | \py@OldEndAbstract 96 | } 97 | 98 | % This wraps the \tableofcontents macro with all the magic to get the spacing 99 | % right and have the right number of pages if the 'openright' option has been 100 | % used. This eliminates a fair amount of crud in the individual document files. 101 | % 102 | \let\py@OldTableofcontents=\tableofcontents 103 | \renewcommand{\tableofcontents}{% 104 | \setcounter{page}{1}% 105 | \pagebreak% 106 | \pagestyle{plain}% 107 | {% 108 | \parskip = 15mm% 109 | \py@OldTableofcontents% 110 | \if@openright% 111 | \ifodd\value{page}% 112 | \typeout{Adding blank page after the table of contents.}% 113 | \pagebreak\hspace{0pt}% 114 | \fi% 115 | \fi% 116 | \cleardoublepage% 117 | }% 118 | \pagenumbering{arabic}% 119 | \@ifundefined{fancyhf}{}{\pagestyle{normal}}% 120 | } 121 | 122 | % This is needed to get the width of the section # area wide enough in the 123 | % library reference. Doing it here keeps it the same for all the manuals. 124 | % 125 | \renewcommand*\l@section{\@dottedtocline{1}{1.5em}{2.6em}} 126 | \renewcommand*\l@subsection{\@dottedtocline{2}{4.1em}{3.5em}} 127 | -------------------------------------------------------------------------------- /source/_tests/UTF-8.txt: -------------------------------------------------------------------------------- 1 | It wörks -------------------------------------------------------------------------------- /source/_tests/bites.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.version_info < (3,): 3 | class Bites(str): 4 | def __new__(cls, value): 5 | if isinstance(value[0], int): 6 | # It's a list of integers 7 | value = ''.join([chr(x) for x in value]) 8 | return super(Bites, cls).__new__(cls, value) 9 | 10 | def itemint(self, index): 11 | return ord(self[index]) 12 | 13 | def iterint(self): 14 | for x in self: 15 | yield ord(x) 16 | else: 17 | 18 | class Bites(bytes): 19 | def __new__(cls, value): 20 | if isinstance(value, str): 21 | # It's a unicode string: 22 | value = value.encode('ISO-8859-1') 23 | return super(Bites, cls).__new__(cls, value) 24 | 25 | def itemint(self, x): 26 | return self[x] 27 | 28 | def iterint(self): 29 | for x in self: 30 | yield x 31 | -------------------------------------------------------------------------------- /source/_tests/buffer25.txt: -------------------------------------------------------------------------------- 1 | >>> b = buffer('yay!') 2 | >>> assert len(b) == 4 3 | -------------------------------------------------------------------------------- /source/_tests/buffer26.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> if sys.version_info > (3,): 3 | ... buffer = memoryview 4 | >>> b = buffer('yay!'.encode()) 5 | >>> len(b) 6 | 4 7 | -------------------------------------------------------------------------------- /source/_tests/callable26.txt: -------------------------------------------------------------------------------- 1 | >>> hasattr(bool, '__call__') 2 | True 3 | -------------------------------------------------------------------------------- /source/_tests/callable30.txt: -------------------------------------------------------------------------------- 1 | >>> def afunction(): 2 | ... pass 3 | 4 | >>> any("__call__" in klass.__dict__ for 5 | ... klass in type(afunction).__mro__) 6 | True 7 | 8 | >>> import collections 9 | >>> isinstance(afunction, collections.Callable) 10 | True 11 | -------------------------------------------------------------------------------- /source/_tests/constantexample.py: -------------------------------------------------------------------------------- 1 | from foo import CONSTANT 2 | 3 | def afunction(alist): 4 | return [x * CONSTANT for x in alist] 5 | -------------------------------------------------------------------------------- /source/_tests/constanttarget.py: -------------------------------------------------------------------------------- 1 | from foo import get_constant 2 | 3 | def afunction(alist): 4 | return [x * get_constant() for x in alist] 5 | -------------------------------------------------------------------------------- /source/_tests/csv26.txt: -------------------------------------------------------------------------------- 1 | >>> import csvutil 2 | >>> with csvutil.UnicodeWriter('eggs.csv', delimiter=' ', quotechar='|') as writer: 3 | ... writer.writerow([u'Spam', u'Spam', u'Spam', u'Spam', u'Spam', u'B\xe4ked Beans']) 4 | ... writer.writerow([u'Spam', u'L\xf6vely Spam', u'Wonderf\xfcl Spam']) 5 | 6 | >>> with csvutil.UnicodeReader('eggs.csv', delimiter=' ', quotechar='|') as reader: 7 | ... [row for row in reader] 8 | [[u'Spam', u'Spam', u'Spam', u'Spam', u'Spam', u'B\xe4ked Beans'], [u'Spam', u'L\xf6vely Spam', u'Wonderf\xfcl Spam']] 9 | -------------------------------------------------------------------------------- /source/_tests/csv30.txt: -------------------------------------------------------------------------------- 1 | >>> import csvutil 2 | >>> with csvutil.UnicodeWriter('eggs.csv', delimiter=' ', quotechar='|') as writer: 3 | ... writer.writerow(['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'B\xe4ked Beans']) 4 | ... writer.writerow(['Spam', 'L\xf6vely Spam', 'Wonderf\xfcl Spam']) 5 | 6 | >>> with csvutil.UnicodeReader('eggs.csv', delimiter=' ', quotechar='|') as reader: 7 | ... [row for row in reader] 8 | [['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'B\xe4ked Beans'], ['Spam', 'L\xf6vely Spam', 'Wonderf\xfcl Spam']] 9 | -------------------------------------------------------------------------------- /source/_tests/csvutil.py: -------------------------------------------------------------------------------- 1 | import sys, csv, codecs 2 | 3 | PY3 = sys.version_info > (3,) 4 | 5 | class UnicodeReader: 6 | def __init__(self, filename, dialect=csv.excel, 7 | encoding="utf-8", **kw): 8 | self.filename = filename 9 | self.dialect = dialect 10 | self.encoding = encoding 11 | self.kw = kw 12 | 13 | def __enter__(self): 14 | if PY3: 15 | self.f = open(self.filename, 'rt', 16 | encoding=self.encoding, newline='') 17 | else: 18 | self.f = open(self.filename, 'rb') 19 | self.reader = csv.reader(self.f, dialect=self.dialect, 20 | **self.kw) 21 | return self 22 | 23 | def __exit__(self, type, value, traceback): 24 | self.f.close() 25 | 26 | def next(self): 27 | row = next(self.reader) 28 | if PY3: 29 | return row 30 | return [s.decode(self.encoding) for s in row] 31 | 32 | __next__ = next 33 | 34 | def __iter__(self): 35 | return self 36 | 37 | class UnicodeWriter: 38 | def __init__(self, filename, dialect=csv.excel, 39 | encoding="utf-8", **kw): 40 | self.filename = filename 41 | self.dialect = dialect 42 | self.encoding = encoding 43 | self.kw = kw 44 | 45 | def __enter__(self): 46 | if PY3: 47 | self.f = open(self.filename, 'wt', 48 | encoding=self.encoding, newline='') 49 | else: 50 | self.f = open(self.filename, 'wb') 51 | self.writer = csv.writer(self.f, dialect=self.dialect, 52 | **self.kw) 53 | return self 54 | 55 | def __exit__(self, type, value, traceback): 56 | self.f.close() 57 | 58 | def writerow(self, row): 59 | if not PY3: 60 | row = [s.encode(self.encoding) for s in row] 61 | self.writer.writerow(row) 62 | 63 | def writerows(self, rows): 64 | for row in rows: 65 | self.writerow(row) 66 | -------------------------------------------------------------------------------- /source/_tests/dict26.txt: -------------------------------------------------------------------------------- 1 | >>> d = {'key1': 'value1', 2 | ... 'key2': 'value2', 3 | ... 'key3': 'value3', 4 | ... } 5 | 6 | >>> try: 7 | ... values = d.itervalues() 8 | ... except AttributeError: 9 | ... values = d.values() 10 | 11 | >>> isinstance(values, list) 12 | False 13 | 14 | >>> for value in values: 15 | ... print(value) 16 | value3 17 | value2 18 | value1 19 | -------------------------------------------------------------------------------- /source/_tests/division26.txt: -------------------------------------------------------------------------------- 1 | >>> from __future__ import division 2 | >>> 1/2 3 | 0.5 4 | -------------------------------------------------------------------------------- /source/_tests/doctest-checker.txt: -------------------------------------------------------------------------------- 1 | import re, sys 2 | from doctest import OutputChecker, DocTestSuite 3 | 4 | class Py23DocChecker(OutputChecker): 5 | def check_output(self, want, got, optionflags): 6 | if sys.version_info[0] < 3: 7 | # if running on py2, attempt to prefix all the strings 8 | # with "u" to signify that they're unicode literals 9 | want = re.sub("'(.*?)'", "u'\\1'", want) 10 | return OutputChecker.check_output(self, want, got, optionflags) 11 | 12 | def load_tests(loader, tests, ignore): 13 | tests.addTests(DocTestSuite(mymodule, checker=Py23DocChecker())) 14 | return tests 15 | -------------------------------------------------------------------------------- /source/_tests/doctest-unicode-fail.txt: -------------------------------------------------------------------------------- 1 | >>> readfile() 2 | 'Hello' 3 | -------------------------------------------------------------------------------- /source/_tests/doctest-unicode.txt: -------------------------------------------------------------------------------- 1 | >>> readfile() == 'Hello' 2 | True 3 | -------------------------------------------------------------------------------- /source/_tests/exception_syntax26.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> try: 3 | ... raise Exception("Something happened") 4 | ... except Exception: 5 | ... e = sys.exc_info()[1] 6 | ... print(e.args[0]) 7 | Something happened 8 | -------------------------------------------------------------------------------- /source/_tests/exceptions25.txt: -------------------------------------------------------------------------------- 1 | >>> e = Exception('arg1', 'arg2') 2 | >>> e[1] 3 | 'arg2' 4 | >>> for a in e: 5 | ... print a 6 | ... 7 | arg1 8 | arg2 9 | -------------------------------------------------------------------------------- /source/_tests/exceptions26.txt: -------------------------------------------------------------------------------- 1 | >>> e = Exception('arg1', 'arg2') 2 | >>> e.args[1] 3 | 'arg2' 4 | >>> for a in e.args: 5 | ... print a 6 | ... 7 | arg1 8 | arg2 9 | -------------------------------------------------------------------------------- /source/_tests/exec25.txt: -------------------------------------------------------------------------------- 1 | >>> g_dict={} 2 | >>> l_dict={} 3 | >>> exec "v = 3" in g_dict, l_dict 4 | >>> l_dict['v'] 5 | 3 6 | -------------------------------------------------------------------------------- /source/_tests/exec26.txt: -------------------------------------------------------------------------------- 1 | >>> exec("v = 3") 2 | >>> v 3 | 3 4 | -------------------------------------------------------------------------------- /source/_tests/exec30.txt: -------------------------------------------------------------------------------- 1 | >>> g_dict={} 2 | >>> l_dict={} 3 | >>> exec("v = 3", g_dict, l_dict) 4 | >>> l_dict['v'] 5 | 3 6 | -------------------------------------------------------------------------------- /source/_tests/fix_constant.py: -------------------------------------------------------------------------------- 1 | from lib2to3.fixer_base import BaseFix 2 | from lib2to3.fixer_util import Call, Name, is_probably_builtin 3 | from lib2to3.patcomp import PatternCompiler 4 | 5 | class FixConstant(BaseFix): 6 | 7 | PATTERN = """ 8 | import_name< 'import' modulename='foo' > 9 | | 10 | import_name< 'import' dotted_as_name< 'foo' 'as' 11 | modulename=any > > 12 | | 13 | import_from< 'from' 'foo' 'import' 14 | importname='CONSTANT' > 15 | | 16 | import_from< 'from' 'foo' 'import' import_as_name< 17 | importname='CONSTANT' 'as' constantname=any > > 18 | | 19 | any 20 | """ 21 | 22 | def start_tree(self, tree, filename): 23 | super(FixConstant, self).start_tree(tree, filename) 24 | # Reset the patterns attribute for every file: 25 | self.usage_patterns = [] 26 | 27 | def match(self, node): 28 | # Match the import patterns: 29 | results = {"node": node} 30 | match = self.pattern.match(node, results) 31 | 32 | if match and 'constantname' in results: 33 | # This is an "from import as" 34 | constantname = results['constantname'].value 35 | # Add a pattern to fix the usage of the constant 36 | # under this name: 37 | self.usage_patterns.append( 38 | PatternCompiler().compile_pattern( 39 | "constant='%s'"%constantname)) 40 | return results 41 | 42 | if match and 'importname' in results: 43 | # This is a "from import" without "as". 44 | # Add a pattern to fix the usage of the constant 45 | # under it's standard name: 46 | self.usage_patterns.append( 47 | PatternCompiler().compile_pattern( 48 | "constant='CONSTANT'")) 49 | return results 50 | 51 | if match and 'modulename' in results: 52 | # This is a "import as" 53 | modulename = results['modulename'].value 54 | # Add a pattern to fix the usage as an attribute: 55 | self.usage_patterns.append( 56 | PatternCompiler().compile_pattern( 57 | "power< '%s' trailer< '.' " \ 58 | "attribute='CONSTANT' > >" % modulename)) 59 | return results 60 | 61 | # Now do the usage patterns 62 | for pattern in self.usage_patterns: 63 | if pattern.match(node, results): 64 | return results 65 | 66 | def transform(self, node, results): 67 | if 'importname' in results: 68 | # Change the import from CONSTANT to get_constant: 69 | node = results['importname'] 70 | node.value = 'get_constant' 71 | node.changed() 72 | 73 | if 'constant' in results or 'attribute' in results: 74 | if 'attribute' in results: 75 | # Here it's used as an attribute. 76 | node = results['attribute'] 77 | else: 78 | # Here it's used standalone. 79 | node = results['constant'] 80 | # Assert that it really is standalone and not 81 | # an attribute of something else, or an 82 | # assignment etc: 83 | if not is_probably_builtin(node): 84 | return None 85 | 86 | # Now we replace the earlier constant name with the 87 | # new function call. If it was renamed on import 88 | # from 'CONSTANT' we keep the renaming else we 89 | # replace it with the new 'get_constant' name: 90 | name = node.value 91 | if name == 'CONSTANT': 92 | name = 'get_constant' 93 | node.replace(Call(Name(name), prefix=node.prefix)) 94 | -------------------------------------------------------------------------------- /source/_tests/fix_indent.py: -------------------------------------------------------------------------------- 1 | from lib2to3.fixer_base import BaseFix 2 | from lib2to3.fixer_util import Leaf 3 | from lib2to3.pgen2 import token 4 | 5 | class FixIndent(BaseFix): 6 | 7 | indents = [] 8 | line = 0 9 | 10 | def match(self, node): 11 | if isinstance(node, Leaf): 12 | return True 13 | return False 14 | 15 | def transform(self, node, results): 16 | if node.type == token.INDENT: 17 | self.line = node.lineno 18 | # Tabs count like 8 spaces. 19 | indent = len(node.value.replace('\t', ' ' * 8)) 20 | self.indents.append(indent) 21 | # Replace this indentation with 4 spaces per level: 22 | new_indent = ' ' * 4 * len(self.indents) 23 | if node.value != new_indent: 24 | node.value = new_indent 25 | # Return the modified node: 26 | return node 27 | elif node.type == token.DEDENT: 28 | self.line = node.lineno 29 | if node.column == 0: 30 | # Complete outdent, reset: 31 | self.indents = [] 32 | else: 33 | # Partial outdent, we find the indentation 34 | # level and drop higher indents. 35 | level = self.indents.index(node.column) 36 | self.indents = self.indents[:level+1] 37 | if node.prefix: 38 | # During INDENT's the indentation level is 39 | # in the value. However, during OUTDENT's 40 | # the value is an empty string and then 41 | # indentation level is instead in the last 42 | # line of the prefix. So we remove the last 43 | # line of the prefix and add the correct 44 | # indententation as a new last line. 45 | prefix_lines = node.prefix.split('\n')[:-1] 46 | prefix_lines.append(' ' * 4 * 47 | len(self.indents)) 48 | new_prefix = '\n'.join(prefix_lines) 49 | if node.prefix != new_prefix: 50 | node.prefix = new_prefix 51 | # Return the modified node: 52 | return node 53 | elif self.line != node.lineno: 54 | self.line = node.lineno 55 | # New line! 56 | if not self.indents: 57 | # First line. Do nothing: 58 | return None 59 | else: 60 | # Continues the same indentation 61 | if node.prefix: 62 | # This lines intentation is the last line 63 | # of the prefix, as during DEDENTS. Remove 64 | # the old indentation and add the correct 65 | # indententation as a new last line. 66 | prefix_lines = node.prefix.split('\n')[:-1] 67 | prefix_lines.append(' ' * 4 * 68 | len(self.indents)) 69 | new_prefix = '\n'.join(prefix_lines) 70 | if node.prefix != new_prefix: 71 | node.prefix = new_prefix 72 | # Return the modified node: 73 | return node 74 | 75 | # Nothing was modified: Return None 76 | return None 77 | -------------------------------------------------------------------------------- /source/_tests/fix_name1.py: -------------------------------------------------------------------------------- 1 | from lib2to3.fixer_base import BaseFix 2 | from lib2to3.pgen2 import token 3 | 4 | class FixName1(BaseFix): 5 | 6 | _accept_type = token.NAME 7 | 8 | def match(self, node): 9 | if node.value == 'oldname': 10 | return True 11 | return False 12 | 13 | def transform(self, node, results): 14 | node.value = 'newname' 15 | node.changed() 16 | -------------------------------------------------------------------------------- /source/_tests/fix_name2.py: -------------------------------------------------------------------------------- 1 | from lib2to3.fixer_base import BaseFix 2 | from lib2to3.fixer_util import Name 3 | 4 | class FixName2(BaseFix): 5 | 6 | PATTERN = "fixnode='oldname'" 7 | 8 | def transform(self, node, results): 9 | fixnode = results['fixnode'] 10 | fixnode.replace(Name('newname', prefix=fixnode.prefix)) 11 | -------------------------------------------------------------------------------- /source/_tests/getitem26.txt: -------------------------------------------------------------------------------- 1 | >>> class StrawberryTart(object): 2 | ... 3 | ... def __getitem__(self, n): 4 | ... """An example of how to use slice objects""" 5 | ... if isinstance(n, slice): 6 | ... # Expand the slice object using range() 7 | ... # to a maximum of eight items. 8 | ... return [self[x] for x in 9 | ... range(*n.indices(8))] 10 | ... 11 | ... # Return one item of the tart 12 | ... return 'A slice of StrawberryTart with ' \ 13 | ... 'not so much rat in it.' 14 | ... 15 | >>> tart = StrawberryTart() 16 | >>> tart[5:6] 17 | ['A slice of StrawberryTart with not so much rat in it.'] 18 | -------------------------------------------------------------------------------- /source/_tests/input26.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... input = raw_input 3 | ... except NameError: 4 | ... pass 5 | 6 | >>> input('Type in a string: ') 7 | Type in a string: It works! 8 | 'It works!' 9 | -------------------------------------------------------------------------------- /source/_tests/intern26.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... from sys import intern 3 | ... except ImportError: 4 | ... pass 5 | 6 | ... something = intern('something') 7 | 'something' 8 | -------------------------------------------------------------------------------- /source/_tests/inttypes26.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> if sys.version_info < (3,): 3 | ... integer_types = (int, long,) 4 | ... else: 5 | ... integer_types = (int,) 6 | >>> isinstance(1, integer_types) 7 | True 8 | -------------------------------------------------------------------------------- /source/_tests/long25.txt: -------------------------------------------------------------------------------- 1 | >>> 1L 2 | 1L 3 | >>> long(1) 4 | 1L 5 | -------------------------------------------------------------------------------- /source/_tests/long26.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> if sys.version_info > (3,): 3 | ... long = int 4 | >>> long(1) 5 | 1L 6 | -------------------------------------------------------------------------------- /source/_tests/makebytes.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.version_info < (3,): 3 | def b(x): 4 | return x 5 | else: 6 | import codecs 7 | def b(x): 8 | return codecs.latin_1_encode(x)[0] 9 | -------------------------------------------------------------------------------- /source/_tests/makeunicode.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.version_info < (3,): 3 | import codecs 4 | def u(x): 5 | return codecs.unicode_escape_decode(x)[0] 6 | else: 7 | def u(x): 8 | return x 9 | -------------------------------------------------------------------------------- /source/_tests/map2.py: -------------------------------------------------------------------------------- 1 | from itertools import starmap, zip_longest 2 | 3 | def map(func, *iterables): 4 | zipped = zip_longest(*iterables) 5 | if func is None: 6 | # No need for a NOOP lambda here 7 | return zipped 8 | return starmap(func, zipped) 9 | -------------------------------------------------------------------------------- /source/_tests/map25.txt: -------------------------------------------------------------------------------- 1 | >>> def fun(a, b): 2 | ... if b is not None: 3 | ... return a - b 4 | ... return -a 5 | >>> map(fun, range(5), [3,2,1]) 6 | [-3, -1, 1, -3, -4] 7 | -------------------------------------------------------------------------------- /source/_tests/map26.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> if sys.version_info > (3,): 3 | ... from map2 import map 4 | >>> def fun(a, b): 5 | ... if b is not None: 6 | ... return a - b 7 | ... return -a 8 | >>> list(map(fun, range(5), [3,2,1])) 9 | [-3, -1, 1, -3, -4] 10 | -------------------------------------------------------------------------------- /source/_tests/map30.txt: -------------------------------------------------------------------------------- 1 | >>> from itertools import starmap, zip_longest 2 | >>> def fun(a, b): 3 | ... if b is not None: 4 | ... return a - b 5 | ... return -a 6 | >>> list(starmap(fun, zip_longest(range(5), [3,2,1]))) 7 | [-3, -1, 1, -3, -4] 8 | -------------------------------------------------------------------------------- /source/_tests/maybe_a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/maybe_a.gif -------------------------------------------------------------------------------- /source/_tests/mixin.py: -------------------------------------------------------------------------------- 1 | class ComparableMixin(object): 2 | def _compare(self, other, method): 3 | try: 4 | return method(self._cmpkey(), other._cmpkey()) 5 | except (AttributeError, TypeError): 6 | # _cmpkey not implemented, or return different type, 7 | # so I can't compare with "other". 8 | return NotImplemented 9 | 10 | def __lt__(self, other): 11 | return self._compare(other, lambda s, o: s < o) 12 | 13 | def __le__(self, other): 14 | return self._compare(other, lambda s, o: s <= o) 15 | 16 | def __eq__(self, other): 17 | return self._compare(other, lambda s, o: s == o) 18 | 19 | def __ge__(self, other): 20 | return self._compare(other, lambda s, o: s >= o) 21 | 22 | def __gt__(self, other): 23 | return self._compare(other, lambda s, o: s > o) 24 | 25 | def __ne__(self, other): 26 | return self._compare(other, lambda s, o: s != o) 27 | -------------------------------------------------------------------------------- /source/_tests/pythons.txt: -------------------------------------------------------------------------------- 1 | Eric 2 | Terry 3 | John 4 | Graham 5 | Michael 6 | Terry 7 | -------------------------------------------------------------------------------- /source/_tests/raise25.txt: -------------------------------------------------------------------------------- 1 | >>> raise "This is a string exception", 3, traceback 2 | Traceback (most recent call last): 3 | File "", line 1, in 4 | This is a string exception: 3 5 | 6 | >>> raise Exception, "Something happened", traceback 7 | Traceback (most recent call last): 8 | File "", line 1, in 9 | Exception: Something happened 10 | 11 | >>> raise Exception("Something happened"), None, traceback 12 | Traceback (most recent call last): 13 | File "", line 1, in 14 | Exception: Something happened 15 | -------------------------------------------------------------------------------- /source/_tests/raise30.txt: -------------------------------------------------------------------------------- 1 | >>> raise Exception("Something happened").with_traceback(traceback) 2 | Traceback (most recent call last): 3 | File "", line 1, in 4 | Exception: Something happened 5 | -------------------------------------------------------------------------------- /source/_tests/renames26.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> if sys.version_info < (3,): 3 | ... defaults_attr = 'func_defaults' 4 | ... get_method_class = lambda x: x.im_class 5 | ... else: 6 | ... defaults_attr = '__defaults__' 7 | ... get_method_class = lambda x: x.__self__.__class__ 8 | >>> class Test(object): 9 | ... def hasdefaults(a=1, b=2): 10 | ... pass 11 | 12 | >>> method = Test().hasdefaults 13 | >>> getattr(method, defaults_attr) 14 | (1, 2) 15 | 16 | >>> get_method_class(method) 17 | 18 | -------------------------------------------------------------------------------- /source/_tests/repr25.txt: -------------------------------------------------------------------------------- 1 | >>> `sorted` 2 | '' 3 | 4 | >>> `2+3` 5 | '5' 6 | -------------------------------------------------------------------------------- /source/_tests/repr26.txt: -------------------------------------------------------------------------------- 1 | >>> repr(sorted) 2 | '' 3 | 4 | >>> repr(2+3) 5 | '5' 6 | -------------------------------------------------------------------------------- /source/_tests/round24.txt: -------------------------------------------------------------------------------- 1 | >>> round(1.5) 2 | 2.0 3 | >>> round(2.5) 4 | 3.0 5 | >>> round(10.0/3, 0) 6 | 3.0 7 | -------------------------------------------------------------------------------- /source/_tests/round26.txt: -------------------------------------------------------------------------------- 1 | >>> import math 2 | >>> def my_round(x, d=0): 3 | ... p = 10 ** d 4 | ... if x > 0: 5 | ... return float(math.floor((x * p) + 0.5))/p 6 | ... else: 7 | ... return float(math.ceil((x * p) - 0.5))/p 8 | 9 | >>> my_round(1.5) 10 | 2.0 11 | >>> my_round(2.5) 12 | 3.0 13 | >>> my_round(10.0/3, 0) 14 | 3.0 15 | -------------------------------------------------------------------------------- /source/_tests/round30.txt: -------------------------------------------------------------------------------- 1 | >>> round(1.5) 2 | 2 3 | >>> round(2.5) 4 | 2 5 | >>> round(10.0/3, 0) 6 | 3.0 7 | -------------------------------------------------------------------------------- /source/_tests/shouldraise.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def shouldRaise(eclass, method, *args, **kw): 4 | try: 5 | method(*args, **kw) 6 | except: 7 | e = sys.exc_info()[1] 8 | if not isinstance(e, eclass): 9 | raise 10 | return 11 | raise Exception("Expected exception %s not raised" % 12 | str(eclass)) 13 | -------------------------------------------------------------------------------- /source/_tests/test-1.1.txt: -------------------------------------------------------------------------------- 1 | >>> with_ = True 2 | >>> as_ = False -------------------------------------------------------------------------------- /source/_tests/test-1.10.txt: -------------------------------------------------------------------------------- 1 | >>> dict = {'Adam': 'Eve', 'John': 'Yoko', 'Donald': 'Daisy'} 2 | >>> for x in dict: 3 | ... print '%s + %s == True' % (x, dict[x]) 4 | Donald + Daisy == True 5 | John + Yoko == True 6 | Adam + Eve == True 7 | -------------------------------------------------------------------------------- /source/_tests/test-1.11.txt: -------------------------------------------------------------------------------- 1 | >>> import urllib2 2 | >>> urllib2.url2pathname('/tmp/A%20file') 3 | '/tmp/A file' 4 | -------------------------------------------------------------------------------- /source/_tests/test-1.12.txt: -------------------------------------------------------------------------------- 1 | >>> 5//2 2 | 2 3 | 4 | >>> 5.0//2.0 5 | 2.0 6 | -------------------------------------------------------------------------------- /source/_tests/test-1.2.txt: -------------------------------------------------------------------------------- 1 | >>> raise "Something went wrong!" 2 | Traceback (most recent call last): 3 | ... 4 | Something went wrong! 5 | -------------------------------------------------------------------------------- /source/_tests/test-1.3.txt: -------------------------------------------------------------------------------- 1 | >>> raise Exception("Something went wrong!") 2 | Traceback (most recent call last): 3 | ... 4 | Exception: Something went wrong! 5 | -------------------------------------------------------------------------------- /source/_tests/test-1.4.txt: -------------------------------------------------------------------------------- 1 | >>> 5/2 2 | 2 3 | -------------------------------------------------------------------------------- /source/_tests/test-1.5.txt: -------------------------------------------------------------------------------- 1 | >>> 5/2 2 | 2.5 3 | -------------------------------------------------------------------------------- /source/_tests/test-1.6.txt: -------------------------------------------------------------------------------- 1 | >>> 5/2.0 2 | 2.5 3 | 4 | >>> a = 5 5 | >>> b = 2 6 | >>> float(a)/b 7 | 2.5 8 | -------------------------------------------------------------------------------- /source/_tests/test-1.7.txt: -------------------------------------------------------------------------------- 1 | >>> from __future__ import division 2 | >>> 5/2 3 | 2.5 4 | -------------------------------------------------------------------------------- /source/_tests/test-1.8.txt: -------------------------------------------------------------------------------- 1 | >>> dict = {'Adam': 'Eve', 'John': 'Yoko', 'Donald': 'Daisy'} 2 | >>> dict.keys() 3 | ['Donald', 'John', 'Adam'] 4 | 5 | >>> dict.iterkeys() # doctest: +ELLIPSIS 6 | 7 | -------------------------------------------------------------------------------- /source/_tests/test-1.9.txt: -------------------------------------------------------------------------------- 1 | >>> dict = {'Adam': 'Eve', 'John': 'Yoko', 'Donald': 'Daisy'} 2 | >>> dict.keys()[0] 3 | 'Donald' 4 | -------------------------------------------------------------------------------- /source/_tests/test-2.1.txt: -------------------------------------------------------------------------------- 1 | >>> import os, sys, subprocess, shutil 2 | >>> curdir = os.path.abspath(os.curdir) 3 | >>> os.chdir('./test-2.1') 4 | >>> proc = subprocess.Popen([sys.executable, 'setup.py', 'build'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 5 | >>> print(proc.stdout.read().decode()) 6 | running build 7 | 8 | 9 | >>> print(proc.stderr.read().decode()) 10 | 11 | 12 | >>> if os.path.exists('build'): 13 | ... shutil.rmtree('build') 14 | >>> os.chdir(curdir) 15 | -------------------------------------------------------------------------------- /source/_tests/test-2.1/docs/CHANGES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.1/docs/CHANGES.txt -------------------------------------------------------------------------------- /source/_tests/test-2.1/docs/README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.1/docs/README.txt -------------------------------------------------------------------------------- /source/_tests/test-2.1/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | readme = open('docs/README.txt', 'rt').read() 4 | changes = open('docs/CHANGES.txt', 'rt').read() 5 | 6 | setup(name='Supporting Python 3 examples', 7 | version="1.0", 8 | description="An example project for Supporting Python 3", 9 | long_description=readme + '\n' + changes, 10 | classifiers=[ 11 | "Programming Language :: Python :: 2", 12 | "Topic :: Software Development :: Documentation"], 13 | keywords='python3 porting documentation examples', 14 | author='Lennart Regebro', 15 | author_email='regebro@gmail.com', 16 | license='GPL', 17 | packages=find_packages(exclude=['ez_setup']), 18 | include_package_data=True) 19 | -------------------------------------------------------------------------------- /source/_tests/test-2.2.txt: -------------------------------------------------------------------------------- 1 | >>> import os, sys, subprocess, shutil 2 | >>> curdir = os.path.abspath(os.curdir) 3 | >>> os.chdir('./test-2.2') 4 | >>> proc = subprocess.Popen([sys.executable, 'setup.py', 'test'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 5 | >>> print(proc.stdout.read().decode()) 6 | running test 7 | ... 8 | running build_ext 9 | 10 | 11 | >>> print(proc.stderr.read().decode()) 12 | test_2to3 (py3example.tests.TestCase1) ... ok 13 | 14 | ---------------------------------------------------------------------- 15 | Ran 1 test in 0.000s 16 | 17 | OK 18 | 19 | 20 | >>> if os.path.exists('build'): 21 | ... shutil.rmtree('build') 22 | >>> os.chdir(curdir) 23 | -------------------------------------------------------------------------------- /source/_tests/test-2.2/docs/CHANGES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.2/docs/CHANGES.txt -------------------------------------------------------------------------------- /source/_tests/test-2.2/docs/README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.2/docs/README.txt -------------------------------------------------------------------------------- /source/_tests/test-2.2/py3example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.2/py3example/__init__.py -------------------------------------------------------------------------------- /source/_tests/test-2.2/py3example/tests.py: -------------------------------------------------------------------------------- 1 | import unittest, doctest, StringIO 2 | 3 | class TestCase1(unittest.TestCase): 4 | 5 | def test_2to3(self): 6 | assert True 7 | 8 | def test_suite(): 9 | suite = unittest.makeSuite(TestCase1) 10 | return suite 11 | -------------------------------------------------------------------------------- /source/_tests/test-2.2/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | readme = open('docs/README.txt', 'rt').read() 4 | changes = open('docs/CHANGES.txt', 'rt').read() 5 | 6 | setup(name='Supporting Python 3 examples', 7 | version="1.0", 8 | description="An example project for Supporting Python 3", 9 | long_description=readme + '\n' + changes, 10 | classifiers=[ 11 | "Programming Language :: Python :: 2", 12 | "Topic :: Software Development :: Documentation"], 13 | keywords='python3 porting documentation examples', 14 | author='Lennart Regebro', 15 | author_email='regebro@gmail.com', 16 | license='GPL', 17 | packages=find_packages(exclude=['ez_setup']), 18 | include_package_data=True, 19 | test_suite='py3example.tests.test_suite') 20 | -------------------------------------------------------------------------------- /source/_tests/test-2.3.txt: -------------------------------------------------------------------------------- 1 | >>> import os, sys, subprocess, shutil 2 | >>> curdir = os.path.abspath(os.curdir) 3 | >>> os.chdir('./test-2.3') 4 | >>> proc = subprocess.Popen([sys.executable, 'setup.py', 'test'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 5 | >>> print(proc.stdout.read().decode()) 6 | running test 7 | ... 8 | running build_ext 9 | 10 | 11 | >>> print(proc.stderr.read().decode()) 12 | test_2to3 (py3example.tests.TestCase1) ... ok 13 | 14 | ---------------------------------------------------------------------- 15 | Ran 1 test in 0.000s 16 | 17 | OK 18 | 19 | 20 | >>> if os.path.exists('build'): 21 | ... shutil.rmtree('build') 22 | >>> os.chdir(curdir) 23 | -------------------------------------------------------------------------------- /source/_tests/test-2.3/docs/CHANGES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.3/docs/CHANGES.txt -------------------------------------------------------------------------------- /source/_tests/test-2.3/docs/README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.3/docs/README.txt -------------------------------------------------------------------------------- /source/_tests/test-2.3/py3example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.3/py3example/__init__.py -------------------------------------------------------------------------------- /source/_tests/test-2.3/py3example/tests.py: -------------------------------------------------------------------------------- 1 | import unittest, doctest 2 | import StringIO 3 | 4 | class TestCase1(unittest.TestCase): 5 | 6 | def test_2to3(self): 7 | out = StringIO.StringIO() 8 | print >> out, "This should end up in the tempfile" 9 | 10 | def test_suite(): 11 | suite = unittest.makeSuite(TestCase1) 12 | return suite 13 | -------------------------------------------------------------------------------- /source/_tests/test-2.3/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | readme = open('docs/README.txt', 'rt').read() 4 | changes = open('docs/CHANGES.txt', 'rt').read() 5 | 6 | setup(name='Supporting Python 3 examples', 7 | version="1.0", 8 | description="An example project for Supporting Python 3", 9 | long_description=readme + '\n' + changes, 10 | classifiers=[ 11 | "Programming Language :: Python :: 2", 12 | "Programming Language :: Python :: 3", 13 | "Topic :: Software Development :: Documentation"], 14 | keywords='python3 porting documentation examples', 15 | author='Lennart Regebro', 16 | author_email='regebro@gmail.com', 17 | license='GPL', 18 | packages=find_packages(exclude=['ez_setup']), 19 | include_package_data=True, 20 | test_suite='py3example.tests.test_suite', 21 | use_2to3=True) 22 | -------------------------------------------------------------------------------- /source/_tests/test-2.4.txt: -------------------------------------------------------------------------------- 1 | >>> import os, sys, subprocess, shutil 2 | >>> curdir = os.path.abspath(os.curdir) 3 | >>> os.chdir('./test-2.4') 4 | >>> proc = subprocess.Popen([sys.executable, 'setup.py', 'test'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 5 | >>> print(proc.stdout.read().decode()) 6 | running test 7 | ... 8 | running build_ext 9 | 10 | 11 | >>> print(proc.stderr.read().decode()) 12 | test_2to3 (py3example.tests.TestCase1) ... ok 13 | 14 | ---------------------------------------------------------------------- 15 | Ran 1 test in 0.000s 16 | 17 | OK 18 | 19 | 20 | >>> if os.path.exists('build'): 21 | ... shutil.rmtree('build') 22 | >>> os.chdir(curdir) 23 | -------------------------------------------------------------------------------- /source/_tests/test-2.4/docs/CHANGES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.4/docs/CHANGES.txt -------------------------------------------------------------------------------- /source/_tests/test-2.4/docs/README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.4/docs/README.txt -------------------------------------------------------------------------------- /source/_tests/test-2.4/py3example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.4/py3example/__init__.py -------------------------------------------------------------------------------- /source/_tests/test-2.4/py3example/tests.py: -------------------------------------------------------------------------------- 1 | import unittest, doctest 2 | import StringIO 3 | 4 | class TestCase1(unittest.TestCase): 5 | 6 | def test_2to3(self): 7 | out = StringIO.StringIO() 8 | print >> out, "This should end up in the tempfile" 9 | 10 | def test_suite(): 11 | suite = unittest.makeSuite(TestCase1) 12 | return suite 13 | -------------------------------------------------------------------------------- /source/_tests/test-2.4/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | readme = open('docs/README.txt', 'rt').read() 4 | changes = open('docs/CHANGES.txt', 'rt').read() 5 | 6 | setup(name='Supporting Python 3 examples', 7 | version="1.0", 8 | description="An example project for Supporting Python 3", 9 | long_description=readme + '\n' + changes, 10 | classifiers=[ 11 | "Programming Language :: Python :: 2", 12 | "Programming Language :: Python :: 3", 13 | "Topic :: Software Development :: Documentation"], 14 | keywords='python3 porting documentation examples', 15 | author='Lennart Regebro', 16 | author_email='regebro@gmail.com', 17 | license='GPL', 18 | packages=find_packages(exclude=['ez_setup']), 19 | include_package_data=True, 20 | test_suite='py3example.tests.test_suite', 21 | use_2to3=True, 22 | convert_2to3_doctests=['doc/README.txt']) 23 | -------------------------------------------------------------------------------- /source/_tests/test-2.5.txt: -------------------------------------------------------------------------------- 1 | >>> import os, sys, subprocess, shutil 2 | >>> curdir = os.path.abspath(os.curdir) 3 | >>> os.chdir('./test-2.5') 4 | >>> proc = subprocess.Popen([sys.executable, 'setup.py', 'test'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 5 | >>> print(proc.stdout.read().decode()) 6 | running test 7 | ... 8 | running build_ext 9 | 10 | 11 | >>> print(proc.stderr.read().decode()) 12 | ... 13 | test_2to3 (py3example.tests.TestCase1) ... ok 14 | 15 | ---------------------------------------------------------------------- 16 | Ran 1 test in 0.000s 17 | 18 | OK 19 | 20 | 21 | >>> if os.path.exists('build'): 22 | ... shutil.rmtree('build') 23 | >>> os.chdir(curdir) 24 | -------------------------------------------------------------------------------- /source/_tests/test-2.5/docs/CHANGES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.5/docs/CHANGES.txt -------------------------------------------------------------------------------- /source/_tests/test-2.5/docs/README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.5/docs/README.txt -------------------------------------------------------------------------------- /source/_tests/test-2.5/py3example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/regebro/supporting-python-3/4898b8785f956a92d0d2ec646b96438b6c127030/source/_tests/test-2.5/py3example/__init__.py -------------------------------------------------------------------------------- /source/_tests/test-2.5/py3example/tests.py: -------------------------------------------------------------------------------- 1 | import unittest, doctest 2 | import StringIO 3 | 4 | class TestCase1(unittest.TestCase): 5 | 6 | def test_2to3(self): 7 | out = StringIO.StringIO() 8 | print >> out, "This should end up in the tempfile" 9 | 10 | def test_suite(): 11 | suite = unittest.makeSuite(TestCase1) 12 | return suite 13 | -------------------------------------------------------------------------------- /source/_tests/test-2.5/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | readme = open('docs/README.txt', 'rt').read() 4 | changes = open('docs/CHANGES.txt', 'rt').read() 5 | 6 | setup(name='Supporting Python 3 examples', 7 | version="1.0", 8 | description="An example project for Supporting Python 3", 9 | long_description=readme + '\n' + changes, 10 | classifiers=[ 11 | "Programming Language :: Python :: 2", 12 | "Programming Language :: Python :: 3", 13 | "Topic :: Software Development :: Documentation"], 14 | keywords='python3 porting documentation examples', 15 | author='Lennart Regebro', 16 | author_email='regebro@gmail.com', 17 | license='GPL', 18 | packages=find_packages(exclude=['ez_setup']), 19 | include_package_data=True, 20 | test_suite='py3example.tests.test_suite', 21 | use_2to3=True, 22 | convert_2to3_doctests=['doc/README.txt'], 23 | install_requires=['zope.fixers'], 24 | use_2to3_fixers=['zope.fixers']) 25 | -------------------------------------------------------------------------------- /source/_tests/test-2.6.txt: -------------------------------------------------------------------------------- 1 | >>> import os, sys, subprocess, shutil 2 | >>> curdir = os.path.abspath(os.curdir) 3 | >>> os.chdir('./test-2.6') 4 | >>> proc = subprocess.Popen([sys.executable, 'setup.py', 'build'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 5 | >>> print(proc.stdout.read().decode()) 6 | running build 7 | running build_py 8 | ... 9 | 10 | 11 | >>> print(proc.stderr.read().decode()) 12 | 13 | 14 | >>> if os.path.exists('build'): 15 | ... shutil.rmtree('build') 16 | >>> os.chdir(curdir) 17 | -------------------------------------------------------------------------------- /source/_tests/test-2.6/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.version_info < (3,): 3 | import setup2 4 | else: 5 | import setup3 -------------------------------------------------------------------------------- /source/_tests/test-2.6/setup2.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | try: 4 | readme = open('docs/README.txt', 'rt').read() 5 | changes = open('docs/CHANGES.txt', 'rt').read() 6 | except IOError: 7 | readme = "" 8 | changes = "" 9 | 10 | setup(name='Supporting Python 3 examples', 11 | version="1.0", 12 | packages=['test'], 13 | package_dir={'test': 'src2'}, 14 | description="The example project for Supporting Python 2", 15 | long_description=readme + '\n' + changes, 16 | classifiers=[ 17 | "Programming Language :: Python :: 2", 18 | "Topic :: Software Development :: Documentation"], 19 | keywords='python3 porting documentation examples', 20 | author='Lennart Regebro', 21 | author_email='regebro@gmail.com', 22 | license='GPL') 23 | -------------------------------------------------------------------------------- /source/_tests/test-2.6/setup3.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | try: 4 | readme = open('docs/README.txt', 'rt').read() 5 | changes = open('docs/CHANGES.txt', 'rt').read() 6 | except IOError: 7 | readme = "" 8 | changes = "" 9 | 10 | setup(name='Supporting Python 3 examples', 11 | version="1.0", 12 | packages=['test'], 13 | package_dir={'test': 'src2'}, 14 | description="An example project for Supporting Python 3", 15 | long_description=readme + '\n' + changes, 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "Topic :: Software Development :: Documentation"], 19 | keywords='python3 porting documentation examples', 20 | author='Lennart Regebro', 21 | author_email='regebro@gmail.com', 22 | license='GPL') 23 | -------------------------------------------------------------------------------- /source/_tests/test-2.6/src2/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | print >> sys.stderr, "This should not work in Python 3" 3 | 4 | try: 5 | readme = open('docs/README.txt', 'rt').read() 6 | changes = open('docs/CHANGES.txt', 'rt').read() 7 | except IOError, e: 8 | readme = "" 9 | changes = "" 10 | -------------------------------------------------------------------------------- /source/_tests/test-2.6/src3/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | print("This should not work in Python 2", file=sys.stderr) 3 | 4 | try: 5 | readme = open('docs/README.txt', 'rt').read() 6 | changes = open('docs/CHANGES.txt', 'rt').read() 7 | except IOError as e: 8 | readme = "" 9 | changes = "" 10 | -------------------------------------------------------------------------------- /source/_tests/test-3.1.txt: -------------------------------------------------------------------------------- 1 | >>> class Orderable(object): 2 | ... 3 | ... def __init__(self, firstname, lastname): 4 | ... self.first = firstname 5 | ... self.last = lastname 6 | ... 7 | ... def __cmp__(self, other): 8 | ... return cmp("%s, %s" % (self.last, self.first), 9 | ... "%s, %s" % (other.last, other.first)) 10 | ... 11 | ... def __repr__(self): 12 | ... return "%s %s" % (self.first, self.last) 13 | ... 14 | >>> sorted([Orderable('Donald', 'Duck'), 15 | ... Orderable('Paul', 'Anka')]) 16 | [Paul Anka, Donald Duck] 17 | -------------------------------------------------------------------------------- /source/_tests/test-3.10.txt: -------------------------------------------------------------------------------- 1 | >>> b'GIF89a'[2:3] 2 | b'F' 3 | -------------------------------------------------------------------------------- /source/_tests/test-3.11.txt: -------------------------------------------------------------------------------- 1 | >>> file = open('maybe_a.gif', 'rb') 2 | >>> file.read(6) == u'GIF89a'.encode('ISO-8859-1') 3 | True 4 | -------------------------------------------------------------------------------- /source/_tests/test-3.11.txt.2to3.bak: -------------------------------------------------------------------------------- 1 | >>> file = open('maybe_a.gif', 'rb') 2 | >>> file.read(6) == u'GIF89a'.encode('ISO-8859-1') 3 | True 4 | -------------------------------------------------------------------------------- /source/_tests/test-3.12.txt: -------------------------------------------------------------------------------- 1 | >>> from makebytes import b 2 | >>> b('GIF89a') 3 | 'GIF89a' 4 | -------------------------------------------------------------------------------- /source/_tests/test-3.13.txt: -------------------------------------------------------------------------------- 1 | >>> from makebytes import b 2 | >>> b('GIF89a') 3 | b'GIF89a' 4 | -------------------------------------------------------------------------------- /source/_tests/test-3.14.txt: -------------------------------------------------------------------------------- 1 | >>> from bites import Bites 2 | >>> Bites([71, 73, 70, 56, 57, 97]).itemint(2) 3 | 70 4 | >>> binary_data = Bites(open('maybe_a.gif', 'rb').read()) 5 | >>> binary_data.itemint(2) 6 | 70 7 | >>> print([x for x in Bites('GIF89a').iterint()]) 8 | [71, 73, 70, 56, 57, 97] 9 | -------------------------------------------------------------------------------- /source/_tests/test-3.15.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> data = open('maybe_a.gif', 'rb').read() 3 | >>> if sys.version_info > (3,): 4 | ... data = data.decode('latin-1') 5 | >>> data[:6] 6 | 'GIF89a' 7 | -------------------------------------------------------------------------------- /source/_tests/test-3.16.txt: -------------------------------------------------------------------------------- 1 | >>> ignore = open('/tmp/file.out', 'wt').write('Some text') 2 | -------------------------------------------------------------------------------- /source/_tests/test-3.17.txt: -------------------------------------------------------------------------------- 1 | >>> type([]) 2 | 3 | -------------------------------------------------------------------------------- /source/_tests/test-3.18.txt: -------------------------------------------------------------------------------- 1 | >>> type([]) 2 | 3 | -------------------------------------------------------------------------------- /source/_tests/test-3.19.txt: -------------------------------------------------------------------------------- 1 | >>> isinstance([], list) 2 | True 3 | -------------------------------------------------------------------------------- /source/_tests/test-3.2.txt: -------------------------------------------------------------------------------- 1 | >>> class Orderable(object): 2 | ... 3 | ... def __init__(self, firstname, lastname): 4 | ... self.first = firstname 5 | ... self.last = lastname 6 | ... 7 | ... def __lt__(self, other): 8 | ... return ("%s, %s" % (self.last, self.first) < 9 | ... "%s, %s" % (other.last, other.first)) 10 | ... 11 | ... def __repr__(self): 12 | ... return "%s %s" % (self.first, self.last) 13 | ... 14 | >>> sorted([Orderable('Donald', 'Duck'), 15 | ... Orderable('Paul', 'Anka')]) 16 | [Paul Anka, Donald Duck] 17 | -------------------------------------------------------------------------------- /source/_tests/test-3.20.txt: -------------------------------------------------------------------------------- 1 | >>> type([]) 2 | <... 'list'> 3 | -------------------------------------------------------------------------------- /source/_tests/test-3.21.txt: -------------------------------------------------------------------------------- 1 | >>> import socket 2 | >>> socket.gethostbyname("www.python.rog") 3 | Traceback (most recent call last): 4 | gaierror: [Errno -2] Name or service not known 5 | -------------------------------------------------------------------------------- /source/_tests/test-3.22.txt: -------------------------------------------------------------------------------- 1 | >>> import socket 2 | >>> socket.gethostbyname("www.python.rog") 3 | Traceback (most recent call last): 4 | socket.gaierror: [Errno -2] Name or service not known 5 | -------------------------------------------------------------------------------- /source/_tests/test-3.23.txt: -------------------------------------------------------------------------------- 1 | >>> import socket 2 | >>> try: 3 | ... socket.gethostbyname("www.python.rog") 4 | ... raise AssertionError("gaierror exception was not raised") 5 | ... except socket.gaierror: 6 | ... pass 7 | -------------------------------------------------------------------------------- /source/_tests/test-3.24.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... from UserDict import UserDict 3 | ... from UserDict import DictMixin 4 | ... except ImportError: 5 | ... from collections import UserDict 6 | ... from collections import MutableMapping as DictMixin 7 | -------------------------------------------------------------------------------- /source/_tests/test-3.25.txt: -------------------------------------------------------------------------------- 1 | >>> import socket 2 | >>> shouldRaise(socket.gaierror, 3 | ... socket.gethostbyname, "www.python.rog") 4 | -------------------------------------------------------------------------------- /source/_tests/test-3.26.txt: -------------------------------------------------------------------------------- 1 | >>> def bprint(data): 2 | ... if not isinstance(data, str): 3 | ... data = data.decode() 4 | ... print(data.strip()) 5 | -------------------------------------------------------------------------------- /source/_tests/test-3.27.txt: -------------------------------------------------------------------------------- 1 | >>> import io 2 | >>> infile = io.open('UTF-8.txt', 'rt', encoding='UTF-8') 3 | >>> print(infile.read()) 4 | It wörks 5 | -------------------------------------------------------------------------------- /source/_tests/test-3.28.txt: -------------------------------------------------------------------------------- 1 | >>> import codecs 2 | >>> infile = codecs.open('UTF-8.txt', 'r', encoding='UTF-8') 3 | >>> print(infile.read()) 4 | It wörks 5 | -------------------------------------------------------------------------------- /source/_tests/test-3.29.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> if sys.version_info < (3,): 3 | ... def byteindex(data, index): 4 | ... return ord(data[index]) 5 | ... 6 | ... def iterbytes(data): 7 | ... return (ord (char) for char in data) 8 | ... 9 | ... else: 10 | ... byteindex = lambda x, i: x[i] 11 | ... iterbytes = lambda x: iter(x) 12 | ... 13 | >>> from makebytes import b 14 | >>> byteindex(b('Test'), 2) 15 | 115 16 | >>> print([x for x in iterbytes(b('Test'))]) 17 | [84, 101, 115, 116] 18 | -------------------------------------------------------------------------------- /source/_tests/test-3.3.txt: -------------------------------------------------------------------------------- 1 | >>> from mixin import ComparableMixin 2 | 3 | >>> class Orderable(ComparableMixin): 4 | ... 5 | ... def __init__(self, firstname, lastname): 6 | ... self.first = firstname 7 | ... self.last = lastname 8 | ... 9 | ... def _cmpkey(self): 10 | ... return (self.last, self.first) 11 | ... 12 | ... def __repr__(self): 13 | ... return "%s %s" % (self.first, self.last) 14 | ... 15 | >>> sorted([Orderable('Donald', 'Duck'), 16 | ... Orderable('Paul', 'Anka')]) 17 | [Paul Anka, Donald Duck] 18 | -------------------------------------------------------------------------------- /source/_tests/test-3.30.txt: -------------------------------------------------------------------------------- 1 | >>> data = bytearray(b'Monty Python') 2 | >>> data[5] = 33 3 | >>> data 4 | bytearray(b'Monty!Python') 5 | -------------------------------------------------------------------------------- /source/_tests/test-3.31.txt: -------------------------------------------------------------------------------- 1 | >>> from mixin import ComparableMixin 2 | 3 | >>> class Hashable(ComparableMixin): 4 | ... def __init__(self, firstname, lastname): 5 | ... self._first = firstname 6 | ... self._last = lastname 7 | ... 8 | ... def _cmpkey(self): 9 | ... return (self._last, self._first) 10 | ... 11 | ... def __repr__(self): 12 | ... return "%s(%r, %r)" % (self.__class__.__name__, 13 | ... self._first, self._last) 14 | ... 15 | ... def __hash__(self): 16 | ... return hash(self._cmpkey()) 17 | ... 18 | >>> d = {Hashable('Donald', 'Duck'): 'Daisy Duck'} 19 | >>> d 20 | {Hashable('Donald', 'Duck'): 'Daisy Duck'} 21 | -------------------------------------------------------------------------------- /source/_tests/test-3.4.txt: -------------------------------------------------------------------------------- 1 | >>> def ignorecase(a, b): 2 | ... return cmp(a.lower(), b.lower()) 3 | >>> sorted(['ant', 'Aardvark', 'banana', 'Dingo'], cmp=ignorecase) 4 | ['Aardvark', 'ant', 'banana', 'Dingo'] 5 | -------------------------------------------------------------------------------- /source/_tests/test-3.5.txt: -------------------------------------------------------------------------------- 1 | >>> def ignorecase(x): 2 | ... return x.lower() 3 | >>> sorted(['ant', 'Aardvark', 'banana', 'Dingo'], key=ignorecase) 4 | ['Aardvark', 'ant', 'banana', 'Dingo'] 5 | -------------------------------------------------------------------------------- /source/_tests/test-3.6.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> if sys.version_info < (2, 4): 3 | ... def sorted(data, key): 4 | ... mapping = {} 5 | ... for x in data: 6 | ... mapping[key(x)] = x 7 | ... keys = mapping.keys() 8 | ... keys.sort() 9 | ... return [mapping[x] for x in keys] 10 | >>> data = ['ant', 'Aardvark', 'banana', 'Dingo'] 11 | >>> sorted(data, key=str.lower) 12 | ['Aardvark', 'ant', 'banana', 'Dingo'] 13 | -------------------------------------------------------------------------------- /source/_tests/test-3.7.txt: -------------------------------------------------------------------------------- 1 | >>> file = open('maybe_a.gif', 'rb') 2 | >>> file.read(6) == 'GIF89a' 3 | True 4 | -------------------------------------------------------------------------------- /source/_tests/test-3.8.txt: -------------------------------------------------------------------------------- 1 | >>> file = open('maybe_a.gif', 'rb') 2 | >>> file.read(6) == b'GIF89a' 3 | True 4 | -------------------------------------------------------------------------------- /source/_tests/test-3.9.txt: -------------------------------------------------------------------------------- 1 | >>> 'GIF89a'[2] 2 | 'F' 3 | >>> b'GIF89a'[2] 4 | 70 5 | -------------------------------------------------------------------------------- /source/_tests/test-4.1.txt: -------------------------------------------------------------------------------- 1 | >>> infile = open('pythons.txt') 2 | >>> pythons = infile.readlines() 3 | >>> pythons.sort() 4 | >>> [x.strip() for x in pythons] 5 | ['Eric', 'Graham', 'John', 'Michael', 'Terry', 'Terry'] 6 | -------------------------------------------------------------------------------- /source/_tests/test-4.10.txt: -------------------------------------------------------------------------------- 1 | >>> dict = {'Adam': 'Eve', 'John': 'Yoko', 'Donald': 'Daisy'} 2 | >>> dict.has_key('Dilbert') 3 | False 4 | -------------------------------------------------------------------------------- /source/_tests/test-4.11.txt: -------------------------------------------------------------------------------- 1 | >>> dict = {'Adam': 'Eve', 'John': 'Yoko', 'Donald': 'Daisy'} 2 | >>> 'Dilbert' in dict 3 | False 4 | -------------------------------------------------------------------------------- /source/_tests/test-4.12.txt: -------------------------------------------------------------------------------- 1 | >>> infile = open('pythons.txt') 2 | >>> [x.strip() for x in sorted(infile)] 3 | ['Eric', 'Graham', 'John', 'Michael', 'Terry', 'Terry'] 4 | -------------------------------------------------------------------------------- /source/_tests/test-4.13.txt: -------------------------------------------------------------------------------- 1 | >>> def keyfunction(item): 2 | ... """Sorting on descending length and alphabetically""" 3 | ... return -len(item), item 4 | >>> names = ['Adam', 'Donald', 'John'] 5 | >>> names.sort(key=keyfunction) 6 | >>> names 7 | ['Donald', 'Adam', 'John'] 8 | -------------------------------------------------------------------------------- /source/_tests/test-4.14.txt: -------------------------------------------------------------------------------- 1 | >>> def allcombinations(starters, endings): 2 | ... result = [] 3 | ... for s in starters: 4 | ... for e in endings: 5 | ... result.append(s+e) 6 | ... return result 7 | -------------------------------------------------------------------------------- /source/_tests/test-4.15.txt: -------------------------------------------------------------------------------- 1 | >>> def allcombinations(starters, endings): 2 | ... for s in starters: 3 | ... for e in endings: 4 | ... yield s+e 5 | -------------------------------------------------------------------------------- /source/_tests/test-4.16.txt: -------------------------------------------------------------------------------- 1 | >>> sum([x*x for x in xrange(4000000)]) 2 | 21333325333334000000L 3 | -------------------------------------------------------------------------------- /source/_tests/test-4.17.txt: -------------------------------------------------------------------------------- 1 | >>> sum(x*x for x in xrange(4000000)) 2 | 21333325333334000000L 3 | -------------------------------------------------------------------------------- /source/_tests/test-4.18.txt: -------------------------------------------------------------------------------- 1 | >>> from contextlib import closing 2 | >>> import urllib 3 | >>> 4 | >>> book_url = 'http://python3porting.com/' 5 | >>> with closing(urllib.urlopen(book_url)) as page: 6 | ... print len(page.readlines()) 7 | 145 8 | -------------------------------------------------------------------------------- /source/_tests/test-4.19.txt: -------------------------------------------------------------------------------- 1 | >>> from functools import cmp_to_key 2 | >>> def compare(a, b): return cmp(a[1:], b[1:]) 3 | >>> sorted(['Adam', 'Donald', 'John'], key=cmp_to_key(compare)) 4 | ['Adam', 'John', 'Donald'] 5 | -------------------------------------------------------------------------------- /source/_tests/test-4.2.txt: -------------------------------------------------------------------------------- 1 | >>> def compare(a, b): 2 | ... """Comparison that ignores the first letter""" 3 | ... return cmp(a[1:], b[1:]) 4 | >>> names = ['Adam', 'Donald', 'John'] 5 | >>> names.sort(cmp=compare) 6 | >>> names 7 | ['Adam', 'John', 'Donald'] 8 | -------------------------------------------------------------------------------- /source/_tests/test-4.3.txt: -------------------------------------------------------------------------------- 1 | >>> def keyfunction(item): 2 | ... """Key for comparison that ignores the first letter""" 3 | ... return item[1:] 4 | >>> names = ['Adam', 'Donald', 'John'] 5 | >>> names.sort(key=keyfunction) 6 | >>> names 7 | ['Adam', 'John', 'Donald'] 8 | -------------------------------------------------------------------------------- /source/_tests/test-4.4.txt: -------------------------------------------------------------------------------- 1 | >>> names = ['Adam', 'Donald', 'John'] 2 | >>> names.sort(key=lambda x: x[1:]) 3 | >>> names 4 | ['Adam', 'John', 'Donald'] 5 | -------------------------------------------------------------------------------- /source/_tests/test-4.5.txt: -------------------------------------------------------------------------------- 1 | >>> names = ['Adam', 'Donald', 'John'] 2 | >>> names.sort(key=lambda x: x[1:], reverse=True) 3 | >>> names 4 | ['Donald', 'John', 'Adam'] 5 | -------------------------------------------------------------------------------- /source/_tests/test-4.6.txt: -------------------------------------------------------------------------------- 1 | >>> names = ['Adam', 'Donald', 'John'] 2 | >>> # Alphabetical sort 3 | >>> names.sort() 4 | >>> # Long names should go first 5 | >>> names.sort(key=lambda x: len(x), reverse=True) 6 | >>> names 7 | ['Donald', 'Adam', 'John'] 8 | -------------------------------------------------------------------------------- /source/_tests/test-4.7.txt: -------------------------------------------------------------------------------- 1 | >>> f = open('/tmp/afile.txt', 'w') 2 | >>> try: 3 | ... n = f.write('sometext') 4 | ... finally: 5 | ... f.close() 6 | -------------------------------------------------------------------------------- /source/_tests/test-4.8.txt: -------------------------------------------------------------------------------- 1 | >>> with open('/tmp/afile.txt', 'w') as f: 2 | ... n = f.write('sometext') 3 | -------------------------------------------------------------------------------- /source/_tests/test-4.9.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> from StringIO import StringIO 3 | >>> class redirect_stdout: 4 | ... def __init__(self, target): 5 | ... self.stdout = sys.stdout 6 | ... self.target = target 7 | ... 8 | ... def __enter__(self): 9 | ... sys.stdout = self.target 10 | ... 11 | ... def __exit__(self, type, value, tb): 12 | ... sys.stdout = self.stdout 13 | ... 14 | >>> out = StringIO() 15 | >>> with redirect_stdout(out): 16 | ... print 'Test' 17 | ... 18 | >>> out.getvalue() == 'Test\n' 19 | True 20 | -------------------------------------------------------------------------------- /source/_tests/test-5.1.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... a = 1/0 3 | ... except ZeroDivisionError, e: 4 | ... print e.args[0] 5 | integer division or modulo by zero 6 | -------------------------------------------------------------------------------- /source/_tests/test-5.10.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> if sys.version_info > (3,): 3 | ... long = int 4 | >>> long(1) 5 | 1L 6 | -------------------------------------------------------------------------------- /source/_tests/test-5.11.txt: -------------------------------------------------------------------------------- 1 | >>> from __future__ import unicode_literals 2 | >>> type("A standard string literal") 3 | 4 | >>> type(b"A binary literal") 5 | 6 | -------------------------------------------------------------------------------- /source/_tests/test-5.12.txt: -------------------------------------------------------------------------------- 1 | >>> from __future__ import unicode_literals 2 | >>> type(b"Binary data") 3 | 4 | >>> type("Unicode text") 5 | 6 | -------------------------------------------------------------------------------- /source/_tests/test-5.13.txt: -------------------------------------------------------------------------------- 1 | >>> from __future__ import unicode_literals 2 | >>> import sys 3 | >>> if sys.version_info < (3,): 4 | ... text_type = unicode 5 | ... binary_type = str 6 | ... else: 7 | ... text_type = str 8 | ... binary_type = bytes 9 | >>> isinstance('U\xf1ic\xf6de', text_type) 10 | True 11 | -------------------------------------------------------------------------------- /source/_tests/test-5.14.txt: -------------------------------------------------------------------------------- 1 | >>> from makeunicode import u 2 | >>> u('GIF89a') 3 | u'GIF89a' 4 | -------------------------------------------------------------------------------- /source/_tests/test-5.15.txt: -------------------------------------------------------------------------------- 1 | >>> from makeunicode import u 2 | >>> u('GIF89a') 3 | 'GIF89a' 4 | -------------------------------------------------------------------------------- /source/_tests/test-5.16.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... f = 1/0 3 | ... except ZeroDivisionError, e: 4 | ... for m in e: 5 | ... print m 6 | ... print e[0] 7 | integer division or modulo by zero 8 | integer division or modulo by zero 9 | -------------------------------------------------------------------------------- /source/_tests/test-5.17.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... f = "1" + 1 3 | ... except TypeError as e: 4 | ... for m in e.args: 5 | ... print(m) 6 | ... print(e.args[0]) 7 | Can't convert 'int' object to str implicitly 8 | Can't convert 'int' object to str implicitly 9 | -------------------------------------------------------------------------------- /source/_tests/test-5.18.txt: -------------------------------------------------------------------------------- 1 | >>> from makeunicode import u 2 | >>> print(u('\u00dcnic\u00f6de')) 3 | Ünicöde 4 | >>> print(u('\xdcnic\N{Latin Small Letter O with diaeresis}de')) 5 | Ünicöde 6 | -------------------------------------------------------------------------------- /source/_tests/test-5.2.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... a = 1/"Dinsdale" 3 | ... except (ZeroDivisionError, TypeError): 4 | ... print "You can't divide by zero or by strings!" 5 | You can't divide by zero or by strings! 6 | -------------------------------------------------------------------------------- /source/_tests/test-5.3.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... a = 1/'0' 3 | ... except (ZeroDivisionError, TypeError) as e: 4 | ... print(e.args[0]) 5 | unsupported operand type(s) for /: 'int' and 'str' 6 | -------------------------------------------------------------------------------- /source/_tests/test-5.4.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> try: 3 | ... a = 1/'0' 4 | ... except (ZeroDivisionError, TypeError): 5 | ... e = sys.exc_info()[1] 6 | ... print(e.args[0]) 7 | unsupported operand type(s) for /: 'int' and 'str' 8 | -------------------------------------------------------------------------------- /source/_tests/test-5.5.txt: -------------------------------------------------------------------------------- 1 | >>> print("This works in all versions of Python!") 2 | This works in all versions of Python! 3 | -------------------------------------------------------------------------------- /source/_tests/test-5.6.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... import configparser 3 | ... except ImportError: 4 | ... import ConfigParser as configparser 5 | -------------------------------------------------------------------------------- /source/_tests/test-5.7.txt: -------------------------------------------------------------------------------- 1 | >>> import urllib 2 | >>> import urlparse 3 | >>> 4 | >>> url = 'http://docs.python.org/library/' 5 | >>> parts = urlparse.urlparse(url) 6 | >>> parts = parts._replace(path='/3.0'+parts.path) 7 | >>> page = urllib.urlopen(parts.geturl()) 8 | -------------------------------------------------------------------------------- /source/_tests/test-5.8.txt: -------------------------------------------------------------------------------- 1 | >>> import urllib.request, urllib.parse, urllib.error 2 | >>> import urllib.parse 3 | >>> 4 | >>> url = 'http://docs.python.org/library/' 5 | >>> parts = urllib.parse.urlparse(url) 6 | >>> parts = parts._replace(path='/3.0'+parts.path) 7 | >>> page = urllib.request.urlopen(parts.geturl()) 8 | -------------------------------------------------------------------------------- /source/_tests/test-5.9.txt: -------------------------------------------------------------------------------- 1 | >>> try: 2 | ... from urllib.request import urlopen 3 | ... from urllib.parse import urlparse 4 | ... except ImportError: 5 | ... from urlparse import urlparse 6 | ... from urllib import urlopen 7 | ... 8 | >>> url = 'http://docs.python.org/library/' 9 | >>> parts = urlparse(url) 10 | >>> parts = parts._replace(path='/3.0'+parts.path) 11 | >>> page = urlopen(parts.geturl()) 12 | -------------------------------------------------------------------------------- /source/_tests/test-6.1.txt: -------------------------------------------------------------------------------- 1 | >>> from zope.interface import Interface, implements 2 | >>> 3 | >>> class IMyInterface(Interface): 4 | ... def amethod(): 5 | ... '''This is just an example''' 6 | ... 7 | >>> class MyClass(object): 8 | ... 9 | ... implements(IMyInterface) 10 | ... 11 | ... def amethod(self): 12 | ... return True 13 | -------------------------------------------------------------------------------- /source/_tests/test-6.2.txt: -------------------------------------------------------------------------------- 1 | >>> from zope.interface import Interface, implementer 2 | >>> 3 | >>> class IMyInterface(Interface): 4 | ... def amethod(): 5 | ... '''This is just an example''' 6 | ... 7 | >>> @implementer(IMyInterface) 8 | ... class MyClass(object): 9 | ... 10 | ... def amethod(self): 11 | ... return True 12 | -------------------------------------------------------------------------------- /source/_tests/test-7.1.txt: -------------------------------------------------------------------------------- 1 | >>> set([1,2,3]) 2 | {1, 2, 3} 3 | -------------------------------------------------------------------------------- /source/_tests/test-7.10.txt: -------------------------------------------------------------------------------- 1 | >>> i = iter(range(5)) 2 | >>> i.next() 3 | 0 4 | >>> i.next() 5 | 1 6 | -------------------------------------------------------------------------------- /source/_tests/test-7.11.txt: -------------------------------------------------------------------------------- 1 | >>> i = iter(range(5)) 2 | >>> next(i) 3 | 0 4 | >>> next(i) 5 | 1 6 | -------------------------------------------------------------------------------- /source/_tests/test-7.12.txt: -------------------------------------------------------------------------------- 1 | >>> department = 'Silly Walk' 2 | >>> result = {x: department.count(x) for x in department} 3 | >>> result == {'a': 1, ' ': 1, 'i': 1, 'k': 1, 'l': 3, 'S': 1, 'W': 1, 'y': 1} 4 | True 5 | 6 | >>> result = {x for x in department} 7 | >>> result == {'a', ' ', 'i', 'k', 'l', 'S', 'W', 'y'} 8 | True 9 | -------------------------------------------------------------------------------- /source/_tests/test-7.2.txt: -------------------------------------------------------------------------------- 1 | >>> {1, 2, 3} 2 | set([1, 2, 3]) 3 | -------------------------------------------------------------------------------- /source/_tests/test-7.3.txt: -------------------------------------------------------------------------------- 1 | >>> (x for x in 'Silly Walk') 2 | at ...> 3 | -------------------------------------------------------------------------------- /source/_tests/test-7.4.txt: -------------------------------------------------------------------------------- 1 | >>> list(x for x in 'Silly Walk') 2 | ['S', 'i', 'l', 'l', 'y', ' ', 'W', 'a', 'l', 'k'] 3 | 4 | >>> [x for x in 'Silly Walk'] 5 | ['S', 'i', 'l', 'l', 'y', ' ', 'W', 'a', 'l', 'k'] 6 | -------------------------------------------------------------------------------- /source/_tests/test-7.5.txt: -------------------------------------------------------------------------------- 1 | >>> department = 'Silly Walk' 2 | >>> {x: department.count(x) for x in department} 3 | {'a': 1, ' ': 1, 'i': 1, 'k': 1, 'l': 3, 'S': 1, 'W': 1, 'y': 1} 4 | 5 | >>> {x for x in department} 6 | {'a', ' ', 'i', 'k', 'l', 'S', 'W', 'y'} 7 | -------------------------------------------------------------------------------- /source/_tests/test-7.6.txt: -------------------------------------------------------------------------------- 1 | >>> from collections import OrderedDict 2 | >>> squares = OrderedDict((x, x*x) for x in range(5)) 3 | >>> list(squares.values()) 4 | [0, 1, 4, 9, 16] 5 | -------------------------------------------------------------------------------- /source/_tests/test-7.7.txt: -------------------------------------------------------------------------------- 1 | >>> from fractions import Fraction 2 | >>> Fraction(3,4) / Fraction('2/3') 3 | Fraction(9, 8) 4 | -------------------------------------------------------------------------------- /source/_tests/test-7.8.txt: -------------------------------------------------------------------------------- 1 | Old style formatting: 2 | >>> 'I %s Python %i' % ('like', 2) 3 | 'I like Python 2' 4 | 5 | New style formatting: 6 | >>> 'I {0} Python {1}'.format('♥', 3) 7 | 'I ♥ Python 3' 8 | -------------------------------------------------------------------------------- /source/_tests/test-7.9.txt: -------------------------------------------------------------------------------- 1 | >>> import sys 2 | >>> 'Python {0.version_info[0]:!<9.1%}'.format(sys) 3 | 'Python 300.0%!!!' 4 | -------------------------------------------------------------------------------- /source/_tests/test_base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | import unittest 3 | 4 | class FixerTest(unittest.TestCase): 5 | 6 | def _test(self, source, target): 7 | refactored = str(self.refactor(source, 'zope.fixer.test')) 8 | if refactored != target: 9 | match = '' 10 | for i in range(min(len(refactored), len(target))): 11 | if refactored[i] == target[i]: 12 | match += refactored[i] 13 | else: 14 | break 15 | msg = "\nResult:\n" + refactored 16 | msg += "\nFailed:\n" + refactored[i:] 17 | msg += "\nTarget:\n" + target[i:] 18 | # Make spaces and tabs visible: 19 | msg = msg.replace(' ', '·') 20 | msg = msg.replace('\t', '------->') 21 | msg = ("Test failed at character %i" % i) + msg 22 | self.fail(msg) 23 | -------------------------------------------------------------------------------- /source/_tests/test_fixers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | from lib2to3.refactor import RefactoringTool 4 | from test_base import FixerTest 5 | 6 | # Check that various import syntaxes get renamed properly. 7 | indent_source = """ 8 | class Klas(object): 9 | '''Only the first line 10 | of docstrings and other strings 11 | are touched. 12 | ''' 13 | 14 | # Do some code in the class construction: 15 | for x in range(10): 16 | print x 17 | 18 | # Comments are left alone. 19 | 20 | def __init__(self): 21 | def function(): # Ugly place for a comment 22 | if False: 23 | # This comment is unaligned 24 | a = 1 25 | b = 2.0 26 | c = 0x3 27 | elif True: 28 | x = "Four"; y = "Five" 29 | # Hepp! 30 | z = 6 31 | else: 32 | q = 'Huh?' 33 | """ 34 | 35 | indent_target = """ 36 | class Klas(object): 37 | '''Only the first line 38 | of docstrings and other strings 39 | are touched. 40 | ''' 41 | 42 | # Do some code in the class construction: 43 | for x in range(10): 44 | print x 45 | 46 | # Comments are left alone. 47 | 48 | def __init__(self): 49 | def function(): # Ugly place for a comment 50 | if False: 51 | # This comment is unaligned 52 | a = 1 53 | b = 2.0 54 | c = 0x3 55 | elif True: 56 | x = "Four"; y = "Five" 57 | # Hepp! 58 | z = 6 59 | else: 60 | q = 'Huh?' 61 | """ 62 | 63 | class IndentFixerTest(FixerTest): 64 | 65 | def setUp(self): 66 | self.refactor = RefactoringTool(['fix_indent']).refactor_string 67 | 68 | def test_indent(self): 69 | self._test(indent_source, indent_target) 70 | 71 | # Check that various import syntaxes get renamed properly. 72 | name_source = """from foo import oldname 73 | 74 | def afunction(alist): 75 | return oldname(alist) 76 | """ 77 | 78 | name_target = """from foo import newname 79 | 80 | def afunction(alist): 81 | return newname(alist) 82 | """ 83 | 84 | class Name1FixerTest(FixerTest): 85 | 86 | def setUp(self): 87 | self.refactor = RefactoringTool(['fix_name1']).refactor_string 88 | 89 | def test_name(self): 90 | self._test(name_source, name_target) 91 | 92 | 93 | class Name2FixerTest(FixerTest): 94 | 95 | def setUp(self): 96 | self.refactor = RefactoringTool(['fix_name2']).refactor_string 97 | 98 | def test_name(self): 99 | self._test(name_source, name_target) 100 | 101 | class Name3FixerTest(FixerTest): 102 | 103 | def setUp(self): 104 | self.refactor = RefactoringTool(['fix_name3']).refactor_string 105 | 106 | def test_name(self): 107 | self._test(name_source, name_target) 108 | 109 | constant1_source = """import foo as bar 110 | 111 | def afunction(alist): 112 | return [x*CONSTANT for x in alist] 113 | 114 | def bfunction(blist): 115 | return [x*bar.CONSTANT for x in blist] 116 | """ 117 | 118 | 119 | constant1_target = """import foo as bar 120 | 121 | def afunction(alist): 122 | return [x*CONSTANT for x in alist] 123 | 124 | def bfunction(blist): 125 | return [x*bar.get_constant() for x in blist] 126 | """ 127 | 128 | constant2_source = """from foo import CONSTANT as renamed 129 | 130 | def afunction(alist): 131 | return [x*renamed for x in alist] 132 | 133 | def bfunction(blist): 134 | return [x*bar.renamed for x in blist] 135 | """ 136 | 137 | 138 | constant2_target = """from foo import get_constant as renamed 139 | 140 | def afunction(alist): 141 | return [x*renamed() for x in alist] 142 | 143 | def bfunction(blist): 144 | return [x*bar.renamed for x in blist] 145 | """ 146 | 147 | constant3_source = """from foo import CONSTANT 148 | 149 | def afunction(alist): 150 | return [x*CONSTANT for x in alist] 151 | 152 | def bfunction(blist): 153 | return [x*bar.CONSTANT for x in blist] 154 | """ 155 | 156 | constant3_target = """from foo import get_constant 157 | 158 | def afunction(alist): 159 | return [x*get_constant() for x in alist] 160 | 161 | def bfunction(blist): 162 | return [x*bar.CONSTANT for x in blist] 163 | """ 164 | 165 | constant1_source = """import foo 166 | 167 | def afunction(alist): 168 | return [x*CONSTANT for x in alist] 169 | 170 | def bfunction(blist): 171 | return [x*foo.CONSTANT for x in blist] 172 | 173 | def cfunction(clist): 174 | return [x*bar.CONSTANT for x in clist] 175 | """ 176 | 177 | 178 | constant1_target = """import foo 179 | 180 | def afunction(alist): 181 | return [x*CONSTANT for x in alist] 182 | 183 | def bfunction(blist): 184 | return [x*foo.get_constant() for x in blist] 185 | 186 | def cfunction(clist): 187 | return [x*bar.CONSTANT for x in clist] 188 | """ 189 | 190 | class ConstantFixerTest(FixerTest): 191 | 192 | def setUp(self): 193 | self.refactor = RefactoringTool(['fix_constant']).refactor_string 194 | 195 | def test_constant(self): 196 | self._test(constant1_source, constant1_target) 197 | self._test(constant2_source, constant2_target) 198 | self._test(constant3_source, constant3_target) 199 | self._test(open('constantexample.py').read(), 200 | open('constanttarget.py').read()) 201 | -------------------------------------------------------------------------------- /source/_tests/tests23.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import doctest 3 | import types 4 | import sys 5 | 6 | fail = False 7 | 8 | # Having them all in one suite doesn't work in Python 2.3. 9 | for testfile in ['test-3.6.txt', 10 | 'test-3.11.txt', 11 | 'test-3.12.txt', 12 | 'test-4.7.txt', 13 | 'test-5.4.txt', 14 | 'test-5.5.txt', 15 | 'test-7.10.txt', 16 | ]: 17 | test = open(testfile).read() 18 | mod = types.ModuleType('testmodule') 19 | mod.__doc__ = test 20 | mod.__file__ = '' 21 | sys.modules['testmodule'] = mod 22 | 23 | suite = doctest.DocTestSuite('testmodule') 24 | 25 | runner = unittest.TextTestRunner() 26 | results = runner.run(suite) 27 | if results.failures: 28 | fail = True 29 | 30 | del sys.modules['testmodule'] 31 | 32 | if fail: 33 | sys.exit(1) 34 | -------------------------------------------------------------------------------- /source/_tests/tests24.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import doctest 3 | import sys 4 | 5 | try: 6 | 1/0 7 | except ZeroDivisionError: 8 | traceback = sys.exc_info()[2] 9 | 10 | suite = doctest.DocFileSuite('test-1.2.txt', 11 | 'test-3.5.txt', 12 | 'test-2.6.txt', 13 | 'test-3.12.txt', 14 | 'test-3.14.txt', 15 | 'test-3.15.txt', 16 | 'test-3.29.txt', 17 | 'test-4.1.txt', 18 | 'test-4.2.txt', 19 | 'test-4.3.txt', 20 | 'test-4.4.txt', 21 | 'test-4.5.txt', 22 | 'test-4.6.txt', 23 | 'test-4.7.txt', 24 | 'test-5.1.txt', 25 | 'test-5.4.txt', 26 | 'test-5.5.txt', 27 | 'buffer25.txt', 28 | 'buffer26.txt', 29 | 'callable26.txt', 30 | 'dict26.txt', 31 | #'division26.txt', Same old bug in doctest... 32 | 'exceptions25.txt', 33 | 'exceptions25.txt', 34 | 'exception_syntax26.txt', 35 | 'exec25.txt', 36 | 'exec26.txt', 37 | #'input26.txt', This reads from stdin, so we skip it 38 | 'intern26.txt', 39 | 'inttypes26.txt', 40 | 'long25.txt', 41 | 'long26.txt', 42 | 'map25.txt', 43 | 'raise25.txt', 44 | 'repr26.txt', 45 | 'renames26.txt', 46 | 'round24.txt', 47 | 'unpacking25.txt', 48 | 'unpacking26.txt', 49 | 'upper24.txt', 50 | optionflags=doctest.ELLIPSIS, 51 | globs={'traceback': traceback, 52 | } 53 | ) 54 | 55 | runner = unittest.TextTestRunner() 56 | results = runner.run(suite) 57 | 58 | if results.failures: 59 | sys.exit(1) 60 | -------------------------------------------------------------------------------- /source/_tests/tests26.py: -------------------------------------------------------------------------------- 1 | from shouldraise import shouldRaise 2 | from test_fixers import IndentFixerTest, Name1FixerTest, Name2FixerTest, ConstantFixerTest 3 | import doctest 4 | import sys 5 | import unittest 6 | 7 | suite = doctest.DocFileSuite('test-1.1.txt', 8 | 'test-1.3.txt', 9 | 'test-1.4.txt', 10 | 'test-1.6.txt', 11 | # 'test-1.7.txt', Doesn't work because of a bug in doctests. 12 | 'test-1.8.txt', 13 | 'test-1.9.txt', 14 | 'test-1.10.txt', 15 | 'test-1.11.txt', 16 | 'test-1.12.txt', 17 | 'test-2.1.txt', 18 | 'test-2.2.txt', 19 | 'test-2.3.txt', 20 | 'test-2.4.txt', 21 | 'test-2.5.txt', 22 | 'test-3.1.txt', 23 | 'test-3.2.txt', 24 | 'test-3.3.txt', 25 | 'test-3.4.txt', 26 | 'test-3.5.txt', 27 | 'test-3.6.txt', 28 | 'test-3.7.txt', 29 | 'test-3.11.txt', 30 | 'test-3.12.txt', 31 | 'test-3.14.txt', 32 | 'test-3.15.txt', 33 | 'test-3.16.txt', 34 | 'test-3.17.txt', 35 | 'test-3.19.txt', 36 | 'test-3.20.txt', 37 | 'test-3.21.txt', 38 | 'test-3.23.txt', 39 | 'test-3.24.txt', 40 | 'test-3.25.txt', 41 | 'test-3.26.txt', 42 | #'test-3.27.txt', Bug in doctest 43 | #'test-3.28.txt', Bug in doctest 44 | 'test-3.29.txt', 45 | 'test-3.30.txt', 46 | 'test-4.1.txt', 47 | 'test-4.2.txt', 48 | 'test-4.3.txt', 49 | 'test-4.4.txt', 50 | 'test-4.5.txt', 51 | 'test-4.6.txt', 52 | 'test-4.7.txt', 53 | 'test-4.8.txt', 54 | 'test-4.9.txt', 55 | 'test-4.10.txt', 56 | 'test-4.11.txt', 57 | 'test-4.12.txt', 58 | 'test-4.16.txt', 59 | 'test-4.17.txt', 60 | 'test-4.18.txt', 61 | 'test-5.1.txt', 62 | 'test-5.2.txt', 63 | 'test-5.3.txt', 64 | 'test-5.4.txt', 65 | 'test-5.5.txt', 66 | 'test-5.6.txt', 67 | 'test-5.7.txt', 68 | 'test-5.9.txt', 69 | 'test-5.10.txt', 70 | #'test-5.11.txt', Doesn't work because of a bug in doctests. 71 | #'test-5.12.txt', Doesn't work because of a bug in doctests. 72 | 'test-5.14.txt', 73 | 'test-5.16.txt', 74 | #'test-5.18.txt', Doesn't work because of a bug in doctests. 75 | 'test-6.1.txt', 76 | 'test-7.7.txt', 77 | 'test-7.11.txt', 78 | 'csv26.txt', 79 | 'upper26.txt', 80 | 'map26.txt', 81 | optionflags=doctest.ELLIPSIS, 82 | globs={'shouldRaise': shouldRaise, 83 | } 84 | ) 85 | 86 | suite.addTests(unittest.makeSuite(IndentFixerTest)) 87 | suite.addTests(unittest.makeSuite(Name1FixerTest)) 88 | suite.addTests(unittest.makeSuite(Name2FixerTest)) 89 | suite.addTests(unittest.makeSuite(ConstantFixerTest)) 90 | runner = unittest.TextTestRunner() 91 | results = runner.run(suite) 92 | 93 | if results.failures: 94 | sys.exit(1) 95 | -------------------------------------------------------------------------------- /source/_tests/tests27.py: -------------------------------------------------------------------------------- 1 | from shouldraise import shouldRaise 2 | import doctest 3 | import sys 4 | import unittest 5 | 6 | suite = doctest.DocFileSuite('test-4.19.txt', 7 | optionflags=doctest.ELLIPSIS, 8 | globs={'shouldRaise': shouldRaise, 9 | } 10 | ) 11 | 12 | runner = unittest.TextTestRunner() 13 | results = runner.run(suite) 14 | 15 | sys.exit(1 if results.failures else 0) 16 | -------------------------------------------------------------------------------- /source/_tests/tests31.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import doctest 3 | from lib2to3 import main 4 | import shutil 5 | import os 6 | import sys 7 | from test_fixers import IndentFixerTest, Name1FixerTest, Name2FixerTest, ConstantFixerTest 8 | from shouldraise import shouldRaise 9 | 10 | files = [] 11 | for filename in ('test-3.11.txt', 'test-4.9.txt',): 12 | name2 = filename #os.path.join('source/_tests', filename) 13 | name3 = name2 + '.2to3' 14 | shutil.copy(name2, name3) 15 | files.append(name3) 16 | main.main('lib2to3.fixes', args=['-d', '-w', '--no-diffs'] + files) 17 | 18 | try: 19 | 1/0 20 | except ZeroDivisionError: 21 | traceback = sys.exc_info()[2] 22 | 23 | suite = doctest.DocFileSuite('test-1.1.txt', 24 | 'test-1.3.txt', 25 | 'test-1.5.txt', 26 | 'test-1.6.txt', 27 | 'test-1.7.txt', 28 | 'test-1.12.txt', 29 | 'test-2.1.txt', 30 | 'test-2.3.txt', 31 | 'test-2.4.txt', 32 | 'test-2.5.txt', 33 | 'test-2.6.txt', 34 | 'test-3.2.txt', 35 | 'test-3.3.txt', 36 | 'test-3.5.txt', 37 | 'test-3.6.txt', 38 | 'test-3.8.txt', 39 | 'test-3.9.txt', 40 | 'test-3.10.txt', 41 | 'test-3.11.txt.2to3', 42 | 'test-3.13.txt', 43 | 'test-3.14.txt', 44 | 'test-3.15.txt', 45 | 'test-3.16.txt', 46 | 'test-3.18.txt', 47 | 'test-3.19.txt', 48 | 'test-3.20.txt', 49 | 'test-3.22.txt', 50 | 'test-3.23.txt', 51 | 'test-3.24.txt', 52 | 'test-3.25.txt', 53 | 'test-3.26.txt', 54 | 'test-3.27.txt', 55 | 'test-3.28.txt', 56 | 'test-3.29.txt', 57 | 'test-3.30.txt', 58 | 'test-3.31.txt', 59 | 'test-4.4.txt', 60 | 'test-4.5.txt', 61 | 'test-4.6.txt', 62 | 'test-4.7.txt', 63 | 'test-4.8.txt', 64 | 'test-4.9.txt.2to3', 65 | 'test-4.11.txt', 66 | 'test-5.3.txt', 67 | 'test-5.4.txt', 68 | 'test-5.5.txt', 69 | 'test-5.6.txt', 70 | 'test-5.8.txt', 71 | 'test-5.9.txt', 72 | 'test-5.13.txt', 73 | 'test-5.15.txt', 74 | 'test-5.17.txt', 75 | 'test-5.18.txt', 76 | 'test-6.2.txt', 77 | 'test-7.1.txt', 78 | 'test-7.3.txt', 79 | 'test-7.4.txt', 80 | 'test-7.5.txt', 81 | 'test-7.6.txt', 82 | 'test-7.7.txt', 83 | 'test-7.8.txt', 84 | 'test-7.9.txt', 85 | 'test-7.11.txt', 86 | 'buffer26.txt', 87 | 'callable26.txt', 88 | 'callable30.txt', 89 | 'csv30.txt', 90 | 'dict26.txt', 91 | 'division26.txt', 92 | 'exception_syntax26.txt', 93 | 'exec26.txt', 94 | 'exec30.txt', 95 | 'getitem26.txt', 96 | #'input26.txt', Requires input frm stdin 97 | 'intern26.txt', 98 | 'inttypes26.txt', 99 | #'long26.txt', Damn doctests 100 | 'map26.txt', 101 | 'map30.txt', 102 | 'raise30.txt', 103 | 'renames26.txt', 104 | 'repr26.txt', 105 | 'unpacking26.txt', 106 | 'round26.txt', 107 | 'round30.txt', 108 | 'upper26.txt', 109 | 'unicodesort30.txt', 110 | optionflags=doctest.ELLIPSIS, 111 | globs={'traceback': traceback, 112 | 'shouldRaise': shouldRaise, 113 | } 114 | ) 115 | 116 | suite.addTests(unittest.makeSuite(IndentFixerTest)) 117 | suite.addTests(unittest.makeSuite(Name1FixerTest)) 118 | suite.addTests(unittest.makeSuite(Name2FixerTest)) 119 | suite.addTests(unittest.makeSuite(ConstantFixerTest)) 120 | runner = unittest.TextTestRunner() 121 | results = runner.run(suite) 122 | sys.exit(1 if results.failures else 0) 123 | -------------------------------------------------------------------------------- /source/_tests/tests32.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import doctest 3 | from lib2to3 import main 4 | import shutil 5 | import os 6 | import sys 7 | from test_fixers import IndentFixerTest, Name1FixerTest, Name2FixerTest, ConstantFixerTest 8 | from shouldraise import shouldRaise 9 | 10 | files = [] 11 | for filename in ('test-3.11.txt', 'test-4.9.txt',): 12 | name2 = filename #os.path.join('source/_tests', filename) 13 | name3 = name2 + '.2to3' 14 | shutil.copy(name2, name3) 15 | files.append(name3) 16 | main.main('lib2to3.fixes', args=['-d', '-w', '--no-diffs'] + files) 17 | 18 | try: 19 | 1/0 20 | except ZeroDivisionError: 21 | traceback = sys.exc_info()[2] 22 | 23 | suite = doctest.DocFileSuite('test-1.1.txt', 24 | 'test-1.3.txt', 25 | 'test-1.5.txt', 26 | 'test-1.6.txt', 27 | 'test-1.7.txt', 28 | 'test-1.12.txt', 29 | 'test-2.1.txt', 30 | 'test-2.3.txt', 31 | 'test-2.4.txt', 32 | # 'test-2.5.txt', Won't work on travis... 33 | 'test-2.6.txt', 34 | 'test-3.2.txt', 35 | 'test-3.3.txt', 36 | 'test-3.5.txt', 37 | 'test-3.6.txt', 38 | 'test-3.8.txt', 39 | 'test-3.9.txt', 40 | 'test-3.10.txt', 41 | 'test-3.11.txt.2to3', 42 | 'test-3.13.txt', 43 | 'test-3.14.txt', 44 | 'test-3.15.txt', 45 | 'test-3.16.txt', 46 | 'test-3.18.txt', 47 | 'test-3.19.txt', 48 | 'test-3.20.txt', 49 | 'test-3.22.txt', 50 | 'test-3.23.txt', 51 | 'test-3.24.txt', 52 | 'test-3.25.txt', 53 | 'test-3.26.txt', 54 | 'test-3.27.txt', 55 | 'test-3.28.txt', 56 | 'test-3.29.txt', 57 | 'test-3.30.txt', 58 | 'test-4.4.txt', 59 | 'test-4.5.txt', 60 | 'test-4.6.txt', 61 | 'test-4.7.txt', 62 | 'test-4.8.txt', 63 | 'test-4.9.txt.2to3', 64 | 'test-4.11.txt', 65 | 'test-5.3.txt', 66 | 'test-5.4.txt', 67 | 'test-5.5.txt', 68 | 'test-5.6.txt', 69 | 'test-5.8.txt', 70 | 'test-5.9.txt', 71 | 'test-5.13.txt', 72 | 'test-5.15.txt', 73 | 'test-5.17.txt', 74 | 'test-5.18.txt', 75 | 'test-6.2.txt', 76 | 'test-7.1.txt', 77 | 'test-7.3.txt', 78 | 'test-7.4.txt', 79 | 'test-7.12.txt', # Different order in 3.2 80 | 'test-7.6.txt', 81 | 'test-7.7.txt', 82 | 'test-7.8.txt', 83 | 'test-7.9.txt', 84 | 'test-7.11.txt', 85 | 'buffer26.txt', 86 | 'callable26.txt', 87 | 'callable30.txt', 88 | #'dict26.txt', # Different order in 3.2 89 | 'division26.txt', 90 | 'exception_syntax26.txt', 91 | 'exec26.txt', 92 | 'exec30.txt', 93 | 'getitem26.txt', 94 | #'input26.txt', Requires input frm stdin 95 | 'intern26.txt', 96 | 'inttypes26.txt', 97 | #'long26.txt', Damn doctests 98 | 'raise30.txt', 99 | 'renames26.txt', 100 | 'repr26.txt', 101 | 'unpacking26.txt', 102 | #'unicodesort26.txt', No worky on Travis 103 | #'unicodesort30.txt', No worky on Travis 104 | optionflags=doctest.ELLIPSIS, 105 | globs={'traceback': traceback, 106 | 'shouldRaise': shouldRaise, 107 | } 108 | ) 109 | 110 | suite.addTests(unittest.makeSuite(IndentFixerTest)) 111 | suite.addTests(unittest.makeSuite(Name1FixerTest)) 112 | suite.addTests(unittest.makeSuite(Name2FixerTest)) 113 | suite.addTests(unittest.makeSuite(ConstantFixerTest)) 114 | runner = unittest.TextTestRunner() 115 | results = runner.run(suite) 116 | 117 | sys.exit(1 if results.failures else 0) 118 | -------------------------------------------------------------------------------- /source/_tests/travistest.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | version = '%s%s' % (sys.version_info[0], sys.version_info[1]) 4 | testfile = 'tests' + version + '.py' 5 | with open(testfile, 'rb') as f: 6 | exec(f.read()) 7 | -------------------------------------------------------------------------------- /source/_tests/unicodesort26.txt: -------------------------------------------------------------------------------- 1 | >>> from functools import cmp_to_key 2 | >>> import locale 3 | >>> locale.setlocale(locale.LC_ALL, 'sv_SE.UTF-8') 4 | 'sv_SE.UTF-8' 5 | >>> corpus = ["art", "Älg", "ærgeligt", "Aardvark"] 6 | >>> sorted(corpus, key=cmp_to_key(locale.strcoll)) 7 | ['Aardvark', 'art', 'Älg', 'ærgeligt'] 8 | -------------------------------------------------------------------------------- /source/_tests/unicodesort30.txt: -------------------------------------------------------------------------------- 1 | >>> import locale 2 | >>> locale.setlocale(locale.LC_ALL, 'sv_SE.UTF-8') 3 | 'sv_SE.UTF-8' 4 | >>> corpus = ["art", "Älg", "ærgeligt", "Aardvark"] 5 | >>> sorted(corpus, key=locale.strxfrm) 6 | ['Aardvark', 'art', 'Älg', 'ærgeligt'] 7 | -------------------------------------------------------------------------------- /source/_tests/unpacking25.txt: -------------------------------------------------------------------------------- 1 | >>> def unpacks(a, (b, c)): 2 | ... return a,b,c 3 | 4 | >>> unpacks(1, (2,3)) 5 | (1, 2, 3) 6 | -------------------------------------------------------------------------------- /source/_tests/unpacking26.txt: -------------------------------------------------------------------------------- 1 | >>> def unpacks(a, b): 2 | ... return a,b[0],b[1] 3 | 4 | >>> unpacks(1, (2,3)) 5 | (1, 2, 3) 6 | -------------------------------------------------------------------------------- /source/_tests/upper24.txt: -------------------------------------------------------------------------------- 1 | >>> import string 2 | >>> string.upper('Dinsdale!') 3 | 'DINSDALE!' 4 | -------------------------------------------------------------------------------- /source/_tests/upper26.txt: -------------------------------------------------------------------------------- 1 | >>> 'Dinsdale!'.upper() 2 | 'DINSDALE!' 3 | 4 | >>> str.upper('Dinsdale!') 5 | 'DINSDALE!' 6 | -------------------------------------------------------------------------------- /source/about.rst: -------------------------------------------------------------------------------- 1 | .. raw:: latex 2 | 3 | \setcounter{secnumdepth}{-1} 4 | 5 | =========================================================================== 6 | About this book 7 | =========================================================================== 8 | 9 | This book is an open book whose source is available on GitHub\ [#GitHub]_ 10 | so that anyone can contribute to the book. The author and editor is 11 | Lennart Regebro, and future contributors will be listed here. 12 | 13 | This book is written in reStructuredText\ [#rest]_, typeset with 14 | Sphinx\ [#sphinx]_ and LaTeX\ [#latex]_ and printed on 15 | CreateSpace\ [#createspace]_. The typefaces used are TeX Gyre Schola for the 16 | main text, DejaVu Sans Mono for the code and Flux Bold for headings. 17 | 18 | The cover photo is taken by **Emmanuel "Tambako" Keller**, 19 | http://www.flickr.com/photos/tambako/ 20 | 21 | There is a list of errata on the books web page, http://python3porting.com/. 22 | 23 | Lennart Regebro would like to thank the following persons for helping making 24 | this book much better: 25 | 26 | **Andreas Braukmann**, **Nicholas Coghlan**, **Paul Everitt**, **Cris 27 | Ewing**, **Luca Fabbri**, **Russell Ferriday**, **Patrick Gerken**, **Janko 28 | Hauser**, **Domen Kožar**, **Baptiste Mispelon**, **Alexander Pilz**, 29 | **Johannes Raggam**, **Luciano Ramalho**, **Matt Russell**, **Christian 30 | Theune**, **Sébastien Verbois** and **Ian Watt** for generously funding the 31 | crowd funding campaign to make this book a community effort. 32 | 33 | **Brett Cannon** for writing the foreword. 34 | 35 | **Martin von Löwis** for indispensable feedback with his technical review of 36 | the book, as well as helping a lot on early efforts to support Python 3, like 37 | Distribute and zope.interface. 38 | 39 | **Godefroid Chapelle**, **Jasper Spaans** and **Wyn Williams** for reviewing the 40 | book for content, grammar and errors. 41 | 42 | **Brandon Craig Rhodes** for editing the first chapters of this book when the 43 | idea was that it would be a series of articles in Python Magazine. 44 | 45 | See also CONTRIBUTORS.txt for everyone who has made bugfixes, spelling 46 | corrections and other additions to this book. 47 | 48 | --------------------------------------------------------------------------- 49 | A note on terminology 50 | --------------------------------------------------------------------------- 51 | 52 | The process of switching to Python 3 is most often called "porting". The first 53 | two editions of this book was also called "Porting to Python 3". However, 54 | this gave the false impression that this was a lot of work, and that Python 3 55 | was "a different language" than Python 2. Although moving to Python 3 can be 56 | hard work, for the most time it is not, and as Python 2.5 and Python 3.2 are 57 | rapidly falling out of use, it gets even easier. 58 | 59 | For that reason, I changed the name of the book to "Supporting Python 3" as this 60 | better reflects the effort. It's not porting, the vast majority of your code 61 | will work with no changes, or changes that can be done automatically with 2to3. 62 | 63 | I have tried to avoid using the word "porting" in the text as a part of this. 64 | 65 | .. rubric:: Footnotes 66 | 67 | .. [#rest] `http://docutils.sourceforge.net/rst.html `_ 68 | .. [#sphinx] `http://sphinx.pocoo.org/ `_ 69 | .. [#latex] `http://www.latex-project.org/ `_ 70 | .. [#createspace] `https://www.createspace.com/ `_ 71 | .. [#GitHub] `https://github.com/regebro/supporting-python-3 `_ 72 | -------------------------------------------------------------------------------- /source/bookindex.rst: -------------------------------------------------------------------------------- 1 | =========================================================================== 2 | Supporting Python 3 3 | =========================================================================== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | about.rst 9 | foreword.rst 10 | intro.rst 11 | strategies.rst 12 | preparing.rst 13 | 2to3.rst 14 | problems.rst 15 | improving.rst 16 | noconv.rst 17 | cextensions.rst 18 | fixers.rst 19 | -------------------------------------------------------------------------------- /source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Supporting Python 3 documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Dec 15 13:40:39 2009. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.append(os.path.abspath('.')) 20 | 21 | # -- General configuration ---------- 22 | 23 | # Add any Sphinx extension module names here, as strings. They can be extensions 24 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 25 | extensions = ['sphinx.ext.todo',] 26 | 27 | # Add any paths that contain templates here, relative to this directory. 28 | templates_path = ['_templates'] 29 | 30 | # The suffix of source filenames. 31 | source_suffix = '.rst' 32 | 33 | # The encoding of source files. 34 | source_encoding = 'UTF-8' 35 | 36 | # The master toctree document. 37 | master_doc = 'bookindex' 38 | 39 | # General information about the project. 40 | project = u'Supporting Python 3' 41 | copyright = u'2011-2015, Lennart Regebro' 42 | 43 | # The version info for the project you're documenting, acts as replacement for 44 | # |version| and |release|, also used in various other places throughout the 45 | # built documents. 46 | # 47 | # The short X.Y version. 48 | version = '1.0' 49 | # The full version, including alpha/beta/rc tags. 50 | release = '1.0' 51 | 52 | # The language for content autogenerated by Sphinx. Refer to documentation 53 | # for a list of supported languages. 54 | #language = None 55 | 56 | # There are two options for replacing |today|: either, you set today to some 57 | # non-false value, then it is used: 58 | #today = '' 59 | # Else, today_fmt is used as the format for a strftime call. 60 | #today_fmt = '%B %d, %Y' 61 | 62 | # List of documents that shouldn't be included in the build. 63 | unused_docs = [] 64 | 65 | # List of directories, relative to source directory, that shouldn't be searched 66 | # for source files. 67 | exclude_trees = [] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'none' 85 | #pygments_style = 'sphinx' 86 | 87 | # A list of ignored prefixes for module index sorting. 88 | #modindex_common_prefix = [] 89 | 90 | #rst_prolog = "\n.. include:: /%s/source/intro.rst\n\n"%(os.path.abspath('.'),) 91 | 92 | # -- Options for HTML output ---------- 93 | 94 | # The theme to use for HTML and HTML Help pages. Major themes that come with 95 | # Sphinx are currently 'default' and 'sphinxdoc'. 96 | html_theme = 'agogo' 97 | 98 | # Theme options are theme-specific and customize the look and feel of a theme 99 | # further. For a list of options available for each theme, see the 100 | # documentation. 101 | html_theme_options = {'footerbg': '#FFCC33', 102 | 'headerbg': '#FFCC33', 103 | 'documentwidth': '40em', 104 | 'pagewidth': '60em', 105 | } 106 | 107 | # Add any paths that contain custom themes here, relative to this directory. 108 | #html_theme_path = [] 109 | 110 | # The name for this set of Sphinx documents. If None, it defaults to 111 | # " v documentation". 112 | html_title = u'Supporting Python 3: An in-depth guide' 113 | 114 | # A shorter title for the navigation bar. Default is the same as html_title. 115 | #html_short_title = None 116 | 117 | # The name of an image file (relative to this directory) to place at the top 118 | # of the sidebar. 119 | #html_logo = None 120 | 121 | # The name of an image file (within the static path) to use as favicon of the 122 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 123 | # pixels large. 124 | #html_favicon = None 125 | 126 | # Add any paths that contain custom static files (such as style sheets) here, 127 | # relative to this directory. They are copied after the builtin static files, 128 | # so a file named "default.css" will overwrite the builtin "default.css". 129 | html_static_path = ['_static'] 130 | 131 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 132 | # using the given strftime format. 133 | #html_last_updated_fmt = '%b %d, %Y' 134 | 135 | # If true, SmartyPants will be used to convert quotes and dashes to 136 | # typographically correct entities. 137 | html_use_smartypants = True 138 | 139 | # Custom sidebar templates, maps document names to template names. 140 | #html_sidebars = {} 141 | 142 | # Additional templates that should be rendered to pages, maps page names to 143 | # template names. 144 | #html_additional_pages = {} 145 | 146 | # If false, no module index is generated. 147 | html_use_modindex = False 148 | 149 | # If false, no index is generated. 150 | html_use_index = True 151 | 152 | # If true, the index is split into individual pages for each letter. 153 | #html_split_index = False 154 | 155 | # If true, links to the reST sources are added to the pages. 156 | html_show_sourcelink = False 157 | 158 | # If true, an OpenSearch description file will be output, and all pages will 159 | # contain a tag referring to it. The value of this option must be the 160 | # base URL from which the finished HTML is served. 161 | #html_use_opensearch = '' 162 | 163 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 164 | #html_file_suffix = '' 165 | 166 | # Output file base name for HTML help builder. 167 | htmlhelp_basename = 'SupportingPython3' 168 | 169 | html_show_sphinx = False 170 | 171 | # -- Options for LaTeX output ---------- 172 | 173 | # The paper size ('letter' or 'a4'). 174 | #latex_paper_size = '{6in,9in}' 175 | 176 | # The font size ('10pt', '11pt' or '12pt'). 177 | #latex_font_size = '11pt' 178 | 179 | # Grouping the document tree into LaTeX files. List of tuples 180 | # (source start file, target name, title, author, documentclass [howto/manual]). 181 | latex_documents = [ 182 | ('bookindex', 'SupportingPython3.tex', u'Supporting Python 3', 183 | u'Lennart Regebro', 'sphinxcustom'), 184 | ] 185 | 186 | # The name of an image file (relative to this directory) to place at the top of 187 | # the title page. 188 | #latex_logo = None 189 | 190 | # For "manual" documents, if this is true, then toplevel headings are parts, 191 | # not chapters. 192 | #latex_use_parts = False 193 | 194 | # Additional stuff for the LaTeX preamble. 195 | print_form = r"hdivide={0.75in,*,0.75in},vdivide={1in,*,1in},papersize={6in,9in}" 196 | screen_form = r"hdivide={1in,*,1in},vdivide={1in,*,1in},papersize={7.32in,11in}" 197 | tablets_form = r"hdivide={0.1in,*,0.1in},vdivide={0.7in,*,0.7in},papersize={7in,9in}" 198 | phones_form = r"hdivide={0.1in,*,0.1in},vdivide={0.7in,*,0.7in},papersize={4.8in,7.2in}" 199 | 200 | form = os.environ.get('BOOK_SIZE', 'print_form') 201 | 202 | print_latex_elements = { 203 | 'preamble': r""" 204 | \usepackage[dvips]{geometry} 205 | \geometry{bindingoffset=0in,""" + eval(form) + r"""} 206 | \authoraddress{Lennart Regebro\\*K\k{a}cka 15\\*54-004 Wroc\l{}aw\\*Poland\\*e-mail: regebro@gmail.com} 207 | \usepackage{fontspec} 208 | \usepackage{wasysym} 209 | \usepackage[titles]{tocloft} 210 | 211 | \hyphenpenalty=1500 212 | \widowpenalty=10000 213 | \clubpenalty=10000 214 | 215 | %% Font changes: 216 | %% Yes, use bold for regular: 217 | \setsansfont{Linux Biolinum O} 218 | \setmonofont[Scale=0.85]{DejaVu Sans Mono} 219 | \setmainfont[ 220 | Extension=.otf, 221 | UprightFont= *-regular, 222 | BoldFont=*-bold, 223 | ItalicFont=*-italic, 224 | BoldItalicFont=*-bolditalic, 225 | Mapping=tex-text 226 | ]{texgyreschola} 227 | %%\setmainfont{TeX Gyre Schola} 228 | \setlength{\headheight}{14pt} 229 | \renewcommand{\cftchapfont}{% 230 | \Large\sffamily 231 | } 232 | \renewcommand{\cftchappagefont}{% 233 | \Large\sffamily 234 | } 235 | \renewcommand{\cftsecfont}{% 236 | \large\sffamily 237 | } 238 | \renewcommand{\cftsecpagefont}{% 239 | \large\sffamily 240 | } 241 | \renewcommand{\textgreater}{>} 242 | 243 | %% Get rid of link colors 244 | \definecolor{TitleColor}{rgb}{0,0,0} 245 | \definecolor{InnerLinkColor}{rgb}{0,0,0} 246 | \definecolor{OuterLinkColor}{rgb}{0,0,0} 247 | 248 | %% Unicode character redefinitions 249 | \catcode`^^^^00a0=\active 250 | \def^^^^00a0{\nobreakspace} 251 | \catcode`^^^^00f6=\active 252 | \def^^^^00f6{\"{o}} 253 | \catcode`^^^^00dc=\active 254 | \def^^^^00dc{\"{U}} 255 | \catcode`^^^^00c4=\active 256 | \def^^^^00c4{\"{A}} 257 | \catcode`^^^^00e6=\active 258 | \def^^^^00e6{\ae} 259 | \catcode`^^^^00d6=\active 260 | \def^^^^00d6{\"{O}} 261 | \catcode`^^^^00e9=\active 262 | \def^^^^00e9{\'{e}} 263 | 264 | %% Unicode character redefinitions to checkboxes 265 | \catcode`^^^^2610=\active 266 | \def^^^^2610{\leavevmode\hbox{\wasyfamily\char50\hss}} 267 | \catcode`^^^^2611=\active 268 | \def^^^^2611{\ensuremath{\CheckedBox}} 269 | """, 270 | 'releasename': 'Revision', 271 | 'pointsize': '11pt', 272 | } 273 | 274 | 275 | latex_elements = print_latex_elements 276 | 277 | # Documents to append as an appendix to all manuals. 278 | latex_appendices = ['differences', 'stdlib'] 279 | 280 | # If false, no module index is generated. 281 | latex_use_modindex = True 282 | 283 | latex_show_pagerefs = True 284 | 285 | # PDFBuilder stuff 286 | 287 | pdf_documents = [ 288 | ('bookindex', 'SupportingPython3.pdf', u'Supporting Python 3', u'Lennart Regebro'), 289 | ] 290 | 291 | pdf_stylesheets = ['sphinx','kerning','a4'] 292 | 293 | pdf_appendices = ['differences', 'stdlib'] 294 | -------------------------------------------------------------------------------- /source/errata.rst: -------------------------------------------------------------------------------- 1 | ====== 2 | Errata 3 | ====== 4 | 5 | *Errors in spelling and grammar is not included in this list.* 6 | *Page numbers refer to the print edition.* 7 | 8 | Errata for the second edition, Revision 1.0 9 | =========================================== 10 | 11 | **Section 3.2, Page 21:** 12 | 13 | The second example should say:: 14 | 15 | >>> 5/2 16 | 2.5 17 | 18 | **Appendix I, Page ??:** 19 | 20 | In Python 3, division of integers will always return floats. 21 | 22 | 23 | Errata for the first edition 24 | ============================ 25 | 26 | **Section 3.2, Page 21:** 27 | 28 | The second example should say:: 29 | 30 | >>> 5/2 31 | 2.5 32 | 33 | **Section 3.5.1, Page 27:** 34 | 35 | The bug fix for ``total_ordering`` in Python 3.2 only solved some of the 36 | cases. It's still broken and can still in some cases cause infinite recursion. 37 | There is a `bug report `_ for this. 38 | 39 | **Section 6.7, Page 65:** 40 | 41 | Although the term *"Generator comprehension"* was used in the beginning of the 42 | discussion of the feature, it was deemed confusing and that terminology was 43 | replaced by the term *"Generator expression"*. 44 | 45 | Also, generator expressions have been around since Python 2.4 not 2.6. 46 | 47 | **Appendix I, Page ??:** 48 | 49 | In Python 3, division of integers will always return floats. 50 | -------------------------------------------------------------------------------- /source/foreword.rst: -------------------------------------------------------------------------------- 1 | =========================================================================== 2 | Foreword 3 | =========================================================================== 4 | 5 | .. raw:: latex 6 | 7 | \paragraph{\hfill{By Brett Cannon}} 8 | 9 | .. raw:: html 10 | 11 |

By Brett Cannon

12 | 13 | When I joined the python-dev mailing list back in June 2002, the term "Python 14 | 3000" was brought out every few months to represent an idea the Python 15 | development team wished they could act on, but for compatibility reasons could 16 | not. Saying we could do something "maybe for Python 3000" meant it had no chance 17 | of happening. 18 | 19 | But then we started to say something could happen in Python 3000 more and more 20 | often. Eventually it got to the point that "Python 3000" was referenced so often 21 | on python-dev that the acronym "py3k" was created out of our inherent 22 | programmer's laziness. Then we began to believe our own hype about what py3k 23 | would be like and how much better it would be. It got to the point where Andrew 24 | Kuchling created PEP 3100 (which was the original PEP 3000, which I eventually 25 | took responsibility for) to keep track of the various ideas we had for py3k in 26 | late 2004 as it was obvious around that time that we were actually going to go 27 | through with the "crazy" idea of making Python 3000 happen. This all led to serious 28 | development starting in March 2006 and culminating in the release of Python 3.0 29 | on December 3, 2008. 30 | 31 | While all of this was happening, there were mixed feelings from the community 32 | about the feasibility/sanity of creating Python 3. When PEP 3100 was created in 33 | 2004, Python's popularity took a very noticeable uptick. This trend continued 34 | and around 2006, when py3k development started in earnest, Python's popularity 35 | exceeded that of Perl. So while Python was becoming one of the most popular 36 | dynamic programming languages in the world, the development team was beginning 37 | to create the next major version which would break compatibility with the 38 | version of the language that all of these people were now learning. Some people 39 | called us a little nuts for obvious reasons. 40 | 41 | But we would like to think we knew what we were doing. While Python 2 is a great 42 | language, Guido and everyone on the development team knew it had its flaws (if 43 | it didn't then we would not have been able to create a PEP with nearly 100 44 | things we wanted to change). Guido also realized that more Python code would be 45 | written in the future then had been written to that point and into the future 46 | that would continue to be true. As a service to our community (and partly 47 | because it was fun) we decided we should try to fix some of our previous 48 | mistakes so that future Python code could be written better and faster than was 49 | possible with Python 2, hence why we created Python 3. 50 | 51 | But while some considered us a little nuts to break compatibility with Python 2, 52 | We also realized that we didn't want to leave our existing community behind and 53 | develop Python 3 only for new code. The development team knew as we created 54 | Python 3 that it was a superior language and so we wanted to share it with 55 | everyone by making sure they could bring their Python 2 code with them into 56 | their Python 3 work. From the beginning we made sure that the changes we made could 57 | either be warned against in the worst case, and automated in the best. 58 | Techniques we learned and tools we developed were used to port Python's 59 | extensive standard library so as to learn from our own mistakes and make sure 60 | that other people could port their own code. We always kept in the back of our 61 | heads the goal of making porting Python 2 code as easy as possible. 62 | 63 | The continual increase in the number of Python 3 projects available and the fact 64 | that all of the major Linux distributions ship with Python 3, or will do so in 65 | their next major release, is a testament that we didn't screw up. Guido always 66 | said it would take 3 to 5 years for Python 3 to gain traction within the 67 | community. The constant trend of Python 3 projects being released is a testament 68 | that the timeline Guido set out is turning out to be true as major libraries 69 | have already been ported, allowing their dependents to make the switch 70 | themselves. 71 | 72 | While some might question the utility in moving Python 2 code to Python 3, 73 | there are two things to keep in mind. One is that Python 3 is simply a nicer 74 | language than Python 2. While there are only a handful of major changes, it's 75 | all of the little changes that add up to make the experience of programming 76 | Python 3 that much more pleasant than Python 2. It's rather common to 77 | hear core developers say how they prefer coding in Python 3 over Python 2. I for 78 | one have simply stopped coding in Python 2 as it just feels slightly off 79 | compared to the more uniform feel of Python 3. And secondly, more code will be 80 | written in Python 3 than in Python 2 over the history of the Python language, so 81 | not porting means your project will eventually be left behind (this is already 82 | starting to happen for projects which have publicly said they will not switch, 83 | leading people to find alternatives for both their Python 2 and Python 3 code, to 84 | make sure they can switch to Python 3 when ready). Sitting idly by as the world 85 | changes around you is not a good thing to do if you want to stay relevant. 86 | 87 | I still remember the day that Python 3 was released. It was the end of the 88 | workday and I was on IRC in #python-dev waiting for Barry Warsaw, the Python 3.0 89 | release manager, to flip the switch on the release. When it was hit, I just 90 | swivelled around in my chair and told Guido that it was done; Python 3 was no 91 | longer a dream but an actual thing. I stood up, we gave each other an ecstatic 92 | high-five, and just smiled (the next day people asked us at work what we were so 93 | giddy about that night). 94 | 95 | At that moment, and to this day, the thought that Python 3 would flop or would not 96 | be worth the amount of time and effort my fellow core developers and I put into it 97 | never crossed my mind. And the fact that people care enough about seeing Python 98 | 3 work that there is now a book dedicated to helping people get from Python 2 to 99 | Python 3 is a testament that Python 3 has not, and will not, flop. 100 | 101 | .. raw:: latex 102 | 103 | \setcounter{secnumdepth}{2} 104 | -------------------------------------------------------------------------------- /source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome! 2 | ======== 3 | 4 | **Supporting Python 3** doesn't have to be daunting. This book guides you 5 | through the process of adding Python 3 support, from choosing a 6 | strategy to solving your distribution issues. Using plenty of code 7 | examples, it guides you across the hurdles and shows you the new Python features. 8 | 9 | 2016-10-23: The first release of the community version! 10 | ------------------------------------------------------- 11 | 12 | After almost a year of delay, the community version of the book is finally 13 | here! The book has been renamed to "Supporting Python 3" to signal both that 14 | something happened to the book, and to signal that it's no longer a question 15 | of porting. 16 | 17 | Another change is that this site will be rebuilt on a nightly basis, so it's 18 | always the latest version. I have also retired the paper version completely. 19 | If you want to sponsor a print run of the paper book, contact me! They make 20 | good giveaways for Python conferences! 21 | 22 | There are also PDF versions for screen_, which will also fit an A4 and letter 23 | paper, tablets_ and phones_ available for download. 24 | 25 | Contents 26 | -------- 27 | 28 | .. toctree:: 29 | :maxdepth: 1 30 | 31 | about.rst 32 | foreword.rst 33 | intro.rst 34 | strategies.rst 35 | preparing.rst 36 | 2to3.rst 37 | problems.rst 38 | improving.rst 39 | noconv.rst 40 | cextensions.rst 41 | fixers.rst 42 | differences.rst 43 | stdlib.rst 44 | 45 | There is also a list of :doc:`errata` that covers the earlier printed editions 46 | of "Porting to Python 3". 47 | 48 | About the Author 49 | ---------------- 50 | 51 | **Lennart Regebro**, has been a full time Python developer since 2001 and has 52 | been using Python 3 since early 2008. 53 | 54 | If you have any questions mail regebro@gmail.com . 55 | 56 | .. _screen: http://python3porting.com/pdfs/SupportingPython3-screen-1.0-latest.pdf 57 | .. _phones: http://python3porting.com/pdfs/SupportingPython3-phone-1.0-latest.pdf 58 | .. _tablets: http://python3porting.com/pdfs/SupportingPython3-tablet-1.0-latest.pdf 59 | -------------------------------------------------------------------------------- /source/intro.rst: -------------------------------------------------------------------------------- 1 | =========================================================================== 2 | Welcome to Python 3 3 | =========================================================================== 4 | 5 | On Christmas Day 1999 I sat down to write my first piece of software in Python. 6 | My experience seems to be typical for Python users. I was initially surprised 7 | that indentation was significant, it felt scary to not define variables and I 8 | was hesitant to use a dynamic language to make serious software. However, in no 9 | time at all these worries were gone and I noticed I wrote code faster than ever. 10 | 18 months later a friend hired me to his start-up to help him write a content 11 | management system and I ended up in the enviable position of being a full time 12 | Python developer. I don't even mention other languages on my CV anymore, because 13 | I don't want to use them. I've become a full fledged, fanatic, Pythonista. 14 | 15 | I got interested in Python 3 at EuroPython 2007 in lovely Vilnius. Guido van 16 | Rossum's keynote was about the upcoming changes in Python 3 and although he 17 | emphatically said that you could not run the same code under Python 2 and 18 | Python 3, I couldn't see many reasons why it couldn't be done, considering the 19 | forward compatibility that was planned for Python 2.6. So I started looking at 20 | the differences between Python 2 and Python 3 and tried to find out how to 21 | write code that would run under both versions and learned a whole lot about 22 | Python on the way. 23 | 24 | Most surprising was how little the fundamentals have changed. Writing code with 25 | Python 3 still feels just like the old comfortable shoes, but newly shined and 26 | with new laces. The hardest change to get used to is to remember that ``print`` 27 | is a function. The relatively small differences don't necessarily mean that 28 | supporting Python 3 is easy, but it can be and hopefully this book will make it 29 | even easier. 30 | 31 | --------------------------------------------------------------------------- 32 | The time is nigh 33 | --------------------------------------------------------------------------- 34 | 35 | Python 2 is still more widely used than Python 3. A major reason for not 36 | switching yet is that Python 2 is so good that most developers feel little 37 | incentive to switch. Although it has been officially declared that 38 | Python 2.7 will be the last version of Python 2, it will receive bug-fixes 39 | until 2020, so there are still a few years left. 40 | 41 | Despite this you should move to Python 3 as soon as as possible. Python 3 is 42 | a nicer cleaner language, and all the new cool features end up there. Those 43 | features are out of scope for this book, but I can just mention features like 44 | keyword-only arguments, chained exceptions, ``yield from``, and enums. 45 | 46 | If you are writing a package that other `developers` use, every day it doesn't 47 | support Python 3 is a day when you are blocking your users from using Python 3, 48 | and a day when Python 3 users have to look for another package than yours. In 49 | this case you should really try to add Python 3 support immediately, and if you 50 | have dependencies that does not support Python 3, then help with those first. 51 | 52 | --------------------------------------------------------------------------- 53 | What if I can't switch right now? 54 | --------------------------------------------------------------------------- 55 | 56 | .. index:: trove classifier 57 | 58 | In any case all the packages `you` depend on need support Python 3 before you 59 | can switch. Most packages that support Python 3 are listed on the CheeseShop 60 | under the "Python 3 packages" heading\ [#pypi3]_. That list is a list of all 61 | packages that includes ``"Programming Language :: Python :: 3"`` as a trove 62 | classifier in the package meta data. If your dependencies do not support Python 63 | 3 it is a good idea to contact the maintainers of your dependencies to see what 64 | plans they have for Python 3 support. Perhaps they do already support Python 3, 65 | but didn't update their meta data? Maybe they just didn't know anyone was 66 | waiting for Python 3 support? Maybe you can help? 67 | 68 | It's always a good idea to publish information on your plans for Python 3 on your 69 | software's homepage or in the description of the package on the CheeseShop. 70 | Include a list of your dependencies that are blocking you. That way your users can 71 | see if there is something they can help with. Open source is all about 72 | programmers helping each other; both using and contributing to each other's 73 | software. Supporting Python 3 is no different. 74 | 75 | And even if you aren't switching right now, there are things you can do already. 76 | Chapter 3, :ref:`preparing-chapter` lists things you should change before adding 77 | Python 3 support, and Chapter 6 :ref:`improving-chapter` lists modern idioms in 78 | Python that you already can use, depending on what Python versions you need to 79 | support. To ease the transition to Python 3, many of the new functions and 80 | modules in Python 3 have been backported to Python 2.6 or Python 2.7, and the 81 | only thing that stops you from using this already is if you need to support 82 | Python 2.5 or earlier. 83 | 84 | --------------------------------------------------------------------------- 85 | Python and its versions 86 | --------------------------------------------------------------------------- 87 | 88 | Since I started writing this book, Python 2.7 and Python 3.2 have been 89 | released. For the purposes of this book, Python 2.6 and Python 2.7 can be seen 90 | as equal. So most of the times the book says Python 2.6, you can read that as 91 | Python 2.6 or Python 2.7. 92 | 93 | Python 3.1 was released quite quickly after Python 3.0 and before any 94 | significant adoption of Python 3. Therefore it was decided to drop support for 95 | Python 3.0. As most platforms that support Python 3 already use Python 3.1 96 | for that support it is unlikely that you ever need to care about Python 3.0. 97 | When running your tests under Python 3 you only have to run it with Python 3.1 98 | and Python 3.2, and you can safely ignore Python 3.0. So when this book says 99 | Python 3, you can read that as Python 3.1 and later. 100 | 101 | --------------------------------------------------------------------------- 102 | Further resources 103 | --------------------------------------------------------------------------- 104 | 105 | There is still very little documentation on how to support Python 3. There is a 106 | short how-to in the Python 3.2 documentation at 107 | http://docs.python.org/dev/howto/pyporting.html. There is also a page on the 108 | official Python wiki at http://wiki.python.org/moin/PortingPythonToPy3k which 109 | contain a good list of other resources. 110 | 111 | If you need help, or if you want to help out, there is the 112 | ``python-porting@python.org`` mailing list. You can subscribe to it from 113 | http://mail.python.org/mailman/listinfo/python-porting. 114 | 115 | 116 | .. rubric:: Footnotes 117 | 118 | .. [#pypi3] `http://pypi.python.org/pypi?:action=browse&c=533&show=all `_ 119 | -------------------------------------------------------------------------------- /source/strategies.rst: -------------------------------------------------------------------------------- 1 | =========================================================================== 2 | Migration strategies 3 | =========================================================================== 4 | 5 | Making a new release of software that is backwards incompatible is a high risk 6 | strategy. When people need to rewrite their software, or maintain separate 7 | versions of their source code to support both versions of a language or a 8 | framework, the risk is that they never make the transition and stay on the old 9 | version forever, or worse, that they switch to another framework. 10 | 11 | For that reason Python versions 2.6 and 2.7 include both several forward 12 | compatibility features to enable you to write code for both Python 2 and 13 | Python 3, as well as support for migrating in the form of ``2to3``, a program 14 | and package that can convert code 15 | from Python 2 to Python 3. There are other techniques and strategies you can 16 | use and there are also different ways to use ``2to3``. Which strategy to use 17 | depends very much on what type of software you are converting. 18 | 19 | --------------------------------------------------------------------------- 20 | Only supporting Python 3 21 | --------------------------------------------------------------------------- 22 | 23 | The easiest case is when you only need to support one version of Python at a 24 | time. In these cases you can just convert your code to Python 3 and forget 25 | about Python 2. With this strategy you will first use the ``2to3`` tool to make 26 | an automatic conversion of most of the changes and then fix every problem that 27 | remains manually in the Python 3 code. You will probably also want to go 28 | through all of the converted code and clean it up, as the ``2to3`` conversions 29 | may not always be the most elegant solution for your case. 30 | 31 | --------------------------------------------------------------------------- 32 | Separate branches for Python 2 and Python 3 33 | --------------------------------------------------------------------------- 34 | 35 | .. index:: CheeseShop, PyPI, setup.py, Distutils 36 | 37 | If you need to continue to support Python 2, the simplest case is having a 38 | branch in your source tree with the code for Python 2 and another branch for 39 | Python 3. You will then have to make every change on the two different 40 | branches, which is a bit more work, but feasible if the code doesn't change that 41 | often. 42 | 43 | One problem with this strategy is that your distribution becomes complex, 44 | because you now have two distributions and you need to make sure that Python 3 45 | users get the Python 3 version and Python 2 users get the Python 2 version of 46 | your package. Solutions for this are documented in :ref:`distribution-section`. 47 | 48 | --------------------------------------------------------------------------- 49 | Converting to Python 3 with 2to3 50 | --------------------------------------------------------------------------- 51 | 52 | .. index:: 2to3 53 | 54 | In complex cases you can support both Python 2 and Python 3 by 55 | maintaining the source code in a Python 2 version and converting it with ``2to3`` for 56 | Python 3 support. That means you will have to run ``2to3`` each time you have 57 | made a code change so you can test it under Python 3, but on the other hand 58 | ``2to3`` deals with many of the differences. 59 | 60 | To do this you need a script that performs the ``2to3`` conversion, because 61 | doing all the steps manually quickly becomes really boring. Since you need to do 62 | the conversion every time you have changed something so you can run the tests 63 | under Python 3, you want to run the conversion only on the files that have been 64 | modified as the conversion can be rather slow. That means that the conversion 65 | script should compare time stamps on the files to see which ones have been 66 | modified and convert only them, which means the script can not be a trivial 67 | shell script. 68 | 69 | You can of course write these conversion scripts yourself, but you might not 70 | need to. If you are using Distutils it has support for running ``2to3`` as a 71 | part of the build process. This also solves the distribution problems, as this 72 | way you can distribute only Python 2 code and ``2to3`` will be run on that code 73 | during install when installed on Python 3. That way you don't have to have 74 | separate packages or even two copies of the code in your package. 75 | :ref:`distribution-section` also has information on this. 76 | 77 | However, the lazy coders approach here would be to use Distribute, as it 78 | includes some extensions to the ``2to3``-story. 79 | 80 | 81 | Using Distribute to support the 2to3 conversion 82 | =========================================================================== 83 | 84 | .. index:: Distribute, Setuptools, Distutils 85 | 86 | Distribute\ [#distribute]_ is a fork of Phillip J. Eby's popular Setuptools 87 | package and provides Python 3 compatibility, as well as extensions simplifying 88 | the support of Python 2 and Python 3 from the same source. Basically what 89 | Distribute has done is to extend the principles of the Distutils ``build_py_2to3`` 90 | command and integrated ``2to3`` into all parts of the packaging story. 91 | 92 | These changes will be merged back into Setuptools during 2013, but at the time 93 | of writing Setuptools doesn't support Python 3. 94 | 95 | With Distribute you can add a few extra parameters in the ``setup.py`` file to 96 | have ``2to3`` run the conversion at build time. This means you only need to have 97 | one version of the source in your version control system and you therefore only 98 | need to fix bugs once. You also need only one source release, so you only have 99 | to release the software once and there is only one package to download and 100 | install for both Python 2 and Python 3. 101 | 102 | You still need to run your tests under all versions of Python that you want to 103 | support, but Distribute includes a test command that will convert your code with 104 | ``2to3`` before running the tests. You can easily set up your package to use 105 | that. Then testing becomes just running ``python setup.py test`` once for every 106 | python version you want to support. 107 | 108 | The main drawback with this solution is that you can't use the earliest versions 109 | of ``2to3``, because they are too buggy. In practice it means you need to have 110 | Python 3.1 or later installed on the target machine. This is generally not a 111 | problem, as most platforms that support Python 3 already use Python 3.1 for 112 | that support. 113 | 114 | You can find examples of how to set up your module or package to use Distribute 115 | for your Python 3 support under :ref:`usingdistribute` as well as in the 116 | standard Distribute documentation\ [#distributedoc]_. 117 | 118 | --------------------------------------------------------------------------- 119 | Python 2 and Python 3 without conversion 120 | --------------------------------------------------------------------------- 121 | 122 | In many cases it's often perfectly feasible to modify the code so that it runs 123 | under both Python 2 and Python 3 without needing any conversion, although you 124 | have to apply several tricks to avoid the incompatibilities between Python 2 125 | and Python 3. 126 | 127 | Python 2.6 and 2.7 have a lot of forward compatibility, making supporting 128 | Python 2.6 and Python 3 much easier than supporting Python 2.5 and Python 3. 129 | Supporting 2.5 or even older versions means you have to employ more tricks. 130 | Python 3.3 also re-introduces the ``u''`` literal for strings, which helps with 131 | one of the major difficulties in supporting Python 3. 132 | 133 | .. index:: six 134 | 135 | Benjamin Peterson's excellent ``six`` module\ [#six]_ also helps by wrapping 136 | much of the incompatibilities, and since the need to support older Python 137 | versions is shrinking, supporting both Python 2 and Python 3 without conversion 138 | is becoming the preferred method. 139 | 140 | There are also cases where you can't use Distribute, or don't want to. You may 141 | need to distribute your code in a format that is not installable with Distutils 142 | and therefore not Distribute. In those cases you can't use Distribute's ``2to3`` 143 | support and then using ``2to3`` is more work and not using ``2to3`` becomes a 144 | more attractive prospect. 145 | 146 | Even if you do use ``2to3`` for your project as a whole, you still may 147 | end up with having to write some code so it runs on both Python 2 and Python 3 148 | without conversion. This is useful for bootstrapping scripts and setup scripts 149 | or if your code generates code from strings, for example to create command line 150 | scripts. You can of course have two separate strings depending on the Python 151 | version, or even run ``2to3`` on the string using ``lib2to3``. However, in these 152 | cases it's generally easier to make the generated code snippets run on all 153 | Python versions without ``2to3``. 154 | 155 | My recommendation for the development workflow if you want to support Python 156 | 3 without using ``2to3`` is to run ``2to3`` on the code once and then fix it up 157 | until it works on Python 3. Only then introduce Python 2 support into the 158 | Python 3 code, using ``six`` where needed. Add support for Python 2.7 first, 159 | and then Python 2.6. Doing it this way can sometimes result in a very quick 160 | and painless process. 161 | 162 | There is also a tool called ``python-modernize`` which will do a 163 | ``2to3``-type conversion of your code, but it will keep Python 2 164 | compatibility together with the ``six`` library. This can be a good start. 165 | 166 | More information on the techniques necessary to do this is in the chapter 167 | :ref:`noconv-chapter`. 168 | 169 | --------------------------------------------------------------------------- 170 | Using 3to2 171 | --------------------------------------------------------------------------- 172 | 173 | .. index:: 3to2 174 | 175 | The ``2to3`` tool is flexible enough for you to define what changes should be 176 | done by writing "fixers". Almost any kind of Python code conversion is 177 | imaginable here and ``3to2``\ [#3to2]_ is a set of fixers written by Joe Amenta 178 | that does the conversion from Python 3 to Python 2. This enables you to write 179 | your code for Python 3 and then convert it to Python 2 before release. 180 | 181 | However, there is no Distribute support for ``3to2`` and also Python 2.5 or 182 | earlier do not include the required ``lib2to3`` package. Therefore ``3to2`` 183 | currently remains only an interesting experiment, although this may change in 184 | the future. 185 | 186 | --------------------------------------------------------------------------- 187 | Which strategy is for you? 188 | --------------------------------------------------------------------------- 189 | 190 | Applications 191 | =========================================================================== 192 | 193 | Unless your code is a reusable package or framework you probably do not need to 194 | support older versions of Python, unless some of your customers are stuck on 195 | Python 2 while others demand that you support Python 3. In most cases you 196 | can just drop Python 2 support completely. 197 | 198 | 199 | Python modules and packages 200 | =========================================================================== 201 | 202 | If you are developing some sort of module or package that other Python 203 | developers use you would probably like to support both Python 2 and Python 3 204 | at the same time. The majority of your users will run Python 2 for some time to 205 | come, so you want to give them access to new functionality, but if you don't 206 | support Python 3, the users of Python 3 must find another package to fulfill 207 | their need. 208 | 209 | Today you typically only need to support Python 2.7, Python 3.4 and Python 210 | 3.5. These versions have enough backwards and forwards compatibility to make 211 | it easy to make code that runs on both Python 2 and Python 3. So this is the 212 | recommended strategy for reusable packages. 213 | 214 | --------------------------------------------------------------------------- 215 | Summary 216 | --------------------------------------------------------------------------- 217 | 218 | In general, if you write end-user software, you can just switch to Python 3, 219 | starting with a one-time run of ``2to3`` on your code. If you write a Python 220 | package you want to support both Python 2 and Python 3 at the same time, and 221 | you can drop Python 2.5 support, try first to support Python 2 and 3 without 222 | ``2to3`` conversion. 223 | 224 | If you need to support Python 2.5 or older, using ``2to3`` is often the best 225 | option. 226 | 227 | .. rubric:: Footnotes 228 | 229 | .. [#distribute] `http://pypi.python.org/pypi/distribute `_ 230 | .. [#distributedoc] `http://packages.python.org/distribute/python3.html `_ 231 | .. [#six] `http://pypi.python.org/pypi/six `_ 232 | .. [#3to2] `http://pypi.python.org/pypi/3to2 `_ 233 | -------------------------------------------------------------------------------- /source/toc.rst: -------------------------------------------------------------------------------- 1 | =========================================================================== 2 | Supporting Python 3 3 | =========================================================================== 4 | 5 | .. toctree:: 6 | :maxdepth: 3 7 | 8 | about.rst 9 | foreword.rst 10 | intro.rst 11 | strategies.rst 12 | preparing.rst 13 | 2to3.rst 14 | problems.rst 15 | improving.rst 16 | noconv.rst 17 | cextensions.rst 18 | fixers.rst 19 | differences.rst 20 | stdlib.rst 21 | --------------------------------------------------------------------------------