pax_global_header00006660000000000000000000000064137353114610014516gustar00rootroot0000000000000052 comment=83908884208b80f7d460f075baf5a70577e18d5e modernize-0.9rc0/000077500000000000000000000000001373531146100137675ustar00rootroot00000000000000modernize-0.9rc0/.github/000077500000000000000000000000001373531146100153275ustar00rootroot00000000000000modernize-0.9rc0/.github/workflows/000077500000000000000000000000001373531146100173645ustar00rootroot00000000000000modernize-0.9rc0/.github/workflows/main.yml000066400000000000000000000040161373531146100210340ustar00rootroot00000000000000name: CI on: push: branches: - master tags: - v* pull_request: jobs: tox: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: python-version: [3.6, 3.7, 3.8] os: [macOS-latest, ubuntu-latest, windows-latest] steps: - name: Checkout uses: actions/checkout@v2 - name: Set Up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Get pip cache dir id: pip-cache run: | echo "::set-output name=dir::$(pip cache dir)" - name: pip cache uses: actions/cache@v2 with: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml', 'setup.py', 'setup.cfg') }} restore-keys: | ${{ runner.os }}-pip- - name: Install run: | pip install tox - name: tox env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: tox -e py,lint,coveralls,release - name: upload dist uses: actions/upload-artifact@v2 with: name: ${{ matrix.os }}_${{ matrix.python-version}}_dist path: dist all-successful: # https://github.community/t/is-it-possible-to-require-all-github-actions-tasks-to-pass-without-enumerating-them/117957/4?u=graingert runs-on: ubuntu-latest needs: [tox] steps: - name: Download dists for PyPI uses: actions/download-artifact@v2 with: name: ubuntu-latest_3.8_dist path: dist - name: Display structure of donwloaded files run: ls -R - name: Publish package if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@master with: user: __token__ password: ${{ secrets.pypi_password }} - name: note that all tests succeeded run: echo "🎉" modernize-0.9rc0/.gitignore000066400000000000000000000037631373531146100157700ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ modernize-0.9rc0/.pre-commit-config.yaml000066400000000000000000000023071373531146100202520ustar00rootroot00000000000000repos: - repo: https://github.com/asottile/pyupgrade rev: v2.7.2 hooks: - id: pyupgrade args: ["--py36-plus"] - repo: https://github.com/psf/black rev: 20.8b1 hooks: - id: black args: ["--target-version", "py36"] - repo: https://gitlab.com/pycqa/flake8 rev: 3.8.3 hooks: - id: flake8 additional_dependencies: [flake8-2020] - repo: https://github.com/pycqa/isort rev: 5.4.2 hooks: - id: isort - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.6.0 hooks: - id: python-check-blanket-noqa - repo: https://github.com/pre-commit/pre-commit-hooks rev: v3.2.0 hooks: - id: check-merge-conflict - id: check-toml - id: check-yaml - id: mixed-line-ending - repo: https://github.com/prettier/prettier rev: 2.1.1 hooks: - id: prettier args: [--prose-wrap=always, --print-width=88] - repo: https://github.com/myint/autoflake rev: v1.4 hooks: - id: autoflake args: - --in-place - --remove-all-unused-imports - --expand-star-imports - --remove-duplicate-keys - --remove-unused-variables modernize-0.9rc0/.pre-commit-hooks.yaml000066400000000000000000000005341373531146100201300ustar00rootroot00000000000000- &modernize id: modernize name: modernize description: Modernizes Python code for eventual Python 3 migration. entry: modernize args: [--write, --fix=default, --nobackups] language: python types: [python] - <<: *modernize id: python-modernize name: modernize (deprecated) description: "Deprecated in favour of `id: modernize`" modernize-0.9rc0/.readthedocs.yml000066400000000000000000000002301373531146100170500ustar00rootroot00000000000000version: 2 sphinx: fail_on_warning: true python: version: 3.8 install: - method: pip path: . extra_requirements: - docs modernize-0.9rc0/CHANGELOG.rst000066400000000000000000000131161373531146100160120ustar00rootroot000000000000000.9rc0 (2020-10-01) =================== - move all code under the ``modernize`` namespace. - use flit to create PyPI distributions. 0.8.0 (2020-09-27) ================== Features -------- * add ``modernize`` console_script Breaking -------- * use ``fissix`` instead of deprecated ``lib2to3`` https://github.com/PyCQA/modernize/pull/203 modernize itself will no-longer run under Python 2, or Python <3.6, but will always be able to process Python 2 code. Bugfixes -------- * Fix for ``dict.viewitems()``, ``dict.iteritems()`` etc in chained calls https://github.com/PyCQA/modernize/pull/181 * Fix for SLASHEQUAL ``/=`` in fix_classic_divivion https://github.com/PyCQA/modernize/pull/197 Docs/tests/meta --------------- * Travis CI: Add Python 3.7, 3.8 and more flake8 tests https://github.com/PyCQA/modernize/pull/199 * ``six`` documentation has moved to https://six.readthedocs.io/ https://github.com/PyCQA/modernize/pull/198 * Fix typo in help string for --enforce option https://github.com/PyCQA/modernize/pull/191 * move project to https://github.com/PyCQA/modernize/ https://github.com/PyCQA/modernize/pull/220 https://github.com/PyCQA/modernize/pull/215 * switch from Travis CI to Github Actions https://github.com/PyCQA/modernize/pull/224 * use tox, pre-commit, pyupgrade and black https://github.com/PyCQA/modernize/pull/216 0.8rc4 (2020-09-27) =================== - Nothing changed yet. 0.8rc2 (2020-09-22) =================== Features -------- * add ``modernize`` console_script Meta ---- * move project to https://github.com/PyCQA/modernize/ https://github.com/PyCQA/modernize/pull/220 https://github.com/PyCQA/modernize/pull/215 * switch from Travis CI to Github Actions https://github.com/PyCQA/modernize/pull/224 * use tox, pre-commit, pyupgrade and black https://github.com/PyCQA/modernize/pull/216 Version 0.8rc1 ============== Released 2020-07-20. Breaking -------- * use ``fissix`` instead of deprecated ``lib2to3`` https://github.com/PyCQA/modernize/pull/203 modernize itself will no-longer run under Python 2, or Python <3.6, but will always be able to process Python 2 code. Bugfixes -------- * Fix for ``dict.viewitems()``, ``dict.iteritems()`` etc in chained calls https://github.com/PyCQA/modernize/pull/181 * Fix for SLASHEQUAL ``/=`` in fix_classic_divivion https://github.com/PyCQA/modernize/pull/197 Docs/tests/meta --------------- * Travis CI: Add Python 3.7, 3.8 and more flake8 tests https://github.com/PyCQA/modernize/pull/199 * ``six`` documentation has moved to https://six.readthedocs.io/ https://github.com/PyCQA/modernize/pull/198 * Fix typo in help string for --enforce option https://github.com/PyCQA/modernize/pull/191 Version 0.5-0.7 =============== * Added the opt-in classic_division fixer. * Updated the ``dict_six`` fixer to support ``six.viewitems()`` and friends. * New fixer for ``unichr``, changed to ``six.unichr``. * Documentation corrections. Version 0.4 =========== Released 2014-10-14. * `Documentation`_ has been added. * All fixers are now idempotent, which allows modernize to safely be applied more than once to the same source code. * The option to include default fixers when ``-f`` options are used is now spelled ``-f default``, rather than ``-f all``. * Added a ``--version`` option to the modernize command. * Calls to ``zip``, ``map``, and ``filter`` are now wrapped with ``list()`` in non-iterator contexts, to preserve Python 2 semantics. * Improved fixer for ``xrange`` using ``six.moves.range``. * Simplified use of ``six.with_metaclass`` for classes with more than one base class. * New fixer for imports of renamed standard library modules, using ``six.moves``. * New fixer to add ``from __future__ import absolute_import`` to all files with imports, and change any implicit relative imports to explicit (see PEP 328). * New fixer for ``input()`` and ``raw_input()``, changed to ``eval(input())`` and ``input()`` respectively. * New fixer for ``file()``, changed to ``open()``. There is also an opt-in fixer that changes both of these to ``io.open()``. * New fixer for ``(int, long)`` or ``(long, int)``, changed to ``six.integer_types``. Other references to ``long`` are changed to ``int``. * New fixer for ``basestring``, changed to ``six.string_types``. * New fixer for ``unicode``, changed to ``six.text_type``. * The ``fix_next`` fixer uses the ``next()`` builtin rather than ``six.advance_iterator``. * There is test coverage for all ``libmodernize`` fixers. * Simplified the implementation of many ``libmodernize`` fixers by extending similar fixers from ``lib2to3``. * Fixed a bug where ``fix_raise_six`` was adding an incorrect import statement. * Support for targeting Python 2.5 or lower has been officially dropped. (Previously some fixers did output constructs that were only added in Python 2.6, such as the ``except ... as`` construct, but this was not documented.) .. _Documentation: https://modernize.readthedocs.org/en/latest/ Version 0.3 =========== Released 2014-08-12. * New fixer for ``raise E, V, T``, changed to ``six.reraise(E, V, T)``. * New fixer for metaclasses, using ``six.with_metaclass``. * Avoid adding redundant parentheses to ``print(x)``. * modernize can now be installed and run on Python 3. * Fixed a bug where ``__future__`` imports were added multiple times. * Fixed a bug where fixer for ``zip()`` was recognising ``map()``. * The default is now to leave Unicode literals unchanged. (In previous versions this required the ``--compat-unicode`` option, which has now been removed.) A new ``--six-unicode`` option has been added to obtain the previous behaviour of adding ``six.u`` wrappers around Unicode literals. modernize-0.9rc0/LICENSE000066400000000000000000000033321373531146100147750ustar00rootroot00000000000000Copyright (c) 2011 by Armin Ronacher. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. Parts of this software package are built on fissix which is a backport of lib2to3 both licensed under the PSF license. For more information refer to the Python license: https://docs.python.org/license.html and the fissix license https://github.com/jreese/fissix/blob/master/LICENSE THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. modernize-0.9rc0/README.rst000066400000000000000000000034501373531146100154600ustar00rootroot00000000000000:: Python _ _ _ __ ___ __| |___ _ _ _ _ (_)______ | ' \/ _ \/ _` / -_) '_| ' \| |_ / -_) |_|_|_\___/\__,_\___|_| |_||_|_/__\___| .. image:: https://img.shields.io/coveralls/github/PyCQA/modernize?label=coveralls&logo=coveralls :alt: Coveralls :target: https://coveralls.io/github/PyCQA/modernize .. image:: https://img.shields.io/readthedocs/modernize?logo=read-the-docs :alt: Read the Docs :target: https://modernize.readthedocs.io/en/latest/ .. image:: https://img.shields.io/github/workflow/status/PyCQA/modernize/CI?label=GitHub%20Actions&logo=github :alt: GitHub Actions :target: https://github.com/PyCQA/modernize .. image:: https://img.shields.io/pypi/v/modernize?logo=pypi :alt: PyPI :target: https://pypi.org/project/modernize/ This library is a very thin wrapper around `fissix `_, a fork of lib2to3, to utilize it to make Python 2 code more modern with the intention of eventually porting it over to Python 3. The ``python -m modernize`` command works like ``python -m fissix``, see `fissix `_. Here's how you'd rewrite a single file:: python -m modernize -w example.py It does not guarantee, but it attempts to spit out a codebase compatible with Python 2.6+ or Python 3. The code that it generates has a runtime dependency on `six `_, unless the ``--no-six`` option is used. Version 1.9.0 or later of ``six`` is recommended. Some of the fixers output code that is not compatible with Python 2.5 or lower. **Documentation:** `modernize.readthedocs.io `_. See the ``LICENSE`` file for the license of ``modernize``. Using this tool does not affect licensing of the modernized code. modernize-0.9rc0/docs/000077500000000000000000000000001373531146100147175ustar00rootroot00000000000000modernize-0.9rc0/docs/Makefile000066400000000000000000000151731373531146100163660ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://www.sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/modernize.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/modernize.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/modernize" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/modernize" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." modernize-0.9rc0/docs/conf.py000066400000000000000000000202361373531146100162210ustar00rootroot00000000000000# # modernize documentation build configuration file, created by # sphinx-quickstart on Fri Sep 26 08:36:35 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. from __future__ import generator_stop # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ["sphinx.ext.intersphinx"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = "modernize" copyright = "2014, python-modernize team; 2020 pycqa/modernize team" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = "0.7" # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "python2": ("https://docs.python.org/2", None), "six": ("https://six.readthedocs.io/", None), "fissix": ("https://fissix.readthedocs.io/en/latest/", None), } # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = "modernizedoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ( "index", "modernize.tex", "modernize Documentation", "pycqa/modernize team", "manual", ) ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ("index", "modernize", "modernize Documentation", ["pycqa/modernize team"], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( "index", "modernize", "modernize Documentation", "pycqa/modernize team", "modernize", "One line description of project.", "Miscellaneous", ) ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False modernize-0.9rc0/docs/fixers.rst000066400000000000000000000223531373531146100167560ustar00rootroot00000000000000Fixers ====== Fixers come in two types: Default_ and Opt-in_. Default fixers should not break code except for corner cases, and are idempotent (applying them more than once to given source code will make no changes after the first application). Opt-in fixers are allowed to break these rules. Python 2 code from Python 2.6 and older will be upgraded to code that is compatible with Python 2.6, 2.7, and Python 3. If code is using a feature unique to Python 2.7, it will not be downgraded to work with Python 2.6. For example, ``dict.viewitems()`` usage will not be removed to make the code compatible with Python 2.6. Some fixers rely on the latest release of the `six project`_ to work (see `Fixers requiring six`_). If you wish to turn off these fixers to avoid an external dependency on ``six``, then use the ``--no-six`` flag. Fixers use the API defined by fissix. For details of how this works, and how to implement your own fixers, see `Creating a fixer, at python3porting.com `_. ``python -m modernize`` will try to load fixers whose full dotted-path is specified as a ``-f`` argument, but will fail if they are not found. By default, fixers will not be found in the current directory; use ``--fixers-here`` to make ``python -m modernize`` look for them there, or see the `Python tutorial on modules `_ (in particular, the parts on the `search path `_ and `packages `_) for more info on how Python finds modules. Default ------- A default fixer will be enabled when: - Either no ``-f``/``--fix`` options are used, or ``-f default``/``--fix=default`` is used, or the fixer is listed explicitly in an ``-f``/``--fix`` option; and - The fixer is not listed in an ``-x``/``--nofix`` option; and - For fixers that are dependent on the `six project`_, ``--no-six`` is *not* specified (see `Fixers requiring six`_). The ``-x``/``--nofix`` and ``--no-six`` options always override fixers specified using ``-f``/``--fix``. The ``--six-unicode`` and ``--future-unicode`` options also disable fixers that are not applicable for those options. Fixers requiring six ++++++++++++++++++++ The `six project`_ provides the ``six`` module which contains various tidbits in helping to support Python 2/3 code. All ``six``-related fixers assume the latest version of ``six`` is installed. .. attribute:: basestring Replaces all references to :func:`basestring` with :data:`six.string_types`. .. versionadded:: 0.4 .. attribute:: dict_six Fixes various methods on the ``dict`` type for getting all keys, values, or items. E.g.:: x.values() x.itervalues() x.viewvalues() becomes:: list(x.values()) six.itervalues(x) six.viewvalues(x) Care is taken to only call ``list()`` when not in an iterating context (e.g. not the iterable for a ``for`` loop). .. attribute:: filter When a call to :func:`filter ` is discovered, ``from six.moves import filter`` is added to the module. Wrapping the use in a call to ``list()`` is done when necessary. .. attribute:: imports_six Uses :mod:`six.moves` to fix various renamed modules, e.g.:: import ConfigParser ConfigParser.ConfigParser() becomes:: import six.moves.configparser six.moves.configparser.ConfigParser() The modules in Python 2 whose renaming in Python 3 is supported are: - ``__builtin__`` - ``_winreg`` - ``BaseHTTPServer`` - ``CGIHTTPServer`` - ``ConfigParser`` - ``copy_reg`` - ``Cookie`` - ``cookielib`` - ``cPickle`` - ``Dialog`` - ``dummy_thread`` - ``FileDialog`` - ``gdbm`` - ``htmlentitydefs`` - ``HTMLParser`` - ``httplib`` - ``Queue`` - ``repr`` - ``robotparser`` - ``ScrolledText`` - ``SimpleDialog`` - ``SimpleHTTPServer`` - ``SimpleXMLRPCServer`` - ``SocketServer`` - ``thread`` - ``Tix`` - ``tkColorChooser`` - ``tkCommonDialog`` - ``Tkconstants`` - ``Tkdnd`` - ``tkFileDialog`` - ``tkFont`` - ``Tkinter`` - ``tkMessageBox`` - ``tkSimpleDialog`` - ``ttk`` - ``xmlrpclib`` .. versionadded:: 0.4 .. attribute:: input_six Changes:: input(x) raw_input(x) to:: from six.moves import input eval(input(x)) input(x) .. versionadded:: 0.4 .. attribute:: int_long_tuple Changes ``(int, long)`` or ``(long, int)`` to :data:`six.integer_types`. .. versionadded:: 0.4 .. attribute:: map If a call to :func:`map ` is discovered, ``from six.moves import map`` is added to the module. Wrapping the use in a call to ``list()`` is done when necessary. .. attribute:: metaclass Changes:: class Foo: __metaclass__ = Meta to:: import six class Foo(six.with_metaclass(Meta)): pass .. seealso:: :func:`six.with_metaclass` .. attribute:: raise_six Changes ``raise E, V, T`` to ``six.reraise(E, V, T)``. .. attribute:: unicode_type Changes all reference of :func:`unicode ` to :data:`six.text_type`. .. attribute:: urllib_six Changes:: from urllib import quote_plus quote_plus('hello world') to:: from six.moves.urllib.parse import quote_plus quote_plus('hello world') .. attribute:: unichr Changes all reference of :func:`unichr ` to :data:`six.unichr`. .. attribute:: xrange_six Changes:: w = xrange(x) y = range(z) to:: from six.moves import range w = range(x) y = list(range(z)) Care is taken not to call ``list()`` when ``range()`` is used in an iterating context. .. attribute:: zip If :func:`zip ` is called, ``from six.moves import zip`` is added to the module. Wrapping the use in a call to ``list()`` is done when necessary. ``fissix`` fixers +++++++++++++++++ Some :doc:`fixers from fissix ` in Python's standard library are run by default unmodified as their transformations are Python 2 compatible. - :attr:`apply ` - :attr:`except ` - :attr:`exec ` - :attr:`execfile ` - :attr:`exitfunc ` - :attr:`funcattrs ` - :attr:`has_key ` - :attr:`idioms ` - :attr:`long ` - :attr:`methodattrs ` - :attr:`ne ` - :attr:`numliterals ` - :attr:`operator ` - :attr:`paren ` - :attr:`reduce ` - :attr:`repr ` - :attr:`set_literal ` - :attr:`standarderror ` - :attr:`sys_exc ` - :attr:`throw ` - :attr:`tuple_params ` - :attr:`types ` - :attr:`ws_comma ` - :attr:`xreadlines ` Fixers with no dependencies +++++++++++++++++++++++++++ .. attribute:: file Changes all calls to :func:`file ` to :func:`open `. .. versionadded:: 0.4 .. attribute:: import Changes implicit relative imports to explicit relative imports and adds ``from __future__ import absolute_import``. .. versionadded:: 0.4 .. attribute:: next Changes all method calls from ``x.next()`` to ``next(x)``. .. attribute:: print Changes all usage of the ``print`` statement to use the :func:`print` function and adds ``from __future__ import print_function``. .. attribute:: raise Changes comma-based ``raise`` statements from:: raise E, V raise (((E, E1), E2), E3), V to:: raise E(V) raise E(V) Opt-in ------ To specify an opt-in fixer while also running all the default fixers, make sure to specify the ``-f default`` or ``--fix=default`` option, e.g.:: python -m modernize -f default -f modernize.fixes.fix_open .. attribute:: classic_division When a use of the division operator -- ``/`` -- is found, add ``from __future__ import division`` and change the operator to ``//``. If ``from __future__ import division`` is already present, this fixer is skipped. This is intended for use in programs where ``/`` is conventionally only used for integer division, or where it is intended to do a manual pass after running ``python -m modernize`` to look for cases that should not have been changed to ``//``. The results of division on non-integers may differ after running this fixer: for example, ``3.5 / 2 == 1.75``, but ``3.5 // 2 == 1.0``. Some objects may override the ``__div__`` method for a use other than division, and thus would break when changed to use a ``__floordiv__`` method instead. This fixer is opt-in because it may change the meaning of code as described above. .. versionadded:: 1.0 .. attribute:: open When a call to :func:`open ` is discovered, add ``from io import open`` at the top of the module so as to use :func:`io.open` instead. This fixer is opt-in because it changes what object is returned by a call to ``open()``. .. versionadded:: 0.4 .. _six project: https://six.readthedocs.io/ modernize-0.9rc0/docs/index.rst000066400000000000000000000037401373531146100165640ustar00rootroot00000000000000:tocdepth: 3 Modernize ///////// .. toctree:: :maxdepth: 2 fixers Purpose of the project ====================== .. TODO Explain WHY someone would want to have their code be Python 2/3 compatible This library is a very thin wrapper around ``fissix`` to utilize it to make Python 2 code more modern with the intention of eventually porting it over to Python 3. The ``python -m modernize`` command works like `fissix `_. Here's how you'd rewrite a single file:: python -m modernize -w example.py See the ``LICENSE`` file for the license of ``python -m modernize``. Using this tool does not affect licensing of the modernized code. The `project website`_ can be found on GitHub and the PyPI project name is modernize_ A note about handling text literals =================================== .. TODO Explain what a "native string" is if it is going to be referenced - By default modernize does not change Unicode literals at all, which means that you can take advantage of `PEP 414 `_. This is the simplest option if you only want to support Python 3.3 and above along with Python 2. - Alternatively, there is the ``--six-unicode`` flag which will wrap Unicode literals with the six helper function ``six.u()`` using the ``modernize.fixes.fix_unicode`` fixer. This is useful if you want to support Python 3.1 and Python 3.2 without bigger changes. - The last alternative is the ``--future-unicode`` flag which imports the ``unicode_literals`` from the ``__future__`` module using the ``modernize.fixes.fix_unicode_future`` fixer. This requires Python 2.6 and later, and will require that you mark bytestrings with ``b''`` and native strings in ``str('')`` or something similar that survives the transformation. Indices and tables ////////////////// * :ref:`genindex` * :ref:`search` .. _modernize: https://pypi.python.org/pypi/modernize .. _project website: https://github.com/pycqa/modernize modernize-0.9rc0/docs/make.bat000066400000000000000000000145131373531146100163300ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.https://pypi.org/project/sphinx/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\modernize.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\modernize.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end modernize-0.9rc0/modernize/000077500000000000000000000000001373531146100157635ustar00rootroot00000000000000modernize-0.9rc0/modernize/__init__.py000066400000000000000000000002311373531146100200700ustar00rootroot00000000000000""" A hack on top of fissix (lib2to3 fork) for modernizing code for hybrid codebases. """ from __future__ import generator_stop __version__ = "0.9rc0" modernize-0.9rc0/modernize/__main__.py000066400000000000000000000001731373531146100200560ustar00rootroot00000000000000from __future__ import generator_stop import sys from .main import main if __name__ == "__main__": sys.exit(main()) modernize-0.9rc0/modernize/fixes/000077500000000000000000000000001373531146100171015ustar00rootroot00000000000000modernize-0.9rc0/modernize/fixes/__init__.py000066400000000000000000000032301373531146100212100ustar00rootroot00000000000000from __future__ import generator_stop fissix_fix_names = { "fissix.fixes.fix_apply", "fissix.fixes.fix_except", "fissix.fixes.fix_exec", "fissix.fixes.fix_execfile", "fissix.fixes.fix_exitfunc", "fissix.fixes.fix_funcattrs", "fissix.fixes.fix_has_key", "fissix.fixes.fix_idioms", "fissix.fixes.fix_long", "fissix.fixes.fix_methodattrs", "fissix.fixes.fix_ne", "fissix.fixes.fix_numliterals", "fissix.fixes.fix_operator", "fissix.fixes.fix_paren", "fissix.fixes.fix_reduce", "fissix.fixes.fix_renames", "fissix.fixes.fix_repr", "fissix.fixes.fix_set_literal", "fissix.fixes.fix_standarderror", "fissix.fixes.fix_sys_exc", "fissix.fixes.fix_throw", "fissix.fixes.fix_tuple_params", "fissix.fixes.fix_types", "fissix.fixes.fix_ws_comma", "fissix.fixes.fix_xreadlines", } # fixes that involve using six six_fix_names = { "modernize.fixes.fix_basestring", "modernize.fixes.fix_dict_six", "modernize.fixes.fix_filter", "modernize.fixes.fix_imports_six", "modernize.fixes.fix_itertools_six", "modernize.fixes.fix_itertools_imports_six", "modernize.fixes.fix_input_six", "modernize.fixes.fix_int_long_tuple", "modernize.fixes.fix_map", "modernize.fixes.fix_metaclass", "modernize.fixes.fix_raise_six", "modernize.fixes.fix_unicode", "modernize.fixes.fix_unicode_type", "modernize.fixes.fix_urllib_six", "modernize.fixes.fix_unichr", "modernize.fixes.fix_xrange_six", "modernize.fixes.fix_zip", } # Fixes that are opt-in only. opt_in_fix_names = { "modernize.fixes.fix_classic_division", "modernize.fixes.fix_open", } modernize-0.9rc0/modernize/fixes/fix_basestring.py000066400000000000000000000005311373531146100224610ustar00rootroot00000000000000from __future__ import generator_stop from fissix import fixer_base, fixer_util class FixBasestring(fixer_base.BaseFix): BM_compatible = True PATTERN = """'basestring'""" def transform(self, node, results): fixer_util.touch_import(None, "six", node) return fixer_util.Name("six.string_types", prefix=node.prefix) modernize-0.9rc0/modernize/fixes/fix_classic_division.py000066400000000000000000000013551373531146100236520ustar00rootroot00000000000000from __future__ import generator_stop from fissix import fixer_base, pytree from fissix.pgen2 import token from .. import utils class FixClassicDivision(fixer_base.BaseFix): PATTERN = """ '/=' | '/' """ def start_tree(self, tree, name): super().start_tree(tree, name) self.skip = "division" in tree.future_features def match(self, node): return node.value in ("/", "/=") def transform(self, node, results): if self.skip: return utils.add_future(node, "division") if node.value == "/": return pytree.Leaf(token.DOUBLESLASH, "//", prefix=node.prefix) else: return pytree.Leaf(token.DOUBLESLASHEQUAL, "//=", prefix=node.prefix) modernize-0.9rc0/modernize/fixes/fix_dict_six.py000066400000000000000000000035121373531146100221300ustar00rootroot00000000000000""" Fixer for iterkeys() -> six.iterkeys(), and similarly for iteritems and itervalues. """ from __future__ import generator_stop # Local imports from fissix import fixer_util, pytree from fissix.fixes import fix_dict class FixDictSix(fix_dict.FixDict): def transform_iter(self, node, results): """Call six.(iter|view)items() and friends.""" # Make sure six is imported. fixer_util.touch_import(None, "six", node) # Copy of self.transform() from fissix.fix_dict with some changes to # use the six.* methods. head = results["head"] method = results["method"][0] # Extract node for method name tail = results["tail"] syms = self.syms method_name = method.value name = fixer_util.Name("six." + method_name, prefix=node.prefix) assert method_name.startswith(("iter", "view")), repr(method) assert method_name[4:] in ("keys", "items", "values"), repr(method) head = [n.clone() for n in head] tail = [n.clone() for n in tail] new = pytree.Node(syms.power, head) new.prefix = "" new = fixer_util.Call(name, [new]) if tail: new = pytree.Node(syms.power, [new] + tail) new.prefix = node.prefix return new def transform(self, node, results): method = results["method"][0] method_name = method.value if method_name in ("keys", "items", "values"): return super().transform(node, results) else: return self.transform_iter(node, results) def in_special_context(self, node, isiter): # Redefined from parent class to make "for x in d.items()" count as # in special context; fissix only counts for loops as special context # for the iter* methods. return super().in_special_context(node, True) modernize-0.9rc0/modernize/fixes/fix_file.py000066400000000000000000000006501373531146100212410ustar00rootroot00000000000000from __future__ import generator_stop from fissix import fixer_base from fissix.fixer_util import Name class FixFile(fixer_base.BaseFix): BM_compatible = True order = "pre" PATTERN = """ power< name='file' trailer< '(' any ')' > any* > """ def transform(self, node, results): name = results["name"] name.replace(Name("open", prefix=name.prefix)) modernize-0.9rc0/modernize/fixes/fix_filter.py000066400000000000000000000011541373531146100216070ustar00rootroot00000000000000# Copyright 2008 Armin Ronacher. # Licensed to PSF under a Contributor Agreement. from __future__ import generator_stop from fissix import fixer_util from fissix.fixes import fix_filter from .. import utils class FixFilter(fix_filter.FixFilter): skip_on = "six.moves.filter" def transform(self, node, results): result = super().transform(node, results) if not utils.is_listcomp(result): # Keep performance improvement from six.moves.filter in iterator # contexts on Python 2.7. fixer_util.touch_import("six.moves", "filter", node) return result modernize-0.9rc0/modernize/fixes/fix_import.py000066400000000000000000000014351373531146100216360ustar00rootroot00000000000000from __future__ import generator_stop from fissix.fixer_util import syms from fissix.fixes import fix_import from .. import utils class FixImport(fix_import.FixImport): # Make sure this runs before any other fixer to guarantee that any other # added absolute_import doesn't block this fixer's execution. run_order = 1 def transform(self, node, results): if self.skip: return # We're not interested in __future__ imports here if ( node.type == syms.import_from and getattr(results["imp"], "value", None) == "__future__" ): return # If there are any non-future imports, add absolute_import utils.add_future(node, "absolute_import") return super().transform(node, results) modernize-0.9rc0/modernize/fixes/fix_imports_six.py000066400000000000000000000046451373531146100227120ustar00rootroot00000000000000from __future__ import generator_stop from fissix.fixes import fix_imports class FixImportsSix(fix_imports.FixImports): mapping = { "__builtin__": "six.moves.builtins", "_winreg": "six.moves.winreg", "BaseHTTPServer": "six.moves.BaseHTTPServer", "CGIHTTPServer": "six.moves.CGIHTTPServer", "ConfigParser": "six.moves.configparser", "copy_reg": "six.moves.copyreg", "Cookie": "six.moves.http_cookies", "cookielib": "six.moves.http_cookiejar", "cPickle": "six.moves.cPickle", "Dialog": "six.moves.tkinter_dialog", "dummy_thread": "six.moves._dummy_thread", # cStringIO.StringIO() # email.MIMEBase # email.MIMEMultipart # email.MIMENonMultipart # email.MIMEText "FileDialog": "six.moves.tkinter_filedialog", "gdbm": "six.moves.dbm_gnu", "htmlentitydefs": "six.moves.html_entities", "HTMLParser": "six.moves.html_parser", "httplib": "six.moves.http_client", # intern() # itertools.ifilter() # itertools.ifilterfalse() # itertools.imap() # itertools.izip() # itertools.zip_longest() # pipes.quote "Queue": "six.moves.queue", # reduce() # reload() "repr": "six.moves.reprlib", "robotparser": "six.moves.urllib_robotparser", "ScrolledText": "six.moves.tkinter_scrolledtext", "SimpleDialog": "six.moves.tkinter_simpledialog", "SimpleHTTPServer": "six.moves.SimpleHTTPServer", "SimpleXMLRPCServer": "six.moves.xmlrpc_server", "SocketServer": "six.moves.socketserver", "thread": "six.moves._thread", "Tix": "six.moves.tkinter_tix", "tkColorChooser": "six.moves.tkinter_colorchooser", "tkCommonDialog": "six.moves.tkinter_commondialog", "Tkconstants": "six.moves.tkinter_constants", "Tkdnd": "six.moves.tkinter_dnd", "tkFileDialog": "six.moves.tkinter_filedialog", "tkFont": "six.moves.tkinter_font", "Tkinter": "six.moves.tkinter", "tkMessageBox": "six.moves.tkinter_messagebox", "tkSimpleDialog": "six.moves.tkinter_tksimpledialog", "ttk": "six.moves.tkinter_ttk", # urllib "urlparse": "six.moves.urllib.parse", # UserDict.UserDict # UserList.UserList # UserString.UserString "xmlrpclib": "six.moves.xmlrpc_client", } modernize-0.9rc0/modernize/fixes/fix_input_six.py000066400000000000000000000023331373531146100223440ustar00rootroot00000000000000# This is a derived work of Lib/lib2to3/fixes/fix_input.py and # Lib/lib2to3/fixes/fix_raw_input.py. Those files are under the # copyright of the Python Software Foundation and licensed under the # Python Software Foundation License 2. # # Copyright notice: # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013, 2014 Python Software Foundation. All rights reserved. from __future__ import generator_stop from fissix import fixer_base, fixer_util from fissix.fixer_util import Call, Name class FixInputSix(fixer_base.ConditionalFix): BM_compatible = True order = "pre" skip_on = "six.moves.input" PATTERN = """ power< (name='input' | name='raw_input') trailer< '(' [any] ')' > any* > """ def transform(self, node, results): if self.should_skip(node): return fixer_util.touch_import("six.moves", "input", node) name = results["name"] if name.value == "raw_input": name.replace(Name("input", prefix=name.prefix)) else: new_node = node.clone() new_node.prefix = "" return Call(Name("eval"), [new_node], prefix=node.prefix) modernize-0.9rc0/modernize/fixes/fix_int_long_tuple.py000066400000000000000000000007771373531146100233560ustar00rootroot00000000000000from __future__ import generator_stop from fissix import fixer_base, fixer_util class FixIntLongTuple(fixer_base.BaseFix): run_order = 4 # Must run before fix_long. PATTERN = """ pair=atom < '(' testlist_gexp < ( ('int' ',' 'long') | ('long' ',' 'int') ) > ')' > """ def transform(self, node, results): fixer_util.touch_import(None, "six", node) pair = results["pair"] pair.replace(fixer_util.Name("six.integer_types", prefix=pair.prefix)) modernize-0.9rc0/modernize/fixes/fix_itertools_imports_six.py000066400000000000000000000047001373531146100250060ustar00rootroot00000000000000""" Fixer for imports of itertools.(imap|ifilter|izip|ifilterfalse) """ from __future__ import generator_stop # Local imports from fissix import fixer_base, fixer_util from fissix.fixer_util import BlankLine, syms, token # This is a derived work of Lib/lib2to3/fixes/fix_itertools_imports.py. That file # is under the copyright of the Python Software Foundation and licensed # under the Python Software Foundation License 2. # # Copyright notice: # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013 Python Software Foundation. All rights reserved. class FixItertoolsImportsSix(fixer_base.BaseFix): BM_compatible = True PATTERN = """ import_from< 'from' 'itertools' 'import' imports=any > """ % ( locals() ) def transform(self, node, results): imports = results["imports"] if imports.type == syms.import_as_name or not imports.children: children = [imports] else: children = imports.children for child in children[::2]: if child.type == token.NAME: name_node = child elif child.type == token.STAR: # Just leave the import as is. return else: assert child.type == syms.import_as_name name_node = child.children[0] member_name = name_node.value if member_name in ( "imap", "izip", "ifilter", "ifilterfalse", "izip_longest", ): child.value = None fixer_util.touch_import("six.moves", member_name[1:], node) child.remove() # Make sure the import statement is still sane children = imports.children[:] or [imports] remove_comma = True for child in children: if remove_comma and child.type == token.COMMA: child.remove() else: remove_comma ^= True while children and children[-1].type == token.COMMA: children.pop().remove() # If there are no imports left, just get rid of the entire statement if ( not (imports.children or getattr(imports, "value", None)) or imports.parent is None ): p = node.prefix node = BlankLine() node.prefix = p return node modernize-0.9rc0/modernize/fixes/fix_itertools_six.py000066400000000000000000000040621373531146100232320ustar00rootroot00000000000000""" Fixer for itertools.(imap|ifilter|izip) --> (six.moves.map|six.moves.filter|six.moves.zip) and itertools.ifilterfalse --> six.moves.filterfalse (bugs 2360-2363) imports from itertools are fixed in fix_itertools_imports_six.py If itertools is imported as something else (ie: import itertools as it; it.izip(spam, eggs)) method calls will not get fixed. """ # This is a derived work of Lib/lib2to3/fixes/fix_itertools_import.py. That file # is under the copyright of the Python Software Foundation and licensed # under the Python Software Foundation License 2. # # Copyright notice: # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013 Python Software Foundation. All rights reserved. from __future__ import generator_stop # Local imports from fissix import fixer_base, fixer_util from fissix.fixer_util import Name class FixItertoolsSix(fixer_base.BaseFix): BM_compatible = True it_funcs = "('imap'|'ifilter'|'izip'|'izip_longest'|'ifilterfalse')" PATTERN = """ power< it='itertools' trailer< dot='.' func=%(it_funcs)s > trailer< '(' [any] ')' > > | power< func=%(it_funcs)s trailer< '(' [any] ')' > > """ % ( locals() ) # Needs to be run after fix_(map|zip|filter) run_order = 6 def transform(self, node, results): prefix = None func = results["func"][0] if "it" in results and func.value not in ("ifilterfalse", "izip_longest"): dot, it = (results["dot"], results["it"]) # Remove the 'itertools' prefix = it.prefix it.remove() # Replace the node wich contains ('.', 'function') with the # function (to be consistant with the second part of the pattern) dot.remove() func.parent.replace(func) fixer_util.touch_import("six.moves", func.value[1:], node) prefix = prefix or func.prefix func.replace(Name(func.value[1:], prefix=prefix)) modernize-0.9rc0/modernize/fixes/fix_map.py000066400000000000000000000012031373531146100210720ustar00rootroot00000000000000# Copyright 2008 Armin Ronacher. # Licensed to PSF under a Contributor Agreement. from __future__ import generator_stop from fissix import fixer_util from fissix.fixes import fix_map from .. import utils class FixMap(fix_map.FixMap): skip_on = "six.moves.map" def transform(self, node, results): result = super().transform(node, results) if not utils.is_listcomp(result): # Always use the import even if no change is required so as to have # improved performance in iterator contexts even on Python 2.7. fixer_util.touch_import("six.moves", "map", node) return result modernize-0.9rc0/modernize/fixes/fix_metaclass.py000066400000000000000000000204351373531146100223010ustar00rootroot00000000000000"""Fixer for __metaclass__ = X -> (six.with_metaclass(X)) methods. The various forms of classdef (inherits nothing, inherits once, inherits many) don't parse the same in the CST, so we look at ALL classes for a __metaclass__ and if we find one normalize the inherits to all be an arglist. For one-liner classes ('class X: pass') there is no indent/dedent so we normalize those into having a suite. Moving the __metaclass__ into the classdef can also cause the class body to be empty so there is some special casing for that as well. This fixer also tries very hard to keep original indenting and spacing in all those corner cases. """ # This is a derived work of Lib/lib2to3/fixes/fix_metaclass.py. That file # is under the copyright of the Python Software Foundation and licensed # under the Python Software Foundation License 2. # # Copyright notice: # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013 Python Software Foundation. All rights reserved. from __future__ import generator_stop # Local imports from fissix import fixer_base, fixer_util from fissix.fixer_util import Call, Comma, Leaf, Name, Node, syms from fissix.pygram import token # Author: Jack Diederich, Daniel Neuhäuser def has_metaclass(parent): """we have to check the cls_node without changing it. There are two possibilities: 1) clsdef => suite => simple_stmt => expr_stmt => Leaf('__meta') 2) clsdef => simple_stmt => expr_stmt => Leaf('__meta') """ for node in parent.children: if node.type == syms.suite: return has_metaclass(node) elif node.type == syms.simple_stmt and node.children: expr_node = node.children[0] if expr_node.type == syms.expr_stmt and expr_node.children: left_side = expr_node.children[0] if isinstance(left_side, Leaf) and left_side.value == "__metaclass__": return True return False def fixup_parse_tree(cls_node): """one-line classes don't get a suite in the parse tree so we add one to normalize the tree """ for node in cls_node.children: if node.type == syms.suite: # already in the preferred format, do nothing return # !%@#! oneliners have no suite node, we have to fake one up for i, node in enumerate(cls_node.children): if node.type == token.COLON: break else: raise ValueError("No class suite and no ':'!") # pragma: no cover # move everything into a suite node suite = Node(syms.suite, []) while cls_node.children[i + 1 :]: move_node = cls_node.children[i + 1] suite.append_child(move_node.clone()) move_node.remove() cls_node.append_child(suite) node = suite def fixup_simple_stmt(parent, i, stmt_node): """if there is a semi-colon all the parts count as part of the same simple_stmt. We just want the __metaclass__ part so we move everything efter the semi-colon into its own simple_stmt node """ for semi_ind, node in enumerate(stmt_node.children): if node.type == token.SEMI: # *sigh* break else: return node.remove() # kill the semicolon new_expr = Node(syms.expr_stmt, []) new_stmt = Node(syms.simple_stmt, [new_expr]) while stmt_node.children[semi_ind:]: move_node = stmt_node.children[semi_ind] new_expr.append_child(move_node.clone()) move_node.remove() parent.insert_child(i, new_stmt) new_leaf1 = new_stmt.children[0].children[0] old_leaf1 = stmt_node.children[0].children[0] new_leaf1.prefix = old_leaf1.prefix def remove_trailing_newline(node): if node.children and node.children[-1].type == token.NEWLINE: node.children[-1].remove() def find_metas(cls_node): # find the suite node (Mmm, sweet nodes) for node in cls_node.children: if node.type == syms.suite: break else: raise ValueError("No class suite!") # pragma: no cover # look for simple_stmt[ expr_stmt[ Leaf('__metaclass__') ] ] for i, simple_node in list(enumerate(node.children)): if simple_node.type == syms.simple_stmt and simple_node.children: expr_node = simple_node.children[0] if expr_node.type == syms.expr_stmt and expr_node.children: # Check if the expr_node is a simple assignment. left_node = expr_node.children[0] if isinstance(left_node, Leaf) and left_node.value == "__metaclass__": # We found an assignment to __metaclass__. fixup_simple_stmt(node, i, simple_node) remove_trailing_newline(simple_node) yield (node, i, simple_node) def fixup_indent(suite): """If an INDENT is followed by a thing with a prefix then nuke the prefix Otherwise we get in trouble when removing __metaclass__ at suite start """ kids = suite.children[::-1] # find the first indent while kids: node = kids.pop() if node.type == token.INDENT: break # find the first Leaf while kids: node = kids.pop() if isinstance(node, Leaf) and node.type != token.DEDENT: if node.prefix: node.prefix = "" return else: kids.extend(node.children[::-1]) class FixMetaclass(fixer_base.BaseFix): BM_compatible = True PATTERN = """ classdef """ def transform(self, node, results): if not has_metaclass(node): return # pragma: no cover fixup_parse_tree(node) # find metaclasses, keep the last one last_metaclass = None for suite, i, stmt in find_metas(node): last_metaclass = stmt stmt.remove() text_type = node.children[0].type # always Leaf(nnn, 'class') # figure out what kind of classdef we have if len(node.children) == 7: # Node(classdef, ['class', 'name', '(', arglist, ')', ':', suite]) # 0 1 2 3 4 5 6 if node.children[3].type == syms.arglist: arglist = node.children[3] # Node(classdef, ['class', 'name', '(', 'Parent', ')', ':', suite]) else: parent = node.children[3].clone() arglist = Node(syms.arglist, [parent]) node.set_child(3, arglist) elif len(node.children) == 6: # Node(classdef, ['class', 'name', '(', ')', ':', suite]) # 0 1 2 3 4 5 arglist = Node(syms.arglist, []) node.insert_child(3, arglist) elif len(node.children) == 4: # Node(classdef, ['class', 'name', ':', suite]) # 0 1 2 3 arglist = Node(syms.arglist, []) node.insert_child(2, Leaf(token.RPAR, ")")) node.insert_child(2, arglist) node.insert_child(2, Leaf(token.LPAR, "(")) else: raise ValueError("Unexpected class definition") # pragma: no cover fixer_util.touch_import(None, "six", node) metaclass = last_metaclass.children[0].children[2].clone() metaclass.prefix = "" arguments = [metaclass] if arglist.children: bases = arglist.clone() bases.prefix = " " arguments.extend([Comma(), bases]) arglist.replace( Call(Name("six.with_metaclass", prefix=arglist.prefix), arguments) ) fixup_indent(suite) # check for empty suite if not suite.children: # one-liner that was just __metaclass__ suite.remove() pass_leaf = Leaf(text_type, "pass") pass_leaf.prefix = last_metaclass.prefix node.append_child(pass_leaf) node.append_child(Leaf(token.NEWLINE, "\n")) elif len(suite.children) > 1 and ( suite.children[-2].type == token.INDENT and suite.children[-1].type == token.DEDENT ): # there was only one line in the class body and it was __metaclass__ pass_leaf = Leaf(text_type, "pass") suite.insert_child(-1, pass_leaf) suite.insert_child(-1, Leaf(token.NEWLINE, "\n")) modernize-0.9rc0/modernize/fixes/fix_next.py000066400000000000000000000012171373531146100213000ustar00rootroot00000000000000"""Fixer for it.next() -> next(it)""" from __future__ import generator_stop # Local imports from fissix import fixer_base from fissix.fixer_util import Call, Name bind_warning = "Calls to builtin next() possibly shadowed by global binding" class FixNext(fixer_base.BaseFix): BM_compatible = True PATTERN = """ power< base=any+ trailer< '.' attr='next' > trailer< '(' ')' > > """ order = "pre" # Pre-order tree traversal def transform(self, node, results): base = results["base"] base = [n.clone() for n in base] base[0].prefix = "" node.replace(Call(Name("next", prefix=node.prefix), base)) modernize-0.9rc0/modernize/fixes/fix_open.py000066400000000000000000000006121373531146100212610ustar00rootroot00000000000000from __future__ import generator_stop from fissix import fixer_base, fixer_util class FixOpen(fixer_base.BaseFix): BM_compatible = True # Fixers don't directly stack, so make sure the 'file' case is covered. PATTERN = """ power< ('open' | 'file') trailer< '(' any+ ')' > > """ def transform(self, node, results): fixer_util.touch_import("io", "open", node) modernize-0.9rc0/modernize/fixes/fix_print.py000066400000000000000000000004471373531146100214620ustar00rootroot00000000000000from __future__ import generator_stop from fissix.fixes import fix_print from .. import utils class FixPrint(fix_print.FixPrint): def transform(self, node, results): result = super().transform(node, results) utils.add_future(node, "print_function") return result modernize-0.9rc0/modernize/fixes/fix_raise.py000066400000000000000000000014321373531146100214240ustar00rootroot00000000000000"""Fixer for 'raise E, V, T' raise -> raise raise E -> raise E raise E, V -> raise E(V) raise (((E, E'), E''), E'''), V -> raise E(V) CAVEATS: 1) "raise E, V" will be incorrectly translated if V is an exception instance. The correct Python 3 idiom is raise E from V but since we can't detect instance-hood by syntax alone and since any client code would have to be changed as well, we don't automate this. """ # Author: Collin Winter, Armin Ronacher from __future__ import generator_stop from fissix.fixes import fix_raise class FixRaise(fix_raise.FixRaise): # We don't want to match 3-argument raise, with a traceback; # that is handled separately by fix_raise_six PATTERN = """ raise_stmt< 'raise' exc=any [',' val=any] > """ modernize-0.9rc0/modernize/fixes/fix_raise_six.py000066400000000000000000000014151373531146100223100ustar00rootroot00000000000000"""Fixer for 'raise E, V, T' raise E, V, T -> six.reraise(E, V, T) """ # Author : Markus Unterwaditzer from __future__ import generator_stop # Local imports from fissix import fixer_base, fixer_util from fissix.fixer_util import Call, Comma, Name class FixRaiseSix(fixer_base.BaseFix): BM_compatible = True PATTERN = """ raise_stmt< 'raise' exc=any ',' val=any ',' tb=any > """ def transform(self, node, results): exc = results["exc"].clone() val = results["val"].clone() tb = results["tb"].clone() exc.prefix = "" val.prefix = tb.prefix = " " fixer_util.touch_import(None, "six", node) return Call( Name("six.reraise"), [exc, Comma(), val, Comma(), tb], prefix=node.prefix ) modernize-0.9rc0/modernize/fixes/fix_unichr.py000066400000000000000000000007111373531146100216100ustar00rootroot00000000000000from __future__ import generator_stop from fissix import fixer_base, fixer_util from fissix.fixer_util import is_probably_builtin class FixUnichr(fixer_base.ConditionalFix): BM_compatible = True skip_on = "six.moves.unichr" PATTERN = """'unichr'""" def transform(self, node, results): if self.should_skip(node): return if is_probably_builtin(node): fixer_util.touch_import("six", "unichr", node) modernize-0.9rc0/modernize/fixes/fix_unicode.py000066400000000000000000000011641373531146100217510ustar00rootroot00000000000000from __future__ import generator_stop import re from fissix import fixer_base, fixer_util from fissix.fixer_util import Call, Name _mapping = {"unichr": "chr", "unicode": "str"} _literal_re = re.compile("[uU][rR]?[\\'\\\"]") class FixUnicode(fixer_base.BaseFix): BM_compatible = True PATTERN = """STRING""" def transform(self, node, results): if _literal_re.match(node.value): fixer_util.touch_import(None, "six", node) new = node.clone() new.value = new.value[1:] new.prefix = "" node.replace(Call(Name("six.u", prefix=node.prefix), [new])) modernize-0.9rc0/modernize/fixes/fix_unicode_future.py000066400000000000000000000005051373531146100233410ustar00rootroot00000000000000from __future__ import generator_stop from fissix.fixes import fix_unicode from .. import utils class FixUnicodeFuture(fix_unicode.FixUnicode): def transform(self, node, results): res = super().transform(node, results) if res: utils.add_future(node, "unicode_literals") return res modernize-0.9rc0/modernize/fixes/fix_unicode_type.py000066400000000000000000000005241373531146100230110ustar00rootroot00000000000000from __future__ import generator_stop from fissix import fixer_base, fixer_util class FixUnicodeType(fixer_base.BaseFix): BM_compatible = True PATTERN = """'unicode'""" def transform(self, node, results): fixer_util.touch_import(None, "six", node) return fixer_util.Name("six.text_type", prefix=node.prefix) modernize-0.9rc0/modernize/fixes/fix_urllib_six.py000066400000000000000000000220221373531146100224730ustar00rootroot00000000000000"""Fix changes imports of urllib which are now incompatible. This is a copy of Lib/lib2to3/fixes/fix_urllib.py, but modified to point to the six.moves locations for new libraries instead of the Python 3 locations. """ # This is a derived work of Lib/lib2to3/fixes/fix_urllib.py. That file # is under the copyright of the Python Software Foundation and licensed # under the Python Software Foundation License 2. # # Copyright notice: # # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013 Python Software Foundation. All rights reserved. from __future__ import generator_stop from fissix.fixer_util import ( Comma, FromImport, Name, Newline, Node, find_indentation, syms, ) # Local imports from fissix.fixes.fix_imports import FixImports, alternates # Author: Nick Edds MAPPING = { "urllib": [ ( "six.moves.urllib.request", [ "URLopener", "FancyURLopener", "urlretrieve", "_urlopener", "urlopen", "urlcleanup", "pathname2url", "url2pathname", ], ), ( "six.moves.urllib.parse", [ "quote", "quote_plus", "unquote", "unquote_plus", "urlencode", "splitattr", "splithost", "splitnport", "splitpasswd", "splitport", "splitquery", "splittag", "splittype", "splituser", "splitvalue", ], ), ("six.moves.urllib.error", ["ContentTooShortError"]), ], "urllib2": [ ( "six.moves.urllib.request", [ "urlopen", "install_opener", "build_opener", "Request", "OpenerDirector", "BaseHandler", "HTTPDefaultErrorHandler", "HTTPRedirectHandler", "HTTPCookieProcessor", "ProxyHandler", "HTTPPasswordMgr", "HTTPPasswordMgrWithDefaultRealm", "AbstractBasicAuthHandler", "HTTPBasicAuthHandler", "ProxyBasicAuthHandler", "AbstractDigestAuthHandler", "HTTPDigestAuthHandler", "ProxyDigestAuthHandler", "HTTPHandler", "HTTPSHandler", "FileHandler", "FTPHandler", "CacheFTPHandler", "UnknownHandler", ], ), ("six.moves.urllib.error", ["URLError", "HTTPError"]), ], } # Duplicate the url parsing functions for urllib2. MAPPING["urllib2"].append(MAPPING["urllib"][1]) def build_pattern(): for old_module, changes in MAPPING.items(): for change in changes: new_module, members = change members = alternates(members) yield """import_name< 'import' (module={!r} | dotted_as_names< any* module={!r} any* >) > """.format( old_module, old_module ) yield """import_from< 'from' mod_member={!r} 'import' ( member={} | import_as_name< member={} 'as' any > | import_as_names< members=any* >) > """.format( old_module, members, members ) yield """import_from< 'from' module_star=%r 'import' star='*' > """ % old_module yield """import_name< 'import' dotted_as_name< module_as=%r 'as' any > > """ % old_module # bare_with_attr has a special significance for FixImports.match(). yield """power< bare_with_attr={!r} trailer< '.' member={} > any* > """.format( old_module, members ) class FixUrllibSix(FixImports): def build_pattern(self): return "|".join(build_pattern()) def transform_import(self, node, results): """Transform for the basic import case. Replaces the old import name with a comma separated list of its replacements. """ import_mod = results.get("module") pref = import_mod.prefix names = [] # create a Node list of the replacement modules for name in MAPPING[import_mod.value][:-1]: names.extend([Name(name[0], prefix=pref), Comma()]) names.append(Name(MAPPING[import_mod.value][-1][0], prefix=pref)) import_mod.replace(names) def transform_member(self, node, results): """Transform for imports of specific module elements. Replaces the module to be imported from with the appropriate new module. """ mod_member = results.get("mod_member") pref = mod_member.prefix member = results.get("member") # Simple case with only a single member being imported if member: # this may be a list of length one, or just a node if isinstance(member, list): member = member[0] new_name = None for change in MAPPING[mod_member.value]: if member.value in change[1]: new_name = change[0] break if new_name: mod_member.replace(Name(new_name, prefix=pref)) else: self.cannot_convert(node, "This is an invalid module element") # Multiple members being imported else: # a dictionary for replacements, order matters modules = [] mod_dict = {} members = results["members"] for member in members: # we only care about the actual members if member.type == syms.import_as_name: member_name = member.children[0].value else: member_name = member.value if member_name != ",": for change in MAPPING[mod_member.value]: if member_name in change[1]: if change[0] not in mod_dict: modules.append(change[0]) mod_dict.setdefault(change[0], []).append(member) new_nodes = [] indentation = find_indentation(node) first = True def handle_name(name, prefix): if name.type == syms.import_as_name: kids = [ Name(name.children[0].value, prefix=prefix), name.children[1].clone(), name.children[2].clone(), ] return [Node(syms.import_as_name, kids)] return [Name(name.value, prefix=prefix)] for module in modules: elts = mod_dict[module] names = [] for elt in elts[:-1]: names.extend(handle_name(elt, pref)) names.append(Comma()) names.extend(handle_name(elts[-1], pref)) new = FromImport(module, names) if not first or node.parent.prefix.endswith(indentation): new.prefix = indentation new_nodes.append(new) first = False if new_nodes: nodes = [] for new_node in new_nodes[:-1]: nodes.extend([new_node, Newline()]) nodes.append(new_nodes[-1]) node.replace(nodes) else: self.cannot_convert(node, "All module elements are invalid") def transform_dot(self, node, results): """Transform for calls to module members in code.""" module_dot = results.get("bare_with_attr") member = results.get("member") new_name = None if isinstance(member, list): member = member[0] for change in MAPPING[module_dot.value]: if member.value in change[1]: new_name = change[0] break if new_name: module_dot.replace(Name(new_name, prefix=module_dot.prefix)) else: self.cannot_convert(node, "This is an invalid module element") def transform(self, node, results): if results.get("module"): self.transform_import(node, results) elif results.get("mod_member"): self.transform_member(node, results) elif results.get("bare_with_attr"): self.transform_dot(node, results) # Renaming and star imports are not supported for these modules. elif results.get("module_star"): self.cannot_convert(node, "Cannot handle star imports.") elif results.get("module_as"): self.cannot_convert(node, "This module is now multiple modules") modernize-0.9rc0/modernize/fixes/fix_xrange_six.py000066400000000000000000000007731373531146100224770ustar00rootroot00000000000000# Copyright 2008 Armin Ronacher. # Licensed to PSF under a Contributor Agreement. from __future__ import generator_stop from fissix import fixer_base, fixer_util from fissix.fixes import fix_xrange class FixXrangeSix(fixer_base.ConditionalFix, fix_xrange.FixXrange): skip_on = "six.moves.range" def transform(self, node, results): if self.should_skip(node): return fixer_util.touch_import("six.moves", "range", node) return super().transform(node, results) modernize-0.9rc0/modernize/fixes/fix_zip.py000066400000000000000000000010521373531146100211210ustar00rootroot00000000000000# Copyright 2008 Armin Ronacher. # Licensed to PSF under a Contributor Agreement. from __future__ import generator_stop from fissix import fixer_util from fissix.fixes import fix_zip class FixZip(fix_zip.FixZip): skip_on = "six.moves.zip" def transform(self, node, results): result = super().transform(node, results) # Always use six.moves.zip so that even Python 2.7 gets performance # boost from using itertools in iterator contexts. fixer_util.touch_import("six.moves", "zip", node) return result modernize-0.9rc0/modernize/main.py000066400000000000000000000204261373531146100172650ustar00rootroot00000000000000"""\ Python _ _ _ __ ___ __| |___ _ _ _ _ (_)______ | ' \\/ _ \\/ _` / -_) '_| ' \\| |_ / -_) |_|_|_\\___/\\__,_\\___|_| |_||_|_/__\\___|\ """ from __future__ import generator_stop import logging import optparse import os import sys from fissix import refactor from fissix.main import StdoutRefactoringTool, warn from . import __version__ from .fixes import fissix_fix_names, opt_in_fix_names, six_fix_names usage = ( __doc__ + """\ %s Usage: modernize [options] file|dir ... """ % __version__ ) def format_usage(usage): """Method that doesn't output "Usage:" prefix""" return usage def main(args=None): """Main program. Returns a suggested exit status (0, 1, 2). """ # Set up option parser parser = optparse.OptionParser(usage=usage, version="modernize %s" % __version__) parser.formatter.format_usage = format_usage parser.add_option( "-v", "--verbose", action="store_true", help="Show more verbose logging." ) parser.add_option( "--no-diffs", action="store_true", help="Don't show diffs of the refactoring." ) parser.add_option( "-l", "--list-fixes", action="store_true", help="List standard transformations." ) parser.add_option( "-d", "--doctests_only", action="store_true", help="Fix up doctests only." ) parser.add_option( "-f", "--fix", action="append", default=[], help="Each FIX specifies a transformation; " "'-f default' includes default fixers.", ) parser.add_option( "--fixers-here", action="store_true", help="Add current working directory to python path (so fixers can be found)", ) parser.add_option( "-j", "--processes", action="store", default=1, type="int", help="Run fissix concurrently.", ) parser.add_option( "-x", "--nofix", action="append", default=[], help="Prevent a fixer from being run.", ) parser.add_option( "-p", "--print-function", action="store_true", help="Modify the grammar so that print() is a function.", ) parser.add_option( "-w", "--write", action="store_true", help="Write back modified files." ) parser.add_option( "-n", "--nobackups", action="store_true", default=False, help="Don't write backups for modified files.", ) parser.add_option( "--six-unicode", action="store_true", default=False, help="Wrap unicode literals in six.u().", ) parser.add_option( "--future-unicode", action="store_true", default=False, help="Use 'from __future__ import unicode_literals'" "(only useful for Python 2.6+).", ) parser.add_option( "--no-six", action="store_true", default=False, help="Exclude fixes that depend on the six package.", ) parser.add_option( "--enforce", action="store_true", default=False, help="Returns non-zero exit code if any fixers had to be applied. " "Useful for enforcing Python 3 compatibility.", ) fixer_pkg = "modernize.fixes" avail_fixes = set(refactor.get_fixers_from_package(fixer_pkg)) avail_fixes.update(fissix_fix_names) # Parse command line arguments refactor_stdin = False flags = {} options, args = parser.parse_args(args) if not options.write and options.no_diffs: warn("Not writing files and not printing diffs; that's not very useful.") if not options.write and options.nobackups: parser.error("Can't use '-n' without '-w'.") if options.list_fixes: print( "Standard transformations available for the " "-f/--fix and -x/--nofix options:" ) for fixname in sorted(avail_fixes): print(" {} ({})".format(fixname, fixname.split(".fix_", 1)[1])) print() if not args: return 0 if not args: print("At least one file or directory argument required.", file=sys.stderr) print("Use --help to show usage.", file=sys.stderr) return 2 if "-" in args: refactor_stdin = True if options.write: print("Can't write to stdin.", file=sys.stderr) return 2 if options.print_function: flags["print_function"] = True if options.fixers_here: sys.path.append(os.getcwd()) # Set up logging handler level = logging.DEBUG if options.verbose else logging.INFO logging.basicConfig(format="%(name)s: %(message)s", level=level) # Initialize the refactoring tool unwanted_fixes = set() splitfixes = [] for fix in options.nofix: splitfixes.extend(fix.split(",")) for fix in splitfixes: matched = None for tgt in avail_fixes: if tgt == fix or tgt.endswith(f".fix_{fix}"): matched = tgt unwanted_fixes.add(matched) break else: print(f"Error: fix '{fix}' was not found", file=sys.stderr) return 2 default_fixes = avail_fixes.difference(opt_in_fix_names) # Remove unicode fixers depending on command line options if options.six_unicode: unwanted_fixes.add("modernize.fixes.fix_unicode_future") elif options.future_unicode: unwanted_fixes.add("modernize.fixes.fix_unicode") else: unwanted_fixes.add("modernize.fixes.fix_unicode") unwanted_fixes.add("modernize.fixes.fix_unicode_future") if options.no_six: unwanted_fixes.update(six_fix_names) explicit = set() if options.fix: default_present = False splitfixes = [] for fix in options.fix: splitfixes.extend(fix.split(",")) for fix in splitfixes: if fix == "default": default_present = True else: matched = None for tgt in avail_fixes: if tgt == fix or tgt.endswith(f".fix_{fix}"): matched = tgt explicit.add(matched) break else: # A non-standard fix -- trust user to have supplied path explicit.add(fix) requested = default_fixes.union(explicit) if default_present else explicit else: requested = default_fixes fixer_names = requested.difference(unwanted_fixes) # Filter out unwanted fixers explicit = explicit.intersection( fixer_names ) # Filter `explicit` fixers vs remaining fixers print(" Loading the following fixers:", file=sys.stderr) if fixer_names: for fixname in sorted(fixer_names): print( " {} ({})".format(fixname, fixname.split(".fix_", 1)[1]), file=sys.stderr, ) else: print(" (None)", file=sys.stderr) print(" Applying the following explicit transformations:", file=sys.stderr) if explicit: for fixname in sorted(explicit): print( " {} ({})".format(fixname, fixname.split(".fix_", 1)[1]), file=sys.stderr, ) else: print(" (None)", file=sys.stderr) print(file=sys.stderr) # Refactor all files and directories passed as arguments rt = StdoutRefactoringTool( sorted(fixer_names), flags, sorted(explicit), options.nobackups, not options.no_diffs, ) if not rt.errors: if refactor_stdin: rt.refactor_stdin() else: try: rt.refactor( args, options.write, options.doctests_only, options.processes ) except refactor.MultiprocessingUnsupported: # pragma: no cover assert options.processes > 1 print("Sorry, -j isn't supported on this platform.", file=sys.stderr) return 1 rt.summarize() # Return error status (0 if rt.errors is zero) return_code = int(bool(rt.errors)) # If we are enforcing python 3 compatibility, return a non-zero exit code # if we had to modify any files. if options.enforce and rt.files: return_code |= 2 return return_code modernize-0.9rc0/modernize/utils.py000066400000000000000000000064441373531146100175050ustar00rootroot00000000000000from __future__ import generator_stop from fissix import fixer_util from fissix.pgen2 import token from fissix.pygram import python_symbols as syms from fissix.pytree import Leaf, Node def _check_future_import(node): """If this is a future import, return set of symbols that are imported, else return None.""" # node should be the import statement here if not (node.type == syms.simple_stmt and node.children): return set() node = node.children[0] # now node is the import_from node if not ( node.type == syms.import_from and node.children[1].type == token.NAME and node.children[1].value == "__future__" ): return set() if node.children[3].type == token.LPAR: # from __future__ import (.. node = node.children[4] else: # from __future__ import ... node = node.children[3] # now node is the import_as_name[s] # print(python_grammar.number2symbol[node.type]) if node.type == syms.import_as_names: result = set() for n in node.children: if n.type == token.NAME: result.add(n.value) elif n.type == syms.import_as_name: n = n.children[0] assert n.type == token.NAME result.add(n.value) return result elif node.type == syms.import_as_name: node = node.children[0] assert node.type == token.NAME return {node.value} elif node.type == token.NAME: return {node.value} else: # pragma: no cover assert 0, "strange import" def add_future(node, symbol): root = fixer_util.find_root(node) for idx, node in enumerate(root.children): if ( node.type == syms.simple_stmt and len(node.children) > 0 and node.children[0].type == token.STRING ): # skip over docstring continue names = _check_future_import(node) if not names: # not a future statement; need to insert before this break if symbol in names: # already imported return import_ = fixer_util.FromImport( "__future__", [Leaf(token.NAME, symbol, prefix=" ")] ) # Place after any comments or whitespace. (copyright, shebang etc.) import_.prefix = node.prefix node.prefix = "" children = [import_, fixer_util.Newline()] root.insert_child(idx, Node(syms.simple_stmt, children)) def is_listcomp(node): def _is_listcomp(node): return ( isinstance(node, Node) and node.type == syms.atom and len(node.children) >= 2 and isinstance(node.children[0], Leaf) and node.children[0].value == "[" and isinstance(node.children[-1], Leaf) and node.children[-1].value == "]" ) def _is_noop_power_node(node): """https://github.com/python/cpython/pull/2235 changed the node structure for fix_map / fix_filter to contain a top-level `power` node """ return ( isinstance(node, Node) and node.type == syms.power and len(node.children) == 1 ) return ( _is_listcomp(node) or _is_noop_power_node(node) and _is_listcomp(node.children[0]) ) modernize-0.9rc0/pyproject.toml000066400000000000000000000053101373531146100167020ustar00rootroot00000000000000[build-system] requires = ["flit_core >=2,<4"] build-backend = "flit_core.buildapi" [tool.flit.metadata] module = "modernize" author = "Armin Ronacher" author-email = "armin.ronacher@active-4.com" maintainer = "PyCQA" maintainer-email = "code-quality@python.org" home-page = "https://github.com/PyCQA/modernize" classifiers = [ "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", ] description-file = "README.rst" requires = ["fissix"] requires-python = "~=3.6" [tool.flit.metadata.requires-extra] docs = [ "alabaster~=0.7.12", "commonmark~=0.9.1", "docutils~=0.16.0", "mock~=4.0", "pillow~=7.2", "readthedocs-sphinx-ext~=2.1", "recommonmark~=0.6.0", "sphinx~=3.2", "sphinx-rtd-theme~=0.5.0", ] test = ["pytest", "pytest-cov", "coverage>=5.3"] [tool.flit.scripts] modernize = "modernize.main:main" python-modernize = "modernize.main:main" [tool.isort] profile = "black" add_imports=["from __future__ import generator_stop"] [tool.pytest.ini_options] addopts = [ "--strict-config", "--strict-markers", "--cov", "--cov-fail-under=92.11", "--cov-report=term-missing:skip-covered", ] xfail_strict = true junit_family = "xunit2" filterwarnings = ["error"] [tool.coverage.run] branch = true source_pkgs = ["modernize"] source = ["tests"] [tool.coverage.paths] source = [ ".", ".tox/*/lib/*/site-packages/", '.tox\\*\\Lib\\site-packages\\', ] [tool.tox] legacy_tox_ini = """ ; tox configuration file for running tests on local dev env and Travis CI. ; ; The local dev environment will be executed against latest released Twisted. ; The coverage is reported only and local dev and not on Travis-CI as there ; we have separate reported (ex codecov.io) [tox] envlist = py{36,37,38},lint minversion=3.20.0 requires= virtualenv >= 20.0.31 tox-wheel >= 0.5.0 [testenv] extras = test commands = pytest {posargs} wheel = True wheel_pep517 = True wheel_build_env = build [testenv:build] # empty environment to build universal wheel once per tox invocation # https://github.com/ionelmc/tox-wheel#build-configuration [testenv:coveralls] passenv = GITHUB_* deps = coveralls coverage>=5.3 commands = coveralls [testenv:lint] deps = pre-commit commands = pre-commit run --all-files --show-diff-on-failure {posargs} skip_install = true [testenv:release] deps = pep517 whitelist_externals = cp rm commands = rm -rf {toxinidir}/dist cp -r {distdir} {toxinidir}/dist # copy the wheel built by tox-wheel {envpython} -m pep517.build --source --out-dir={toxinidir}/dist {toxinidir} """ modernize-0.9rc0/setup.cfg000066400000000000000000000003531373531146100156110ustar00rootroot00000000000000[zest.releaser] python-file-with-version = modernize/__init__.py tag-format = v{version} [flake8] disable-noqa = True max-line-length = 88 extend-ignore = E203, # whitespace before : is not PEP8 compliant (& conflicts with black) modernize-0.9rc0/tests/000077500000000000000000000000001373531146100151315ustar00rootroot00000000000000modernize-0.9rc0/tests/test_fix_basestring.py000066400000000000000000000004331373531146100215510ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input BASESTRING = ( """\ isinstance(x, basestring) """, """\ from __future__ import absolute_import import six isinstance(x, six.string_types) """, ) def test_basestring(): check_on_input(*BASESTRING) modernize-0.9rc0/tests/test_fix_classic_division.py000066400000000000000000000012601373531146100227340ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input CLASSIC_DIVISION = ( """\ a /= 3 1 / 2 """, """\ from __future__ import division a //= 3 1 // 2 """, ) NEW_DIVISION = ( """\ from __future__ import division 1 / 2 a /= 3 """, """\ from __future__ import division 1 / 2 a /= 3 """, ) def test_optional(): check_on_input(CLASSIC_DIVISION[0], CLASSIC_DIVISION[0]) def test_fix_classic_division(): check_on_input( *CLASSIC_DIVISION, extra_flags=["-f", "modernize.fixes.fix_classic_division"] ) def test_new_division(): check_on_input( *NEW_DIVISION, extra_flags=["-f", "modernize.fixes.fix_classic_division"] ) modernize-0.9rc0/tests/test_fix_dict_six.py000066400000000000000000000031601373531146100212160ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input TYPES = "keys", "items", "values" DICT_ITER = ( """\ x.iter{type}() """, """\ from __future__ import absolute_import import six six.iter{type}(x) """, ) DICT_VIEW = ( """\ x.view{type}() """, """\ from __future__ import absolute_import import six six.view{type}(x) """, ) DICT_PLAIN = ( """\ x.{type}() """, """\ list(x.{type}()) """, ) DICT_IN_LOOP = ( """\ for k in x.items(): pass """, """\ for k in x.items(): pass """, ) DICT_ITER_IN_LOOP = ( """\ for k in x.iter{type}(): pass """, """\ from __future__ import absolute_import import six for k in six.iter{type}(x): pass """, ) DICT_ITER_IN_LIST = ( """\ for k in list(x.iter{type}()): pass """, """\ from __future__ import absolute_import import six for k in list(six.iter{type}(x)): pass """, ) CHAINED_CALLS = ( """\ (x + y).foo().iter{type}().bar() """, """\ from __future__ import absolute_import import six six.iter{type}((x + y).foo()).bar() """, ) def check_all_types(input, output): for type_ in TYPES: check_on_input(input.format(type=type_), output.format(type=type_)) def test_dict_iter(): check_all_types(*DICT_ITER) def test_dict_view(): check_all_types(*DICT_VIEW) def test_dict_plain(): check_all_types(*DICT_PLAIN) def test_dict_in_loop(): check_on_input(*DICT_IN_LOOP) def test_dict_iter_in_loop(): check_all_types(*DICT_ITER_IN_LOOP) def test_dict_iter_in_list(): check_all_types(*DICT_ITER_IN_LIST) def test_chained_calls(): check_all_types(*CHAINED_CALLS) modernize-0.9rc0/tests/test_fix_file.py000066400000000000000000000013161373531146100203300ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input FILE_CALL = ( """\ file('some/file/path', 'r') """, """\ open('some/file/path', 'r') """, ) FILE_CONTEXT_MANAGER = ( """\ with file('some/file/path', 'r') as file_: pass """, """\ with open('some/file/path', 'r') as file_: pass """, ) FILE_ATTR = ( """\ file('path').readlines """, """\ open('path').readlines """, ) FILE_REF = ( """\ file """, """\ file """, ) def test_file_call(): check_on_input(*FILE_CALL) def test_file_context_manager(): check_on_input(*FILE_CONTEXT_MANAGER) def test_file_attr(): check_on_input(*FILE_ATTR) def test_file_ref(): check_on_input(*FILE_REF) modernize-0.9rc0/tests/test_fix_filter.py000066400000000000000000000013161373531146100206760ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input FILTER_CALL = ( """\ filter(func, [1]) """, """\ from __future__ import absolute_import from six.moves import filter list(filter(func, [1])) """, ) FILTER_ITERATOR_CONTEXT = ( """\ for a in filter(func, [1]): pass """, """\ from __future__ import absolute_import from six.moves import filter for a in filter(func, [1]): pass """, ) FILTER_NONE = ( """\ filter(None, x) """, """\ [_f for _f in x if _f] """, ) def test_filter_call(): check_on_input(*FILTER_CALL) def test_filter_iterator_context(): check_on_input(*FILTER_ITERATOR_CONTEXT) def test_filter_None(): check_on_input(*FILTER_NONE) modernize-0.9rc0/tests/test_fix_import.py000066400000000000000000000042011373531146100207170ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input NO_IMPORTS = ( """\ """, """\ """, ) ONLY_FUTURE_IMPORTS = ( """\ from __future__ import print_function """, """\ from __future__ import print_function """, ) ONLY_NORMAL_IMPORTS = ( """\ import foo """, """\ from __future__ import absolute_import import foo """, ) NORMAL_AND_FUTURE_IMPORTS = ( """\ from __future__ import print_function import foo """, """\ from __future__ import print_function from __future__ import absolute_import import foo """, ) DOCSTRING = ( """\ \""" Docstring \""" import foo """, """\ \""" Docstring \""" from __future__ import absolute_import import foo """, ) SHEBANG = ( """\ #!/usr/bin/env python import foo """, """\ #!/usr/bin/env python from __future__ import absolute_import import foo """, ) DOCSTING_AND_SHEBANG = ( """\ #!/usr/bin/env python \""" Docstring \""" import foo """, """\ #!/usr/bin/env python \""" Docstring \""" from __future__ import absolute_import import foo """, ) COPYRIGHT_AND_SHEBANG = ( """\ #!/usr/bin/env python # # Copyright notice # import foo """, """\ #!/usr/bin/env python # # Copyright notice # from __future__ import absolute_import import foo """, ) COPYRIGHT_AND_DOCSTRING = ( """\ # # Copyright notice # \"""Docstring\""" import foo """, """\ # # Copyright notice # \"""Docstring\""" from __future__ import absolute_import import foo """, ) def test_no_imports(): check_on_input(*NO_IMPORTS) def test_only_future_imports(): check_on_input(*ONLY_FUTURE_IMPORTS) def test_only_normal_imports(): check_on_input(*ONLY_NORMAL_IMPORTS) def test_normal_and_future_imports(): check_on_input(*NORMAL_AND_FUTURE_IMPORTS) def test_import_with_docstring(): check_on_input(*DOCSTRING) def test_import_with_shebang(): check_on_input(*SHEBANG) def test_import_with_docstring_and_shebang(): check_on_input(*DOCSTING_AND_SHEBANG) def test_import_with_copyright_and_shebang(): check_on_input(*COPYRIGHT_AND_SHEBANG) def test_import_with_copyright_and_docstring(): check_on_input(*COPYRIGHT_AND_DOCSTRING) modernize-0.9rc0/tests/test_fix_imports_six.py000066400000000000000000000026361373531146100217770ustar00rootroot00000000000000from __future__ import generator_stop import sys import unittest try: from six.moves import tkinter except ImportError: tkinter = None from utils import check_on_input from modernize.fixes import fix_imports_six MOVED_MODULE = ( """\ import ConfigParser ConfigParser.ConfigParser() """, """\ from __future__ import absolute_import import six.moves.configparser six.moves.configparser.ConfigParser() """, ) MOVED_MODULE_FROMLIST = ( """\ from ConfigParser import ConfigParser ConfigParser() """, """\ from __future__ import absolute_import from six.moves.configparser import ConfigParser ConfigParser() """, ) def test_moved_module(): check_on_input(*MOVED_MODULE) def test_moved_module_fromlist(): check_on_input(*MOVED_MODULE_FROMLIST) @unittest.skipIf(sys.version_info[0] >= 3, "Test only runs on Python 2") def test_validate_mapping(): for py2_name, six_name in fix_imports_six.FixImportsSix.mapping.items(): try: __import__(py2_name) __import__(six_name) except ImportError: if "tkinter" in six_name: # Ignore error if tkinter not installed if tkinter is not None: raise elif "winreg" in six_name: # Ignore error if we're not on Windows if sys.platform.startswith("win"): raise else: raise modernize-0.9rc0/tests/test_fix_input_six.py000066400000000000000000000020761373531146100214370ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input INPUT = ( """\ input() """, """\ from __future__ import absolute_import from six.moves import input eval(input()) """, ) INPUT_ARGS = ( """\ input('hello') """, """\ from __future__ import absolute_import from six.moves import input eval(input('hello')) """, ) RAW_INPUT = ( """\ raw_input() """, """\ from __future__ import absolute_import from six.moves import input input() """, ) RAW_INPUT_TRAILER = ( """\ raw_input()[0] """, """\ from __future__ import absolute_import from six.moves import input input()[0] """, ) RAW_INPUT_INPUT = ( """\ raw_input() input() """, """\ from __future__ import absolute_import from six.moves import input input() eval(input()) """, ) def test_input(): check_on_input(*INPUT) def test_input_args(): check_on_input(*INPUT_ARGS) def test_raw_input(): check_on_input(*RAW_INPUT) def test_raw_input_trailer(): check_on_input(*RAW_INPUT_TRAILER) def test_raw_input_input(): check_on_input(*RAW_INPUT_INPUT) modernize-0.9rc0/tests/test_fix_int_long_tuple.py000066400000000000000000000010501373531146100224260ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input INT_LONG_ISINSTANCE = ( """\ isinstance(1, (int, long)) """, """\ from __future__ import absolute_import import six isinstance(1, six.integer_types) """, ) LONG_INT_ISINSTANCE = ( """\ isinstance(1, (long, int)) """, """\ from __future__ import absolute_import import six isinstance(1, six.integer_types) """, ) def test_int_long_isinstance(): check_on_input(*INT_LONG_ISINSTANCE) def test_long_int_isinstance(): check_on_input(*LONG_INT_ISINSTANCE) modernize-0.9rc0/tests/test_fix_itertools_imports_six.py000066400000000000000000000017451373531146100241030ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input IZIP_AND_CHAIN_REFERENCE = ( """\ from itertools import izip_longest, chain izip_longest([1, 2], [1]) """, """\ from __future__ import absolute_import from itertools import chain from six.moves import zip_longest zip_longest([1, 2], [1]) """, ) REMOVE_ITERTOOLS_REFERENCE = ( """\ from itertools import izip izip([1, 2], [1]) """, """\ from __future__ import absolute_import from six.moves import zip zip([1, 2], [1]) """, ) IMAP_TO_MAP_MODULE_IMPORT = ( """\ import itertools itertools.imap(lambda x: x * 2, [1, 2]) """, """\ from __future__ import absolute_import import itertools from six.moves import map map(lambda x: x * 2, [1, 2]) """, ) def test_izip_longest_and_chain(): check_on_input(*IZIP_AND_CHAIN_REFERENCE) def test_removes_import_line(): check_on_input(*REMOVE_ITERTOOLS_REFERENCE) def test_imap_to_map_module_import(): check_on_input(*IMAP_TO_MAP_MODULE_IMPORT) modernize-0.9rc0/tests/test_fix_map.py000066400000000000000000000026111373531146100201650ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input MAP_1_ARG = ( """\ map(*args) """, """\ from __future__ import absolute_import from six.moves import map list(map(*args)) """, ) MAP_2_ARGS = ( """\ map(x, [1]) """, """\ from __future__ import absolute_import from six.moves import map list(map(x, [1])) """, ) MAP_3_ARGS = ( """\ map(x, [1], [2]) """, """\ from __future__ import absolute_import from six.moves import map list(map(x, [1], [2])) """, ) MAP_4_ARGS = ( """\ map(x, [1], [2], [3]) """, """\ from __future__ import absolute_import from six.moves import map list(map(x, [1], [2], [3])) """, ) MAP_REF = ( """\ x = map """, """\ x = map """, ) MAP_ITERATOR_CONTEXT = ( """\ for a in map(x, [1]): pass """, """\ from __future__ import absolute_import from six.moves import map for a in map(x, [1]): pass """, ) MAP_LAMBDA = ( """\ x = map(lambda x: x+1, stuff) """, """\ x = [x+1 for x in stuff] """, ) def test_map_1_arg(): check_on_input(*MAP_1_ARG) def test_map_2_args(): check_on_input(*MAP_2_ARGS) def test_map_3_args(): check_on_input(*MAP_3_ARGS) def test_map_4_args(): check_on_input(*MAP_4_ARGS) def test_map_ref(): check_on_input(*MAP_REF) def test_map_iterator_context(): check_on_input(*MAP_ITERATOR_CONTEXT) def test_map_lambda(): check_on_input(*MAP_LAMBDA) modernize-0.9rc0/tests/test_fix_metaclass.py000066400000000000000000000033211373531146100213630ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input METACLASS_NO_BASE = ( """\ class Foo: __metaclass__ = Meta """, """\ from __future__ import absolute_import import six class Foo(six.with_metaclass(Meta)): pass """, ) METACLASS_NO_BASE_PARENS = ( """\ class Foo(): __metaclass__ = Meta """, """\ from __future__ import absolute_import import six class Foo(six.with_metaclass(Meta)): pass """, ) METACLASS_SINGLE_BASE = ( """\ class Foo(Bar): __metaclass__ = Meta """, """\ from __future__ import absolute_import import six class Foo(six.with_metaclass(Meta, Bar)): pass """, ) METACLASS_MANY_BASES = ( """\ class Foo(Bar, Spam): __metaclass__ = Meta """, """\ from __future__ import absolute_import import six class Foo(six.with_metaclass(Meta, Bar, Spam)): pass """, ) METACLASS_ONE_LINER = ( """\ class Foo: __metaclass__ = Meta """, """\ from __future__ import absolute_import import six class Foo(six.with_metaclass(Meta)): pass """, ) METACLASS_SEMICOLON_STMT = ( """\ class Foo(Bar): __metaclass__ = Meta; a = 12 b = 64 """, """\ from __future__ import absolute_import import six class Foo(six.with_metaclass(Meta, Bar)): a = 12 b = 64 """, ) def test_metaclass_no_base(): check_on_input(*METACLASS_NO_BASE) def test_metaclass_no_base_parens(): check_on_input(*METACLASS_NO_BASE_PARENS) def test_metaclass_single_base(): check_on_input(*METACLASS_SINGLE_BASE) def test_metaclass_many_bases(): check_on_input(*METACLASS_MANY_BASES) def test_metaclass_one_liner(): check_on_input(*METACLASS_ONE_LINER) def test_metaclass_semicolon_stmt(): check_on_input(*METACLASS_SEMICOLON_STMT) modernize-0.9rc0/tests/test_fix_next.py000066400000000000000000000005201373531146100203630ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input NEXT_METHOD = ( """ spam.next() """, """ next(spam) """, ) NEXT_NESTED = ( """ eggs.spam.next() """, """ next(eggs.spam) """, ) def test_next_method(): check_on_input(*NEXT_METHOD) def test_next_nested(): check_on_input(*NEXT_NESTED) modernize-0.9rc0/tests/test_fix_open.py000066400000000000000000000011071373531146100203500ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input OPEN = ( """\ {0}('some/path') """, """\ from io import open open('some/path') """, ) def test_open(): check_on_input( OPEN[0].format("open"), OPEN[1], extra_flags=["-f", "modernize.fixes.fix_open"], ) def test_open_optional(): check_on_input(OPEN[0].format("open"), OPEN[0].format("open")) def test_file(): flags = ["-f", "modernize.fixes.fix_open", "-f", "modernize.fixes.fix_file"] check_on_input(OPEN[0].format("file"), OPEN[1], extra_flags=flags) modernize-0.9rc0/tests/test_fix_print.py000066400000000000000000000026301373531146100205450ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input PRINT_BARE = ( """\ print """, """\ from __future__ import print_function print() """, ) PRINT_SIMPLE = ( """\ print 'Hello' """, """\ from __future__ import print_function print('Hello') """, ) PRINT_MULTIPLE = ( """\ print 'Hello', 'world' """, """\ from __future__ import print_function print('Hello', 'world') """, ) PRINT_WITH_PARENS = ( """\ print('Hello') """, """\ from __future__ import print_function print('Hello') """, ) PRINT_WITH_COMMA = ( """\ print 'Hello', """, """\ from __future__ import print_function print('Hello', end=' ') """, ) PRINT_TO_STREAM = ( """\ print >>x, 'Hello' """, """\ from __future__ import print_function print('Hello', file=x) """, ) PRINT_TO_STREAM_WITH_COMMA = ( """\ print >>x, 'Hello', """, """\ from __future__ import print_function print('Hello', end=' ', file=x) """, ) def test_print_bare(): check_on_input(*PRINT_BARE) def test_print_simple(): check_on_input(*PRINT_SIMPLE) def test_print_multiple(): check_on_input(*PRINT_MULTIPLE) def test_print_with_parens(): check_on_input(*PRINT_WITH_PARENS) def test_print_with_comma(): check_on_input(*PRINT_WITH_COMMA) def test_print_to_stream(): check_on_input(*PRINT_TO_STREAM) def test_print_to_stream_with_comma(): check_on_input(*PRINT_TO_STREAM_WITH_COMMA) modernize-0.9rc0/tests/test_fix_raise.py000066400000000000000000000016321373531146100205150ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input RAISE = ( """\ raise """, """\ raise """, ) RAISE_EXC = ( """\ raise e """, """\ raise e """, ) RAISE_VALUE = ( """\ raise e, v """, """\ raise e(v) """, ) RAISE_TUPLE = ( """\ raise ((((E1, E2), E3), E4), E5), V """, """\ raise E1(V) """, ) # Can't be converted; translation would emit a warning. RAISE_STRING = ( """\ raise 'exception' """, """\ raise 'exception' """, ) RAISE_TUPLE_ARGS = ( """\ raise e, (1, 2) """, """\ raise e(1, 2) """, ) def test_raise(): check_on_input(*RAISE) def test_raise_exc(): check_on_input(*RAISE_EXC) def test_raise_value(): check_on_input(*RAISE_VALUE) def test_raise_tuple(): check_on_input(*RAISE_TUPLE) def test_raise_string(): check_on_input(*RAISE_STRING) def test_raise_tuple_args(): check_on_input(*RAISE_TUPLE_ARGS) modernize-0.9rc0/tests/test_fix_unichr_type.py000066400000000000000000000012301373531146100217350ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input UNICHR_METHOD_REF = ( """\ converter = unichr """, """\ from __future__ import absolute_import from six import unichr converter = unichr """, ) UNICHR_METHOD_CALL = ( """\ unichr(42) """, """\ from __future__ import absolute_import from six import unichr unichr(42) """, ) UNICHR_USER_CALL = ( """\ foobar.unichr(42) """, """\ foobar.unichr(42) """, ) def test_unichr_method_ref(): check_on_input(*UNICHR_METHOD_REF) def test_unichr_method_call(): check_on_input(*UNICHR_METHOD_CALL) def test_unichr_user_call(): check_on_input(*UNICHR_USER_CALL) modernize-0.9rc0/tests/test_fix_unicode.py000066400000000000000000000017111373531146100210360ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input UNICODE_LITERALS = """\ a = u'' b = U"\\u2041" c = ur'''blah foo''' import sys """ UNICODE_LITERALS_six = """\ from __future__ import absolute_import import six a = six.u('') b = six.u("\\u2041") c = six.u(r'''blah foo''') import sys """ UNICODE_LITERALS_compat = """\ from __future__ import absolute_import a = u'' b = U"\\u2041" c = ur'''blah foo''' import sys """ UNICODE_LITERALS_future = """\ from __future__ import absolute_import from __future__ import unicode_literals a = '' b = "\\u2041" c = r'''blah foo''' import sys """ def test_unicode_six(): check_on_input( UNICODE_LITERALS, UNICODE_LITERALS_six, extra_flags=["--six-unicode"] ) def test_unicode_compat(): check_on_input(UNICODE_LITERALS, UNICODE_LITERALS_compat) def test_unicode_future(): check_on_input( UNICODE_LITERALS, UNICODE_LITERALS_future, extra_flags=["--future-unicode"] ) modernize-0.9rc0/tests/test_fix_unicode_type.py000066400000000000000000000007731373531146100221060ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input UNICODE_TYPE_REF = ( """\ isinstance(u'str', unicode) """, """\ from __future__ import absolute_import import six isinstance(u'str', six.text_type) """, ) UNICODE_TYPE_CALL = ( """\ unicode(x) """, """\ from __future__ import absolute_import import six six.text_type(x) """, ) def test_unicode_type_ref(): check_on_input(*UNICODE_TYPE_REF) def test_unicode_type_call(): check_on_input(*UNICODE_TYPE_CALL) modernize-0.9rc0/tests/test_fix_urllib_six.py000066400000000000000000000040071373531146100215650ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input URLLIB_MODULE_REFERENCE = ( """\ import urllib urllib.quote_plus('hello world') """, """\ from __future__ import absolute_import import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error six.moves.urllib.parse.quote_plus('hello world') """, ) URLLIB_FUNCTION_REFERENCE = ( """\ from urllib2 import urlopen urlopen('https://www.python.org') """, """\ from __future__ import absolute_import from six.moves.urllib.request import urlopen urlopen('https://www.python.org') """, ) URLLIB_MULTI_IMPORT_REFERENCE = ( """\ from urllib2 import HTTPError, urlopen """, """\ from __future__ import absolute_import from six.moves.urllib.error import HTTPError from six.moves.urllib.request import urlopen """, ) URLLIB_IMPORT_AS = ( """\ from urllib2 import urlopen as urlo from urllib2 import HTTPError, URLError as urle """, """\ from __future__ import absolute_import from six.moves.urllib.request import urlopen as urlo from six.moves.urllib.error import HTTPError, URLError as urle """, ) # Can't be converted; translation would emit a warning. URLIB_INVALID_CODE = ( """\ from urllib2 import * from urllib2 import foobarraz from urllib2 import foo, bar as raz import urllib as urllib_py2 import urllib urllib.foobarraz('hello world') """, """\ from __future__ import absolute_import from urllib2 import * from urllib2 import foobarraz from urllib2 import foo, bar as raz import urllib as urllib_py2 import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error urllib.foobarraz('hello world') """, ) def test_urllib_module_reference(): check_on_input(*URLLIB_MODULE_REFERENCE) def test_urllib_function_reference(): check_on_input(*URLLIB_FUNCTION_REFERENCE) def test_urllib_multi_import(): check_on_input(*URLLIB_MULTI_IMPORT_REFERENCE) def test_urllib_import_as(): check_on_input(*URLLIB_IMPORT_AS) def test_urllib_invalid_imports(): check_on_input(*URLIB_INVALID_CODE) modernize-0.9rc0/tests/test_fix_xrange_six.py000066400000000000000000000012371373531146100215620ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input RANGE = ( """\ x = range(1) """, """\ from __future__ import absolute_import from six.moves import range x = list(range(1)) """, ) XRANGE = ( """\ xrange(1) """, """\ from __future__ import absolute_import from six.moves import range range(1) """, ) XRANGE_RANGE = ( """\ x = xrange(1) y = range(1) """, """\ from __future__ import absolute_import from six.moves import range x = range(1) y = list(range(1)) """, ) def test_range(): check_on_input(*RANGE) def test_xrange(): check_on_input(*XRANGE) def test_xrange_range(): check_on_input(*XRANGE_RANGE) modernize-0.9rc0/tests/test_fix_zip.py000066400000000000000000000022351373531146100202140ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input ZIP_CALL_NO_ARGS = ( """\ zip() """, """\ from __future__ import absolute_import from six.moves import zip list(zip()) """, ) ZIP_CALL_1_ARG = ( """\ zip(x) """, """\ from __future__ import absolute_import from six.moves import zip list(zip(x)) """, ) ZIP_CALL_2_ARGS = ( """\ zip(x, y) zip(w, z) """, """\ from __future__ import absolute_import from six.moves import zip list(zip(x, y)) list(zip(w, z)) """, ) ZIP_CALL_STAR_ARGS = ( """\ zip(*args) """, """\ from __future__ import absolute_import from six.moves import zip list(zip(*args)) """, ) ZIP_ITERATOR_CONTEXT = ( """\ for a in zip(x): pass """, """\ from __future__ import absolute_import from six.moves import zip for a in zip(x): pass """, ) def test_zip_call_no_args(): check_on_input(*ZIP_CALL_NO_ARGS) def test_zip_call_1_arg(): check_on_input(*ZIP_CALL_1_ARG) def test_zip_call_2_args(): check_on_input(*ZIP_CALL_2_ARGS) def test_zip_call_star_args(): check_on_input(*ZIP_CALL_STAR_ARGS) def test_zip_iterator_context(): check_on_input(*ZIP_ITERATOR_CONTEXT) modernize-0.9rc0/tests/test_fixes.py000066400000000000000000000021471373531146100176640ustar00rootroot00000000000000from __future__ import generator_stop from fissix import refactor from modernize import fixes FISSIX_FIXES_PKG = "fissix.fixes" MODERNIZE_FIXES_PKG = "modernize.fixes" def check_existence(prefix, module_names): """Check that module_names have the expected prefix and exist.""" dotted_prefix = prefix + "." for module_name in module_names: if not module_name.startswith(dotted_prefix): msg = f"{module_name!r} does not start with {prefix!r}" raise AssertionError(msg) try: __import__(module_name) except ImportError: raise AssertionError(f"{module_name!r} cannot be imported") def test_fissix_fix_names(): check_existence(FISSIX_FIXES_PKG, fixes.fissix_fix_names) def test_six_fix_names(): check_existence(MODERNIZE_FIXES_PKG, fixes.six_fix_names) def test_fixers_importable(): fixers = refactor.get_fixers_from_package(MODERNIZE_FIXES_PKG) for module_name in fixers: try: __import__(module_name) except ImportError: raise AssertionError(f"{module_name!r} cannot be imported") modernize-0.9rc0/tests/test_future_behaviour.py000066400000000000000000000070741373531146100221300ustar00rootroot00000000000000# Tests for problem with multiple futures added to single file from __future__ import generator_stop import os import shutil import tempfile from utils import check_on_input from modernize.__main__ import main as modernize_main SINGLE_PRINT_CONTENT = """ print 'world' """ TWO_PRINTS_CONTENT = """ print 'Hello' print 'world' """ COMPLICATED_CONTENT = """ print 'Hello' print u'world' def sub(x): print x, u"here" """ PROBLEMATIC_CONTENT = ''' """ Hello """ from a.b.c import d def test_existing(): print d ''' def _check_for_multiple_futures(file_name, source_content): """ Checks for multiple identical futures in given file, raises if found. Returns dictionary of found futures (name => 1) """ counts = {} result_content = "" with open(file_name) as input: for line in input: if line.startswith("from __future__"): counts[line] = 1 + counts.get(line, 0) result_content += line for future, how_many in counts.items(): if how_many > 1: raise Exception( f"The same future repeated more than once ({how_many} times):\n" f"{future}\n\n* Input file:\n{source_content}\n\n" f"* Output file:\n{result_content}\n" ) return counts def _check_on_input(file_content, extra_flags=[]): try: tmpdirname = tempfile.mkdtemp() test_input_name = os.path.join(tmpdirname, "input.py") with open(test_input_name, "wt") as input: input.write(file_content) modernize_main(extra_flags + ["-w", test_input_name]) _check_for_multiple_futures(test_input_name, file_content) finally: shutil.rmtree(tmpdirname) def test_single_print(): _check_on_input(SINGLE_PRINT_CONTENT) def test_two_prints(): _check_on_input(TWO_PRINTS_CONTENT) def test_many_prints_and_unicode(): _check_on_input(COMPLICATED_CONTENT, ["--future-unicode"]) def test_two_files_on_single_run(): # Mostly to test whether second file gets its "from future ..." try: tmpdirname = tempfile.mkdtemp() input_names = [ os.path.join(tmpdirname, f"input_{idx}.py") for idx in range(0, 3) ] for input_name in input_names: with open(input_name, "wt") as input: input.write(TWO_PRINTS_CONTENT) modernize_main(["-w"] + input_names) for input_name in input_names: futs = _check_for_multiple_futures(input_name, TWO_PRINTS_CONTENT) if not futs: raise Exception("File {0} got no from __future__ (but it should)") finally: shutil.rmtree(tmpdirname) def test_problematic_file(): # ON this one I get crash _check_on_input(PROBLEMATIC_CONTENT) FUTURE_IMPORT_AS = ( """\ from __future__ import print_function as pf print("abc") """, """\ from __future__ import print_function as pf print("abc") """, ) FUTURE_IMPORT_AS_MULTIPLE = ( """\ from __future__ import print_function as pf, division as dv print("abc") """, """\ from __future__ import print_function as pf, division as dv print("abc") """, ) FUTURE_IMPORT_PAREN = ( """\ from __future__ import (absolute_import, division, print_function) unicode("abc") """, """\ from __future__ import (absolute_import, division, print_function) import six six.text_type("abc") """, ) def test_future_import_as(): check_on_input(*FUTURE_IMPORT_AS) def test_future_import_as_multiple(): check_on_input(*FUTURE_IMPORT_AS_MULTIPLE) def test_future_import_paren(): check_on_input(*FUTURE_IMPORT_PAREN) modernize-0.9rc0/tests/test_main.py000066400000000000000000000024321373531146100174670ustar00rootroot00000000000000from __future__ import generator_stop import contextlib import io from utils import check_on_input from modernize.__main__ import main as modernize_main def test_list_fixers(): stdout = io.StringIO() with contextlib.redirect_stdout(stdout): returncode = modernize_main(["-l"]) assert returncode == 0 assert "xrange_six" in stdout.getvalue() def test_nofix_fixers(tmp_path): stdout = io.StringIO() stderr = io.StringIO() with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr): returncode = modernize_main(["--nofix=ham", str(tmp_path)]) assert returncode == 2 assert stderr.getvalue() == "Error: fix 'ham' was not found\n" assert stdout.getvalue() == "" NO_SIX_SAMPLE = """\ a = range(10) class B(object): __metaclass__ = Meta """ EXPECTED_SIX_RESULT = """\ from __future__ import absolute_import import six from six.moves import range a = list(range(10)) class B(six.with_metaclass(Meta, object)): pass """ def test_no_six(): check_on_input( NO_SIX_SAMPLE, NO_SIX_SAMPLE, extra_flags=["--no-six"], expected_return_code=0 ) def test_enforce(): check_on_input( NO_SIX_SAMPLE, EXPECTED_SIX_RESULT, extra_flags=["--enforce"], expected_return_code=2, ) modernize-0.9rc0/tests/test_raise_six.py000066400000000000000000000004731373531146100205340ustar00rootroot00000000000000from __future__ import generator_stop from utils import check_on_input RAISE_TRACEBACK = ( """\ raise Exception, value, traceback """, """\ from __future__ import absolute_import import six six.reraise(Exception, value, traceback) """, ) def test_raise_traceback(): check_on_input(*RAISE_TRACEBACK) modernize-0.9rc0/tests/utils.py000066400000000000000000000054131373531146100166460ustar00rootroot00000000000000from __future__ import generator_stop import os.path import shutil import tempfile from modernize.__main__ import main as modernize_main def check_on_input( input_content, expected_content, extra_flags=[], expected_return_code=None ): """ Check that input_content is fixed to expected_content, idempotently: Writes input_content to a temporary file Runs modernize on it with any extra arguments as given in extra_flags Runs modernize again with the same arguments, to flush out cumulative effects (e.g., 'import' fixer isn't triggered until an import exists) Asserts that the resulting file matches expected_content Runs modernize again with any extra arguments Asserts that the final run makes no changes """ tmpdirname = tempfile.mkdtemp() try: test_input_name = os.path.join(tmpdirname, "input.py") with open(test_input_name, "wt") as input_file: input_file.write(input_content) def _check(this_input_content, which_check, check_return_code=True): return_code = modernize_main(extra_flags + ["-w", test_input_name]) if check_return_code and expected_return_code is not None: if expected_return_code != return_code: raise AssertionError( "Actual return code: %s\nExpected return code: %s" % (return_code, expected_return_code) ) # Second pass to deal with cumulative effects that affect 'import' return_code = modernize_main(extra_flags + ["-w", test_input_name]) if check_return_code and expected_return_code is not None: if expected_return_code != return_code: raise AssertionError( "Actual return code: %s\nExpected return code: %s" % (return_code, expected_return_code) ) output_content = "" with open(test_input_name) as output_file: for line in output_file: if line: output_content += line if output_content != expected_content: raise AssertionError( "%s\nInput:\n%sOutput:\n%s\nExpecting:\n%s" % ( which_check, this_input_content, output_content, expected_content, ) ) _check(input_content, "output check failed") if input_content != expected_content: _check( expected_content, "idempotence check failed", check_return_code=False ) finally: shutil.rmtree(tmpdirname)