diff --git a/.ci/appveyor.yml b/.ci/appveyor.yml deleted file mode 100644 index 91758f415..000000000 --- a/.ci/appveyor.yml +++ /dev/null @@ -1,38 +0,0 @@ -# From https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor.yml - -environment: - global: - # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the - # /E:ON and /V:ON options are not enabled in the batch script intepreter - # See: https://stackoverflow.com/a/13751649 - CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\.ci\\run_with_env.cmd" - - matrix: - - PYTHON: "C:\\Python35" - PYTHON_VERSION: "3.5.x" - PYTHON_ARCH: "32" - - - PYTHON: "C:\\Python35-x64" - PYTHON_VERSION: "3.5.x" - PYTHON_ARCH: "64" - -branches: # Only build official branches, PRs are built anyway. - only: - - master - - /release.*/ - -install: - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - # Check that we have the expected version and architecture for Python - - "python --version" - - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" - # Build data files - - "pip install --upgrade pytest==4.3.1 pytest-cov==2.6.1 codecov freezegun==0.3.12" - - "pip install --editable ." - - "python setup.py import_cldr" - -build: false # Not a C# project, build stuff at the test step instead. - -test_script: - - "%CMD_IN_ENV% python -m pytest --cov=babel" - - "codecov" diff --git a/.ci/deploy.linux.sh b/.ci/deploy.linux.sh deleted file mode 100644 index 4d59382d7..000000000 --- a/.ci/deploy.linux.sh +++ /dev/null @@ -1,4 +0,0 @@ -set -x -set -e - -bash <(curl -s https://codecov.io/bash) diff --git a/.ci/deploy.osx.sh b/.ci/deploy.osx.sh deleted file mode 100644 index c44550eff..000000000 --- a/.ci/deploy.osx.sh +++ /dev/null @@ -1,4 +0,0 @@ -set -x -set -e - -echo "Due to a bug in codecov, coverage cannot be deployed for Mac builds." diff --git a/.ci/deps.linux.sh b/.ci/deps.linux.sh deleted file mode 100644 index 13cc9e1ef..000000000 --- a/.ci/deps.linux.sh +++ /dev/null @@ -1,4 +0,0 @@ -set -x -set -e - -echo "No dependencies to install for linux." diff --git a/.ci/deps.osx.sh b/.ci/deps.osx.sh deleted file mode 100644 index b52a84f6d..000000000 --- a/.ci/deps.osx.sh +++ /dev/null @@ -1,11 +0,0 @@ -set -e -set -x - -# Install packages with brew -brew update >/dev/null -brew outdated pyenv || brew upgrade --quiet pyenv - -# Install required python version for this build -pyenv install -ks $PYTHON_VERSION -pyenv global $PYTHON_VERSION -python --version diff --git a/.ci/run_with_env.cmd b/.ci/run_with_env.cmd deleted file mode 100644 index 0f5b8e097..000000000 --- a/.ci/run_with_env.cmd +++ /dev/null @@ -1,47 +0,0 @@ -:: To build extensions for 64 bit Python 3, we need to configure environment -:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) -:: -:: To build extensions for 64 bit Python 2, we need to configure environment -:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) -:: -:: 32 bit builds do not require specific environment configurations. -:: -:: Note: this script needs to be run with the /E:ON and /V:ON flags for the -:: cmd interpreter, at least for (SDK v7.0) -:: -:: More details at: -:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows -:: https://stackoverflow.com/a/13751649 -:: -:: Author: Olivier Grisel -:: License: CC0 1.0 Universal: https://creativecommons.org/publicdomain/zero/1.0/ -@ECHO OFF - -SET COMMAND_TO_RUN=%* -SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows - -SET MAJOR_PYTHON_VERSION="%PYTHON_VERSION:~0,1%" -IF %MAJOR_PYTHON_VERSION% == "2" ( - SET WINDOWS_SDK_VERSION="v7.0" -) ELSE IF %MAJOR_PYTHON_VERSION% == "3" ( - SET WINDOWS_SDK_VERSION="v7.1" -) ELSE ( - ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" - EXIT 1 -) - -IF "%PYTHON_ARCH%"=="64" ( - ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture - SET DISTUTILS_USE_SDK=1 - SET MSSdk=1 - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 -) ELSE ( - ECHO Using default MSVC build environment for 32 bit architecture - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 -) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..e9c411862 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-18.04, windows-2019, macos-10.15] + python-version: [3.6, 3.7, 3.8, 3.9, pypy3] + exclude: + - os: windows-2019 + python-version: pypy3 + # TODO: Remove this; see: + # https://github.com/actions/setup-python/issues/151 + # https://github.com/tox-dev/tox/issues/1704 + # https://foss.heptapod.net/pypy/pypy/-/issues/3331 + env: + BABEL_CLDR_NO_DOWNLOAD_PROGRESS: "1" + BABEL_CLDR_QUIET: "1" + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + python -m pip install tox tox-gh-actions==2.1.0 + - name: Run test via Tox + run: tox --skip-missing-interpreters + - uses: codecov/codecov-action@v1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1bebe7f87..000000000 --- a/.travis.yml +++ /dev/null @@ -1,74 +0,0 @@ -dist: xenial -language: python - -cache: - directories: - - cldr - - "$HOME/.cache/pip" - - "$HOME/.pyenv" - -matrix: - include: - - os: linux - python: 2.7 - env: - - PYTEST_VERSION=4.3.1 - - os: linux - python: 2.7 - env: - - CDECIMAL=m3-cdecimal - - PYTEST_VERSION=4.3.1 - - os: linux - dist: trusty - python: pypy - env: - - PYTEST_VERSION=4.3.1 - - os: linux - dist: trusty - python: pypy3 - env: - - PYTEST_VERSION=6.1.2 - - os: linux - python: 3.4 - env: - - PYTEST_VERSION=4.3.1 - - os: linux - python: 3.5 - env: - - PYTHON_TEST_FLAGS=-bb - - PYTEST_VERSION=6.1.2 - - os: linux - python: 3.6 - env: - - PYTEST_VERSION=6.1.2 - - os: linux - python: 3.7 - env: - - PYTEST_VERSION=6.1.2 - - os: linux - python: 3.8 - env: - - PYTEST_VERSION=6.1.2 - - os: linux - python: 3.9 - env: - - PYTEST_VERSION=6.1.2 -install: - - bash .ci/deps.${TRAVIS_OS_NAME}.sh - - pip install --upgrade pip - - pip install --upgrade $CDECIMAL pytest==$PYTEST_VERSION pytest-cov freezegun==0.3.12 'backports.zoneinfo;python_version>="3.6" and python_version<"3.9"' - - pip install --editable . - -script: - - make test-cov - - bash .ci/deploy.${TRAVIS_OS_NAME}.sh - -notifications: - email: false - irc: - channels: - - "chat.freenode.net#pocoo" - on_success: change - on_failure: always - use_notice: true - skip_join: true diff --git a/scripts/download_import_cldr.py b/scripts/download_import_cldr.py index 531a04c62..805772a16 100755 --- a/scripts/download_import_cldr.py +++ b/scripts/download_import_cldr.py @@ -75,12 +75,13 @@ def main(): cldr_path = os.path.join(repo, 'cldr', os.path.splitext(FILENAME)[0]) zip_path = os.path.join(cldr_dl_path, FILENAME) changed = False + show_progress = (False if os.environ.get("BABEL_CLDR_NO_DOWNLOAD_PROGRESS") else sys.stdout.isatty()) while not is_good_file(zip_path): log('Downloading \'%s\'', FILENAME) if os.path.isfile(zip_path): os.remove(zip_path) - urlretrieve(URL, zip_path, reporthook) + urlretrieve(URL, zip_path, (reporthook if show_progress else None)) changed = True print() common_path = os.path.join(cldr_path, 'common') diff --git a/scripts/import_cldr.py b/scripts/import_cldr.py index 8e5eebb95..0dbd39369 100755 --- a/scripts/import_cldr.py +++ b/scripts/import_cldr.py @@ -17,6 +17,7 @@ import os import re import sys +import logging try: from xml.etree import cElementTree as ElementTree @@ -62,16 +63,7 @@ def _text(elem): 'timeFormats': 'time_formats' } - -def log(message, *args): - if args: - message = message % args - sys.stderr.write(message + '\r\n') - sys.stderr.flush() - - -def error(message, *args): - log('ERROR: %s' % message, *args) +log = logging.getLogger("import_cldr") def need_conversion(dst_filename, data_dict, source_filename): @@ -182,10 +174,19 @@ def main(): '-j', '--json', dest='dump_json', action='store_true', default=False, help='also export debugging JSON dumps of locale data' ) + parser.add_option( + '-q', '--quiet', dest='quiet', action='store_true', default=bool(os.environ.get('BABEL_CLDR_QUIET')), + help='quiesce info/warning messages', + ) options, args = parser.parse_args() if len(args) != 1: parser.error('incorrect number of arguments') + + logging.basicConfig( + level=(logging.ERROR if options.quiet else logging.INFO), + ) + return process_data( srcdir=args[0], destdir=BABEL_PACKAGE_ROOT, @@ -383,8 +384,10 @@ def _process_local_datas(sup, srcdir, destdir, force=False, dump_json=False): territory = '001' # world regions = territory_containment.get(territory, []) - log('Processing %s (Language = %s; Territory = %s)', - filename, language, territory) + log.info( + 'Processing %s (Language = %s; Territory = %s)', + filename, language, territory, + ) locale_id = '_'.join(filter(None, [ language, @@ -392,6 +395,7 @@ def _process_local_datas(sup, srcdir, destdir, force=False, dump_json=False): ])) data['locale_id'] = locale_id + data['unsupported_number_systems'] = set() if locale_id in plural_rules: data['plural_form'] = plural_rules[locale_id] @@ -432,6 +436,13 @@ def _process_local_datas(sup, srcdir, destdir, force=False, dump_json=False): parse_character_order(data, tree) parse_measurement_systems(data, tree) + unsupported_number_systems_string = ', '.join(sorted(data.pop('unsupported_number_systems'))) + if unsupported_number_systems_string: + log.warning('%s: unsupported number systems were ignored: %s' % ( + locale_id, + unsupported_number_systems_string, + )) + write_datafile(data_filename, data, dump_json=dump_json) @@ -440,21 +451,14 @@ def _should_skip_number_elem(data, elem): Figure out whether the numbering-containing element `elem` is in a currently non-supported (i.e. currently non-Latin) numbering system. - If it is, a warning is raised. - - :param data: The root data element, for formatting the warning. + :param data: The root data element, for stashing the warning. :param elem: Element with `numberSystem` key :return: Boolean """ number_system = elem.get('numberSystem', 'latn') if number_system != 'latn': - log('%s: Unsupported number system "%s" in <%s numberSystem="%s">' % ( - data['locale_id'], - number_system, - elem.tag, - number_system, - )) + data['unsupported_number_systems'].add(number_system) return True return False @@ -686,7 +690,7 @@ def parse_calendar_date_formats(data, calendar): text_type(elem.findtext('dateFormat/pattern')) ) except ValueError as e: - error(e) + log.error(e) elif elem.tag == 'alias': date_formats = Alias(_translate_alias( ['date_formats'], elem.attrib['path']) @@ -706,7 +710,7 @@ def parse_calendar_time_formats(data, calendar): text_type(elem.findtext('timeFormat/pattern')) ) except ValueError as e: - error(e) + log.error(e) elif elem.tag == 'alias': time_formats = Alias(_translate_alias( ['time_formats'], elem.attrib['path']) @@ -725,7 +729,7 @@ def parse_calendar_datetime_skeletons(data, calendar): try: datetime_formats[type] = text_type(elem.findtext('dateTimeFormat/pattern')) except ValueError as e: - error(e) + log.error(e) elif elem.tag == 'alias': datetime_formats = Alias(_translate_alias( ['datetime_formats'], elem.attrib['path']) diff --git a/setup.py b/setup.py index 0032a3a05..adf3bb5d9 100755 --- a/setup.py +++ b/setup.py @@ -44,18 +44,16 @@ def run(self): 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - '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', 'Topic :: Software Development :: Libraries :: Python Modules', ], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', + python_requires='>=3.6', packages=['babel', 'babel.messages', 'babel.localtime'], include_package_data=True, install_requires=[ diff --git a/tests/test_dates.py b/tests/test_dates.py index 48ed05c49..8e693d34c 100644 --- a/tests/test_dates.py +++ b/tests/test_dates.py @@ -15,6 +15,7 @@ from datetime import date, datetime, time, timedelta import unittest +import freezegun import pytest import pytz from pytz import timezone @@ -809,19 +810,10 @@ def test_zh_TW_format(): assert dates.format_time(datetime(2016, 4, 8, 12, 34, 56), locale='zh_TW') == u'\u4e0b\u534812:34:56' -def test_format_current_moment(monkeypatch): - import datetime as datetime_module +def test_format_current_moment(): frozen_instant = datetime.utcnow() - - class frozen_datetime(datetime): - - @classmethod - def utcnow(cls): - return frozen_instant - - # Freeze time! Well, some of it anyway. - monkeypatch.setattr(datetime_module, "datetime", frozen_datetime) - assert dates.format_datetime(locale="en_US") == dates.format_datetime(frozen_instant, locale="en_US") + with freezegun.freeze_time(time_to_freeze=frozen_instant): + assert dates.format_datetime(locale="en_US") == dates.format_datetime(frozen_instant, locale="en_US") @pytest.mark.all_locales diff --git a/tox.ini b/tox.ini index 27faf5bbb..14b450ff8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,20 +1,25 @@ [tox] -envlist = py27, pypy, py34, py35, py36, py37, pypy3, py27-cdecimal +envlist = + py{36,37,38,39} + pypy3 [testenv] deps = - pytest==4.3.1;python_version<"3.5" - pytest==6.1.2;python_version>="3.5" + pytest pytest-cov - cdecimal: m3-cdecimal freezegun==0.3.12 - backports.zoneinfo;python_version>"3.6" and python_version<"3.9" + backports.zoneinfo;python_version<"3.9" + tzdata;sys_platform == 'win32' whitelist_externals = make -commands = make clean-cldr test -passenv = PYTHON_TEST_FLAGS +commands = make clean-cldr test-cov +passenv = + BABEL_* + PYTHON_* -[pep8] -ignore = E501,E731,W503 - -[flake8] -ignore = E501,E731,W503 +[gh-actions] +python = + pypy3: pypy3 + 3.6: py36 + 3.7: py37 + 3.8: py38 + 3.9: py39