From 1bcb5d1a265ed78e02b57174171aae9d813dbcf5 Mon Sep 17 00:00:00 2001
From: Panagiotis Simakis <sp1thas@autistici.org>
Date: Thu, 5 Oct 2023 18:39:58 +0300
Subject: [PATCH 01/64] Add missing parameter description (#462)

---
 fire/core.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fire/core.py b/fire/core.py
index c1e97367..6367262d 100644
--- a/fire/core.py
+++ b/fire/core.py
@@ -94,6 +94,8 @@ def Fire(component=None, command=None, name=None, serialize=None):
         a string or a list of strings; a list of strings is preferred.
     name: Optional. The name of the command as entered at the command line.
         Used in interactive mode and for generating the completion script.
+    serialize: Optional. If supplied, all objects are serialized to text via
+        the provided callable.
   Returns:
     The result of executing the Fire command. Execution begins with the initial
     target component. The component is updated by using the command arguments

From d44d33d4ac9389854b046ca0270c112693b309e6 Mon Sep 17 00:00:00 2001
From: Max <4649120+maximehk@users.noreply.github.com>
Date: Mon, 13 Nov 2023 19:49:58 +0100
Subject: [PATCH 02/64] Fix missing `$` sign in bash completion (#472)

Related issue  https://github.com/google/python-fire/issues/64

Co-authored-by: Max Hacker <maximeh@google.com>
---
 fire/completion.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fire/completion.py b/fire/completion.py
index 9659ec6a..4393880d 100644
--- a/fire/completion.py
+++ b/fire/completion.py
@@ -104,7 +104,7 @@ def _BashScript(name, commands, default_options=None):
 option_already_entered()
 {{
   local opt
-  for opt in ${{COMP_WORDS[@]:0:COMP_CWORD}}
+  for opt in ${{COMP_WORDS[@]:0:$COMP_CWORD}}
   do
     if [ $1 == $opt ]; then
       return 0

From 1d8a137893222b977fc23764ad6e56293cf83a32 Mon Sep 17 00:00:00 2001
From: Hai Zhu <35182391+cocolato@users.noreply.github.com>
Date: Wed, 3 Jan 2024 04:25:30 +0800
Subject: [PATCH 03/64] remove asyncio.coroutine (#440)

---
 fire/test_components_py3.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/fire/test_components_py3.py b/fire/test_components_py3.py
index 5140921d..17fb932c 100644
--- a/fire/test_components_py3.py
+++ b/fire/test_components_py3.py
@@ -57,8 +57,7 @@ def lru_cache_decorated(arg1):
 
 class WithAsyncio(object):
 
-  @asyncio.coroutine
-  def double(self, count=0):
+  async def double(self, count=0):
     return 2 * count
 
 

From ffb8121ab8d342bad2e613df41fe406b2ed5e133 Mon Sep 17 00:00:00 2001
From: David Bieber <dbieber@google.com>
Date: Tue, 2 Jan 2024 15:45:49 -0500
Subject: [PATCH 04/64] Update build.yml dropping Python 2.7 (#479)

We'll want to reenable later.
---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9bf78e8b..9864ae98 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: ["2.7", "3.5", "3.7", "3.8", "3.9", "3.10"]
+        python-version: ["3.5", "3.7", "3.8", "3.9", "3.10"]
 
     steps:
      # Checkout the repo.

From 8a41c91a474209b38d58839437f5711190fd3cee Mon Sep 17 00:00:00 2001
From: eXcript <excriptbrasil@gmail.com>
Date: Tue, 9 Jan 2024 15:22:03 -0300
Subject: [PATCH 05/64] Update formatting_windows.py (#477)

Add a check if 'isatty' member exists in the object, required for packaging with PyInstaller.
---
 fire/formatting_windows.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fire/formatting_windows.py b/fire/formatting_windows.py
index 2b85820d..4bcf82e0 100644
--- a/fire/formatting_windows.py
+++ b/fire/formatting_windows.py
@@ -35,7 +35,7 @@ def initialize_or_disable():
   """Enables ANSI processing on Windows or disables it as needed."""
   if HAS_COLORAMA:
     wrap = True
-    if sys.stdout.isatty() and platform.release() == '10':
+    if hasattr(sys.stdout, "isatty") and sys.stdout.isatty() and platform.release() == '10':
       # Enables native ANSI sequences in console.
       # Windows 10, 2016, and 2019 only.
 

From 343e6b6cec2d174d511e99dec7e5a24849121c2e Mon Sep 17 00:00:00 2001
From: David Bieber <dbieber@google.com>
Date: Sat, 24 Feb 2024 13:07:41 -0500
Subject: [PATCH 06/64] Add Python 3.11 and Python 3.12 to build workflow
 (#485)

This change brings github actions back into the green.
---
 .github/scripts/requirements.txt | 11 +++++++----
 .github/workflows/build.yml      |  2 +-
 fire/__init__.py                 |  2 +-
 fire/__main__.py                 |  2 +-
 fire/console/encoding.py         | 12 ++++++------
 fire/formatting_windows.py       |  4 +++-
 fire/inspectutils.py             |  4 ++--
 fire/testutils.py                |  2 +-
 pylintrc                         |  2 +-
 setup.py                         |  4 +++-
 10 files changed, 26 insertions(+), 19 deletions(-)

diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt
index 9e48e20d..13880c9c 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -1,10 +1,13 @@
-setuptools <65.7.0
-pip <23.0
+setuptools <65.7.0 ; python_version == '2.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-pylint <=1.1.2
 pytest-runner <6.0.0
 termcolor <2.2.0
 hypothesis <6.62.0
-python-Levenshtein <0.20.9
-mock <5.0.0
\ No newline at end of file
+python-Levenshtein <0.20.9 ; python_version == '2.7'
+levenshtein <=0.25.0 ; python_version >= '3.5'
+mock <5.0.0
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9864ae98..eb510f03 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"]
+        python-version: ["3.5", "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 4cc76210..fae18489 100644
--- a/fire/__init__.py
+++ b/fire/__init__.py
@@ -21,4 +21,4 @@
 from fire.core import Fire
 
 __all__ = ['Fire']
-__version__ = '0.5.0'
+__version__ = '0.6.0'
diff --git a/fire/__main__.py b/fire/__main__.py
index 2ad217d6..9d8227ad 100644
--- a/fire/__main__.py
+++ b/fire/__main__.py
@@ -80,7 +80,7 @@ def import_from_file_path(path):
     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 imp  # pylint: disable=g-import-not-at-top,import-outside-toplevel,deprecated-module,import-error
     module = imp.load_source(module_name, path)
 
   return module, module_name
diff --git a/fire/console/encoding.py b/fire/console/encoding.py
index 780e5a28..41bda634 100644
--- a/fire/console/encoding.py
+++ b/fire/console/encoding.py
@@ -86,7 +86,7 @@ def Decode(data, encoding=None):
 
   try:
     # Just return the string if its pure ASCII.
-    return string.decode('ascii')
+    return string.decode('ascii')  # pytype: disable=attribute-error
   except UnicodeError:
     # The string is not ASCII encoded.
     pass
@@ -94,7 +94,7 @@ def Decode(data, encoding=None):
   # Try the suggested encoding if specified.
   if encoding:
     try:
-      return string.decode(encoding)
+      return string.decode(encoding)  # pytype: disable=attribute-error
     except UnicodeError:
       # Bad suggestion.
       pass
@@ -103,21 +103,21 @@ def Decode(data, encoding=None):
   # be exceptional if a valid extended ascii encoding with extended chars
   # were also a valid UITF-8 encoding.
   try:
-    return string.decode('utf8')
+    return string.decode('utf8')  # pytype: disable=attribute-error
   except UnicodeError:
     # Not a UTF-8 encoding.
     pass
 
   # Try the filesystem encoding.
   try:
-    return string.decode(sys.getfilesystemencoding())
+    return string.decode(sys.getfilesystemencoding())  # pytype: disable=attribute-error
   except UnicodeError:
     # string is not encoded for filesystem paths.
     pass
 
   # Try the system default encoding.
   try:
-    return string.decode(sys.getdefaultencoding())
+    return string.decode(sys.getdefaultencoding())  # pytype: disable=attribute-error
   except UnicodeError:
     # string is not encoded using the default encoding.
     pass
@@ -137,7 +137,7 @@ def Decode(data, encoding=None):
   #   string = '\xdc'
   #   string = string.decode('iso-8859-1')
   #   string = string.encode('ascii', 'backslashreplace')
-  return string.decode('iso-8859-1')
+  return string.decode('iso-8859-1')  # pytype: disable=attribute-error
 
 
 def GetEncodedValue(env, name, default=None):
diff --git a/fire/formatting_windows.py b/fire/formatting_windows.py
index 4bcf82e0..ce0f677d 100644
--- a/fire/formatting_windows.py
+++ b/fire/formatting_windows.py
@@ -35,7 +35,9 @@ def initialize_or_disable():
   """Enables ANSI processing on Windows or disables it as needed."""
   if HAS_COLORAMA:
     wrap = True
-    if hasattr(sys.stdout, "isatty") and sys.stdout.isatty() and platform.release() == '10':
+    if (hasattr(sys.stdout, "isatty")
+        and sys.stdout.isatty()
+        and platform.release() == '10'):
       # Enables native ANSI sequences in console.
       # Windows 10, 2016, and 2019 only.
 
diff --git a/fire/inspectutils.py b/fire/inspectutils.py
index 0fa8e7d3..15f32f91 100644
--- a/fire/inspectutils.py
+++ b/fire/inspectutils.py
@@ -98,10 +98,10 @@ class with an __init__ method.
 def Py2GetArgSpec(fn):
   """A wrapper around getargspec that tries both fn and fn.__call__."""
   try:
-    return inspect.getargspec(fn)  # pylint: disable=deprecated-method
+    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
+      return inspect.getargspec(fn.__call__)  # pylint: disable=deprecated-method,no-member
     raise
 
 
diff --git a/fire/testutils.py b/fire/testutils.py
index ea410e82..5f875147 100644
--- a/fire/testutils.py
+++ b/fire/testutils.py
@@ -74,7 +74,7 @@ def assertOutputMatches(self, stdout='.*', stderr='.*', capture=True):
 
   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
+      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
 
diff --git a/pylintrc b/pylintrc
index b89b16d1..558d3ba2 100644
--- a/pylintrc
+++ b/pylintrc
@@ -32,7 +32,7 @@ enable=indexing-exception,old-raise-syntax
 # Disable the message, report, category or checker with the given id(s). You
 # can either give multiple identifier separated by comma (,) or put this option
 # multiple time.
-disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-disabled,star-args,pointless-except,bad-option-value,global-statement,fixme,suppressed-message,useless-suppression,locally-enabled,file-ignored,wrong-import-order,useless-object-inheritance,no-else-return,super-with-arguments,raise-missing-from,consider-using-f-string,unspecified-encoding,unnecessary-lambda-assignment
+disable=design,similarities,no-self-use,attribute-defined-outside-init,locally-disabled,star-args,pointless-except,bad-option-value,global-statement,fixme,suppressed-message,useless-suppression,locally-enabled,file-ignored,wrong-import-order,useless-object-inheritance,no-else-return,super-with-arguments,raise-missing-from,consider-using-f-string,unspecified-encoding,unnecessary-lambda-assignment,wrong-import-position,ungrouped-imports,deprecated-module
 
 
 [REPORTS]
diff --git a/setup.py b/setup.py
index 8e95f414..24e0e325 100644
--- a/setup.py
+++ b/setup.py
@@ -40,7 +40,7 @@
     'python-Levenshtein',
 ]
 
-VERSION = '0.5.0'
+VERSION = '0.6.0'
 URL = 'https://github.com/google/python-fire'
 
 setup(
@@ -72,6 +72,8 @@
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
+        'Programming Language :: Python :: 3.12',
 
         'Operating System :: OS Independent',
         'Operating System :: POSIX',

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 07/64] Adding GitHub dependabot (#432)

Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
---
 .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 08/64] 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] <support@github.com>
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 09/64] 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] <support@github.com>
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 10/64] #444: Removed pipes dependency (#447)

Co-authored-by: Svayp11 <bayakl227@gmail.com>
---
 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 11/64] 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] <support@github.com>
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 12/64] 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] <support@github.com>
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 13/64] 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] <support@github.com>
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 14/64] 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] <support@github.com>
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 15/64] 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 16/64] 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] <support@github.com>
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 17/64] 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] <support@github.com>
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 18/64] 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] <support@github.com>
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 <wdhongtw@gmail.com>
Date: Sat, 10 Aug 2024 04:16:48 +0800
Subject: [PATCH 19/64] 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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 20:20:44 -0400
Subject: [PATCH 20/64] 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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 20:57:15 -0400
Subject: [PATCH 21/64] 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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 21:08:42 -0400
Subject: [PATCH 22/64] 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 23/64] 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] <support@github.com>
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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 21:20:37 -0400
Subject: [PATCH 24/64] 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 25/64] 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] <support@github.com>
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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 21:30:54 -0400
Subject: [PATCH 26/64] 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 27/64] 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] <support@github.com>
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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 21:41:22 -0400
Subject: [PATCH 28/64] 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 29/64] 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] <support@github.com>
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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 22:06:38 -0400
Subject: [PATCH 30/64] 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 <file or directory> 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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 22:18:32 -0400
Subject: [PATCH 31/64] 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 <foreverbonfy@163.com>
Date: Fri, 20 Sep 2024 10:22:06 +0800
Subject: [PATCH 32/64] 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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 23:06:02 -0400
Subject: [PATCH 33/64] 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 '<function {name}>'.format(name=result.__name__)
+    return f'<function {result.__name__}>'
 
   if inspect.ismodule(result):
