From 2f179b6b5c1e2eb88240e3ec42ff9edeb946ea65 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 5 Mar 2020 23:19:42 +0200 Subject: [PATCH 01/10] Fix: AP style for 0 is 'zero' --- src/humanize/number.py | 7 ++++--- tests/test_number.py | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index ffe257f..650279c 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -106,16 +106,17 @@ def intword(value, format="%.1f"): def apnumber(value): - """For numbers 1-9, returns the number spelled out. Otherwise, returns the + """For numbers 0-9, returns the number spelled out. Otherwise, returns the number. This follows Associated Press style. This always returns a string unless the value was not int-able, unlike the Django filter.""" try: value = int(value) except (TypeError, ValueError): return value - if not 0 < value < 10: + if not 0 <= value < 10: return str(value) return ( + _("zero"), _("one"), _("two"), _("three"), @@ -125,7 +126,7 @@ def apnumber(value): _("seven"), _("eight"), _("nine"), - )[value - 1] + )[value] def fractional(value): diff --git a/tests/test_number.py b/tests/test_number.py index 4b827d3..cdb0d7c 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -86,6 +86,7 @@ def test_intword(test_args, expected): @pytest.mark.parametrize( "test_input, expected", [ + (0, "zero"), (1, "one"), (2, "two"), (4, "four"), From 3c13dc0d5a9f2ae81e8a945815772e1071576ccb Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 5 Mar 2020 23:22:40 +0200 Subject: [PATCH 02/10] Updates --- .github/PULL_REQUEST_TEMPLATE.md | 6 +++--- .pre-commit-config.yaml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 225fdc9..7fa46ec 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,6 +2,6 @@ Fixes # Changes proposed in this pull request: - * - * - * +* +* +* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50a676f..3883da1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v2.0.1 + rev: v2.1.0 hooks: - id: pyupgrade args: ["--py3-plus"] @@ -18,7 +18,7 @@ repos: additional_dependencies: [flake8-2020, flake8-implicit-str-concat] - repo: https://github.com/asottile/seed-isort-config - rev: v1.9.4 + rev: v2.1.0 hooks: - id: seed-isort-config From a2a8d83a967c433ed9df367f81fbc09891d6b457 Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 5 Mar 2020 23:36:58 +0200 Subject: [PATCH 03/10] Update docstring --- src/humanize/number.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index 650279c..f57588e 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -106,9 +106,15 @@ def intword(value, format="%.1f"): def apnumber(value): - """For numbers 0-9, returns the number spelled out. Otherwise, returns the - number. This follows Associated Press style. This always returns a string - unless the value was not int-able, unlike the Django filter.""" + """Converts an integer to Associated Press style. + + Args: + value (int, float, string): Integer to convert. + + Returns: + str: For numbers 0-9, the number spelled out. Otherwise, the number. This always + returns a string unless the value was not int-able, unlike the Django filter. + """ try: value = int(value) except (TypeError, ValueError): From db9678288054dba85f5ce8959c20cc2436f7a1fa Mon Sep 17 00:00:00 2001 From: Hugo Date: Thu, 5 Mar 2020 23:55:56 +0200 Subject: [PATCH 04/10] Show more than bytes for negative file sizes --- src/humanize/filesize.py | 15 ++++++++------- tests/test_filesize.py | 6 +++++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/humanize/filesize.py b/src/humanize/filesize.py index 754a9f3..c076563 100644 --- a/src/humanize/filesize.py +++ b/src/humanize/filesize.py @@ -10,7 +10,7 @@ def naturalsize(value, binary=False, gnu=False, format="%.1f"): - """Format a number of byteslike a human readable filesize (eg. 10 kB). By + """Format a number of bytes like a human readable filesize (eg. 10 kB). By default, decimal suffixes (kB, MB) are used. Passing binary=true will use binary suffixes (KiB, MiB) are used and the base will be 2**10 instead of 10**3. If ``gnu`` is True, the binary argument is ignored and GNU-style @@ -25,19 +25,20 @@ def naturalsize(value, binary=False, gnu=False, format="%.1f"): base = 1024 if (gnu or binary) else 1000 bytes = float(value) + abs_bytes = abs(bytes) - if bytes == 1 and not gnu: - return "1 Byte" - elif bytes < base and not gnu: + if abs_bytes == 1 and not gnu: + return "%d Byte" % bytes + elif abs_bytes < base and not gnu: return "%d Bytes" % bytes - elif bytes < base and gnu: + elif abs_bytes < base and gnu: return "%dB" % bytes for i, s in enumerate(suffix): unit = base ** (i + 2) - if bytes < unit and not gnu: + if abs_bytes < unit and not gnu: return (format + " %s") % ((base * bytes / unit), s) - elif bytes < unit and gnu: + elif abs_bytes < unit and gnu: return (format + "%s") % ((base * bytes / unit), s) if gnu: return (format + "%s") % ((base * bytes / unit), s) diff --git a/tests/test_filesize.py b/tests/test_filesize.py index 959ce88..871c61a 100644 --- a/tests/test_filesize.py +++ b/tests/test_filesize.py @@ -31,5 +31,9 @@ ([10 ** 26 * 30, True, False, "%.3f"], "2481.542 YiB"), ], ) -def test_naturaltime_minimum_unit_default(test_args, expected): +def test_naturalsize(test_args, expected): assert humanize.naturalsize(*test_args) == expected + + args_with_negative = test_args + args_with_negative[0] *= -1 + assert humanize.naturalsize(*args_with_negative) == "-" + expected From 8c56ba753fcb08d381b28113be1d0bd3c0bfad18 Mon Sep 17 00:00:00 2001 From: Hugo Date: Fri, 6 Mar 2020 00:03:45 +0200 Subject: [PATCH 05/10] Update docstring --- src/humanize/filesize.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/humanize/filesize.py b/src/humanize/filesize.py index c076563..694e264 100644 --- a/src/humanize/filesize.py +++ b/src/humanize/filesize.py @@ -10,12 +10,20 @@ def naturalsize(value, binary=False, gnu=False, format="%.1f"): - """Format a number of bytes like a human readable filesize (eg. 10 kB). By - default, decimal suffixes (kB, MB) are used. Passing binary=true will use - binary suffixes (KiB, MiB) are used and the base will be 2**10 instead of - 10**3. If ``gnu`` is True, the binary argument is ignored and GNU-style - (ls -sh style) prefixes are used (K, M) with the 2**10 definition. - Non-gnu modes are compatible with jinja2's ``filesizeformat`` filter.""" + """Format a number of bytes like a human readable filesize (eg. 10 kB). + + By default, decimal suffixes (kB, MB) are used. + + Non-gnu modes are compatible with jinja2's ``filesizeformat`` filter. + + Args: + value (int, float, string): Integer to convert. + binary (Boolean): If `True`, uses binary suffixes (KiB, MiB) with base 2**10 + instead of 10**3. + gnu (Boolean): If `True`, the binary argument is ignored and GNU-style + (`ls -sh` style) prefixes are used (K, M) with the 2**10 definition. + format (str): Custom formatter. + """ if gnu: suffix = suffixes["gnu"] elif binary: From ab83065ceea8eb0c6e9fe89ed9cc13d3a16dd091 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 15 Mar 2020 13:46:55 +0200 Subject: [PATCH 06/10] Test on Python 3.9-dev --- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 30 +++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0d6030e..337b177 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,7 +10,7 @@ jobs: python-version: [3.8] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: pip cache uses: actions/cache@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 77a662e..a084984 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,19 +8,19 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.5, 3.6, 3.7, 3.8] + python-version: ["3.5", "3.6", "3.7", "3.8", "3.9"] os: [ubuntu-latest, macos-latest, windows-latest] include: # Include new variables for Codecov - - os: ubuntu-latest - codecov-flag: GHA_Ubuntu - - os: macOS-latest - codecov-flag: GHA_macOS - - os: windows-latest - codecov-flag: GHA_Windows + - { codecov-flag: GHA_Ubuntu, os: ubuntu-latest } + - { codecov-flag: GHA_macOS, os: macos-latest } + - { codecov-flag: GHA_Windows, os: windows-latest } + exclude: + - { python-version: 3.9, os: macos-latest } + - { python-version: 3.9, os: windows-latest } steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Ubuntu cache uses: actions/cache@v1 @@ -35,7 +35,7 @@ jobs: - name: macOS cache uses: actions/cache@v1 - if: startsWith(matrix.os, 'macOS') + if: startsWith(matrix.os, 'macos') with: path: ~/Library/Caches/pip key: @@ -55,7 +55,19 @@ jobs: restore-keys: | ${{ matrix.os }}-${{ matrix.python-version }}- + - name: Install Python 3.9 + if: matrix.python-version == '3.9' + run: | + sudo add-apt-repository ppa:deadsnakes/ppa + sudo apt-get update + sudo apt-get install -y --no-install-recommends python3.9-dev python3.9-distutils python3.9-venv + python3.9 -m pip install --upgrade pip setuptools + python3.9 -m venv $HOME/venv-python3.9 + echo "::set-env name=VIRTUAL_ENV::$HOME/venv-python3.9" + echo "::add-path::$HOME/venv-python3.9/bin" + - name: Set up Python ${{ matrix.python-version }} + if: matrix.python-version != '3.9' uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} From 1944341a63d92feb1b7b2af523c612adfbe2e044 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 15 Mar 2020 14:06:07 +0200 Subject: [PATCH 07/10] Retry curl Codecov script, it exits with '28 Operation timeout' ~20% of the time --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a084984..fef96a8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -85,7 +85,7 @@ jobs: - name: Upload coverage if: success() run: | - curl -s https://codecov.io/bash -o codecov.sh + curl --retry 5 -s https://codecov.io/bash -o codecov.sh bash codecov.sh -F ${{ matrix.codecov-flag }} env: CODECOV_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} From acf0d612cad18c17d7fd3de3644c3bc496b85fbc Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 15 Mar 2020 14:18:34 +0200 Subject: [PATCH 08/10] GHA: Test on PyPy3 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fef96a8..bb18dcf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.5", "3.6", "3.7", "3.8", "3.9"] + python-version: ["pypy3", "3.5", "3.6", "3.7", "3.8", "3.9"] os: [ubuntu-latest, macos-latest, windows-latest] include: # Include new variables for Codecov From 480b022d8bf1c7c1c1d6bf4169850572232981a5 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 15 Mar 2020 14:26:43 +0200 Subject: [PATCH 09/10] Exclude PyPy3 on Windows --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb18dcf..2cc50ed 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,6 +18,7 @@ jobs: exclude: - { python-version: 3.9, os: macos-latest } - { python-version: 3.9, os: windows-latest } + - { python-version: pypy3, os: windows-latest } steps: - uses: actions/checkout@v2 From 4f1c04ad64415df40e54fcd791a8446f5d617d33 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 15 Mar 2020 14:46:41 +0200 Subject: [PATCH 10/10] Retry curl Codecov script, it exits with '28 Operation timeout' ~10% of the time --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cc50ed..be14943 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -86,7 +86,7 @@ jobs: - name: Upload coverage if: success() run: | - curl --retry 5 -s https://codecov.io/bash -o codecov.sh + curl --retry 8 -s https://codecov.io/bash -o codecov.sh bash codecov.sh -F ${{ matrix.codecov-flag }} env: CODECOV_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }}