From cda97e4fa63deac934e3085e8bc0edb0a30b85da Mon Sep 17 00:00:00 2001
From: Itamar Turner-Trauring <itamar@pythonspeed.com>
Date: Mon, 17 Apr 2023 10:06:50 -0400
Subject: [PATCH 1/6] Remove pylint, replacing with faster alternative.

---
 tox.ini | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/tox.ini b/tox.ini
index 447745784..982157bf1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -100,9 +100,7 @@ commands =
 [testenv:codechecks]
 basepython = python3
 deps =
-     # Make sure we get a version of PyLint that respects config, and isn't too
-     # old.
-     pylint < 2.18, >2.14
+     ruff
 # On macOS, git inside of towncrier needs $HOME.
 passenv = HOME
 setenv =
@@ -114,9 +112,10 @@ commands =
          python misc/coding_tools/check-umids.py {posargs:{env:DEFAULT_FILES}}
          python misc/coding_tools/check-debugging.py {posargs:{env:DEFAULT_FILES}}
          python misc/coding_tools/find-trailing-spaces.py -r {posargs:{env:DEFAULT_FILES}}
-         # PyLint has other useful checks, might want to enable them:
-         # http://pylint.pycqa.org/en/latest/technical_reference/features.html
-         pylint --disable=all --enable=cell-var-from-loop {posargs:{env:DEFAULT_FILES}}
+         # B023: Find loop variables that aren't bound in a loop, equivalent of pylint
+         #       cell-var-from-loop.
+         # ruff could probably replace flake8 and perhaps above tools as well...
+         ruff check --select=B023 {posargs:{env:DEFAULT_FILES}}
 
          # If towncrier.check fails, you forgot to add a towncrier news
          # fragment explaining the change in this branch.  Create one at

From aafbb00333312e6fc77dcb9558a8b5c9cad75b9e Mon Sep 17 00:00:00 2001
From: Itamar Turner-Trauring <itamar@pythonspeed.com>
Date: Mon, 17 Apr 2023 10:10:09 -0400
Subject: [PATCH 2/6] Use ruff for trailing whitespace.

---
 misc/coding_tools/find-trailing-spaces.py | 44 -----------------------
 newsfragments/4014.minor                  |  0
 tox.ini                                   |  4 +--
 3 files changed, 2 insertions(+), 46 deletions(-)
 delete mode 100644 misc/coding_tools/find-trailing-spaces.py
 create mode 100644 newsfragments/4014.minor

diff --git a/misc/coding_tools/find-trailing-spaces.py b/misc/coding_tools/find-trailing-spaces.py
deleted file mode 100644
index 19e7e3c28..000000000
--- a/misc/coding_tools/find-trailing-spaces.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import print_function
-
-import os, sys
-
-from twisted.python import usage
-
-class Options(usage.Options):
-    optFlags = [
-        ("recursive", "r", "Search for .py files recursively"),
-        ]
-    def parseArgs(self, *starting_points):
-        self.starting_points = starting_points
-
-found = [False]
-
-def check(fn):
-    f = open(fn, "r")
-    for i,line in enumerate(f.readlines()):
-        if line == "\n":
-            continue
-        if line[-1] == "\n":
-            line = line[:-1]
-        if line.rstrip() != line:
-            # the %s:%d:%d: lets emacs' compile-mode jump to those locations
-            print("%s:%d:%d: trailing whitespace" % (fn, i+1, len(line)+1))
-            found[0] = True
-    f.close()
-
-o = Options()
-o.parseOptions()
-if o['recursive']:
-    for starting_point in o.starting_points:
-        for root, dirs, files in os.walk(starting_point):
-            for fn in [f for f in files if f.endswith(".py")]:
-                fn = os.path.join(root, fn)
-                check(fn)
-else:
-    for fn in o.starting_points:
-        check(fn)
-if found[0]:
-    sys.exit(1)
-sys.exit(0)
diff --git a/newsfragments/4014.minor b/newsfragments/4014.minor
new file mode 100644
index 000000000..e69de29bb
diff --git a/tox.ini b/tox.ini
index 982157bf1..a191d6078 100644
--- a/tox.ini
+++ b/tox.ini
@@ -111,11 +111,11 @@ commands =
          flake8 {posargs:{env:DEFAULT_FILES}}
          python misc/coding_tools/check-umids.py {posargs:{env:DEFAULT_FILES}}
          python misc/coding_tools/check-debugging.py {posargs:{env:DEFAULT_FILES}}