-    return '<module {name}>'.format(name=result.__name__)
+    return f'<module {result.__name__}>'
 
   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('<flags>')
 
   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('<flags>')
 
   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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 23:24:46 -0400
Subject: [PATCH 34/64] 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 <dbieber@google.com>
Date: Thu, 19 Sep 2024 23:47:19 -0400
Subject: [PATCH 35/64] 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 <dbieber@google.com>
Date: Fri, 20 Sep 2024 07:20:04 -0700
Subject: [PATCH 36/64] 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 <dbieber@google.com>
Date: Sat, 21 Sep 2024 08:04:39 -0700
Subject: [PATCH 37/64] 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 38/64] 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 <david810@gmail.com>
Date: Sat, 21 Sep 2024 08:12:09 -0700
Subject: [PATCH 39/64] 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 <dbieber@google.com>
Date: Sat, 21 Sep 2024 08:45:04 -0700
Subject: [PATCH 40/64] 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 41/64] 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 <dbieber@google.com>
---
 .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 <dbieber@google.com>
Date: Sun, 22 Sep 2024 09:14:50 -0700
Subject: [PATCH 42/64] 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)

From 8feb04a5936ba7b94e96430cc04f6f4b9ba8170c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 1 Oct 2024 10:34:19 -0400
Subject: [PATCH 43/64] Update pylint requirement from <3.2.8 to <3.3.2 in
 /.github/scripts (#562)

Updates the requirements on [pylint](https://github.com/pylint-dev/pylint) to permit the latest version.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/pylint-version-0.18.1...v3.3.1)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 a5648989..0b32ac05 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -1,6 +1,6 @@
 setuptools <=75.1.0
 pip
-pylint <3.2.8
+pylint <3.3.2
 pytest <=8.3.3
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0

From 0aff6b9452080a1fe53bf8537b5e71bd04986c24 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 13:56:00 -0400
Subject: [PATCH 44/64] Update hypothesis requirement in /.github/scripts
 (#566)

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.116.0)

---
updated-dependencies:
- dependency-name: hypothesis
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 0b32ac05..5c947b3e 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -5,5 +5,5 @@ pytest <=8.3.3
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0
 termcolor <2.5.0
-hypothesis <6.113.0
+hypothesis <6.117.0
 levenshtein <=0.26.0

From dfa1071b7a9aee0ca1538dc4e08236c0c5dedda4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 14:09:07 -0400
Subject: [PATCH 45/64] Update termcolor requirement from <2.5.0 to <2.6.0 in
 /.github/scripts (#569)

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.5.0)

---
updated-dependencies:
- dependency-name: termcolor
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 5c947b3e..6147fcbb 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -4,6 +4,6 @@ pylint <3.3.2
 pytest <=8.3.3
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0
-termcolor <2.5.0
+termcolor <2.6.0
 hypothesis <6.117.0
 levenshtein <=0.26.0

From 98d7fbce9c93a6b0c3b209a8ed1144cd233decc0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 14:14:29 -0400
Subject: [PATCH 46/64] Update setuptools requirement in /.github/scripts
 (#567)

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.3.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 6147fcbb..6df54e28 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -1,4 +1,4 @@
-setuptools <=75.1.0
+setuptools <=75.3.0
 pip
 pylint <3.3.2
 pytest <=8.3.3

From c3b4474ea42c2f5330ee28fea7a9fc31e4c59451 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Nov 2024 18:49:46 -0400
Subject: [PATCH 47/64] Update levenshtein requirement in /.github/scripts
 (#568)

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.1)

---
updated-dependencies:
- dependency-name: levenshtein
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 6df54e28..cf8a3420 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.6.0
 hypothesis <6.117.0
-levenshtein <=0.26.0
+levenshtein <=0.26.1

From deb25efee883191f9dac1cec579d3f96f1e32226 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Dec 2024 11:52:02 -0500
Subject: [PATCH 48/64] Update setuptools requirement in /.github/scripts
 (#573)

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.6.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 cf8a3420..0480a001 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -1,4 +1,4 @@
-setuptools <=75.3.0
+setuptools <=75.6.0
 pip
 pylint <3.3.2
 pytest <=8.3.3

From 6cf45c663075c96b20dd0dfa733c2374545a4ad6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 11 Dec 2024 11:53:58 -0500
Subject: [PATCH 49/64] Update hypothesis requirement in /.github/scripts
 (#574)

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.122.0)

---
updated-dependencies:
- dependency-name: hypothesis
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 0480a001..958c6248 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -5,5 +5,5 @@ pytest <=8.3.3
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0
 termcolor <2.6.0
-hypothesis <6.117.0
+hypothesis <6.123.0
 levenshtein <=0.26.1

From 2e0867d3371db9db6e95fad7f82d58ccb894d94c Mon Sep 17 00:00:00 2001
From: David Bieber <dbieber@google.com>
Date: Sun, 23 Mar 2025 14:43:44 -0400
Subject: [PATCH 50/64] Use Neutral theme for Inspector (#588)

* Use Neutral theme for Inspector
* Catch when theme_name not available
---
 fire/inspectutils.py | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fire/inspectutils.py b/fire/inspectutils.py
index d1438972..06c30ef1 100644
--- a/fire/inspectutils.py
+++ b/fire/inspectutils.py
@@ -256,7 +256,10 @@ def Info(component):
   """
   try:
     from IPython.core import oinspect  # pylint: disable=import-outside-toplevel,g-import-not-at-top
-    inspector = oinspect.Inspector()
+    try:
+      inspector = oinspect.Inspector(theme_name="Neutral")
+    except TypeError:  # Only recent versions of IPython support theme_name.
+      inspector = oinspect.Inspector()
     info = inspector.info(component)
 
     # IPython's oinspect.Inspector.info may return '<no docstring>'

From 45152e18255e5c5803f3805604eb738c50befeff Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Mar 2025 19:34:56 -0400
Subject: [PATCH 51/64] Update pylint requirement from <3.3.2 to <3.3.5 in
 /.github/scripts (#581)

Updates the requirements on [pylint](https://github.com/pylint-dev/pylint) to permit the latest version.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/pylint-version-0.18.1...v3.3.4)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 958c6248..5810abf5 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -1,6 +1,6 @@
 setuptools <=75.6.0
 pip
-pylint <3.3.2
+pylint <3.3.5
 pytest <=8.3.3
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0

From 525708c3d7bcfc36a71e23694f09d0b587a7bf72 Mon Sep 17 00:00:00 2001
From: David Bieber <dbieber@google.com>
Date: Mon, 14 Apr 2025 14:40:30 -0400
Subject: [PATCH 52/64] Use lowercase neutral instead of upper (#596)

---
 fire/inspectutils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fire/inspectutils.py b/fire/inspectutils.py
index 06c30ef1..d9c62ca7 100644
--- a/fire/inspectutils.py
+++ b/fire/inspectutils.py
@@ -257,7 +257,7 @@ def Info(component):
   try:
     from IPython.core import oinspect  # pylint: disable=import-outside-toplevel,g-import-not-at-top
     try:
-      inspector = oinspect.Inspector(theme_name="Neutral")
+      inspector = oinspect.Inspector(theme_name="neutral")
     except TypeError:  # Only recent versions of IPython support theme_name.
       inspector = oinspect.Inspector()
     info = inspector.info(component)

From c5ab602240a160902986e48db8980d59338be944 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Apr 2025 14:40:45 -0400
Subject: [PATCH 53/64] Update hypothesis requirement in /.github/scripts
 (#594)

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.130.6)

---
updated-dependencies:
- dependency-name: hypothesis
  dependency-version: 6.130.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 5810abf5..b922f04a 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -5,5 +5,5 @@ pytest <=8.3.3
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0
 termcolor <2.6.0
-hypothesis <6.123.0
+hypothesis <6.131.0
 levenshtein <=0.26.1

From 8527235d18835223dad5055e29d50664ab5bfb2d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Apr 2025 14:41:02 -0400
Subject: [PATCH 54/64] Update setuptools requirement in /.github/scripts
 (#593)

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...v78.1.0)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 78.1.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 b922f04a..8db34c71 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -1,4 +1,4 @@
-setuptools <=75.6.0
+setuptools <=78.1.0
 pip
 pylint <3.3.5
 pytest <=8.3.3

From fb01c7c619eda3107c7e32c42370573f6f63f33c Mon Sep 17 00:00:00 2001
From: David Bieber <dbieber@google.com>
Date: Wed, 21 May 2025 12:23:08 -0700
Subject: [PATCH 55/64] Call inspectutils.GetClassAttrsDict on component, not
 None (#606)

* inspectutils.GetClassAttrsDict on component, not None
* Remove ubuntu-20.04 in favor of ubuntu-22.04 for Python 3.7
---
 .github/workflows/build.yml | 2 +-
 fire/completion.py          | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 59b0a4ba..75a687f3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,7 +18,7 @@ jobs:
         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"}
+          - {os: "ubuntu-22.04", python-version: "3.7"}
 
     steps:
      # Checkout the repo.
diff --git a/fire/completion.py b/fire/completion.py
index 625e9d86..1597d464 100644
--- a/fire/completion.py
+++ b/fire/completion.py
@@ -321,7 +321,7 @@ def MemberVisible(component, name, member, class_attrs=None, verbose=False):
   if inspect.isclass(component):
     # If class_attrs has not been provided, compute it.
     if class_attrs is None:
-      class_attrs = inspectutils.GetClassAttrsDict(class_attrs) or {}
+      class_attrs = inspectutils.GetClassAttrsDict(component) or {}
     class_attr = class_attrs.get(name)
     if class_attr:
       # Methods and properties should only be accessible on instantiated

From 51974c67bf72ac649ed28015d960884712bcbc0f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 May 2025 12:23:37 -0700
Subject: [PATCH 56/64] Update pylint requirement from <3.3.5 to <3.3.7 in
 /.github/scripts (#591)

Updates the requirements on [pylint](https://github.com/pylint-dev/pylint) to permit the latest version.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/pylint-version-0.18.1...v3.3.6)

---
updated-dependencies:
- dependency-name: pylint
  dependency-version: 3.3.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 8db34c71..82b1be4a 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -1,6 +1,6 @@
 setuptools <=78.1.0
 pip
-pylint <3.3.5
+pylint <3.3.7
 pytest <=8.3.3
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0

From dba7e1d0da014e555d174225fdf5ab4c4574b18b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 1 Jun 2025 10:55:56 -0400
Subject: [PATCH 57/64] Update hypothesis requirement in /.github/scripts
 (#608)

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.132.0)

---
updated-dependencies:
- dependency-name: hypothesis
  dependency-version: 6.132.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
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 82b1be4a..613c4da0 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -5,5 +5,5 @@ pytest <=8.3.3
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0
 termcolor <2.6.0
-hypothesis <6.131.0
+hypothesis <6.133.0
 levenshtein <=0.26.1

From 2e6f8d2b248411fb4bbfb7fbf3701ee96c0e9a61 Mon Sep 17 00:00:00 2001
From: David Bieber <dbieber@google.com>
Date: Fri, 18 Jul 2025 21:23:40 -0400
Subject: [PATCH 58/64] Bump version to 0.7.1 (#609)

* Bump version to 0.7.1

* Bump dependency versions
---
 .github/scripts/requirements.txt | 8 ++++----
 fire/__init__.py                 | 2 +-
 setup.py                         | 2 +-
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt
index 613c4da0..882dd440 100644
--- a/.github/scripts/requirements.txt
+++ b/.github/scripts/requirements.txt
@@ -1,9 +1,9 @@
-setuptools <=78.1.0
+setuptools <=80.9.0
 pip
 pylint <3.3.7
-pytest <=8.3.3
+pytest <=8.3.5
 pytest-pylint <=1.1.2
 pytest-runner <7.0.0
-termcolor <2.6.0
+termcolor <3.2.0
 hypothesis <6.133.0
-levenshtein <=0.26.1
+levenshtein <=0.27.1
diff --git a/fire/__init__.py b/fire/__init__.py
index 9ff696d3..b1470692 100644
--- a/fire/__init__.py
+++ b/fire/__init__.py
@@ -17,4 +17,4 @@
 from fire.core import Fire
 
 __all__ = ['Fire']
-__version__ = '0.7.0'
+__version__ = '0.7.1'
diff --git a/setup.py b/setup.py
index 8d4a381b..23b7b472 100644
--- a/setup.py
+++ b/setup.py
@@ -37,7 +37,7 @@
     'levenshtein',
 ]
 
-VERSION = '0.7.0'
+VERSION = '0.7.1'
 URL = 'https://github.com/google/python-fire'
 
 setup(

From d33056cb32f217c57b432040484901f34b9f5411 Mon Sep 17 00:00:00 2001
From: David Bieber <dbieber@google.com>
Date: Fri, 18 Jul 2025 22:27:20 -0400
Subject: [PATCH 59/64] Move to pyproject.toml (#613)

* Migrate from setup.py and setup.cfg to pyproject.toml
* Point dependabot at the pyproject file
---
 .github/dependabot.yml           |  2 +-
 .github/scripts/build.sh         |  3 +-
 .github/scripts/requirements.txt |  9 ----
 pyproject.toml                   | 69 ++++++++++++++++++++++++++
 requirements.txt                 |  1 -
 setup.cfg                        | 10 ----
 setup.py                         | 85 --------------------------------
 7 files changed, 71 insertions(+), 108 deletions(-)
 delete mode 100644 .github/scripts/requirements.txt
 create mode 100644 pyproject.toml
 delete mode 100644 requirements.txt
 delete mode 100644 setup.cfg
 delete mode 100644 setup.py

diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index ba1b7f19..8be46672 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,7 +4,7 @@ version: 2
 updates:
   # Enable version updates for python
   - package-ecosystem: "pip"
-    directory: ".github/scripts/"
+    directory: "/"
     schedule:
       interval: "monthly"
     labels: ["dependabot"]
diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh
index 111257ae..7f5cf491 100755
--- a/.github/scripts/build.sh
+++ b/.github/scripts/build.sh
@@ -19,8 +19,7 @@ set -e
 
 PYTHON_VERSION=${PYTHON_VERSION:-3.7}
 
-pip install -U -r .github/scripts/requirements.txt
-python setup.py develop
+pip install -e .[test]
 python -m pytest  # Run the tests without IPython.
 pip install ipython
 python -m pytest  # Now run the tests with IPython.
diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt
deleted file mode 100644
index 882dd440..00000000
--- a/.github/scripts/requirements.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-setuptools <=80.9.0
-pip
-pylint <3.3.7
-pytest <=8.3.5
-pytest-pylint <=1.1.2
-pytest-runner <7.0.0
-termcolor <3.2.0
-hypothesis <6.133.0
-levenshtein <=0.27.1
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..6a6ba63e
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,69 @@
+[build-system]
+requires = ["setuptools>=45", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "fire"
+version = "0.7.1"
+description = "A library for automatically generating command line interfaces."
+readme = "README.md"
+license = {text = "Apache-2.0"}
+authors = [
+    {name = "David Bieber", email = "dbieber@google.com"}
+]
+classifiers = [
+    "Development Status :: 4 - Beta",
+    "Intended Audience :: Developers",
+    "Topic :: Software Development :: Libraries :: Python Modules",
+    "Programming Language :: Python",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
+    "Operating System :: OS Independent",
+    "Operating System :: POSIX",
+    "Operating System :: MacOS",
+    "Operating System :: Unix",
+]
+keywords = ["command", "line", "interface", "cli", "python", "fire", "interactive", "bash", "tool"]
+requires-python = ">=3.7"
+dependencies = [
+    "termcolor",
+]
+
+[project.urls]
+Homepage = "https://github.com/google/python-fire"
+Repository = "https://github.com/google/python-fire"
+
+[project.optional-dependencies]
+test = [
+    "setuptools<=80.9.0",
+    "pip",
+    "pylint<3.3.7",
+    "pytest<=8.3.5",
+    "pytest-pylint<=1.1.2",
+    "pytest-runner<7.0.0",
+    "termcolor<3.2.0",
+    "hypothesis<6.133.0",
+    "levenshtein<=0.27.1",
+]
+
+[tool.setuptools.packages.find]
+include = ["fire*"]
+
+[tool.setuptools.package-data]
+fire = ["console/*"]
+
+[tool.pytest.ini_options]
+addopts = [
+    "--ignore=fire/test_components_py3.py",
+    "--ignore=fire/parser_fuzz_test.py"
+]
+
+[tool.pytype]
+inputs = "."
+output = ".pytype"
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 9c558e35..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-.
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index ed53d83b..00000000
--- a/setup.cfg
+++ /dev/null
@@ -1,10 +0,0 @@
-[aliases]
-test = pytest
-
-[tool:pytest]
-addopts = --ignore=fire/test_components_py3.py
-          --ignore=fire/parser_fuzz_test.py
-
-[pytype]
-inputs = .
-output = .pytype
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 23b7b472..00000000
--- a/setup.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# Copyright (C) 2018 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""The setup.py file for Python Fire."""
-
-from setuptools import setup
-
-LONG_DESCRIPTION = """
-Python Fire is a library for automatically generating command line interfaces
-(CLIs) with a single line of code.
-
-It will turn any Python module, class, object, function, etc. (any Python
-component will work!) into a CLI. It's called Fire because when you call Fire(),
-it fires off your command.
-""".strip()
-
-SHORT_DESCRIPTION = """
-A library for automatically generating command line interfaces.""".strip()
-
-DEPENDENCIES = [
-    'termcolor',
-]
-
-TEST_DEPENDENCIES = [
-    'hypothesis',
-    'levenshtein',
-]
-
-VERSION = '0.7.1'
-URL = 'https://github.com/google/python-fire'
-
-setup(
-    name='fire',
-    version=VERSION,
-    description=SHORT_DESCRIPTION,
-    long_description=LONG_DESCRIPTION,
-    url=URL,
-
-    author='David Bieber',
-    author_email='dbieber@google.com',
-    license='Apache Software License',
-
-    classifiers=[
-        'Development Status :: 4 - Beta',
-
-        'Intended Audience :: Developers',
-        'Topic :: Software Development :: Libraries :: Python Modules',
-
-        'License :: OSI Approved :: Apache Software License',
-
-        'Programming Language :: Python',
-        'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.7',
-        'Programming Language :: Python :: 3.8',
-        'Programming Language :: Python :: 3.9',
-        'Programming Language :: Python :: 3.10',
-        'Programming Language :: Python :: 3.11',
-        'Programming Language :: Python :: 3.12',
-        'Programming Language :: Python :: 3.13',
-
-        'Operating System :: OS Independent',
-        'Operating System :: POSIX',
-        'Operating System :: MacOS',
-        'Operating System :: Unix',
-    ],
-
-    keywords='command line interface cli python fire interactive bash tool',
-
-    requires_python='>=3.7',
-    packages=['fire', 'fire.console'],
-
-    install_requires=DEPENDENCIES,
-    tests_require=TEST_DEPENDENCIES,
-)

From 84496196045c96ade6ef7c42ebd374f9b6bddee0 Mon Sep 17 00:00:00 2001
From: David Bieber <dbieber@google.com>
Date: Fri, 18 Jul 2025 23:21:02 -0400
Subject: [PATCH 60/64] Use ty in place of pytype (#617)

* Use ty in place of pytype
---
 .github/scripts/build.sh        |  8 ++++----
 fire/__main__.py                |  4 ++--
 fire/console/console_attr_os.py |  5 +----
 fire/console/encoding.py        | 12 ++++++------
 fire/core.py                    |  4 ++--
 fire/core_test.py               |  4 ++--
 fire/custom_descriptions.py     |  8 ++++----
 fire/decorators.py              |  2 +-
 fire/docstrings.py              |  2 +-
 fire/formatting_windows.py      |  6 +++---
 fire/helptext.py                | 15 +++++++++------
 fire/helptext_test.py           | 12 ++++++------
 fire/inspectutils.py            | 17 ++++++++---------
 fire/main_test.py               |  2 +-
 fire/parser.py                  |  2 +-
 fire/trace.py                   |  2 --
 pyproject.toml                  |  4 ----
 17 files changed, 51 insertions(+), 58 deletions(-)

diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh
index 7f5cf491..d9207dfe 100755
--- a/.github/scripts/build.sh
+++ b/.github/scripts/build.sh
@@ -24,8 +24,8 @@ python -m pytest  # Run the tests without IPython.
 pip install ipython
 python -m pytest  # Now run the tests with IPython.
 pylint fire --ignore=test_components_py3.py,parser_fuzz_test.py,console
-if [[ ${PYTHON_VERSION} == 3.7 ]]; then
-  # Run type-checking.
-  pip install pytype;
-  pytype -x fire/test_components_py3.py;
+if [[ ${PYTHON_VERSION} == 3.12 ]]; then
+  # Run type-checking
+  pip install ty
+  python -m ty check --python $(which python) --exclude fire/test_components_py3.py --exclude fire/console/ --exclude fire/formatting_windows.py
 fi
diff --git a/fire/__main__.py b/fire/__main__.py
index 140b4a76..eb98b1a4 100644
--- a/fire/__main__.py
+++ b/fire/__main__.py
@@ -60,11 +60,11 @@ def import_from_file_path(path):
 
   spec = util.spec_from_file_location(module_name, path)
 
-  if spec is None:
+  if spec is None or spec.loader is None:
     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
+  spec.loader.exec_module(module)
 
   return module, module_name
 
diff --git a/fire/console/console_attr_os.py b/fire/console/console_attr_os.py
index 869c5949..a7f38d4f 100644
--- a/fire/console/console_attr_os.py
+++ b/fire/console/console_attr_os.py
@@ -14,9 +14,6 @@
 # limitations under the License.
 
 """OS specific console_attr helper functions."""
-# This file contains platform specific code which is not currently handled
-# by pytype.
-# pytype: skip-file
 
 from __future__ import absolute_import
 from __future__ import division
@@ -73,7 +70,7 @@ def _GetXY(fd):
     try:
       # This magic incantation converts a struct from ioctl(2) containing two
       # binary shorts to a (rows, columns) int tuple.
-      rc = struct.unpack(b'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 'junk'))
+      rc = struct.unpack(b'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, b'junk'))
       return (rc[1], rc[0]) if rc else None
     except:  # pylint: disable=bare-except
       return None
diff --git a/fire/console/encoding.py b/fire/console/encoding.py
index 3ce30cb5..662342c6 100644
--- a/fire/console/encoding.py
+++ b/fire/console/encoding.py
@@ -67,7 +67,7 @@ def Decode(data, encoding=None):
 
   try:
     # Just return the string if its pure ASCII.
-    return string.decode('ascii')  # pytype: disable=attribute-error
+    return string.decode('ascii')
   except UnicodeError:
     # The string is not ASCII encoded.
     pass
@@ -75,7 +75,7 @@ def Decode(data, encoding=None):
   # Try the suggested encoding if specified.
   if encoding:
     try:
-      return string.decode(encoding)  # pytype: disable=attribute-error
+      return string.decode(encoding)
     except UnicodeError:
       # Bad suggestion.
       pass
@@ -84,21 +84,21 @@ def Decode(data, encoding=None):
   # be exceptional if a valid extended ascii encoding with extended chars
   # were also a valid UITF-8 encoding.
   try:
-    return string.decode('utf8')  # pytype: disable=attribute-error
+    return string.decode('utf8')
   except UnicodeError:
     # Not a UTF-8 encoding.
     pass
 
   # Try the filesystem encoding.
   try:
-    return string.decode(sys.getfilesystemencoding())  # pytype: disable=attribute-error
+    return string.decode(sys.getfilesystemencoding())
   except UnicodeError:
     # string is not encoded for filesystem paths.
     pass
 
   # Try the system default encoding.
   try:
-    return string.decode(sys.getdefaultencoding())  # pytype: disable=attribute-error
+    return string.decode(sys.getdefaultencoding())
   except UnicodeError:
     # string is not encoded using the default encoding.
     pass
@@ -118,7 +118,7 @@ def Decode(data, encoding=None):
   #   string = '\xdc'
   #   string = string.decode('iso-8859-1')
   #   string = string.encode('ascii', 'backslashreplace')
-  return string.decode('iso-8859-1')  # pytype: disable=attribute-error
+  return string.decode('iso-8859-1')
 
 
 def GetEncodedValue(env, name, default=None):
diff --git a/fire/core.py b/fire/core.py
index 26a25753..32e0e9cc 100644
--- a/fire/core.py
+++ b/fire/core.py
@@ -504,7 +504,7 @@ def _Fire(component, args, parsed_flag_args, context, name=None):
 
       # Treat namedtuples as dicts when handling them as a map.
       if inspectutils.IsNamedTuple(component):
-        component_dict = component._asdict()  # pytype: disable=attribute-error
+        component_dict = component._asdict()
       else:
         component_dict = component
 
@@ -519,7 +519,7 @@ def _Fire(component, args, parsed_flag_args, context, name=None):
         # a key as another type.
         # TODO(dbieber): Consider alternatives for accessing non-string keys.
         for key, value in (
-            component_dict.items()):  # pytype: disable=attribute-error
+            component_dict.items()):
           if target == str(key):
             component = value
             handled = True
diff --git a/fire/core_test.py b/fire/core_test.py
index 90b7f466..f48d6e2d 100644
--- a/fire/core_test.py
+++ b/fire/core_test.py
@@ -215,12 +215,12 @@ def serialize(x):
 
   def testLruCacheDecoratorBoundArg(self):
     self.assertEqual(
-        core.Fire(tc.py3.LruCacheDecoratedMethod,  # pytype: disable=module-attr
+        core.Fire(tc.py3.LruCacheDecoratedMethod,
                   command=['lru_cache_in_class', 'foo']), 'foo')
 
   def testLruCacheDecorator(self):
     self.assertEqual(
-        core.Fire(tc.py3.lru_cache_decorated,  # pytype: disable=module-attr
+        core.Fire(tc.py3.lru_cache_decorated,
                   command=['foo']), 'foo')
 
 
diff --git a/fire/custom_descriptions.py b/fire/custom_descriptions.py
index 768f0e23..ef1130a3 100644
--- a/fire/custom_descriptions.py
+++ b/fire/custom_descriptions.py
@@ -131,14 +131,14 @@ def GetStringTypeDescription(obj, available_space, line_length):
 def GetSummary(obj, available_space, line_length):
   obj_type_name = type(obj).__name__
   if obj_type_name in CUSTOM_DESC_SUM_FN_DICT:
-    return CUSTOM_DESC_SUM_FN_DICT.get(obj_type_name)[0](obj, available_space,
-                                                         line_length)
+    return CUSTOM_DESC_SUM_FN_DICT[obj_type_name][0](obj, available_space,
+                                                     line_length)
   return None
 
 
 def GetDescription(obj, available_space, line_length):
   obj_type_name = type(obj).__name__
   if obj_type_name in CUSTOM_DESC_SUM_FN_DICT:
-    return CUSTOM_DESC_SUM_FN_DICT.get(obj_type_name)[1](obj, available_space,
-                                                         line_length)
+    return CUSTOM_DESC_SUM_FN_DICT[obj_type_name][1](obj, available_space,
+                                                     line_length)
   return None
diff --git a/fire/decorators.py b/fire/decorators.py
index 914b1de6..547153c6 100644
--- a/fire/decorators.py
+++ b/fire/decorators.py
@@ -68,7 +68,7 @@ def SetParseFns(*positional, **named):
   def _Decorator(fn):
     parse_fns = GetParseFns(fn)
     parse_fns['positional'] = positional
-    parse_fns['named'].update(named)  # pytype: disable=attribute-error
+    parse_fns['named'].update(named)
     _SetMetadata(fn, FIRE_PARSE_FNS, parse_fns)
     return fn
 
diff --git a/fire/docstrings.py b/fire/docstrings.py
index 2d7c7e63..2adfe5ec 100644
--- a/fire/docstrings.py
+++ b/fire/docstrings.py
@@ -436,7 +436,7 @@ def _consume_line(line_info, state):
   if state.section.new and state.section.format == Formats.RST:
     # The current line starts with an RST directive, e.g. ":param arg:".
     directive = _get_directive(line_info)
-    directive_tokens = directive.split()  # pytype: disable=attribute-error
+    directive_tokens = directive.split()
     if state.section.title == Sections.ARGS:
       name = directive_tokens[-1]
       arg = _get_or_create_arg_by_name(
diff --git a/fire/formatting_windows.py b/fire/formatting_windows.py
index cee6f393..749ab6d0 100644
--- a/fire/formatting_windows.py
+++ b/fire/formatting_windows.py
@@ -21,7 +21,7 @@
 import sys
 
 try:
-  import colorama  # pylint: disable=g-import-not-at-top,  # pytype: disable=import-error
+  import colorama  # pylint: disable=g-import-not-at-top
   HAS_COLORAMA = True
 except ImportError:
   HAS_COLORAMA = False
@@ -38,9 +38,9 @@ def initialize_or_disable():
       # Windows 10, 2016, and 2019 only.
 
       wrap = False
-      kernel32 = ctypes.windll.kernel32  # pytype: disable=module-attr
+      kernel32 = ctypes.windll.kernel32
       enable_virtual_terminal_processing = 0x04
-      out_handle = kernel32.GetStdHandle(subprocess.STD_OUTPUT_HANDLE)  # pylint: disable=line-too-long,  # pytype: disable=module-attr
+      out_handle = kernel32.GetStdHandle(subprocess.STD_OUTPUT_HANDLE)  # pylint: disable=line-too-long,
       # GetConsoleMode fails if the terminal isn't native.
       mode = ctypes.wintypes.DWORD()
       if kernel32.GetConsoleMode(out_handle, ctypes.byref(mode)) == 0:
diff --git a/fire/helptext.py b/fire/helptext.py
index 318d6276..347278da 100644
--- a/fire/helptext.py
+++ b/fire/helptext.py
@@ -29,6 +29,8 @@
 information.
 """
 
+from __future__ import annotations
+
 import collections
 import itertools
 
@@ -85,13 +87,14 @@ def HelpText(component, trace=None, verbose=False):
       + usage_details_sections
       + notes_sections
   )
+  valid_sections = [section for section in sections if section is not None]
   return '\n\n'.join(
-      _CreateOutputSection(*section)
-      for section in sections if section is not None
+      _CreateOutputSection(name, content)
+      for name, content in valid_sections
   )
 
 
-def _NameSection(component, info, trace=None, verbose=False):
+def _NameSection(component, info, trace=None, verbose=False) -> tuple[str, str]:
   """The "Name" section of the help string."""
 
   # Only include separators in the name in verbose mode.
@@ -113,7 +116,7 @@ def _NameSection(component, info, trace=None, verbose=False):
 
 
 def _SynopsisSection(component, actions_grouped_by_kind, spec, metadata,
-                     trace=None):
+                     trace=None) -> tuple[str, str]:
   """The "Synopsis" section of the help string."""
   current_command = _GetCurrentCommand(trace=trace, include_separators=True)
 
@@ -136,7 +139,7 @@ def _SynopsisSection(component, actions_grouped_by_kind, spec, metadata,
   return ('SYNOPSIS', text)
 
 
-def _DescriptionSection(component, info):
+def _DescriptionSection(component, info) -> tuple[str, str] | None:
   """The "Description" sections of the help string.
 
   Args:
@@ -408,7 +411,7 @@ def _GetCurrentCommand(trace=None, include_separators=True):
   return current_command
 
 
-def _CreateOutputSection(name, content):
+def _CreateOutputSection(name: str, content: str) -> str:
   return f"""{formatting.Bold(name)}
 {formatting.Indent(content, SECTION_INDENTATION)}"""
 
diff --git a/fire/helptext_test.py b/fire/helptext_test.py
index aeff5240..c7098fc4 100644
--- a/fire/helptext_test.py
+++ b/fire/helptext_test.py
@@ -125,7 +125,7 @@ def testHelpTextFunctionWithKwargsAndDefaults(self):
 
   def testHelpTextFunctionWithDefaultsAndTypes(self):
     component = (
-        tc.py3.WithDefaultsAndTypes().double)  # pytype: disable=module-attr
+        tc.py3.WithDefaultsAndTypes().double)
     help_screen = helptext.HelpText(
         component=component,
         trace=trace.FireTrace(component, name='double'))
@@ -139,7 +139,7 @@ def testHelpTextFunctionWithDefaultsAndTypes(self):
 
   def testHelpTextFunctionWithTypesAndDefaultNone(self):
     component = (
-        tc.py3.WithDefaultsAndTypes().get_int)  # pytype: disable=module-attr
+        tc.py3.WithDefaultsAndTypes().get_int)
     help_screen = helptext.HelpText(
         component=component,
         trace=trace.FireTrace(component, name='get_int'))
@@ -153,7 +153,7 @@ def testHelpTextFunctionWithTypesAndDefaultNone(self):
     self.assertNotIn('NOTES', help_screen)
 
   def testHelpTextFunctionWithTypes(self):
-    component = tc.py3.WithTypes().double  # pytype: disable=module-attr
+    component = tc.py3.WithTypes().double
     help_screen = helptext.HelpText(
         component=component,
         trace=trace.FireTrace(component, name='double'))
@@ -168,7 +168,7 @@ def testHelpTextFunctionWithTypes(self):
         help_screen)
 
   def testHelpTextFunctionWithLongTypes(self):
-    component = tc.py3.WithTypes().long_type  # pytype: disable=module-attr
+    component = tc.py3.WithTypes().long_type
     help_screen = helptext.HelpText(
         component=component,
         trace=trace.FireTrace(component, name='long_type'))
@@ -263,14 +263,14 @@ def testHelpTextNoInit(self):
     self.assertIn('SYNOPSIS\n    OldStyleEmpty', help_screen)
 
   def testHelpTextKeywordOnlyArgumentsWithDefault(self):
-    component = tc.py3.KeywordOnly.with_default  # pytype: disable=module-attr
+    component = tc.py3.KeywordOnly.with_default
     output = helptext.HelpText(
         component=component, trace=trace.FireTrace(component, 'with_default'))
     self.assertIn('NAME\n    with_default', output)
     self.assertIn('FLAGS\n    -x, --x=X', output)
 
   def testHelpTextKeywordOnlyArgumentsWithoutDefault(self):
-    component = tc.py3.KeywordOnly.double  # pytype: disable=module-attr
+    component = tc.py3.KeywordOnly.double
     output = helptext.HelpText(
         component=component, trace=trace.FireTrace(component, 'double'))
     self.assertIn('NAME\n    double', output)
diff --git a/fire/inspectutils.py b/fire/inspectutils.py
index d9c62ca7..6dd8fd67 100644
--- a/fire/inspectutils.py
+++ b/fire/inspectutils.py
@@ -100,9 +100,9 @@ def Py3GetFullArgSpec(fn):
     An inspect.FullArgSpec namedtuple with the full arg spec of the function.
   """
   # pylint: disable=no-member
-  # pytype: disable=module-attr
+
   try:
-    sig = inspect._signature_from_callable(  # pylint: disable=protected-access
+    sig = inspect._signature_from_callable(  # pylint: disable=protected-access  # type: ignore
         fn,
         skip_bound_arg=True,
         follow_wrapper_chains=True,
@@ -129,19 +129,19 @@ def Py3GetFullArgSpec(fn):
     name = param.name
 
     # pylint: disable=protected-access
-    if kind is inspect._POSITIONAL_ONLY:
+    if kind is inspect._POSITIONAL_ONLY:  # type: ignore
       args.append(name)
-    elif kind is  inspect._POSITIONAL_OR_KEYWORD:
+    elif kind is inspect._POSITIONAL_OR_KEYWORD:  # type: ignore
       args.append(name)
       if param.default is not param.empty:
         defaults += (param.default,)
-    elif kind is  inspect._VAR_POSITIONAL:
+    elif kind is inspect._VAR_POSITIONAL:  # type: ignore
       varargs = name
-    elif kind is  inspect._KEYWORD_ONLY:
+    elif kind is inspect._KEYWORD_ONLY:  # type: ignore
       kwonlyargs.append(name)
       if param.default is not param.empty:
         kwdefaults[name] = param.default
-    elif kind is  inspect._VAR_KEYWORD:
+    elif kind is inspect._VAR_KEYWORD:  # type: ignore
       varkw = name
     if param.annotation is not param.empty:
       annotations[name] = param.annotation
@@ -157,7 +157,6 @@ def Py3GetFullArgSpec(fn):
   return inspect.FullArgSpec(args, varargs, varkw, defaults,
                              kwonlyargs, kwdefaults, annotations)
   # pylint: enable=no-member
-  # pytype: enable=module-attr
 
 
 def GetFullArgSpec(fn):
@@ -259,7 +258,7 @@ def Info(component):
     try:
       inspector = oinspect.Inspector(theme_name="neutral")
     except TypeError:  # Only recent versions of IPython support theme_name.
-      inspector = oinspect.Inspector()
+      inspector = oinspect.Inspector()  # type: ignore
     info = inspector.info(component)
 
     # IPython's oinspect.Inspector.info may return '<no docstring>'
diff --git a/fire/main_test.py b/fire/main_test.py
index a2723347..9e1c382b 100644
--- a/fire/main_test.py
+++ b/fire/main_test.py
@@ -78,7 +78,7 @@ def testFileNameModuleDuplication(self):
   def testFileNameModuleFileFailure(self):
     # Confirm that an invalid file that masks a non-existent module fails.
     with self.assertRaisesRegex(ValueError,
-                                r'Fire can only be called on \.py files\.'):  # pylint: disable=line-too-long,  # pytype: disable=attribute-error
+                                r'Fire can only be called on \.py files\.'):  # pylint: disable=line-too-long,
       dirname = os.path.dirname(self.file.name)
       with testutils.ChangeDirectory(dirname):
         with open('foobar', 'w'):
diff --git a/fire/parser.py b/fire/parser.py
index d945b8ce..b8e7f19c 100644
--- a/fire/parser.py
+++ b/fire/parser.py
@@ -96,7 +96,7 @@ def _LiteralEval(value):
     SyntaxError: If the value string has a syntax error.
   """
   root = ast.parse(value, mode='eval')
-  if isinstance(root.body, ast.BinOp):  # pytype: disable=attribute-error
+  if isinstance(root.body, ast.BinOp):
     raise ValueError(value)
 
   for node in ast.walk(root):
diff --git a/fire/trace.py b/fire/trace.py
index 4a6d4776..601026fd 100644
--- a/fire/trace.py
+++ b/fire/trace.py
@@ -62,9 +62,7 @@ def __init__(self, initial_component, name=None, separator='-', verbose=False,
 
   def GetResult(self):
     """Returns the component from the last element of the trace."""
-    # pytype: disable=attribute-error
     return self.GetLastHealthyElement().component
-    # pytype: enable=attribute-error
 
   def GetLastHealthyElement(self):
     """Returns the last element of the trace that is not an error.
diff --git a/pyproject.toml b/pyproject.toml
index 6a6ba63e..dfb1eeba 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -63,7 +63,3 @@ addopts = [
     "--ignore=fire/test_components_py3.py",
     "--ignore=fire/parser_fuzz_test.py"
 ]
-
-[tool.pytype]
-inputs = "."
-output = ".pytype"

From cec0119b10d2007e9de7c58ea4d7eac22682dc04 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Jul 2025 23:22:05 -0400
Subject: [PATCH 61/64] Update hypothesis requirement from <6.133.0 to <6.136.0
 (#616)

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.135.33)

---
updated-dependencies:
- dependency-name: hypothesis
  dependency-version: 6.135.33
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 pyproject.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyproject.toml b/pyproject.toml
index dfb1eeba..b42c0aec 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -48,7 +48,7 @@ test = [
     "pytest-pylint<=1.1.2",
     "pytest-runner<7.0.0",
     "termcolor<3.2.0",
-    "hypothesis<6.133.0",
+    "hypothesis<6.136.0",
     "levenshtein<=0.27.1",
 ]
 

From 8c62e05569cd7111731e2f704cbba5e3e4157b01 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Jul 2025 23:25:48 -0400
Subject: [PATCH 62/64] Update pytest requirement from <=8.3.5 to <=8.4.1
 (#615)

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.4.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.4.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 pyproject.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyproject.toml b/pyproject.toml
index b42c0aec..34ad8426 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -44,7 +44,7 @@ test = [
     "setuptools<=80.9.0",
     "pip",
     "pylint<3.3.7",
-    "pytest<=8.3.5",
+    "pytest<=8.4.1",
     "pytest-pylint<=1.1.2",
     "pytest-runner<7.0.0",
     "termcolor<3.2.0",

From 86bf4ca693106a85827d9419ae36ff2c7ac29a9a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Jul 2025 23:30:59 -0400
Subject: [PATCH 63/64] Update pylint requirement from <3.3.7 to <3.3.8 (#614)

Updates the requirements on [pylint](https://github.com/pylint-dev/pylint) to permit the latest version.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/pylint-version-0.18.1...v3.3.7)

---
updated-dependencies:
- dependency-name: pylint
  dependency-version: 3.3.7
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 pyproject.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pyproject.toml b/pyproject.toml
index 34ad8426..b9a7217b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -43,7 +43,7 @@ Repository = "https://github.com/google/python-fire"
 test = [
     "setuptools<=80.9.0",
     "pip",
-    "pylint<3.3.7",
+    "pylint<3.3.8",
     "pytest<=8.4.1",
     "pytest-pylint<=1.1.2",
     "pytest-runner<7.0.0",

From ea8c7f5e74157c9f6bf2e251fce8ddcac81ef3d5 Mon Sep 17 00:00:00 2001
From: David Bieber <david810@gmail.com>
Date: Sat, 19 Jul 2025 08:20:55 -0400
Subject: [PATCH 64/64] Remove unused MANIFEST

---
 MANIFEST.in | 1 -
 1 file changed, 1 deletion(-)
 delete mode 100644 MANIFEST.in

diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 1aba38f6..00000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1 +0,0 @@
-include LICENSE