summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Howitz <mh@gocept.com>2021-09-06 08:11:11 +0200
committerGitHub <noreply@github.com>2021-09-06 08:11:11 +0200
commit53141afa75f6cdf0c75e2496d697313820a03039 (patch)
tree9084f1a2994c8c9a6e592a433c4d6f66b2de5b51
parent59d212f62d20f9bad168a0dcb8bb137bd625130b (diff)
parent51480fc597fa04b2a044b873605cb114efacf9f6 (diff)
downloadzope-i18n-53141afa75f6cdf0c75e2496d697313820a03039.tar.gz
Merge pull request #49 from zopefoundation/config-with-pure-python-autopep8
Config with pure python
-rw-r--r--.coveragerc19
-rw-r--r--.editorconfig39
-rw-r--r--.github/workflows/tests.yml63
-rw-r--r--.gitignore41
-rw-r--r--.meta.toml44
-rw-r--r--.travis.yml21
-rw-r--r--CHANGES.rst3
-rw-r--r--MANIFEST.in29
-rw-r--r--bootstrap.py210
-rw-r--r--docs/conf.py15
-rw-r--r--setup.cfg21
-rw-r--r--setup.py9
-rw-r--r--src/zope/i18n/__init__.py7
-rw-r--r--src/zope/i18n/_compat.py8
-rw-r--r--src/zope/i18n/compile.py16
-rw-r--r--src/zope/i18n/config.py3
-rw-r--r--src/zope/i18n/format.py89
-rw-r--r--src/zope/i18n/interfaces/__init__.py9
-rw-r--r--src/zope/i18n/interfaces/locales.py16
-rw-r--r--src/zope/i18n/locales/__init__.py27
-rw-r--r--src/zope/i18n/locales/fallbackcollator.py1
-rw-r--r--src/zope/i18n/locales/inheritance.py9
-rw-r--r--src/zope/i18n/locales/provider.py2
-rw-r--r--src/zope/i18n/locales/tests/test_docstrings.py6
-rw-r--r--src/zope/i18n/locales/tests/test_fallbackcollator.py10
-rw-r--r--src/zope/i18n/locales/tests/test_locales.py2
-rw-r--r--src/zope/i18n/locales/tests/test_xmlfactory.py29
-rw-r--r--src/zope/i18n/locales/xmlfactory.py60
-rw-r--r--src/zope/i18n/negotiator.py4
-rw-r--r--src/zope/i18n/simpletranslationdomain.py4
-rw-r--r--src/zope/i18n/testmessagecatalog.py7
-rw-r--r--src/zope/i18n/tests/test.py5
-rw-r--r--src/zope/i18n/tests/test_formats.py62
-rw-r--r--src/zope/i18n/tests/test_gettextmessagecatalog.py1
-rw-r--r--src/zope/i18n/tests/test_imessagecatalog.py4
-rw-r--r--src/zope/i18n/tests/test_itranslationdomain.py6
-rw-r--r--src/zope/i18n/tests/test_negotiator.py18
-rw-r--r--src/zope/i18n/tests/test_plurals.py6
-rw-r--r--src/zope/i18n/tests/test_simpletranslationdomain.py4
-rw-r--r--src/zope/i18n/tests/test_testmessagecatalog.py4
-rw-r--r--src/zope/i18n/tests/test_translationdomain.py2
-rw-r--r--src/zope/i18n/tests/test_zcml.py6
-rw-r--r--src/zope/i18n/tests/testi18nawareobject.py4
-rw-r--r--src/zope/i18n/translationdomain.py5
-rw-r--r--src/zope/i18n/zcml.py4
-rw-r--r--tox.ini90
46 files changed, 524 insertions, 520 deletions
diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index b5ae109..0000000
--- a/.coveragerc
+++ /dev/null
@@ -1,19 +0,0 @@
-[run]
-source = zope.i18n
-omit =
- */flycheck_*py
-
-[paths]
-source =
- src/
- .tox/*/lib/python*/site-packages/
- .tox/pypy*/site-packages/
-
-[report]
-precision = 2
-exclude_lines =
- pragma: no cover
- if __name__ == '__main__':
- raise NotImplementedError
- self.fail
- raise AssertionError
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..c5508b9
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,39 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/pure-python
+#
+# EditorConfig Configuration file, for more details see:
+# http://EditorConfig.org
+# EditorConfig is a convention description, that could be interpreted
+# by multiple editors to enforce common coding conventions for specific
+# file types
+
+# top-most EditorConfig file:
+# Will ignore other EditorConfig files in Home directory or upper tree level.
+root = true
+
+
+[*] # For All Files
+# Unix-style newlines with a newline ending every file
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+# Set default charset
+charset = utf-8
+# Indent style default
+indent_style = space
+# Max Line Length - a hard line wrap, should be disabled
+max_line_length = off
+
+[*.{py,cfg,ini}]
+# 4 space indentation
+indent_size = 4
+
+[*.{yml,zpt,pt,dtml,zcml}]
+# 2 space indentation
+indent_size = 2
+
+[{Makefile,.gitmodules}]
+# Tab indentation (no size specified, but view as 4 spaces)
+indent_style = tab
+indent_size = unset
+tab_width = unset
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..9ea114e
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,63 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/pure-python
+name: tests
+
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '0 12 * * 0' # run once a week on Sunday
+ # Allow to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ build:
+ strategy:
+ # We want to see all failures:
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu
+ config:
+ # [Python version, tox env]
+ - ["3.8", "lint"]
+ - ["2.7", "py27"]
+ - ["3.5", "py35"]
+ - ["3.6", "py36"]
+ - ["3.7", "py37"]
+ - ["3.8", "py38"]
+ - ["3.9", "py39"]
+ - ["pypy2", "pypy"]
+ - ["pypy3", "pypy3"]
+ - ["3.8", "docs"]
+ - ["3.8", "coverage"]
+
+ runs-on: ${{ matrix.os }}-latest
+ name: ${{ matrix.config[1] }}
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.config[0] }}
+ - name: Pip cache
+ uses: actions/cache@v2
+ with:
+ path: ~/.cache/pip
+ key: ${{ runner.os }}-pip-${{ matrix.config[0] }}-${{ hashFiles('setup.*', 'tox.ini') }}
+ restore-keys: |
+ ${{ runner.os }}-pip-${{ matrix.config[0] }}-
+ ${{ runner.os }}-pip-
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install tox
+ - name: Test
+ run: tox -e ${{ matrix.config[1] }}
+ - name: Coverage
+ if: matrix.config[1] == 'coverage'
+ run: |
+ pip install coveralls coverage-python-version
+ coveralls --service=github
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index fec8ebe..c724a76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,18 +1,31 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/pure-python
+*.dll
+*.egg-info/
+*.profraw
*.pyc
+*.pyo
*.so
-*.dll
-*.mo
-__pycache__
-src/*.egg-info
-
-.installed.cfg
-.tox
-bin
-build
-develop-eggs
-parts
-docs/_build/
.coverage
+.coverage.*
+.eggs/
+.installed.cfg
+.mr.developer.cfg
+.tox/
+.vscode/
+__pycache__/
+bin/
+build/
coverage.xml
-nosetests.xml
-htmlcov/
+develop-eggs/
+develop/
+dist/
+docs/_build
+eggs/
+etc/
+lib/
+lib64
+log/
+parts/
+pyvenv.cfg
+var/
diff --git a/.meta.toml b/.meta.toml
new file mode 100644
index 0000000..877be7c
--- /dev/null
+++ b/.meta.toml
@@ -0,0 +1,44 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/pure-python
+[meta]
+template = "pure-python"
+commit-id = "7f5b73fe9d2bcbabafaef5f69dd97408a142d42a"
+
+[python]
+with-windows = false
+with-pypy = true
+with-future-python = false
+with-legacy-python = true
+with-docs = true
+with-sphinx-doctests = true
+
+[tox]
+use-flake8 = true
+
+[coverage]
+fail-under = 99
+
+[manifest]
+additional-rules = [
+ "recursive-include docs *.bat",
+ "recursive-include src *.dtd",
+ "recursive-include src *.html",
+ "recursive-include src *.in",
+ "recursive-include src *.po",
+ "recursive-include src *.rst",
+ "recursive-include src *.txt",
+ "recursive-include src *.xml",
+ "recursive-include src *.zcml",
+ ]
+
+[check-manifest]
+ignore-bad-ideas = [
+ "src/zope/i18n/tests/de-default.mo",
+ "src/zope/i18n/tests/en-alt.mo",
+ "src/zope/i18n/tests/en-default.mo",
+ "src/zope/i18n/tests/locale/de/LC_MESSAGES/zope-i18n.mo",
+ "src/zope/i18n/tests/locale/en/LC_MESSAGES/zope-i18n.mo",
+ "src/zope/i18n/tests/locale2/en/LC_MESSAGES/zope-i18n.mo",
+ "src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mo",
+ "src/zope/i18n/tests/pl-default.mo",
+ ]
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 7dccb10..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-language: python
-dist: xenial
-python:
- - 2.7
- - 3.5
- - 3.6
- - 3.7
- - pypy
- - pypy3
-install:
- - pip install -U pip setuptools
- - pip install -U coverage coveralls
- - pip install -U -e .[test,docs]
-script:
- - coverage run -m zope.testrunner --test-path=src
- - coverage run -a -m sphinx -b doctest -d docs/_build/doctrees docs docs/_build/doctest
-after_success:
- - coveralls
-notifications:
- email: false
-cache: pip
diff --git a/CHANGES.rst b/CHANGES.rst
index 013bb05..c554ca2 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,7 +5,8 @@
4.7.1 (unreleased)
==================
-- Nothing changed yet.
+- Support and test Python 3.8 and 3.9.
+ Full supported list is now: 2.7, 3.5, 3.6, 3.7, 3.8, 3.9, PyPy, PyPy3.
4.7.0 (2019-07-10)
diff --git a/MANIFEST.in b/MANIFEST.in
index e4564d6..b2b9c96 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,25 +1,22 @@
-include *.txt
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/pure-python
include *.rst
-include *.py
-include *.ini
+include *.txt
include buildout.cfg
-include .travis.yml
include tox.ini
-include .coveragerc
-recursive-include src *.txt *.py *.dtd *.xml *.html *.po *.mo *.in *.zcml
+recursive-include docs *.py
+recursive-include docs *.rst
+recursive-include docs *.txt
+recursive-include docs Makefile
+
recursive-include src *.py
+recursive-include docs *.bat
recursive-include src *.dtd
-recursive-include src *.xml
recursive-include src *.html
-recursive-include src *.po
-recursive-include src *.mo
recursive-include src *.in
-recursive-include src *.zcml
+recursive-include src *.po
recursive-include src *.rst
-
-recursive-include src/zope/i18n/tests *.mo
-recursive-include docs *.rst
-recursive-include docs *.py
-recursive-include docs *.bat
-recursive-include docs Makefile
+recursive-include src *.txt
+recursive-include src *.xml
+recursive-include src *.zcml
diff --git a/bootstrap.py b/bootstrap.py
deleted file mode 100644
index a459921..0000000
--- a/bootstrap.py
+++ /dev/null
@@ -1,210 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2006 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Bootstrap a buildout-based project
-
-Simply run this script in a directory containing a buildout.cfg.
-The script accepts buildout command-line options, so you can
-use the -c option to specify an alternate configuration file.
-"""
-
-import os
-import shutil
-import sys
-import tempfile
-
-from optparse import OptionParser
-
-__version__ = '2015-07-01'
-# See zc.buildout's changelog if this version is up to date.
-
-tmpeggs = tempfile.mkdtemp(prefix='bootstrap-')
-
-usage = '''\
-[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
-
-Bootstraps a buildout-based project.
-
-Simply run this script in a directory containing a buildout.cfg, using the
-Python that you want bin/buildout to use.
-
-Note that by using --find-links to point to local resources, you can keep
-this script from going over the network.
-'''
-
-parser = OptionParser(usage=usage)
-parser.add_option("--version",
- action="store_true", default=False,
- help=("Return bootstrap.py version."))
-parser.add_option("-t", "--accept-buildout-test-releases",
- dest='accept_buildout_test_releases',
- action="store_true", default=False,
- help=("Normally, if you do not specify a --version, the "
- "bootstrap script and buildout gets the newest "
- "*final* versions of zc.buildout and its recipes and "
- "extensions for you. If you use this flag, "
- "bootstrap and buildout will get the newest releases "
- "even if they are alphas or betas."))
-parser.add_option("-c", "--config-file",
- help=("Specify the path to the buildout configuration "
- "file to be used."))
-parser.add_option("-f", "--find-links",
- help=("Specify a URL to search for buildout releases"))
-parser.add_option("--allow-site-packages",
- action="store_true", default=False,
- help=("Let bootstrap.py use existing site packages"))
-parser.add_option("--buildout-version",
- help="Use a specific zc.buildout version")
-parser.add_option("--setuptools-version",
- help="Use a specific setuptools version")
-parser.add_option("--setuptools-to-dir",
- help=("Allow for re-use of existing directory of "
- "setuptools versions"))
-
-options, args = parser.parse_args()
-if options.version:
- print("bootstrap.py version %s" % __version__)
- sys.exit(0)
-
-
-######################################################################
-# load/install setuptools
-
-try:
- from urllib.request import urlopen
-except ImportError:
- from urllib2 import urlopen
-
-ez = {}
-if os.path.exists('ez_setup.py'):
- exec(open('ez_setup.py').read(), ez)
-else:
- exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez)
-
-if not options.allow_site_packages:
- # ez_setup imports site, which adds site packages
- # this will remove them from the path to ensure that incompatible versions
- # of setuptools are not in the path
- import site
- # inside a virtualenv, there is no 'getsitepackages'.
- # We can't remove these reliably
- if hasattr(site, 'getsitepackages'):
- for sitepackage_path in site.getsitepackages():
- # Strip all site-packages directories from sys.path that
- # are not sys.prefix; this is because on Windows
- # sys.prefix is a site-package directory.
- if sitepackage_path != sys.prefix:
- sys.path[:] = [x for x in sys.path
- if sitepackage_path not in x]
-
-setup_args = dict(to_dir=tmpeggs, download_delay=0)
-
-if options.setuptools_version is not None:
- setup_args['version'] = options.setuptools_version
-if options.setuptools_to_dir is not None:
- setup_args['to_dir'] = options.setuptools_to_dir
-
-ez['use_setuptools'](**setup_args)
-import setuptools
-import pkg_resources
-
-# This does not (always?) update the default working set. We will
-# do it.
-for path in sys.path:
- if path not in pkg_resources.working_set.entries:
- pkg_resources.working_set.add_entry(path)
-
-######################################################################
-# Install buildout
-
-ws = pkg_resources.working_set
-
-setuptools_path = ws.find(
- pkg_resources.Requirement.parse('setuptools')).location
-
-# Fix sys.path here as easy_install.pth added before PYTHONPATH
-cmd = [sys.executable, '-c',
- 'import sys; sys.path[0:0] = [%r]; ' % setuptools_path +
- 'from setuptools.command.easy_install import main; main()',
- '-mZqNxd', tmpeggs]
-
-find_links = os.environ.get(
- 'bootstrap-testing-find-links',
- options.find_links or
- ('http://downloads.buildout.org/'
- if options.accept_buildout_test_releases else None)
- )
-if find_links:
- cmd.extend(['-f', find_links])
-
-requirement = 'zc.buildout'
-version = options.buildout_version
-if version is None and not options.accept_buildout_test_releases:
- # Figure out the most recent final version of zc.buildout.
- import setuptools.package_index
- _final_parts = '*final-', '*final'
-
- def _final_version(parsed_version):
- try:
- return not parsed_version.is_prerelease
- except AttributeError:
- # Older setuptools
- for part in parsed_version:
- if (part[:1] == '*') and (part not in _final_parts):
- return False
- return True
-
- index = setuptools.package_index.PackageIndex(
- search_path=[setuptools_path])
- if find_links:
- index.add_find_links((find_links,))
- req = pkg_resources.Requirement.parse(requirement)
- if index.obtain(req) is not None:
- best = []
- bestv = None
- for dist in index[req.project_name]:
- distv = dist.parsed_version
- if _final_version(distv):
- if bestv is None or distv > bestv:
- best = [dist]
- bestv = distv
- elif distv == bestv:
- best.append(dist)
- if best:
- best.sort()
- version = best[-1].version
-if version:
- requirement = '=='.join((requirement, version))
-cmd.append(requirement)
-
-import subprocess
-if subprocess.call(cmd) != 0:
- raise Exception(
- "Failed to execute command:\n%s" % repr(cmd)[1:-1])
-
-######################################################################
-# Import and run buildout
-
-ws.add_entry(tmpeggs)
-ws.require(requirement)
-import zc.buildout.buildout
-
-if not [a for a in args if '=' not in a]:
- args.append('bootstrap')
-
-# if -c was provided, we push it back into args for buildout' main function
-if options.config_file is not None:
- args[0:0] = ['-c', options.config_file]
-
-zc.buildout.buildout.main(args)
-shutil.rmtree(tmpeggs)
diff --git a/docs/conf.py b/docs/conf.py
index 0ed66d4..02f419e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,11 +18,11 @@ import pkg_resources
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.append(os.path.abspath('.'))
+# sys.path.append(os.path.abspath('.'))
sys.path.append(os.path.abspath('../src'))
rqmt = pkg_resources.require('zope.i18n')[0]
-# -- General configuration -----------------------------------------------------
+# -- General configuration -----------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
@@ -77,7 +77,8 @@ release = rqmt.version
# for source files.
exclude_trees = ['_build']
-# The reST default role (used for this markup: `text`) to use for all documents.
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
default_role = 'obj'
# If true, '()' will be appended to :func: etc. cross-reference text.
@@ -98,7 +99,7 @@ pygments_style = 'sphinx'
#modindex_common_prefix = []
-# -- Options for HTML output ---------------------------------------------------
+# -- Options for HTML output ---------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
@@ -172,7 +173,7 @@ html_static_path = ['_static']
htmlhelp_basename = 'zopei18ndoc'
-# -- Options for LaTeX output --------------------------------------------------
+# -- Options for LaTeX output --------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
@@ -183,8 +184,8 @@ htmlhelp_basename = 'zopei18ndoc'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
- ('index', 'zopei18n.tex', u'zope.i18n Documentation',
- u'Zope Foundation and Contributors', 'manual'),
+ ('index', 'zopei18n.tex', u'zope.i18n Documentation',
+ u'Zope Foundation and Contributors', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
diff --git a/setup.cfg b/setup.cfg
index 2a9acf1..43050e9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,23 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/pure-python
[bdist_wheel]
universal = 1
+
+[flake8]
+doctests = 1
+
+[check-manifest]
+ignore =
+ .editorconfig
+ .meta.toml
+ docs/_build/html/_sources/*
+ docs/_build/doctest/*
+ignore-bad-ideas =
+ src/zope/i18n/tests/de-default.mo
+ src/zope/i18n/tests/en-alt.mo
+ src/zope/i18n/tests/en-default.mo
+ src/zope/i18n/tests/locale/de/LC_MESSAGES/zope-i18n.mo
+ src/zope/i18n/tests/locale/en/LC_MESSAGES/zope-i18n.mo
+ src/zope/i18n/tests/locale2/en/LC_MESSAGES/zope-i18n.mo
+ src/zope/i18n/tests/locale3/en/LC_MESSAGES/zope-i18n.mo
+ src/zope/i18n/tests/pl-default.mo
diff --git a/setup.py b/setup.py
index dc3b83c..6a7c83e 100644
--- a/setup.py
+++ b/setup.py
@@ -21,10 +21,12 @@
import os
from setuptools import setup, find_packages
+
def read(*rnames):
with open(os.path.join(os.path.dirname(__file__), *rnames)) as f:
return f.read()
+
def alltests():
import sys
import unittest
@@ -39,6 +41,7 @@ def alltests():
suites = list(zope.testrunner.find.find_suites(options))
return unittest.TestSuite(suites)
+
COMPILE_REQUIRES = [
# python-gettext used to be here, but it's now
# a fixed requirement. Keep the extra to avoid
@@ -83,6 +86,8 @@ setup(
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
+ 'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Natural Language :: English',
@@ -93,7 +98,7 @@ setup(
url='https://github.com/zopefoundation/zope.i18n',
packages=find_packages('src'),
package_dir={'': 'src'},
- namespace_packages=['zope',],
+ namespace_packages=['zope', ],
install_requires=[
'setuptools',
'python-gettext',
@@ -116,4 +121,4 @@ setup(
test_suite='__main__.alltests',
include_package_data=True,
zip_safe=False,
- )
+)
diff --git a/src/zope/i18n/__init__.py b/src/zope/i18n/__init__.py
index dd525ea..b737d00 100644
--- a/src/zope/i18n/__init__.py
+++ b/src/zope/i18n/__init__.py
@@ -16,14 +16,17 @@
import re
from zope.component import queryUtility
-from zope.i18nmessageid import MessageFactory, Message
+from zope.i18nmessageid import Message
+# MessageFactory is not used, but it might be here for BBB reasons,
+# as it could be imported by other packages.
+from zope.i18nmessageid import MessageFactory # noqa
+from zope.i18n._compat import text_type
from zope.i18n.config import ALLOWED_LANGUAGES
from zope.i18n.interfaces import INegotiator
from zope.i18n.interfaces import ITranslationDomain
from zope.i18n.interfaces import IFallbackTranslationDomainFactory
-text_type = str if bytes is not str else unicode
# Set up regular expressions for finding interpolation variables in text.
# NAME_RE must exactly match the expression of the same name in the
diff --git a/src/zope/i18n/_compat.py b/src/zope/i18n/_compat.py
new file mode 100644
index 0000000..f6bf93b
--- /dev/null
+++ b/src/zope/i18n/_compat.py
@@ -0,0 +1,8 @@
+# This gives a linting error because unicode is not defined on Python 3:
+# text_type = str if bytes is not str else unicode
+try:
+ # Python 2
+ text_type = unicode
+except NameError:
+ # Python 3
+ text_type = str
diff --git a/src/zope/i18n/compile.py b/src/zope/i18n/compile.py
index 834bf24..81a6a17 100644
--- a/src/zope/i18n/compile.py
+++ b/src/zope/i18n/compile.py
@@ -11,12 +11,14 @@ logger = logging.getLogger('zope.i18n')
HAS_PYTHON_GETTEXT = True
+
def _safe_mtime(path):
try:
return os.path.getmtime(path)
except (IOError, OSError):
return None
+
def compile_mo_file(domain, lc_messages_path):
"""Creates or updates a mo file in the locales folder."""
@@ -34,16 +36,18 @@ def compile_mo_file(domain, lc_messages_path):
if po_mtime > mo_mtime:
try:
- # Msgfmt.getAsFile returns io.BytesIO on Python 3, and cStringIO.StringIO
- # on Python 2; sadly StringIO isn't a proper context manager, so we have to
- # wrap it with `closing`. Also, Msgfmt doesn't properly close a file
- # it opens for reading if you pass the path, but it does if you pass
- # the file.
+ # Msgfmt.getAsFile returns io.BytesIO on Python 3,
+ # and cStringIO.StringIO on Python 2;
+ # sadly StringIO isn't a proper context manager, so we have to
+ # wrap it with `closing`. Also, Msgfmt doesn't properly close a
+ # file it opens for reading if you pass the path,
+ # but it does if you pass the file.
with open(pofile, 'rb') as pofd:
with closing(Msgfmt(pofd, domain).getAsFile()) as mo:
with open(mofile, 'wb') as fd:
fd.write(mo.read())
except PoSyntaxError as err:
- logger.warning('Syntax error while compiling %s (%s).', pofile, err.msg)
+ logger.warning(
+ 'Syntax error while compiling %s (%s).', pofile, err.msg)
except (IOError, OSError) as err:
logger.warning('Error while compiling %s (%s).', pofile, err)
diff --git a/src/zope/i18n/config.py b/src/zope/i18n/config.py
index 1636083..b0068c5 100644
--- a/src/zope/i18n/config.py
+++ b/src/zope/i18n/config.py
@@ -45,4 +45,5 @@ def _parse_languages(value):
#: A set of languages that `zope.i18n.negotiate` will pass to the
#: `zope.i18n.interfaces.INegotiator` utility. If this is None,
#: no utility will be used.
-ALLOWED_LANGUAGES = _parse_languages(os.environ.get(ALLOWED_LANGUAGES_KEY, None))
+ALLOWED_LANGUAGES = _parse_languages(
+ os.environ.get(ALLOWED_LANGUAGES_KEY, None))
diff --git a/src/zope/i18n/format.py b/src/zope/i18n/format.py
index abc3e50..2a2421c 100644
--- a/src/zope/i18n/format.py
+++ b/src/zope/i18n/format.py
@@ -23,15 +23,16 @@ import datetime
import pytz
import pytz.reference
+from zope.i18n._compat import text_type
from zope.i18n.interfaces import IDateTimeFormat, INumberFormat
from zope.interface import implementer
-text_type = str if bytes is not str else unicode
+
NATIVE_NUMBER_TYPES = (int, float)
try:
NATIVE_NUMBER_TYPES += (long,)
except NameError:
- pass # Py3
+ pass # Py3
def roundHalfUp(n):
@@ -139,10 +140,11 @@ class DateTimeFormat(object):
if not ampm_entry:
raise DateTimeParseError(
'Cannot handle 12-hour format without am/pm marker.')
- ampm = self.calendar.pm == results[bin_pattern.index(ampm_entry[0])]
+ ampm = self.calendar.pm == results[bin_pattern.index(
+ ampm_entry[0])]
if hour == 12:
ampm = not ampm
- ordered[3] = (hour + 12*ampm)%24
+ ordered[3] = (hour + 12 * ampm) % 24
# Shortcut for the simple int functions
dt_fields_map = {'d': 2, 'H': 3, 'm': 4, 's': 5, 'S': 6}
@@ -155,7 +157,7 @@ class DateTimeFormat(object):
# Handle timezones
tzinfo = None
- pytz_tzinfo = False # If True, we should use pytz specific syntax
+ pytz_tzinfo = False # If True, we should use pytz specific syntax
tz_entry = _findFormattingCharacterInPattern('z', bin_pattern)
if ordered[3:] != [None, None, None, None] and tz_entry:
length = tz_entry[0][1]
@@ -187,9 +189,10 @@ class DateTimeFormat(object):
return tzinfo.localize(
datetime.datetime.combine(
datetime.date.today(),
- datetime.time(*[e or 0 for e in ordered[3:]]))).timetz()
+ datetime.time(*[e or 0 for e in ordered[3:]]))
+ ).timetz()
return datetime.time(
- *[e or 0 for e in ordered[3:]], **{'tzinfo' :tzinfo}
+ *[e or 0 for e in ordered[3:]], **{'tzinfo': tzinfo}
)
if pytz_tzinfo:
@@ -198,7 +201,7 @@ class DateTimeFormat(object):
))
return datetime.datetime(
- *[e or 0 for e in ordered], **{'tzinfo' :tzinfo}
+ *[e or 0 for e in ordered], **{'tzinfo': tzinfo}
)
def format(self, obj, pattern=None):
@@ -235,7 +238,7 @@ class NumberFormat(object):
self.symbols = {
u"decimal": u".",
u"group": u",",
- u"list": u";",
+ u"list": u";",
u"percentSign": u"%",
u"nativeZeroDigit": u"0",
u"patternDigit": u"#",
@@ -281,20 +284,20 @@ class NumberFormat(object):
min_size = bin_pattern[sign][INTEGER].count('0')
if bin_pattern[sign][GROUPING]:
regex += self.symbols['group']
- min_size += min_size/3
- regex += ']{%i,100}' %(min_size)
+ min_size += min_size / 3
+ regex += ']{%i,100}' % (min_size)
if bin_pattern[sign][FRACTION]:
max_precision = len(bin_pattern[sign][FRACTION])
min_precision = bin_pattern[sign][FRACTION].count('0')
- regex += '['+self.symbols['decimal']+']?'
- regex += '[0-9]{%i,%i}' %(min_precision, max_precision)
+ regex += '[' + self.symbols['decimal'] + ']?'
+ regex += '[0-9]{%i,%i}' % (min_precision, max_precision)
if bin_pattern[sign][EXPONENTIAL] != '':
regex += self.symbols['exponential']
min_exp_size = bin_pattern[sign][EXPONENTIAL].count('0')
pre_symbols = self.symbols['minusSign']
if bin_pattern[sign][EXPONENTIAL][0] == '+':
pre_symbols += self.symbols['plusSign']
- regex += '[%s]?[0-9]{%i,100}' %(pre_symbols, min_exp_size)
+ regex += '[%s]?[0-9]{%i,100}' % (pre_symbols, min_exp_size)
regex += ')'
if bin_pattern[sign][PADDING3] is not None:
regex += '[' + bin_pattern[sign][PADDING3] + ']+'
@@ -326,13 +329,14 @@ class NumberFormat(object):
num_str = num_str.replace(self.symbols['exponential'], 'E')
if self.type:
type = self.type
- return sign*type(num_str)
+ return sign * type(num_str)
def _format_integer(self, integer, pattern):
size = len(integer)
min_size = pattern.count('0')
if size < min_size:
- integer = self.symbols['nativeZeroDigit']*(min_size-size) + integer
+ integer = self.symbols['nativeZeroDigit'] * \
+ (min_size - size) + integer
return integer
def _format_fraction(self, fraction, pattern, rounding=True):
@@ -353,7 +357,7 @@ class NumberFormat(object):
fractionLen = len(fraction)
rounded = int(fraction) + 1
fraction = ('%0' + str(fractionLen) + 'i') % rounded
- if len(fraction) > fractionLen: # rounded fraction >= 1
+ if len(fraction) > fractionLen: # rounded fraction >= 1
roundInt = True
fraction = fraction[1:]
else:
@@ -361,8 +365,8 @@ class NumberFormat(object):
roundInt = True
if precision < min_precision:
- fraction += self.symbols['nativeZeroDigit']*(min_precision -
- precision)
+ fraction += self.symbols['nativeZeroDigit'] * (min_precision -
+ precision)
if fraction != '':
fraction = self.symbols['decimal'] + fraction
return fraction, roundInt
@@ -439,16 +443,16 @@ class NumberFormat(object):
# abs() of number smaller 1
if len(obj_int_frac) > 1:
res = re.match('(0*)[0-9]*', obj_int_frac[1]).groups()[0]
- exponent = self._format_integer(str(len(res)+1),
+ exponent = self._format_integer(str(len(res) + 1),
exp_bin_pattern)
- exponent = self.symbols['minusSign']+exponent
+ exponent = self.symbols['minusSign'] + exponent
number = obj_int_frac[1][len(res):]
else:
# We have exactly 0
exponent = self._format_integer('0', exp_bin_pattern)
number = self.symbols['nativeZeroDigit']
else:
- exponent = self._format_integer(str(len(obj_int_frac[0])-1),
+ exponent = self._format_integer(str(len(obj_int_frac[0]) - 1),
exp_bin_pattern)
number = ''.join(obj_int_frac)
@@ -483,28 +487,28 @@ class NumberFormat(object):
if bin_pattern[GROUPING]:
integer = self._group(integer, bin_pattern[GROUPING])
pre_padding = len(bin_pattern[INTEGER]) - len(integer)
- post_padding = len(bin_pattern[FRACTION]) - len(fraction)+1
+ post_padding = len(bin_pattern[FRACTION]) - len(fraction) + 1
number = integer + fraction
# Put it all together
text = ''
if bin_pattern[PADDING1] is not None and pre_padding > 0:
- text += bin_pattern[PADDING1]*pre_padding
+ text += bin_pattern[PADDING1] * pre_padding
text += bin_pattern[PREFIX]
if bin_pattern[PADDING2] is not None and pre_padding > 0:
if bin_pattern[PADDING1] is not None:
text += bin_pattern[PADDING2]
- else: # pragma: no cover
+ else: # pragma: no cover
text += bin_pattern[PADDING2] * pre_padding
text += number
if bin_pattern[PADDING3] is not None and post_padding > 0:
if bin_pattern[PADDING4] is not None:
text += bin_pattern[PADDING3]
else:
- text += bin_pattern[PADDING3]*post_padding
+ text += bin_pattern[PADDING3] * post_padding
text += bin_pattern[SUFFIX]
if bin_pattern[PADDING4] is not None and post_padding > 0:
- text += bin_pattern[PADDING4]*post_padding
+ text += bin_pattern[PADDING4] * post_padding
# TODO: Need to make sure unicode is everywhere
return text_type(text)
@@ -578,7 +582,7 @@ def parseDateTimePattern(pattern, DATETIMECHARS="aGyMdEDFwWhHmsSkKz"):
# Some cleaning up
if state == IN_QUOTE:
- if quote_start == -1: # pragma: no cover
+ if quote_start == -1: # pragma: no cover
# It should not be possible to get into this state.
# The only time we set quote_start to -1 we also set the state
# to DEFAULT.
@@ -605,7 +609,7 @@ def buildDateTimeParseInfo(calendar, pattern):
for entry in _findFormattingCharacterInPattern(field, pattern):
# The maximum amount of digits should be infinity, but 1000 is
# close enough here.
- info[entry] = r'([0-9]{%i,1000})' %entry[1]
+ info[entry] = r'([0-9]{%i,1000})' % entry[1]
# year (Number)
for entry in _findFormattingCharacterInPattern('y', pattern):
@@ -618,12 +622,12 @@ def buildDateTimeParseInfo(calendar, pattern):
# am/pm marker (Text)
for entry in _findFormattingCharacterInPattern('a', pattern):
- info[entry] = r'(%s|%s)' %(calendar.am, calendar.pm)
+ info[entry] = r'(%s|%s)' % (calendar.am, calendar.pm)
# era designator (Text)
# TODO: works for gregorian only right now
for entry in _findFormattingCharacterInPattern('G', pattern):
- info[entry] = r'(%s|%s)' %(calendar.eras[1][1], calendar.eras[2][1])
+ info[entry] = r'(%s|%s)' % (calendar.eras[1][1], calendar.eras[2][1])
# time zone (Text)
for entry in _findFormattingCharacterInPattern('z', pattern):
@@ -643,9 +647,10 @@ def buildDateTimeParseInfo(calendar, pattern):
elif entry[1] == 2:
info[entry] = r'([0-9]{2})'
elif entry[1] == 3:
- info[entry] = r'('+'|'.join(calendar.getMonthAbbreviations())+')'
+ info[entry] = r'(' + \
+ '|'.join(calendar.getMonthAbbreviations()) + ')'
else:
- info[entry] = r'('+'|'.join(calendar.getMonthNames())+')'
+ info[entry] = r'(' + '|'.join(calendar.getMonthNames()) + ')'
# day in week (Text and Number)
for entry in _findFormattingCharacterInPattern('E', pattern):
@@ -654,9 +659,9 @@ def buildDateTimeParseInfo(calendar, pattern):
elif entry[1] == 2:
info[entry] = r'([0-9]{2})'
elif entry[1] == 3:
- info[entry] = r'('+'|'.join(calendar.getDayAbbreviations())+')'
+ info[entry] = r'(' + '|'.join(calendar.getDayAbbreviations()) + ')'
else:
- info[entry] = r'('+'|'.join(calendar.getDayNames())+')'
+ info[entry] = r'(' + '|'.join(calendar.getDayNames()) + ')'
return info
@@ -676,7 +681,7 @@ def buildDateTimeInfo(dt, calendar, pattern):
else:
ampm = calendar.am
- h = dt.hour%12
+ h = dt.hour % 12
if h == 0:
h = 12
@@ -693,7 +698,7 @@ def buildDateTimeInfo(dt, calendar, pattern):
tz_mins = int(math.fabs(tz_secs % 3600 / 60))
tz_hours = int(math.fabs(tz_secs / 3600))
tz_sign = '-' if tz_secs < 0 else '+'
- tz_defaultname = "%s%i%.2i" %(tz_sign, tz_hours, tz_mins)
+ tz_defaultname = "%s%i%.2i" % (tz_sign, tz_hours, tz_mins)
tz_name = tzinfo.tzname(dt) or tz_defaultname
tz_fullname = getattr(tzinfo, 'zone', None) or tz_name
@@ -705,12 +710,12 @@ def buildDateTimeInfo(dt, calendar, pattern):
# Generic Numbers
for field, value in (('d', dt.day), ('D', int(dt.strftime('%j'))),
('F', day_of_week_in_month), ('k', dt.hour or 24),
- ('K', dt.hour%12), ('h', h), ('H', dt.hour),
+ ('K', dt.hour % 12), ('h', h), ('H', dt.hour),
('m', dt.minute), ('s', dt.second),
('S', dt.microsecond), ('w', int(dt.strftime('%W'))),
('W', week_in_month)):
for entry in _findFormattingCharacterInPattern(field, pattern):
- info[entry] = (u"%%.%ii" %entry[1]) %value
+ info[entry] = (u"%%.%ii" % entry[1]) % value
# am/pm marker (Text)
for entry in _findFormattingCharacterInPattern('a', pattern):
@@ -724,9 +729,9 @@ def buildDateTimeInfo(dt, calendar, pattern):
# time zone (Text)
for entry in _findFormattingCharacterInPattern('z', pattern):
if entry[1] == 1:
- info[entry] = u"%s%i%.2i" %(tz_sign, tz_hours, tz_mins)
+ info[entry] = u"%s%i%.2i" % (tz_sign, tz_hours, tz_mins)
elif entry[1] == 2:
- info[entry] = u"%s%.2i:%.2i" %(tz_sign, tz_hours, tz_mins)
+ info[entry] = u"%s%.2i:%.2i" % (tz_sign, tz_hours, tz_mins)
elif entry[1] == 3:
info[entry] = tz_name
else:
@@ -964,7 +969,7 @@ def parseNumberPattern(pattern):
last_index = -1
for index, char in enumerate(reversed(integer)):
if char == ",":
- grouping += (index-last_index-1,)
+ grouping += (index - last_index - 1,)
last_index = index
# use last group ad infinitum
grouping += (0,)
diff --git a/src/zope/i18n/interfaces/__init__.py b/src/zope/i18n/interfaces/__init__.py
index c5a3328..025c477 100644
--- a/src/zope/i18n/interfaces/__init__.py
+++ b/src/zope/i18n/interfaces/__init__.py
@@ -203,7 +203,6 @@ class IMessageImportFilter(Interface):
Classes implementing this interface should usually be Adaptors, as
they adapt the IEditableTranslationService interface."""
-
def importMessages(domains, languages, file):
"""Import all messages that are defined in the specified domains and
languages.
@@ -254,7 +253,6 @@ class IMessageExportFilter(Interface):
Classes implementing this interface should usually be Adaptors, as
they adapt the IEditableTranslationService interface."""
-
def exportMessages(domains, languages):
"""Export all messages that are defined in the specified domains and
languages.
@@ -326,7 +324,6 @@ class IFormat(Interface):
"""Format an object to a string using the pattern as a rule."""
-
class INumberFormat(IFormat):
r"""Specific number formatting interface. Here are the formatting
rules (I modified the rules from ICU a bit, since I think they did not
@@ -373,9 +370,9 @@ class INumberFormat(IFormat):
\u00A4 This is the currency sign. it will be replaced by a currency
symbol. If it is present in a pattern, the monetary decimal
separator is used instead of the decimal separator.
- \u00A4\u00A4 This is the international currency sign. It will be replaced
- by an international currency symbol. If it is present in a
- pattern, the monetary decimal separator is used instead of
+ \u00A4\u00A4 This is the international currency sign. It will be
+ replaced by an international currency symbol. If it is present
+ in a pattern, the monetary decimal separator is used instead of
the decimal separator.
X Any other characters can be used in the prefix or suffix
' Used to quote special characters in a prefix or suffix
diff --git a/src/zope/i18n/interfaces/locales.py b/src/zope/i18n/interfaces/locales.py
index f6017ad..01c9cbe 100644
--- a/src/zope/i18n/interfaces/locales.py
+++ b/src/zope/i18n/interfaces/locales.py
@@ -17,7 +17,7 @@ import datetime
import re
from zope.interface import Interface, Attribute
from zope.schema import \
- Field, Text, TextLine, Int, Bool, Tuple, List, Dict, Date
+ Field, Text, TextLine, Int, Bool, Tuple, List, Dict, Date
from zope.schema import Choice
@@ -188,7 +188,6 @@ class ILocaleTimeZone(Interface):
required=True,
readonly=True)
-
names = Dict(
title=u"Time Zone Names",
description=u"Various names of the timezone.",
@@ -230,7 +229,7 @@ class ILocaleFormatLength(Interface):
title=u"Format Length Type",
description=u"Name of the format length",
values=(u"full", u"long", u"medium", u"short")
- )
+ )
default = TextLine(
title=u"Default Format",
@@ -491,6 +490,7 @@ class ILocaleCurrency(Interface):
symbolChoice = Bool(title=u"Symbol Choice")
+
class ILocaleNumbers(Interface):
"""This object contains various data about numbers and currencies."""
@@ -556,7 +556,6 @@ class ILocaleNumbers(Interface):
description=u"Name of the format length"),
value_type=Field(title=u"ILocaleCurrency object"))
-
def getFormatter(category, length=None, name=u""):
"""Get the NumberFormat based on the category, length and name of the
format.
@@ -577,8 +576,11 @@ class ILocaleNumbers(Interface):
def getDefaultCurrency():
"""Get the default currency."""
+
_orientations = [u"left-to-right", u"right-to-left",
u"top-to-bottom", u"bottom-to-top"]
+
+
class ILocaleOrientation(Interface):
"""Information about the orientation of text."""
@@ -586,13 +588,14 @@ class ILocaleOrientation(Interface):
title=u"Orientation of characters",
values=_orientations,
default=u"left-to-right"
- )
+ )
lines = Choice(
title=u"Orientation of characters",
values=_orientations,
default=u"top-to-bottom"
- )
+ )
+
class ILocale(Interface):
"""This class contains all important information about the locale.
@@ -699,6 +702,7 @@ class IDictionaryInheritance(ILocaleInheritance):
object is consulted.
"""
+
class ICollator(Interface):
"""Provide support for collating text strings
diff --git a/src/zope/i18n/locales/__init__.py b/src/zope/i18n/locales/__init__.py
index 030c453..12009a6 100644
--- a/src/zope/i18n/locales/__init__.py
+++ b/src/zope/i18n/locales/__init__.py
@@ -29,8 +29,10 @@ from zope.i18n.interfaces.locales import ILocaleOrientation
from zope.i18n.interfaces.locales import ILocaleDayContext, ILocaleMonthContext
from zope.i18n.format import NumberFormat, DateTimeFormat
from zope.i18n.locales.inheritance import \
- AttributeInheritance, InheritingDictionary, NoParentException
-from zope.i18n.locales.provider import LocaleProvider, LoadLocaleError
+ AttributeInheritance, InheritingDictionary, NoParentException
+# LoadLocaleError is not used, but might be imported from here by others.
+from zope.i18n.locales.provider import LoadLocaleError # noqa
+from zope.i18n.locales.provider import LocaleProvider
# Setup the locale directory
@@ -73,6 +75,7 @@ calendarAliases = {'islamic': ('arabic',),
'islamic-civil': ('civil-arabic',),
'buddhist': ('thai-buddhist', )}
+
@implementer(ILocaleIdentity)
class LocaleIdentity(object):
"""Represents a unique identification of the locale
@@ -102,7 +105,8 @@ class LocaleIdentity(object):
<LocaleIdentity (en, None, US, POSIX)>
"""
- def __init__(self, language=None, script=None, territory=None, variant=None):
+ def __init__(self, language=None, script=None,
+ territory=None, variant=None):
"""Initialize object."""
self.language = language
self.script = script
@@ -112,7 +116,7 @@ class LocaleIdentity(object):
def __repr__(self):
"""See zope.i18n.interfaces.ILocaleIdentity
"""
- return "<LocaleIdentity (%s, %s, %s, %s)>" %(
+ return "<LocaleIdentity (%s, %s, %s, %s)>" % (
self.language, self.script, self.territory, self.variant)
@@ -158,6 +162,7 @@ class LocaleVersion(object):
return ((self.generationDate, self.number) ==
(other.generationDate, other.number))
+
@implementer(ILocaleDisplayNames)
class LocaleDisplayNames(AttributeInheritance):
"""Locale display names with inheritable data.
@@ -233,7 +238,6 @@ class LocaleFormatLength(AttributeInheritance):
"""Specifies one of the format lengths of a specific quantity, like
numbers, dates, times and datetimes."""
-
def __init__(self, type=None):
"""Initialize the object."""
self.type = type
@@ -343,7 +347,8 @@ class LocaleCalendar(AttributeInheritance):
def getMonthNames(self):
"""See zope.i18n.interfaces.ILocaleCalendar"""
- return [self.months.get(type, (None, None))[0] for type in range(1, 13)]
+ return [self.months.get(type, (None, None))[0]
+ for type in range(1, 13)]
def getMonthTypeFromName(self, name):
"""See zope.i18n.interfaces.ILocaleCalendar"""
@@ -353,7 +358,8 @@ class LocaleCalendar(AttributeInheritance):
def getMonthAbbreviations(self):
"""See zope.i18n.interfaces.ILocaleCalendar"""
- return [self.months.get(type, (None, None))[1] for type in range(1, 13)]
+ return [self.months.get(type, (None, None))[1]
+ for type in range(1, 13)]
def getMonthTypeFromAbbreviation(self, abbr):
"""See zope.i18n.interfaces.ILocaleCalendar"""
@@ -505,11 +511,11 @@ class LocaleDates(AttributeInheritance):
cal = self.calendars[calendar]
- formats = getattr(cal, category+'Formats')
+ formats = getattr(cal, category + 'Formats')
if length is None:
length = getattr(
cal,
- 'default'+category[0].upper()+category[1:]+'Format',
+ 'default' + category[0].upper() + category[1:] + 'Format',
list(formats.keys())[0])
# 'datetime' is always a bit special; we often do not have a length
@@ -652,6 +658,7 @@ class LocaleOrientation(AttributeInheritance):
"""Implementation of ILocaleOrientation
"""
+
@implementer(ILocale)
class Locale(AttributeInheritance):
"""Implementation of the ILocale interface."""
@@ -690,7 +697,7 @@ class Locale(AttributeInheritance):
# Notice that 'pieces' is always empty.
pieces = [key + '=' + type for (key, type) in ()]
assert not pieces
- if pieces: # pragma: no cover
+ if pieces: # pragma: no cover
id_string += '@' + ','.join(pieces)
return id_string
diff --git a/src/zope/i18n/locales/fallbackcollator.py b/src/zope/i18n/locales/fallbackcollator.py
index fc76a58..55c9b77 100644
--- a/src/zope/i18n/locales/fallbackcollator.py
+++ b/src/zope/i18n/locales/fallbackcollator.py
@@ -16,6 +16,7 @@
from unicodedata import normalize
+
class FallbackCollator:
def __init__(self, locale):
diff --git a/src/zope/i18n/locales/inheritance.py b/src/zope/i18n/locales/inheritance.py
index 912d8c9..e597b78 100644
--- a/src/zope/i18n/locales/inheritance.py
+++ b/src/zope/i18n/locales/inheritance.py
@@ -24,11 +24,13 @@ from zope.deprecation import deprecate
from zope.interface import implementer
from zope.i18n.interfaces.locales import \
- ILocaleInheritance, IAttributeInheritance, IDictionaryInheritance
+ ILocaleInheritance, IAttributeInheritance, IDictionaryInheritance
+
class NoParentException(AttributeError):
pass
+
@implementer(ILocaleInheritance)
class Inheritance(object):
"""A simple base version of locale inheritance.
@@ -37,7 +39,6 @@ class Inheritance(object):
'ILocaleInheritance' implementations.
"""
-
# See zope.i18n.interfaces.locales.ILocaleInheritance
__parent__ = None
@@ -100,7 +101,6 @@ class AttributeInheritance(Inheritance):
True
"""
-
def __setattr__(self, name, value):
"""See zope.i18n.interfaces.locales.ILocaleInheritance"""
# If we have a value that can also inherit data from other locales, we
@@ -111,7 +111,6 @@ class AttributeInheritance(Inheritance):
value.__name__ = name
super(AttributeInheritance, self).__setattr__(name, value)
-
def __getattr__(self, name):
"""See zope.i18n.interfaces.locales.ILocaleInheritance"""
try:
@@ -134,7 +133,6 @@ class AttributeInheritance(Inheritance):
return value
-
@implementer(IDictionaryInheritance)
class InheritingDictionary(Inheritance, dict):
"""Implementation of a dictionary that can also inherit values.
@@ -197,7 +195,6 @@ class InheritingDictionary(Inheritance, dict):
`value` is a deprecated synonym for `values`
"""
-
def __setitem__(self, name, value):
"""See zope.i18n.interfaces.locales.ILocaleInheritance"""
if ILocaleInheritance.providedBy(value):
diff --git a/src/zope/i18n/locales/provider.py b/src/zope/i18n/locales/provider.py
index 3864b7a..9e7f66b 100644
--- a/src/zope/i18n/locales/provider.py
+++ b/src/zope/i18n/locales/provider.py
@@ -20,6 +20,7 @@ import os
from zope.interface import implementer
from zope.i18n.interfaces.locales import ILocaleProvider
+
class LoadLocaleError(Exception):
"""This error is raised if a locale cannot be loaded."""
@@ -28,7 +29,6 @@ class LoadLocaleError(Exception):
class LocaleProvider(object):
"""A locale provider that gets its data from the XML data."""
-
def __init__(self, locale_dir):
self._locales = {}
self._locale_dir = locale_dir
diff --git a/src/zope/i18n/locales/tests/test_docstrings.py b/src/zope/i18n/locales/tests/test_docstrings.py
index fa52078..faa4e97 100644
--- a/src/zope/i18n/locales/tests/test_docstrings.py
+++ b/src/zope/i18n/locales/tests/test_docstrings.py
@@ -20,6 +20,7 @@ from zope.i18n.locales.inheritance import NoParentException
from zope.i18n.testing import unicode_checker
+
class LocaleInheritanceStub(AttributeInheritance):
def __init__(self, nextLocale=None):
@@ -36,7 +37,4 @@ def test_suite():
DocTestSuite('zope.i18n.locales', checker=unicode_checker),
DocTestSuite('zope.i18n.locales.inheritance', checker=unicode_checker),
DocTestSuite('zope.i18n.locales.xmlfactory', checker=unicode_checker),
- ))
-
-if __name__ == '__main__':
- unittest.main()
+ ))
diff --git a/src/zope/i18n/locales/tests/test_fallbackcollator.py b/src/zope/i18n/locales/tests/test_fallbackcollator.py
index 7e10b50..ad78b72 100644
--- a/src/zope/i18n/locales/tests/test_fallbackcollator.py
+++ b/src/zope/i18n/locales/tests/test_fallbackcollator.py
@@ -17,11 +17,9 @@ import doctest
from zope.i18n.testing import unicode_checker
+
def test_suite():
return unittest.TestSuite((
- doctest.DocFileSuite('../fallbackcollator.txt', checker=unicode_checker),
- ))
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
-
+ doctest.DocFileSuite('../fallbackcollator.txt',
+ checker=unicode_checker),
+ ))
diff --git a/src/zope/i18n/locales/tests/test_locales.py b/src/zope/i18n/locales/tests/test_locales.py
index 2b100b8..523b1d2 100644
--- a/src/zope/i18n/locales/tests/test_locales.py
+++ b/src/zope/i18n/locales/tests/test_locales.py
@@ -24,6 +24,7 @@ from zope.i18n.locales.provider import LocaleProvider, LoadLocaleError
import zope.i18n
datadir = os.path.join(os.path.dirname(zope.i18n.__file__), 'locales', 'data')
+
class AbstractTestILocaleProviderMixin(object):
"""Test the functionality of an implmentation of the ILocaleProvider
interface."""
@@ -143,6 +144,7 @@ class TestGlobalLocaleProvider(TestCase):
self.assertEqual(locale.id.territory, 'GB')
self.assertEqual(locale.id.variant, None)
+
class TestRootLocale(TestCase):
"""There were some complaints that the root locale does not work
correctly, so make sure it does."""
diff --git a/src/zope/i18n/locales/tests/test_xmlfactory.py b/src/zope/i18n/locales/tests/test_xmlfactory.py
index 792c96d..e11097c 100644
--- a/src/zope/i18n/locales/tests/test_xmlfactory.py
+++ b/src/zope/i18n/locales/tests/test_xmlfactory.py
@@ -19,6 +19,7 @@ from unittest import TestCase, TestSuite
from zope.i18n.locales.xmlfactory import LocaleFactory
import zope.i18n
+
class LocaleXMLFileTestCase(TestCase):
"""This test verifies that every locale XML file can be loaded."""
@@ -33,20 +34,24 @@ class LocaleXMLFileTestCase(TestCase):
# XXX: The tests below are commented out because it's not
# necessary for the xml files to have all format definitions.
- ## Making sure all number format patterns parse
- #for category in (u'decimal', u'scientific', u'percent', u'currency'):
- # for length in getattr(locale.numbers, category+'Formats').values():
+ # Making sure all number format patterns parse
+ # for category in (u'decimal', u'scientific', u'percent', u'currency'):
+ # for length in getattr(
+ # locale.numbers, category + 'Formats'
+ # ).values():
# for format in length.formats.values():
- # self.assert_(parseNumberPattern(format.pattern) is not None)
-
- ## Making sure all datetime patterns parse
- #for calendar in locale.dates.calendars.values():
- # for category in ('date', 'time', 'dateTime'):
- # for length in getattr(calendar, category+'Formats').values():
- # for format in length.formats.values():
- # self.assert_(
- # parseDateTimePattern(format.pattern) is not None)
+ # self.assertIsNotNone(parseNumberPattern(format.pattern)
+ # Making sure all datetime patterns parse
+ # for calendar in locale.dates.calendars.values():
+ # for category in ('date', 'time', 'dateTime'):
+ # for length in getattr(
+ # calendar, category + 'Formats'
+ # ).values():
+ # for format in length.formats.values():
+ # self.assertIsNotNone(
+ # parseDateTimePattern(format.pattern)
+ # )
def test_suite():
diff --git a/src/zope/i18n/locales/xmlfactory.py b/src/zope/i18n/locales/xmlfactory.py
index 5b2dbf5..f1ed219 100644
--- a/src/zope/i18n/locales/xmlfactory.py
+++ b/src/zope/i18n/locales/xmlfactory.py
@@ -41,7 +41,6 @@ class LocaleFactory(object):
rc = rc + node.data
return rc
-
def _extractVersion(self, identity_node):
"""Extract the Locale's version info based on data from the DOM
tree.
@@ -81,7 +80,6 @@ class LocaleFactory(object):
return LocaleVersion(number, generationDate, notes)
-
def _extractIdentity(self):
"""Extract the Locale's identity object based on info from the DOM
tree.
@@ -119,7 +117,7 @@ class LocaleFactory(object):
# Retrieve the language of the locale
nodes = identity.getElementsByTagName('language')
if nodes != []:
- id.language = nodes[0].getAttribute('type') or None
+ id.language = nodes[0].getAttribute('type') or None
# Retrieve the territory of the locale
nodes = identity.getElementsByTagName('territory')
if nodes != []:
@@ -132,7 +130,6 @@ class LocaleFactory(object):
id.version = self._extractVersion(identity)
return id
-
def _extractTypes(self, names_node):
"""Extract all types from the names_node.
@@ -179,7 +176,6 @@ class LocaleFactory(object):
types[(type, key)] = self._getText(type_node.childNodes)
return types
-
def _extractDisplayNames(self):
"""Extract all display names from the DOM tree.
@@ -285,7 +281,6 @@ class LocaleFactory(object):
displayNames.types = types
return displayNames
-
def _extractMonths(self, months_node, calendar):
"""Extract all month entries from cal_node and store them in calendar.
@@ -350,7 +345,8 @@ class LocaleFactory(object):
>>> names[7:]
[u'August', u'September', u'Oktober', u'November', u'Dezember']
- >>> abbrs = [ctx.months[u"abbreviated"][type] for type in range(1,13)]
+ >>> abbrs = [ctx.months[u"abbreviated"][type]
+ ... for type in range(1,13)]
>>> abbrs[:6]
[u'Jan', u'Feb', u'Mrz', u'Apr', u'Mai', u'Jun']
>>> abbrs[6:]
@@ -385,14 +381,15 @@ class LocaleFactory(object):
defaultMonthContext_node = months_node.getElementsByTagName('default')
if defaultMonthContext_node:
- calendar.defaultMonthContext = defaultMonthContext_node[0].getAttribute('type')
+ calendar.defaultMonthContext = defaultMonthContext_node[
+ 0].getAttribute('type')
monthContext_nodes = months_node.getElementsByTagName('monthContext')
if not monthContext_nodes:
return
calendar.monthContexts = InheritingDictionary()
- names_node = abbrs_node = None # BBB
+ names_node = abbrs_node = None # BBB
for node in monthContext_nodes:
context_type = node.getAttribute('type')
@@ -441,7 +438,6 @@ class LocaleFactory(object):
calendar.months[type] = (names.get(type, None),
abbrs.get(type, None))
-
def _extractDays(self, days_node, calendar):
"""Extract all day entries from cal_node and store them in
calendar.
@@ -529,14 +525,15 @@ class LocaleFactory(object):
defaultDayContext_node = days_node.getElementsByTagName('default')
if defaultDayContext_node:
- calendar.defaultDayContext = defaultDayContext_node[0].getAttribute('type')
+ calendar.defaultDayContext = defaultDayContext_node[
+ 0].getAttribute('type')
dayContext_nodes = days_node.getElementsByTagName('dayContext')
if not dayContext_nodes:
return
calendar.dayContexts = InheritingDictionary()
- names_node = abbrs_node = None # BBB
+ names_node = abbrs_node = None # BBB
for node in dayContext_nodes:
context_type = node.getAttribute('type')
@@ -584,7 +581,6 @@ class LocaleFactory(object):
calendar.days[type] = (names.get(type, None),
abbrs.get(type, None))
-
def _extractWeek(self, cal_node, calendar):
"""Extract all week entries from cal_node and store them in
calendar.
@@ -644,7 +640,6 @@ class LocaleFactory(object):
time_args = map(int, node.getAttribute('time').split(':'))
calendar.week['weekendEnd'] = (day, time(*time_args))
-
def _extractEras(self, cal_node, calendar):
"""Extract all era entries from cal_node and store them in
calendar.
@@ -703,8 +698,8 @@ class LocaleFactory(object):
calendar.eras = InheritingDictionary()
for type in abbrs.keys():
- calendar.eras[type] = (names.get(type, None), abbrs.get(type, None))
-
+ calendar.eras[type] = (names.get(type, None),
+ abbrs.get(type, None))
def _extractFormats(self, formats_node, lengthNodeName, formatNodeName):
"""Extract all format entries from formats_node and return a
@@ -765,14 +760,16 @@ class LocaleFactory(object):
if length_node.getElementsByTagName(formatNodeName):
length.formats = InheritingDictionary()
- for format_node in length_node.getElementsByTagName(formatNodeName):
+ for format_node in length_node.getElementsByTagName(
+ formatNodeName):
format = LocaleFormat()
format.type = format_node.getAttribute('type') or None
pattern_node = format_node.getElementsByTagName('pattern')[0]
format.pattern = self._getText(pattern_node.childNodes)
name_nodes = format_node.getElementsByTagName('displayName')
if name_nodes:
- format.displayName = self._getText(name_nodes[0].childNodes)
+ format.displayName = self._getText(
+ name_nodes[0].childNodes)
length.formats[format.type] = format
lengths[length.type] = length
@@ -907,14 +904,15 @@ class LocaleFactory(object):
for formatsName, lengthName, formatName in (
('dateFormats', 'dateFormatLength', 'dateFormat'),
('timeFormats', 'timeFormatLength', 'timeFormat'),
- ('dateTimeFormats', 'dateTimeFormatLength', 'dateTimeFormat')):
+ ('dateTimeFormats', 'dateTimeFormatLength',
+ 'dateTimeFormat')):
formats_nodes = cal_node.getElementsByTagName(formatsName)
if formats_nodes:
default, formats = self._extractFormats(
formats_nodes[0], lengthName, formatName)
setattr(calendar,
- 'default'+formatName[0].upper()+formatName[1:],
+ 'default' + formatName[0].upper() + formatName[1:],
default)
setattr(calendar, formatsName, formats)
@@ -925,7 +923,6 @@ class LocaleFactory(object):
return calendars
-
def _extractTimeZones(self, dates_node):
"""Extract all timezone information for the locale from the DOM
tree.
@@ -1009,7 +1006,6 @@ class LocaleFactory(object):
return zones
-
def _extractDates(self):
"""Extract all date information from the DOM tree"""
dates_nodes = self._data.getElementsByTagName('dates')
@@ -1025,7 +1021,6 @@ class LocaleFactory(object):
dates.timezones = timezones
return dates
-
def _extractSymbols(self, numbers_node):
"""Extract all week entries from cal_node and store them in
calendar.
@@ -1081,7 +1076,6 @@ class LocaleFactory(object):
return symbols
-
def _extractNumberFormats(self, numbers_node, numbers):
"""Extract all number formats from the numbers_node and save the data
in numbers.
@@ -1162,10 +1156,10 @@ class LocaleFactory(object):
"""
for category in ('decimal', 'scientific', 'percent', 'currency'):
- formatsName = category+'Formats'
- lengthName = category+'FormatLength'
- formatName = category+'Format'
- defaultName = 'default'+formatName[0].upper()+formatName[1:]
+ formatsName = category + 'Formats'
+ lengthName = category + 'FormatLength'
+ formatName = category + 'Format'
+ defaultName = 'default' + formatName[0].upper() + formatName[1:]
formats_nodes = numbers_node.getElementsByTagName(formatsName)
if formats_nodes:
@@ -1174,7 +1168,6 @@ class LocaleFactory(object):
setattr(numbers, defaultName, default)
setattr(numbers, formatsName, formats)
-
def _extractCurrencies(self, numbers_node):
"""Extract all currency definitions and their information from the
Locale's DOM tree.
@@ -1232,7 +1225,7 @@ class LocaleFactory(object):
if nodes:
currency.symbol = self._getText(nodes[0].childNodes)
currency.symbolChoice = \
- nodes[0].getAttribute('choice') == u"true"
+ nodes[0].getAttribute('choice') == u"true"
nodes = curr_node.getElementsByTagName('displayName')
if nodes:
@@ -1242,7 +1235,6 @@ class LocaleFactory(object):
return currencies
-
def _extractNumbers(self):
"""Extract all number information from the DOM tree"""
numbers_nodes = self._data.getElementsByTagName('numbers')
@@ -1259,7 +1251,6 @@ class LocaleFactory(object):
numbers.currencies = currencies
return numbers
-
def _extractDelimiters(self):
"""Extract all delimiter entries from the DOM tree.
@@ -1315,7 +1306,6 @@ class LocaleFactory(object):
return delimiters
-
def _extractOrientation(self):
"""Extract orientation information.
@@ -1324,7 +1314,8 @@ class LocaleFactory(object):
>>> xml = u'''
... <ldml>
... <layout>
- ... <orientation lines="bottom-to-top" characters="right-to-left" />
+ ... <orientation lines="bottom-to-top"
+ ... characters="right-to-left" />
... </layout>
... </ldml>'''
>>> dom = parseString(xml)
@@ -1345,7 +1336,6 @@ class LocaleFactory(object):
setattr(orientation, name, value)
return orientation
-
def __call__(self):
"""Create the Locale."""
locale = Locale(self._extractIdentity())
diff --git a/src/zope/i18n/negotiator.py b/src/zope/i18n/negotiator.py
index 3c5fa17..3ece221 100644
--- a/src/zope/i18n/negotiator.py
+++ b/src/zope/i18n/negotiator.py
@@ -29,8 +29,8 @@ def normalize_langs(langs):
# Make a mapping from normalized->original so we keep can match
# the normalized lang and return the original string.
n_langs = {}
- for l in langs:
- n_langs[normalize_lang(l)] = l
+ for lang in langs:
+ n_langs[normalize_lang(lang)] = lang
return n_langs
diff --git a/src/zope/i18n/simpletranslationdomain.py b/src/zope/i18n/simpletranslationdomain.py
index b093db8..b20385a 100644
--- a/src/zope/i18n/simpletranslationdomain.py
+++ b/src/zope/i18n/simpletranslationdomain.py
@@ -15,13 +15,11 @@
"""
from zope.interface import implementer
from zope.component import getUtility
+from zope.i18n._compat import text_type
from zope.i18n.interfaces import ITranslationDomain, INegotiator
from zope.i18n import interpolate
-text_type = str if bytes is not str else unicode
-
-
@implementer(ITranslationDomain)
class SimpleTranslationDomain(object):
"""This is the simplest implementation of the ITranslationDomain I
diff --git a/src/zope/i18n/testmessagecatalog.py b/src/zope/i18n/testmessagecatalog.py
index 8420c58..b88b3d7 100644
--- a/src/zope/i18n/testmessagecatalog.py
+++ b/src/zope/i18n/testmessagecatalog.py
@@ -18,6 +18,7 @@ from zope import interface
import zope.i18n.interfaces
from zope.i18n.translationdomain import TranslationDomain
+
@interface.implementer(zope.i18n.interfaces.IGlobalMessageCatalog)
class TestMessageCatalog(object):
@@ -28,7 +29,7 @@ class TestMessageCatalog(object):
def queryMessage(self, msgid, default=None):
default = getattr(msgid, 'default', default)
- if default != None and default != msgid:
+ if default is not None and default != msgid:
msg = u"%s (%s)" % (msgid, default)
else:
msg = msgid
@@ -43,13 +44,15 @@ class TestMessageCatalog(object):
def reload(self):
pass
+
@interface.implementer(zope.i18n.interfaces.ITranslationDomain)
def TestMessageFallbackDomain(domain_id=u""):
domain = TranslationDomain(domain_id)
domain.addCatalog(TestMessageCatalog(domain_id))
return domain
+
interface.directlyProvides(
TestMessageFallbackDomain,
zope.i18n.interfaces.IFallbackTranslationDomainFactory,
- )
+)
diff --git a/src/zope/i18n/tests/test.py b/src/zope/i18n/tests/test.py
index 4fb4f4c..62082c3 100644
--- a/src/zope/i18n/tests/test.py
+++ b/src/zope/i18n/tests/test.py
@@ -22,6 +22,7 @@ from zope.i18n.testing import unicode_checker
def test_suite():
options = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+
def suite(name):
return doctest.DocTestSuite(
name,
@@ -34,7 +35,3 @@ def test_suite():
suite("zope.i18n.config"),
suite("zope.i18n.testing"),
])
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
diff --git a/src/zope/i18n/tests/test_formats.py b/src/zope/i18n/tests/test_formats.py
index 83c9129..a266de6 100644
--- a/src/zope/i18n/tests/test_formats.py
+++ b/src/zope/i18n/tests/test_formats.py
@@ -34,6 +34,7 @@ from zope.i18n.format import NumberPatternParseError
class LocaleStub(object):
pass
+
class LocaleCalendarStub(object):
type = u"gregorian"
@@ -71,7 +72,8 @@ class LocaleCalendarStub(object):
week = {'firstDay': 1, 'minDays': 1}
def getMonthNames(self):
- return [self.months.get(type, (None, None))[0] for type in range(1, 13)]
+ return [self.months.get(type, (None, None))[0]
+ for type in range(1, 13)]
def getMonthTypeFromName(self, name):
for item in self.months.items():
@@ -79,7 +81,8 @@ class LocaleCalendarStub(object):
return item[0]
def getMonthAbbreviations(self):
- return [self.months.get(type, (None, None))[1] for type in range(1, 13)]
+ return [self.months.get(type, (None, None))[1]
+ for type in range(1, 13)]
def getMonthTypeFromAbbreviation(self, abbr):
for item in self.months.items():
@@ -102,13 +105,13 @@ class LocaleCalendarStub(object):
class _TestCase(TestCase):
# Avoid deprecation warnings in Python 3 by making the preferred
# method name available for Python 2.
- assertRaisesRegex = getattr(TestCase, 'assertRaisesRegex', TestCase.assertRaisesRegexp)
+ assertRaisesRegex = getattr(
+ TestCase, 'assertRaisesRegex', TestCase.assertRaisesRegexp)
class TestDateTimePatternParser(_TestCase):
"""Extensive tests for the ICU-based-syntax datetime pattern parser."""
-
def testParseSimpleTimePattern(self):
self.assertEqual(parseDateTimePattern('HH'),
[('H', 2)])
@@ -217,7 +220,8 @@ class TestBuildDateTimeParseInfo(_TestCase):
for char in 'dDFkKhHmsSwW':
for length in range(1, 6):
self.assertEqual(self.info((char, length)),
- '([0-9]{%i,1000})' %length)
+ '([0-9]{%i,1000})' % length)
+
def testYear(self):
self.assertEqual(self.info(('y', 2)), '([0-9]{2})')
self.assertEqual(self.info(('y', 4)), '([0-9]{4})')
@@ -228,7 +232,8 @@ class TestBuildDateTimeParseInfo(_TestCase):
def testAMPMMarker(self):
names = ['vorm.', 'nachm.']
for length in range(1, 6):
- self.assertEqual(self.info(('a', length)), '('+'|'.join(names)+')')
+ self.assertEqual(self.info(('a', length)),
+ '(' + '|'.join(names) + ')')
def testEra(self):
self.assertEqual(self.info(('G', 1)), '(v. Chr.|n. Chr.)')
@@ -248,12 +253,12 @@ class TestBuildDateTimeParseInfo(_TestCase):
names = [u"Januar", u"Februar", u"Maerz", u"April",
u"Mai", u"Juni", u"Juli", u"August", u"September", u"Oktober",
u"November", u"Dezember"]
- self.assertEqual(self.info(('M', 4)), '('+'|'.join(names)+')')
+ self.assertEqual(self.info(('M', 4)), '(' + '|'.join(names) + ')')
def testMonthAbbr(self):
names = ['Jan', 'Feb', 'Mrz', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug',
'Sep', 'Okt', 'Nov', 'Dez']
- self.assertEqual(self.info(('M', 3)), '('+'|'.join(names)+')')
+ self.assertEqual(self.info(('M', 3)), '(' + '|'.join(names) + ')')
def testWeekdayNumber(self):
self.assertEqual(self.info(('E', 1)), '([0-9])')
@@ -262,13 +267,13 @@ class TestBuildDateTimeParseInfo(_TestCase):
def testWeekdayNames(self):
names = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag',
'Freitag', 'Samstag', 'Sonntag']
- self.assertEqual(self.info(('E', 4)), '('+'|'.join(names)+')')
- self.assertEqual(self.info(('E', 5)), '('+'|'.join(names)+')')
- self.assertEqual(self.info(('E', 10)), '('+'|'.join(names)+')')
+ self.assertEqual(self.info(('E', 4)), '(' + '|'.join(names) + ')')
+ self.assertEqual(self.info(('E', 5)), '(' + '|'.join(names) + ')')
+ self.assertEqual(self.info(('E', 10)), '(' + '|'.join(names) + ')')
def testWeekdayAbbr(self):
names = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']
- self.assertEqual(self.info(('E', 3)), '('+'|'.join(names)+')')
+ self.assertEqual(self.info(('E', 3)), '(' + '|'.join(names) + ')')
class TestDateTimeFormat(_TestCase):
@@ -466,10 +471,10 @@ class TestDateTimeFormat(_TestCase):
for day in range(1, 8):
self.assertEqual(
self.format.format(
- datetime.datetime(2003, 1, day+5, 21, 48),
+ datetime.datetime(2003, 1, day + 5, 21, 48),
"EEEE, d. MMMM yyyy H:mm' Uhr 'z"),
'%s, %i. Januar 2003 21:48 Uhr +000' % (
- self.format.calendar.days[day][0], day+5))
+ self.format.calendar.days[day][0], day + 5))
def testFormatTimeZone(self):
self.assertEqual(
@@ -674,7 +679,6 @@ class TestDateTimeFormat(_TestCase):
"F. EEEE 'im' MMMM, yyyy"),
u"2. Freitag im Januar, 2003")
-
def testFormatGregorianEra(self):
self.assertEqual(
self.format.format(datetime.date(2017, 12, 17), 'G'),
@@ -711,7 +715,7 @@ class TestNumberPatternParser(_TestCase):
self.assertEqual(
parseNumberPattern('###0;#0'),
((None, '', None, '###0', '', '', None, '', None, ()),
- (None, '', None, '#0', '', '', None, '', None, ())))
+ (None, '', None, '#0', '', '', None, '', None, ())))
def testParsePrefixedIntegerPattern(self):
self.assertEqual(
@@ -753,7 +757,7 @@ class TestNumberPatternParser(_TestCase):
self.assertEqual(
parseNumberPattern('###0.00#;#0.0#'),
((None, '', None, '###0', '00#', '', None, '', None, ()),
- (None, '', None, '#0', '0#', '', None, '', None, ())))
+ (None, '', None, '#0', '0#', '', None, '', None, ())))
def testParsePosNegFractionPattern(self):
self.assertEqual(
@@ -783,7 +787,8 @@ class TestNumberPatternParser(_TestCase):
self.assertEqual(
parseNumberPattern('#,##,##0.###;-#,##,##0.###'),
((None, '', None, '#####0', '###', '', None, '', None, (3, 2, 0)),
- (None, '-', None, '#####0', '###', '', None, '', None, (3, 2, 0))))
+ (None, '-', None, '#####0', '###', '', None, '', None,
+ (3, 2, 0))))
self.assertEqual(
parseNumberPattern('#,##0.##;-#,##0.##'),
@@ -808,7 +813,8 @@ class TestNumberPatternParser(_TestCase):
self.assertEqual(
parseNumberPattern('##,##,##0.###;-##,##,##0.###'),
((None, '', None, '######0', '###', '', None, '', None, (3, 2, 0)),
- (None, '-', None, '######0', '###', '', None, '', None, (3, 2, 0))))
+ (None, '-', None, '######0', '###', '', None, '', None,
+ (3, 2, 0))))
self.assertEqual(
parseNumberPattern('##,##0.##;-##,##0.##'),
@@ -858,7 +864,8 @@ class TestNumberPatternParser(_TestCase):
self.assertEqual(
parseNumberPattern('##,##,##0.00;-##,##,##0.00'),
((None, '', None, '######0', '00', '', None, '', None, (3, 2, 0)),
- (None, '-', None, '######0', '00', '', None, '', None, (3, 2, 0))))
+ (None, '-', None, '######0', '00', '', None, '', None,
+ (3, 2, 0))))
self.assertEqual(
parseNumberPattern('###0.00;-###0.00'),
@@ -1329,7 +1336,7 @@ class TestNumberFormat(_TestCase):
def testFormatBadThousandSeparator(self):
self.assertRaises(ValueError,
- self.format.format, 23341, '0,')
+ self.format.format, 23341, '0,')
def testFormatDecimal(self):
self.assertEqual(self.format.format(23341.02357, '###0.0#'),
@@ -1348,7 +1355,6 @@ class TestNumberFormat(_TestCase):
self.assertEqual(self.format.format(1.9999, '0.000'), '2.000')
self.assertEqual(self.format.format(1.9999, '0.0000'), '1.9999')
-
def testFormatScientificDecimal(self):
self.assertEqual(self.format.format(23341.02357, '0.00####E00'),
'2.334102E04')
@@ -1426,27 +1432,27 @@ class TestNumberFormat(_TestCase):
def testFormatHighPrecisionNumbers(self):
self.assertEqual(
self.format.format(
- 1+1e-7, '(#0.00#####);(-#0.00#####)'),
+ 1 + 1e-7, '(#0.00#####);(-#0.00#####)'),
'(1.0000001)')
self.assertEqual(
self.format.format(
- 1+1e-7, '(#0.00###)'),
+ 1 + 1e-7, '(#0.00###)'),
'(1.00000)')
self.assertEqual(
self.format.format(
- 1+1e-9, '(#0.00#######);(-#0.00#######)'),
+ 1 + 1e-9, '(#0.00#######);(-#0.00#######)'),
'(1.000000001)')
self.assertEqual(
self.format.format(
- 1+1e-9, '(#0.00###)'),
+ 1 + 1e-9, '(#0.00###)'),
'(1.00000)')
self.assertEqual(
self.format.format(
- 1+1e-12, '(#0.00##########);(-#0.00##########)'),
+ 1 + 1e-12, '(#0.00##########);(-#0.00##########)'),
'(1.000000000001)')
self.assertEqual(
self.format.format(
- 1+1e-12, '(#0.00###)'),
+ 1 + 1e-12, '(#0.00###)'),
'(1.00000)')
def testNoRounding(self):
diff --git a/src/zope/i18n/tests/test_gettextmessagecatalog.py b/src/zope/i18n/tests/test_gettextmessagecatalog.py
index 07a0932..5bd93df 100644
--- a/src/zope/i18n/tests/test_gettextmessagecatalog.py
+++ b/src/zope/i18n/tests/test_gettextmessagecatalog.py
@@ -27,6 +27,5 @@ class GettextMessageCatalogTest(test_imessagecatalog.TestIMessageCatalog):
catalog = GettextMessageCatalog('en', 'default', self._path)
return catalog
-
def _getUniqueIndentifier(self):
return self._path
diff --git a/src/zope/i18n/tests/test_imessagecatalog.py b/src/zope/i18n/tests/test_imessagecatalog.py
index 803d4f0..36fb43c 100644
--- a/src/zope/i18n/tests/test_imessagecatalog.py
+++ b/src/zope/i18n/tests/test_imessagecatalog.py
@@ -21,7 +21,6 @@ from zope.schema import getValidationErrors
class TestIMessageCatalog(unittest.TestCase):
-
# This should be overridden by every class that inherits this test
def _getMessageCatalog(self):
raise NotImplementedError()
@@ -29,7 +28,6 @@ class TestIMessageCatalog(unittest.TestCase):
def _getUniqueIndentifier(self):
raise NotImplementedError()
-
def setUp(self):
self._catalog = self._getMessageCatalog()
@@ -63,4 +61,4 @@ class TestIMessageCatalog(unittest.TestCase):
def test_suite():
- return unittest.TestSuite() # Deliberately empty
+ return unittest.TestSuite() # Deliberately empty
diff --git a/src/zope/i18n/tests/test_itranslationdomain.py b/src/zope/i18n/tests/test_itranslationdomain.py
index 79c385f..bea5360 100644
--- a/src/zope/i18n/tests/test_itranslationdomain.py
+++ b/src/zope/i18n/tests/test_itranslationdomain.py
@@ -22,22 +22,22 @@ from zope.component.testing import PlacelessSetup
from zope.schema import getValidationErrors
+from zope.i18n._compat import text_type
from zope.i18n.negotiator import negotiator
from zope.i18n.interfaces import INegotiator, IUserPreferredLanguages
from zope.i18n.interfaces import ITranslationDomain
-text_type = str if bytes is not str else unicode
@implementer(IUserPreferredLanguages)
class Environment(object):
-
def __init__(self, langs=()):
self.langs = langs
def getPreferredLanguages(self):
return self.langs
+
class TestITranslationDomain(PlacelessSetup):
# This should be overwritten by every class that inherits this test
@@ -108,4 +108,4 @@ class TestITranslationDomain(PlacelessSetup):
def test_suite():
- return unittest.TestSuite() # Deliberately empty
+ return unittest.TestSuite() # Deliberately empty
diff --git a/src/zope/i18n/tests/test_negotiator.py b/src/zope/i18n/tests/test_negotiator.py
index f8a0336..9267332 100644
--- a/src/zope/i18n/tests/test_negotiator.py
+++ b/src/zope/i18n/tests/test_negotiator.py
@@ -20,6 +20,7 @@ from zope.i18n.interfaces import IUserPreferredLanguages
from zope.component.testing import PlacelessSetup
from zope.interface import implementer
+
@implementer(IUserPreferredLanguages)
class Env(object):
@@ -39,12 +40,12 @@ class NegotiatorTest(PlacelessSetup, unittest.TestCase):
def test_findLanguages(self):
_cases = (
- (('en','de'), ('en','de','fr'), 'en'),
- (('en'), ('it','de','fr'), None),
- (('pt-br','de'), ('pt_BR','de','fr'), 'pt_BR'),
- (('pt-br','en'), ('pt', 'en', 'fr'), 'pt'),
- (('pt-br','en-us', 'de'), ('de', 'en', 'fr'), 'en'),
- )
+ (('en', 'de'), ('en', 'de', 'fr'), 'en'),
+ (('en'), ('it', 'de', 'fr'), None),
+ (('pt-br', 'de'), ('pt_BR', 'de', 'fr'), 'pt_BR'),
+ (('pt-br', 'en'), ('pt', 'en', 'fr'), 'pt'),
+ (('pt-br', 'en-us', 'de'), ('de', 'en', 'fr'), 'en'),
+ )
for user_pref_langs, obj_langs, expected in _cases:
env = Env(user_pref_langs)
@@ -55,7 +56,4 @@ class NegotiatorTest(PlacelessSetup, unittest.TestCase):
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(NegotiatorTest),
- ))
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
+ ))
diff --git a/src/zope/i18n/tests/test_plurals.py b/src/zope/i18n/tests/test_plurals.py
index d4f57c7..bc97bf3 100644
--- a/src/zope/i18n/tests/test_plurals.py
+++ b/src/zope/i18n/tests/test_plurals.py
@@ -160,18 +160,18 @@ class TestPlurals(unittest.TestCase):
self.assertEqual(catalog.getPluralMessage(
'The item is rated 1/5 star.',
'The item is rated %s/5 stars.', 3.5),
- 'The item is rated 3.5/5 stars.')
+ 'The item is rated 3.5/5 stars.')
# It's cast either to an int or a float because of the %s in
# the translation string.
self.assertEqual(catalog.getPluralMessage(
'There is %d chance.',
'There are %f chances.', 1.5),
- 'There are 1.500000 chances.')
+ 'There are 1.500000 chances.')
self.assertEqual(catalog.getPluralMessage(
'There is %d chance.',
'There are %f chances.', 3.5),
- 'There are 3.500000 chances.')
+ 'There are 3.500000 chances.')
def test_translate_without_defaults(self):
domain = self._getTranslationDomain('en')
diff --git a/src/zope/i18n/tests/test_simpletranslationdomain.py b/src/zope/i18n/tests/test_simpletranslationdomain.py
index 7c87c40..2f233d6 100644
--- a/src/zope/i18n/tests/test_simpletranslationdomain.py
+++ b/src/zope/i18n/tests/test_simpletranslationdomain.py
@@ -42,7 +42,3 @@ def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestSimpleTranslationDomain))
return suite
-
-
-if __name__ == '__main__':
- unittest.TextTestRunner().run(test_suite())
diff --git a/src/zope/i18n/tests/test_testmessagecatalog.py b/src/zope/i18n/tests/test_testmessagecatalog.py
index ce5adc6..e5c8e57 100644
--- a/src/zope/i18n/tests/test_testmessagecatalog.py
+++ b/src/zope/i18n/tests/test_testmessagecatalog.py
@@ -15,10 +15,8 @@
import unittest
import doctest
+
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite('../testmessagecatalog.rst')
))
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
diff --git a/src/zope/i18n/tests/test_translationdomain.py b/src/zope/i18n/tests/test_translationdomain.py
index f8efbd9..550ec85 100644
--- a/src/zope/i18n/tests/test_translationdomain.py
+++ b/src/zope/i18n/tests/test_translationdomain.py
@@ -18,7 +18,7 @@ import os
from zope.i18n.translationdomain import TranslationDomain
from zope.i18n.gettextmessagecatalog import GettextMessageCatalog
from zope.i18n.tests.test_itranslationdomain import \
- TestITranslationDomain, Environment
+ TestITranslationDomain, Environment
from zope.i18nmessageid import MessageFactory
from zope.i18n.interfaces import ITranslationDomain
diff --git a/src/zope/i18n/tests/test_zcml.py b/src/zope/i18n/tests/test_zcml.py
index 52ee437..cd3d2b4 100644
--- a/src/zope/i18n/tests/test_zcml.py
+++ b/src/zope/i18n/tests/test_zcml.py
@@ -25,10 +25,10 @@ from zope.component.testing import PlacelessSetup
from zope.configuration import xmlconfig
import zope.i18n.tests
+from zope.i18n._compat import text_type
from zope.i18n.interfaces import ITranslationDomain
from zope.i18n import config
-text_type = str if bytes is not str else unicode
template = """\
<configure
@@ -37,6 +37,7 @@ template = """\
%s
</configure>"""
+
class DirectivesTest(PlacelessSetup, unittest.TestCase):
# This test suite needs the [zcml] and [compile] extra dependencies
@@ -156,7 +157,8 @@ class DirectivesTest(PlacelessSetup, unittest.TestCase):
xmlconfig.string(
template % '''
<configure package="zope.i18n.tests">
- <i18n:registerTranslations directory="locale3" domain="zope-i18n" />
+ <i18n:registerTranslations directory="locale3"
+ domain="zope-i18n" />
</configure>
''', self.context)
path = os.path.join(os.path.dirname(zope.i18n.tests.__file__),
diff --git a/src/zope/i18n/tests/testi18nawareobject.py b/src/zope/i18n/tests/testi18nawareobject.py
index 21a61f4..996416b 100644
--- a/src/zope/i18n/tests/testi18nawareobject.py
+++ b/src/zope/i18n/tests/testi18nawareobject.py
@@ -54,6 +54,7 @@ class I18nAwareContentObject(object):
#
############################################################
+
class AbstractTestII18nAwareMixin(object):
def setUp(self):
@@ -73,7 +74,8 @@ class AbstractTestII18nAwareMixin(object):
self.assertEqual(self.object.getDefaultLanguage(), 'lt')
def testGetAvailableLanguages(self):
- self.assertEqual(sorted(self.object.getAvailableLanguages()), ['en', 'fr', 'lt'])
+ self.assertEqual(sorted(self.object.getAvailableLanguages()), [
+ 'en', 'fr', 'lt'])
class TestI18nAwareObject(AbstractTestII18nAwareMixin, unittest.TestCase):
diff --git a/src/zope/i18n/translationdomain.py b/src/zope/i18n/translationdomain.py
index b9287f0..c62741b 100644
--- a/src/zope/i18n/translationdomain.py
+++ b/src/zope/i18n/translationdomain.py
@@ -19,6 +19,7 @@ import zope.interface
from zope.i18nmessageid import Message
from zope.i18n import translate, interpolate
+from zope.i18n._compat import text_type
from zope.i18n.interfaces import ITranslationDomain, INegotiator
@@ -32,8 +33,6 @@ from zope.i18n.interfaces import ITranslationDomain, INegotiator
# message in a catalog is not translated, tough luck, you get the msgid.
LANGUAGE_FALLBACKS = ['en']
-text_type = str if bytes is not str else unicode
-
@zope.interface.implementer(ITranslationDomain)
class TranslationDomain(object):
@@ -85,7 +84,7 @@ class TranslationDomain(object):
msgid_plural, default_plural, number)
def _recursive_translate(self, msgid, mapping, target_language, default,
- context, msgid_plural, default_plural, number,
+ context, msgid_plural, default_plural, number,
seen=None):
"""Recursively translate msg."""
# MessageID attributes override arguments
diff --git a/src/zope/i18n/zcml.py b/src/zope/i18n/zcml.py
index 4df9ccb..6934485 100644
--- a/src/zope/i18n/zcml.py
+++ b/src/zope/i18n/zcml.py
@@ -45,14 +45,14 @@ class IRegisterTranslationsDirective(Interface):
title=u"Directory",
description=u"Directory containing the translations",
required=True
- )
+ )
domain = TextLine(
title=u"Domain",
description=(u"Translation domain to register. If not specified, "
u"all domains found in the directory are registered"),
required=False
- )
+ )
def allow_language(lang):
diff --git a/tox.ini b/tox.ini
index 757b7d4..da82bb1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,32 +1,86 @@
+# Generated from:
+# https://github.com/zopefoundation/meta/tree/master/config/pure-python
[tox]
+minversion = 3.18
envlist =
- py27,py35,py36,py37,pypy,pypy3,coverage,docs
+ lint
+ py27
+ py35
+ py36
+ py37
+ py38
+ py39
+ pypy
+ pypy3
+ docs
+ coverage
[testenv]
+usedevelop = true
+deps =
+ # Until repoze.sphinx.autointerface supports Sphinx 4.x we cannot use it:
+ Sphinx < 4
commands =
- zope-testrunner --test-path=src {posargs:-pvc}
- sphinx-build -b doctest -d {envdir}/doctrees docs {envdir}/doctest
+ zope-testrunner --test-path=src {posargs:-vc}
+ !py27-!pypy: sphinx-build -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest
extras =
test
docs
- compile
-[testenv:coverage]
-usedevelop = true
-basepython =
- python3.6
-commands =
- coverage erase
- coverage run -p -m zope.testrunner --test-path=src {posargs:-pvc}
- coverage run -p -m sphinx -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest
- coverage combine
- coverage report --fail-under=100
+[testenv:lint]
+basepython = python3
+skip_install = true
deps =
- coverage
-parallel_show_output = true
+ flake8
+ check-manifest
+ check-python-versions
+ wheel
+commands =
+ flake8 src setup.py
+ check-manifest
+ check-python-versions
[testenv:docs]
-basepython =
- python3.6
+basepython = python3
+skip_install = false
+# Until repoze.sphinx.autointerface supports Sphinx 4.x we cannot use it:
+deps = Sphinx < 4
+commands_pre =
commands =
sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
+ sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
+
+[testenv:coverage]
+basepython = python3
+allowlist_externals =
+ mkdir
+deps =
+ coverage
+ coverage-python-version
+ # Until repoze.sphinx.autointerface supports Sphinx 4.x we cannot use it:
+ Sphinx < 4
+commands =
+ mkdir -p {toxinidir}/parts/htmlcov
+ coverage run -m zope.testrunner --test-path=src {posargs:-vc}
+ coverage run -a -m sphinx -b doctest -d {envdir}/.cache/doctrees docs {envdir}/.cache/doctest
+ coverage html
+ coverage report -m --fail-under=99
+
+[coverage:run]
+branch = True
+plugins = coverage_python_version
+source = zope.i18n
+
+[coverage:report]
+precision = 2
+exclude_lines =
+ pragma: no cover
+ pragma: nocover
+ except ImportError:
+ raise NotImplementedError
+ if __name__ == '__main__':
+ self.fail
+ raise AssertionError
+
+[coverage:html]
+directory = parts/htmlcov