[2/9] python3-pystache: remove, unmaintained

Message ID 20220310132523.2253188-2-ross.burton@arm.com
State New
Headers show
Series [1/9] packagegroup-meta-python: add poetry-core and unattended-upgrade | expand

Commit Message

Ross Burton March 10, 2022, 1:25 p.m. UTC
Pystache hasn't been maintained since 2014.  One maintained alternative
is https://github.com/noahmorrison/chevron.

Signed-off-by: Ross Burton <ross.burton@arm.com>
---
 .../packagegroups/packagegroup-meta-python.bb |    1 -
 ...-versions-remove-py2x-and-fix-tests-.patch | 4150 -----------------
 .../python/python3-pystache_0.5.4.bb          |   17 -
 3 files changed, 4168 deletions(-)
 delete mode 100644 meta-python/recipes-devtools/python/python3-pystache/0001-Modernize-python-versions-remove-py2x-and-fix-tests-.patch
 delete mode 100644 meta-python/recipes-devtools/python/python3-pystache_0.5.4.bb

Patch

diff --git a/meta-python/recipes-core/packagegroups/packagegroup-meta-python.bb b/meta-python/recipes-core/packagegroups/packagegroup-meta-python.bb
index 86bf6db30b..ae454e789b 100644
--- a/meta-python/recipes-core/packagegroups/packagegroup-meta-python.bb
+++ b/meta-python/recipes-core/packagegroups/packagegroup-meta-python.bb
@@ -347,7 +347,6 @@  RDEPENDS:packagegroup-meta-python3 = "\
     python3-pyserial \
     python3-pysocks \
     python3-pysonos \
-    python3-pystache \
     python3-pystemd \
     python3-pytest-asyncio \
     python3-pytest-benchmark \
diff --git a/meta-python/recipes-devtools/python/python3-pystache/0001-Modernize-python-versions-remove-py2x-and-fix-tests-.patch b/meta-python/recipes-devtools/python/python3-pystache/0001-Modernize-python-versions-remove-py2x-and-fix-tests-.patch
deleted file mode 100644
index d268cc5cd9..0000000000
--- a/meta-python/recipes-devtools/python/python3-pystache/0001-Modernize-python-versions-remove-py2x-and-fix-tests-.patch
+++ /dev/null
@@ -1,4150 +0,0 @@ 
-From 8bb1ac2d81f697598a766714f2c439d78c85d71e Mon Sep 17 00:00:00 2001
-From: Stephen L Arnold <nerdboy@gentoo.org>
-Date: Sat, 7 Nov 2020 12:38:33 -0800
-Subject: [PATCH] Modernize python versions (remove py2x) and fix tests, update
- spec
-
-* migrate to github actions for CI, add conda recipe/workflow
-* fix document processing, update pandoc args and history
-* convert doctests and modules to py3
-* convert packaging/setup.py to pep517, keep doc processing
-* cleanup tox cfg, add coverage, readme status
-* add pep8speaks cfg, cleanup warnings, use correct env
-* update setup_description.rst for packaging
-* set version for test release => 0.6.0 and deploy
-
-Upstream-Status: Backport [https://github.com/defunkt/pystache/pull/214]
-Signed-off-by: Stephen L Arnold <nerdboy@gentoo.org>
----
- .coveragerc                               |  38 +++
- .gitchangelog.rc                          | 295 +++++++++++++++++++++
- .github/workflows/ci.yml                  |  73 ++++++
- .github/workflows/conda.yml               |  55 ++++
- .github/workflows/release.yml             |  94 +++++++
- .github/workflows/wheels.yml              |  82 ++++++
- .pep8speaks.yml                           |  15 ++
- HISTORY.md                                |  37 ++-
- MANIFEST.in                               |   8 +-
- README.md                                 | 141 +++++-----
- TODO.md                                   |   5 +-
- conda/meta.yaml                           |  50 ++++
- pyproject.toml                            |   3 +
- pystache/__init__.py                      |   2 +-
- pystache/commands/render.py               |   4 +-
- pystache/common.py                        |  13 +-
- pystache/defaults.py                      |   2 +-
- pystache/loader.py                        |  14 +-
- pystache/parsed.py                        |   6 +-
- pystache/parser.py                        |  20 +-
- pystache/renderengine.py                  |   2 +-
- pystache/renderer.py                      |  22 +-
- pystache/specloader.py                    |   2 +-
- pystache/tests/benchmark.py               |  15 +-
- pystache/tests/common.py                  |  10 +-
- pystache/tests/examples/unicode_output.py |   2 +-
- pystache/tests/main.py                    |  28 +-
- pystache/tests/spectesting.py             |  16 +-
- pystache/tests/test___init__.py           |   4 +-
- pystache/tests/test_commands.py           |   2 +-
- pystache/tests/test_defaults.py           |  18 +-
- pystache/tests/test_examples.py           |  40 +--
- pystache/tests/test_loader.py             |  46 ++--
- pystache/tests/test_pystache.py           |   6 +-
- pystache/tests/test_renderengine.py       | 148 +++++------
- pystache/tests/test_renderer.py           |  86 +++----
- pystache/tests/test_simple.py             |  20 +-
- pystache/tests/test_specloader.py         |  60 ++---
- setup.cfg                                 |  74 +++++-
- setup.py                                  | 134 +---------
- setup_description.rst                     | 297 +++++++++++++---------
- tox.ini                                   | 118 +++++++--
- travis.yml_disabled                       |  52 ++++
- 43 files changed, 1487 insertions(+), 672 deletions(-)
- create mode 100644 .coveragerc
- create mode 100644 .gitchangelog.rc
- create mode 100644 .github/workflows/ci.yml
- create mode 100644 .github/workflows/conda.yml
- create mode 100644 .github/workflows/release.yml
- create mode 100644 .github/workflows/wheels.yml
- create mode 100644 .pep8speaks.yml
- create mode 100644 conda/meta.yaml
- create mode 100644 pyproject.toml
- create mode 100644 travis.yml_disabled
-
-diff --git a/.coveragerc b/.coveragerc
-new file mode 100644
-index 0000000..9a336dd
---- /dev/null
-+++ b/.coveragerc
-@@ -0,0 +1,38 @@
-+# .coveragerc to control coverage.py
-+[run]
-+branch = True
-+
-+source = pystache
-+
-+omit =
-+    .tox/*
-+    setup.py
-+    pystache/tests/*
-+
-+#plugins =
-+#    coverage_python_version
-+
-+[report]
-+# must set this to True to see missing
-+#show_missing = True
-+
-+# Regexes for lines to exclude from consideration
-+exclude_lines =
-+    # Have to re-enable the standard pragma
-+    pragma: no cover
-+
-+    # Don't complain about missing debug-only code:
-+    def __repr__
-+    if self\.debug
-+
-+    # Don't complain if tests don't hit defensive assertion code:
-+    raise AssertionError
-+    raise NotImplementedError
-+
-+    # Don't complain if non-runnable code isn't run:
-+    if 0:
-+
-+ignore_errors = True
-+
-+[html]
-+directory = cover
-diff --git a/.gitchangelog.rc b/.gitchangelog.rc
-new file mode 100644
-index 0000000..5cf63a0
---- /dev/null
-+++ b/.gitchangelog.rc
-@@ -0,0 +1,295 @@
-+# -*- coding: utf-8; mode: python -*-
-+##
-+## Format
-+##
-+##   ACTION: [AUDIENCE:] COMMIT_MSG [!TAG ...]
-+##
-+## Description
-+##
-+##   ACTION is one of 'chg', 'fix', 'new'
-+##
-+##       Is WHAT the change is about.
-+##
-+##       'chg' is for refactor, small improvement, cosmetic changes...
-+##       'fix' is for bug fixes
-+##       'new' is for new features, big improvement
-+##
-+##   AUDIENCE is optional and one of 'dev', 'usr', 'pkg', 'test', 'doc'
-+##
-+##       Is WHO is concerned by the change.
-+##
-+##       'dev'  is for developpers (API changes, refactors...)
-+##       'usr'  is for final users (UI changes)
-+##       'pkg'  is for packagers   (packaging changes)
-+##       'test' is for testers     (test only related changes)
-+##       'doc'  is for doc guys    (doc only changes)
-+##
-+##   COMMIT_MSG is ... well ... the commit message itself.
-+##
-+##   TAGs are additionnal adjective as 'refactor' 'minor' 'cosmetic'
-+##
-+##       They are preceded with a '!' or a '@' (prefer the former, as the
-+##       latter is wrongly interpreted in github.) Commonly used tags are:
-+##
-+##       'refactor' is obviously for refactoring code only
-+##       'minor' is for a very meaningless change (a typo, adding a comment)
-+##       'cosmetic' is for cosmetic driven change (re-indentation, 80-col...)
-+##       'wip' is for partial functionality but complete subfunctionality.
-+##
-+## Example:
-+##
-+##   new: usr: support of bazaar implemented
-+##   chg: re-indentend some lines !cosmetic
-+##   new: dev: updated code to be compatible with last version of killer lib.
-+##   fix: pkg: updated year of licence coverage.
-+##   new: test: added a bunch of test around user usability of feature X.
-+##   fix: typo in spelling my name in comment. !minor
-+##
-+##   Please note that multi-line commit message are supported, and only the
-+##   first line will be considered as the "summary" of the commit message. So
-+##   tags, and other rules only applies to the summary.  The body of the commit
-+##   message will be displayed in the changelog without reformatting.
-+
-+
-+##
-+## ``ignore_regexps`` is a line of regexps
-+##
-+## Any commit having its full commit message matching any regexp listed here
-+## will be ignored and won't be reported in the changelog.
-+##
-+ignore_regexps = [
-+    r'@minor', r'!minor',
-+    r'@cosmetic', r'!cosmetic',
-+    r'@refactor', r'!refactor',
-+    r'@wip', r'!wip',
-+    r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:',
-+    r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:',
-+    r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$',
-+    r'^$',  ## ignore commits with empty messages
-+]
-+
-+
-+## ``section_regexps`` is a list of 2-tuples associating a string label and a
-+## list of regexp
-+##
-+## Commit messages will be classified in sections thanks to this. Section
-+## titles are the label, and a commit is classified under this section if any
-+## of the regexps associated is matching.
-+##
-+## Please note that ``section_regexps`` will only classify commits and won't
-+## make any changes to the contents. So you'll probably want to go check
-+## ``subject_process`` (or ``body_process``) to do some changes to the subject,
-+## whenever you are tweaking this variable.
-+##
-+section_regexps = [
-+    ('New', [
-+        r'^[nN]ew\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
-+     ]),
-+    ('Features', [
-+       r'^([nN]ew|[fF]eat)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
-+     ]),
-+    ('Changes', [
-+        r'^[cC]hg\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
-+     ]),
-+    ('Fixes', [
-+        r'^[fF]ix\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
-+     ]),
-+
-+    ('Other', None ## Match all lines
-+     ),
-+]
-+
-+
-+## ``body_process`` is a callable
-+##
-+## This callable will be given the original body and result will
-+## be used in the changelog.
-+##
-+## Available constructs are:
-+##
-+##   - any python callable that take one txt argument and return txt argument.
-+##
-+##   - ReSub(pattern, replacement): will apply regexp substitution.
-+##
-+##   - Indent(chars="  "): will indent the text with the prefix
-+##     Please remember that template engines gets also to modify the text and
-+##     will usually indent themselves the text if needed.
-+##
-+##   - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns
-+##
-+##   - noop: do nothing
-+##
-+##   - ucfirst: ensure the first letter is uppercase.
-+##     (usually used in the ``subject_process`` pipeline)
-+##
-+##   - final_dot: ensure text finishes with a dot
-+##     (usually used in the ``subject_process`` pipeline)
-+##
-+##   - strip: remove any spaces before or after the content of the string
-+##
-+##   - SetIfEmpty(msg="No commit message."): will set the text to
-+##     whatever given ``msg`` if the current text is empty.
-+##
-+## Additionally, you can `pipe` the provided filters, for instance:
-+#body_process = Wrap(regexp=r'\n(?=\w+\s*:)') | Indent(chars="  ")
-+#body_process = Wrap(regexp=r'\n(?=\w+\s*:)')
-+#body_process = noop
-+body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip
-+
-+
-+## ``subject_process`` is a callable
-+##
-+## This callable will be given the original subject and result will
-+## be used in the changelog.
-+##
-+## Available constructs are those listed in ``body_process`` doc.
-+subject_process = (strip |
-+    ReSub(r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') |
-+    SetIfEmpty("No commit message.") | ucfirst | final_dot)
-+
-+
-+## ``tag_filter_regexp`` is a regexp
-+##
-+## Tags that will be used for the changelog must match this regexp.
-+##
-+#tag_filter_regexp = r'^v?[0-9]+\.[0-9]+(\.[0-9]+)?$'
-+tag_filter_regexp = r'^[0-9]+\.[0-9]+(\.[0-9]+)?$'
-+
-+
-+## ``unreleased_version_label`` is a string or a callable that outputs a string
-+##
-+## This label will be used as the changelog Title of the last set of changes
-+## between last valid tag and HEAD if any.
-+unreleased_version_label = "(unreleased)"
-+#unreleased_version_label = lambda: swrap(
-+#    ["git", "describe", "--tags"],
-+#shell=False)
-+
-+
-+## ``output_engine`` is a callable
-+##
-+## This will change the output format of the generated changelog file
-+##
-+## Available choices are:
-+##
-+##   - rest_py
-+##
-+##        Legacy pure python engine, outputs ReSTructured text.
-+##        This is the default.
-+##
-+##   - mustache(<template_name>)
-+##
-+##        Template name could be any of the available templates in
-+##        ``templates/mustache/*.tpl``.
-+##        Requires python package ``pystache``.
-+##        Examples:
-+##           - mustache("markdown")
-+##           - mustache("restructuredtext")
-+##
-+##   - makotemplate(<template_name>)
-+##
-+##        Template name could be any of the available templates in
-+##        ``templates/mako/*.tpl``.
-+##        Requires python package ``mako``.
-+##        Examples:
-+##           - makotemplate("restructuredtext")
-+##
-+#output_engine = rest_py
-+#output_engine = mustache("restructuredtext")
-+output_engine = mustache("markdown")
-+#output_engine = makotemplate("restructuredtext")
-+
-+
-+## ``include_merge`` is a boolean
-+##
-+## This option tells git-log whether to include merge commits in the log.
-+## The default is to include them.
-+include_merge = True
-+
-+
-+## ``log_encoding`` is a string identifier
-+##
-+## This option tells gitchangelog what encoding is outputed by ``git log``.
-+## The default is to be clever about it: it checks ``git config`` for
-+## ``i18n.logOutputEncoding``, and if not found will default to git's own
-+## default: ``utf-8``.
-+#log_encoding = 'utf-8'
-+
-+
-+## ``publish`` is a callable
-+##
-+## Sets what ``gitchangelog`` should do with the output generated by
-+## the output engine. ``publish`` is a callable taking one argument
-+## that is an interator on lines from the output engine.
-+##
-+## Some helper callable are provided:
-+##
-+## Available choices are:
-+##
-+##   - stdout
-+##
-+##        Outputs directly to standard output
-+##        (This is the default)
-+##
-+##   - FileInsertAtFirstRegexMatch(file, pattern, idx=lamda m: m.start(), flags)
-+##
-+##        Creates a callable that will parse given file for the given
-+##        regex pattern and will insert the output in the file.
-+##        ``idx`` is a callable that receive the matching object and
-+##        must return a integer index point where to insert the
-+##        the output in the file. Default is to return the position of
-+##        the start of the matched string.
-+##
-+##   - FileRegexSubst(file, pattern, replace, flags)
-+##
-+##        Apply a replace inplace in the given file. Your regex pattern must
-+##        take care of everything and might be more complex. Check the README
-+##        for a complete copy-pastable example.
-+##
-+# publish = FileInsertIntoFirstRegexMatch(
-+#     "CHANGELOG.rst",
-+#     r'/(?P<rev>[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n/',
-+#     idx=lambda m: m.start(1)
-+# )
-+#publish = stdout
-+
-+
-+## ``revs`` is a list of callable or a list of string
-+##
-+## callable will be called to resolve as strings and allow dynamical
-+## computation of these. The result will be used as revisions for
-+## gitchangelog (as if directly stated on the command line). This allows
-+## to filter exaclty which commits will be read by gitchangelog.
-+##
-+## To get a full documentation on the format of these strings, please
-+## refer to the ``git rev-list`` arguments. There are many examples.
-+##
-+## Using callables is especially useful, for instance, if you
-+## are using gitchangelog to generate incrementally your changelog.
-+##
-+## Some helpers are provided, you can use them::
-+##
-+##   - FileFirstRegexMatch(file, pattern): will return a callable that will
-+##     return the first string match for the given pattern in the given file.
-+##     If you use named sub-patterns in your regex pattern, it'll output only
-+##     the string matching the regex pattern named "rev".
-+##
-+##   - Caret(rev): will return the rev prefixed by a "^", which is a
-+##     way to remove the given revision and all its ancestor.
-+##
-+## Please note that if you provide a rev-list on the command line, it'll
-+## replace this value (which will then be ignored).
-+##
-+## If empty, then ``gitchangelog`` will act as it had to generate a full
-+## changelog.
-+##
-+## The default is to use all commits to make the changelog.
-+#revs = ["^1.0.3", ]
-+#revs = [
-+#    Caret(
-+#        FileFirstRegexMatch(
-+#            "CHANGELOG.rst",
-+#            r"(?P<rev>[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n")),
-+#    "HEAD"
-+#]
-+revs = []
-diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
-new file mode 100644
-index 0000000..356dd2c
---- /dev/null
-+++ b/.github/workflows/ci.yml
-@@ -0,0 +1,73 @@
-+# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
-+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
-+
-+name: ci
-+
-+on:
-+  push:
-+    branches: [ master ]
-+  pull_request:
-+    branches: [ master ]
-+
-+jobs:
-+  build:
-+
-+    runs-on: ${{ matrix.os }}
-+    defaults:
-+      run:
-+        shell: bash
-+    env:
-+      OS: ${{ matrix.os }}
-+      PYTHON: ${{ matrix.python-version }}
-+      PYTHONIOENCODING: utf-8
-+      PIP_DOWNLOAD_CACHE: ${{ github.workspace }}/../.pip_download_cache
-+    strategy:
-+      fail-fast: false
-+      matrix:
-+        os: [ubuntu-20.04, macos-latest, windows-latest]
-+        python-version: [3.6, 3.7, 3.8, 3.9]
-+    steps:
-+    - name: Set git crlf/eol
-+      run: |
-+        git config --global core.autocrlf false
-+        git config --global core.eol lf
-+
-+    - uses: actions/checkout@v2
-+      with:
-+        submodules: True
-+
-+    - name: Set up Python ${{ matrix.python-version }}
-+      uses: actions/setup-python@v2
-+      with:
-+        python-version: ${{ matrix.python-version }}
-+
-+    - name: Install dependencies
-+      run: |
-+        python -m pip install --upgrade pip
-+        pip install tox tox-gh-actions
-+
-+    - name: Run tests with coverage
-+      run: |
-+        tox
-+      env:
-+        PLATFORM: ${{ matrix.os }}
-+
-+    - name: Upload coverage to Codecov
-+      uses: codecov/codecov-action@v1
-+      with:
-+        env_vars: OS,PYTHON
-+
-+    - name: Test with specs and pystache-test
-+      run: |
-+        tox -e setup . ext/spec/specs
-+
-+    - name: Check pkg builds
-+      run: |
-+        tox -e deploy
-+
-+    - name: Check docs
-+      if: runner.os == 'Linux'
-+      run: |
-+        sudo apt-get -qq update
-+        sudo apt-get install -y pandoc
-+        tox -e docs
-diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml
-new file mode 100644
-index 0000000..261f9ad
---- /dev/null
-+++ b/.github/workflows/conda.yml
-@@ -0,0 +1,55 @@
-+name: Conda
-+
-+on:
-+  workflow_dispatch:
-+  pull_request:
-+  push:
-+    branches:
-+      - master
-+
-+jobs:
-+  build:
-+    strategy:
-+      fail-fast: false
-+      matrix:
-+        platform: [ubuntu-18.04, windows-latest, macos-latest]
-+        python-version: [3.6, 3.7, 3.8, 3.9]
-+
-+    runs-on: ${{ matrix.platform }}
-+
-+    # The setup-miniconda action needs this to activate miniconda
-+    defaults:
-+      run:
-+        shell: "bash -l {0}"
-+
-+    steps:
-+      - uses: actions/checkout@v2
-+        with:
-+          fetch-depth: 0
-+
-+      - name: Cache conda
-+        uses: actions/cache@v1
-+        with:
-+          path: ~/conda_pkgs_dir
-+          key: ${{matrix.os}}-conda-pkgs-${{hashFiles('**/conda/meta.yaml')}}
-+
-+      - name: Get conda
-+        uses: conda-incubator/setup-miniconda@v2
-+        with:
-+          python-version: ${{ matrix.python-version }}
-+          channels: conda-forge
-+          channel-priority: strict
-+          use-only-tar-bz2: true
-+          auto-activate-base: true
-+
-+      - name: Prepare
-+        run: conda install conda-build conda-verify
-+
-+      - name: Build
-+        run: conda build conda
-+
-+      - name: Install
-+        run: conda install -c ${CONDA_PREFIX}/conda-bld/ pystache
-+
-+      - name: Test
-+        run: python test_pystache.py
-diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
-new file mode 100644
-index 0000000..f33c4b5
---- /dev/null
-+++ b/.github/workflows/release.yml
-@@ -0,0 +1,94 @@
-+name: Release
-+
-+on:
-+  push:
-+    # release on tag push
-+    tags:
-+      - '*'
-+
-+jobs:
-+  wheels:
-+
-+    runs-on: ${{ matrix.os }}
-+    defaults:
-+      run:
-+        shell: bash
-+    env:
-+      PYTHONIOENCODING: utf-8
-+    strategy:
-+      fail-fast: false
-+      matrix:
-+        os: [ubuntu-18.04, macos-latest, windows-latest]
-+        python-version: [3.6, 3.7, 3.8, 3.9]
-+        exclude:
-+          - os: windows-latest
-+            python-version: 2.7
-+
-+    steps:
-+    - name: Set git crlf/eol
-+      run: |
-+        git config --global core.autocrlf false
-+        git config --global core.eol lf
-+
-+    - uses: actions/checkout@v2
-+      with:
-+        fetch-depth: 0
-+
-+    - name: Set up Python ${{ matrix.python-version }}
-+      uses: actions/setup-python@v2
-+      with:
-+        python-version: ${{ matrix.python-version }}
-+
-+    - name: Install dependencies
-+      run: |
-+        python -m pip install --upgrade pip wheel
-+        pip install tox tox-gh-actions
-+
-+    - name: Build dist pkgs
-+      run: |
-+        tox -e deploy
-+
-+    - name: Upload artifacts
-+      if: matrix.python-version == 3.7 && runner.os == 'Linux'
-+      uses: actions/upload-artifact@v2
-+      with:
-+        name: wheels
-+        path: ./dist/*.whl
-+
-+  create_release:
-+    name: Create Release
-+    needs: [wheels]
-+    runs-on: ubuntu-18.04
-+
-+    steps:
-+      - name: Get version
-+        id: get_version
-+        run: |
-+          echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
-+          echo ${{ env.VERSION }}
-+
-+      - uses: actions/checkout@v2
-+        with:
-+          fetch-depth: 0
-+
-+      # download all artifacts to project dir
-+      - uses: actions/download-artifact@v2
-+
-+      - name: Generate changes file
-+        uses: sarnold/gitchangelog-action@master
-+        with:
-+          github_token: ${{ secrets.GITHUB_TOKEN}}
-+
-+      - name: Create release
-+        id: create_release
-+        uses: softprops/action-gh-release@v1
-+        env:
-+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-+        with:
-+          tag_name: ${{ env.VERSION }}
-+          name: Release v${{ env.VERSION }}
-+          body_path: CHANGES.md
-+          draft: false
-+          prerelease: false
-+          files: |
-+            wheels/pystache*.whl
-diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
-new file mode 100644
-index 0000000..58f0c5e
---- /dev/null
-+++ b/.github/workflows/wheels.yml
-@@ -0,0 +1,82 @@
-+name: Wheels
-+
-+on:
-+  workflow_dispatch:
-+  pull_request:
-+  #push:
-+    #branches: [ master ]
-+
-+jobs:
-+  build:
-+
-+    runs-on: ${{ matrix.os }}
-+    defaults:
-+      run:
-+        shell: bash
-+    env:
-+      PYTHONIOENCODING: utf-8
-+    strategy:
-+      fail-fast: false
-+      matrix:
-+        os: [ubuntu-18.04, macos-latest, windows-latest]
-+        python-version: [3.6, 3.7, 3.8, 3.9]
-+
-+    steps:
-+      - name: Set git crlf/eol
-+        run: |
-+          git config --global core.autocrlf false
-+          git config --global core.eol lf
-+
-+      - uses: actions/checkout@v2
-+        with:
-+          fetch-depth: 0
-+
-+      - name: Set up Python ${{ matrix.python-version }}
-+        uses: actions/setup-python@v2
-+        with:
-+          python-version: ${{ matrix.python-version }}
-+
-+      - name: Install dependencies
-+        run: |
-+          python -m pip install --upgrade pip wheel
-+          pip install tox tox-gh-actions
-+
-+      - name: Build dist pkgs
-+        run: |
-+          tox -e deploy
-+
-+      - name: Upload artifacts
-+        if: matrix.python-version == 3.7 && runner.os == 'Linux'
-+        uses: actions/upload-artifact@v2
-+        with:
-+          name: wheels
-+          path: ./dist/*.whl
-+
-+  check_artifact:
-+    name: Check wheel artifact
-+    needs: [build]
-+    runs-on: ${{ matrix.os }}
-+    defaults:
-+      run:
-+        shell: bash
-+    env:
-+      PYTHONIOENCODING: utf-8
-+    strategy:
-+      fail-fast: false
-+      matrix:
-+        os: [ubuntu-18.04, macos-latest, windows-latest]
-+        python-version: [3.6, 3.8, 3.9]
-+
-+    steps:
-+      - name: Set up Python ${{ matrix.python-version }}
-+        uses: actions/setup-python@v2
-+        with:
-+          python-version: ${{ matrix.python-version }}
-+
-+      # download all artifacts to project dir
-+      - uses: actions/download-artifact@v2
-+
-+      - name: Check wheel install
-+        run: |
-+          bash -c 'export WHL=$(ls wheels/*.whl); python -m pip install $WHL'
-+          pystache-test
-diff --git a/.pep8speaks.yml b/.pep8speaks.yml
-new file mode 100644
-index 0000000..e841b66
---- /dev/null
-+++ b/.pep8speaks.yml
-@@ -0,0 +1,15 @@
-+scanner:
-+    linter: flake8  # Other option is pycodestyle
-+
-+no_blank_comment: False  # If True, no comment is made on PR without any errors.
-+descending_issues_order: True  # If True, PEP 8 issues in message will be displayed in descending order of line numbers in the file
-+
-+[flake8]
-+exclude =
-+    .git,
-+    .github,
-+    __pycache__,
-+    build,
-+    dist
-+
-+max-line-length = 110
-diff --git a/HISTORY.md b/HISTORY.md
-index e5b7638..60b6308 100644
---- a/HISTORY.md
-+++ b/HISTORY.md
-@@ -1,7 +1,42 @@
- History
- =======
- 
--**Note:** Official support for Python 2.4 will end with Pystache version 0.6.0.
-+**Note:** Official support for Python 2.7 will end with Pystache version 0.6.0.
-+
-+0.6.0 (2021-03-04)
-+------------------
-+
-+-   Bump spec versions to latest => v1.1.3
-+-   Modernize python and CI tools, update docs/doctests
-+-   Update unicode conversion test for py3-only
-+-   Add pep8speaks cfg, cleanup warnings
-+-   Remove superfluous setup test/unused imports
-+-   Add conda recipe/CI build
-+
-+0.5.6 (2021-02-28)
-+------------------
-+
-+-   Use correct wheel name in release workflow, limit wheels
-+-   Add install check/test of downloaded wheel
-+-   Update/add ci workflows and tox cfg, bump to next dev0 version
-+
-+0.5.5 (2020-12-16)
-+------------------
-+
-+-   fix document processing, update pandoc args and history
-+-   add release.yml to CI, test env settings
-+-   fix bogus commit message, update versions and tox cf
-+-   add post-test steps for building pkgs with/without doc updates
-+-   add CI build check, fix MANIFEST.in pruning
-+
-+0.5.4-2 (2020-11-09)
-+--------------------
-+
-+-   Merge pull request #1 from sarnold/rebase-up
-+-   Bugfix: test_specloader.py: fix test_find__with_directory on other OSs
-+-   Bugfix: pystache/loader.py: remove stray windows line-endings
-+-   fix crufty (and insecure) http urls
-+-   Bugfix: modernize python versions (keep py27) and fix spec_test load cmd
- 
- 0.5.4 (2014-07-11)
- ------------------
-diff --git a/MANIFEST.in b/MANIFEST.in
-index bdc64bf..1593143 100644
---- a/MANIFEST.in
-+++ b/MANIFEST.in
-@@ -1,7 +1,4 @@
--include README.md
--include HISTORY.md
--include LICENSE
--include TODO.md
-+include README.md HISTORY.md TODO.md LICENSE
- include setup_description.rst
- include tox.ini
- include test_pystache.py
-@@ -11,3 +8,6 @@ recursive-include pystache/tests *.mustache *.txt
- # We deliberately exclude the gh/ directory because it contains copies
- # of resources needed only for the web page hosted on GitHub (via the
- # gh-pages branch).
-+exclude *.ini *travis*
-+prune gh
-+prune .git*
-diff --git a/README.md b/README.md
-index 54a9608..1203b7a 100644
---- a/README.md
-+++ b/README.md
-@@ -10,11 +10,25 @@ Pystache
- <!-- -->
- <!-- We leave the leading brackets empty here.  Otherwise, unwanted -->
- <!-- caption text shows up in the reST version converted by pandoc. -->
--![](http://defunkt.github.com/pystache/images/logo_phillips.png "mustachioed, monocled snake by David Phillips")
-+[![ci](https://github.com/sarnold/pystache/actions/workflows/ci.yml/badge.svg)](https://github.com/sarnold/pystache/actions/workflows/ci.yml)
-+[![Conda](https://github.com/sarnold/pystache/actions/workflows/conda.yml/badge.svg)](https://github.com/sarnold/pystache/actions/workflows/conda.yml)
-+[![Wheels](https://github.com/sarnold/pystache/actions/workflows/wheels.yml/badge.svg)](https://github.com/sarnold/pystache/actions/workflows/wheels.yml)
-+[![Release](https://github.com/sarnold/pystache/actions/workflows/release.yml/badge.svg)](https://github.com/sarnold/pystache/actions/workflows/release.yml)
-+[![Python](https://img.shields.io/badge/python-3.6+-blue.svg)](https://www.python.org/downloads/)
- 
--![](https://secure.travis-ci.org/defunkt/pystache.png "Travis CI current build status")
-+[![Latest release](https://img.shields.io/github/v/release/sarnold/pystache?include_prereleases)](https://github.com/sarnold/pystache/releases/latest)
-+[![License](https://img.shields.io/github/license/sarnold/pystache)](https://github.com/sarnold/pystache/blob/master/LICENSE)
-+[![Maintainability](https://api.codeclimate.com/v1/badges/a8fa1bf4638bfc6581b6/maintainability)](https://codeclimate.com/github/sarnold/pystache/maintainability)
-+[![codecov](https://codecov.io/gh/sarnold/pystache/branch/master/graph/badge.svg?token=5PZNMZBI6K)](https://codecov.io/gh/sarnold/pystache)
- 
--[Pystache](http://defunkt.github.com/pystache) is a Python
-+
-+
-+This updated fork of Pystache is currently tested on Python 3.6+ and in
-+Conda, on Linux, Macos, and Windows (Python 2.7 support has been removed).
-+
-+![](gh/images/logo_phillips_small.png "mustachioed, monocled snake by David Phillips")
-+
-+[Pystache](http://sarnold.github.com/pystache) is a Python
- implementation of [Mustache](http://mustache.github.com/). Mustache is a
- framework-agnostic, logic-free templating system inspired by
- [ctemplate](http://code.google.com/p/google-ctemplate/) and
-@@ -27,10 +41,10 @@ provides a good introduction to Mustache's syntax. For a more complete
- (and more current) description of Mustache's behavior, see the official
- [Mustache spec](https://github.com/mustache/spec).
- 
--Pystache is [semantically versioned](http://semver.org) and can be found
--on [PyPI](http://pypi.python.org/pypi/pystache). This version of
--Pystache passes all tests in [version
--1.1.2](https://github.com/mustache/spec/tree/v1.1.2) of the spec.
-+Pystache is [semantically versioned](http://semver.org) and older versions
-+can still be found on [PyPI](http://pypi.python.org/pypi/pystache). This
-+version of Pystache now passes all tests in [version
-+1.1.3](https://github.com/mustache/spec/tree/v1.1.3) of the spec.
- 
- 
- Requirements
-@@ -38,41 +52,25 @@ Requirements
- 
- Pystache is tested with--
- 
---   Python 2.4 (requires simplejson [version
--    2.0.9](http://pypi.python.org/pypi/simplejson/2.0.9) or earlier)
---   Python 2.5 (requires
--    [simplejson](http://pypi.python.org/pypi/simplejson/))
---   Python 2.6
---   Python 2.7
---   Python 3.1
---   Python 3.2
---   Python 3.3
---   [PyPy](http://pypy.org/)
-+-   Python 3.6
-+-   Python 3.7
-+-   Python 3.8
-+-   Python 3.9
-+-   Conda (py36-py39)
- 
- [Distribute](http://packages.python.org/distribute/) (the setuptools fork)
--is recommended over [setuptools](http://pypi.python.org/pypi/setuptools),
--and is required in some cases (e.g. for Python 3 support).
--If you use [pip](http://www.pip-installer.org/), you probably already satisfy
--this requirement.
-+is no longer required over [setuptools](http://pypi.python.org/pypi/setuptools),
-+as the current packaging is now PEP517-compliant.
- 
- JSON support is needed only for the command-line interface and to run
--the spec tests. We require simplejson for earlier versions of Python
--since Python's [json](http://docs.python.org/library/json.html) module
--was added in Python 2.6.
--
--For Python 2.4 we require an earlier version of simplejson since
--simplejson stopped officially supporting Python 2.4 in simplejson
--version 2.1.0. Earlier versions of simplejson can be installed manually,
--as follows:
-+the spec tests; PyYAML can still be used (see the Develop section).
- 
--    pip install 'simplejson<2.1.0'
--
--Official support for Python 2.4 will end with Pystache version 0.6.0.
-+Official support for Python 2 will end with Pystache version 0.6.0.
- 
- Install It
- ----------
- 
--    pip install pystache
-+    pip install -U pystache -f https://github.com/sarnold/pystache/releases/
- 
- And test it--
- 
-@@ -85,12 +83,12 @@ Use It
- ------
- 
-     >>> import pystache
--    >>> print pystache.render('Hi {{person}}!', {'person': 'Mom'})
-+    >>> print(pystache.render('Hi {{person}}!', {'person': 'Mom'}))
-     Hi Mom!
- 
- You can also create dedicated view classes to hold your view logic.
- 
--Here's your view class (in .../examples/readme.py):
-+Here's your view class (in ../pystache/tests/examples/readme.py):
- 
-     class SayHello(object):
-         def to(self):
-@@ -109,7 +107,7 @@ directory as your class definition):
- Pull it together:
- 
-     >>> renderer = pystache.Renderer()
--    >>> print renderer.render(hello)
-+    >>> print(renderer.render(hello))
-     Hello, Pizza!
- 
- For greater control over rendering (e.g. to specify a custom template
-@@ -117,22 +115,22 @@ directory), use the `Renderer` class like above. One can pass attributes
- to the Renderer class constructor or set them on a Renderer instance. To
- customize template loading on a per-view basis, subclass `TemplateSpec`.
- See the docstrings of the
--[Renderer](https://github.com/defunkt/pystache/blob/master/pystache/renderer.py)
-+[Renderer](https://github.com/sarnold/pystache/blob/master/pystache/renderer.py)
- class and
--[TemplateSpec](https://github.com/defunkt/pystache/blob/master/pystache/template_spec.py)
-+[TemplateSpec](https://github.com/sarnold/pystache/blob/master/pystache/template_spec.py)
- class for more information.
- 
- You can also pre-parse a template:
- 
-     >>> parsed = pystache.parse(u"Hey {{#who}}{{.}}!{{/who}}")
--    >>> print parsed
--    [u'Hey ', _SectionNode(key=u'who', index_begin=12, index_end=18, parsed=[_EscapeNode(key=u'.'), u'!'])]
-+    >>> print(parsed)
-+    ['Hey ', _SectionNode(key='who', index_begin=12, index_end=18, parsed=[_EscapeNode(key='.'), '!'])]
- 
- And then:
- 
--    >>> print renderer.render(parsed, {'who': 'Pops'})
-+    >>> print(renderer.render(parsed, {'who': 'Pops'}))
-     Hey Pops!
--    >>> print renderer.render(parsed, {'who': 'you'})
-+    >>> print(renderer.render(parsed, {'who': 'you'}))
-     Hey you!
- 
- Python 3
-@@ -194,15 +192,16 @@ To test from a source distribution (without installing)--
-     python test_pystache.py
- 
- To test Pystache with multiple versions of Python (with a single
--command!), you can use [tox](http://pypi.python.org/pypi/tox):
-+command!) and different platforms, you can use [tox](http://pypi.python.org/pypi/tox):
-+
-+    pip install tox
-+    tox -e setup
- 
--    pip install 'virtualenv<1.8'  # Version 1.8 dropped support for Python 2.4.
--    pip install 'tox<1.4'  # Version 1.4 dropped support for Python 2.4.
--    tox
-+To run tests on multiple versions with coverage, run:
- 
--If you do not have all Python versions listed in `tox.ini`--
-+    tox -e py38-linux,py39-linux  # for example
- 
--    tox -e py26,py32  # for example
-+(substitute your platform above, eg, macos or windows)
- 
- The source distribution tests also include doctests and tests from the
- Mustache spec. To include tests from the Mustache spec in your test
-@@ -217,57 +216,33 @@ parses the json files. To install PyYAML--
- 
-     pip install pyyaml
- 
-+Once the submodule is available, you can run the full test set with:
-+
-+    tox -e setup . ext/spec/specs
-+
- To run a subset of the tests, you can use
- [nose](http://somethingaboutorange.com/mrl/projects/nose/0.11.1/testing.html):
- 
-     pip install nose
-     nosetests --tests pystache/tests/test_context.py:GetValueTests.test_dictionary__key_present
- 
--### Using Python 3 with Pystache from source
--
--Pystache is written in Python 2 and must be converted to Python 3 prior to
--using it with Python 3.  The installation process (and tox) do this
--automatically.
- 
--To convert the code to Python 3 manually (while using Python 3)--
-+Mailing List (old)
-+------------------
- 
--    python setup.py build
--
--This writes the converted code to a subdirectory called `build`.
--By design, Python 3 builds
--[cannot](https://bitbucket.org/tarek/distribute/issue/292/allow-use_2to3-with-python-2)
--be created from Python 2.
--
--To convert the code without using setup.py, you can use
--[2to3](http://docs.python.org/library/2to3.html) as follows (two steps)--
--
--    2to3 --write --nobackups --no-diffs --doctests_only pystache
--    2to3 --write --nobackups --no-diffs pystache
--
--This converts the code (and doctests) in place.
--
--To `import pystache` from a source distribution while using Python 3, be
--sure that you are importing from a directory containing a converted
--version of the code (e.g. from the `build` directory after converting),
--and not from the original (unconverted) source directory.  Otherwise, you will
--get a syntax error.  You can help prevent this by not running the Python
--IDE from the project directory when importing Pystache while using Python 3.
--
--
--Mailing List
--------------
--
--There is a [mailing list](http://librelist.com/browser/pystache/). Note
-+There is(was) a [mailing list](http://librelist.com/browser/pystache/). Note
- that there is a bit of a delay between posting a message and seeing it
- appear in the mailing list archive.
- 
- Credits
- -------
- 
--    >>> context = { 'author': 'Chris Wanstrath', 'maintainer': 'Chris Jerdonek' }
--    >>> print pystache.render("Author: {{author}}\nMaintainer: {{maintainer}}", context)
-+    >>> import pystache
-+    >>> context = { 'author': 'Chris Wanstrath', 'maintainer': 'Chris Jerdonek','refurbisher': 'Steve Arnold' }
-+    >>> print(pystache.render("Author: {{author}}\nMaintainer: {{maintainer}}\nRefurbisher: {{refurbisher}}", context))
-     Author: Chris Wanstrath
-     Maintainer: Chris Jerdonek
-+    Refurbisher: Steve Arnold
- 
- Pystache logo by [David Phillips](http://davidphillips.us/) is licensed
- under a [Creative Commons Attribution-ShareAlike 3.0 Unported
-diff --git a/TODO.md b/TODO.md
-index cd82417..76853a4 100644
---- a/TODO.md
-+++ b/TODO.md
-@@ -6,11 +6,10 @@ In development branch:
- * Figure out a way to suppress center alignment of images in reST output.
- * Add a unit test for the change made in 7ea8e7180c41.  This is with regard
-   to not requiring spec tests when running tests from a downloaded sdist.
--* End support for Python 2.4.
--* Add Python 3.3 to tox file (after deprecating 2.4).
-+* End support for Python 2.7 (done as of 03/03/21 - SA)
-+* Release 0.6.0 on github, make a pypi account (SA)
- * Turn the benchmarking script at pystache/tests/benchmark.py into a command
-   in pystache/commands, or make it a subcommand of one of the existing
-   commands (i.e. using a command argument).
- * Provide support for logging in at least one of the commands.
--* Make sure command parsing to pystache-test doesn't break with Python 2.4 and earlier.
- * Combine pystache-test with the main command.
-diff --git a/conda/meta.yaml b/conda/meta.yaml
-new file mode 100644
-index 0000000..e7f4fd9
---- /dev/null
-+++ b/conda/meta.yaml
-@@ -0,0 +1,50 @@
-+{% set name = "pystache" %}
-+{% set version = "0.6.0.dev0" %}
-+
-+package:
-+  name: {{ name|lower }}
-+  version: {{ version }}
-+
-+source:
-+  path: ..
-+
-+build:
-+  number: 0
-+  script: {{ PYTHON }} -m pip install . --no-deps --ignore-installed -vvv
-+  noarch: python
-+  entry_points:
-+    - pystache = pystache.commands.render:main
-+    - pystache-test = pystache.commands.test:main
-+
-+requirements:
-+  build:
-+    - python
-+    - setuptools
-+
-+  run:
-+    - python
-+
-+test:
-+  imports:
-+    - pystache
-+    - pystache.commands
-+    - pystache.tests
-+    - pystache.tests.data
-+    - pystache.tests.data.locator
-+    - pystache.tests.examples
-+
-+  commands:
-+    - pystache --help
-+    - pystache-test
-+
-+
-+about:
-+  home: https://github.com/sarnold/pystache
-+  license: MIT
-+  license_family: MIT
-+  license_file: LICENSE
-+  summary: Mustache for Python
-+
-+extra:
-+  recipe-maintainers:
-+    - sarnold
-diff --git a/pyproject.toml b/pyproject.toml
-new file mode 100644
-index 0000000..2f21011
---- /dev/null
-+++ b/pyproject.toml
-@@ -0,0 +1,3 @@
-+[build-system]
-+requires = ["setuptools>=40.8.0", "wheel"]
-+build-backend = "setuptools.build_meta"
-diff --git a/pystache/__init__.py b/pystache/__init__.py
-index 4cf2434..5edc1c5 100644
---- a/pystache/__init__.py
-+++ b/pystache/__init__.py
-@@ -10,4 +10,4 @@ from pystache.init import parse, render, Renderer, TemplateSpec
- 
- __all__ = ['parse', 'render', 'Renderer', 'TemplateSpec']
- 
--__version__ = '0.5.4'  # Also change in setup.py.
-+__version__ = '0.6.0'
-diff --git a/pystache/commands/render.py b/pystache/commands/render.py
-index 1a9c309..9c913e7 100644
---- a/pystache/commands/render.py
-+++ b/pystache/commands/render.py
-@@ -22,7 +22,7 @@ except:
-         from sys import exc_info
-         ex_type, ex_value, tb = exc_info()
-         new_ex = Exception("%s: %s" % (ex_type.__name__, ex_value))
--        raise new_ex.__class__, new_ex, tb
-+        raise new_ex.__class__(new_ex).with_traceback(tb)
- 
- # The optparse module is deprecated in Python 2.7 in favor of argparse.
- # However, argparse is not available in Python 2.6 and earlier.
-@@ -88,7 +88,7 @@ def main(sys_argv=sys.argv):
-         context = json.loads(context)
- 
-     rendered = renderer.render(template, context)
--    print rendered
-+    print(rendered)
- 
- 
- if __name__=='__main__':
-diff --git a/pystache/common.py b/pystache/common.py
-index fb266dd..0e9b091 100644
---- a/pystache/common.py
-+++ b/pystache/common.py
-@@ -5,17 +5,12 @@ Exposes functionality needed throughout the project.
- 
- """
- 
--from sys import version_info
- 
- def _get_string_types():
--    # TODO: come up with a better solution for this.  One of the issues here
--    #   is that in Python 3 there is no common base class for unicode strings
--    #   and byte strings, and 2to3 seems to convert all of "str", "unicode",
--    #   and "basestring" to Python 3's "str".
--    if version_info < (3, ):
--         return basestring
--    # The latter evaluates to "bytes" in Python 3 -- even after conversion by 2to3.
--    return (unicode, type(u"a".encode('utf-8')))
-+    """
-+    Return the Python3 string type (no more python2)
-+    """
-+    return (str, type("a".encode('utf-8')))
- 
- 
- _STRING_TYPES = _get_string_types()
-diff --git a/pystache/defaults.py b/pystache/defaults.py
-index bcfdf4c..2fab0e0 100644
---- a/pystache/defaults.py
-+++ b/pystache/defaults.py
-@@ -39,7 +39,7 @@ STRING_ENCODING = sys.getdefaultencoding()
- FILE_ENCODING = sys.getdefaultencoding()
- 
- # The delimiters to start with when parsing.
--DELIMITERS = (u'{{', u'}}')
-+DELIMITERS = ('{{', '}}')
- 
- # How to handle missing tags when rendering a template.
- MISSING_TAGS = MissingTags.ignore
-diff --git a/pystache/loader.py b/pystache/loader.py
-index d4a7e53..ea01d17 100644
---- a/pystache/loader.py
-+++ b/pystache/loader.py
-@@ -6,6 +6,7 @@ This module provides a Loader class for locating and reading templates.
- """
- 
- import os
-+import platform
- import sys
- 
- from pystache import common
-@@ -24,7 +25,7 @@ def _make_to_unicode():
-         """
-         if encoding is None:
-             encoding = defaults.STRING_ENCODING
--        return unicode(s, encoding, defaults.DECODE_ERRORS)
-+        return str(s, encoding, defaults.DECODE_ERRORS)
-     return to_unicode
- 
- 
-@@ -86,7 +87,7 @@ class Loader(object):
-     def _make_locator(self):
-         return Locator(extension=self.extension)
- 
--    def unicode(self, s, encoding=None):
-+    def str(self, s, encoding=None):
-         """
-         Convert a string to unicode using the given encoding, and return it.
- 
-@@ -104,8 +105,8 @@ class Loader(object):
-             Defaults to None.
- 
-         """
--        if isinstance(s, unicode):
--            return unicode(s)
-+        if isinstance(s, str):
-+            return str(s)
- 
-         return self.to_unicode(s, encoding)
- 
-@@ -118,8 +119,9 @@ class Loader(object):
- 
-         if encoding is None:
-             encoding = self.file_encoding
--
--        return self.unicode(b, encoding)
-+        if platform.system() == "Windows":
-+            return self.str(b, encoding).replace('\r', '')
-+        return self.str(b, encoding)
- 
-     def load_file(self, file_name):
-         """
-diff --git a/pystache/parsed.py b/pystache/parsed.py
-index 372d96c..75d417d 100644
---- a/pystache/parsed.py
-+++ b/pystache/parsed.py
-@@ -41,10 +41,10 @@ class ParsedTemplate(object):
-         """
-         # We avoid use of the ternary operator for Python 2.4 support.
-         def get_unicode(node):
--            if type(node) is unicode:
-+            if type(node) is str:
-                 return node
-             return node.render(engine, context)
--        parts = map(get_unicode, self._parse_tree)
-+        parts = list(map(get_unicode, self._parse_tree))
-         s = ''.join(parts)
- 
--        return unicode(s)
-+        return str(s)
-diff --git a/pystache/parser.py b/pystache/parser.py
-index c6a171f..1afd50a 100644
---- a/pystache/parser.py
-+++ b/pystache/parser.py
-@@ -11,8 +11,8 @@ from pystache import defaults
- from pystache.parsed import ParsedTemplate
- 
- 
--END_OF_LINE_CHARACTERS = [u'\r', u'\n']
--NON_BLANK_RE = re.compile(ur'^(.)', re.M)
-+END_OF_LINE_CHARACTERS = ['\r', '\n']
-+NON_BLANK_RE = re.compile(r'^(.)', re.M)
- 
- 
- # TODO: add some unit tests for this.
-@@ -30,12 +30,12 @@ def parse(template, delimiters=None):
- 
-     Examples:
- 
--    >>> parsed = parse(u"Hey {{#who}}{{name}}!{{/who}}")
--    >>> print str(parsed).replace('u', '')  # This is a hack to get the test to pass both in Python 2 and 3.
-+    >>> parsed = parse("Hey {{#who}}{{name}}!{{/who}}")
-+    >>> print(str(parsed).replace('u', ''))  # This is an old hack.
-     ['Hey ', _SectionNode(key='who', index_begin=12, index_end=21, parsed=[_EscapeNode(key='name'), '!'])]
- 
-     """
--    if type(template) is not unicode:
-+    if type(template) is not str:
-         raise Exception("Template is not unicode: %s" % type(template))
-     parser = _Parser(delimiters)
-     return parser.parse(template)
-@@ -94,7 +94,7 @@ class _CommentNode(object):
-         return _format(self)
- 
-     def render(self, engine, context):
--        return u''
-+        return ''
- 
- 
- class _ChangeNode(object):
-@@ -106,7 +106,7 @@ class _ChangeNode(object):
-         return _format(self)
- 
-     def render(self, engine, context):
--        return u''
-+        return ''
- 
- 
- class _EscapeNode(object):
-@@ -147,7 +147,7 @@ class _PartialNode(object):
-     def render(self, engine, context):
-         template = engine.resolve_partial(self.key)
-         # Indent before rendering.
--        template = re.sub(NON_BLANK_RE, self.indent + ur'\1', template)
-+        template = re.sub(NON_BLANK_RE, self.indent + r'\1', template)
- 
-         return engine.render(template, context)
- 
-@@ -168,7 +168,7 @@ class _InvertedNode(object):
-         # Note that lambdas are considered truthy for inverted sections
-         # per the spec.
-         if data:
--            return u''
-+            return ''
-         return self.parsed_section.render(engine, context)
- 
- 
-@@ -218,7 +218,7 @@ class _SectionNode(object):
-             parts.append(self.parsed.render(engine, context))
-             context.pop()
- 
--        return unicode(''.join(parts))
-+        return str(''.join(parts))
- 
- 
- class _Parser(object):
-diff --git a/pystache/renderengine.py b/pystache/renderengine.py
-index c797b17..2f1e341 100644
---- a/pystache/renderengine.py
-+++ b/pystache/renderengine.py
-@@ -160,7 +160,7 @@ class RenderEngine(object):
-         if not is_string(val):
-             # In case the template is an integer, for example.
-             val = self.to_str(val)
--        if type(val) is not unicode:
-+        if type(val) is not str:
-             val = self.literal(val)
-         return self.render(val, context, delimiters)
- 
-diff --git a/pystache/renderer.py b/pystache/renderer.py
-index ff6a90c..064f040 100644
---- a/pystache/renderer.py
-+++ b/pystache/renderer.py
-@@ -32,7 +32,7 @@ class Renderer(object):
-     >>> partials = {'partial': 'Hello, {{thing}}!'}
-     >>> renderer = Renderer(partials=partials)
-     >>> # We apply print to make the test work in Python 3 after 2to3.
--    >>> print renderer.render('{{>partial}}', {'thing': 'world'})
-+    >>> print(renderer.render('{{>partial}}', {'thing': 'world'}))
-     Hello, world!
- 
-     To customize string coercion (e.g. to render False values as ''), one can
-@@ -130,7 +130,7 @@ class Renderer(object):
-         if string_encoding is None:
-             string_encoding = defaults.STRING_ENCODING
- 
--        if isinstance(search_dirs, basestring):
-+        if isinstance(search_dirs, str):
-             search_dirs = [search_dirs]
- 
-         self._context = None
-@@ -177,16 +177,16 @@ class Renderer(object):
-         """
-         # We type-check to avoid "TypeError: decoding Unicode is not supported".
-         # We avoid the Python ternary operator for Python 2.4 support.
--        if isinstance(s, unicode):
-+        if isinstance(s, str):
-             return s
--        return self.unicode(s)
-+        return self.str(s)
- 
-     def _to_unicode_hard(self, s):
-         """
-         Convert a basestring to a string with type unicode (not subclass).
- 
-         """
--        return unicode(self._to_unicode_soft(s))
-+        return str(self._to_unicode_soft(s))
- 
-     def _escape_to_unicode(self, s):
-         """
-@@ -195,9 +195,9 @@ class Renderer(object):
-         Returns a unicode string (not subclass).
- 
-         """
--        return unicode(self.escape(self._to_unicode_soft(s)))
-+        return str(self.escape(self._to_unicode_soft(s)))
- 
--    def unicode(self, b, encoding=None):
-+    def str(self, b, encoding=None):
-         """
-         Convert a byte string to unicode, using string_encoding and decode_errors.
- 
-@@ -222,7 +222,7 @@ class Renderer(object):
- 
-         # TODO: Wrap UnicodeDecodeErrors with a message about setting
-         # the string_encoding and decode_errors attributes.
--        return unicode(b, encoding, self.decode_errors)
-+        return str(b, encoding, self.decode_errors)
- 
-     def _make_loader(self):
-         """
-@@ -230,7 +230,7 @@ class Renderer(object):
- 
-         """
-         return Loader(file_encoding=self.file_encoding, extension=self.file_extension,
--                      to_unicode=self.unicode, search_dirs=self.search_dirs)
-+                      to_unicode=self.str, search_dirs=self.search_dirs)
- 
-     def _make_load_template(self):
-         """
-@@ -299,7 +299,7 @@ class Renderer(object):
-             try:
-                 return load_partial(name)
-             except TemplateNotFoundError:
--                return u''
-+                return ''
- 
-         return resolve_partial
- 
-@@ -316,7 +316,7 @@ class Renderer(object):
-             try:
-                 return context_get(stack, name)
-             except KeyNotFoundError:
--                return u''
-+                return ''
- 
-         return resolve_context
- 
-diff --git a/pystache/specloader.py b/pystache/specloader.py
-index 3a77d4c..a82d52a 100644
---- a/pystache/specloader.py
-+++ b/pystache/specloader.py
-@@ -83,7 +83,7 @@ class SpecLoader(object):
- 
-         """
-         if spec.template is not None:
--            return self.loader.unicode(spec.template, spec.template_encoding)
-+            return self.loader.str(spec.template, spec.template_encoding)
- 
-         path = self._find(spec)
- 
-diff --git a/pystache/tests/benchmark.py b/pystache/tests/benchmark.py
-index d46e973..6cb54f8 100755
---- a/pystache/tests/benchmark.py
-+++ b/pystache/tests/benchmark.py
-@@ -13,6 +13,13 @@ tests/benchmark.py 10000
- import sys
- from timeit import Timer
- 
-+try:
-+    import chevron as pystache
-+    print('Using module: chevron')
-+except (ImportError):
-+    import pystache
-+    print('Using module: pystache')
-+
- import pystache
- 
- # TODO: make the example realistic.
-@@ -76,17 +83,17 @@ def main(sys_argv):
-     args = sys_argv[1:]
-     count = int(args[0])
- 
--    print "Benchmarking: %sx" % count
--    print
-+    print("Benchmarking: %sx" % count)
-+    print()
- 
-     for example in examples:
- 
-         test = make_test_function(example)
- 
-         t = Timer(test,)
--        print min(t.repeat(repeat=3, number=count))
-+        print(min(t.repeat(repeat=3, number=count)))
- 
--    print "Done"
-+    print("Done")
- 
- 
- if __name__ == '__main__':
-diff --git a/pystache/tests/common.py b/pystache/tests/common.py
-index 222e14f..12b76b5 100644
---- a/pystache/tests/common.py
-+++ b/pystache/tests/common.py
-@@ -72,8 +72,8 @@ def _find_files(root_dir, should_include):
-     #   http://docs.python.org/library/os.html#os.walk
-     for dir_path, dir_names, file_names in os.walk(root_dir):
-         new_paths = [os.path.join(dir_path, file_name) for file_name in file_names]
--        new_paths = filter(is_module, new_paths)
--        new_paths = filter(should_include, new_paths)
-+        new_paths = list(filter(is_module, new_paths))
-+        new_paths = list(filter(should_include, new_paths))
-         paths.extend(new_paths)
- 
-     return paths
-@@ -183,7 +183,7 @@ class AssertExceptionMixin:
-         try:
-             callable(*args, **kwds)
-             raise Exception("Expected exception: %s: %s" % (exception_type, repr(msg)))
--        except exception_type, err:
-+        except exception_type as err:
-             self.assertEqual(str(err), msg)
- 
- 
-@@ -228,10 +228,10 @@ class Attachable(object):
-     """
-     def __init__(self, **kwargs):
-         self.__args__ = kwargs
--        for arg, value in kwargs.iteritems():
-+        for arg, value in kwargs.items():
-             setattr(self, arg, value)
- 
-     def __repr__(self):
-         return "%s(%s)" % (self.__class__.__name__,
-                            ", ".join("%s=%s" % (k, repr(v))
--                                     for k, v in self.__args__.iteritems()))
-+                                     for k, v in self.__args__.items()))
-diff --git a/pystache/tests/examples/unicode_output.py b/pystache/tests/examples/unicode_output.py
-index da0e1d2..7bdea36 100644
---- a/pystache/tests/examples/unicode_output.py
-+++ b/pystache/tests/examples/unicode_output.py
-@@ -8,4 +8,4 @@ TODO: add a docstring.
- class UnicodeOutput(object):
- 
-     def name(self):
--        return u'Henri Poincaré'
-+        return 'Henri Poincaré'
-diff --git a/pystache/tests/main.py b/pystache/tests/main.py
-index 8af6b2e..17f2fb2 100644
---- a/pystache/tests/main.py
-+++ b/pystache/tests/main.py
-@@ -88,7 +88,7 @@ def main(sys_argv):
- 
-     """
-     # TODO: use logging module
--    print "pystache: running tests: argv: %s" % repr(sys_argv)
-+    print("pystache: running tests: argv: %s" % repr(sys_argv))
- 
-     should_source_exist = False
-     spec_test_dir = None
-@@ -131,11 +131,9 @@ def main(sys_argv):
-         module_names = _discover_test_modules(PACKAGE_DIR)
-         sys_argv.extend(module_names)
-         if project_dir is not None:
--            # Add the current module for unit tests contained here (e.g.
--            # to include SetupTests).
-+            # Add the current module for unit tests contained here
-             sys_argv.append(__name__)
- 
--    SetupTests.project_dir = project_dir
- 
-     extra_tests = make_extra_tests(project_dir, spec_test_dir)
-     test_program_class = make_test_program_class(extra_tests)
-@@ -166,25 +164,3 @@ def _discover_test_modules(package_dir):
-         raise Exception("No unit-test modules found--\n  in %s" % package_dir)
- 
-     return names
--
--
--class SetupTests(TestCase):
--
--    """Tests about setup.py."""
--
--    project_dir = None
--
--    def test_version(self):
--        """
--        Test that setup.py's version matches the package's version.
--
--        """
--        original_path = list(sys.path)
--
--        sys.path.insert(0, self.project_dir)
--
--        try:
--            from setup import VERSION
--            self.assertEqual(VERSION, pystache.__version__)
--        finally:
--            sys.path = original_path
-diff --git a/pystache/tests/spectesting.py b/pystache/tests/spectesting.py
-index ec8a08d..2dd57e8 100644
---- a/pystache/tests/spectesting.py
-+++ b/pystache/tests/spectesting.py
-@@ -37,7 +37,7 @@ except ImportError:
-             from sys import exc_info
-             ex_type, ex_value, tb = exc_info()
-             new_ex = Exception("%s: %s" % (ex_type.__name__, ex_value))
--            raise new_ex.__class__, new_ex, tb
-+            raise new_ex.__class__(new_ex).with_traceback(tb)
-     file_extension = 'json'
-     parser = json
- else:
-@@ -62,7 +62,7 @@ def get_spec_tests(spec_test_dir):
- 
-     """
-     # TODO: use logging module instead.
--    print "pystache: spec tests: using %s" % _get_parser_info()
-+    print("pystache: spec tests: using %s" % _get_parser_info())
- 
-     cases = []
- 
-@@ -103,7 +103,7 @@ def _read_spec_tests(path):
- 
-     """
-     b = common.read(path)
--    u = unicode(b, encoding=FILE_ENCODING)
-+    u = str(b, encoding=FILE_ENCODING)
-     spec_data = parse(u)
-     tests = spec_data['tests']
- 
-@@ -133,7 +133,7 @@ def _convert_children(node):
-         return
-     # Otherwise, node is a dict, so attempt the conversion.
- 
--    for key in node.keys():
-+    for key in list(node.keys()):
-         val = node[key]
- 
-         if not isinstance(val, dict) or val.get('__tag__') != 'code':
-@@ -158,9 +158,9 @@ def _deserialize_spec_test(data, file_path):
-     context = data['data']
-     description = data['desc']
-     # PyYAML seems to leave ASCII strings as byte strings.
--    expected = unicode(data['expected'])
-+    expected = str(data['expected'])
-     # TODO: switch to using dict.get().
--    partials = data.has_key('partials') and data['partials'] or {}
-+    partials = 'partials' in data and data['partials'] or {}
-     template = data['template']
-     test_name = data['name']
- 
-@@ -237,8 +237,8 @@ def parse(u):
-         value = loader.construct_mapping(node)
-         return eval(value['python'], {})
- 
--    yaml.add_constructor(u'!code', code_constructor)
--    return yaml.load(u)
-+    yaml.add_constructor('!code', code_constructor)
-+    return yaml.full_load(u)
- 
- 
- class SpecTestBase(unittest.TestCase, AssertStringMixin):
-diff --git a/pystache/tests/test___init__.py b/pystache/tests/test___init__.py
-index eae42c1..63d2c3b 100644
---- a/pystache/tests/test___init__.py
-+++ b/pystache/tests/test___init__.py
-@@ -6,9 +6,9 @@ Tests of __init__.py.
- """
- 
- # Calling "import *" is allowed only at the module level.
--GLOBALS_INITIAL = globals().keys()
-+GLOBALS_INITIAL = list(globals().keys())
- from pystache import *
--GLOBALS_PYSTACHE_IMPORTED = globals().keys()
-+GLOBALS_PYSTACHE_IMPORTED = list(globals().keys())
- 
- import unittest
- 
-diff --git a/pystache/tests/test_commands.py b/pystache/tests/test_commands.py
-index 2529d25..34fe8ba 100644
---- a/pystache/tests/test_commands.py
-+++ b/pystache/tests/test_commands.py
-@@ -39,7 +39,7 @@ class CommandsTestCase(unittest.TestCase):
- 
-         """
-         actual = self.callScript("Hi {{thing}}", '{"thing": "world"}')
--        self.assertEqual(actual, u"Hi world\n")
-+        self.assertEqual(actual, "Hi world\n")
- 
-     def tearDown(self):
-         sys.stdout = ORIGINAL_STDOUT
-diff --git a/pystache/tests/test_defaults.py b/pystache/tests/test_defaults.py
-index c78ea7c..5399bb0 100644
---- a/pystache/tests/test_defaults.py
-+++ b/pystache/tests/test_defaults.py
-@@ -31,37 +31,37 @@ class DefaultsConfigurableTestCase(unittest.TestCase, AssertStringMixin):
-             self.saved[e] = getattr(pystache.defaults, e)
- 
-     def tearDown(self):
--        for key, value in self.saved.items():
-+        for key, value in list(self.saved.items()):
-             setattr(pystache.defaults, key, value)
- 
-     def test_tag_escape(self):
-         """Test that changes to defaults.TAG_ESCAPE take effect."""
--        template = u"{{foo}}"
-+        template = "{{foo}}"
-         context = {'foo': '<'}
-         actual = pystache.render(template, context)
--        self.assertString(actual, u"&lt;")
-+        self.assertString(actual, "&lt;")
- 
-         pystache.defaults.TAG_ESCAPE = lambda u: u
-         actual = pystache.render(template, context)
--        self.assertString(actual, u"<")
-+        self.assertString(actual, "<")
- 
-     def test_delimiters(self):
-         """Test that changes to defaults.DELIMITERS take effect."""
--        template = u"[[foo]]{{foo}}"
-+        template = "[[foo]]{{foo}}"
-         context = {'foo': 'FOO'}
-         actual = pystache.render(template, context)
--        self.assertString(actual, u"[[foo]]FOO")
-+        self.assertString(actual, "[[foo]]FOO")
- 
-         pystache.defaults.DELIMITERS = ('[[', ']]')
-         actual = pystache.render(template, context)
--        self.assertString(actual, u"FOO{{foo}}")
-+        self.assertString(actual, "FOO{{foo}}")
- 
-     def test_missing_tags(self):
-         """Test that changes to defaults.MISSING_TAGS take effect."""
--        template = u"{{foo}}"
-+        template = "{{foo}}"
-         context = {}
-         actual = pystache.render(template, context)
--        self.assertString(actual, u"")
-+        self.assertString(actual, "")
- 
-         pystache.defaults.MISSING_TAGS = 'strict'
-         self.assertRaises(pystache.context.KeyNotFoundError,
-diff --git a/pystache/tests/test_examples.py b/pystache/tests/test_examples.py
-index 5c9f74d..9f93de3 100644
---- a/pystache/tests/test_examples.py
-+++ b/pystache/tests/test_examples.py
-@@ -7,15 +7,15 @@ TODO: add a docstring.
- 
- import unittest
- 
--from examples.comments import Comments
--from examples.double_section import DoubleSection
--from examples.escaped import Escaped
--from examples.unescaped import Unescaped
--from examples.template_partial import TemplatePartial
--from examples.delimiters import Delimiters
--from examples.unicode_output import UnicodeOutput
--from examples.unicode_input import UnicodeInput
--from examples.nested_context import NestedContext
-+from .examples.comments import Comments
-+from .examples.double_section import DoubleSection
-+from .examples.escaped import Escaped
-+from .examples.unescaped import Unescaped
-+from .examples.template_partial import TemplatePartial
-+from .examples.delimiters import Delimiters
-+from .examples.unicode_output import UnicodeOutput
-+from .examples.unicode_input import UnicodeInput
-+from .examples.nested_context import NestedContext
- from pystache import Renderer
- from pystache.tests.common import EXAMPLES_DIR
- from pystache.tests.common import AssertStringMixin
-@@ -29,34 +29,34 @@ class TestView(unittest.TestCase, AssertStringMixin):
-         self.assertString(actual, expected)
- 
-     def test_comments(self):
--        self._assert(Comments(), u"<h1>A Comedy of Errors</h1>")
-+        self._assert(Comments(), "<h1>A Comedy of Errors</h1>")
- 
-     def test_double_section(self):
--        self._assert(DoubleSection(), u"* first\n* second\n* third")
-+        self._assert(DoubleSection(), "* first\n* second\n* third")
- 
-     def test_unicode_output(self):
-         renderer = Renderer()
-         actual = renderer.render(UnicodeOutput())
--        self.assertString(actual, u'<p>Name: Henri Poincaré</p>')
-+        self.assertString(actual, '<p>Name: Henri Poincaré</p>')
- 
-     def test_unicode_input(self):
-         renderer = Renderer()
-         actual = renderer.render(UnicodeInput())
--        self.assertString(actual, u'abcdé')
-+        self.assertString(actual, 'abcdé')
- 
-     def test_escaping(self):
--        self._assert(Escaped(), u"<h1>Bear &gt; Shark</h1>")
-+        self._assert(Escaped(), "<h1>Bear &gt; Shark</h1>")
- 
-     def test_literal(self):
-         renderer = Renderer()
-         actual = renderer.render(Unescaped())
--        self.assertString(actual, u"<h1>Bear > Shark</h1>")
-+        self.assertString(actual, "<h1>Bear > Shark</h1>")
- 
-     def test_template_partial(self):
-         renderer = Renderer(search_dirs=EXAMPLES_DIR)
-         actual = renderer.render(TemplatePartial(renderer=renderer))
- 
--        self.assertString(actual, u"""<h1>Welcome</h1>
-+        self.assertString(actual, """<h1>Welcome</h1>
- Again, Welcome!""")
- 
-     def test_template_partial_extension(self):
-@@ -65,7 +65,7 @@ Again, Welcome!""")
-         view = TemplatePartial(renderer=renderer)
- 
-         actual = renderer.render(view)
--        self.assertString(actual, u"""Welcome
-+        self.assertString(actual, """Welcome
- -------
- 
- ## Again, Welcome! ##""")
-@@ -73,7 +73,7 @@ Again, Welcome!""")
-     def test_delimiters(self):
-         renderer = Renderer()
-         actual = renderer.render(Delimiters())
--        self.assertString(actual, u"""\
-+        self.assertString(actual, """\
- * It worked the first time.
- * And it worked the second time.
- * Then, surprisingly, it worked the third time.
-@@ -82,7 +82,7 @@ Again, Welcome!""")
-     def test_nested_context(self):
-         renderer = Renderer()
-         actual = renderer.render(NestedContext(renderer))
--        self.assertString(actual, u"one and foo and two")
-+        self.assertString(actual, "one and foo and two")
- 
-     def test_nested_context_is_available_in_view(self):
-         renderer = Renderer()
-@@ -91,7 +91,7 @@ Again, Welcome!""")
-         view.template = '{{#herp}}{{#derp}}{{nested_context_in_view}}{{/derp}}{{/herp}}'
- 
-         actual = renderer.render(view)
--        self.assertString(actual, u'it works!')
-+        self.assertString(actual, 'it works!')
- 
-     def test_partial_in_partial_has_access_to_grand_parent_context(self):
-         renderer = Renderer(search_dirs=EXAMPLES_DIR)
-diff --git a/pystache/tests/test_loader.py b/pystache/tests/test_loader.py
-index f2c2187..315daff 100644
---- a/pystache/tests/test_loader.py
-+++ b/pystache/tests/test_loader.py
-@@ -55,23 +55,23 @@ class LoaderTests(unittest.TestCase, AssertStringMixin, SetupDefaults):
- 
-     def test_init__to_unicode__default(self):
-         loader = Loader()
--        self.assertRaises(TypeError, loader.to_unicode, u"abc")
-+        self.assertRaises(TypeError, loader.to_unicode, "abc")
- 
-         decode_errors = defaults.DECODE_ERRORS
-         string_encoding = defaults.STRING_ENCODING
- 
--        nonascii = u'abcdé'.encode('utf-8')
-+        nonascii = 'abcdé'.encode('utf-8')
- 
-         loader = Loader()
-         self.assertRaises(UnicodeDecodeError, loader.to_unicode, nonascii)
- 
-         defaults.DECODE_ERRORS = 'ignore'
-         loader = Loader()
--        self.assertString(loader.to_unicode(nonascii), u'abcd')
-+        self.assertString(loader.to_unicode(nonascii), 'abcd')
- 
-         defaults.STRING_ENCODING = 'utf-8'
-         loader = Loader()
--        self.assertString(loader.to_unicode(nonascii), u'abcdé')
-+        self.assertString(loader.to_unicode(nonascii), 'abcdé')
- 
- 
-     def _get_path(self, filename):
-@@ -83,9 +83,9 @@ class LoaderTests(unittest.TestCase, AssertStringMixin, SetupDefaults):
- 
-         """
-         loader = Loader()
--        actual = loader.unicode("foo")
-+        actual = loader.str("foo")
- 
--        self.assertString(actual, u"foo")
-+        self.assertString(actual, "foo")
- 
-     def test_unicode__basic__input_unicode(self):
-         """
-@@ -93,24 +93,24 @@ class LoaderTests(unittest.TestCase, AssertStringMixin, SetupDefaults):
- 
-         """
-         loader = Loader()
--        actual = loader.unicode(u"foo")
-+        actual = loader.str("foo")
- 
--        self.assertString(actual, u"foo")
-+        self.assertString(actual, "foo")
- 
-     def test_unicode__basic__input_unicode_subclass(self):
-         """
-         Test unicode(): default arguments with unicode-subclass input.
- 
-         """
--        class UnicodeSubclass(unicode):
-+        class UnicodeSubclass(str):
-             pass
- 
--        s = UnicodeSubclass(u"foo")
-+        s = UnicodeSubclass("foo")
- 
-         loader = Loader()
--        actual = loader.unicode(s)
-+        actual = loader.str(s)
- 
--        self.assertString(actual, u"foo")
-+        self.assertString(actual, "foo")
- 
-     def test_unicode__to_unicode__attribute(self):
-         """
-@@ -119,16 +119,16 @@ class LoaderTests(unittest.TestCase, AssertStringMixin, SetupDefaults):
-         """
-         loader = Loader()
- 
--        non_ascii = u'abcdé'.encode('utf-8')
--        self.assertRaises(UnicodeDecodeError, loader.unicode, non_ascii)
-+        non_ascii = 'abcdé'.encode('utf-8')
-+        self.assertRaises(UnicodeDecodeError, loader.str, non_ascii)
- 
-         def to_unicode(s, encoding=None):
-             if encoding is None:
-                 encoding = 'utf-8'
--            return unicode(s, encoding)
-+            return str(s, encoding)
- 
-         loader.to_unicode = to_unicode
--        self.assertString(loader.unicode(non_ascii), u"abcdé")
-+        self.assertString(loader.str(non_ascii), "abcdé")
- 
-     def test_unicode__encoding_argument(self):
-         """
-@@ -137,12 +137,12 @@ class LoaderTests(unittest.TestCase, AssertStringMixin, SetupDefaults):
-         """
-         loader = Loader()
- 
--        non_ascii = u'abcdé'.encode('utf-8')
-+        non_ascii = 'abcdé'.encode('utf-8')
- 
--        self.assertRaises(UnicodeDecodeError, loader.unicode, non_ascii)
-+        self.assertRaises(UnicodeDecodeError, loader.str, non_ascii)
- 
--        actual = loader.unicode(non_ascii, encoding='utf-8')
--        self.assertString(actual, u'abcdé')
-+        actual = loader.str(non_ascii, encoding='utf-8')
-+        self.assertString(actual, 'abcdé')
- 
-     # TODO: check the read() unit tests.
-     def test_read(self):
-@@ -153,7 +153,7 @@ class LoaderTests(unittest.TestCase, AssertStringMixin, SetupDefaults):
-         loader = Loader()
-         path = self._get_path('ascii.mustache')
-         actual = loader.read(path)
--        self.assertString(actual, u'ascii: abc')
-+        self.assertString(actual, 'ascii: abc')
- 
-     def test_read__file_encoding__attribute(self):
-         """
-@@ -167,7 +167,7 @@ class LoaderTests(unittest.TestCase, AssertStringMixin, SetupDefaults):
- 
-         loader.file_encoding = 'utf-8'
-         actual = loader.read(path)
--        self.assertString(actual, u'non-ascii: é')
-+        self.assertString(actual, 'non-ascii: é')
- 
-     def test_read__encoding__argument(self):
-         """
-@@ -180,7 +180,7 @@ class LoaderTests(unittest.TestCase, AssertStringMixin, SetupDefaults):
-         self.assertRaises(UnicodeDecodeError, loader.read, path)
- 
-         actual = loader.read(path, encoding='utf-8')
--        self.assertString(actual, u'non-ascii: é')
-+        self.assertString(actual, 'non-ascii: é')
- 
-     def test_read__to_unicode__attribute(self):
-         """
-diff --git a/pystache/tests/test_pystache.py b/pystache/tests/test_pystache.py
-index 5447f8d..cf5d6af 100644
---- a/pystache/tests/test_pystache.py
-+++ b/pystache/tests/test_pystache.py
-@@ -71,14 +71,14 @@ class PystacheTests(unittest.TestCase):
-         template = "{{#stats}}({{key}} & {{value}}){{/stats}}"
-         stats = []
-         stats.append({'key': 123, 'value': ['something']})
--        stats.append({'key': u"chris", 'value': 0.900})
-+        stats.append({'key': "chris", 'value': 0.900})
-         context = { 'stats': stats }
-         self._assert_rendered(self.non_strings_expected, template, context)
- 
-     def test_unicode(self):
-         template = 'Name: {{name}}; Age: {{age}}'
--        context = {'name': u'Henri Poincaré', 'age': 156 }
--        self._assert_rendered(u'Name: Henri Poincaré; Age: 156', template, context)
-+        context = {'name': 'Henri Poincaré', 'age': 156}
-+        self._assert_rendered('Name: Henri Poincaré; Age: 156', template, context)
- 
-     def test_sections(self):
-         template = """<ul>{{#users}}<li>{{name}}</li>{{/users}}</ul>"""
-diff --git a/pystache/tests/test_renderengine.py b/pystache/tests/test_renderengine.py
-index db916f7..ed604c5 100644
---- a/pystache/tests/test_renderengine.py
-+++ b/pystache/tests/test_renderengine.py
-@@ -33,11 +33,11 @@ def mock_literal(s):
-       s: a byte string or unicode string.
- 
-     """
--    if isinstance(s, unicode):
-+    if isinstance(s, str):
-         # Strip off unicode super classes, if present.
--        u = unicode(s)
-+        u = str(s)
-     else:
--        u = unicode(s, encoding='ascii')
-+        u = str(s, encoding='ascii')
- 
-     # We apply upper() to make sure we are actually using our custom
-     # function in the tests
-@@ -94,17 +94,17 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         engine = kwargs.get('engine', self._engine())
- 
-         if partials is not None:
--            engine.resolve_partial = lambda key: unicode(partials[key])
-+            engine.resolve_partial = lambda key: str(partials[key])
- 
-         context = ContextStack(*context)
- 
-         # RenderEngine.render() only accepts unicode template strings.
--        actual = engine.render(unicode(template), context)
-+        actual = engine.render(str(template), context)
- 
-         self.assertString(actual=actual, expected=expected)
- 
-     def test_render(self):
--        self._assert_render(u'Hi Mom', 'Hi {{person}}', {'person': 'Mom'})
-+        self._assert_render('Hi Mom', 'Hi {{person}}', {'person': 'Mom'})
- 
-     def test__resolve_partial(self):
-         """
-@@ -112,10 +112,10 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
- 
-         """
-         engine = self._engine()
--        partials = {'partial': u"{{person}}"}
-+        partials = {'partial': "{{person}}"}
-         engine.resolve_partial = lambda key: partials[key]
- 
--        self._assert_render(u'Hi Mom', 'Hi {{>partial}}', {'person': 'Mom'}, engine=engine)
-+        self._assert_render('Hi Mom', 'Hi {{>partial}}', {'person': 'Mom'}, engine=engine)
- 
-     def test__literal(self):
-         """
-@@ -125,13 +125,13 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         engine = self._engine()
-         engine.literal = lambda s: s.upper()
- 
--        self._assert_render(u'BAR', '{{{foo}}}', {'foo': 'bar'}, engine=engine)
-+        self._assert_render('BAR', '{{{foo}}}', {'foo': 'bar'}, engine=engine)
- 
-     def test_literal__sigil(self):
-         template = "<h1>{{& thing}}</h1>"
-         context = {'thing': 'Bear > Giraffe'}
- 
--        expected = u"<h1>Bear > Giraffe</h1>"
-+        expected = "<h1>Bear > Giraffe</h1>"
- 
-         self._assert_render(expected, template, context)
- 
-@@ -143,7 +143,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         engine = self._engine()
-         engine.escape = lambda s: "**" + s
- 
--        self._assert_render(u'**bar', '{{foo}}', {'foo': 'bar'}, engine=engine)
-+        self._assert_render('**bar', '{{foo}}', {'foo': 'bar'}, engine=engine)
- 
-     def test__escape_does_not_call_literal(self):
-         """
-@@ -157,7 +157,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = 'literal: {{{foo}}} escaped: {{foo}}'
-         context = {'foo': 'bar'}
- 
--        self._assert_render(u'literal: BAR escaped: **bar', template, context, engine=engine)
-+        self._assert_render('literal: BAR escaped: **bar', template, context, engine=engine)
- 
-     def test__escape_preserves_unicode_subclasses(self):
-         """
-@@ -167,7 +167,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         variable value is markupsafe.Markup when escaping.
- 
-         """
--        class MyUnicode(unicode):
-+        class MyUnicode(str):
-             pass
- 
-         def escape(s):
-@@ -182,7 +182,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{foo1}} {{foo2}}'
-         context = {'foo1': MyUnicode('bar'), 'foo2': 'bar'}
- 
--        self._assert_render(u'**bar bar**', template, context, engine=engine)
-+        self._assert_render('**bar bar**', template, context, engine=engine)
- 
-     # Custom to_str for testing purposes.
-     def _to_str(self, val):
-@@ -197,9 +197,9 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{value}}'
-         context = {'value': None}
- 
--        self._assert_render(u'None', template, context, engine=engine)
-+        self._assert_render('None', template, context, engine=engine)
-         engine.to_str = self._to_str
--        self._assert_render(u'', template, context, engine=engine)
-+        self._assert_render('', template, context, engine=engine)
- 
-     def test_to_str__lambda(self):
-         """Test the to_str attribute for a lambda."""
-@@ -207,9 +207,9 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{value}}'
-         context = {'value': lambda: None}
- 
--        self._assert_render(u'None', template, context, engine=engine)
-+        self._assert_render('None', template, context, engine=engine)
-         engine.to_str = self._to_str
--        self._assert_render(u'', template, context, engine=engine)
-+        self._assert_render('', template, context, engine=engine)
- 
-     def test_to_str__section_list(self):
-         """Test the to_str attribute for a section list."""
-@@ -217,9 +217,9 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{#list}}{{.}}{{/list}}'
-         context = {'list': [None, None]}
- 
--        self._assert_render(u'NoneNone', template, context, engine=engine)
-+        self._assert_render('NoneNone', template, context, engine=engine)
-         engine.to_str = self._to_str
--        self._assert_render(u'', template, context, engine=engine)
-+        self._assert_render('', template, context, engine=engine)
- 
-     def test_to_str__section_lambda(self):
-         # TODO: add a test for a "method with an arity of 1".
-@@ -239,7 +239,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{text}} {{int}} {{{int}}}'
-         context = {'int': 100, 'text': 'foo'}
- 
--        self._assert_render(u'FOO 100 100', template, context, engine=engine)
-+        self._assert_render('FOO 100 100', template, context, engine=engine)
- 
-     def test_tag__output_not_interpolated(self):
-         """
-@@ -248,7 +248,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '{{template}}: {{planet}}'
-         context = {'template': '{{planet}}', 'planet': 'Earth'}
--        self._assert_render(u'{{planet}}: Earth', template, context)
-+        self._assert_render('{{planet}}: Earth', template, context)
- 
-     def test_tag__output_not_interpolated__section(self):
-         """
-@@ -257,7 +257,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '{{test}}'
-         context = {'test': '{{#hello}}'}
--        self._assert_render(u'{{#hello}}', template, context)
-+        self._assert_render('{{#hello}}', template, context)
- 
-     ## Test interpolation with "falsey" values
-     #
-@@ -268,17 +268,17 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-     def test_interpolation__falsey__zero(self):
-         template = '{{.}}'
-         context = 0
--        self._assert_render(u'0', template, context)
-+        self._assert_render('0', template, context)
- 
-     def test_interpolation__falsey__none(self):
-         template = '{{.}}'
-         context = None
--        self._assert_render(u'None', template, context)
-+        self._assert_render('None', template, context)
- 
-     def test_interpolation__falsey__zero(self):
-         template = '{{.}}'
-         context = False
--        self._assert_render(u'False', template, context)
-+        self._assert_render('False', template, context)
- 
-     # Built-in types:
-     #
-@@ -310,7 +310,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         Check tag interpolation with a built-in type: string.
- 
-         """
--        self._assert_builtin_type('abc', 'upper', 'ABC', u'xyz')
-+        self._assert_builtin_type('abc', 'upper', 'ABC', 'xyz')
- 
-     def test_interpolation__built_in_type__integer(self):
-         """
-@@ -324,7 +324,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         #
-         # we need to resort to built-in attributes (double-underscored) on
-         # the integer type.
--        self._assert_builtin_type(15, '__neg__', -15, u'999')
-+        self._assert_builtin_type(15, '__neg__', -15, '999')
- 
-     def test_interpolation__built_in_type__list(self):
-         """
-@@ -338,7 +338,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
- 
-         template = '{{#section}}{{%s}}{{/section}}' % attr_name
-         context = {'section': item, attr_name: 7}
--        self._assert_render(u'7', template, context)
-+        self._assert_render('7', template, context)
- 
-     # This test is also important for testing 2to3.
-     def test_interpolation__nonascii_nonunicode(self):
-@@ -347,8 +347,8 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
- 
-         """
-         template = '{{nonascii}}'
--        context = {'nonascii': u'abcdé'.encode('utf-8')}
--        self._assert_render(u'abcdé', template, context)
-+        context = {'nonascii': 'abcdé'.encode('utf-8')}
-+        self._assert_render('abcdé', template, context)
- 
-     def test_implicit_iterator__literal(self):
-         """
-@@ -358,7 +358,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = """{{#test}}{{{.}}}{{/test}}"""
-         context = {'test': ['<', '>']}
- 
--        self._assert_render(u'<>', template, context)
-+        self._assert_render('<>', template, context)
- 
-     def test_implicit_iterator__escaped(self):
-         """
-@@ -368,7 +368,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = """{{#test}}{{.}}{{/test}}"""
-         context = {'test': ['<', '>']}
- 
--        self._assert_render(u'&lt;&gt;', template, context)
-+        self._assert_render('&lt;&gt;', template, context)
- 
-     def test_literal__in_section(self):
-         """
-@@ -378,7 +378,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{#test}}1 {{{less_than}}} 2{{/test}}'
-         context = {'test': {'less_than': '<'}}
- 
--        self._assert_render(u'1 < 2', template, context)
-+        self._assert_render('1 < 2', template, context)
- 
-     def test_literal__in_partial(self):
-         """
-@@ -389,11 +389,11 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         partials = {'partial': '1 {{{less_than}}} 2'}
-         context = {'less_than': '<'}
- 
--        self._assert_render(u'1 < 2', template, context, partials=partials)
-+        self._assert_render('1 < 2', template, context, partials=partials)
- 
-     def test_partial(self):
-         partials = {'partial': "{{person}}"}
--        self._assert_render(u'Hi Mom', 'Hi {{>partial}}', {'person': 'Mom'}, partials=partials)
-+        self._assert_render('Hi Mom', 'Hi {{>partial}}', {'person': 'Mom'}, partials=partials)
- 
-     def test_partial__context_values(self):
-         """
-@@ -406,7 +406,9 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         partials = {'partial': 'unescaped: {{{foo}}} escaped: {{foo}}'}
-         context = {'foo': '<'}
- 
--        self._assert_render(u'unescaped: < escaped: &lt;', template, context, engine=engine, partials=partials)
-+        self._assert_render(
-+            'unescaped: < escaped: &lt;',
-+            template, context, engine=engine, partials=partials)
- 
-     ## Test cases related specifically to lambdas.
- 
-@@ -417,8 +419,8 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
- 
-         """
-         template = '{{#nonascii}}{{.}}{{/nonascii}}'
--        context = {'nonascii': u'abcdé'.encode('utf-8')}
--        self._assert_render(u'abcdé', template, context)
-+        context = {'nonascii': 'abcdé'.encode('utf-8')}
-+        self._assert_render('abcdé', template, context)
- 
-     # This test is also important for testing 2to3.
-     def test_lambda__returning_nonascii_nonunicode(self):
-@@ -427,8 +429,8 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
- 
-         """
-         template = '{{lambda}}'
--        context = {'lambda': lambda: u'abcdé'.encode('utf-8')}
--        self._assert_render(u'abcdé', template, context)
-+        context = {'lambda': lambda: 'abcdé'.encode('utf-8')}
-+        self._assert_render('abcdé', template, context)
- 
-     ## Test cases related specifically to sections.
- 
-@@ -440,7 +442,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{/section}}'
-         try:
-             self._assert_render(None, template)
--        except ParsingError, err:
-+        except ParsingError as err:
-             self.assertEqual(str(err), "Section end tag mismatch: section != None")
- 
-     def test_section__end_tag_mismatch(self):
-@@ -451,7 +453,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{#section_start}}{{/section_end}}'
-         try:
-             self._assert_render(None, template)
--        except ParsingError, err:
-+        except ParsingError as err:
-             self.assertEqual(str(err), "Section end tag mismatch: section_end != section_start")
- 
-     def test_section__context_values(self):
-@@ -464,7 +466,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{#test}}unescaped: {{{foo}}} escaped: {{foo}}{{/test}}'
-         context = {'test': {'foo': '<'}}
- 
--        self._assert_render(u'unescaped: < escaped: &lt;', template, context, engine=engine)
-+        self._assert_render('unescaped: < escaped: &lt;', template, context, engine=engine)
- 
-     def test_section__context_precedence(self):
-         """
-@@ -473,7 +475,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '{{entree}} : {{#vegetarian}}{{entree}}{{/vegetarian}}'
-         context = {'entree': 'chicken', 'vegetarian': {'entree': 'beans and rice'}}
--        self._assert_render(u'chicken : beans and rice', template, context)
-+        self._assert_render('chicken : beans and rice', template, context)
- 
-     def test_section__list_referencing_outer_context(self):
-         """
-@@ -491,7 +493,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
- 
-         template = "{{#list}}{{greeting}} {{name}}, {{/list}}"
- 
--        self._assert_render(u"Hi Al, Hi Bob, ", template, context)
-+        self._assert_render("Hi Al, Hi Bob, ", template, context)
- 
-     def test_section__output_not_interpolated(self):
-         """
-@@ -500,7 +502,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '{{#section}}{{template}}{{/section}}: {{planet}}'
-         context = {'section': True, 'template': '{{planet}}', 'planet': 'Earth'}
--        self._assert_render(u'{{planet}}: Earth', template, context)
-+        self._assert_render('{{planet}}: Earth', template, context)
- 
-     # TODO: have this test case added to the spec.
-     def test_section__string_values_not_lists(self):
-@@ -511,7 +513,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{#section}}foo{{/section}}'
-         context = {'section': '123'}
-         # If strings were interpreted as lists, this would give "foofoofoo".
--        self._assert_render(u'foo', template, context)
-+        self._assert_render('foo', template, context)
- 
-     def test_section__nested_truthy(self):
-         """
-@@ -525,7 +527,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |'
-         context = {'bool': True}
--        self._assert_render(u'| A B C D E |', template, context)
-+        self._assert_render('| A B C D E |', template, context)
- 
-     def test_section__nested_with_same_keys(self):
-         """
-@@ -537,16 +539,16 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         # Start with an easier, working case.
-         template = '{{#x}}{{#z}}{{y}}{{/z}}{{/x}}'
-         context = {'x': {'z': {'y': 1}}}
--        self._assert_render(u'1', template, context)
-+        self._assert_render('1', template, context)
- 
-         template = '{{#x}}{{#x}}{{y}}{{/x}}{{/x}}'
-         context = {'x': {'x': {'y': 1}}}
--        self._assert_render(u'1', template, context)
-+        self._assert_render('1', template, context)
- 
-     def test_section__lambda(self):
-         template = '{{#test}}Mom{{/test}}'
-         context = {'test': (lambda text: 'Hi %s' % text)}
--        self._assert_render(u'Hi Mom', template, context)
-+        self._assert_render('Hi Mom', template, context)
- 
-     # This test is also important for testing 2to3.
-     def test_section__lambda__returning_nonascii_nonunicode(self):
-@@ -555,8 +557,8 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
- 
-         """
-         template = '{{#lambda}}{{/lambda}}'
--        context = {'lambda': lambda text: u'abcdé'.encode('utf-8')}
--        self._assert_render(u'abcdé', template, context)
-+        context = {'lambda': lambda text: 'abcdé'.encode('utf-8')}
-+        self._assert_render('abcdé', template, context)
- 
-     def test_section__lambda__returning_nonstring(self):
-         """
-@@ -565,7 +567,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '{{#lambda}}foo{{/lambda}}'
-         context = {'lambda': lambda text: len(text)}
--        self._assert_render(u'3', template, context)
-+        self._assert_render('3', template, context)
- 
-     def test_section__iterable(self):
-         """
-@@ -575,10 +577,10 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = '{{#iterable}}{{.}}{{/iterable}}'
- 
-         context = {'iterable': (i for i in range(3))}  # type 'generator'
--        self._assert_render(u'012', template, context)
-+        self._assert_render('012', template, context)
- 
--        context = {'iterable': xrange(4)}  # type 'xrange'
--        self._assert_render(u'0123', template, context)
-+        context = {'iterable': range(4)}  # type 'xrange'
-+        self._assert_render('0123', template, context)
- 
-         d = {'foo': 0, 'bar': 0}
-         # We don't know what order of keys we'll be given, but from the
-@@ -586,8 +588,8 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         #  "If items(), keys(), values(), iteritems(), iterkeys(), and
-         #   itervalues() are called with no intervening modifications to
-         #   the dictionary, the lists will directly correspond."
--        expected = u''.join(d.keys())
--        context = {'iterable': d.iterkeys()}  # type 'dictionary-keyiterator'
-+        expected = ''.join(list(d.keys()))
-+        context = {'iterable': iter(d.keys())}  # type 'dictionary-keyiterator'
-         self._assert_render(expected, template, context)
- 
-     def test_section__lambda__tag_in_output(self):
-@@ -605,7 +607,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '{{#test}}Hi {{person}}{{/test}}'
-         context = {'person': 'Mom', 'test': (lambda text: text + " :)")}
--        self._assert_render(u'Hi Mom :)', template, context)
-+        self._assert_render('Hi Mom :)', template, context)
- 
-     def test_section__lambda__list(self):
-         """
-@@ -621,7 +623,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-                    'lambdas': [lambda text: "~{{%s}}~" % text,
-                                lambda text: "#{{%s}}#" % text]}
- 
--        self._assert_render(u'<~bar~#bar#>', template, context)
-+        self._assert_render('<~bar~#bar#>', template, context)
- 
-     def test_section__lambda__mixed_list(self):
-         """
-@@ -636,7 +638,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         context = {'foo': 'bar',
-                    'lambdas': [lambda text: "~{{%s}}~" % text, 1]}
- 
--        self._assert_render(u'<~bar~foo>', template, context)
-+        self._assert_render('<~bar~foo>', template, context)
- 
-     def test_section__lambda__not_on_context_stack(self):
-         """
-@@ -653,7 +655,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         context = {'foo': 'bar', 'lambda': (lambda text: "{{.}}")}
-         template = '{{#foo}}{{#lambda}}blah{{/lambda}}{{/foo}}'
--        self._assert_render(u'bar', template, context)
-+        self._assert_render('bar', template, context)
- 
-     def test_section__lambda__no_reinterpolation(self):
-         """
-@@ -670,15 +672,15 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '{{#planet}}{{#lambda}}dot{{/lambda}}{{/planet}}'
-         context = {'planet': 'Earth', 'dot': '~{{.}}~', 'lambda': (lambda text: "#{{%s}}#" % text)}
--        self._assert_render(u'#~{{.}}~#', template, context)
-+        self._assert_render('#~{{.}}~#', template, context)
- 
-     def test_comment__multiline(self):
-         """
-         Check that multiline comments are permitted.
- 
-         """
--        self._assert_render(u'foobar', 'foo{{! baz }}bar')
--        self._assert_render(u'foobar', 'foo{{! \nbaz }}bar')
-+        self._assert_render('foobar', 'foo{{! baz }}bar')
-+        self._assert_render('foobar', 'foo{{! \nbaz }}bar')
- 
-     def test_custom_delimiters__sections(self):
-         """
-@@ -689,7 +691,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = '{{=[[ ]]=}}[[#foo]]bar[[/foo]]'
-         context = {'foo': True}
--        self._assert_render(u'bar', template, context)
-+        self._assert_render('bar', template, context)
- 
-     def test_custom_delimiters__not_retroactive(self):
-         """
-@@ -698,7 +700,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         Test case for issue #35: https://github.com/defunkt/pystache/issues/35
- 
-         """
--        expected = u' {{foo}} '
-+        expected = ' {{foo}} '
-         self._assert_render(expected, '{{=$ $=}} {{foo}} ')
-         self._assert_render(expected, '{{=$ $=}} {{foo}} $={{ }}=$')  # was yielding u'  '.
- 
-@@ -713,7 +715,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         template = 'Hello, {{person.name}}. I see you are {{person.details.age}}.'
-         person = Attachable(name='Biggles', details={'age': 42})
-         context = {'person': person}
--        self._assert_render(u'Hello, Biggles. I see you are 42.', template, context)
-+        self._assert_render('Hello, Biggles. I see you are 42.', template, context)
- 
-     def test_dot_notation__multiple_levels(self):
-         """
-@@ -722,7 +724,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         """
-         template = """Hello, Mr. {{person.name.lastname}}.
-         I see you're back from {{person.travels.last.country.city}}."""
--        expected = u"""Hello, Mr. Pither.
-+        expected = """Hello, Mr. Pither.
-         I see you're back from Cornwall."""
-         context = {'person': {'name': {'firstname': 'unknown', 'lastname': 'Pither'},
-                             'travels': {'last': {'country': {'city': 'Cornwall'}}},
-@@ -758,10 +760,10 @@ class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
-         context = {'a': {'b': 'A.B'}, 'c': {'a': 'A'} }
- 
-         template = '{{a.b}}'
--        self._assert_render(u'A.B', template, context)
-+        self._assert_render('A.B', template, context)
- 
-         template = '{{#c}}{{a}}{{/c}}'
--        self._assert_render(u'A', template, context)
-+        self._assert_render('A', template, context)
- 
-         template = '{{#c}}{{a.b}}{{/c}}'
-         self.assertException(KeyNotFoundError, "Key %(unicode)s'a.b' not found: missing %(unicode)s'b'" %
-diff --git a/pystache/tests/test_renderer.py b/pystache/tests/test_renderer.py
-index 0dbe0d9..e0d2448 100644
---- a/pystache/tests/test_renderer.py
-+++ b/pystache/tests/test_renderer.py
-@@ -10,7 +10,7 @@ import os
- import sys
- import unittest
- 
--from examples.simple import Simple
-+from .examples.simple import Simple
- from pystache import Renderer
- from pystache import TemplateSpec
- from pystache.common import TemplateNotFoundError
-@@ -33,7 +33,7 @@ def _make_renderer():
- def mock_unicode(b, encoding=None):
-     if encoding is None:
-         encoding = 'ascii'
--    u = unicode(b, encoding=encoding)
-+    u = str(b, encoding=encoding)
-     return u.upper()
- 
- 
-@@ -197,13 +197,13 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
- 
-         """
-         renderer = self._renderer()
--        b = u"é".encode('utf-8')
-+        b = "é".encode('utf-8')
- 
-         renderer.string_encoding = "ascii"
--        self.assertRaises(UnicodeDecodeError, renderer.unicode, b)
-+        self.assertRaises(UnicodeDecodeError, renderer.str, b)
- 
-         renderer.string_encoding = "utf-8"
--        self.assertEqual(renderer.unicode(b), u"é")
-+        self.assertEqual(renderer.str(b), "é")
- 
-     def test_unicode__decode_errors(self):
-         """
-@@ -212,14 +212,14 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
-         """
-         renderer = self._renderer()
-         renderer.string_encoding = "ascii"
--        b = u"déf".encode('utf-8')
-+        b = "déf".encode('utf-8')
- 
-         renderer.decode_errors = "ignore"
--        self.assertEqual(renderer.unicode(b), "df")
-+        self.assertEqual(renderer.str(b), "df")
- 
-         renderer.decode_errors = "replace"
-         # U+FFFD is the official Unicode replacement character.
--        self.assertEqual(renderer.unicode(b), u'd\ufffd\ufffdf')
-+        self.assertEqual(renderer.str(b), u'd\ufffd\ufffdf')
- 
-     ## Test the _make_loader() method.
- 
-@@ -243,7 +243,7 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
-         renderer = self._renderer()
-         renderer.file_encoding = 'enc'
-         renderer.file_extension = 'ext'
--        renderer.unicode = unicode_
-+        renderer.str = unicode_
- 
-         loader = renderer._make_loader()
- 
-@@ -260,12 +260,12 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
-         """
-         renderer = self._renderer()
-         rendered = renderer.render('foo')
--        self.assertEqual(type(rendered), unicode)
-+        self.assertEqual(type(rendered), str)
- 
-     def test_render__unicode(self):
-         renderer = self._renderer()
--        actual = renderer.render(u'foo')
--        self.assertEqual(actual, u'foo')
-+        actual = renderer.render('foo')
-+        self.assertEqual(actual, 'foo')
- 
-     def test_render__str(self):
-         renderer = self._renderer()
-@@ -274,8 +274,8 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
- 
-     def test_render__non_ascii_character(self):
-         renderer = self._renderer()
--        actual = renderer.render(u'Poincaré')
--        self.assertEqual(actual, u'Poincaré')
-+        actual = renderer.render('Poincaré')
-+        self.assertEqual(actual, 'Poincaré')
- 
-     def test_render__context(self):
-         """
-@@ -326,7 +326,7 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
- 
-         """
-         renderer = _make_renderer()
--        template = u"déf".encode("utf-8")
-+        template = "déf".encode("utf-8")
- 
-         # Check that decode_errors and string_encoding are both respected.
-         renderer.decode_errors = 'ignore'
-@@ -334,7 +334,7 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
-         self.assertEqual(renderer.render(template), "df")
- 
-         renderer.string_encoding = 'utf_8'
--        self.assertEqual(renderer.render(template), u"déf")
-+        self.assertEqual(renderer.render(template), "déf")
- 
-     def test_make_resolve_partial(self):
-         """
-@@ -347,7 +347,7 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
- 
-         actual = resolve_partial('foo')
-         self.assertEqual(actual, 'bar')
--        self.assertEqual(type(actual), unicode, "RenderEngine requires that "
-+        self.assertEqual(type(actual), str, "RenderEngine requires that "
-             "resolve_partial return unicode strings.")
- 
-     def test_make_resolve_partial__unicode(self):
-@@ -362,7 +362,7 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
-         self.assertEqual(resolve_partial("partial"), "foo")
- 
-         # Now with a value that is already unicode.
--        renderer.partials = {'partial': u'foo'}
-+        renderer.partials = {'partial': 'foo'}
-         resolve_partial = renderer._make_resolve_partial()
-         # If the next line failed, we would get the following error:
-         #   TypeError: decoding Unicode is not supported
-@@ -373,7 +373,7 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
-         data_dir = get_data_path()
-         renderer = Renderer(search_dirs=data_dir)
-         actual = renderer.render_name("say_hello", to='foo')
--        self.assertString(actual, u"Hello, foo")
-+        self.assertString(actual, "Hello, foo")
- 
-     def test_render_path(self):
-         """
-@@ -412,7 +412,7 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
- 
-         spec = Spec()
-         actual = renderer.render(spec)
--        self.assertString(actual, u'hello, world')
-+        self.assertString(actual, 'hello, world')
- 
-     def test_render__view(self):
-         """
-@@ -484,7 +484,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
-         Check that resolve_partial returns unicode (and not a subclass).
- 
-         """
--        class MyUnicode(unicode):
-+        class MyUnicode(str):
-             pass
- 
-         renderer = Renderer()
-@@ -495,12 +495,12 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
- 
-         actual = engine.resolve_partial('str')
-         self.assertEqual(actual, "foo")
--        self.assertEqual(type(actual), unicode)
-+        self.assertEqual(type(actual), str)
- 
-         # Check that unicode subclasses are not preserved.
-         actual = engine.resolve_partial('subclass')
-         self.assertEqual(actual, "abc")
--        self.assertEqual(type(actual), unicode)
-+        self.assertEqual(type(actual), str)
- 
-     def test__resolve_partial__not_found(self):
-         """
-@@ -512,7 +512,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
-         engine = renderer._make_render_engine()
-         resolve_partial = engine.resolve_partial
- 
--        self.assertString(resolve_partial('foo'), u'')
-+        self.assertString(resolve_partial('foo'), '')
- 
-     def test__resolve_partial__not_found__missing_tags_strict(self):
-         """
-@@ -539,7 +539,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
-         engine = renderer._make_render_engine()
-         resolve_partial = engine.resolve_partial
- 
--        self.assertString(resolve_partial('foo'), u'')
-+        self.assertString(resolve_partial('foo'), '')
- 
-     def test__resolve_partial__not_found__partials_dict__missing_tags_strict(self):
-         """
-@@ -566,12 +566,12 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
- 
-         """
-         renderer = self._make_renderer()
--        renderer.unicode = mock_unicode
-+        renderer.str = mock_unicode
- 
-         engine = renderer._make_render_engine()
-         literal = engine.literal
- 
--        b = u"foo".encode("ascii")
-+        b = "foo".encode("ascii")
-         self.assertEqual(literal(b), "FOO")
- 
-     def test__literal__handles_unicode(self):
-@@ -585,7 +585,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
-         engine = renderer._make_render_engine()
-         literal = engine.literal
- 
--        self.assertEqual(literal(u"foo"), "foo")
-+        self.assertEqual(literal("foo"), "foo")
- 
-     def test__literal__returns_unicode(self):
-         """
-@@ -598,16 +598,16 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
-         engine = renderer._make_render_engine()
-         literal = engine.literal
- 
--        self.assertEqual(type(literal("foo")), unicode)
-+        self.assertEqual(type(literal("foo")), str)
- 
--        class MyUnicode(unicode):
-+        class MyUnicode(str):
-             pass
- 
-         s = MyUnicode("abc")
- 
-         self.assertEqual(type(s), MyUnicode)
--        self.assertTrue(isinstance(s, unicode))
--        self.assertEqual(type(literal(s)), unicode)
-+        self.assertTrue(isinstance(s, str))
-+        self.assertEqual(type(literal(s)), str)
- 
-     ## Test the engine's escape attribute.
- 
-@@ -630,12 +630,12 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
- 
-         """
-         renderer = Renderer()
--        renderer.unicode = mock_unicode
-+        renderer.str = mock_unicode
- 
-         engine = renderer._make_render_engine()
-         escape = engine.escape
- 
--        b = u"foo".encode('ascii')
-+        b = "foo".encode('ascii')
-         self.assertEqual(escape(b), "FOO")
- 
-     def test__escape__has_access_to_original_unicode_subclass(self):
-@@ -644,16 +644,16 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
- 
-         """
-         renderer = Renderer()
--        renderer.escape = lambda s: unicode(type(s).__name__)
-+        renderer.escape = lambda s: str(type(s).__name__)
- 
-         engine = renderer._make_render_engine()
-         escape = engine.escape
- 
--        class MyUnicode(unicode):
-+        class MyUnicode(str):
-             pass
- 
--        self.assertEqual(escape(u"foo".encode('ascii')), unicode.__name__)
--        self.assertEqual(escape(u"foo"), unicode.__name__)
-+        self.assertEqual(escape("foo".encode('ascii')), str.__name__)
-+        self.assertEqual(escape("foo"), str.__name__)
-         self.assertEqual(escape(MyUnicode("foo")), MyUnicode.__name__)
- 
-     def test__escape__returns_unicode(self):
-@@ -667,17 +667,17 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
-         engine = renderer._make_render_engine()
-         escape = engine.escape
- 
--        self.assertEqual(type(escape("foo")), unicode)
-+        self.assertEqual(type(escape("foo")), str)
- 
-         # Check that literal doesn't preserve unicode subclasses.
--        class MyUnicode(unicode):
-+        class MyUnicode(str):
-             pass
- 
-         s = MyUnicode("abc")
- 
-         self.assertEqual(type(s), MyUnicode)
--        self.assertTrue(isinstance(s, unicode))
--        self.assertEqual(type(escape(s)), unicode)
-+        self.assertTrue(isinstance(s, str))
-+        self.assertEqual(type(escape(s)), str)
- 
-     ## Test the missing_tags attribute.
- 
-@@ -706,7 +706,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
-         stack = ContextStack({'foo': 'bar'})
- 
-         self.assertEqual('bar', engine.resolve_context(stack, 'foo'))
--        self.assertString(u'', engine.resolve_context(stack, 'missing'))
-+        self.assertString('', engine.resolve_context(stack, 'missing'))
- 
-     def test__resolve_context__missing_tags_strict(self):
-         """
-diff --git a/pystache/tests/test_simple.py b/pystache/tests/test_simple.py
-index 07b059f..b88bf35 100644
---- a/pystache/tests/test_simple.py
-+++ b/pystache/tests/test_simple.py
-@@ -2,11 +2,11 @@ import unittest
- 
- import pystache
- from pystache import Renderer
--from examples.nested_context import NestedContext
--from examples.complex import Complex
--from examples.lambdas import Lambdas
--from examples.template_partial import TemplatePartial
--from examples.simple import Simple
-+from .examples.nested_context import NestedContext
-+from .examples.complex import Complex
-+from .examples.lambdas import Lambdas
-+from .examples.template_partial import TemplatePartial
-+from .examples.simple import Simple
- 
- from pystache.tests.common import EXAMPLES_DIR
- from pystache.tests.common import AssertStringMixin
-@@ -20,7 +20,7 @@ class TestSimple(unittest.TestCase, AssertStringMixin):
-         view.template = '{{#foo}}{{thing1}} and {{thing2}} and {{outer_thing}}{{/foo}}{{^foo}}Not foo!{{/foo}}'
- 
-         actual = renderer.render(view)
--        self.assertString(actual, u"one and foo and two")
-+        self.assertString(actual, "one and foo and two")
- 
-     def test_looping_and_negation_context(self):
-         template = '{{#item}}{{header}}: {{name}} {{/item}}{{^item}} Shouldnt see me{{/item}}'
-@@ -40,7 +40,7 @@ class TestSimple(unittest.TestCase, AssertStringMixin):
- 
-         renderer = Renderer()
-         actual = renderer.render(view)
--        self.assertString(actual, u'bar != bar. oh, it does!')
-+        self.assertString(actual, 'bar != bar. oh, it does!')
- 
-     def test_rendering_partial(self):
-         renderer = Renderer(search_dirs=EXAMPLES_DIR)
-@@ -49,11 +49,11 @@ class TestSimple(unittest.TestCase, AssertStringMixin):
-         view.template = '{{>inner_partial}}'
- 
-         actual = renderer.render(view)
--        self.assertString(actual, u'Again, Welcome!')
-+        self.assertString(actual, 'Again, Welcome!')
- 
-         view.template = '{{#looping}}{{>inner_partial}} {{/looping}}'
-         actual = renderer.render(view)
--        self.assertString(actual, u"Again, Welcome! Again, Welcome! Again, Welcome! ")
-+        self.assertString(actual, "Again, Welcome! Again, Welcome! Again, Welcome! ")
- 
-     def test_non_existent_value_renders_blank(self):
-         view = Simple()
-@@ -77,7 +77,7 @@ class TestSimple(unittest.TestCase, AssertStringMixin):
-         view = TemplatePartial(renderer=renderer)
- 
-         actual = renderer.render(view)
--        self.assertString(actual, u"""Welcome
-+        self.assertString(actual, """Welcome
- -------
- 
- ## Again, Welcome! ##""")
-diff --git a/pystache/tests/test_specloader.py b/pystache/tests/test_specloader.py
-index cacc0fc..dcdc55f 100644
---- a/pystache/tests/test_specloader.py
-+++ b/pystache/tests/test_specloader.py
-@@ -9,11 +9,11 @@ import os.path
- import sys
- import unittest
- 
--import examples
--from examples.simple import Simple
--from examples.complex import Complex
--from examples.lambdas import Lambdas
--from examples.inverted import Inverted, InvertedLists
-+from . import examples
-+from .examples.simple import Simple
-+from .examples.complex import Complex
-+from .examples.lambdas import Lambdas
-+from .examples.inverted import Inverted, InvertedLists
- from pystache import Renderer
- from pystache import TemplateSpec
- from pystache.common import TemplateNotFoundError
-@@ -70,7 +70,7 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
-         renderer2 = Renderer(search_dirs=EXAMPLES_DIR)
- 
-         actual = renderer1.render(spec)
--        self.assertString(actual, u"Partial: ")
-+        self.assertString(actual, "Partial: ")
- 
-         actual = renderer2.render(spec)
-         self.assertEqual(actual, "Partial: No tags...")
-@@ -79,7 +79,7 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
-         renderer = Renderer()
-         actual = renderer.render(Simple())
- 
--        self.assertString(actual, u"Hi pizza!")
-+        self.assertString(actual, "Hi pizza!")
- 
-     def test_non_callable_attributes(self):
-         view = Simple()
-@@ -92,7 +92,7 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
-     def test_complex(self):
-         renderer = Renderer()
-         actual = renderer.render(Complex())
--        self.assertString(actual, u"""\
-+        self.assertString(actual, """\
- <h1>Colors</h1>
- <ul>
- <li><strong>red</strong></li>
-@@ -111,7 +111,7 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
- 
-         renderer = Renderer()
-         actual = renderer.render(view)
--        self.assertString(actual, u'nopqrstuvwxyz')
-+        self.assertString(actual, 'nopqrstuvwxyz')
- 
-     def test_higher_order_lambda(self):
-         view = Lambdas()
-@@ -119,7 +119,7 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
- 
-         renderer = Renderer()
-         actual = renderer.render(view)
--        self.assertString(actual, u'abcdefghijklmnopqrstuvwxyz')
-+        self.assertString(actual, 'abcdefghijklmnopqrstuvwxyz')
- 
-     def test_partials_with_lambda(self):
-         view = Lambdas()
-@@ -127,7 +127,7 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
- 
-         renderer = Renderer(search_dirs=EXAMPLES_DIR)
-         actual = renderer.render(view)
--        self.assertEqual(actual, u'nopqrstuvwxyz')
-+        self.assertEqual(actual, 'nopqrstuvwxyz')
- 
-     def test_hierarchical_partials_with_lambdas(self):
-         view = Lambdas()
-@@ -135,12 +135,12 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
- 
-         renderer = Renderer(search_dirs=EXAMPLES_DIR)
-         actual = renderer.render(view)
--        self.assertString(actual, u'nopqrstuvwxyznopqrstuvwxyz')
-+        self.assertString(actual, 'nopqrstuvwxyznopqrstuvwxyz')
- 
-     def test_inverted(self):
-         renderer = Renderer()
-         actual = renderer.render(Inverted())
--        self.assertString(actual, u"""one, two, three, empty list""")
-+        self.assertString(actual, """one, two, three, empty list""")
- 
-     def test_accessing_properties_on_parent_object_from_child_objects(self):
-         parent = Thing()
-@@ -152,12 +152,12 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
-         renderer = Renderer()
-         actual = renderer.render(view, {'parent': parent})
- 
--        self.assertString(actual, u'derp')
-+        self.assertString(actual, 'derp')
- 
-     def test_inverted_lists(self):
-         renderer = Renderer()
-         actual = renderer.render(InvertedLists())
--        self.assertString(actual, u"""one, two, three, empty list""")
-+        self.assertString(actual, """one, two, three, empty list""")
- 
- 
- def _make_specloader():
-@@ -176,7 +176,7 @@ def _make_specloader():
-         """
-         if encoding is None:
-             encoding = 'ascii'
--        return unicode(s, encoding, 'strict')
-+        return str(s, encoding, 'strict')
- 
-     loader = Loader(file_encoding='ascii', to_unicode=to_unicode)
-     return SpecLoader(loader=loader)
-@@ -222,7 +222,7 @@ class SpecLoaderTests(unittest.TestCase, AssertIsMixin, AssertStringMixin,
-         custom.template = "abc"
- 
-         spec_loader = self._make_specloader()
--        self._assert_template(spec_loader, custom, u"abc")
-+        self._assert_template(spec_loader, custom, "abc")
- 
-     def test_load__template__type_unicode(self):
-         """
-@@ -230,10 +230,10 @@ class SpecLoaderTests(unittest.TestCase, AssertIsMixin, AssertStringMixin,
- 
-         """
-         custom = TemplateSpec()
--        custom.template = u"abc"
-+        custom.template = "abc"
- 
-         spec_loader = self._make_specloader()
--        self._assert_template(spec_loader, custom, u"abc")
-+        self._assert_template(spec_loader, custom, "abc")
- 
-     def test_load__template__unicode_non_ascii(self):
-         """
-@@ -241,10 +241,10 @@ class SpecLoaderTests(unittest.TestCase, AssertIsMixin, AssertStringMixin,
- 
-         """
-         custom = TemplateSpec()
--        custom.template = u"é"
-+        custom.template = "é"
- 
-         spec_loader = self._make_specloader()
--        self._assert_template(spec_loader, custom, u"é")
-+        self._assert_template(spec_loader, custom, "é")
- 
-     def test_load__template__with_template_encoding(self):
-         """
-@@ -252,14 +252,14 @@ class SpecLoaderTests(unittest.TestCase, AssertIsMixin, AssertStringMixin,
- 
-         """
-         custom = TemplateSpec()
--        custom.template = u'é'.encode('utf-8')
-+        custom.template = 'é'.encode('utf-8')
- 
-         spec_loader = self._make_specloader()
- 
--        self.assertRaises(UnicodeDecodeError, self._assert_template, spec_loader, custom, u'é')
-+        self.assertRaises(UnicodeDecodeError, self._assert_template, spec_loader, custom, 'é')
- 
-         custom.template_encoding = 'utf-8'
--        self._assert_template(spec_loader, custom, u'é')
-+        self._assert_template(spec_loader, custom, 'é')
- 
-     # TODO: make this test complete.
-     def test_load__template__correct_loader(self):
-@@ -279,10 +279,10 @@ class SpecLoaderTests(unittest.TestCase, AssertIsMixin, AssertStringMixin,
-                 self.encoding = None
- 
-             # Overrides the existing method.
--            def unicode(self, s, encoding=None):
-+            def str(self, s, encoding=None):
-                 self.s = s
-                 self.encoding = encoding
--                return u"foo"
-+                return "foo"
- 
-         loader = MockLoader()
-         custom_loader = SpecLoader()
-@@ -293,7 +293,7 @@ class SpecLoaderTests(unittest.TestCase, AssertIsMixin, AssertStringMixin,
-         view.template_encoding = "encoding-foo"
- 
-         # Check that our unicode() above was called.
--        self._assert_template(custom_loader, view, u'foo')
-+        self._assert_template(custom_loader, view, 'foo')
-         self.assertEqual(loader.s, "template-foo")
-         self.assertEqual(loader.encoding, "encoding-foo")
- 
-@@ -410,7 +410,7 @@ class TemplateSpecTests(unittest.TestCase, AssertPathsMixin):
-         loader = self._make_loader()
-         actual = loader.load(custom)
- 
--        self.assertEqual(type(actual), unicode)
-+        self.assertEqual(type(actual), str)
-         self.assertEqual(actual, expected)
- 
-     def test_get_template(self):
-@@ -420,7 +420,7 @@ class TemplateSpecTests(unittest.TestCase, AssertPathsMixin):
-         """
-         view = SampleView()
- 
--        self._assert_get_template(view, u"ascii: abc")
-+        self._assert_get_template(view, "ascii: abc")
- 
-     def test_get_template__template_encoding(self):
-         """
-@@ -432,4 +432,4 @@ class TemplateSpecTests(unittest.TestCase, AssertPathsMixin):
-         self.assertRaises(UnicodeDecodeError, self._assert_get_template, view, 'foo')
- 
-         view.template_encoding = 'utf-8'
--        self._assert_get_template(view, u"non-ascii: é")
-+        self._assert_get_template(view, "non-ascii: é")
-diff --git a/setup.cfg b/setup.cfg
-index 861a9f5..f6f1279 100644
---- a/setup.cfg
-+++ b/setup.cfg
-@@ -1,5 +1,71 @@
--[egg_info]
--tag_build = 
--tag_date = 0
--tag_svn_revision = 0
-+[metadata]
-+name = pystache
-+version = attr: pystache.__version__
-+author = Chris Wanstrath
-+author_email = chris@ozmm.org
-+maintainer = Steve Arnold
-+maintainer_email = nerdboy@gentoo.org
-+description = Mustache for Python
-+url = https://github.com/sarnold/pystache
-+license = MIT
-+license_files = LICENSE
-+classifiers =
-+    Development Status :: 4 - Beta
-+    Intended Audience :: Developers
-+    License :: OSI Approved :: MIT License
-+    Programming Language :: Python :: 3
-+    Programming Language :: Python :: 3.6
-+    Programming Language :: Python :: 3.7
-+    Programming Language :: Python :: 3.8
-+    Programming Language :: Python :: 3.9
- 
-+[options]
-+python_requires = >=3.6
-+zip_safe = True
-+include_package_data = True
-+packages = find:
-+
-+[options.package_data]
-+* = *.mustache, *.txt
-+
-+[options.entry_points]
-+console_scripts =
-+    pystache=pystache.commands.render:main
-+    pystache-test=pystache.commands.test:main
-+
-+[options.extras_require]
-+test =
-+    nose
-+
-+cov =
-+    coverage
-+
-+[bdist_wheel]
-+universal = 0
-+
-+[check-manifest]
-+ignore =
-+    .codeclimate.yml
-+    .gitattributes
-+    .coveragerc
-+    .gitignore
-+    .pep8speaks.yml
-+    codecov.yml
-+
-+[flake8]
-+exclude =
-+    .git,
-+    __pycache__,
-+    build,
-+    dist
-+
-+max-line-length = 110
-+
-+[nosetests]
-+traverse-namespace = 1
-+verbosity = 3
-+with-coverage = 1
-+with-doctest = 1
-+doctest-extension = rst
-+cover-package = pystache
-+cover-xml = 1
-diff --git a/setup.py b/setup.py
-index 0d99aae..f0b7d7f 100644
---- a/setup.py
-+++ b/setup.py
-@@ -28,7 +28,7 @@ it on the PyPI project page.  If PyPI finds any issues, it will render it
- instead as plain-text, which we do not want.
- 
- To check in advance that PyPI will accept and parse the reST file as HTML,
--you can use the rst2html program installed by the docutils package
-+you can use the rst2html.py program installed by the docutils package
- (http://docutils.sourceforge.net/).  To install docutils:
- 
-     $ pip install docutils
-@@ -89,30 +89,7 @@ import os
- import shutil
- import sys
- 
--
--py_version = sys.version_info
--
--# distutils does not seem to support the following setup() arguments.
--# It displays a UserWarning when setup() is passed those options:
--#
--#  * entry_points
--#  * install_requires
--#
--# distribute works with Python 2.3.5 and above:
--#
--#   http://packages.python.org/distribute/setuptools.html#building-and-distributing-packages-with-distribute
--#
--if py_version < (2, 3, 5):
--    # TODO: this might not work yet.
--    import distutils as dist
--    from distutils import core
--    setup = core.setup
--else:
--    import setuptools as dist
--    setup = dist.setup
--
--
--VERSION = '0.5.4'  # Also change in pystache/__init__.py.
-+from setuptools import setup
- 
- FILE_ENCODING = 'utf-8'
- 
-@@ -126,22 +103,6 @@ TEMP_EXTENSION = '.temp'
- 
- PREP_COMMAND = 'prep'
- 
--CLASSIFIERS = (
--    'Development Status :: 4 - Beta',
--    'License :: OSI Approved :: MIT License',
--    'Programming Language :: Python',
--    'Programming Language :: Python :: 2',
--    'Programming Language :: Python :: 2.4',
--    'Programming Language :: Python :: 2.5',
--    'Programming Language :: Python :: 2.6',
--    'Programming Language :: Python :: 2.7',
--    'Programming Language :: Python :: 3',
--    'Programming Language :: Python :: 3.1',
--    'Programming Language :: Python :: 3.2',
--    'Programming Language :: Python :: 3.3',
--    'Programming Language :: Python :: Implementation :: PyPy',
--)
--
- # Comments in reST begin with two dots.
- RST_LONG_DESCRIPTION_INTRO = """\
- .. Do not edit this file.  This file is auto-generated for PyPI by setup.py
-@@ -221,7 +182,7 @@ def convert_md_to_rst(md_path, rst_temp_path):
- 
-     """
-     # Pandoc uses the UTF-8 character encoding for both input and output.
--    command = "pandoc --write=rst --output=%s %s" % (rst_temp_path, md_path)
-+    command = "pandoc -f markdown-smart --write=rst --output=%s %s" % (rst_temp_path, md_path)
-     print("converting with pandoc: %s to %s\n-->%s" % (md_path, rst_temp_path,
-                                                        command))
- 
-@@ -308,65 +269,9 @@ Run the following command and commit the changes--
-     os.system('python setup.py sdist upload')
- 
- 
--# We use the package simplejson for older Python versions since Python
--# does not contain the module json before 2.6:
--#
--#   http://docs.python.org/library/json.html
--#
--# Moreover, simplejson stopped officially support for Python 2.4 in version 2.1.0:
--#
--#   https://github.com/simplejson/simplejson/blob/master/CHANGES.txt
--#
--requires = []
--if py_version < (2, 5):
--    requires.append('simplejson<2.1')
--elif py_version < (2, 6):
--    requires.append('simplejson')
--
--INSTALL_REQUIRES = requires
--
--# TODO: decide whether to use find_packages() instead.  I'm not sure that
--#   find_packages() is available with distutils, for example.
--PACKAGES = [
--    'pystache',
--    'pystache.commands',
--    # The following packages are only for testing.
--    'pystache.tests',
--    'pystache.tests.data',
--    'pystache.tests.data.locator',
--    'pystache.tests.examples',
--]
--
--
--# The purpose of this function is to follow the guidance suggested here:
--#
--#   http://packages.python.org/distribute/python3.html#note-on-compatibility-with-setuptools
--#
--# The guidance is for better compatibility when using setuptools (e.g. with
--# earlier versions of Python 2) instead of Distribute, because of new
--# keyword arguments to setup() that setuptools may not recognize.
--def get_extra_args():
--    """
--    Return a dictionary of extra args to pass to setup().
--
--    """
--    extra = {}
--    # TODO: it might be more correct to check whether we are using
--    #   Distribute instead of setuptools, since use_2to3 doesn't take
--    #   effect when using Python 2, even when using Distribute.
--    if py_version >= (3, ):
--        # Causes 2to3 to be run during the build step.
--        extra['use_2to3'] = True
--
--    return extra
--
--
- def main(sys_argv):
- 
-     # TODO: use the logging module instead of printing.
--    # TODO: include the following in a verbose mode.
--    sys.stderr.write("pystache: using: version %s of %s\n" % (repr(dist.__version__), repr(dist)))
--
-     command = sys_argv[-1]
- 
-     if command == 'publish':
-@@ -377,35 +282,10 @@ def main(sys_argv):
-         sys.exit()
- 
-     long_description = read(RST_DESCRIPTION_PATH)
--    template_files = ['*.mustache', '*.txt']
--    extra_args = get_extra_args()
--
--    setup(name='pystache',
--          version=VERSION,
--          license='MIT',
--          description='Mustache for Python',
--          long_description=long_description,
--          author='Chris Wanstrath',
--          author_email='chris@ozmm.org',
--          maintainer='Chris Jerdonek',
--          maintainer_email='chris.jerdonek@gmail.com',
--          url='http://github.com/defunkt/pystache',
--          install_requires=INSTALL_REQUIRES,
--          packages=PACKAGES,
--          package_data = {
--              # Include template files so tests can be run.
--              'pystache.tests.data': template_files,
--              'pystache.tests.data.locator': template_files,
--              'pystache.tests.examples': template_files,
--          },
--          entry_points = {
--            'console_scripts': [
--                'pystache=pystache.commands.render:main',
--                'pystache-test=pystache.commands.test:main',
--            ],
--          },
--          classifiers = CLASSIFIERS,
--          **extra_args
-+
-+    setup(
-+        long_description=long_description,
-+        long_description_content_type='text/x-rst',
-     )
- 
- 
-diff --git a/setup_description.rst b/setup_description.rst
-index 724c457..d7f1bc0 100644
---- a/setup_description.rst
-+++ b/setup_description.rst
-@@ -4,13 +4,17 @@
- Pystache
- ========
- 
--.. figure:: http://defunkt.github.com/pystache/images/logo_phillips.png
--   :alt: mustachioed, monocled snake by David Phillips
-+|ci| |Conda| |Wheels| |Release| |Python|
- 
--.. figure:: https://secure.travis-ci.org/defunkt/pystache.png
--   :alt: Travis CI current build status
-+|Latest release| |License| |Maintainability| |codecov|
- 
--`Pystache <http://defunkt.github.com/pystache>`__ is a Python
-+This updated fork of Pystache is currently tested on Python 3.6+ and in
-+Conda, on Linux, Macos, and Windows (Python 2.7 support has been
-+removed).
-+
-+|image9|
-+
-+`Pystache <http://sarnold.github.com/pystache>`__ is a Python
- implementation of `Mustache <http://mustache.github.com/>`__. Mustache
- is a framework-agnostic, logic-free templating system inspired by
- `ctemplate <http://code.google.com/p/google-ctemplate/>`__ and
-@@ -23,62 +27,45 @@ page provides a good introduction to Mustache's syntax. For a more
- complete (and more current) description of Mustache's behavior, see the
- official `Mustache spec <https://github.com/mustache/spec>`__.
- 
--Pystache is `semantically versioned <http://semver.org>`__ and can be
--found on `PyPI <http://pypi.python.org/pypi/pystache>`__. This version
--of Pystache passes all tests in `version
--1.1.2 <https://github.com/mustache/spec/tree/v1.1.2>`__ of the spec.
-+Pystache is `semantically versioned <http://semver.org>`__ and older
-+versions can still be found on
-+`PyPI <http://pypi.python.org/pypi/pystache>`__. This version of
-+Pystache now passes all tests in `version
-+1.1.3 <https://github.com/mustache/spec/tree/v1.1.3>`__ of the spec.
- 
- Requirements
- ------------
- 
- Pystache is tested with--
- 
---  Python 2.4 (requires simplejson `version
--   2.0.9 <http://pypi.python.org/pypi/simplejson/2.0.9>`__ or earlier)
---  Python 2.5 (requires
--   `simplejson <http://pypi.python.org/pypi/simplejson/>`__)
---  Python 2.6
---  Python 2.7
---  Python 3.1
---  Python 3.2
---  Python 3.3
---  `PyPy <http://pypy.org/>`__
-+-  Python 3.6
-+-  Python 3.7
-+-  Python 3.8
-+-  Python 3.9
-+-  Conda (py36-py39)
- 
- `Distribute <http://packages.python.org/distribute/>`__ (the setuptools
--fork) is recommended over
--`setuptools <http://pypi.python.org/pypi/setuptools>`__, and is required
--in some cases (e.g. for Python 3 support). If you use
--`pip <http://www.pip-installer.org/>`__, you probably already satisfy
--this requirement.
-+fork) is no longer required over
-+`setuptools <http://pypi.python.org/pypi/setuptools>`__, as the current
-+packaging is now PEP517-compliant.
- 
- JSON support is needed only for the command-line interface and to run
--the spec tests. We require simplejson for earlier versions of Python
--since Python's `json <http://docs.python.org/library/json.html>`__
--module was added in Python 2.6.
--
--For Python 2.4 we require an earlier version of simplejson since
--simplejson stopped officially supporting Python 2.4 in simplejson
--version 2.1.0. Earlier versions of simplejson can be installed manually,
--as follows:
--
--::
--
--    pip install 'simplejson<2.1.0'
-+the spec tests; PyYAML can still be used (see the Develop section).
- 
--Official support for Python 2.4 will end with Pystache version 0.6.0.
-+Official support for Python 2 will end with Pystache version 0.6.0.
- 
- Install It
- ----------
- 
- ::
- 
--    pip install pystache
-+   pip install -U pystache -f https://github.com/sarnold/pystache/releases/
- 
- And test it--
- 
- ::
- 
--    pystache-test
-+   pystache-test
- 
- To install and test from source (e.g. from GitHub), see the Develop
- section.
-@@ -88,68 +75,68 @@ Use It
- 
- ::
- 
--    >>> import pystache
--    >>> print pystache.render('Hi {{person}}!', {'person': 'Mom'})
--    Hi Mom!
-+   >>> import pystache
-+   >>> print(pystache.render('Hi {{person}}!', {'person': 'Mom'}))
-+   Hi Mom!
- 
- You can also create dedicated view classes to hold your view logic.
- 
--Here's your view class (in .../examples/readme.py):
-+Here's your view class (in ../pystache/tests/examples/readme.py):
- 
- ::
- 
--    class SayHello(object):
--        def to(self):
--            return "Pizza"
-+   class SayHello(object):
-+       def to(self):
-+           return "Pizza"
- 
- Instantiating like so:
- 
- ::
- 
--    >>> from pystache.tests.examples.readme import SayHello
--    >>> hello = SayHello()
-+   >>> from pystache.tests.examples.readme import SayHello
-+   >>> hello = SayHello()
- 
--Then your template, say\_hello.mustache (by default in the same
--directory as your class definition):
-+Then your template, say_hello.mustache (by default in the same directory
-+as your class definition):
- 
- ::
- 
--    Hello, {{to}}!
-+   Hello, {{to}}!
- 
- Pull it together:
- 
- ::
- 
--    >>> renderer = pystache.Renderer()
--    >>> print renderer.render(hello)
--    Hello, Pizza!
-+   >>> renderer = pystache.Renderer()
-+   >>> print(renderer.render(hello))
-+   Hello, Pizza!
- 
- For greater control over rendering (e.g. to specify a custom template
- directory), use the ``Renderer`` class like above. One can pass
- attributes to the Renderer class constructor or set them on a Renderer
- instance. To customize template loading on a per-view basis, subclass
- ``TemplateSpec``. See the docstrings of the
--`Renderer <https://github.com/defunkt/pystache/blob/master/pystache/renderer.py>`__
-+`Renderer <https://github.com/sarnold/pystache/blob/master/pystache/renderer.py>`__
- class and
--`TemplateSpec <https://github.com/defunkt/pystache/blob/master/pystache/template_spec.py>`__
-+`TemplateSpec <https://github.com/sarnold/pystache/blob/master/pystache/template_spec.py>`__
- class for more information.
- 
- You can also pre-parse a template:
- 
- ::
- 
--    >>> parsed = pystache.parse(u"Hey {{#who}}{{.}}!{{/who}}")
--    >>> print parsed
--    [u'Hey ', _SectionNode(key=u'who', index_begin=12, index_end=18, parsed=[_EscapeNode(key=u'.'), u'!'])]
-+   >>> parsed = pystache.parse(u"Hey {{#who}}{{.}}!{{/who}}")
-+   >>> print(parsed)
-+   ['Hey ', _SectionNode(key='who', index_begin=12, index_end=18, parsed=[_EscapeNode(key='.'), '!'])]
- 
- And then:
- 
- ::
- 
--    >>> print renderer.render(parsed, {'who': 'Pops'})
--    Hey Pops!
--    >>> print renderer.render(parsed, {'who': 'you'})
--    Hey you!
-+   >>> print(renderer.render(parsed, {'who': 'Pops'}))
-+   Hey Pops!
-+   >>> print(renderer.render(parsed, {'who': 'you'}))
-+   Hey you!
- 
- Python 3
- --------
-@@ -211,22 +198,24 @@ To test from a source distribution (without installing)--
- 
- ::
- 
--    python test_pystache.py
-+   python test_pystache.py
- 
- To test Pystache with multiple versions of Python (with a single
--command!), you can use `tox <http://pypi.python.org/pypi/tox>`__:
-+command!) and different platforms, you can use
-+`tox <http://pypi.python.org/pypi/tox>`__:
- 
- ::
- 
--    pip install 'virtualenv<1.8'  # Version 1.8 dropped support for Python 2.4.
--    pip install 'tox<1.4'  # Version 1.4 dropped support for Python 2.4.
--    tox
-+   pip install tox
-+   tox -e setup
- 
--If you do not have all Python versions listed in ``tox.ini``--
-+To run tests on multiple versions with coverage, run:
- 
- ::
- 
--    tox -e py26,py32  # for example
-+   tox -e py38-linux,py39-linux  # for example
-+
-+(substitute your platform above, eg, macos or windows)
- 
- The source distribution tests also include doctests and tests from the
- Mustache spec. To include tests from the Mustache spec in your test
-@@ -234,8 +223,8 @@ runs:
- 
- ::
- 
--    git submodule init
--    git submodule update
-+   git submodule init
-+   git submodule update
- 
- The test harness parses the spec's (more human-readable) yaml files if
- `PyYAML <http://pypi.python.org/pypi/PyYAML>`__ is present. Otherwise,
-@@ -243,94 +232,113 @@ it parses the json files. To install PyYAML--
- 
- ::
- 
--    pip install pyyaml
-+   pip install pyyaml
-+
-+Once the submodule is available, you can run the full test set with:
-+
-+::
-+
-+   tox -e setup . ext/spec/specs
- 
- To run a subset of the tests, you can use
- `nose <http://somethingaboutorange.com/mrl/projects/nose/0.11.1/testing.html>`__:
- 
- ::
- 
--    pip install nose
--    nosetests --tests pystache/tests/test_context.py:GetValueTests.test_dictionary__key_present
-+   pip install nose
-+   nosetests --tests pystache/tests/test_context.py:GetValueTests.test_dictionary__key_present
- 
--Using Python 3 with Pystache from source
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-+Mailing List (old)
-+------------------
- 
--Pystache is written in Python 2 and must be converted to Python 3 prior
--to using it with Python 3. The installation process (and tox) do this
--automatically.
-+There is(was) a `mailing
-+list <http://librelist.com/browser/pystache/>`__. Note that there is a
-+bit of a delay between posting a message and seeing it appear in the
-+mailing list archive.
- 
--To convert the code to Python 3 manually (while using Python 3)--
-+Credits
-+-------
- 
- ::
- 
--    python setup.py build
-+   >>> import pystache
-+   >>> context = { 'author': 'Chris Wanstrath', 'maintainer': 'Chris Jerdonek','refurbisher': 'Steve Arnold' }
-+   >>> print(pystache.render("Author: {{author}}\nMaintainer: {{maintainer}}\nRefurbisher: {{refurbisher}}", context))
-+   Author: Chris Wanstrath
-+   Maintainer: Chris Jerdonek
-+   Refurbisher: Steve Arnold
- 
--This writes the converted code to a subdirectory called ``build``. By
--design, Python 3 builds
--`cannot <https://bitbucket.org/tarek/distribute/issue/292/allow-use_2to3-with-python-2>`__
--be created from Python 2.
-+Pystache logo by `David Phillips <http://davidphillips.us/>`__ is
-+licensed under a `Creative Commons Attribution-ShareAlike 3.0 Unported
-+License <http://creativecommons.org/licenses/by-sa/3.0/deed.en_US>`__.
-+|image10|
- 
--To convert the code without using setup.py, you can use
--`2to3 <http://docs.python.org/library/2to3.html>`__ as follows (two
--steps)--
-+History
-+=======
- 
--::
-+**Note:** Official support for Python 2.7 will end with Pystache version
-+0.6.0.
- 
--    2to3 --write --nobackups --no-diffs --doctests_only pystache
--    2to3 --write --nobackups --no-diffs pystache
-+0.6.0 (2021-03-04)
-+------------------
- 
--This converts the code (and doctests) in place.
-+-  Bump spec versions to latest => v1.1.3
-+-  Modernize python and CI tools, update docs/doctests
-+-  Update unicode conversion test for py3-only
-+-  Add pep8speaks cfg, cleanup warnings
-+-  Remove superfluous setup test/unused imports
-+-  Add conda recipe/CI build
- 
--To ``import pystache`` from a source distribution while using Python 3,
--be sure that you are importing from a directory containing a converted
--version of the code (e.g. from the ``build`` directory after
--converting), and not from the original (unconverted) source directory.
--Otherwise, you will get a syntax error. You can help prevent this by not
--running the Python IDE from the project directory when importing
--Pystache while using Python 3.
-+.. _section-1:
- 
--Mailing List
--------------
-+0.5.6 (2021-02-28)
-+------------------
- 
--There is a `mailing list <http://librelist.com/browser/pystache/>`__.
--Note that there is a bit of a delay between posting a message and seeing
--it appear in the mailing list archive.
-+-  Use correct wheel name in release workflow, limit wheels
-+-  Add install check/test of downloaded wheel
-+-  Update/add ci workflows and tox cfg, bump to next dev0 version
- 
--Credits
---------
-+.. _section-2:
- 
--::
-+0.5.5 (2020-12-16)
-+------------------
- 
--    >>> context = { 'author': 'Chris Wanstrath', 'maintainer': 'Chris Jerdonek' }
--    >>> print pystache.render("Author: {{author}}\nMaintainer: {{maintainer}}", context)
--    Author: Chris Wanstrath
--    Maintainer: Chris Jerdonek
-+-  fix document processing, update pandoc args and history
-+-  add release.yml to CI, test env settings
-+-  fix bogus commit message, update versions and tox cf
-+-  add post-test steps for building pkgs with/without doc updates
-+-  add CI build check, fix MANIFEST.in pruning
- 
--Pystache logo by `David Phillips <http://davidphillips.us/>`__ is
--licensed under a `Creative Commons Attribution-ShareAlike 3.0 Unported
--License <http://creativecommons.org/licenses/by-sa/3.0/deed.en_US>`__.
--|image0|
-+.. _section-3:
- 
--History
--=======
-+0.5.4-2 (2020-11-09)
-+--------------------
- 
--**Note:** Official support for Python 2.4 will end with Pystache version
--0.6.0.
-+-  Merge pull request #1 from sarnold/rebase-up
-+-  Bugfix: test_specloader.py: fix test_find__with_directory on other
-+   OSs
-+-  Bugfix: pystache/loader.py: remove stray windows line-endings
-+-  fix crufty (and insecure) http urls
-+-  Bugfix: modernize python versions (keep py27) and fix spec_test load
-+   cmd
-+
-+.. _section-4:
- 
- 0.5.4 (2014-07-11)
- ------------------
- 
- -  Bugfix: made test with filenames OS agnostic (issue #162).
- 
-+.. _section-5:
-+
- 0.5.3 (2012-11-03)
- ------------------
- 
- -  Added ability to customize string coercion (e.g. to have None render
-    as ``''``) (issue #130).
---  Added Renderer.render\_name() to render a template by name (issue
-+-  Added Renderer.render_name() to render a template by name (issue
-    #122).
---  Added TemplateSpec.template\_path to specify an absolute path to a
-+-  Added TemplateSpec.template_path to specify an absolute path to a
-    template (issue #41).
- -  Added option of raising errors on missing tags/partials:
-    ``Renderer(missing_tags='strict')`` (issue #110).
-@@ -355,6 +363,8 @@ History
- -  More robust handling of byte strings in Python 3.
- -  Added Creative Commons license for David Phillips's logo.
- 
-+.. _section-6:
-+
- 0.5.2 (2012-05-03)
- ------------------
- 
-@@ -367,16 +377,20 @@ History
-    context stack (issue #113).
- -  Bugfix: lists of lambdas for sections were not rendered (issue #114).
- 
-+.. _section-7:
-+
- 0.5.1 (2012-04-24)
- ------------------
- 
- -  Added support for Python 3.1 and 3.2.
- -  Added tox support to test multiple Python versions.
- -  Added test script entry point: pystache-test.
---  Added \_\_version\_\_ package attribute.
-+-  Added \__version_\_ package attribute.
- -  Test harness now supports both YAML and JSON forms of Mustache spec.
- -  Test harness no longer requires nose.
- 
-+.. _section-8:
-+
- 0.5.0 (2012-04-03)
- ------------------
- 
-@@ -435,11 +449,15 @@ Bug fixes:
- -  Passing ``**kwargs`` to ``Template()`` with no context no longer
-    raises an exception.
- 
-+.. _section-9:
-+
- 0.4.1 (2012-03-25)
- ------------------
- 
- -  Added support for Python 2.4. [wangtz, jvantuyl]
- 
-+.. _section-10:
-+
- 0.4.0 (2011-01-12)
- ------------------
- 
-@@ -447,19 +465,25 @@ Bug fixes:
- -  Add support for inverted lists
- -  Decoupled template loading
- 
-+.. _section-11:
-+
- 0.3.1 (2010-05-07)
- ------------------
- 
- -  Fix package
- 
-+.. _section-12:
-+
- 0.3.0 (2010-05-03)
- ------------------
- 
---  View.template\_path can now hold a list of path
-+-  View.template_path can now hold a list of path
- -  Add {{& blah}} as an alias for {{{ blah }}}
- -  Higher Order Sections
- -  Inverted sections
- 
-+.. _section-13:
-+
- 0.2.0 (2010-02-15)
- ------------------
- 
-@@ -473,12 +497,16 @@ Bug fixes:
-    [enaeseth]
- -  Template file encoding awareness. [enaeseth]
- 
-+.. _section-14:
-+
- 0.1.1 (2009-11-13)
- ------------------
- 
- -  Ensure we're dealing with strings, always
- -  Tests can be run by executing the test file directly
- 
-+.. _section-15:
-+
- 0.1.0 (2009-11-12)
- ------------------
- 
-@@ -510,4 +538,23 @@ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- 
--.. |image0| image:: http://i.creativecommons.org/l/by-sa/3.0/88x31.png
-+.. |ci| image:: https://github.com/sarnold/pystache/actions/workflows/ci.yml/badge.svg
-+   :target: https://github.com/sarnold/pystache/actions/workflows/ci.yml
-+.. |Conda| image:: https://github.com/sarnold/pystache/actions/workflows/conda.yml/badge.svg
-+   :target: https://github.com/sarnold/pystache/actions/workflows/conda.yml
-+.. |Wheels| image:: https://github.com/sarnold/pystache/actions/workflows/wheels.yml/badge.svg
-+   :target: https://github.com/sarnold/pystache/actions/workflows/wheels.yml
-+.. |Release| image:: https://github.com/sarnold/pystache/actions/workflows/release.yml/badge.svg
-+   :target: https://github.com/sarnold/pystache/actions/workflows/release.yml
-+.. |Python| image:: https://img.shields.io/badge/python-3.6+-blue.svg
-+   :target: https://www.python.org/downloads/
-+.. |Latest release| image:: https://img.shields.io/github/v/release/sarnold/pystache?include_prereleases
-+   :target: https://github.com/sarnold/pystache/releases/latest
-+.. |License| image:: https://img.shields.io/github/license/sarnold/pystache
-+   :target: https://github.com/sarnold/pystache/blob/master/LICENSE
-+.. |Maintainability| image:: https://api.codeclimate.com/v1/badges/a8fa1bf4638bfc6581b6/maintainability
-+   :target: https://codeclimate.com/github/sarnold/pystache/maintainability
-+.. |codecov| image:: https://codecov.io/gh/sarnold/pystache/branch/master/graph/badge.svg?token=5PZNMZBI6K
-+   :target: https://codecov.io/gh/sarnold/pystache
-+.. |image9| image:: gh/images/logo_phillips_small.png
-+.. |image10| image:: http://i.creativecommons.org/l/by-sa/3.0/88x31.png
-diff --git a/tox.ini b/tox.ini
-index d1eaebf..66c4515 100644
---- a/tox.ini
-+++ b/tox.ini
-@@ -1,36 +1,110 @@
--# A tox configuration file to test across multiple Python versions.
--#
--#   http://pypi.python.org/pypi/tox
--#
- [tox]
--# Tox 1.4 drops py24 and adds py33.  In the current version, we want to
--# support 2.4, so we can't simultaneously support 3.3.
--envlist = py24,py25,py26,py27,py27-yaml,py27-noargs,py31,py32,pypy
-+envlist = py{36,37,38,39}-{linux,macos,windows}
-+skip_missing_interpreters = true
-+isolated_build = true
-+#skipsdist = true
-+
-+[gh-actions]
-+python =
-+    3.6: py36
-+    3.7: py37
-+    3.8: py38
-+    3.9: py39
-+
-+[gh-actions:env]
-+PLATFORM =
-+    ubuntu-18.04: linux
-+    macos-latest: macos
-+    windows-latest: windows
- 
- [testenv]
-+passenv = CI PYTHON PYTHONIOENCODING
-+
-+deps =
-+    pip>=20.0.1
-+    nose
-+    coverage
-+
-+commands =
-+    nosetests -sx . {posargs}
-+
-+[testenv:bare]
- # Change the working directory so that we don't import the pystache located
- # in the original location.
-+deps =
-+    pip>=20.0.1
-+    -e .
-+
- changedir =
-     {envbindir}
-+
- commands =
--    pystache-test {toxinidir}
-+    pystache-test
-+
-+[testenv:bench]
-+passenv = CI PYTHON PYTHONIOENCODING
- 
--# Check that the spec tests work with PyYAML.
--[testenv:py27-yaml]
--basepython =
--    python2.7
- deps =
--    PyYAML
--changedir =
--    {envbindir}
-+    pip>=20.0.1
-+    # uncomment for comparison, posargs expects a number, eg, 10000
-+    #chevron
-+
-+commands_pre =
-+    pip install .
-+
- commands =
--    pystache-test {toxinidir}
-+    python pystache/tests/benchmark.py {posargs}
-+
-+[testenv:setup]
-+passenv = CI PYTHON PYTHONIOENCODING
-+
-+deps =
-+    pyyaml
-+    twine
-+
-+commands =
-+    python setup.py install
-+    twine check dist/*
-+    pystache-test {posargs}
-+
-+[testenv:deploy]
-+passenv = CI PYTHON PYTHONIOENCODING
-+allowlist_externals = bash
-+
-+deps =
-+    pip>=19.0.1
-+    wheel
-+    pep517
-+    twine
-+
-+commands =
-+    python -m pep517.build .
-+    twine check dist/*
-+
-+[testenv:check]
-+passenv = CI PYTHON PYTHONIOENCODING
-+skip_install = true
-+
-+allowlist_externals = bash
-+
-+deps =
-+    pip>=20.0.1
- 
--# Check that pystache-test works from an install with no arguments.
--[testenv:py27-noargs]
--basepython =
--    python2.7
--changedir =
--    {envbindir}
- commands =
-+    bash -c 'export WHL_FILE=$(ls dist/*.whl); \
-+        python -m pip install $WHL_FILE'
-     pystache-test
-+
-+[testenv:docs]
-+passenv = CI PYTHON PYTHONIOENCODING
-+allowlist_externals = bash
-+
-+deps =
-+    pip>=19.0.1
-+    wheel
-+    docutils
-+    # apt/emerge pandoc first
-+
-+commands =
-+    python setup.py prep
-+    bash -c 'python setup.py --long-description | rst2html.py -v --no-raw > out.html'
-diff --git a/travis.yml_disabled b/travis.yml_disabled
-new file mode 100644
-index 0000000..f0b4042
---- /dev/null
-+++ b/travis.yml_disabled
-@@ -0,0 +1,52 @@
-+dist: xenial
-+language: python
-+
-+# Travis CI has no plans to support Jython and no longer supports Python 2.5.
-+python:
-+  - "2.7"
-+  - "3.5"
-+  - "3.6"
-+  - "3.7"
-+  - "3.8"
-+  - "3.9-dev"
-+  - "nightly"
-+
-+matrix:
-+  fast_finish: true
-+  include:
-+    - os: osx
-+      # osx is goofy, ``python`` is always py2, images mutate fast
-+      language: shell
-+      before_install:
-+        - pip3 install --upgrade pip wheel
-+      install:
-+        - python3 setup.py install
-+      script:
-+        - pystache-test . ext/spec/specs
-+    - os: windows
-+      # windows is even goofier, install path is different for python/python3
-+      # but either way you get python3 and the cmd is always ``python``  o.O 
-+      # (also versions mutuate like bacteria)
-+      language: shell
-+      before_install:
-+        - choco install python3 --params "/InstallDir:C:\\Python"
-+        - python -m pip install --upgrade pip wheel
-+      env: PATH="/c/Python:/c/Python/Scripts:$PATH"
-+      install:
-+        - python setup.py install
-+      script:
-+        - pystache-test . ext/spec/specs
-+  allow_failures:
-+    - python: "nightly"
-+
-+# command to install dependencies
-+install:
-+  - pip install --upgrade pip
-+  - pip install codecov
-+
-+script:
-+  - python setup.py install
-+  # Include the spec tests directory for Mustache spec tests and the
-+  # project directory for doctests.
-+  - pystache-test . ext/spec/specs
-+  #- tox
--- 
-2.33.0
-
diff --git a/meta-python/recipes-devtools/python/python3-pystache_0.5.4.bb b/meta-python/recipes-devtools/python/python3-pystache_0.5.4.bb
deleted file mode 100644
index 0ae5a19115..0000000000
--- a/meta-python/recipes-devtools/python/python3-pystache_0.5.4.bb
+++ /dev/null
@@ -1,17 +0,0 @@ 
-SUMMARY = "Python implementation of Mustache"
-HOMEPAGE = "https://github.com/defunkt/pystache"
-LICENSE = "MIT"
-LIC_FILES_CHKSUM = "file://LICENSE;md5=eb4417802c56384aac71b34505528a60"
-
-SRC_URI += "file://0001-Modernize-python-versions-remove-py2x-and-fix-tests-.patch"
-
-SRC_URI[md5sum] = "485885e67a0f6411d5252e69b20a35ca"
-SRC_URI[sha256sum] = "f7bbc265fb957b4d6c7c042b336563179444ab313fb93a719759111eabd3b85a"
-
-inherit pypi setuptools3
-
-RDEPENDS:${PN} += " \
-    ${PYTHON_PN}-netserver \
-"
-
-BBCLASSEXTEND = "native nativesdk"