From 77d9e39a6e59b0a44c81af63773ef7c1bb8fa84d Mon Sep 17 00:00:00 2001 From: Jirka B Date: Mon, 23 Sep 2024 14:13:42 +0200 Subject: [PATCH 1/4] lint: configure pre-commit with Ruff --- .github/scripts/build.sh | 2 +- .github/scripts/requirements.txt | 3 +-- .pre-commit-config.yaml | 27 +++++++++++++++++++++++++++ CONTRIBUTING.md | 2 +- pyproject.toml | 29 +++++++++++++++++++++++++++++ setup.cfg | 10 ---------- 6 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 pyproject.toml delete mode 100644 setup.cfg diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 111257ae..68528188 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -24,7 +24,7 @@ python setup.py develop 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 +pre-commit run --all-files if [[ ${PYTHON_VERSION} == 3.7 ]]; then # Run type-checking. pip install pytype; diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index a5648989..f5d59430 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -1,8 +1,7 @@ setuptools <=75.1.0 pip -pylint <3.2.8 +pre-commit pytest <=8.3.3 -pytest-pylint <=1.1.2 pytest-runner <7.0.0 termcolor <2.5.0 hypothesis <6.113.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..1dff39c5 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,27 @@ +default_language_version: + python: python3 + +#ci: +# autofix_prs: true +# autoupdate_commit_msg: "[pre-commit.ci] pre-commit suggestions" +# autoupdate_schedule: quarterly + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-json + - id: check-yaml + - id: check-toml + - id: detect-private-key + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.5 + hooks: + # try to fix what is possible + - id: ruff + args: ["--fix"] + # perform formatting updates + #- id: ruff-format diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b5d67c96..8e217d45 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,6 +54,6 @@ Then run the tests by running `pytest` in the root directory of the repository. ## Linting Please run lint on your pull requests to make accepting the requests easier. -To do this, run `pylint fire` in the root directory of the repository. +To do this, run `pre-commit run --all-files` in the root directory of the repository. Note that even if lint is passing, additional style changes to your submission may be made during merging. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..4d55c00c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,29 @@ +[tool.aliases] +test = "pytest" + +[tool.pytest] +addopts = [ + "--ignore=fire/test_components_py3.py", + "--ignore=fire/parser_fuzz_test.py" +] + +[tool.pytype] +inputs = "." +output = ".pytype" + +[tool.ruff] +# Enable specific rule categories +select = ["E", "F", "W", "C90"] + +# Exclude specific files and directories +exclude = ["build", "dist", ".venv"] + +# Set the maximum line length +line-length = 80 + +# Enable autofix for certain rules +fixable = ["E", "F", "W", "C90"] + +# Specify additional settings for specific rules +[tool.ruff.per-file-ignores] +"tests/*" = ["D", "E"] \ No newline at end of file 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 From 18122529ed5b02f5d8eb2986628cbc42cfcde5d6 Mon Sep 17 00:00:00 2001 From: Jirka B Date: Mon, 23 Sep 2024 14:38:47 +0200 Subject: [PATCH 2/4] linting with Ruff --- fire/__main__.py | 2 +- fire/console/console_attr_os.py | 10 +++++----- fire/console/encoding.py | 4 ++-- fire/console/text.py | 1 - fire/decorators.py | 2 +- fire/docstrings_test.py | 12 +++++++----- fire/fire_test.py | 4 ++-- fire/formatting.py | 1 - fire/formatting_windows.py | 5 +++-- fire/inspectutils.py | 2 +- fire/interact_test.py | 2 +- fire/main_test.py | 6 ++++-- fire/parser.py | 2 +- fire/test_components.py | 1 - fire/test_components_py3.py | 1 - pyproject.toml | 20 ++++++++++++-------- 16 files changed, 40 insertions(+), 35 deletions(-) diff --git a/fire/__main__.py b/fire/__main__.py index 140b4a76..dee41214 100644 --- a/fire/__main__.py +++ b/fire/__main__.py @@ -64,7 +64,7 @@ def import_from_file_path(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 + 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..54914fe0 100644 --- a/fire/console/console_attr_os.py +++ b/fire/console/console_attr_os.py @@ -47,7 +47,7 @@ def GetTermSize(): xy = get_terminal_size() if xy: break - except: # pylint: disable=bare-except + except: # noqa: E722 pass return xy or (80, 24) @@ -75,7 +75,7 @@ def _GetXY(fd): # binary shorts to a (rows, columns) int tuple. rc = struct.unpack(b'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 'junk')) return (rc[1], rc[0]) if rc else None - except: # pylint: disable=bare-except + except: # noqa: E722 return None xy = _GetXY(0) or _GetXY(1) or _GetXY(2) @@ -84,7 +84,7 @@ def _GetXY(fd): try: fd = os.open(os.ctermid(), os.O_RDONLY) xy = _GetXY(fd) - except: # pylint: disable=bare-except + except: # noqa: E722 xy = None finally: if fd is not None: @@ -153,7 +153,7 @@ def GetRawKeyFunction(): _GetRawKeyFunctionWindows): try: return get_raw_key_function() - except: # pylint: disable=bare-except + except: # noqa: E722 pass return lambda: None @@ -212,7 +212,7 @@ def _GetKeyChar(): c = prev_c break return ansi_to_key.get(c, '') - except: # pylint:disable=bare-except + except: # noqa: E722 c = None finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) diff --git a/fire/console/encoding.py b/fire/console/encoding.py index 3ce30cb5..f7ff8fdd 100644 --- a/fire/console/encoding.py +++ b/fire/console/encoding.py @@ -91,14 +91,14 @@ def Decode(data, encoding=None): # Try the filesystem encoding. try: - return string.decode(sys.getfilesystemencoding()) # pytype: disable=attribute-error + return string.decode(sys.getfilesystemencoding()) # pytype: disable=attribute-error, # noqa: E501 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()) # pytype: disable=attribute-error, # noqa: E501 except UnicodeError: # string is not encoded using the default encoding. pass diff --git a/fire/console/text.py b/fire/console/text.py index 73e68488..9574bd6d 100644 --- a/fire/console/text.py +++ b/fire/console/text.py @@ -100,4 +100,3 @@ class TextTypes(_TextTypes): OUTPUT = 7 PT_SUCCESS = 8 PT_FAILURE = 9 - diff --git a/fire/decorators.py b/fire/decorators.py index 914b1de6..5c23c9d1 100644 --- a/fire/decorators.py +++ b/fire/decorators.py @@ -100,7 +100,7 @@ def GetMetadata(fn) -> Dict[str, Any]: return metadata else: return default - except: # pylint: disable=bare-except + except: # noqa: E722 return default diff --git a/fire/docstrings_test.py b/fire/docstrings_test.py index ce516944..3d3efaa5 100644 --- a/fire/docstrings_test.py +++ b/fire/docstrings_test.py @@ -46,7 +46,9 @@ def test_one_line_simple_whitespace(self): def test_one_line_too_long(self): # pylint: disable=line-too-long - docstring = """A one line docstring that is both a little too verbose and a little too long so it keeps going well beyond a reasonable length for a one-liner. + docstring = """A one line docstring that is both a little too verbose + and a little too long so it keeps going well beyond a reasonable length + for a one-liner. """ # pylint: enable=line-too-long docstring_info = docstrings.parse(docstring) @@ -59,8 +61,8 @@ def test_one_line_too_long(self): def test_one_line_runs_over(self): # pylint: disable=line-too-long - docstring = """A one line docstring that is both a little too verbose and a little too long - so it runs onto a second line. + docstring = """A one line docstring that is both a little too verbose + and a little too long so it runs onto a second line. """ # pylint: enable=line-too-long docstring_info = docstrings.parse(docstring) @@ -72,8 +74,8 @@ def test_one_line_runs_over(self): def test_one_line_runs_over_whitespace(self): docstring = """ - A one line docstring that is both a little too verbose and a little too long - so it runs onto a second line. + A one line docstring that is both a little too verbose + and a little too long so it runs onto a second line. """ docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( diff --git a/fire/fire_test.py b/fire/fire_test.py index 99b4a7c6..39321e1a 100644 --- a/fire/fire_test.py +++ b/fire/fire_test.py @@ -323,10 +323,10 @@ def testFireObjectWithTupleAsObject(self): def testFireNoComponent(self): self.assertEqual(fire.Fire(command=['tc', 'WithDefaults', 'double', '10']), 20) - last_char = lambda text: text[-1] # pylint: disable=unused-variable + # last_char = lambda text: text[-1] self.assertEqual(fire.Fire(command=['last_char', '"Hello"']), 'o') self.assertEqual(fire.Fire(command=['last-char', '"World"']), 'd') - rset = lambda count=0: set(range(count)) # pylint: disable=unused-variable + # rset = lambda count=0: set(range(count)) self.assertEqual(fire.Fire(command=['rset', '5']), {0, 1, 2, 3, 4}) def testFireUnderscores(self): diff --git a/fire/formatting.py b/fire/formatting.py index 68484c27..d51b1070 100644 --- a/fire/formatting.py +++ b/fire/formatting.py @@ -14,7 +14,6 @@ """Formatting utilities for use in creating help text.""" -from fire import formatting_windows # pylint: disable=unused-import import termcolor diff --git a/fire/formatting_windows.py b/fire/formatting_windows.py index cee6f393..08587cf6 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 # pytype: disable=import-error HAS_COLORAMA = True except ImportError: HAS_COLORAMA = False @@ -40,7 +40,8 @@ def initialize_or_disable(): wrap = False kernel32 = ctypes.windll.kernel32 # pytype: disable=module-attr 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( # pytype: disable=module-attr + subprocess.STD_OUTPUT_HANDLE) # 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/inspectutils.py b/fire/inspectutils.py index d1438972..b4ff78f8 100644 --- a/fire/inspectutils.py +++ b/fire/inspectutils.py @@ -344,5 +344,5 @@ def GetClassAttrsDict(component): def IsCoroutineFunction(fn): try: return asyncio.iscoroutinefunction(fn) - except: # pylint: disable=bare-except + except: # noqa: E722 return False diff --git a/fire/interact_test.py b/fire/interact_test.py index 2f286824..4b4718ed 100644 --- a/fire/interact_test.py +++ b/fire/interact_test.py @@ -21,7 +21,7 @@ try: - import IPython # pylint: disable=unused-import, g-import-not-at-top + import IPython # noqa: F401 INTERACT_METHOD = 'IPython.start_ipython' except ImportError: INTERACT_METHOD = 'code.InteractiveConsole' diff --git a/fire/main_test.py b/fire/main_test.py index a2723347..cd3cd3f9 100644 --- a/fire/main_test.py +++ b/fire/main_test.py @@ -77,8 +77,10 @@ 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 + with self.assertRaisesRegex( + ValueError, + r'Fire can only be called on \.py files\.' + ): # pytype: disable=attribute-error 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/test_components.py b/fire/test_components.py index 887a0dc6..a49868c5 100644 --- a/fire/test_components.py +++ b/fire/test_components.py @@ -18,7 +18,6 @@ import enum import functools -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/test_components_py3.py b/fire/test_components_py3.py index 192302d3..697cf6b9 100644 --- a/fire/test_components_py3.py +++ b/fire/test_components_py3.py @@ -14,7 +14,6 @@ """This module has components that use Python 3 specific syntax.""" -import asyncio import functools from typing import Tuple diff --git a/pyproject.toml b/pyproject.toml index 4d55c00c..13a9b6e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,18 +12,22 @@ inputs = "." output = ".pytype" [tool.ruff] +# Set the maximum line length +line-length = 80 + # Enable specific rule categories -select = ["E", "F", "W", "C90"] +lint.select = ["E", "F", "W", "RUF100"] # Exclude specific files and directories -exclude = ["build", "dist", ".venv"] - -# Set the maximum line length -line-length = 80 +lint.exclude = ["build", "dist", ".venv"] # Enable autofix for certain rules -fixable = ["E", "F", "W", "C90"] +lint.fixable = ["E", "F", "W"] + +# exlude some rules globally +lint.ignore = ["E731"] # Specify additional settings for specific rules -[tool.ruff.per-file-ignores] -"tests/*" = ["D", "E"] \ No newline at end of file +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["E501", "E722"] +"fire/helptext_test.py" = ["E501"] From 27190ff10289853df00666186c703012552f72eb Mon Sep 17 00:00:00 2001 From: Jirka B Date: Mon, 23 Sep 2024 14:39:51 +0200 Subject: [PATCH 3/4] remove # pylint: ... --- fire/__main__.py | 4 ++-- fire/completion_test.py | 4 ++-- fire/console/console_attr.py | 4 ++-- fire/console/console_attr_os.py | 16 ++++++++-------- fire/console/platforms.py | 6 +++--- fire/core.py | 2 +- fire/core_test.py | 10 +++++----- fire/decorators_test.py | 2 +- fire/docstrings_test.py | 14 +++++++------- fire/helptext.py | 2 +- fire/inspectutils.py | 14 +++++++------- fire/interact.py | 4 ++-- fire/main_test.py | 4 ++-- fire/test_components.py | 23 +++++++++++------------ fire/test_components_py3.py | 2 +- fire/test_components_test.py | 4 ++-- fire/testutils.py | 3 +-- 17 files changed, 58 insertions(+), 60 deletions(-) diff --git a/fire/__main__.py b/fire/__main__.py index dee41214..3fdd3217 100644 --- a/fire/__main__.py +++ b/fire/__main__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# pylint: disable=invalid-name + """Enables use of Python Fire as a "main" function (i.e. "python -m fire"). This allows using Fire with third-party libraries without modifying their code. @@ -63,7 +63,7 @@ def import_from_file_path(path): if spec is None: raise OSError('Unable to load module from specified path.') - module = util.module_from_spec(spec) # pylint: disable=no-member + module = util.module_from_spec(spec) spec.loader.exec_module(module) return module, module_name diff --git a/fire/completion_test.py b/fire/completion_test.py index c0d5d24f..173006ec 100644 --- a/fire/completion_test.py +++ b/fire/completion_test.py @@ -29,7 +29,7 @@ def testCompletionBashScript(self): ['halt'], ['halt', '--now'], ] - script = completion._BashScript(name='command', commands=commands) # pylint: disable=protected-access + script = completion._BashScript(name='command', commands=commands) self.assertIn('command', script) self.assertIn('halt', script) @@ -44,7 +44,7 @@ def testCompletionFishScript(self): ['halt'], ['halt', '--now'], ] - script = completion._FishScript(name='command', commands=commands) # pylint: disable=protected-access + script = completion._FishScript(name='command', commands=commands) self.assertIn('command', script) self.assertIn('halt', script) self.assertIn('-l now', script) diff --git a/fire/console/console_attr.py b/fire/console/console_attr.py index c0a3d784..6043982d 100644 --- a/fire/console/console_attr.py +++ b/fire/console/console_attr.py @@ -649,7 +649,7 @@ def GetConsoleAttr(encoding=None, reset=False): Returns: The global ConsoleAttr state object. """ - attr = ConsoleAttr._CONSOLE_ATTR_STATE # pylint: disable=protected-access + attr = ConsoleAttr._CONSOLE_ATTR_STATE if not reset: if not attr: reset = True @@ -657,7 +657,7 @@ def GetConsoleAttr(encoding=None, reset=False): reset = True if reset: attr = ConsoleAttr(encoding=encoding) - ConsoleAttr._CONSOLE_ATTR_STATE = attr # pylint: disable=protected-access + ConsoleAttr._CONSOLE_ATTR_STATE = attr return attr diff --git a/fire/console/console_attr_os.py b/fire/console/console_attr_os.py index 54914fe0..24b9a7a0 100644 --- a/fire/console/console_attr_os.py +++ b/fire/console/console_attr_os.py @@ -54,11 +54,11 @@ def GetTermSize(): def _GetTermSizePosix(): """Returns the Posix terminal x and y dimensions.""" - # pylint: disable=g-import-not-at-top + import fcntl - # pylint: disable=g-import-not-at-top + import struct - # pylint: disable=g-import-not-at-top + import termios def _GetXY(fd): @@ -96,7 +96,7 @@ def _GetTermSizeWindows(): """Returns the Windows terminal x and y dimensions.""" # pylint:disable=g-import-not-at-top import struct - # pylint: disable=g-import-not-at-top + from ctypes import create_string_buffer # pylint:disable=g-import-not-at-top from ctypes import windll @@ -124,7 +124,7 @@ def _GetTermSizeEnvironment(): def _GetTermSizeTput(): """Returns the terminal x and y dimensions from tput(1).""" - import subprocess # pylint: disable=g-import-not-at-top + import subprocess output = encoding.Decode(subprocess.check_output(['tput', 'cols'], stderr=subprocess.STDOUT)) cols = int(output) @@ -160,9 +160,9 @@ def GetRawKeyFunction(): def _GetRawKeyFunctionPosix(): """_GetRawKeyFunction helper using Posix APIs.""" - # pylint: disable=g-import-not-at-top + import tty - # pylint: disable=g-import-not-at-top + import termios def _GetRawKeyPosix(): @@ -223,7 +223,7 @@ def _GetKeyChar(): def _GetRawKeyFunctionWindows(): """_GetRawKeyFunction helper using Windows APIs.""" - # pylint: disable=g-import-not-at-top + import msvcrt def _GetRawKeyWindows(): diff --git a/fire/console/platforms.py b/fire/console/platforms.py index 13fd8204..d1d70b92 100644 --- a/fire/console/platforms.py +++ b/fire/console/platforms.py @@ -30,7 +30,7 @@ class Error(Exception): pass -class InvalidEnumValue(Error): # pylint: disable=g-bad-exception-name +class InvalidEnumValue(Error): """Exception for when a string could not be parsed to a valid enum value.""" def __init__(self, given, enum_type, options): @@ -53,7 +53,7 @@ class OperatingSystem(object): class _OS(object): """A single operating system.""" - # pylint: disable=redefined-builtin + def __init__(self, id, name, file_name): self.id = id self.name = name @@ -169,7 +169,7 @@ class Architecture(object): class _ARCH(object): """A single architecture.""" - # pylint: disable=redefined-builtin + def __init__(self, id, name, file_name): self.id = id self.name = name diff --git a/fire/core.py b/fire/core.py index 26a25753..04fd0192 100644 --- a/fire/core.py +++ b/fire/core.py @@ -182,7 +182,7 @@ class FireError(Exception): """ -class FireExit(SystemExit): # pylint: disable=g-bad-exception-name +class FireExit(SystemExit): """An exception raised by Fire to the client in the case of a FireError. The trace of the Fire program is available on the `trace` property. diff --git a/fire/core_test.py b/fire/core_test.py index 90b7f466..1d136208 100644 --- a/fire/core_test.py +++ b/fire/core_test.py @@ -25,14 +25,14 @@ class CoreTest(testutils.BaseTestCase): def testOneLineResult(self): - self.assertEqual(core._OneLineResult(1), '1') # pylint: disable=protected-access - self.assertEqual(core._OneLineResult('hello'), 'hello') # pylint: disable=protected-access - self.assertEqual(core._OneLineResult({}), '{}') # pylint: disable=protected-access - self.assertEqual(core._OneLineResult({'x': 'y'}), '{"x": "y"}') # pylint: disable=protected-access + self.assertEqual(core._OneLineResult(1), '1') + self.assertEqual(core._OneLineResult('hello'), 'hello') + self.assertEqual(core._OneLineResult({}), '{}') + self.assertEqual(core._OneLineResult({'x': 'y'}), '{"x": "y"}') def testOneLineResultCircularRef(self): circular_reference = tc.CircularReference() - self.assertEqual(core._OneLineResult(circular_reference.create()), # pylint: disable=protected-access + self.assertEqual(core._OneLineResult(circular_reference.create()), "{'y': {...}}") @mock.patch('fire.interact.Embed') diff --git a/fire/decorators_test.py b/fire/decorators_test.py index 9988743c..9c3e21fc 100644 --- a/fire/decorators_test.py +++ b/fire/decorators_test.py @@ -82,7 +82,7 @@ def example6(self, **kwargs): class WithVarArgs: @decorators.SetParseFn(str) - def example7(self, arg1, arg2=None, *varargs, **kwargs): # pylint: disable=keyword-arg-before-vararg + def example7(self, arg1, arg2=None, *varargs, **kwargs): return arg1, arg2, varargs, kwargs diff --git a/fire/docstrings_test.py b/fire/docstrings_test.py index 3d3efaa5..ee97dd7e 100644 --- a/fire/docstrings_test.py +++ b/fire/docstrings_test.py @@ -17,11 +17,11 @@ from fire import docstrings from fire import testutils -# pylint: disable=invalid-name + DocstringInfo = docstrings.DocstringInfo ArgInfo = docstrings.ArgInfo KwargInfo = docstrings.KwargInfo -# pylint: enable=invalid-name + class DocstringsTest(testutils.BaseTestCase): @@ -45,12 +45,12 @@ def test_one_line_simple_whitespace(self): self.assertEqual(expected_docstring_info, docstring_info) def test_one_line_too_long(self): - # pylint: disable=line-too-long + docstring = """A one line docstring that is both a little too verbose and a little too long so it keeps going well beyond a reasonable length for a one-liner. """ - # pylint: enable=line-too-long + docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='A one line docstring that is both a little too verbose and ' @@ -60,11 +60,11 @@ def test_one_line_too_long(self): self.assertEqual(expected_docstring_info, docstring_info) def test_one_line_runs_over(self): - # pylint: disable=line-too-long + docstring = """A one line docstring that is both a little too verbose and a little too long so it runs onto a second line. """ - # pylint: enable=line-too-long + docstring_info = docstrings.parse(docstring) expected_docstring_info = DocstringInfo( summary='A one line docstring that is both a little too verbose and ' @@ -306,7 +306,7 @@ def test_strip_blank_lines(self): lines = [' ', ' foo ', ' '] expected_output = [' foo '] - self.assertEqual(expected_output, docstrings._strip_blank_lines(lines)) # pylint: disable=protected-access + self.assertEqual(expected_output, docstrings._strip_blank_lines(lines)) def test_numpy_colon_in_description(self): docstring = """ diff --git a/fire/helptext.py b/fire/helptext.py index 318d6276..632dfd44 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -463,7 +463,7 @@ def _CreateFlagItem(flag, docstring_info, spec, required=False, Returns: A string to be used in constructing the help screen for the function. """ - # pylint: disable=g-bad-todo + # TODO(MichaelCG8): Get type and default information from docstrings if it is # not available in FullArgSpec. This will require updating # fire.docstrings.parser(). diff --git a/fire/inspectutils.py b/fire/inspectutils.py index b4ff78f8..1741c59b 100644 --- a/fire/inspectutils.py +++ b/fire/inspectutils.py @@ -99,10 +99,10 @@ def Py3GetFullArgSpec(fn): Returns: 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( fn, skip_bound_arg=True, follow_wrapper_chains=True, @@ -128,7 +128,7 @@ def Py3GetFullArgSpec(fn): kind = param.kind name = param.name - # pylint: disable=protected-access + if kind is inspect._POSITIONAL_ONLY: args.append(name) elif kind is inspect._POSITIONAL_OR_KEYWORD: @@ -145,7 +145,7 @@ def Py3GetFullArgSpec(fn): varkw = name if param.annotation is not param.empty: annotations[name] = param.annotation - # pylint: enable=protected-access + if not kwdefaults: # compatibility with 'func.__kwdefaults__' @@ -156,7 +156,7 @@ def Py3GetFullArgSpec(fn): defaults = None return inspect.FullArgSpec(args, varargs, varkw, defaults, kwonlyargs, kwdefaults, annotations) - # pylint: enable=no-member + # pytype: enable=module-attr @@ -171,7 +171,7 @@ def GetFullArgSpec(fn): kwonlyargs, kwonlydefaults, annotations) = Py3GetFullArgSpec(fn) else: # Specifically Python 3.4. (args, varargs, varkw, defaults, - kwonlyargs, kwonlydefaults, annotations) = inspect.getfullargspec(fn) # pylint: disable=deprecated-method,no-member + kwonlyargs, kwonlydefaults, annotations) = inspect.getfullargspec(fn) except TypeError: # If we can't get the argspec, how do we know if the fn should take args? @@ -255,7 +255,7 @@ def Info(component): A dict with information about the component. """ try: - from IPython.core import oinspect # pylint: disable=import-outside-toplevel,g-import-not-at-top + from IPython.core import oinspect inspector = oinspect.Inspector() info = inspector.info(component) diff --git a/fire/interact.py b/fire/interact.py index eccd3990..578c0754 100644 --- a/fire/interact.py +++ b/fire/interact.py @@ -86,11 +86,11 @@ def _EmbedIPython(variables, argv=None): Values are variable values. argv: The argv to use for starting ipython. Defaults to an empty list. """ - import IPython # pylint: disable=import-outside-toplevel,g-import-not-at-top + import IPython argv = argv or [] IPython.start_ipython(argv=argv, user_ns=variables) def _EmbedCode(variables): - import code # pylint: disable=import-outside-toplevel,g-import-not-at-top + import code code.InteractiveConsole(variables).interact() diff --git a/fire/main_test.py b/fire/main_test.py index cd3cd3f9..7953bd6f 100644 --- a/fire/main_test.py +++ b/fire/main_test.py @@ -44,11 +44,11 @@ class MainModuleFileTest(testutils.BaseTestCase): def setUp(self): super().setUp() - self.file = tempfile.NamedTemporaryFile(suffix='.py') # pylint: disable=consider-using-with + self.file = tempfile.NamedTemporaryFile(suffix='.py') self.file.write(b'class Foo:\n def double(self, n):\n return 2 * n\n') self.file.flush() - self.file2 = tempfile.NamedTemporaryFile() # pylint: disable=consider-using-with + self.file2 = tempfile.NamedTemporaryFile() def testFileNameFire(self): # Confirm that the file is correctly imported and doubles the number. diff --git a/fire/test_components.py b/fire/test_components.py index a49868c5..3dd1b3d3 100644 --- a/fire/test_components.py +++ b/fire/test_components.py @@ -20,7 +20,7 @@ -def identity(arg1, arg2, arg3=10, arg4=20, *arg5, **arg6): # pylint: disable=keyword-arg-before-vararg +def identity(arg1, arg2, arg3=10, arg4=20, *arg5, **arg6): return arg1, arg2, arg3, arg4, arg5, arg6 identity.__annotations__ = {'arg2': int, 'arg4': int} @@ -38,7 +38,7 @@ def multiplier_with_docstring(num, rate=2): return num * rate -def function_with_help(help=True): # pylint: disable=redefined-builtin +def function_with_help(help=True): return help @@ -46,7 +46,7 @@ class Empty: pass -class OldStyleEmpty: # pylint: disable=old-style-class,no-init +class OldStyleEmpty: pass @@ -66,7 +66,7 @@ def __init__(self, value='value'): class WithHelpArg: """Test class for testing when class has a help= arg.""" - def __init__(self, help=True): # pylint: disable=redefined-builtin + def __init__(self, help=True): self.has_help = help self.dictionary = {'__help': 'help in a dict'} @@ -105,7 +105,7 @@ def text( return string -class OldStyleWithDefaults: # pylint: disable=old-style-class,no-init +class OldStyleWithDefaults: def double(self, count=0): return 2 * count @@ -137,7 +137,7 @@ def identity2(self, a=None, alpha=None): class CapitalizedArgNames: - def sum(self, Delta=1.0, Gamma=2.0): # pylint: disable=invalid-name + def sum(self, Delta=1.0, Gamma=2.0): return Delta + Gamma @@ -186,7 +186,7 @@ def cumsums(self, *items): sums.append(total) return sums - def varchars(self, alpha=0, beta=0, *chars): # pylint: disable=keyword-arg-before-vararg + def varchars(self, alpha=0, beta=0, *chars): return alpha, beta, ''.join(chars) @@ -292,13 +292,13 @@ class NamedTuple: def point(self): """Point example straight from Python docs.""" - # pylint: disable=invalid-name + Point = collections.namedtuple('Point', ['x', 'y']) return Point(11, y=22) def matching_names(self): """Field name equals value.""" - # pylint: disable=invalid-name + Point = collections.namedtuple('Point', ['x', 'y']) return Point(x='x', y='y') @@ -429,7 +429,7 @@ def class_fn(cls, args): return args + cls.CLASS_STATE -def function_with_varargs(arg1, arg2, arg3=1, *varargs): # pylint: disable=keyword-arg-before-vararg +def function_with_varargs(arg1, arg2, arg3=1, *varargs): """Function with varargs. Args: @@ -532,7 +532,7 @@ def decorated_method(name='World'): return 'Hello %s' % name -# pylint: disable=g-doc-args,g-doc-return-or-yield + def fn_with_kwarg(arg1, arg2, **kwargs): """Function with kwarg. @@ -564,4 +564,3 @@ 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/test_components_py3.py b/fire/test_components_py3.py index 697cf6b9..6c5f7f1a 100644 --- a/fire/test_components_py3.py +++ b/fire/test_components_py3.py @@ -18,7 +18,7 @@ from typing import Tuple -# pylint: disable=keyword-arg-before-vararg + def identity(arg1, arg2: int, arg3=10, arg4: int = 20, *arg5, arg6, arg7: int, arg8=30, arg9: int = 40, **arg10): return arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 diff --git a/fire/test_components_test.py b/fire/test_components_test.py index 531f882c..cae196f3 100644 --- a/fire/test_components_test.py +++ b/fire/test_components_test.py @@ -27,9 +27,9 @@ def testTestComponents(self): def testNonComparable(self): with self.assertRaises(ValueError): - tc.NonComparable() != 2 # pylint: disable=expression-not-assigned + tc.NonComparable() != 2 with self.assertRaises(ValueError): - tc.NonComparable() == 2 # pylint: disable=expression-not-assigned + tc.NonComparable() == 2 if __name__ == '__main__': diff --git a/fire/testutils.py b/fire/testutils.py index eca37f43..b7798ed0 100644 --- a/fire/testutils.py +++ b/fire/testutils.py @@ -105,8 +105,7 @@ def ChangeDirectory(directory): os.chdir(cwdir) -# pylint: disable=invalid-name + main = unittest.main skip = unittest.skip skipIf = unittest.skipIf -# pylint: enable=invalid-name From dca6c8fe1f543a00da91366419fd1f7dca49e868 Mon Sep 17 00:00:00 2001 From: Jirka B Date: Mon, 23 Sep 2024 14:53:05 +0200 Subject: [PATCH 4/4] fixing R rules --- .github/scripts/build.sh | 2 +- fire/completion.py | 5 ++--- fire/console/console_attr.py | 11 +++++------ fire/console/console_pager.py | 4 ++-- fire/console/files.py | 3 +-- fire/console/platforms.py | 22 ++++++++++------------ fire/core.py | 9 ++++----- fire/decorators.py | 3 +-- fire/docstrings.py | 20 +++++++------------- fire/helptext.py | 5 ++--- fire/inspectutils.py | 5 ++--- fire/interact_test.py | 2 +- fire/trace.py | 23 +++++++++++------------ pyproject.toml | 8 +++++++- 14 files changed, 56 insertions(+), 66 deletions(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 68528188..2439f10a 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -24,7 +24,7 @@ python setup.py develop python -m pytest # Run the tests without IPython. pip install ipython python -m pytest # Now run the tests with IPython. -pre-commit run --all-files +pre-commit run --all-files --show-diff-on-failure -- if [[ ${PYTHON_VERSION} == 3.7 ]]; then # Run type-checking. pip install pytype; diff --git a/fire/completion.py b/fire/completion.py index 625e9d86..28d2a7a1 100644 --- a/fire/completion.py +++ b/fire/completion.py @@ -151,8 +151,7 @@ def _BashScript(name, commands, default_options=None): def _GetOptsAssignmentTemplate(command): if command == name: return opts_assignment_main_command_template - else: - return opts_assignment_subcommand_template + return opts_assignment_subcommand_template lines = [] commands_set = set() @@ -281,7 +280,7 @@ def _FishScript(name, commands, default_options=None): ) -def MemberVisible(component, name, member, class_attrs=None, verbose=False): +def MemberVisible(component, name, member, class_attrs=None, verbose=False): # noqa: C901 """Returns whether a member should be included in auto-completion or help. Determines whether a member of an object with the specified name should be diff --git a/fire/console/console_attr.py b/fire/console/console_attr.py index 6043982d..c94b5a4d 100644 --- a/fire/console/console_attr.py +++ b/fire/console/console_attr.py @@ -314,7 +314,7 @@ def _GetConsoleEncoding(self): console_encoding = console_encoding.lower() if 'utf-8' in console_encoding: return 'utf8' - elif 'cp437' in console_encoding: + if 'cp437' in console_encoding: return 'cp437' return None @@ -700,15 +700,14 @@ def GetCharacterDisplayWidth(char): if unicodedata.combining(char) != 0: # Modifies the previous character and does not move the cursor. return 0 - elif unicodedata.category(char) == 'Cf': + if unicodedata.category(char) == 'Cf': # Unprintable formatting char. return 0 - elif unicodedata.east_asian_width(char) in 'FW': + if unicodedata.east_asian_width(char) in 'FW': # Fullwidth or Wide chars take 2 character positions. return 2 - else: - # Don't use this function on control chars. - return 1 + # Don't use this function on control chars. + return 1 def SafeText(data, encoding=None, escape=True): diff --git a/fire/console/console_pager.py b/fire/console/console_pager.py index 565c7e1e..bc4d052c 100644 --- a/fire/console/console_pager.py +++ b/fire/console/console_pager.py @@ -166,7 +166,7 @@ def _Help(self): self._attr.GetRawKey() self._Write('\n') - def Run(self): + def Run(self): # noqa: C901 """Run the pager.""" # No paging if the contents are small enough. if len(self._lines) <= self._height: @@ -215,7 +215,7 @@ def Run(self): ): # Quit. return - elif c in ('/', '?'): + if c in ('/', '?'): c = self._GetSearchCommand(c) elif c.isdigit(): # Collect digits for operation count. diff --git a/fire/console/files.py b/fire/console/files.py index 97222c3d..0fec27cf 100644 --- a/fire/console/files.py +++ b/fire/console/files.py @@ -66,8 +66,7 @@ def _FindExecutableOnPath(executable, path, pathext): def _PlatformExecutableExtensions(platform): if platform == platforms.OperatingSystem.WINDOWS: return ('.exe', '.cmd', '.bat', '.com', '.ps1') - else: - return ('', '.sh') + return ('', '.sh') def FindExecutableOnPath(executable, path=None, pathext=None, diff --git a/fire/console/platforms.py b/fire/console/platforms.py index d1d70b92..d5f6936b 100644 --- a/fire/console/platforms.py +++ b/fire/console/platforms.py @@ -147,13 +147,13 @@ def Current(): """ if os.name == 'nt': return OperatingSystem.WINDOWS - elif 'linux' in sys.platform: + if 'linux' in sys.platform: return OperatingSystem.LINUX - elif 'darwin' in sys.platform: + if 'darwin' in sys.platform: return OperatingSystem.MACOSX - elif 'cygwin' in sys.platform: + if 'cygwin' in sys.platform: return OperatingSystem.CYGWIN - elif 'msys' in sys.platform: + if 'msys' in sys.platform: return OperatingSystem.MSYS return None @@ -323,12 +323,12 @@ def UserAgentFragment(self): # '#1 SMP Tue May 21 02:35:06 PDT 2013', 'x86_64', 'x86_64') return '({name} {version})'.format( name=self.operating_system.name, version=platform.release()) - elif self.operating_system == OperatingSystem.WINDOWS: + if self.operating_system == OperatingSystem.WINDOWS: # ('Windows', '', '7', '6.1.7601', 'AMD64', # 'Intel64 Family 6 Model 45 Stepping 7, GenuineIntel') return '({name} NT {version})'.format( name=self.operating_system.name, version=platform.version()) - elif self.operating_system == OperatingSystem.MACOSX: + if self.operating_system == OperatingSystem.MACOSX: # ('Darwin', '', '12.4.0', # 'Darwin Kernel Version 12.4.0: Wed May 1 17:57:12 PDT 2013; # root:xnu-2050.24.15~1/RELEASE_X86_64', 'x86_64', 'i386') @@ -337,8 +337,7 @@ def UserAgentFragment(self): if self.architecture == Architecture.ppc else 'Intel') return format_string.format( name=arch_string, version=platform.release()) - else: - return '()' + return '()' def AsyncPopenArgs(self): """Returns the args for spawning an async process using Popen on this OS. @@ -413,10 +412,9 @@ def SupportedVersionMessage(self, allow_py3): PythonVersion.MIN_SUPPORTED_PY2_VERSION[1], PythonVersion.MIN_SUPPORTED_PY3_VERSION[0], PythonVersion.MIN_SUPPORTED_PY3_VERSION[1]) - else: - return 'Please use Python version {0}.{1}.x.'.format( - PythonVersion.MIN_SUPPORTED_PY2_VERSION[0], - PythonVersion.MIN_SUPPORTED_PY2_VERSION[1]) + return 'Please use Python version {0}.{1}.x.'.format( + PythonVersion.MIN_SUPPORTED_PY2_VERSION[0], + PythonVersion.MIN_SUPPORTED_PY2_VERSION[1]) def IsCompatible(self, allow_py3=False, raise_exception=False): """Ensure that the Python version we are using is compatible. diff --git a/fire/core.py b/fire/core.py index 04fd0192..bd39587a 100644 --- a/fire/core.py +++ b/fire/core.py @@ -160,8 +160,7 @@ def Fire(component=None, command=None, name=None, serialize=None): # The command succeeded normally; print the result. _PrintResult( component_trace, verbose=component_trace.verbose, serialize=serialize) - result = component_trace.GetResult() - return result + return component_trace.GetResult() def Display(lines, out): @@ -236,7 +235,7 @@ def _IsHelpShortcut(component_trace, remaining_args): return show_help -def _PrintResult(component_trace, verbose=False, serialize=None): +def _PrintResult(component_trace, verbose=False, serialize=None): # noqa: C901 """Prints the result of the Fire call to stdout in a human readable way.""" # TODO(dbieber): Design human readable deserializable serialization method # and move serialization to its own module. @@ -358,7 +357,7 @@ def _OneLineResult(result): return str(result).replace('\n', ' ') -def _Fire(component, args, parsed_flag_args, context, name=None): +def _Fire(component, args, parsed_flag_args, context, name=None): # noqa: C901 """Execute a Fire command on a target component using the args supplied. Arguments that come after a final isolated '--' are treated as Flags, eg for @@ -813,7 +812,7 @@ def _ParseArgs(fn_args, fn_defaults, num_required_args, kwargs, return parsed_args, kwargs, remaining_args, capacity -def _ParseKeywordArgs(args, fn_spec): +def _ParseKeywordArgs(args, fn_spec): # noqa: C901 """Parses the supplied arguments for keyword arguments. Given a list of arguments, finds occurrences of --name value, and uses 'name' diff --git a/fire/decorators.py b/fire/decorators.py index 5c23c9d1..137dce47 100644 --- a/fire/decorators.py +++ b/fire/decorators.py @@ -98,8 +98,7 @@ def GetMetadata(fn) -> Dict[str, Any]: metadata = getattr(fn, FIRE_METADATA, default) if ACCEPTS_POSITIONAL_ARGS in metadata: return metadata - else: - return default + return default except: # noqa: E722 return default diff --git a/fire/docstrings.py b/fire/docstrings.py index 2d7c7e63..076ae282 100644 --- a/fire/docstrings.py +++ b/fire/docstrings.py @@ -338,8 +338,7 @@ def _as_arg_name_and_type(text): type_token = ' '.join(tokens[1:]) type_token = type_token.lstrip('{([').rstrip('])}') return tokens[0], type_token - else: - return None + return None def _as_arg_names(names_str): @@ -408,7 +407,7 @@ def _consume_google_args_line(line_info, state): state.current_arg.description.lines.append(split_line[0]) -def _consume_line(line_info, state): +def _consume_line(line_info, state): # noqa: C901 """Consumes one line of text, updating the state accordingly. When _consume_line is called, part of the line may already have been processed @@ -689,8 +688,7 @@ def _get_directive(line_info): """ if line_info.stripped.startswith(':'): return line_info.stripped.split(':', 2)[1] - else: - return None + return None def _get_after_directive(line_info): @@ -698,8 +696,7 @@ def _get_after_directive(line_info): sections = line_info.stripped.split(':', 2) if len(sections) > 2: return sections[-1] - else: - return '' + return '' def _rst_section(line_info): @@ -717,8 +714,7 @@ def _rst_section(line_info): if directive: possible_title = directive.split()[0] return _section_from_possible_title(possible_title) - else: - return None + return None def _line_is_hyphens(line): @@ -744,8 +740,7 @@ def _numpy_section(line_info): if next_line_is_hyphens: possible_title = line_info.remaining return _section_from_possible_title(possible_title) - else: - return None + return None def _line_is_numpy_parameter_type(line_info): @@ -769,6 +764,5 @@ def _line_is_numpy_parameter_type(line_info): if ':' in line_info.previous.line and current_indent > previous_indent: # The parameter type was the previous line; this is the description. return False - else: - return True + return True return False diff --git a/fire/helptext.py b/fire/helptext.py index 632dfd44..e5805347 100644 --- a/fire/helptext.py +++ b/fire/helptext.py @@ -160,8 +160,7 @@ def _DescriptionSection(component, info): text = description or summary or None if text: return ('DESCRIPTION', text) - else: - return None + return None def _CreateKeywordOnlyFlagItem(flag, docstring_info, spec, short_arg): @@ -185,7 +184,7 @@ def _GetShortFlags(flags): return [v for v in short_flags if short_flag_counts[v] == 1] -def _ArgsAndFlagsSections(info, spec, metadata): +def _ArgsAndFlagsSections(info, spec, metadata): # noqa: C901 """The "Args and Flags" sections of the help string.""" args_with_no_defaults = spec.args[:len(spec.args) - len(spec.defaults)] args_with_defaults = spec.args[len(spec.args) - len(spec.defaults):] diff --git a/fire/inspectutils.py b/fire/inspectutils.py index 1741c59b..0d1ce86d 100644 --- a/fire/inspectutils.py +++ b/fire/inspectutils.py @@ -85,7 +85,7 @@ class with an __init__ method. return fn, skip_arg -def Py3GetFullArgSpec(fn): +def Py3GetFullArgSpec(fn): # noqa: C901 """A alternative to the builtin getfullargspec. The builtin inspect.getfullargspec uses: @@ -326,8 +326,7 @@ def IsNamedTuple(component): if not isinstance(component, tuple): return False - has_fields = bool(getattr(component, '_fields', None)) - return has_fields + return bool(getattr(component, '_fields', None)) # whether it has files def GetClassAttrsDict(component): diff --git a/fire/interact_test.py b/fire/interact_test.py index 4b4718ed..852e39b7 100644 --- a/fire/interact_test.py +++ b/fire/interact_test.py @@ -21,7 +21,7 @@ try: - import IPython # noqa: F401 + import IPython INTERACT_METHOD = 'IPython.start_ipython' except ImportError: INTERACT_METHOD = 'code.InteractiveConsole' diff --git a/fire/trace.py b/fire/trace.py index 4a6d4776..ceb1321c 100644 --- a/fire/trace.py +++ b/fire/trace.py @@ -294,15 +294,14 @@ def ErrorAsStr(self): def __str__(self): if self.HasError(): return self.ErrorAsStr() - else: - # Format is: {action} "{target}" ({filename}:{lineno}) - string = self._action - if self._target is not None: - string += f' "{self._target}"' - if self._filename is not None: - path = self._filename - if self._lineno is not None: - path += f':{self._lineno}' - - string += f' ({path})' - return string + # Format is: {action} "{target}" ({filename}:{lineno}) + string = self._action + if self._target is not None: + string += f' "{self._target}"' + if self._filename is not None: + path = self._filename + if self._lineno is not None: + path += f':{self._lineno}' + + string += f' ({path})' + return string diff --git a/pyproject.toml b/pyproject.toml index 13a9b6e7..b09cfaf7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,13 @@ output = ".pytype" line-length = 80 # Enable specific rule categories -lint.select = ["E", "F", "W", "RUF100"] +lint.select = [ + "E", # Errors + "W", # Warnings + "C", # Conventions + "R", # Refactors + "RUF100", # Remove unused noqas +] # Exclude specific files and directories lint.exclude = ["build", "dist", ".venv"]