From 3c230d8c347225cb4430a823464c5336a01b36a6 Mon Sep 17 00:00:00 2001 From: Jirka Borovec <6035284+Borda@users.noreply.github.com> Date: Mon, 11 Mar 2024 21:34:50 +0100 Subject: [PATCH 01/36] Adding GitHub dependabot (#432) Co-authored-by: Hugo van Kemenade --- .github/dependabot.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..17c20d04 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,31 @@ +# Basic dependabot.yml file with minimum configuration for two package managers + +version: 2 +updates: + # Enable version updates for python + - package-ecosystem: "pip" + directory: ".github/scripts/" + schedule: + interval: "monthly" + labels: ["ci"] + pull-request-branch-name: + separator: "-" + open-pull-requests-limit: 5 + reviewers: + - "dbieber" + + # Enable version updates for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + groups: + pip: + patterns: + - "*" # Check all dependencies + labels: ["ci"] + pull-request-branch-name: + separator: "-" + open-pull-requests-limit: 5 + reviewers: + - "dbieber" From db3fcaf737f917d61015f3b408a6fac0898b2030 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:43:24 -0400 Subject: [PATCH 02/36] 2 dependabot updates (#491) 2 updates: [actions/checkout](https://github.com/actions/checkout) and [actions/setup-python](https://github.com/actions/setup-python). Updates `actions/checkout` from 3 to 4 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) Updates `actions/setup-python` from 4 to 5 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major dependency-group: pip - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major dependency-group: pip ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb510f03..f5562820 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,11 +12,11 @@ jobs: steps: # Checkout the repo. - name: Checkout Python Fire repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Set up Python environment. - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} From c417aec195cee982d24f85223e7831f71fa5adc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:44:16 -0400 Subject: [PATCH 03/36] Update hypothesis requirement in /.github/scripts (#492) Updates the requirements on [hypothesis](https://github.com/HypothesisWorks/hypothesis) to permit the latest version. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-ruby-0.0.1...hypothesis-python-6.99.4) --- updated-dependencies: - dependency-name: hypothesis dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 13880c9c..493ad0f0 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -7,7 +7,7 @@ pytest <=7.2.1 pytest-pylint <=1.1.2 pytest-runner <6.0.0 termcolor <2.2.0 -hypothesis <6.62.0 +hypothesis <6.100.0 python-Levenshtein <0.20.9 ; python_version == '2.7' levenshtein <=0.25.0 ; python_version >= '3.5' mock <5.0.0 From 027c50272911e92f02be7ae93c60b4559c98a1a1 Mon Sep 17 00:00:00 2001 From: Vladimir Pestov <92364726+BasedDepartment1@users.noreply.github.com> Date: Tue, 12 Mar 2024 01:47:19 +0500 Subject: [PATCH 04/36] #444: Removed pipes dependency (#447) Co-authored-by: Svayp11 --- fire/core.py | 5 ++--- fire/trace.py | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fire/core.py b/fire/core.py index 6367262d..fada01b1 100644 --- a/fire/core.py +++ b/fire/core.py @@ -56,7 +56,6 @@ def main(argv): import inspect import json import os -import pipes import re import shlex import sys @@ -240,7 +239,7 @@ def _IsHelpShortcut(component_trace, remaining_args): component_trace.show_help = True command = '{cmd} -- --help'.format(cmd=component_trace.GetCommand()) print('INFO: Showing help with the command {cmd}.\n'.format( - cmd=pipes.quote(command)), file=sys.stderr) + cmd=shlex.quote(command)), file=sys.stderr) return show_help @@ -296,7 +295,7 @@ def _DisplayError(component_trace): if show_help: command = '{cmd} -- --help'.format(cmd=component_trace.GetCommand()) print('INFO: Showing help with the command {cmd}.\n'.format( - cmd=pipes.quote(command)), file=sys.stderr) + cmd=shlex.quote(command)), file=sys.stderr) help_text = helptext.HelpText(result, trace=component_trace, verbose=component_trace.verbose) output.append(help_text) diff --git a/fire/trace.py b/fire/trace.py index 7174f994..4c9674e3 100644 --- a/fire/trace.py +++ b/fire/trace.py @@ -29,7 +29,7 @@ from __future__ import division from __future__ import print_function -import pipes +import shlex from fire import inspectutils @@ -166,8 +166,8 @@ def display(arg1, arg2='!'): def _Quote(self, arg): if arg.startswith('--') and '=' in arg: prefix, value = arg.split('=', 1) - return pipes.quote(prefix) + '=' + pipes.quote(value) - return pipes.quote(arg) + return shlex.quote(prefix) + '=' + shlex.quote(value) + return shlex.quote(arg) def GetCommand(self, include_separators=True): """Returns the command representing the trace up to this point. From 8beb85e4e3192d1ec0c5614ce2f8a10450670e82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:51:50 -0400 Subject: [PATCH 05/36] Update termcolor requirement from <2.2.0 to <2.5.0 in /.github/scripts (#493) Updates the requirements on [termcolor](https://github.com/termcolor/termcolor) to permit the latest version. - [Release notes](https://github.com/termcolor/termcolor/releases) - [Changelog](https://github.com/termcolor/termcolor/blob/main/CHANGES.md) - [Commits](https://github.com/termcolor/termcolor/compare/0.1...2.4.0) --- updated-dependencies: - dependency-name: termcolor dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 493ad0f0..31238e4d 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -6,7 +6,7 @@ pylint <2.15.10 pytest <=7.2.1 pytest-pylint <=1.1.2 pytest-runner <6.0.0 -termcolor <2.2.0 +termcolor <2.5.0 hypothesis <6.100.0 python-Levenshtein <0.20.9 ; python_version == '2.7' levenshtein <=0.25.0 ; python_version >= '3.5' From 595239ec7c096d8d95822153ee61190e8985f7bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:54:31 -0400 Subject: [PATCH 06/36] Update mock requirement from <5.0.0 to <6.0.0 in /.github/scripts (#495) Updates the requirements on [mock](https://github.com/testing-cabal/mock) to permit the latest version. - [Changelog](https://github.com/testing-cabal/mock/blob/master/CHANGELOG.rst) - [Commits](https://github.com/testing-cabal/mock/compare/release-0.5.0...5.1.0) --- updated-dependencies: - dependency-name: mock dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 31238e4d..b4efe7a9 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -10,4 +10,4 @@ termcolor <2.5.0 hypothesis <6.100.0 python-Levenshtein <0.20.9 ; python_version == '2.7' levenshtein <=0.25.0 ; python_version >= '3.5' -mock <5.0.0 +mock <6.0.0 From ab310cf8847b0c47216b37525cd54a6d358f9fc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:02:02 -0400 Subject: [PATCH 07/36] Update pytest requirement from <=7.2.1 to <=8.1.1 in /.github/scripts (#494) Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/1.0.0b3...8.1.1) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index b4efe7a9..8cb6ad15 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -3,7 +3,7 @@ setuptools <=69.1.1 ; python_version >= '3.8' pip <23.0 ; python_version == '2.7' pip ; python_version >= '3.5' pylint <2.15.10 -pytest <=7.2.1 +pytest <=8.1.1 pytest-pylint <=1.1.2 pytest-runner <6.0.0 termcolor <2.5.0 From 014a637f668db5fcfb81c1d426995b08e2b2ade7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:02:46 -0400 Subject: [PATCH 08/36] Update pytest-runner requirement in /.github/scripts (#496) Updates the requirements on [pytest-runner](https://github.com/pytest-dev/pytest-runner) to permit the latest version. - [Release notes](https://github.com/pytest-dev/pytest-runner/releases) - [Changelog](https://github.com/pytest-dev/pytest-runner/blob/main/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-runner/compare/1.0a1...v6.0.1) --- updated-dependencies: - dependency-name: pytest-runner dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 8cb6ad15..654f6079 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -5,7 +5,7 @@ pip ; python_version >= '3.5' pylint <2.15.10 pytest <=8.1.1 pytest-pylint <=1.1.2 -pytest-runner <6.0.0 +pytest-runner <7.0.0 termcolor <2.5.0 hypothesis <6.100.0 python-Levenshtein <0.20.9 ; python_version == '2.7' From f332cb1fe60c3a381d1ef5dc23c6e6d2142117df Mon Sep 17 00:00:00 2001 From: Jirka Borovec <6035284+Borda@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:02:23 +0100 Subject: [PATCH 09/36] Fix typo in dependabot github actions group (#497) --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 17c20d04..d31b409b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -20,7 +20,7 @@ updates: schedule: interval: "monthly" groups: - pip: + gh-actions: patterns: - "*" # Check all dependencies labels: ["ci"] From de2852a41746538e9077d8ab2586875cffd3dc57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:11:36 -0400 Subject: [PATCH 10/36] Update setuptools requirement in /.github/scripts (#500) Updates the requirements on [setuptools](https://github.com/pypa/setuptools) to permit the latest version. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/0.6...v69.2.0) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 654f6079..98111196 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -1,5 +1,5 @@ setuptools <65.7.0 ; python_version == '2.7' -setuptools <=69.1.1 ; python_version >= '3.8' +setuptools <=69.2.0 ; python_version >= '3.8' pip <23.0 ; python_version == '2.7' pip ; python_version >= '3.5' pylint <2.15.10 From 6902939a317dca9d41446168d8e88f108e0c0f11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:21:26 -0400 Subject: [PATCH 11/36] Update hypothesis requirement in /.github/scripts (#506) Updates the requirements on [hypothesis](https://github.com/HypothesisWorks/hypothesis) to permit the latest version. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-ruby-0.0.1...hypothesis-python-6.100.0) --- updated-dependencies: - dependency-name: hypothesis dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 98111196..15d2b017 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -7,7 +7,7 @@ pytest <=8.1.1 pytest-pylint <=1.1.2 pytest-runner <7.0.0 termcolor <2.5.0 -hypothesis <6.100.0 +hypothesis <6.101.0 python-Levenshtein <0.20.9 ; python_version == '2.7' levenshtein <=0.25.0 ; python_version >= '3.5' mock <6.0.0 From e9f49b0fa9d5ee627e80a15e74624fcd41a34add Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 16:15:45 -0400 Subject: [PATCH 12/36] Update levenshtein requirement in /.github/scripts (#510) Updates the requirements on [levenshtein](https://github.com/rapidfuzz/Levenshtein) to permit the latest version. - [Release notes](https://github.com/rapidfuzz/Levenshtein/releases) - [Changelog](https://github.com/rapidfuzz/Levenshtein/blob/main/HISTORY.md) - [Commits](https://github.com/rapidfuzz/Levenshtein/compare/v0.13.0...v0.25.1) --- updated-dependencies: - dependency-name: levenshtein dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 15d2b017..f7d3cacd 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -9,5 +9,5 @@ pytest-runner <7.0.0 termcolor <2.5.0 hypothesis <6.101.0 python-Levenshtein <0.20.9 ; python_version == '2.7' -levenshtein <=0.25.0 ; python_version >= '3.5' +levenshtein <=0.25.1 ; python_version >= '3.5' mock <6.0.0 From a59f6bad3f72ae6035b076504744d3e9f619afea Mon Sep 17 00:00:00 2001 From: Weida Hong Date: Sat, 10 Aug 2024 04:16:48 +0800 Subject: [PATCH 13/36] Use ast.Constant for recent Python versions (#526) ast.Str is planned to removed in Python 3.14, use ast.Constant instead whenever the later is available. --- fire/parser.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fire/parser.py b/fire/parser.py index 2aff8bd7..bdf3cdbf 100644 --- a/fire/parser.py +++ b/fire/parser.py @@ -20,7 +20,12 @@ import argparse import ast +import sys +if sys.version_info[0:2] < (3, 8): + _StrNode = ast.Str +else: + _StrNode = ast.Constant def CreateParser(): parser = argparse.ArgumentParser(add_help=False) @@ -127,4 +132,4 @@ def _Replacement(node): # These are the only builtin constants supported by literal_eval. if value in ('True', 'False', 'None'): return node - return ast.Str(value) + return _StrNode(value) From 8b063b952fba6dec79dfbc8688a9edf047de3b6c Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 20:20:44 -0400 Subject: [PATCH 14/36] Remove future imports now that we've dropped support for Python 2 (#539) * Remove future imports now that we've dropped support for Python 2 * Keep future imports for use in MemberVisible * Drop support for Python 3.5 * Remove indications of support for Python <3.7 and bump version number. --- .github/scripts/build.sh | 2 +- .github/workflows/build.yml | 2 +- fire/__init__.py | 4 ---- fire/__main__.py | 4 ---- fire/completion_test.py | 4 ---- fire/console/console_io.py | 4 ---- fire/core.py | 4 ---- fire/core_test.py | 4 ---- fire/custom_descriptions.py | 4 ---- fire/custom_descriptions_test.py | 4 ---- fire/decorators.py | 4 ---- fire/decorators_test.py | 4 ---- fire/docstrings.py | 4 ---- fire/docstrings_fuzz_test.py | 4 ---- fire/docstrings_test.py | 4 ---- fire/fire_test.py | 4 ---- fire/formatting.py | 4 ---- fire/formatting_test.py | 4 ---- fire/formatting_windows.py | 4 ---- fire/helptext.py | 4 ---- fire/helptext_test.py | 4 ---- fire/inspectutils.py | 4 ---- fire/inspectutils_test.py | 4 ---- fire/interact.py | 4 ---- fire/interact_test.py | 4 ---- fire/parser.py | 4 ---- fire/parser_fuzz_test.py | 4 ---- fire/parser_test.py | 4 ---- fire/test_components.py | 4 ---- fire/test_components_bin.py | 4 ---- fire/test_components_test.py | 4 ---- fire/testutils.py | 4 ---- fire/testutils_test.py | 4 ---- fire/trace.py | 4 ---- fire/trace_test.py | 4 ---- fire/value_types.py | 4 ---- setup.py | 6 +----- 37 files changed, 3 insertions(+), 143 deletions(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 1f9ed766..111257ae 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -17,7 +17,7 @@ # Exit when any command fails. set -e -PYTHON_VERSION=${PYTHON_VERSION:-2.7} +PYTHON_VERSION=${PYTHON_VERSION:-3.7} pip install -U -r .github/scripts/requirements.txt python setup.py develop diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5562820..7f5225c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ["3.5", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] steps: # Checkout the repo. diff --git a/fire/__init__.py b/fire/__init__.py index fae18489..742b03ac 100644 --- a/fire/__init__.py +++ b/fire/__init__.py @@ -14,10 +14,6 @@ """The Python Fire module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire.core import Fire __all__ = ['Fire'] diff --git a/fire/__main__.py b/fire/__main__.py index 9d8227ad..15a9d6c8 100644 --- a/fire/__main__.py +++ b/fire/__main__.py @@ -18,10 +18,6 @@ This allows using Fire with third-party libraries without modifying their code. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import importlib import os import sys diff --git a/fire/completion_test.py b/fire/completion_test.py index 582e5bbc..5bafc279 100644 --- a/fire/completion_test.py +++ b/fire/completion_test.py @@ -14,10 +14,6 @@ """Tests for the completion module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import completion from fire import test_components as tc from fire import testutils diff --git a/fire/console/console_io.py b/fire/console/console_io.py index 3d3b9f81..ec0858d9 100644 --- a/fire/console/console_io.py +++ b/fire/console/console_io.py @@ -15,10 +15,6 @@ """General console printing utilities used by the Cloud SDK.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import signal import subprocess diff --git a/fire/core.py b/fire/core.py index fada01b1..0a6dae7d 100644 --- a/fire/core.py +++ b/fire/core.py @@ -49,10 +49,6 @@ def main(argv): --trace: Get the Fire Trace for the command. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect import json import os diff --git a/fire/core_test.py b/fire/core_test.py index 75b76998..b9033c22 100644 --- a/fire/core_test.py +++ b/fire/core_test.py @@ -14,10 +14,6 @@ """Tests for the core module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import core from fire import test_components as tc from fire import testutils diff --git a/fire/custom_descriptions.py b/fire/custom_descriptions.py index 266671f1..f7df90b0 100644 --- a/fire/custom_descriptions.py +++ b/fire/custom_descriptions.py @@ -36,10 +36,6 @@ descriptions for primitive typed values. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import formatting import six diff --git a/fire/custom_descriptions_test.py b/fire/custom_descriptions_test.py index 79d7c7a1..6cff2d5d 100644 --- a/fire/custom_descriptions_test.py +++ b/fire/custom_descriptions_test.py @@ -14,10 +14,6 @@ """Tests for custom description module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import custom_descriptions from fire import testutils diff --git a/fire/decorators.py b/fire/decorators.py index b2e9b322..eb5b0d20 100644 --- a/fire/decorators.py +++ b/fire/decorators.py @@ -18,10 +18,6 @@ command line arguments to client code. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect FIRE_METADATA = 'FIRE_METADATA' diff --git a/fire/decorators_test.py b/fire/decorators_test.py index cc7d6203..a316b79f 100644 --- a/fire/decorators_test.py +++ b/fire/decorators_test.py @@ -14,10 +14,6 @@ """Tests for the decorators module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import core from fire import decorators from fire import testutils diff --git a/fire/docstrings.py b/fire/docstrings.py index 1cfadea9..2d7c7e63 100644 --- a/fire/docstrings.py +++ b/fire/docstrings.py @@ -49,10 +49,6 @@ - "True | False" indicates bool type. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import collections import enum import re diff --git a/fire/docstrings_fuzz_test.py b/fire/docstrings_fuzz_test.py index 7609f4f8..66be8006 100644 --- a/fire/docstrings_fuzz_test.py +++ b/fire/docstrings_fuzz_test.py @@ -14,10 +14,6 @@ """Fuzz tests for the docstring parser module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import docstrings from fire import testutils diff --git a/fire/docstrings_test.py b/fire/docstrings_test.py index 0d6e5d18..ce516944 100644 --- a/fire/docstrings_test.py +++ b/fire/docstrings_test.py @@ -14,10 +14,6 @@ """Tests for fire docstrings module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import docstrings from fire import testutils diff --git a/fire/fire_test.py b/fire/fire_test.py index 8b904c29..6b9a2fa2 100644 --- a/fire/fire_test.py +++ b/fire/fire_test.py @@ -14,10 +14,6 @@ """Tests for the fire module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys diff --git a/fire/formatting.py b/fire/formatting.py index faef8047..68484c27 100644 --- a/fire/formatting.py +++ b/fire/formatting.py @@ -14,10 +14,6 @@ """Formatting utilities for use in creating help text.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import formatting_windows # pylint: disable=unused-import import termcolor diff --git a/fire/formatting_test.py b/fire/formatting_test.py index 05a88c49..e0f6699d 100644 --- a/fire/formatting_test.py +++ b/fire/formatting_test.py @@ -14,10 +14,6 @@ """Tests for formatting.py.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import formatting from fire import testutils diff --git a/fire/formatting_windows.py b/fire/formatting_windows.py index ce0f677d..f8241eaa 100644 --- a/fire/formatting_windows.py +++ b/fire/formatting_windows.py @@ -14,10 +14,6 @@ """This module is used for enabling formatting on Windows.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import ctypes import os import platform diff --git a/fire/helptext.py b/fire/helptext.py index 6e7fbb07..93072897 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -29,10 +29,6 @@ information. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import collections import itertools import sys diff --git a/fire/helptext_test.py b/fire/helptext_test.py index 404d9812..9a0f4f6d 100644 --- a/fire/helptext_test.py +++ b/fire/helptext_test.py @@ -14,10 +14,6 @@ """Tests for the helptext module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import sys import textwrap diff --git a/fire/inspectutils.py b/fire/inspectutils.py index 15f32f91..ca51a9a5 100644 --- a/fire/inspectutils.py +++ b/fire/inspectutils.py @@ -14,10 +14,6 @@ """Inspection utility functions for Python Fire.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect import sys import types diff --git a/fire/inspectutils_test.py b/fire/inspectutils_test.py index ea8eb0e2..bb62f402 100644 --- a/fire/inspectutils_test.py +++ b/fire/inspectutils_test.py @@ -14,10 +14,6 @@ """Tests for the inspectutils module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import os import unittest diff --git a/fire/interact.py b/fire/interact.py index 7df32841..7bdeb9a7 100644 --- a/fire/interact.py +++ b/fire/interact.py @@ -20,10 +20,6 @@ InteractiveConsole class. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect diff --git a/fire/interact_test.py b/fire/interact_test.py index 29fa7597..99cde285 100644 --- a/fire/interact_test.py +++ b/fire/interact_test.py @@ -14,10 +14,6 @@ """Tests for the interact module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import interact from fire import testutils diff --git a/fire/parser.py b/fire/parser.py index bdf3cdbf..c4708455 100644 --- a/fire/parser.py +++ b/fire/parser.py @@ -14,10 +14,6 @@ """Provides parsing functionality used by Python Fire.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import argparse import ast import sys diff --git a/fire/parser_fuzz_test.py b/fire/parser_fuzz_test.py index af0be038..38e17725 100644 --- a/fire/parser_fuzz_test.py +++ b/fire/parser_fuzz_test.py @@ -14,10 +14,6 @@ """Fuzz tests for the parser module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import parser from fire import testutils from hypothesis import example diff --git a/fire/parser_test.py b/fire/parser_test.py index 8aeabc61..a404eea2 100644 --- a/fire/parser_test.py +++ b/fire/parser_test.py @@ -14,10 +14,6 @@ """Tests for the parser module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import parser from fire import testutils diff --git a/fire/test_components.py b/fire/test_components.py index 5fcb056e..e50f647c 100644 --- a/fire/test_components.py +++ b/fire/test_components.py @@ -14,10 +14,6 @@ """This module has components that are used for testing Python Fire.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import collections import enum import functools diff --git a/fire/test_components_bin.py b/fire/test_components_bin.py index fbb41952..62afdf11 100644 --- a/fire/test_components_bin.py +++ b/fire/test_components_bin.py @@ -17,10 +17,6 @@ This file is useful for replicating test results manually. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import fire from fire import test_components diff --git a/fire/test_components_test.py b/fire/test_components_test.py index f35d7ab5..531f882c 100644 --- a/fire/test_components_test.py +++ b/fire/test_components_test.py @@ -14,10 +14,6 @@ """Tests for the test_components module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import test_components as tc from fire import testutils diff --git a/fire/testutils.py b/fire/testutils.py index 5f875147..76faa3f4 100644 --- a/fire/testutils.py +++ b/fire/testutils.py @@ -14,10 +14,6 @@ """Utilities for Python Fire's tests.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import contextlib import os import re diff --git a/fire/testutils_test.py b/fire/testutils_test.py index ad604193..0999a4c8 100644 --- a/fire/testutils_test.py +++ b/fire/testutils_test.py @@ -14,10 +14,6 @@ """Test the test utilities for Fire's tests.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import sys from fire import testutils diff --git a/fire/trace.py b/fire/trace.py index 4c9674e3..2145186e 100644 --- a/fire/trace.py +++ b/fire/trace.py @@ -25,10 +25,6 @@ component will be None. """ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import shlex from fire import inspectutils diff --git a/fire/trace_test.py b/fire/trace_test.py index 1621a593..1f858f5e 100644 --- a/fire/trace_test.py +++ b/fire/trace_test.py @@ -14,10 +14,6 @@ """Tests for the trace module.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - from fire import testutils from fire import trace diff --git a/fire/value_types.py b/fire/value_types.py index c0a137fd..b2d0a0b3 100644 --- a/fire/value_types.py +++ b/fire/value_types.py @@ -14,10 +14,6 @@ """Types of values.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - import inspect from fire import inspectutils diff --git a/setup.py b/setup.py index 24e0e325..f861f9a5 100644 --- a/setup.py +++ b/setup.py @@ -40,7 +40,7 @@ 'python-Levenshtein', ] -VERSION = '0.6.0' +VERSION = '0.7.0' URL = 'https://github.com/google/python-fire' setup( @@ -63,11 +63,7 @@ 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - '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', From 5d0706d814e8c9297f078fabc0a1638c34c1ef30 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 20:57:15 -0400 Subject: [PATCH 15/36] remove six: Replace six.string_types and six.integer_types, etc. (#541) This commit includes: * assuming Python 3 for test skipping * assuming Python 3 for fire's various type checks * assuming Python 3 for imports (like asyncio) * assuming Python 3 for getting function signatures * six is no longer considered a hidden module (and so if a user of fire has six in their globals when they call Fire(), six will now show up where it used to be hidden) This commit does not remove six from console/ code. --- fire/completion.py | 21 +++++++-------------- fire/core.py | 8 +++----- fire/core_test.py | 4 ---- fire/custom_descriptions.py | 9 +++------ fire/fire_test.py | 3 --- fire/helptext_test.py | 11 ----------- fire/inspectutils.py | 27 ++++----------------------- fire/inspectutils_test.py | 9 +-------- fire/parser_fuzz_test.py | 7 +++---- fire/test_components.py | 5 +---- fire/testutils.py | 11 ++++------- fire/testutils_test.py | 8 +++----- fire/value_types.py | 3 +-- 13 files changed, 30 insertions(+), 96 deletions(-) diff --git a/fire/completion.py b/fire/completion.py index 4393880d..3aa8ab11 100644 --- a/fire/completion.py +++ b/fire/completion.py @@ -23,7 +23,6 @@ import inspect from fire import inspectutils -import six def Script(name, component, default_options=None, shell='bash'): @@ -308,7 +307,7 @@ def MemberVisible(component, name, member, class_attrs=None, verbose=False): Returns A boolean value indicating whether the member should be included. """ - if isinstance(name, six.string_types) and name.startswith('__'): + if isinstance(name, str) and name.startswith('__'): return False if verbose: return True @@ -316,10 +315,11 @@ def MemberVisible(component, name, member, class_attrs=None, verbose=False): or member is division or member is print_function): return False - if isinstance(member, type(absolute_import)) and six.PY34: + if isinstance(member, type(absolute_import)): return False - if inspect.ismodule(member) and member is six: - # TODO(dbieber): Determine more generally which modules to hide. + # TODO(dbieber): Determine more generally which modules to hide. + modules_to_hide = [] + if inspect.ismodule(member) and member in modules_to_hide: return False if inspect.isclass(component): # If class_attrs has not been provided, compute it. @@ -336,14 +336,7 @@ def MemberVisible(component, name, member, class_attrs=None, verbose=False): tuplegetter = getattr(collections, '_tuplegetter', type(None)) if isinstance(class_attr.object, tuplegetter): return False - if (six.PY2 and inspect.isfunction(component) - and name in ('func_closure', 'func_code', 'func_defaults', - 'func_dict', 'func_doc', 'func_globals', 'func_name')): - return False - if (six.PY2 and inspect.ismethod(component) - and name in ('im_class', 'im_func', 'im_self')): - return False - if isinstance(name, six.string_types): + if isinstance(name, str): return not name.startswith('_') return True # Default to including the member @@ -438,7 +431,7 @@ def _FormatForCommand(token): Returns: The transformed token. """ - if not isinstance(token, six.string_types): + if not isinstance(token, str): token = str(token) if token.startswith('_'): diff --git a/fire/core.py b/fire/core.py index 0a6dae7d..c61a8b57 100644 --- a/fire/core.py +++ b/fire/core.py @@ -67,10 +67,8 @@ def main(argv): from fire import trace from fire import value_types from fire.console import console_io -import six -if six.PY34: - import asyncio # pylint: disable=import-error,g-import-not-at-top # pytype: disable=import-error +import asyncio # pylint: disable=import-error,g-import-not-at-top # pytype: disable=import-error def Fire(component=None, command=None, name=None, serialize=None): @@ -109,7 +107,7 @@ def Fire(component=None, command=None, name=None, serialize=None): name = name or os.path.basename(sys.argv[0]) # Get args as a list. - if isinstance(command, six.string_types): + if isinstance(command, str): args = shlex.split(command) elif isinstance(command, (list, tuple)): args = command @@ -344,7 +342,7 @@ def _DictAsString(result, verbose=False): def _OneLineResult(result): """Returns result serialized to a single line string.""" # TODO(dbieber): Ensure line is fewer than eg 120 characters. - if isinstance(result, six.string_types): + if isinstance(result, str): return str(result).replace('\n', ' ') # TODO(dbieber): Show a small amount of usage information about the function diff --git a/fire/core_test.py b/fire/core_test.py index b9033c22..9e1f7dba 100644 --- a/fire/core_test.py +++ b/fire/core_test.py @@ -20,8 +20,6 @@ from fire import trace import mock -import six - class CoreTest(testutils.BaseTestCase): @@ -214,13 +212,11 @@ def serialize(x): with self.assertRaises(core.FireError): core.Fire(ident, command=['asdf'], serialize=55) - @testutils.skipIf(six.PY2, 'lru_cache is Python 3 only.') def testLruCacheDecoratorBoundArg(self): self.assertEqual( core.Fire(tc.py3.LruCacheDecoratedMethod, # pytype: disable=module-attr command=['lru_cache_in_class', 'foo']), 'foo') - @testutils.skipIf(six.PY2, 'lru_cache is Python 3 only.') def testLruCacheDecorator(self): self.assertEqual( core.Fire(tc.py3.lru_cache_decorated, # pytype: disable=module-attr diff --git a/fire/custom_descriptions.py b/fire/custom_descriptions.py index f7df90b0..768f0e23 100644 --- a/fire/custom_descriptions.py +++ b/fire/custom_descriptions.py @@ -37,7 +37,6 @@ """ from fire import formatting -import six TWO_DOUBLE_QUOTES = '""' STRING_DESC_PREFIX = 'The string ' @@ -60,13 +59,11 @@ def NeedsCustomDescription(component): Whether the component should use a custom description and summary. """ type_ = type(component) - if (type_ in six.string_types - or type_ in six.integer_types - or type_ is six.text_type - or type_ is six.binary_type + if ( + type_ in (str, int, bytes) or type_ in (float, complex, bool) or type_ in (dict, tuple, list, set, frozenset) - ): + ): return True return False diff --git a/fire/fire_test.py b/fire/fire_test.py index 6b9a2fa2..74b3bb25 100644 --- a/fire/fire_test.py +++ b/fire/fire_test.py @@ -22,7 +22,6 @@ from fire import testutils import mock -import six class FireTest(testutils.BaseTestCase): @@ -180,7 +179,6 @@ def testFireAnnotatedArgs(self): self.assertEqual(fire.Fire(tc.Annotations, command=['double', '5']), 10) self.assertEqual(fire.Fire(tc.Annotations, command=['triple', '5']), 15) - @testutils.skipIf(six.PY2, 'Keyword-only arguments not in Python 2.') def testFireKeywordOnlyArgs(self): with self.assertRaisesFireExit(2): # Keyword arguments must be passed with flag syntax. @@ -717,7 +715,6 @@ def testHelpKwargsDecorator(self): with self.assertRaisesFireExit(0): fire.Fire(tc.decorated_method, command=['--help']) - @testutils.skipIf(six.PY2, 'Asyncio not available in Python 2.') def testFireAsyncio(self): self.assertEqual(fire.Fire(tc.py3.WithAsyncio, command=['double', '--count', '10']), 20) diff --git a/fire/helptext_test.py b/fire/helptext_test.py index 9a0f4f6d..2250f199 100644 --- a/fire/helptext_test.py +++ b/fire/helptext_test.py @@ -23,7 +23,6 @@ from fire import test_components as tc from fire import testutils from fire import trace -import six class HelpTest(testutils.BaseTestCase): @@ -276,8 +275,6 @@ def testHelpTextNoInit(self): self.assertIn('NAME\n OldStyleEmpty', help_screen) self.assertIn('SYNOPSIS\n OldStyleEmpty', help_screen) - @testutils.skipIf( - six.PY2, 'Python 2 does not support keyword-only arguments.') def testHelpTextKeywordOnlyArgumentsWithDefault(self): component = tc.py3.KeywordOnly.with_default # pytype: disable=module-attr output = helptext.HelpText( @@ -285,8 +282,6 @@ def testHelpTextKeywordOnlyArgumentsWithDefault(self): self.assertIn('NAME\n with_default', output) self.assertIn('FLAGS\n -x, --x=X', output) - @testutils.skipIf( - six.PY2, 'Python 2 does not support keyword-only arguments.') def testHelpTextKeywordOnlyArgumentsWithoutDefault(self): component = tc.py3.KeywordOnly.double # pytype: disable=module-attr output = helptext.HelpText( @@ -294,9 +289,6 @@ def testHelpTextKeywordOnlyArgumentsWithoutDefault(self): self.assertIn('NAME\n double', output) self.assertIn('FLAGS\n -c, --count=COUNT (required)', output) - @testutils.skipIf( - six.PY2, - 'Python 2 does not support required name-only arguments.') def testHelpTextFunctionMixedDefaults(self): component = tc.py3.HelpTextComponent().identity t = trace.FireTrace(component, name='FunctionMixedDefaults') @@ -523,9 +515,6 @@ def testUsageOutputFunctionWithDocstring(self): textwrap.dedent(expected_output).lstrip('\n'), usage_output) - @testutils.skipIf( - six.PY2, - 'Python 2 does not support required name-only arguments.') def testUsageOutputFunctionMixedDefaults(self): component = tc.py3.HelpTextComponent().identity t = trace.FireTrace(component, name='FunctionMixedDefaults') diff --git a/fire/inspectutils.py b/fire/inspectutils.py index ca51a9a5..0d0b048d 100644 --- a/fire/inspectutils.py +++ b/fire/inspectutils.py @@ -20,10 +20,7 @@ from fire import docstrings -import six - -if six.PY34: - import asyncio # pylint: disable=import-error,g-import-not-at-top # pytype: disable=import-error +import asyncio # pylint: disable=import-error,g-import-not-at-top # pytype: disable=import-error class FullArgSpec(object): @@ -74,8 +71,6 @@ class with an __init__ method. if inspect.isclass(fn): # If the function is a class, we try to use its init method. skip_arg = True - if six.PY2 and hasattr(fn, '__init__'): - fn = fn.__init__ elif inspect.ismethod(fn): # If the function is a bound method, we skip the `self` argument. skip_arg = fn.__self__ is not None @@ -91,16 +86,6 @@ class with an __init__ method. return fn, skip_arg -def Py2GetArgSpec(fn): - """A wrapper around getargspec that tries both fn and fn.__call__.""" - try: - return inspect.getargspec(fn) # pylint: disable=deprecated-method,no-member - except TypeError: - if hasattr(fn, '__call__'): - return inspect.getargspec(fn.__call__) # pylint: disable=deprecated-method,no-member - raise - - def Py3GetFullArgSpec(fn): """A alternative to the builtin getfullargspec. @@ -185,13 +170,9 @@ def GetFullArgSpec(fn): if sys.version_info[0:2] >= (3, 5): (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations) = Py3GetFullArgSpec(fn) - elif six.PY3: # Specifically Python 3.4. + else: # Specifically Python 3.4. (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations) = inspect.getfullargspec(fn) # pylint: disable=deprecated-method,no-member - else: # six.PY2 - args, varargs, varkw, defaults = Py2GetArgSpec(fn) - kwonlyargs = kwonlydefaults = None - annotations = getattr(fn, '__annotations__', None) except TypeError: # If we can't get the argspec, how do we know if the fn should take args? @@ -221,7 +202,7 @@ def GetFullArgSpec(fn): return FullArgSpec() # In Python 3.5+ Py3GetFullArgSpec uses skip_bound_arg=True already. - skip_arg_required = six.PY2 or sys.version_info[0:2] == (3, 4) + skip_arg_required = sys.version_info[0:2] == (3, 4) if skip_arg_required and skip_arg and args: args.pop(0) # Remove 'self' or 'cls' from the list of arguments. return FullArgSpec(args, varargs, varkw, defaults, @@ -363,6 +344,6 @@ def GetClassAttrsDict(component): def IsCoroutineFunction(fn): try: - return six.PY34 and asyncio.iscoroutinefunction(fn) + return asyncio.iscoroutinefunction(fn) except: # pylint: disable=bare-except return False diff --git a/fire/inspectutils_test.py b/fire/inspectutils_test.py index bb62f402..47de7e72 100644 --- a/fire/inspectutils_test.py +++ b/fire/inspectutils_test.py @@ -15,14 +15,11 @@ """Tests for the inspectutils module.""" import os -import unittest from fire import inspectutils from fire import test_components as tc from fire import testutils -import six - class InspectUtilsTest(testutils.BaseTestCase): @@ -36,7 +33,6 @@ def testGetFullArgSpec(self): self.assertEqual(spec.kwonlydefaults, {}) self.assertEqual(spec.annotations, {'arg2': int, 'arg4': int}) - @unittest.skipIf(six.PY2, 'No keyword arguments in python 2') def testGetFullArgSpecPy3(self): spec = inspectutils.GetFullArgSpec(tc.py3.identity) self.assertEqual(spec.args, ['arg1', 'arg2', 'arg3', 'arg4']) @@ -121,10 +117,7 @@ def testInfoClass(self): def testInfoClassNoInit(self): info = inspectutils.Info(tc.OldStyleEmpty) - if six.PY2: - self.assertEqual(info.get('type_name'), 'classobj') - else: - self.assertEqual(info.get('type_name'), 'type') + self.assertEqual(info.get('type_name'), 'type') self.assertIn(os.path.join('fire', 'test_components.py'), info.get('file')) self.assertGreater(info.get('line'), 0) diff --git a/fire/parser_fuzz_test.py b/fire/parser_fuzz_test.py index 38e17725..9739ec4e 100644 --- a/fire/parser_fuzz_test.py +++ b/fire/parser_fuzz_test.py @@ -21,7 +21,6 @@ from hypothesis import settings from hypothesis import strategies as st import Levenshtein -import six class ParserFuzzTest(testutils.BaseTestCase): @@ -64,8 +63,8 @@ def testDefaultParseValueFuzz(self, value): raise try: - uvalue = six.text_type(value) - uresult = six.text_type(result) + uvalue = str(value) + uresult = str(result) except UnicodeDecodeError: # This is not what we're testing. return @@ -82,7 +81,7 @@ def testDefaultParseValueFuzz(self, value): if '#' in value: max_distance += len(value) - value.index('#') - if not isinstance(result, six.string_types): + if not isinstance(result, str): max_distance += value.count('0') # Leading 0s are stripped. # Note: We don't check distance for dicts since item order can be changed. diff --git a/fire/test_components.py b/fire/test_components.py index e50f647c..540a9e16 100644 --- a/fire/test_components.py +++ b/fire/test_components.py @@ -18,10 +18,7 @@ import enum import functools -import six - -if six.PY3: - from fire import test_components_py3 as py3 # pylint: disable=unused-import,no-name-in-module,g-import-not-at-top +from fire import test_components_py3 as py3 # pylint: disable=unused-import,no-name-in-module,g-import-not-at-top def identity(arg1, arg2, arg3=10, arg4=20, *arg5, **arg6): # pylint: disable=keyword-arg-before-vararg diff --git a/fire/testutils.py b/fire/testutils.py index 76faa3f4..fa1ca86d 100644 --- a/fire/testutils.py +++ b/fire/testutils.py @@ -15,6 +15,7 @@ """Utilities for Python Fire's tests.""" import contextlib +import io import os import re import sys @@ -24,7 +25,6 @@ from fire import trace import mock -import six class BaseTestCase(unittest.TestCase): @@ -45,8 +45,8 @@ def assertOutputMatches(self, stdout='.*', stderr='.*', capture=True): Yields: Yields to the wrapped context. """ - stdout_fp = six.StringIO() - stderr_fp = six.StringIO() + stdout_fp = io.StringIO() + stderr_fp = io.StringIO() try: with mock.patch.object(sys, 'stdout', stdout_fp): with mock.patch.object(sys, 'stderr', stderr_fp): @@ -69,10 +69,7 @@ def assertOutputMatches(self, stdout='.*', stderr='.*', capture=True): (name, value, regexp)) def assertRaisesRegex(self, *args, **kwargs): # pylint: disable=arguments-differ - if sys.version_info.major == 2: - return super(BaseTestCase, self).assertRaisesRegexp(*args, **kwargs) # pylint: disable=deprecated-method,no-member - else: - return super(BaseTestCase, self).assertRaisesRegex(*args, **kwargs) # pylint: disable=no-member + return super(BaseTestCase, self).assertRaisesRegex(*args, **kwargs) # pylint: disable=no-member @contextlib.contextmanager def assertRaisesFireExit(self, code, regexp='.*'): diff --git a/fire/testutils_test.py b/fire/testutils_test.py index 0999a4c8..4cfc0937 100644 --- a/fire/testutils_test.py +++ b/fire/testutils_test.py @@ -18,8 +18,6 @@ from fire import testutils -import six - class TestTestUtils(testutils.BaseTestCase): """Let's get meta.""" @@ -30,15 +28,15 @@ def testNoCheckOnException(self): raise ValueError() def testCheckStdoutOrStderrNone(self): - with six.assertRaisesRegex(self, AssertionError, 'stdout:'): + with self.assertRaisesRegex(AssertionError, 'stdout:'): with self.assertOutputMatches(stdout=None): print('blah') - with six.assertRaisesRegex(self, AssertionError, 'stderr:'): + with self.assertRaisesRegex(AssertionError, 'stderr:'): with self.assertOutputMatches(stderr=None): print('blah', file=sys.stderr) - with six.assertRaisesRegex(self, AssertionError, 'stderr:'): + with self.assertRaisesRegex(AssertionError, 'stderr:'): with self.assertOutputMatches(stdout='apple', stderr=None): print('apple') print('blah', file=sys.stderr) diff --git a/fire/value_types.py b/fire/value_types.py index b2d0a0b3..81308973 100644 --- a/fire/value_types.py +++ b/fire/value_types.py @@ -17,10 +17,9 @@ import inspect from fire import inspectutils -import six -VALUE_TYPES = (bool, six.string_types, six.integer_types, float, complex, +VALUE_TYPES = (bool, str, bytes, int, float, complex, type(Ellipsis), type(None), type(NotImplemented)) From b13c13bf1767caf80bf38349dd5fbf0dd08e18b8 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 21:08:42 -0400 Subject: [PATCH 16/36] Remove sys.version_info checks (#542) * Remove sys.version_info checks where no longer needed --- fire/__main__.py | 25 ++++++------------------- fire/fire_test.py | 2 -- fire/helptext.py | 5 +---- fire/helptext_test.py | 13 ------------- fire/parser.py | 1 + 5 files changed, 8 insertions(+), 38 deletions(-) diff --git a/fire/__main__.py b/fire/__main__.py index 15a9d6c8..11fb1b42 100644 --- a/fire/__main__.py +++ b/fire/__main__.py @@ -19,6 +19,7 @@ """ import importlib +from importlib import util import os import sys @@ -57,27 +58,13 @@ def import_from_file_path(path): module_name = os.path.basename(path) - if sys.version_info.major == 3 and sys.version_info.minor < 5: - loader = importlib.machinery.SourceFileLoader( # pylint: disable=no-member - fullname=module_name, - path=path, - ) + spec = util.spec_from_file_location(module_name, path) - module = loader.load_module(module_name) # pylint: disable=deprecated-method + if spec is None: + raise IOError('Unable to load module from specified path.') - elif sys.version_info.major == 3: - from importlib import util # pylint: disable=g-import-not-at-top,import-outside-toplevel,no-name-in-module - spec = util.spec_from_file_location(module_name, path) - - if spec is None: - raise IOError('Unable to load module from specified path.') - - module = util.module_from_spec(spec) # pylint: disable=no-member - spec.loader.exec_module(module) # pytype: disable=attribute-error - - else: - import imp # pylint: disable=g-import-not-at-top,import-outside-toplevel,deprecated-module,import-error - module = imp.load_source(module_name, path) + module = util.module_from_spec(spec) # pylint: disable=no-member + spec.loader.exec_module(module) # pytype: disable=attribute-error return module, module_name diff --git a/fire/fire_test.py b/fire/fire_test.py index 74b3bb25..74f1f6e6 100644 --- a/fire/fire_test.py +++ b/fire/fire_test.py @@ -706,8 +706,6 @@ def testClassWithInvalidProperty(self): fire.Fire(tc.InvalidProperty, command=['double', '10']), 20 ) - @testutils.skipIf(sys.version_info[0:2] <= (3, 4), - 'Cannot inspect wrapped signatures in Python 2 or 3.4.') def testHelpKwargsDecorator(self): # Issue #190, follow the wrapped method instead of crashing. with self.assertRaisesFireExit(0): diff --git a/fire/helptext.py b/fire/helptext.py index 93072897..1c0cb626 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -31,7 +31,6 @@ import collections import itertools -import sys from fire import completion from fire import custom_descriptions @@ -533,9 +532,7 @@ def _GetArgType(arg, spec): if arg in spec.annotations: arg_type = spec.annotations[arg] try: - if sys.version_info[0:2] >= (3, 3): - return arg_type.__qualname__ - return arg_type.__name__ + return arg_type.__qualname__ except AttributeError: # Some typing objects, such as typing.Union do not have either a __name__ # or __qualname__ attribute. diff --git a/fire/helptext_test.py b/fire/helptext_test.py index 2250f199..4d35dc0a 100644 --- a/fire/helptext_test.py +++ b/fire/helptext_test.py @@ -15,7 +15,6 @@ """Tests for the helptext module.""" import os -import sys import textwrap from fire import formatting @@ -124,9 +123,6 @@ def testHelpTextFunctionWithKwargsAndDefaults(self): 'Additional undocumented flags may also be accepted.', help_screen) - @testutils.skipIf( - sys.version_info[0:2] < (3, 5), - 'Python < 3.5 does not support type hints.') def testHelpTextFunctionWithDefaultsAndTypes(self): component = ( tc.py3.WithDefaultsAndTypes().double) # pytype: disable=module-attr @@ -141,9 +137,6 @@ def testHelpTextFunctionWithDefaultsAndTypes(self): help_screen) self.assertNotIn('NOTES', help_screen) - @testutils.skipIf( - sys.version_info[0:2] < (3, 5), - 'Python < 3.5 does not support type hints.') def testHelpTextFunctionWithTypesAndDefaultNone(self): component = ( tc.py3.WithDefaultsAndTypes().get_int) # pytype: disable=module-attr @@ -159,9 +152,6 @@ def testHelpTextFunctionWithTypesAndDefaultNone(self): help_screen) self.assertNotIn('NOTES', help_screen) - @testutils.skipIf( - sys.version_info[0:2] < (3, 5), - 'Python < 3.5 does not support type hints.') def testHelpTextFunctionWithTypes(self): component = tc.py3.WithTypes().double # pytype: disable=module-attr help_screen = helptext.HelpText( @@ -177,9 +167,6 @@ def testHelpTextFunctionWithTypes(self): 'NOTES\n You can also use flags syntax for POSITIONAL ARGUMENTS', help_screen) - @testutils.skipIf( - sys.version_info[0:2] < (3, 5), - 'Python < 3.5 does not support type hints.') def testHelpTextFunctionWithLongTypes(self): component = tc.py3.WithTypes().long_type # pytype: disable=module-attr help_screen = helptext.HelpText( diff --git a/fire/parser.py b/fire/parser.py index c4708455..d945b8ce 100644 --- a/fire/parser.py +++ b/fire/parser.py @@ -23,6 +23,7 @@ else: _StrNode = ast.Constant + def CreateParser(): parser = argparse.ArgumentParser(add_help=False) parser.add_argument('--verbose', '-v', action='store_true') From 2d950337499156ccb9a17dbf5f389d0c7d10ec24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:09:02 -0400 Subject: [PATCH 17/36] Update setuptools requirement in /.github/scripts (#540) Updates the requirements on [setuptools](https://github.com/pypa/setuptools) to permit the latest version. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/0.6...v75.1.0) --- updated-dependencies: - dependency-name: setuptools dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index f7d3cacd..1932f53d 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -1,5 +1,5 @@ setuptools <65.7.0 ; python_version == '2.7' -setuptools <=69.2.0 ; python_version >= '3.8' +setuptools <=75.1.0 ; python_version >= '3.8' pip <23.0 ; python_version == '2.7' pip ; python_version >= '3.5' pylint <2.15.10 From f012df240f16bd7e1c3eb90e96472ef5051ac5e2 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 21:20:37 -0400 Subject: [PATCH 18/36] Simplify requirements.txt by assuming Python 3 (#543) * Remove universal=1 line * Update setup.py * Update requirements.txt --- .github/scripts/requirements.txt | 9 +++------ setup.cfg | 3 --- setup.py | 3 +-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 1932f53d..d0344221 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -1,13 +1,10 @@ -setuptools <65.7.0 ; python_version == '2.7' -setuptools <=75.1.0 ; python_version >= '3.8' -pip <23.0 ; python_version == '2.7' -pip ; python_version >= '3.5' +setuptools <=75.1.0 +pip pylint <2.15.10 pytest <=8.1.1 pytest-pylint <=1.1.2 pytest-runner <7.0.0 termcolor <2.5.0 hypothesis <6.101.0 -python-Levenshtein <0.20.9 ; python_version == '2.7' -levenshtein <=0.25.1 ; python_version >= '3.5' +levenshtein <=0.25.1 mock <6.0.0 diff --git a/setup.cfg b/setup.cfg index 977056b0..ed53d83b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,3 @@ -[wheel] -universal = 1 - [aliases] test = pytest diff --git a/setup.py b/setup.py index f861f9a5..6adbef46 100644 --- a/setup.py +++ b/setup.py @@ -31,13 +31,12 @@ DEPENDENCIES = [ 'six', 'termcolor', - 'enum34; python_version < "3.4"' ] TEST_DEPENDENCIES = [ 'hypothesis', 'mock', - 'python-Levenshtein', + 'levenshtein', ] VERSION = '0.7.0' From 9825623bd5c66692be16126087d3492eed9a0161 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:26:51 -0400 Subject: [PATCH 19/36] Update hypothesis requirement in /.github/scripts (#544) Updates the requirements on [hypothesis](https://github.com/HypothesisWorks/hypothesis) to permit the latest version. - [Release notes](https://github.com/HypothesisWorks/hypothesis/releases) - [Commits](https://github.com/HypothesisWorks/hypothesis/compare/hypothesis-ruby-0.0.1...hypothesis-python-6.112.1) --- updated-dependencies: - dependency-name: hypothesis dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index d0344221..669f09e1 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -5,6 +5,6 @@ pytest <=8.1.1 pytest-pylint <=1.1.2 pytest-runner <7.0.0 termcolor <2.5.0 -hypothesis <6.101.0 +hypothesis <6.113.0 levenshtein <=0.25.1 mock <6.0.0 From b83fa05b72d8b225182043740e35cd0c28a29293 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 21:30:54 -0400 Subject: [PATCH 20/36] Remove mock in favor of unittest.mock (#545) * Remove mock in favor of unittest.mock --- .github/scripts/requirements.txt | 1 - fire/core_test.py | 3 ++- fire/fire_import_test.py | 2 +- fire/fire_test.py | 3 +-- fire/interact_test.py | 4 ++-- fire/testutils.py | 3 +-- setup.py | 1 - 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 669f09e1..8c0a5dcc 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -7,4 +7,3 @@ pytest-runner <7.0.0 termcolor <2.5.0 hypothesis <6.113.0 levenshtein <=0.25.1 -mock <6.0.0 diff --git a/fire/core_test.py b/fire/core_test.py index 9e1f7dba..90b7f466 100644 --- a/fire/core_test.py +++ b/fire/core_test.py @@ -14,11 +14,12 @@ """Tests for the core module.""" +from unittest import mock + from fire import core from fire import test_components as tc from fire import testutils from fire import trace -import mock class CoreTest(testutils.BaseTestCase): diff --git a/fire/fire_import_test.py b/fire/fire_import_test.py index c5975681..a6b4acc3 100644 --- a/fire/fire_import_test.py +++ b/fire/fire_import_test.py @@ -15,10 +15,10 @@ """Tests importing the fire module.""" import sys +from unittest import mock import fire from fire import testutils -import mock class FireImportTest(testutils.BaseTestCase): diff --git a/fire/fire_test.py b/fire/fire_test.py index 74f1f6e6..99b4a7c6 100644 --- a/fire/fire_test.py +++ b/fire/fire_test.py @@ -16,13 +16,12 @@ import os import sys +from unittest import mock import fire from fire import test_components as tc from fire import testutils -import mock - class FireTest(testutils.BaseTestCase): diff --git a/fire/interact_test.py b/fire/interact_test.py index 99cde285..2f286824 100644 --- a/fire/interact_test.py +++ b/fire/interact_test.py @@ -14,11 +14,11 @@ """Tests for the interact module.""" +from unittest import mock + from fire import interact from fire import testutils -import mock - try: import IPython # pylint: disable=unused-import, g-import-not-at-top diff --git a/fire/testutils.py b/fire/testutils.py index fa1ca86d..816551b5 100644 --- a/fire/testutils.py +++ b/fire/testutils.py @@ -20,12 +20,11 @@ import re import sys import unittest +from unittest import mock from fire import core from fire import trace -import mock - class BaseTestCase(unittest.TestCase): """Shared test case for Python Fire tests.""" diff --git a/setup.py b/setup.py index 6adbef46..53f3381a 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,6 @@ TEST_DEPENDENCIES = [ 'hypothesis', - 'mock', 'levenshtein', ] From 374d8c60787f66b803aab7590fc7ab5e4307d84a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:36:29 -0400 Subject: [PATCH 21/36] Update pytest requirement from <=8.1.1 to <=8.3.3 in /.github/scripts (#546) Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/1.0.0b3...8.3.3) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 8c0a5dcc..b9cd377e 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -1,7 +1,7 @@ setuptools <=75.1.0 pip pylint <2.15.10 -pytest <=8.1.1 +pytest <=8.3.3 pytest-pylint <=1.1.2 pytest-runner <7.0.0 termcolor <2.5.0 From 32b5142151789d5c64618db1ea5b0a47eb27a6c0 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 21:41:22 -0400 Subject: [PATCH 22/36] Update label used by dependabot (#547) --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d31b409b..ba1b7f19 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,7 +7,7 @@ updates: directory: ".github/scripts/" schedule: interval: "monthly" - labels: ["ci"] + labels: ["dependabot"] pull-request-branch-name: separator: "-" open-pull-requests-limit: 5 @@ -23,7 +23,7 @@ updates: gh-actions: patterns: - "*" # Check all dependencies - labels: ["ci"] + labels: ["dependabot"] pull-request-branch-name: separator: "-" open-pull-requests-limit: 5 From 9c9e8c63f745da0aeb6332a1f027b05972f579d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:59:27 -0700 Subject: [PATCH 23/36] Update levenshtein requirement in /.github/scripts (#548) Updates the requirements on [levenshtein](https://github.com/rapidfuzz/Levenshtein) to permit the latest version. - [Release notes](https://github.com/rapidfuzz/Levenshtein/releases) - [Changelog](https://github.com/rapidfuzz/Levenshtein/blob/main/HISTORY.md) - [Commits](https://github.com/rapidfuzz/Levenshtein/compare/v0.13.0...v0.26.0) --- updated-dependencies: - dependency-name: levenshtein dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index b9cd377e..157c77b2 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -6,4 +6,4 @@ pytest-pylint <=1.1.2 pytest-runner <7.0.0 termcolor <2.5.0 hypothesis <6.113.0 -levenshtein <=0.25.1 +levenshtein <=0.26.0 From f9293c9da7fe6645a25448537d40b563d970b2f4 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 22:06:38 -0400 Subject: [PATCH 24/36] Upgrade pylint version (#549) * Upgrade pylint * Fixing lint errors like removing a useless super call * Merging bad-names lists and other modernization in pylintrc --- .github/scripts/requirements.txt | 2 +- fire/core.py | 1 + fire/test_components.py | 3 +-- fire/testutils.py | 3 --- pylintrc | 22 ++-------------------- 5 files changed, 5 insertions(+), 26 deletions(-) diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 157c77b2..a5648989 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -1,6 +1,6 @@ setuptools <=75.1.0 pip -pylint <2.15.10 +pylint <3.2.8 pytest <=8.3.3 pytest-pylint <=1.1.2 pytest-runner <7.0.0 diff --git a/fire/core.py b/fire/core.py index c61a8b57..bce9b641 100644 --- a/fire/core.py +++ b/fire/core.py @@ -872,6 +872,7 @@ def _ParseKeywordArgs(args, fn_spec): key, value = stripped_argument.split('=', 1) else: key = stripped_argument + value = None # value will be set later on. key = key.replace('-', '_') is_bool_syntax = (not contains_equals and diff --git a/fire/test_components.py b/fire/test_components.py index 540a9e16..eb3a9e24 100644 --- a/fire/test_components.py +++ b/fire/test_components.py @@ -388,8 +388,7 @@ def example_generator(n): [0, 1, 2, 3] """ - for i in range(n): - yield i + yield from range(n) def simple_set(): diff --git a/fire/testutils.py b/fire/testutils.py index 816551b5..eca37f43 100644 --- a/fire/testutils.py +++ b/fire/testutils.py @@ -67,9 +67,6 @@ def assertOutputMatches(self, stdout='.*', stderr='.*', capture=True): raise AssertionError('%s: Expected %r to match %r' % (name, value, regexp)) - def assertRaisesRegex(self, *args, **kwargs): # pylint: disable=arguments-differ - return super(BaseTestCase, self).assertRaisesRegex(*args, **kwargs) # pylint: disable=no-member - @contextlib.contextmanager def assertRaisesFireExit(self, code, regexp='.*'): """Asserts that a FireExit error is raised in the context. diff --git a/pylintrc b/pylintrc index 558d3ba2..8896bb5b 100644 --- a/pylintrc +++ b/pylintrc @@ -7,9 +7,6 @@ # pygtk.require(). #init-hook= -# Profiled execution. -profile=no - # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. ignore= @@ -41,14 +38,6 @@ disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-d # (visual studio) and html output-format=text -# Include message's id in output -include-ids=no - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - # Tells whether to display a full report or only the messages reports=yes @@ -59,10 +48,6 @@ reports=yes # (R0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (R0004). -comment=no - [VARIABLES] @@ -79,9 +64,6 @@ additional-builtins= [BASIC] -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input,reduce - # Regular expression which should only match correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ @@ -114,7 +96,7 @@ inlinevar-rgx=^[a-z][a-z0-9_]*$ good-names=i,j,k,ex,main,Run,_ # Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata +bad-names=map,filter,apply,input,reduce,foo,bar,baz,toto,tutu,tata # Regular expression which should only match functions or classes name which do # not require a docstring @@ -186,7 +168,7 @@ max-locals=15 max-returns=6 # Maximum number of branch for function / method body -max-branchs=12 +max-branches=12 # Maximum number of statements in function / method body max-statements=50 From c5f5f9008303a661558339ea3c298d247248fdbe Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 22:18:32 -0400 Subject: [PATCH 25/36] Run github action on pull_request (#550) * Run github action on pull_request --- .github/workflows/build.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f5225c5..a6649201 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,12 @@ name: Python Fire -on: [push] +on: + push: + branches: + - master + pull_request: + branches: + - master jobs: build: From ca4e80b9072397ec1c03bdf83a0bd62524a601aa Mon Sep 17 00:00:00 2001 From: Kai Chen Date: Fri, 20 Sep 2024 10:22:06 +0800 Subject: [PATCH 26/36] Add current system MSYS check (#278) --- fire/console/platforms.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fire/console/platforms.py b/fire/console/platforms.py index 018eb89e..13fd8204 100644 --- a/fire/console/platforms.py +++ b/fire/console/platforms.py @@ -153,6 +153,8 @@ def Current(): return OperatingSystem.MACOSX elif 'cygwin' in sys.platform: return OperatingSystem.CYGWIN + elif 'msys' in sys.platform: + return OperatingSystem.MSYS return None @staticmethod From 4efd44dbb14ba2bf044f2fae701f787da0bfbe1e Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 23:06:02 -0400 Subject: [PATCH 27/36] Remove .format in favor of f-strings (#551) * Remove .format in favor of f-strings --- docs/guide.md | 10 ++--- examples/widget/widget.py | 2 +- fire/completion.py | 7 +-- fire/completion_test.py | 3 +- fire/core.py | 32 +++++++------- fire/helptext.py | 92 +++++++++++++++++---------------------- fire/interact.py | 11 ++--- fire/trace.py | 13 +++--- 8 files changed, 75 insertions(+), 95 deletions(-) diff --git a/docs/guide.md b/docs/guide.md index 44d8a46d..cdc3b2d0 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -30,7 +30,7 @@ the program to the command line. import fire def hello(name): - return 'Hello {name}!'.format(name=name) + return f'Hello {name}!' if __name__ == '__main__': fire.Fire() @@ -52,7 +52,7 @@ command line. import fire def hello(name): - return 'Hello {name}!'.format(name=name) + return f'Hello {name}!' if __name__ == '__main__': fire.Fire(hello) @@ -76,7 +76,7 @@ We can alternatively write this program like this: import fire def hello(name): - return 'Hello {name}!'.format(name=name) + return f'Hello {name}!' def main(): fire.Fire(hello) @@ -93,7 +93,7 @@ then simply this: import fire def hello(name): - return 'Hello {name}!'.format(name=name) + return f'Hello {name}!' def main(): fire.Fire(hello) @@ -105,7 +105,7 @@ If you have a file `example.py` that doesn't even import fire: ```python def hello(name): - return 'Hello {name}!'.format(name=name) + return f'Hello {name}!' ``` Then you can use it with Fire like this: diff --git a/examples/widget/widget.py b/examples/widget/widget.py index bf1cbeb2..9092ad75 100644 --- a/examples/widget/widget.py +++ b/examples/widget/widget.py @@ -25,7 +25,7 @@ def whack(self, n=1): def bang(self, noise='bang'): """Makes a loud noise.""" - return '{noise} bang!'.format(noise=noise) + return f'{noise} bang!' def main(): diff --git a/fire/completion.py b/fire/completion.py index 3aa8ab11..625e9d86 100644 --- a/fire/completion.py +++ b/fire/completion.py @@ -277,10 +277,7 @@ def _FishScript(name, commands, default_options=None): ) return fish_source.format( - global_options=' '.join( - '"{option}"'.format(option=option) - for option in global_options - ) + global_options=' '.join(f'"{option}"' for option in global_options) ) @@ -385,7 +382,7 @@ def _CompletionsFromArgs(fn_args): completions = [] for arg in fn_args: arg = arg.replace('_', '-') - completions.append('--{arg}'.format(arg=arg)) + completions.append(f'--{arg}') return completions diff --git a/fire/completion_test.py b/fire/completion_test.py index 5bafc279..c0d5d24f 100644 --- a/fire/completion_test.py +++ b/fire/completion_test.py @@ -33,9 +33,8 @@ def testCompletionBashScript(self): self.assertIn('command', script) self.assertIn('halt', script) - assert_template = '{command})' for last_command in ['command', 'halt']: - self.assertIn(assert_template.format(command=last_command), script) + self.assertIn(f'{last_command})', script) def testCompletionFishScript(self): # A sanity check test to make sure the fish completion script satisfies diff --git a/fire/core.py b/fire/core.py index bce9b641..e4156760 100644 --- a/fire/core.py +++ b/fire/core.py @@ -139,7 +139,7 @@ def Fire(component=None, command=None, name=None, serialize=None): _DisplayError(component_trace) raise FireExit(2, component_trace) if component_trace.show_trace and component_trace.show_help: - output = ['Fire trace:\n{trace}\n'.format(trace=component_trace)] + output = [f'Fire trace:\n{component_trace}\n'] result = component_trace.GetResult() help_text = helptext.HelpText( result, trace=component_trace, verbose=component_trace.verbose) @@ -147,7 +147,7 @@ def Fire(component=None, command=None, name=None, serialize=None): Display(output, out=sys.stderr) raise FireExit(0, component_trace) if component_trace.show_trace: - output = ['Fire trace:\n{trace}'.format(trace=component_trace)] + output = [f'Fire trace:\n{component_trace}'] Display(output, out=sys.stderr) raise FireExit(0, component_trace) if component_trace.show_help: @@ -231,9 +231,9 @@ def _IsHelpShortcut(component_trace, remaining_args): if show_help: component_trace.show_help = True - command = '{cmd} -- --help'.format(cmd=component_trace.GetCommand()) - print('INFO: Showing help with the command {cmd}.\n'.format( - cmd=shlex.quote(command)), file=sys.stderr) + command = f'{component_trace.GetCommand()} -- --help' + print(f'INFO: Showing help with the command {shlex.quote(command)}.\n', + file=sys.stderr) return show_help @@ -287,9 +287,9 @@ def _DisplayError(component_trace): show_help = True if show_help: - command = '{cmd} -- --help'.format(cmd=component_trace.GetCommand()) - print('INFO: Showing help with the command {cmd}.\n'.format( - cmd=shlex.quote(command)), file=sys.stderr) + command = f'{component_trace.GetCommand()} -- --help' + print(f'INFO: Showing help with the command {shlex.quote(command)}.\n', + file=sys.stderr) help_text = helptext.HelpText(result, trace=component_trace, verbose=component_trace.verbose) output.append(help_text) @@ -327,14 +327,13 @@ def _DictAsString(result, verbose=False): return '{}' longest_key = max(len(str(key)) for key in result_visible.keys()) - format_string = '{{key:{padding}s}} {{value}}'.format(padding=longest_key + 1) + format_string = f'{{key:{longest_key + 1}s}} {{value}}' lines = [] for key, value in result.items(): if completion.MemberVisible(result, key, value, class_attrs=class_attrs, verbose=verbose): - line = format_string.format(key=str(key) + ':', - value=_OneLineResult(value)) + line = format_string.format(key=f'{key}:', value=_OneLineResult(value)) lines.append(line) return '\n'.join(lines) @@ -348,10 +347,10 @@ def _OneLineResult(result): # TODO(dbieber): Show a small amount of usage information about the function # or module if it fits cleanly on the line. if inspect.isfunction(result): - return ''.format(name=result.__name__) + return f'' if inspect.ismodule(result): - return ''.format(name=result.__name__) + return f'' try: # Don't force conversion to ascii. @@ -890,9 +889,10 @@ def _ParseKeywordArgs(args, fn_spec): if len(matching_fn_args) == 1: keyword = matching_fn_args[0] elif len(matching_fn_args) > 1: - raise FireError("The argument '{}' is ambiguous as it could " - "refer to any of the following arguments: {}".format( - argument, matching_fn_args)) + raise FireError( + f"The argument '{argument}' is ambiguous as it could " + f"refer to any of the following arguments: {matching_fn_args}" + ) # Determine the value. if not keyword: diff --git a/fire/helptext.py b/fire/helptext.py index 1c0cb626..e57eb7d8 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -106,7 +106,7 @@ def _NameSection(component, info, trace=None, verbose=False): LINE_LENGTH) if summary: - text = current_command + ' - ' + summary + text = f'{current_command} - {summary}' else: text = current_command return ('NAME', text) @@ -132,11 +132,7 @@ def _SynopsisSection(component, actions_grouped_by_kind, spec, metadata, continuations.append(trace.separator) continuation = ' | '.join(continuations) - synopsis_template = '{current_command} {continuation}' - text = synopsis_template.format( - current_command=current_command, - continuation=continuation) - + text = f'{current_command} {continuation}' return ('SYNOPSIS', text) @@ -243,8 +239,6 @@ def _ArgsAndFlagsSections(info, spec, metadata): if spec.varkw: # Include kwargs documented via :key param: documented_kwargs = [] - flag_string = '--{name}' - short_flag_string = '-{short_name}, --{name}' # add short flags if possible flags = docstring_info.args or [] @@ -253,11 +247,10 @@ def _ArgsAndFlagsSections(info, spec, metadata): for flag in flags: if isinstance(flag, docstrings.KwargInfo): if flag.name[0] in unique_short_flags: - flag_string = short_flag_string.format( - name=flag.name, short_name=flag.name[0] - ) + short_name = flag.name[0] + flag_string = f'-{short_name}, --{flag.name}' else: - flag_string = flag_string.format(name=flag.name) + flag_string = f'--{flag.name}' flag_item = _CreateFlagItem( flag.name, docstring_info, spec, @@ -347,9 +340,9 @@ def _GetArgsAndFlagsString(spec, metadata): for arg in args_with_no_defaults] else: arg_strings = [ - '--{arg}={arg_upper}'.format( - arg=arg, arg_upper=formatting.Underline(arg.upper())) - for arg in args_with_no_defaults] + f'--{arg}={formatting.Underline(arg.upper())}' + for arg in args_with_no_defaults + ] arg_and_flag_strings.extend(arg_strings) # If there are any arguments that are treated as flags: @@ -357,8 +350,8 @@ def _GetArgsAndFlagsString(spec, metadata): arg_and_flag_strings.append('') if spec.varargs: - varargs_string = '[{varargs}]...'.format( - varargs=formatting.Underline(spec.varargs.upper())) + varargs_underlined = formatting.Underline(spec.varargs.upper()) + varargs_string = f'[{varargs_underlined}]...' arg_and_flag_strings.append(varargs_string) return ' '.join(arg_and_flag_strings) @@ -401,7 +394,7 @@ def _GetActionsGroupedByKind(component, verbose=False): if component_len < 10: indexes.Add(name=', '.join(str(x) for x in range(component_len))) else: - indexes.Add(name='0..{max}'.format(max=component_len-1)) + indexes.Add(name=f'0..{component_len-1}') return [groups, commands, values, indexes] @@ -416,10 +409,8 @@ def _GetCurrentCommand(trace=None, include_separators=True): def _CreateOutputSection(name, content): - return """{name} -{content}""".format( - name=formatting.Bold(name), - content=formatting.Indent(content, SECTION_INDENTATION)) + return f"""{formatting.Bold(name)} +{formatting.Indent(content, SECTION_INDENTATION)}""" def _CreateArgItem(arg, docstring_info, spec): @@ -430,7 +421,7 @@ def _CreateArgItem(arg, docstring_info, spec): docstring_info: A docstrings.DocstringInfo namedtuple with information about the containing function's docstring. spec: An instance of fire.inspectutils.FullArgSpec, containing type and - default information about the arguments to a callable. + default information about the arguments to a callable. Returns: A string to be used in constructing the help screen for the function. @@ -445,7 +436,7 @@ def _CreateArgItem(arg, docstring_info, spec): arg_string = formatting.BoldUnderline(arg.upper()) arg_type = _GetArgType(arg, spec) - arg_type = 'Type: {}'.format(arg_type) if arg_type else '' + arg_type = f'Type: {arg_type}' if arg_type else '' available_space = max_str_length - len(arg_type) arg_type = ( formatting.EllipsisTruncate(arg_type, available_space, max_str_length)) @@ -484,14 +475,13 @@ def _CreateFlagItem(flag, docstring_info, spec, required=False, description = _GetArgDescription(flag, docstring_info) if not flag_string: - flag_string_template = '--{flag_name}={flag_name_upper}' - flag_string = flag_string_template.format( - flag_name=flag, - flag_name_upper=formatting.Underline(flag.upper())) + flag_name_upper=formatting.Underline(flag.upper()) + flag_string = f'--{flag}={flag_name_upper}' if required: flag_string += ' (required)' if short_arg: - flag_string = '-{short_flag}, '.format(short_flag=flag[0]) + flag_string + short_flag = flag[0] + flag_string = f'-{short_flag}, {flag_string}' arg_type = _GetArgType(flag, spec) arg_default = _GetArgDefault(flag, spec) @@ -499,14 +489,14 @@ def _CreateFlagItem(flag, docstring_info, spec, required=False, # We need to handle the case where there is a default of None, but otherwise # the argument has another type. if arg_default == 'None': - arg_type = 'Optional[{}]'.format(arg_type) + arg_type = f'Optional[{arg_type}]' - arg_type = 'Type: {}'.format(arg_type) if arg_type else '' + arg_type = f'Type: {arg_type}' if arg_type else '' available_space = max_str_length - len(arg_type) arg_type = ( formatting.EllipsisTruncate(arg_type, available_space, max_str_length)) - arg_default = 'Default: {}'.format(arg_default) if arg_default else '' + arg_default = f'Default: {arg_default}' if arg_default else '' available_space = max_str_length - len(arg_default) arg_default = ( formatting.EllipsisTruncate(arg_default, available_space, max_str_length)) @@ -567,15 +557,15 @@ def _GetArgDefault(flag, spec): def _CreateItem(name, description, indent=2): if not description: return name - return """{name} -{description}""".format(name=name, - description=formatting.Indent(description, indent)) + description = formatting.Indent(description, indent) + return f"""{name} +{description}""" def _GetArgDescription(name, docstring_info): if docstring_info.args: for arg_in_docstring in docstring_info.args: - if arg_in_docstring.name in (name, '*' + name, '**' + name): + if arg_in_docstring.name in (name, f'*{name}', f'**{name}'): return arg_in_docstring.description return None @@ -621,9 +611,9 @@ def _ValuesUsageDetailsSection(component, values): def _NewChoicesSection(name, choices): + name_formatted = formatting.Bold(formatting.Underline(name)) return _CreateItem( - '{name} is one of the following:'.format( - name=formatting.Bold(formatting.Underline(name))), + f'{name_formatted} is one of the following:', '\n' + '\n\n'.join(choices), indent=1) @@ -639,11 +629,6 @@ def UsageText(component, trace=None, verbose=False): Returns: String suitable for display in an error screen. """ - output_template = """Usage: {continued_command} -{availability_lines} -For detailed information on this command, run: - {help_command}""" - # Get the command so far: if trace: command = trace.GetCommand() @@ -687,15 +672,16 @@ def UsageText(component, trace=None, verbose=False): + '--help' ) - return output_template.format( - continued_command=continued_command, - availability_lines=''.join(availability_lines), - help_command=help_command) + return f"""Usage: {continued_command} +{''.join(availability_lines)} +For detailed information on this command, run: + {help_command}""" def _GetPossibleActionsUsageString(possible_actions): if possible_actions: - return '<{actions}>'.format(actions='|'.join(possible_actions)) + actions_str = '|'.join(possible_actions) + return f'<{actions_str}>' return None @@ -704,7 +690,7 @@ def _UsageAvailabilityLines(actions_grouped_by_kind): for action_group in actions_grouped_by_kind: if action_group.members: availability_line = _CreateAvailabilityLine( - header='available {plural}:'.format(plural=action_group.plural), + header=f'available {action_group.plural}:', items=action_group.names ) availability_lines.append(availability_line) @@ -720,7 +706,7 @@ def _GetCallableUsageItems(spec, metadata): accepts_positional_args = metadata.get(decorators.ACCEPTS_POSITIONAL_ARGS) if not accepts_positional_args: - items = ['--{arg}={upper}'.format(arg=arg, upper=arg.upper()) + items = [f'--{arg}={arg.upper()}' for arg in args_with_no_defaults] else: items = [arg.upper() for arg in args_with_no_defaults] @@ -730,7 +716,7 @@ def _GetCallableUsageItems(spec, metadata): items.append('') if spec.varargs: - items.append('[{varargs}]...'.format(varargs=spec.varargs.upper())) + items.append(f'[{spec.varargs.upper()}]...') return items @@ -745,10 +731,10 @@ def _GetCallableAvailabilityLines(spec): args_with_defaults = spec.args[len(spec.args) - len(spec.defaults):] # TODO(dbieber): Handle args_with_no_defaults if not accepts_positional_args. - optional_flags = [('--' + flag) for flag in itertools.chain( + optional_flags = [f'--{flag}' for flag in itertools.chain( args_with_defaults, _KeywordOnlyArguments(spec, required=False))] required_flags = [ - ('--' + flag) for flag in _KeywordOnlyArguments(spec, required=True) + f'--{flag}' for flag in _KeywordOnlyArguments(spec, required=True) ] # Flags section: diff --git a/fire/interact.py b/fire/interact.py index 7bdeb9a7..eccd3990 100644 --- a/fire/interact.py +++ b/fire/interact.py @@ -65,16 +65,17 @@ def _AvailableString(variables, verbose=False): lists = [ ('Modules', modules), ('Objects', other)] - liststrs = [] + list_strs = [] for name, varlist in lists: if varlist: - liststrs.append( - '{name}: {items}'.format(name=name, items=', '.join(sorted(varlist)))) + items_str = ', '.join(sorted(varlist)) + list_strs.append(f'{name}: {items_str}') + lists_str = '\n'.join(list_strs) return ( 'Fire is starting a Python REPL with the following objects:\n' - '{liststrs}\n' - ).format(liststrs='\n'.join(liststrs)) + f'{lists_str}\n' + ) def _EmbedIPython(variables, argv=None): diff --git a/fire/trace.py b/fire/trace.py index 2145186e..68b48ce5 100644 --- a/fire/trace.py +++ b/fire/trace.py @@ -212,10 +212,7 @@ def NeedsSeparator(self): def __str__(self): lines = [] for index, element in enumerate(self.elements): - line = '{index}. {trace_string}'.format( - index=index + 1, - trace_string=element, - ) + line = f'{index + 1}. {element}' lines.append(line) return '\n'.join(lines) @@ -261,7 +258,7 @@ def __init__(self, Args: component: The result of this element of the trace. - action: The type of action (eg instantiating a class) taking place. + action: The type of action (e.g. instantiating a class) taking place. target: (string) The name of the component being acted upon. args: The args consumed by the represented action. filename: The file in which the action is defined, or None if N/A. @@ -301,11 +298,11 @@ def __str__(self): # Format is: {action} "{target}" ({filename}:{lineno}) string = self._action if self._target is not None: - string += ' "{target}"'.format(target=self._target) + string += f' "{self._target}"' if self._filename is not None: path = self._filename if self._lineno is not None: - path += ':{lineno}'.format(lineno=self._lineno) + path += f':{self._lineno}' - string += ' ({path})'.format(path=path) + string += f' ({path})' return string From 93b0e3243b522ae9ab6ebbdc6dcf9a5bb68cc30d Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 23:24:46 -0400 Subject: [PATCH 28/36] Remove six from console, eliminates six entirely (#552) --- fire/console/console_attr.py | 18 ++++++++---------- fire/console/encoding.py | 31 ++++++------------------------- fire/console/files.py | 4 +--- setup.py | 1 - 4 files changed, 15 insertions(+), 39 deletions(-) diff --git a/fire/console/console_attr.py b/fire/console/console_attr.py index 815e16b8..c0a3d784 100644 --- a/fire/console/console_attr.py +++ b/fire/console/console_attr.py @@ -100,8 +100,6 @@ from fire.console import encoding as encoding_util from fire.console import text -import six - # TODO: Unify this logic with console.style.mappings class BoxLineCharacters(object): @@ -355,9 +353,9 @@ def ConvertOutputToUnicode(self, buf): Returns: The console output string buf converted to unicode. """ - if isinstance(buf, six.text_type): + if isinstance(buf, str): buf = buf.encode(self._encoding) - return six.text_type(buf, self._encoding, 'replace') + return str(buf, self._encoding, 'replace') def GetBoxLineCharacters(self): """Returns the box/line drawing characters object. @@ -480,7 +478,7 @@ def DisplayWidth(self, buf): Returns: The display width of buf, handling unicode and ANSI controls. """ - if not isinstance(buf, six.string_types): + if not isinstance(buf, str): # Handle non-string objects like Colorizer(). return len(buf) @@ -595,16 +593,16 @@ def __init__(self, string, color, justify=None): self._justify = justify def __eq__(self, other): - return self._string == six.text_type(other) + return self._string == str(other) def __ne__(self, other): return not self == other def __gt__(self, other): - return self._string > six.text_type(other) + return self._string > str(other) def __lt__(self, other): - return self._string < six.text_type(other) + return self._string < str(other) def __ge__(self, other): return not self < other @@ -692,7 +690,7 @@ def GetCharacterDisplayWidth(char): Returns: The monospaced terminal display width of char: either 0, 1, or 2. """ - if not isinstance(char, six.text_type): + if not isinstance(char, str): # Non-unicode chars have width 1. Don't use this function on control chars. return 1 @@ -779,7 +777,7 @@ def EncodeToBytes(data): return data # Coerce to text that will be converted to bytes. - s = six.text_type(data) + s = str(data) try: # Assume the text can be directly converted to bytes (8-bit ascii). diff --git a/fire/console/encoding.py b/fire/console/encoding.py index 41bda634..0a7fedfc 100644 --- a/fire/console/encoding.py +++ b/fire/console/encoding.py @@ -22,8 +22,6 @@ import sys -import six - def Encode(string, encoding=None): """Encode the text string to a byte string. @@ -35,18 +33,7 @@ def Encode(string, encoding=None): Returns: str, The binary string. """ - if string is None: - return None - if not six.PY2: - # In Python 3, the environment sets and gets accept and return text strings - # only, and it handles the encoding itself so this is not necessary. - return string - if isinstance(string, six.binary_type): - # Already an encoded byte string, we are done - return string - - encoding = encoding or _GetEncoding() - return string.encode(encoding) + return string def Decode(data, encoding=None): @@ -67,20 +54,13 @@ def Decode(data, encoding=None): return None # First we are going to get the data object to be a text string. - # Don't use six.string_types here because on Python 3 bytes is not considered - # a string type and we want to include that. - if isinstance(data, six.text_type) or isinstance(data, six.binary_type): + if isinstance(data, str) or isinstance(data, bytes): string = data else: # Some non-string type of object. - try: - string = six.text_type(data) - except (TypeError, UnicodeError): - # The string cannot be converted to unicode -- default to str() which will - # catch objects with special __str__ methods. - string = str(data) + string = str(data) - if isinstance(string, six.text_type): + if isinstance(string, str): # Our work is done here. return string @@ -199,7 +179,8 @@ def EncodeEnv(env, encoding=None): encoding = encoding or _GetEncoding() return { Encode(k, encoding=encoding): Encode(v, encoding=encoding) - for k, v in six.iteritems(env)} + for k, v in env.items() + } def _GetEncoding(): diff --git a/fire/console/files.py b/fire/console/files.py index 69970f43..97222c3d 100644 --- a/fire/console/files.py +++ b/fire/console/files.py @@ -24,8 +24,6 @@ from fire.console import encoding as encoding_util from fire.console import platforms -import six - def _GetSystemPath(): """Returns properly encoded system PATH variable string.""" @@ -48,7 +46,7 @@ def _FindExecutableOnPath(executable, path, pathext): ValueError: invalid input. """ - if isinstance(pathext, six.string_types): + if isinstance(pathext, str): raise ValueError('_FindExecutableOnPath(..., pathext=\'{0}\') failed ' 'because pathext must be an iterable of strings, but got ' 'a string.'.format(pathext)) diff --git a/setup.py b/setup.py index 53f3381a..82073be4 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,6 @@ A library for automatically generating command line interfaces.""".strip() DEPENDENCIES = [ - 'six', 'termcolor', ] From 5b2dadd7f3912bf8f05e7f1c381631ef5c14cada Mon Sep 17 00:00:00 2001 From: David Bieber Date: Thu, 19 Sep 2024 23:47:19 -0400 Subject: [PATCH 29/36] Move asyncio imports and update docs (#553) --- docs/guide.md | 19 +++++++++++++++++++ fire/core.py | 3 +-- fire/inspectutils.py | 3 +-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/docs/guide.md b/docs/guide.md index cdc3b2d0..444a76ff 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -589,6 +589,25 @@ default values that you don't want to specify. It is also important to remember to change the separator if you want to pass `-` as an argument. +##### Async Functions + +Fire supports calling async functions too. Here's a simple example. + +```python +import asyncio + +async def count_to_ten(): + for i in range(1, 11): + await asyncio.sleep(1) + print(i) + +if __name__ == '__main__': + fire.Fire(count_to_ten) +``` + +Whenever fire encounters a coroutine function, it runs it, blocking until it completes. + + ### Argument Parsing The types of the arguments are determined by their values, rather than by the diff --git a/fire/core.py b/fire/core.py index e4156760..6cd1907e 100644 --- a/fire/core.py +++ b/fire/core.py @@ -49,6 +49,7 @@ def main(argv): --trace: Get the Fire Trace for the command. """ +import asyncio import inspect import json import os @@ -68,8 +69,6 @@ def main(argv): from fire import value_types from fire.console import console_io -import asyncio # pylint: disable=import-error,g-import-not-at-top # pytype: disable=import-error - def Fire(component=None, command=None, name=None, serialize=None): """This function, Fire, is the main entrypoint for Python Fire. diff --git a/fire/inspectutils.py b/fire/inspectutils.py index 0d0b048d..a3ae7c27 100644 --- a/fire/inspectutils.py +++ b/fire/inspectutils.py @@ -14,14 +14,13 @@ """Inspection utility functions for Python Fire.""" +import asyncio import inspect import sys import types from fire import docstrings -import asyncio # pylint: disable=import-error,g-import-not-at-top # pytype: disable=import-error - class FullArgSpec(object): """The arguments of a function, as in Python 3's inspect.FullArgSpec.""" From d3204373c4bba38a09db92f910d048222b8d6f0f Mon Sep 17 00:00:00 2001 From: David Bieber Date: Fri, 20 Sep 2024 07:20:04 -0700 Subject: [PATCH 30/36] Include Python 3.13 in github actions (#554) * Include Python 3.13 in github actions list * Include version in supported versions list --- .github/workflows/build.yml | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6649201..63c87edf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13.0-rc.2"] steps: # Checkout the repo. diff --git a/setup.py b/setup.py index 82073be4..beb367cf 100644 --- a/setup.py +++ b/setup.py @@ -67,6 +67,7 @@ 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Operating System :: OS Independent', 'Operating System :: POSIX', From 36a56c0a777d874f30e39412b2877ab171118d54 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Sat, 21 Sep 2024 08:04:39 -0700 Subject: [PATCH 31/36] Continue upgrade of codebase to Python 3 (#556) Obtained with pyupgrade, reverting any changes that remove a module we still depend on. `uv run pyupgrade **/**.py --py3-plus` --- fire/__main__.py | 6 ++-- fire/core.py | 2 +- fire/decorators_test.py | 12 +++---- fire/helptext.py | 2 +- fire/helptext_test.py | 2 +- fire/inspectutils.py | 6 ++-- fire/main_test.py | 2 +- fire/parser_fuzz_test.py | 2 +- fire/test_components.py | 66 ++++++++++++++++++------------------- fire/test_components_py3.py | 10 +++--- fire/trace.py | 4 +-- 11 files changed, 57 insertions(+), 57 deletions(-) diff --git a/fire/__main__.py b/fire/__main__.py index 11fb1b42..140b4a76 100644 --- a/fire/__main__.py +++ b/fire/__main__.py @@ -54,14 +54,14 @@ def import_from_file_path(path): """ if not os.path.exists(path): - raise IOError('Given file path does not exist.') + raise OSError('Given file path does not exist.') module_name = os.path.basename(path) spec = util.spec_from_file_location(module_name, path) if spec is None: - raise IOError('Unable to load module from specified path.') + raise OSError('Unable to load module from specified path.') module = util.module_from_spec(spec) # pylint: disable=no-member spec.loader.exec_module(module) # pytype: disable=attribute-error @@ -104,7 +104,7 @@ def import_module(module_or_filename): return import_from_file_path(module_or_filename) if os.path.sep in module_or_filename: # Use / to detect if it was a filename. - raise IOError('Fire was passed a filename which could not be found.') + raise OSError('Fire was passed a filename which could not be found.') return import_from_module_name(module_or_filename) # Assume it's a module. diff --git a/fire/core.py b/fire/core.py index 6cd1907e..26a25753 100644 --- a/fire/core.py +++ b/fire/core.py @@ -199,7 +199,7 @@ def __init__(self, code, component_trace): code: (int) Exit code for the Fire CLI. component_trace: (FireTrace) The trace for the Fire command. """ - super(FireExit, self).__init__(code) + super().__init__(code) self.trace = component_trace diff --git a/fire/decorators_test.py b/fire/decorators_test.py index a316b79f..9988743c 100644 --- a/fire/decorators_test.py +++ b/fire/decorators_test.py @@ -19,7 +19,7 @@ from fire import testutils -class NoDefaults(object): +class NoDefaults: """A class for testing decorated functions without default values.""" @decorators.SetParseFns(count=int) @@ -40,7 +40,7 @@ def double(count): return 2 * count -class WithDefaults(object): +class WithDefaults: @decorators.SetParseFns(float) def example1(self, arg1=10): @@ -51,14 +51,14 @@ def example2(self, arg1=10): return arg1, type(arg1) -class MixedArguments(object): +class MixedArguments: @decorators.SetParseFns(float, arg2=str) def example3(self, arg1, arg2): return arg1, arg2 -class PartialParseFn(object): +class PartialParseFn: @decorators.SetParseFns(arg1=str) def example4(self, arg1, arg2): @@ -69,7 +69,7 @@ def example5(self, arg1, arg2): return arg1, arg2 -class WithKwargs(object): +class WithKwargs: @decorators.SetParseFns(mode=str, count=int) def example6(self, **kwargs): @@ -79,7 +79,7 @@ def example6(self, **kwargs): ) -class WithVarArgs(object): +class WithVarArgs: @decorators.SetParseFn(str) def example7(self, arg1, arg2=None, *varargs, **kwargs): # pylint: disable=keyword-arg-before-vararg diff --git a/fire/helptext.py b/fire/helptext.py index e57eb7d8..9b578fac 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -767,7 +767,7 @@ def _CreateAvailabilityLine(header, items, return indented_header + indented_items_text[len(indented_header):] + '\n' -class ActionGroup(object): +class ActionGroup: """A group of actions of the same kind.""" def __init__(self, name, plural): diff --git a/fire/helptext_test.py b/fire/helptext_test.py index 4d35dc0a..d1a3f368 100644 --- a/fire/helptext_test.py +++ b/fire/helptext_test.py @@ -27,7 +27,7 @@ class HelpTest(testutils.BaseTestCase): def setUp(self): - super(HelpTest, self).setUp() + super().setUp() os.environ['ANSI_COLORS_DISABLED'] = '1' def testHelpTextNoDefaults(self): diff --git a/fire/inspectutils.py b/fire/inspectutils.py index a3ae7c27..d1438972 100644 --- a/fire/inspectutils.py +++ b/fire/inspectutils.py @@ -22,7 +22,7 @@ from fire import docstrings -class FullArgSpec(object): +class FullArgSpec: """The arguments of a function, as in Python 3's inspect.FullArgSpec.""" def __init__(self, args=None, varargs=None, varkw=None, defaults=None, @@ -229,7 +229,7 @@ def GetFileAndLine(component): try: unused_code, lineindex = inspect.findsource(component) lineno = lineindex + 1 - except (IOError, IndexError): + except (OSError, IndexError): lineno = None return filename, lineno @@ -268,7 +268,7 @@ def Info(component): try: unused_code, lineindex = inspect.findsource(component) info['line'] = lineindex + 1 - except (TypeError, IOError): + except (TypeError, OSError): info['line'] = None if 'docstring' in info: diff --git a/fire/main_test.py b/fire/main_test.py index a0184620..a2723347 100644 --- a/fire/main_test.py +++ b/fire/main_test.py @@ -43,7 +43,7 @@ class MainModuleFileTest(testutils.BaseTestCase): """Tests to verify correct import behavior for file executables.""" def setUp(self): - super(MainModuleFileTest, self).setUp() + super().setUp() self.file = tempfile.NamedTemporaryFile(suffix='.py') # pylint: disable=consider-using-with self.file.write(b'class Foo:\n def double(self, n):\n return 2 * n\n') self.file.flush() diff --git a/fire/parser_fuzz_test.py b/fire/parser_fuzz_test.py index 9739ec4e..10f497cf 100644 --- a/fire/parser_fuzz_test.py +++ b/fire/parser_fuzz_test.py @@ -53,7 +53,7 @@ def testDefaultParseValueFuzz(self, value): result = parser.DefaultParseValue(value) except TypeError: # It's OK to get a TypeError if the string has the null character. - if u'\x00' in value: + if '\x00' in value: return raise except MemoryError: diff --git a/fire/test_components.py b/fire/test_components.py index eb3a9e24..2dc4e0cc 100644 --- a/fire/test_components.py +++ b/fire/test_components.py @@ -43,7 +43,7 @@ def function_with_help(help=True): # pylint: disable=redefined-builtin return help -class Empty(object): +class Empty: pass @@ -51,20 +51,20 @@ class OldStyleEmpty: # pylint: disable=old-style-class,no-init pass -class WithInit(object): +class WithInit: def __init__(self): pass -class ErrorInConstructor(object): +class ErrorInConstructor: def __init__(self, value='value'): self.value = value raise ValueError('Error in constructor') -class WithHelpArg(object): +class WithHelpArg: """Test class for testing when class has a help= arg.""" def __init__(self, help=True): # pylint: disable=redefined-builtin @@ -72,7 +72,7 @@ def __init__(self, help=True): # pylint: disable=redefined-builtin self.dictionary = {'__help': 'help in a dict'} -class NoDefaults(object): +class NoDefaults: def double(self, count): return 2 * count @@ -81,7 +81,7 @@ def triple(self, count): return 3 * count -class WithDefaults(object): +class WithDefaults: """Class with functions that have default arguments.""" def double(self, count=0): @@ -115,7 +115,7 @@ def triple(self, count=0): return 3 * count -class MixedDefaults(object): +class MixedDefaults: def ten(self): return 10 @@ -127,7 +127,7 @@ def identity(self, alpha, beta='0'): return alpha, beta -class SimilarArgNames(object): +class SimilarArgNames: def identity(self, bool_one=False, bool_two=False): return bool_one, bool_two @@ -136,13 +136,13 @@ def identity2(self, a=None, alpha=None): return a, alpha -class CapitalizedArgNames(object): +class CapitalizedArgNames: def sum(self, Delta=1.0, Gamma=2.0): # pylint: disable=invalid-name return Delta + Gamma -class Annotations(object): +class Annotations: def double(self, count=0): return 2 * count @@ -154,7 +154,7 @@ def triple(self, count=0): triple.__annotations__ = {'count': float} -class TypedProperties(object): +class TypedProperties: """Test class for testing Python Fire with properties of various types.""" def __init__(self): @@ -173,7 +173,7 @@ def __init__(self): self.gamma = 'myexcitingstring' -class VarArgs(object): +class VarArgs: """Test class for testing Python Fire with a property with varargs.""" def cumsums(self, *items): @@ -191,7 +191,7 @@ def varchars(self, alpha=0, beta=0, *chars): # pylint: disable=keyword-arg-befo return alpha, beta, ''.join(chars) -class Underscores(object): +class Underscores: def __init__(self): self.underscore_example = 'fish fingers' @@ -200,20 +200,20 @@ def underscore_function(self, underscore_arg): return underscore_arg -class BoolConverter(object): +class BoolConverter: def as_bool(self, arg=False): return bool(arg) -class ReturnsObj(object): +class ReturnsObj: def get_obj(self, *items): del items # Unused return BoolConverter() -class NumberDefaults(object): +class NumberDefaults: def reciprocal(self, divisor=10.0): return 1.0 / divisor @@ -222,7 +222,7 @@ def integer_reciprocal(self, divisor=10): return 1.0 / divisor -class InstanceVars(object): +class InstanceVars: def __init__(self, arg1, arg2): self.arg1 = arg1 @@ -232,7 +232,7 @@ def run(self, arg1, arg2): return (self.arg1, self.arg2, arg1, arg2) -class Kwargs(object): +class Kwargs: def props(self, **kwargs): return kwargs @@ -244,13 +244,13 @@ def run(self, positional, named=None, **kwargs): return (positional, named, kwargs) -class ErrorRaiser(object): +class ErrorRaiser: def fail(self): raise ValueError('This error is part of a test.') -class NonComparable(object): +class NonComparable: def __eq__(self, other): raise ValueError('Instances of this class cannot be compared.') @@ -259,7 +259,7 @@ def __ne__(self, other): raise ValueError('Instances of this class cannot be compared.') -class EmptyDictOutput(object): +class EmptyDictOutput: def totally_empty(self): return {} @@ -268,7 +268,7 @@ def nothing_printable(self): return {'__do_not_print_me': 1} -class CircularReference(object): +class CircularReference: def create(self): x = {} @@ -276,7 +276,7 @@ def create(self): return x -class OrderedDictionary(object): +class OrderedDictionary: def empty(self): return collections.OrderedDict() @@ -288,7 +288,7 @@ def non_empty(self): return ordered_dict -class NamedTuple(object): +class NamedTuple: """Functions returning named tuples used for testing.""" def point(self): @@ -304,7 +304,7 @@ def matching_names(self): return Point(x='x', y='y') -class CallableWithPositionalArgs(object): +class CallableWithPositionalArgs: """Test class for supporting callable.""" TEST = 1 @@ -326,12 +326,12 @@ def coordinate_sum(self): return self.x + self.y -class CallableWithKeywordArgument(object): +class CallableWithKeywordArgument: """Test class for supporting callable.""" def __call__(self, **kwargs): for key, value in kwargs.items(): - print('%s: %s' % (key, value)) + print('{}: {}'.format(key, value)) def print_msg(self, msg): print(msg) @@ -340,7 +340,7 @@ def print_msg(self, msg): CALLABLE_WITH_KEYWORD_ARGUMENT = CallableWithKeywordArgument() -class ClassWithDocstring(object): +class ClassWithDocstring: """Test class for testing help text output. This is some detail description of this test class. @@ -363,7 +363,7 @@ def print_msg(self, msg=None): print(msg) -class ClassWithMultilineDocstring(object): +class ClassWithMultilineDocstring: """Test class for testing help text output with multiline docstring. This is a test class that has a long docstring description that spans across @@ -413,7 +413,7 @@ class Color(enum.Enum): BLUE = 3 -class HasStaticAndClassMethods(object): +class HasStaticAndClassMethods: """A class with a static method and a class method.""" CLASS_STATE = 1 @@ -467,7 +467,7 @@ def fn_with_code_in_docstring(): return True -class BinaryCanvas(object): +class BinaryCanvas: """A canvas with which to make binary art, one bit at a time.""" def __init__(self, size=10): @@ -500,7 +500,7 @@ def set(self, value): return self -class DefaultMethod(object): +class DefaultMethod: def double(self, number): return 2 * number @@ -511,7 +511,7 @@ def _missing(): return _missing -class InvalidProperty(object): +class InvalidProperty: def double(self, number): return 2 * number diff --git a/fire/test_components_py3.py b/fire/test_components_py3.py index 17fb932c..192302d3 100644 --- a/fire/test_components_py3.py +++ b/fire/test_components_py3.py @@ -31,7 +31,7 @@ def identity(self, *, alpha, beta='0'): return alpha, beta -class KeywordOnly(object): +class KeywordOnly: def double(self, *, count): return count * 2 @@ -43,7 +43,7 @@ def with_default(self, *, x="x"): print("x: " + x) -class LruCacheDecoratedMethod(object): +class LruCacheDecoratedMethod: @functools.lru_cache() def lru_cache_in_class(self, arg1): @@ -55,13 +55,13 @@ def lru_cache_decorated(arg1): return arg1 -class WithAsyncio(object): +class WithAsyncio: async def double(self, count=0): return 2 * count -class WithTypes(object): +class WithTypes: """Class with functions that have default arguments and types.""" def double(self, count: float) -> float: @@ -83,7 +83,7 @@ def long_type( return long_obj -class WithDefaultsAndTypes(object): +class WithDefaultsAndTypes: """Class with functions that have default arguments and types.""" def double(self, count: float = 0) -> float: diff --git a/fire/trace.py b/fire/trace.py index 68b48ce5..3a75cc9c 100644 --- a/fire/trace.py +++ b/fire/trace.py @@ -38,7 +38,7 @@ INTERACTIVE_MODE = 'Entered interactive mode' -class FireTrace(object): +class FireTrace: """A FireTrace represents the steps taken during a single Fire execution. A FireTrace consists of a sequence of FireTraceElement objects. Each element @@ -238,7 +238,7 @@ def NeedsSeparatingHyphenHyphen(self, flag='help'): or flag in spec.kwonlyargs) -class FireTraceElement(object): +class FireTraceElement: """A FireTraceElement represents a single step taken by a Fire execution. Examples of a FireTraceElement are the instantiation of a class or the From 8227364f113fcaf8661290fa3bb0c79741ff3be7 Mon Sep 17 00:00:00 2001 From: Jirka Borovec <6035284+Borda@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:09:24 +0200 Subject: [PATCH 32/36] Update required Python 3.7 in `setup.py` (#555) --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index beb367cf..8d4a381b 100644 --- a/setup.py +++ b/setup.py @@ -77,6 +77,7 @@ keywords='command line interface cli python fire interactive bash tool', + requires_python='>=3.7', packages=['fire', 'fire.console'], install_requires=DEPENDENCIES, From 1c43c36174feb7020e7c5a5c53f3a47bc8e368c8 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Sat, 21 Sep 2024 08:12:09 -0700 Subject: [PATCH 33/36] Bump version number in __init__ to setup.py --- fire/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fire/__init__.py b/fire/__init__.py index 742b03ac..9ff696d3 100644 --- a/fire/__init__.py +++ b/fire/__init__.py @@ -17,4 +17,4 @@ from fire.core import Fire __all__ = ['Fire'] -__version__ = '0.6.0' +__version__ = '0.7.0' From efcf60f7f8202d9887b9da16e4ff81a554b9d023 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Sat, 21 Sep 2024 08:45:04 -0700 Subject: [PATCH 34/36] Lint improvements and type safety (#558) * Clean up of lint errors * Always return an element when requested --- fire/console/encoding.py | 1 + fire/decorators.py | 2 +- fire/formatting_windows.py | 2 +- fire/helptext.py | 2 +- fire/helptext_test.py | 1 - fire/test_components.py | 3 ++- fire/trace.py | 2 +- 7 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fire/console/encoding.py b/fire/console/encoding.py index 0a7fedfc..3ce30cb5 100644 --- a/fire/console/encoding.py +++ b/fire/console/encoding.py @@ -33,6 +33,7 @@ def Encode(string, encoding=None): Returns: str, The binary string. """ + del encoding # Unused. return string diff --git a/fire/decorators.py b/fire/decorators.py index eb5b0d20..2758b0aa 100644 --- a/fire/decorators.py +++ b/fire/decorators.py @@ -107,5 +107,5 @@ def GetMetadata(fn): def GetParseFns(fn): # type: (...) -> dict metadata = GetMetadata(fn) - default = {"default": None, "positional": [], "named": {}} + default = {'default': None, 'positional': [], 'named': {}} return metadata.get(FIRE_PARSE_FNS, default) diff --git a/fire/formatting_windows.py b/fire/formatting_windows.py index f8241eaa..cee6f393 100644 --- a/fire/formatting_windows.py +++ b/fire/formatting_windows.py @@ -31,7 +31,7 @@ def initialize_or_disable(): """Enables ANSI processing on Windows or disables it as needed.""" if HAS_COLORAMA: wrap = True - if (hasattr(sys.stdout, "isatty") + if (hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() and platform.release() == '10'): # Enables native ANSI sequences in console. diff --git a/fire/helptext.py b/fire/helptext.py index 9b578fac..318d6276 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -475,7 +475,7 @@ def _CreateFlagItem(flag, docstring_info, spec, required=False, description = _GetArgDescription(flag, docstring_info) if not flag_string: - flag_name_upper=formatting.Underline(flag.upper()) + flag_name_upper = formatting.Underline(flag.upper()) flag_string = f'--{flag}={flag_name_upper}' if required: flag_string += ' (required)' diff --git a/fire/helptext_test.py b/fire/helptext_test.py index d1a3f368..aeff5240 100644 --- a/fire/helptext_test.py +++ b/fire/helptext_test.py @@ -428,7 +428,6 @@ def testHelpTextMultipleKeywoardArgumentsWithShortArgs(self): self.assertIn('\n --late', help_screen) - class UsageTest(testutils.BaseTestCase): def testUsageOutput(self): diff --git a/fire/test_components.py b/fire/test_components.py index 2dc4e0cc..887a0dc6 100644 --- a/fire/test_components.py +++ b/fire/test_components.py @@ -554,7 +554,7 @@ def fn_with_kwarg_and_defaults(arg1, arg2, opt=True, **kwargs): """ del arg1, arg2, opt return kwargs.get('arg3') -# pylint: enable=g-doc-args,g-doc-return-or-yield + def fn_with_multiple_defaults(first='first', last='last', late='late'): """Function with kwarg and defaults. @@ -565,3 +565,4 @@ def fn_with_multiple_defaults(first='first', last='last', late='late'): """ del last, late return first +# pylint: enable=g-doc-args,g-doc-return-or-yield diff --git a/fire/trace.py b/fire/trace.py index 3a75cc9c..4a6d4776 100644 --- a/fire/trace.py +++ b/fire/trace.py @@ -77,7 +77,7 @@ def GetLastHealthyElement(self): for element in reversed(self.elements): if not element.HasError(): return element - return None + return self.elements[0] # The initial element is always healthy. def HasError(self): """Returns whether the Fire execution encountered a Fire usage error.""" From a0cb1ca3c2697ea7f5f1f7314353f1c7e920088f Mon Sep 17 00:00:00 2001 From: Jirka Borovec <6035284+Borda@users.noreply.github.com> Date: Sun, 22 Sep 2024 18:00:15 +0200 Subject: [PATCH 35/36] Expand build matrix to include mac (#490) * Expand build matrix to include mac (#490) --------- Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: David Bieber --- .github/workflows/build.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 63c87edf..59b0a4ba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,18 +2,23 @@ name: Python Fire on: push: - branches: - - master + branches: ["master"] pull_request: - branches: - - master + branches: ["master"] + +defaults: + run: + shell: bash jobs: build: - runs-on: ubuntu-20.04 + runs-on: ${{ matrix.os }} strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13.0-rc.2"] + os: ["macos-latest", "ubuntu-latest"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13.0-rc.2"] + include: + - {os: "ubuntu-20.04", python-version: "3.7"} steps: # Checkout the repo. @@ -28,7 +33,6 @@ jobs: # Build Python Fire using the build.sh script. - name: Run build script - shell: bash run: ./.github/scripts/build.sh env: PYTHON_VERSION: ${{ matrix.python-version }} From 90b7f824f2e760e6363b0d10c52b1940346a0fa6 Mon Sep 17 00:00:00 2001 From: David Bieber Date: Sun, 22 Sep 2024 09:14:50 -0700 Subject: [PATCH 36/36] Replace Python 2 type hints with real type annotations (#559) * Replace Python 2 type hints with real type annotations --- CONTRIBUTING.md | 3 --- fire/decorators.py | 7 +++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index baae1a6e..b5d67c96 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,12 +40,9 @@ In addition, the project follows a convention of: - Maximum line length: 80 characters - Indentation: 2 spaces (4 for line continuation) - PascalCase for function and method names. -- No type hints, as described in [PEP 484], to maintain compatibility with -Python versions < 3.5. - Single quotes around strings, three double quotes around docstrings. [Google Python Style Guide]: http://google.github.io/styleguide/pyguide.html -[PEP 484]: https://www.python.org/dev/peps/pep-0484 ## Testing diff --git a/fire/decorators.py b/fire/decorators.py index 2758b0aa..914b1de6 100644 --- a/fire/decorators.py +++ b/fire/decorators.py @@ -18,6 +18,7 @@ command line arguments to client code. """ +from typing import Any, Dict import inspect FIRE_METADATA = 'FIRE_METADATA' @@ -80,8 +81,7 @@ def _SetMetadata(fn, attribute, value): setattr(fn, FIRE_METADATA, metadata) -def GetMetadata(fn): - # type: (...) -> dict +def GetMetadata(fn) -> Dict[str, Any]: """Gets metadata attached to the function `fn` as an attribute. Args: @@ -104,8 +104,7 @@ def GetMetadata(fn): return default -def GetParseFns(fn): - # type: (...) -> dict +def GetParseFns(fn) -> Dict[str, Any]: metadata = GetMetadata(fn) default = {'default': None, 'positional': [], 'named': {}} return metadata.get(FIRE_PARSE_FNS, default)