-         python misc/coding_tools/find-trailing-spaces.py -r {posargs:{env:DEFAULT_FILES}}
          # B023: Find loop variables that aren't bound in a loop, equivalent of pylint
          #       cell-var-from-loop.
+         # W291,W293: Trailing whitespace.
          # ruff could probably replace flake8 and perhaps above tools as well...
-         ruff check --select=B023 {posargs:{env:DEFAULT_FILES}}
+         ruff check --select=B023,W291,W293 {posargs:{env:DEFAULT_FILES}}
 
          # If towncrier.check fails, you forgot to add a towncrier news
          # fragment explaining the change in this branch.  Create one at

From 7b33931df2982bd35e0cdb38d0a1e1d77d34ce47 Mon Sep 17 00:00:00 2001
From: Itamar Turner-Trauring <itamar@pythonspeed.com>
Date: Mon, 17 Apr 2023 10:21:20 -0400
Subject: [PATCH 3/6] Replace flake8 with ruff.

---
 .gitignore |  2 ++
 .ruff.toml | 12 ++++++++++++
 setup.cfg  |  3 +++
 setup.py   |  3 +--
 tox.ini    |  7 +------
 5 files changed, 19 insertions(+), 8 deletions(-)
 create mode 100644 .ruff.toml

diff --git a/.gitignore b/.gitignore
index 7c7fa2afd..0cf688c54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,5 @@ zope.interface-*.egg
 # This is the plaintext of the private environment needed for some CircleCI
 # operations.  It's never supposed to be checked in.
 secret-env-plain
+
+.ruff_cache
\ No newline at end of file
diff --git a/.ruff.toml b/.ruff.toml
new file mode 100644
index 000000000..75ff62c2d
--- /dev/null
+++ b/.ruff.toml
@@ -0,0 +1,12 @@
+select = [
+    # Pyflakes checks
+    "F",
+    # Prohibit tabs:
+    "W191",
+    # No trailing whitespace:
+    "W291",
+    "W293",
+    # Make sure we bind closure variables in a loop (equivalent to pylint
+    # cell-var-from-loop):
+    "B023",
+]
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index f4539279e..9415b3ab4 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,6 +6,9 @@ develop = update_version develop
 bdist_egg = update_version bdist_egg
 bdist_wheel = update_version bdist_wheel
 
+# This has been replaced by ruff (see .ruff.toml), which has same checks as
+# flake8 plus many more, and is also faster. However, we're keeping this config
+# in case people still use flake8 in IDEs, etc..
 [flake8]
 # Enforce all pyflakes constraints, and also prohibit tabs for indentation.
 # Reference:
