Skip to content

Commit

Permalink
Merge pull request #39 from Pylons/tseaver-modernize_python_versions
Browse files Browse the repository at this point in the history
Modernize python versions
  • Loading branch information
tseaver authored Jun 2, 2024
2 parents b79dbee + 933f43b commit a8d0268
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 127 deletions.
13 changes: 7 additions & 6 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ jobs:

strategy:
matrix:
python: ["2.7", "3.6", "3.7", "3.8"]
python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]

name: Test Python ${{ matrix.python }}

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v1
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
allow-prereleases: true

- name: Install dependencies
run: |
Expand All @@ -32,14 +33,14 @@ jobs:
needs: [ test ]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v1
uses: actions/setup-python@v5
with:
python-version: 3.7
python-version: 3.12

- name: Install wheel
run: python -m pip install wheel --user
Expand Down
11 changes: 11 additions & 0 deletions changes.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
translationstring
=================

2.0 (unreleased)
----------------

- Add support for Python 3.8 - 3.13.

- Drop support for Python 2.7 and Python 3 < 3.8 (this release now adds
``python_requires='>=3.8'``, so that older Python versions won't install
it.

- Remove ``translationstring.compat`` module.

1.4 (2020-07-09)
----------------

Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# out serve to show the default value.

import sys, os, datetime
import pkg_resources
from importlib import metadata
import pylons_sphinx_themes

# General configuration
Expand Down Expand Up @@ -41,7 +41,7 @@
# other places throughout the built documents.
#
# The short X.Y version.
version = pkg_resources.get_distribution('translationstring').version
version = metadata.version('translationstring')
# The full version, including alpha/beta/rc tags.
release = version

Expand Down
16 changes: 10 additions & 6 deletions docs/glossary.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ Glossary
predefines a :term:`translation domain`.

Translation Domain
A string representing the "context" in which a particular
translation was made. For example the word "java" might be
translated differently if the translation domain is
"programming-languages" than would be if the translation domain
was "coffee". Every :term:`translation string` has an associated
translation domain.
A string representing the "domain" in which a particular
translation was made. Normally represents the project / package name
that defines the term. Every :term:`translation string` has an
associated translation domain.

Translation Context
An optional string added to help resolve translation ambiguities
associated with very short terms, such as those which appear in
GUI menus, etc. See:
https://www.gnu.org/software/gettext/manual/gettext.html#Contexts

Message Identifier
An unchanging string that is the identifier for a particular
Expand Down
20 changes: 15 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
'pylons-sphinx-themes',
]

testing_extras = [
'pytest',
'pytest-cov',
'coverage'
]

setup(
name='translationstring',
version='1.4dev',
Expand All @@ -27,19 +33,21 @@
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Internationalization",
"Topic :: Software Development :: Localization",
"License :: Repoze Public License",
],
python_requires='>=3.8',
keywords='i18n l10n internationalization localization gettext chameleon',
author="Chris McDonough, Agendaless Consulting",
author_email="[email protected]",
Expand All @@ -50,6 +58,8 @@
zip_safe=False,
test_suite="translationstring",
extras_require={
'test': testing_extras,
'testing': testing_extras,
'docs': docs_extras,
},
)
15 changes: 7 additions & 8 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
[tox]
envlist =
py27,py33,py34,py35,pypy,cover,docs
py38,py39,py310,py311,py312,py313,pypy3,cover,docs

[testenv]
commands =
python setup.py test -q
pytest
deps =
pytest
Babel

[testenv:cover]
basepython =
python2.7
python3.12
commands =
python setup.py nosetests --with-xunit --with-xcoverage
pytest --cov=translationstring
deps =
Babel
nose
coverage==3.4
nosexcover
pytest-cov

[testenv:docs]
basepython =
python2.7
python3.12
commands =
pip install translationstring[docs]
sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
Expand Down
63 changes: 29 additions & 34 deletions translationstring/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import re
from gettext import NullTranslations
from translationstring.compat import text_type
from translationstring.compat import string_types
from translationstring.compat import PY3

NAME_RE = r"[a-zA-Z][-a-zA-Z0-9_]*"

_interp_regex = re.compile(r'(?<!\$)(\$(?:(%(n)s)|{(%(n)s)}))'
% ({'n': NAME_RE}))

CONTEXT_MASK = text_type('%s\x04%s')
CONTEXT_MASK = '%s\x04%s'

class TranslationString(text_type):
class TranslationString(str):
"""
The constructor for a :term:`translation string`. A translation
string is a Unicode-like object that has some extra metadata.
Expand Down Expand Up @@ -63,13 +60,15 @@ class TranslationString(text_type):
"""
__slots__ = ('domain', 'context', 'default', 'mapping')

def __new__(self, msgid, domain=None, default=None, mapping=None, context=None):
def __new__(
self, msgid, domain=None, default=None, mapping=None, context=None
):

# NB: this function should never never lose the *original
# identity* of a non-``None`` but empty ``default`` value
# provided to it. See the comment in ChameleonTranslate.

