diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 72d83d7..837af39 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,13 +1,17 @@ name: Test on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + jobs: tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: [3.7, 3.8, 3.9, '3.10', '3.11', '3.12-dev'] + python-version: [3.7, 3.8, 3.9, '3.10', '3.11', '3.12', '3.13'] max-parallel: 1 steps: @@ -17,30 +21,24 @@ jobs: run: echo $GITHUB_CONTEXT - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 1 - name: pycache - uses: actions/cache@v3 + uses: actions/cache@v4 id: pycache with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- - name: Setup python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - if: "!endsWith(matrix.python-version, '-dev')" - with: - python-version: ${{ matrix.python-version }} - - - name: Setup python ${{ matrix.python-version }} (via deadsnakes) - uses: deadsnakes/action@v2.1.1 - if: "endsWith(matrix.python-version, '-dev')" + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + allow-prereleases: true - name: Install tox and test related run: | @@ -66,12 +64,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 1 - name: Setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 diff --git a/.gitignore b/.gitignore index 525f2ef..c1f2627 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,173 @@ -*.egg-info -__pycache__ -doc/locale -doc/_build +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# End of https://www.toptal.com/developers/gitignore/api/python \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..6d438d7 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,15 @@ +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3" + +sphinx: + configuration: doc/conf.py + +python: + install: + - requirements: doc/requirements.txt + - method: pip + path: . diff --git a/CHANGES.rst b/CHANGES.rst index 561b300..781b05d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,47 @@ CHANGES ======= +2.2.0 (2024/04/20) +================== + +Environments +------------ + +* Support for Python 3.13. + Update version in workflow actions and enable 3.13 by @rffontenelle in https://github.com/sphinx-doc/sphinx-intl/pull/97 + +Incompatibility +--------------- + +Features +-------- + +* Set resource_name in .tx/config files to preserve resource naming behavior prior to 2.1.0 by @jpmckinney in https://github.com/sphinx-doc/sphinx-intl/pull/90 +* Support no obsolete option by @jdknight in https://github.com/sphinx-doc/sphinx-intl/pull/65 + +Bug Fixes +--------- + +* Correctly format hyperlink by @DelazJ in https://github.com/sphinx-doc/sphinx-intl/pull/82 +* Fixed errors in the `~/.transifexrc` file generated by the `create-transifexrc` command by @shimizukawa in https://github.com/sphinx-doc/sphinx-intl/pull/83 + Note: ``create-transifexrc`` command and ``SPHINXINTL_TRANSIFEX_TOKEN`` have been deprecated. They will be removed after six months. Please use the TX_TOKEN environment variable instead. + +Documentation +------------- + +* refs #93 documentation for sphinx-intl comman-line options by @shimizukawa in https://github.com/sphinx-doc/sphinx-intl/pull/94 + +Internals +--------- + +* drop six usage by @jdknight in https://github.com/sphinx-doc/sphinx-intl/pull/87 +* Add pyproject.toml, remove setup.py by @rffontenelle in https://github.com/sphinx-doc/sphinx-intl/pull/86 +* Drop six from pyproject.toml by @rffontenelle in https://github.com/sphinx-doc/sphinx-intl/pull/92 +* add readthedocs conf by @shimizukawa in https://github.com/sphinx-doc/sphinx-intl/pull/95 +* test: Update expectations for tx > 1.6.7 by @jpmckinney in https://github.com/sphinx-doc/sphinx-intl/pull/96 +* Cache using hash from pyproject.toml instead of setup.py by @rffontenelle in https://github.com/sphinx-doc/sphinx-intl/pull/99 + + 2.1.0 (2023/02/05) ================== diff --git a/checklist.rst b/checklist.rst index 9259885..465caa1 100644 --- a/checklist.rst +++ b/checklist.rst @@ -4,7 +4,7 @@ Procedure: 1. check GitHub Actions test results: https://github.com/sphinx-doc/sphinx-intl/actions 2. update release version/date in ``CHANGES.rst`` -3. ``python setup.py release sdist bdist_egg`` +3. ``python setup.py release sdist bdist_wheel`` 4. ``twine upload dist/`` 5. check PyPI page: https://pypi.org/p/sphinx-intl 6. tagging with version name that MUST following semver. e.g.: ``git tag 1.0.1`` diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..e35d885 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1 @@ +_build diff --git a/doc/basic.rst b/doc/basic.rst index 8c1c3bb..ec0452a 100644 --- a/doc/basic.rst +++ b/doc/basic.rst @@ -38,5 +38,5 @@ It is strongly recommended to use virtualenv/venv for this procedure:: $ pip install sphinx-intl If you want to use `Optional Features`_, you need install Transifex CLI tool. -Please refer to `Installation instructions `. +Please refer to `Installation instructions `_. diff --git a/doc/conf.py b/doc/conf.py index 386b99d..74d214d 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -13,11 +13,12 @@ # -- General configuration --------------------------------------------------- extensions = [ + "sphinx_click", ] source_suffix = '.rst' master_doc = 'index' -language = None +language = 'en' exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] pygments_style = 'sphinx' diff --git a/doc/refs.rst b/doc/refs.rst index 379bba3..db16c89 100644 --- a/doc/refs.rst +++ b/doc/refs.rst @@ -10,12 +10,16 @@ Commands Type `sphinx-intl` without arguments, options to show command help. +.. click:: sphinx_intl.commands:main + :prog: sphinx-intl + :nested: full + Environment Variables ===================== All command-line options can be set with environment variables using the -format SPHINXINTL_ . Dashes (-) have to be replaced with +format ``SPHINXINTL_``. Dashes (-) have to be replaced with underscores (_). For example, to set the languages:: diff --git a/doc/requirements.txt b/doc/requirements.txt index 8213302..8fbe6f7 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,2 +1,3 @@ sphinx -sphinx_rtd_theme +sphinx_rtd_theme>=1.3 +sphinx-click diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..aa72909 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,63 @@ +[project] +name = "sphinx-intl" +dynamic = ['version'] +authors = [ + { name = "Takayuki SHIMIZUKAWA", email = "shimizukawa@gmail.com" } +] +description = "Sphinx utility that make it easy to translate and to apply translation." +readme = "README.rst" +requires-python = ">=3.7" +license = {file = "LICENSE"} +dependencies = [ + "setuptools", + "click", + "babel", + "sphinx", +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "License :: OSI Approved :: BSD License", + "Topic :: Documentation", + "Topic :: Documentation :: Sphinx", + "Topic :: Software Development", + "Topic :: Software Development :: Documentation", + "Topic :: Text Processing", + "Topic :: Text Processing :: General", + "Topic :: Utilities", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "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", + "Framework :: Sphinx", +] + +[project.optional-dependencies] +test = [ + "pytest", + "mock", +] + +[project.urls] +Homepage = "https://github.com/sphinx-doc/sphinx-intl" +Documentation = "https://sphinx-intl.readthedocs.io" + +[project.scripts] +sphinx-intl = "sphinx_intl.commands:main" + +[tool.setuptools] +zip-safe = false +include-package-data = true + +[tool.setuptools.dynamic] +version = {attr = "sphinx_intl.__version__"} + +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 4d35a22..4c49585 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,18 +1,17 @@ -[egg_info] -tag_build = dev -tag_date = true +# to bulid release: +# 1. COMMENT OUT `[egg_info]` section +# 2. $ pip install build +# 3. $ python -m build -[build] -build-base = _build +# to test upload: +# $ twine upload --repository-url https://test.pypi.org/legacy/ dist/* -[sdist] -formats = gztar +# to production upload: +# $ twine upload dist/* -[aliases] -release = check -r -s egg_info -Db '' -# to bulid release: python setup.py release sdist bdist_wheel -# to test upload: twine upload --repository-url https://test.pypi.org/legacy/ dist/* -# to production upload: twine upload dist/* +[egg_info] +tag_build = dev +tag_date = true [flake8] ;show-pep8=true diff --git a/setup.py b/setup.py deleted file mode 100644 index acc5c93..0000000 --- a/setup.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -from setuptools import setup, find_packages -import os - -from sphinx_intl import __version__ - -install_requires = [ - 'setuptools', - 'click', - 'babel', - 'sphinx', -] - -extras_require = { - 'test': [ - 'pytest', - 'mock', - 'six', - ], -} - -here = os.path.abspath(os.path.dirname(__file__)) -with open(os.path.join(here, 'README.rst')) as f: - README = f.read() -description = \ - 'Sphinx utility that make it easy to translate and to apply translation.' - -setup( - name='sphinx-intl', - version=__version__, - description=description, - long_description=README, - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "License :: OSI Approved :: BSD License", - "Topic :: Documentation", - "Topic :: Documentation :: Sphinx", - "Topic :: Software Development", - "Topic :: Software Development :: Documentation", - "Topic :: Text Processing", - "Topic :: Text Processing :: General", - "Topic :: Utilities", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "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", - "Framework :: Sphinx", - ], - author="Takayuki SHIMIZUKAWA", - author_email="shimizukawa@gmail.com", - url="https://github.com/sphinx-doc/sphinx-intl", - namespace_packages=[], - packages=find_packages(), - include_package_data=True, - zip_safe=False, - install_requires=install_requires, - python_requires=">=3.7", - extras_require=extras_require, - entry_points="""\ - [console_scripts] - sphinx-intl = sphinx_intl.commands:main - """, -) diff --git a/sphinx_intl/__init__.py b/sphinx_intl/__init__.py index 29cd3f4..5d153f2 100644 --- a/sphinx_intl/__init__.py +++ b/sphinx_intl/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -__version__ = '2.1.0' +__version__ = '2.2.0' diff --git a/sphinx_intl/basic.py b/sphinx_intl/basic.py index 6d30c99..fa035e3 100644 --- a/sphinx_intl/basic.py +++ b/sphinx_intl/basic.py @@ -22,14 +22,15 @@ def get_lang_dirs(path): # ================================== # commands -def update(locale_dir, pot_dir, languages, line_width=76): +def update(locale_dir, pot_dir, languages, line_width=76, ignore_obsolete=False): """ Update specified language's po files from pot. :param unicode locale_dir: path for locale directory :param unicode pot_dir: path for pot directory :param tuple languages: languages to update po files - :param number line_width: maximum line wdith of po files + :param number line_width: maximum line width of po files + :param bool ignore_obsolete: ignore obsolete entries in po files :return: {'create': 0, 'update': 0, 'notchanged': 0} :rtype: dict """ @@ -61,7 +62,8 @@ def update(locale_dir, pot_dir, languages, line_width=76): status['update'] += 1 click.echo('Update: {0} +{1}, -{2}'.format( po_file, len(added), len(deleted))) - c.dump_po(po_file, cat, line_width) + c.dump_po(po_file, cat, width=line_width, + ignore_obsolete=ignore_obsolete) else: status['notchanged'] += 1 click.echo('Not Changed: {0}'.format(po_file)) @@ -69,7 +71,8 @@ def update(locale_dir, pot_dir, languages, line_width=76): status['create'] += 1 click.echo('Create: {0}'.format(po_file)) cat_pot.locale = lang - c.dump_po(po_file, cat_pot, line_width) + c.dump_po(po_file, cat_pot, width=line_width, + ignore_obsolete=ignore_obsolete) return status diff --git a/sphinx_intl/catalog.py b/sphinx_intl/catalog.py index 714416c..f8642a3 100644 --- a/sphinx_intl/catalog.py +++ b/sphinx_intl/catalog.py @@ -6,10 +6,11 @@ from babel.messages import pofile, mofile -def load_po(filename): +def load_po(filename, **kwargs): """read po/pot file and return catalog object :param unicode filename: path to po/pot file + :param kwargs: keyword arguments to forward to babel's read_po call :return: catalog object """ # pre-read to get charset @@ -20,27 +21,36 @@ def load_po(filename): # To decode lines by babel, read po file as binary mode and specify charset for # read_po function. with io.open(filename, 'rb') as f: # FIXME: encoding VS charset - return pofile.read_po(f, charset=charset) + return pofile.read_po(f, charset=charset, **kwargs) -def dump_po(filename, catalog, line_width=76): +def dump_po(filename, catalog, **kwargs): """write po/pot file from catalog object :param unicode filename: path to po file :param catalog: catalog object - :param line_width: maximum line wdith of po files + :param kwargs: keyword arguments to forward to babel's write_po call; also + accepts a deprecated `line_width` option to forward to + write_po's `width` option :return: None """ dirname = os.path.dirname(filename) if not os.path.exists(dirname): os.makedirs(dirname) + # (compatibility) line_width was the original argument used to forward + # line width hints into write_po's `width` argument; if provided, + # set/override the width value + if 'line_width' in kwargs: + kwargs['width'] = kwargs['line_width'] + del kwargs['line_width'] + # Because babel automatically encode strings, file should be open as binary mode. with io.open(filename, 'wb') as f: - pofile.write_po(f, catalog, line_width) + pofile.write_po(f, catalog, **kwargs) -def write_mo(filename, catalog): +def write_mo(filename, catalog, **kwargs): """write mo file from catalog object :param unicode filename: path to mo file @@ -51,7 +61,7 @@ def write_mo(filename, catalog): if not os.path.exists(dirname): os.makedirs(dirname) with io.open(filename, 'wb') as f: - mofile.write_mo(f, catalog) + mofile.write_mo(f, catalog, **kwargs) def translated_entries(catalog): diff --git a/sphinx_intl/commands.py b/sphinx_intl/commands.py index fb378f1..d14fa33 100644 --- a/sphinx_intl/commands.py +++ b/sphinx_intl/commands.py @@ -9,6 +9,7 @@ """ import re import os +import warnings from glob import glob import click @@ -111,14 +112,14 @@ def convert(self, value, param, ctx): option_tag = click.option( '-t', '--tag', envvar=ENVVAR_PREFIX + '_TAG', - type=TAGS, default=(), metavar='', show_default=True, + type=TAGS, metavar='', show_default=True, multiple=True, help="Pass tags to conf.py, as same as passed to sphinx-build -t option.") option_language = click.option( '-l', '--language', envvar=ENVVAR_PREFIX + '_LANGUAGE', - type=LANGUAGES, default=(), metavar='', show_default=True, + type=LANGUAGES, metavar='', show_default=True, multiple=True, help="Target language to update po files. Default is ALL.") @@ -130,11 +131,17 @@ def convert(self, value, param, ctx): help='The maximum line width for the po files, 0 or a negative number ' 'disable line wrapping') +option_no_obsolete = click.option( + '--no-obsolete', + envvar=ENVVAR_PREFIX + '_NO_OBSOLETE', + is_flag=True, default=False, + help='Remove obsolete #~ messages.') + option_transifex_token = click.option( '--transifex-token', envvar=ENVVAR_PREFIX + '_TRANSIFEX_TOKEN', type=str, metavar='', required=True, - help="Your transifex token.") + help="Your transifex token. (DEPRECATED)") option_transifex_organization_name = click.option( '--transifex-organization-name', @@ -161,20 +168,6 @@ def convert(self, value, param, ctx): @option_tag @click.pass_context def main(ctx, config, tag): - """ - Environment Variables: - All command-line options can be set with environment variables using the - format SPHINXINTL_ . Dashes (-) have to replaced with - underscores (_). - - For example, to set the languages: - - export SPHINXINTL_LANGUAGE=de,ja - - This is the same as passing the option to txutil directly: - - sphinx-intl update --language=de --language=ja - """ # load conf.py ctx.config = config if ctx.config is None: @@ -234,7 +227,8 @@ def main(ctx, config, tag): @option_pot_dir @option_language @option_line_width -def update(locale_dir, pot_dir, language, line_width): +@option_no_obsolete +def update(locale_dir, pot_dir, language, line_width, no_obsolete): """ Update specified language's po files from pot. @@ -260,7 +254,7 @@ def update(locale_dir, pot_dir, language, line_width): % locals()) raise click.BadParameter(msg, param_hint='language') - basic.update(locale_dir, pot_dir, languages, line_width) + basic.update(locale_dir, pot_dir, languages, line_width, ignore_obsolete=no_obsolete) @main.command() @@ -300,8 +294,16 @@ def stat(locale_dir, language): @option_transifex_token def create_transifexrc(transifex_token): """ - Create `$HOME/.transifexrc` + Create `$HOME/.transifexrc` (deprecated) """ + warnings.filterwarnings("default", category=DeprecationWarning) + warnings.warn( + ( + "create-transifexrc command and SPHINXINTL_TRANSIFEX_TOKEN " + "will be deprecated. Please use TX_TOKEN environment variable instead." + ), + category=DeprecationWarning, + ) transifex.create_transifexrc(transifex_token) diff --git a/sphinx_intl/transifex.py b/sphinx_intl/transifex.py index d7ebd74..820cf4b 100644 --- a/sphinx_intl/transifex.py +++ b/sphinx_intl/transifex.py @@ -29,7 +29,7 @@ TRANSIFEXRC_TEMPLATE = """\ [https://www.transifex.com] -rest_hostname = https://www.transifex.com +rest_hostname = https://rest.api.transifex.com token = %(transifex_token)s """ @@ -153,7 +153,8 @@ def update_txconfig_resources(transifex_organization_name, transifex_project_nam 'add', '--organization', '%(transifex_organization_name)s', '--project', '%(transifex_project_name)s', - '--resource', '%(resource_name)s', + '--resource', '%(resource_slug)s', + '--resource-name', '%(resource_name)s', '--file-filter', '%(locale_dir)s//LC_MESSAGES/%(resource_path)s.po', '--type', 'PO', '%(pot_dir)s/%(resource_path)s.pot', @@ -174,7 +175,7 @@ def update_txconfig_resources(transifex_organization_name, transifex_project_nam ) as progress_bar: for pot_path in progress_bar: resource_path = str(pot_path.relative_to(pot_dir).with_suffix('')) - resource_name = normalize_resource_name(resource_path) + resource_slug = resource_name = normalize_resource_name(resource_path) pot = load_po(str(pot_path)) if len(pot): lv = locals() diff --git a/tests/path.py b/tests/path.py index 7edd62a..480eb08 100755 --- a/tests/path.py +++ b/tests/path.py @@ -12,23 +12,11 @@ import shutil from codecs import open -from six import text_type - FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding() -class path(text_type): - """ - Represents a path which behaves like a string. - """ - if sys.version_info < (3, 0): - def __new__(cls, s, encoding=FILESYSTEMENCODING, errors='strict'): - if isinstance(s, str): - s = s.decode(encoding, errors) - return text_type.__new__(cls, s) - return text_type.__new__(cls, s) - +class path(str): @property def parent(self): """ @@ -195,4 +183,4 @@ def joinpath(self, *args): __div__ = __truediv__ = joinpath def __repr__(self): - return '%s(%s)' % (self.__class__.__name__, text_type.__repr__(self)) + return '%s(%s)' % (self.__class__.__name__, str.__repr__(self)) diff --git a/tests/test_cmd_pot.py b/tests/test_cmd_pot.py index 865a3b0..2ef062d 100644 --- a/tests/test_cmd_pot.py +++ b/tests/test_cmd_pot.py @@ -8,6 +8,9 @@ :copyright: Copyright 2019 by Takayuki SHIMIZUKAWA. :license: BSD, see LICENSE for details. """ + +import os + from click.testing import CliRunner from sphinx_intl import commands @@ -67,6 +70,83 @@ def test_update_difference_detect(temp): assert r4.output.count('Not Changed:') == 1 +def test_update_line_width(temp): + with open('_build/locale/README.pot', 'r') as f: + template = f.read() + + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + f.write('\nmsgid "foorbar identifier1"\nmsgstr ""\n') + + po_dir = os.path.join('locale', 'ja', 'LC_MESSAGES') + po_file = os.path.join(po_dir, 'README.po') + + r1 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-l', 'ja']) + assert r1.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert '"foorbar identifier1"\n' in contents + + # change the identifier to trigger an update and impose a lower line-width count + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + f.write('\nmsgid "foorbar identifier2"\nmsgstr ""\n') + + r2 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-w', '1']) + assert r2.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert '"foorbar"\n' in contents + assert '"identifier2"\n' in contents + + +def test_update_no_obsolete(temp): + with open('_build/locale/README.pot', 'r') as f: + template = f.read() + + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + f.write('\nmsgid "foorbar1"\nmsgstr ""\n') + f.write('\nmsgid "foorbar2"\nmsgstr ""\n') + + po_dir = os.path.join('locale', 'ja', 'LC_MESSAGES') + po_file = os.path.join(po_dir, 'README.po') + + r1 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-l', 'ja']) + assert r1.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert '\nmsgid "foorbar1"\n' in contents + assert '\nmsgid "foorbar2"\n' in contents + + # remove the foorbar2 and verify we can see the obsolete entry + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + f.write('\nmsgid "foorbar1"\nmsgstr ""\n') + + r2 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale']) + assert r2.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert '\n#~ msgid "foorbar2"\n' in contents + + # remove the foorbar1 and verify we can no longer see any obsolete entry + with open('_build/locale/README.pot', 'w') as f: + f.write(template) + + r3 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '--no-obsolete']) + assert r3.exit_code == 0 + + with open(po_file, 'r') as f: + contents = f.read() + assert 'msgid "foorbar1"' not in contents + assert 'msgid "foorbar2"' not in contents + + def test_stat(temp): r1 = runner.invoke(commands.update, ['-d', 'locale', '-p', '_build/locale', '-l', 'ja']) assert r1.exit_code == 0 diff --git a/tests/test_cmd_transifex.py b/tests/test_cmd_transifex.py index c3f28e7..9b4e174 100644 --- a/tests/test_cmd_transifex.py +++ b/tests/test_cmd_transifex.py @@ -73,6 +73,7 @@ def test_update_txconfig_resources_with_config(home_in_temp, temp): data = (tx_dir / 'config').text() assert re.search(r'\[o:eggs-org:p:ham-project:r:README\]', data) + assert re.search(r'\nresource_name += README\n', data) def test_update_txconfig_resources_with_pot_dir_argument(home_in_temp, temp): @@ -95,6 +96,7 @@ def test_update_txconfig_resources_with_pot_dir_argument(home_in_temp, temp): data = (tx_dir / 'config').text().replace('\\', '/') assert re.search(r'\[o:eggs-org:p:ham-project:r:README\]', data) + assert re.search(r'\nresource_name += README\n', data) assert re.search(r'source_file\W*=\W*_build/locale/README.pot', data) @@ -118,6 +120,7 @@ def test_update_txconfig_resources_with_project_name_including_dots(home_in_temp data = (tx_dir / 'config').text() assert re.search(r'\[o:eggs-org:p:ham-projectcom:r:README\]', data) + assert re.search(r'\nresource_name += README\n', data) def test_update_txconfig_resources_with_project_name_including_spaces(home_in_temp, temp): @@ -140,6 +143,7 @@ def test_update_txconfig_resources_with_project_name_including_spaces(home_in_te data = (tx_dir / 'config').text() assert re.search(r'\[o:eggs-org:p:ham-project-com:r:README\]', data) + assert re.search(r'\nresource_name += README\n', data) def test_update_txconfig_resources_with_potfile_including_symbols(home_in_temp, temp): @@ -170,3 +174,33 @@ def test_update_txconfig_resources_with_potfile_including_symbols(home_in_temp, data = (tx_dir / 'config').text() assert re.search(r'\[o:eggs-org:p:ham-project-com:r:example_document\]', data) assert re.search(r'\[o:eggs-org:p:ham-project-com:r:test_document\]', data) + assert re.search(r'\nresource_name += example_document\n', data) + assert re.search(r'\nresource_name += test_document\n', data) + + +def test_update_txconfig_resources_with_potfile_including_path_separators(home_in_temp, temp): + tx_dir = temp / '.tx' + tx_dir.makedirs() + (tx_dir / 'config').write_text(dedent("""\ + [main] + host = https://www.transifex.com + """)) + + (temp / '_build' / 'locale').copytree(temp / 'locale' / 'pot') + + # copy README.pot to 'example document.pot' + readme = (temp / '_build' / 'locale' / 'README.pot').text() + (temp / '_build' / 'locale' / 'example').makedirs() + (temp / '_build' / 'locale' / 'example' / 'document.pot').write_text(readme) + + r1 = runner.invoke(commands.main, + ['update-txconfig-resources', + '-d', 'locale', + '--transifex-organization-name', 'eggs-org', + '--transifex-project-name', 'ham project com', + ]) + assert r1.exit_code == 0 + + data = (tx_dir / 'config').text() + assert re.search(r'\[o:eggs-org:p:ham-project-com:r:example--document\]', data) + assert re.search(r'\nresource_name += example--document\n', data) diff --git a/tox.ini b/tox.ini index ce50580..09df126 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{37,38,39,310,311,312-dev}, + py{37,38,39,310,311,312,313}, flake8, mypy @@ -11,7 +11,8 @@ python = 3.9: py39 3.10: py310 3.11: py311 - 3.12: py312-dev + 3.12: py312 + 3.13: py313 [testenv] deps=-e.[transifex,test]