diff --git a/setup.py b/setup.py
index 854a333f1..3358aa6c6 100644
--- a/setup.py
+++ b/setup.py
@@ -399,12 +399,11 @@ setup(name="tahoe-lafs", # also set in __init__.py
               "gpg",
           ],
           "test": [
-              "flake8",
               # Pin a specific pyflakes so we don't have different folks
               # disagreeing on what is or is not a lint issue.  We can bump
               # this version from time to time, but we will do it
               # intentionally.
-              "pyflakes == 3.0.1",
+              "ruff==0.0.261",
               "coverage ~= 5.0",
               "mock",
               "tox ~= 3.0",
diff --git a/tox.ini b/tox.ini
index a191d6078..2daf8dca3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -108,14 +108,9 @@ setenv =
 	 # entire codebase, including various pieces of supporting code.
 	 DEFAULT_FILES=src integration static misc setup.py
 commands =
-         flake8 {posargs:{env:DEFAULT_FILES}}
+         ruff check {posargs:{env:DEFAULT_FILES}}
          python misc/coding_tools/check-umids.py {posargs:{env:DEFAULT_FILES}}
          python misc/coding_tools/check-debugging.py {posargs:{env:DEFAULT_FILES}}
-         # B023: Find loop variables that aren't bound in a loop, equivalent of pylint
-         #       cell-var-from-loop.
-         # W291,W293: Trailing whitespace.
-         # ruff could probably replace flake8 and perhaps above tools as well...
-         ruff check --select=B023,W291,W293 {posargs:{env:DEFAULT_FILES}}
 
          # If towncrier.check fails, you forgot to add a towncrier news
          # fragment explaining the change in this branch.  Create one at

From 6517cd4a48338bc13d991299d361e8c5b65fed22 Mon Sep 17 00:00:00 2001
From: Itamar Turner-Trauring <itamar@pythonspeed.com>
Date: Mon, 17 Apr 2023 10:22:27 -0400
Subject: [PATCH 4/6] Fix lint found by ruff.

---
 misc/checkers/check_load.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/misc/checkers/check_load.py b/misc/checkers/check_load.py
index d509b89ae..01a9ed832 100644
--- a/misc/checkers/check_load.py
+++ b/misc/checkers/check_load.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 """
 this is a load-generating client program. It does all of its work through a
 given tahoe node (specified by URL), and performs random reads and writes

From c05afb19dfd7ffb5f053aa2a52972403ccb4fa43 Mon Sep 17 00:00:00 2001
From: Itamar Turner-Trauring <itamar@pythonspeed.com>
Date: Mon, 17 Apr 2023 10:33:31 -0400
Subject: [PATCH 5/6] Don't install code, it's not necessary.

---
 tox.ini | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tox.ini b/tox.ini
index 2daf8dca3..99487bc83 100644
--- a/tox.ini
+++ b/tox.ini
@@ -99,8 +99,10 @@ commands =
 
 [testenv:codechecks]
 basepython = python3
+skip_install = true
 deps =
      ruff
+     towncrier
 # On macOS, git inside of towncrier needs $HOME.
 passenv = HOME
 setenv =

From 1371ffe9dc7e6a6b0346daad1603f6414bbd1fc7 Mon Sep 17 00:00:00 2001
From: Itamar Turner-Trauring <itamar@pythonspeed.com>
Date: Tue, 25 Apr 2023 08:14:26 -0400
Subject: [PATCH 6/6] Just have ruff in one place.

---
 setup.py | 5 -----
 tox.ini  | 4 +++-
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/setup.py b/setup.py
index 3358aa6c6..2418c6dbe 100644
--- a/setup.py
+++ b/setup.py
@@ -399,11 +399,6 @@ setup(name="tahoe-lafs", # also set in __init__.py
               "gpg",
           ],
           "test": [
-              # Pin a specific pyflakes so we don't have different folks
-              # disagreeing on what is or is not a lint issue.  We can bump
-              # this version from time to time, but we will do it
-              # intentionally.
-              "ruff==0.0.261",
               "coverage ~= 5.0",
               "mock",
               "tox ~= 3.0",
diff --git a/tox.ini b/tox.ini
index 99487bc83..5f18b6b95 100644
--- a/tox.ini
+++ b/tox.ini
@@ -101,7 +101,9 @@ commands =
 basepython = python3
 skip_install = true
 deps =
-     ruff
+     # Pin a specific version so we get consistent outcomes; update this
+     # occasionally:
+     ruff == 0.0.263
      towncrier
 # On macOS, git inside of towncrier needs $HOME.
 passenv = HOME