self = text_type.__new__(self, msgid)
self = str.__new__(self, msgid)
if isinstance(msgid, self.__class__):
domain = domain or msgid.domain and msgid.domain[:]
context = context or msgid.context and msgid.context[:]
Expand All @@ -80,11 +79,11 @@ def __new__(self, msgid, domain=None, default=None, mapping=None, context=None):
mapping.setdefault(k, v)
else:
mapping = msgid.mapping.copy()
msgid = text_type(msgid)
msgid = str(msgid)
self.domain = domain
self.context = context
if default is None:
default = text_type(msgid)
default = str(msgid)
self.default = default
self.mapping = mapping
return self
Expand Down Expand Up @@ -129,7 +128,7 @@ def interpolate(self, translated=None):
if self.mapping and translated:
def replace(match):
whole, param1, param2 = match.groups()
return text_type(self.mapping.get(param1 or param2, whole))
return str(self.mapping.get(param1 or param2, whole))
translated = _interp_regex.sub(replace, translated)

return translated
Expand All @@ -138,7 +137,7 @@ def __reduce__(self):
return self.__class__, self.__getstate__()

def __getstate__(self):
return text_type(self), self.domain, self.default, self.mapping, self.context
return str(self), self.domain, self.default, self.mapping, self.context

def TranslationStringFactory(factory_domain):
""" Create a factory which will generate translation strings
Expand Down Expand Up @@ -214,15 +213,17 @@ def translate(msgid, domain=None, mapping=None, context=None,
# preserving ``default`` in the aforementioned case. So we
# spray these indignant comments all over this module. ;-)

if not isinstance(msgid, string_types):
if not isinstance(msgid, str):
if msgid is not None:
msgid = text_type(msgid)
msgid = str(msgid)
return msgid

tstring = msgid

if not hasattr(tstring, 'interpolate'):
tstring = TranslationString(msgid, domain, default, mapping, context)
tstring = TranslationString(
msgid, domain, default, mapping, context
)
if translator is None:
result = tstring.interpolate()
else:
Expand All @@ -236,10 +237,7 @@ def ugettext_policy(translations, tstring, domain, context):
""" A translator policy function which unconditionally uses the
``ugettext`` API on the translations object."""

if PY3: # pragma: no cover
_gettext = translations.gettext
else: # pragma: no cover
_gettext = translations.ugettext
_gettext = translations.gettext

if context:
# Workaround for http://bugs.python.org/issue2504?
Expand Down Expand Up @@ -267,10 +265,7 @@ def dugettext_policy(translations, tstring, domain, context):
if getattr(translations, 'dugettext', None) is not None:
translated = translations.dugettext(domain, msgid)
else:
if PY3: # pragma: no cover
_gettext = translations.gettext
else: # pragma: no cover
_gettext = translations.ugettext
_gettext = translations.gettext

translated = _gettext(msgid)
return tstring if translated == msgid else translated
Expand Down Expand Up @@ -305,14 +300,18 @@ def Translator(translations=None, policy=None):
policy = dugettext_policy
def translator(tstring, domain=None, mapping=None, context=None):
if not hasattr(tstring, 'interpolate'):
tstring = TranslationString(tstring, domain=domain, mapping=mapping, context=context)
tstring = TranslationString(
tstring, domain=domain, mapping=mapping, context=context
)
elif mapping:
if tstring.mapping:
new_mapping = tstring.mapping.copy()
new_mapping.update(mapping)
else:
new_mapping = mapping
tstring = TranslationString(tstring, domain=domain, mapping=new_mapping, context=context)
tstring = TranslationString(
tstring, domain=domain, mapping=new_mapping, context=context
)
translated = tstring
domain = domain or tstring.domain
context = context or tstring.context
Expand All @@ -329,10 +328,7 @@ def ungettext_policy(translations, singular, plural, n, domain, context):
""" A pluralizer policy function which unconditionally uses the
``ungettext`` API on the translations object."""

if PY3: # pragma: no cover
_gettext = translations.ngettext
else: # pragma: no cover
_gettext = translations.ungettext
_gettext = translations.ngettext

if context:
# Workaround for http://bugs.python.org/issue2504?
Expand All @@ -358,10 +354,7 @@ def dungettext_policy(translations, singular, plural, n, domain, context):
if getattr(translations, 'dungettext', None) is not None:
translated = translations.dungettext(domain, msgid, plural, n)
else:
if PY3: # pragma: no cover
_gettext = translations.ngettext
else: # pragma: no cover
_gettext = translations.ungettext
_gettext = translations.ngettext

translated = _gettext(msgid, plural, n)
return singular if translated == msgid else translated
Expand Down Expand Up @@ -400,9 +393,11 @@ def pluralizer(singular, plural, n, domain=None, mapping=None):
policy = dungettext_policy
if translations is None:
translations = NullTranslations()
def pluralizer(singular, plural, n, domain=None, mapping=None, context=None):
def pluralizer(
singular, plural, n, domain=None, mapping=None, context=None
):
""" Pluralize this object """
translated = text_type(
translated = str(
policy(translations, singular, plural, n, domain, context))
if translated and '$' in translated and mapping:
return TranslationString(translated, mapping=mapping).interpolate()
Expand Down
18 changes: 0 additions & 18 deletions translationstring/compat.py

This file was deleted.

Loading

0 comments on commit a8d0268

Please sign in to comment.