blob: d268cc5cd9237d937bd6cd7884acf9e0b084ffd1 [file] [log] [blame]
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