diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0da79ea..03c4749 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -27,7 +27,7 @@ jobs:
       with:
         fetch-depth: 0
     - uses: actions/setup-python@v2
-    - uses: pre-commit/action@v2.0.3
+    - uses: pre-commit/action@v3.0.0
     - name: pylint
       run: |
         echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json"
diff --git a/noxfile.py b/noxfile.py
index 9b68bfe..9fae464 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -22,7 +22,7 @@ def pylint(session):
     Run pylint.
     """
 
-    session.install(".", "paramiko", "ipython", "pylint")
+    session.install(".", "paramiko", "ipython", "pylint~=2.14.3")
     session.run("pylint", "plumbum", *session.posargs)
 
 
diff --git a/plumbum/cli/progress.py b/plumbum/cli/progress.py
index 3d21a8a..fce6b3c 100644
--- a/plumbum/cli/progress.py
+++ b/plumbum/cli/progress.py
@@ -69,7 +69,7 @@ class ProgressBase(ABC):
         return rval
 
     def next(self):
-        return self.__next__()
+        return next(self)
 
     @property
     def value(self):
diff --git a/plumbum/commands/base.py b/plumbum/commands/base.py
index 51e667d..eafbde3 100644
--- a/plumbum/commands/base.py
+++ b/plumbum/commands/base.py
@@ -547,7 +547,7 @@ class StdinDataRedirection(BaseCommand):
         return self.cmd.machine
 
     def popen(self, args=(), **kwargs):
-        if "stdin" in kwargs and kwargs["stdin"] != PIPE:
+        if kwargs.get("stdin") not in (PIPE, None):
             raise RedirectionError("stdin is already redirected")
         data = self.data
         if isinstance(data, str) and self._get_encoding() is not None:
@@ -558,8 +558,9 @@ class StdinDataRedirection(BaseCommand):
             f.write(chunk)
             data = data[self.CHUNK_SIZE :]
         f.seek(0)
+        kwargs["stdin"] = f
         # try:
-        return self.cmd.popen(args, stdin=f, **kwargs)
+        return self.cmd.popen(args, **kwargs)
         # finally:
         #    f.close()
 
diff --git a/plumbum/fs/atomic.py b/plumbum/fs/atomic.py
index 5aebc80..d796b2f 100644
--- a/plumbum/fs/atomic.py
+++ b/plumbum/fs/atomic.py
@@ -290,7 +290,7 @@ class PidFile:
             return
         self._ctx = self.atomicfile.locked(blocking=False)
         try:
-            self._ctx.__enter__()
+            self._ctx.__enter__()  # pylint: disable=unnecessary-dunder-call
         except OSError:
             self._ctx = None
             try:
diff --git a/pyproject.toml b/pyproject.toml
index b34fa89..27ddb99 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -68,6 +68,7 @@ ignore = [
 [tool.pylint]
 master.py-version = "3.6"
 master.jobs = "0"
+master.load-plugins = ["pylint.extensions.no_self_use"]
 reports.output-format = "colorized"
 similarities.ignore-imports = "yes"
 messages_control.enable = [
@@ -103,4 +104,5 @@ messages_control.disable = [
   "too-many-return-statements",
   "too-many-statements",
   "unidiomatic-typecheck", # TODO: might be able to remove
+  "unnecessary-lambda-assignment", # TODO: 4 instances
 ]

diff --git a/tests/test_local.py b/tests/test_local.py
index dc76a7b..d96d39b 100644
--- a/tests/test_local.py
+++ b/tests/test_local.py
@@ -639,6 +639,25 @@ class TestLocalMachine:
             assert result[1] == EXPECT
             assert EXPECT == capfd.readouterr()[0]
 
+    @skip_on_windows
+    @pytest.mark.parametrize(
+        "modifier, expected",
+        [
+            (FG, None),
+            (TF(FG=True), True),
+            (RETCODE(FG=True), 0),
+            (TEE, (0, "meow", "")),
+        ],
+    )
+    def test_redirection_stdin_modifiers_fg(self, modifier, expected, capfd):
+        "StdinDataRedirection compatible with modifiers which write to stdout"
+        from plumbum.cmd import cat
+
+        cmd = cat << "meow"
+
+        assert cmd & modifier == expected
+        assert capfd.readouterr() == ("meow", "")
+
     @skip_on_windows
     def test_logger_pipe(self):
         from plumbum.cmd import bash

StdinDataRedirection commands fail upon modification (TEE, FG, etc.)
Commands to which standard input has been supplied from a Python string – which are `StdinDataRedirection` commands – erroneously throw exception `RedirectionError` when combined with built-in modifiers –

    stdin is already redirected

– even though `stdin` is only being redirected the one time.

---

```python
>>> from plumbum import TEE, FG, local                                          

# this works

>>> local['ls']['-d', '/'] & FG                                                                                                                               
/                                                                              
>>> local['ls']['-d', '/'] & TEE                                                                                                                              
/                                     
(0, '/\n', '')

# this does not work

>>> (local['cat']['-'] << 'this breaks') & FG                                   
Traceback (most recent call last):                                                                                                                            
    …
    raise RedirectionError("stdin is already redirected")
plumbum.commands.base.RedirectionError: stdin is already redirected
```

---

Affected modifiers invoke `cmd.run(stdin=None, …)`. It appears sufficient to change these to simply **omit the keyword** pair `stdin=None`.

However, it's unclear whether this would have unwanted side-effects; and, it seems rather that `StdinDataRedirection` is in the wrong, in throwing an exception over the value `None`.

`BaseRedirection.popen` permits either `PIPE` or `None`:

```python
if self.KWARG in kwargs and kwargs[self.KWARG] not in (PIPE, None):
    raise RedirectionError(…)
```

`StdinDataRedirection.popen` only permits `PIPE`:

```python
if "stdin" in kwargs and kwargs["stdin"] != PIPE:
    raise RedirectionError(…)
```

→ As such, the resolution is perhaps that the above check in `StdinDataRedirection.popen` be made to mirror `BaseRedirection.popen`.
tests/test_local.py::TestLocalMachine::test_redirection_stdin_modifiers_fg[modifier0-None] tests/test_local.py::TestLocalMachine::test_redirection_stdin_modifiers_fg[modifier1-True] tests/test_local.py::TestLocalMachine::test_redirection_stdin_modifiers_fg[modifier2-0] tests/test_local.py::TestLocalMachine::test_redirection_stdin_modifiers_fg[modifier3-expected3]
tests/test_local.py::TestLocalPopen::test_contextmanager tests/test_local.py::TestLocalPath::test_name tests/test_local.py::TestLocalPath::test_dirname tests/test_local.py::TestLocalPath::test_uri tests/test_local.py::TestLocalPath::test_pickle tests/test_local.py::TestLocalPath::test_empty tests/test_local.py::TestLocalPath::test_chown tests/test_local.py::TestLocalPath::test_split tests/test_local.py::TestLocalPath::test_suffix tests/test_local.py::TestLocalPath::test_newname tests/test_local.py::TestLocalPath::test_relative_to tests/test_local.py::TestLocalPath::test_read_write tests/test_local.py::TestLocalPath::test_parts tests/test_local.py::TestLocalPath::test_iterdir tests/test_local.py::TestLocalPath::test_stem tests/test_local.py::TestLocalPath::test_root_drive tests/test_local.py::TestLocalPath::test_compare_pathlib tests/test_local.py::TestLocalPath::test_suffix_expected tests/test_local.py::TestLocalPath::test_touch tests/test_local.py::TestLocalPath::test_copy_override tests/test_local.py::TestLocalPath::test_copy_nonexistant_dir tests/test_local.py::TestLocalPath::test_unlink tests/test_local.py::TestLocalPath::test_unhashable tests/test_local.py::TestLocalPath::test_getpath tests/test_local.py::TestLocalPath::test_path_dir tests/test_local.py::TestLocalPath::test_mkdir tests/test_local.py::TestLocalPath::test_mkdir_mode tests/test_local.py::TestLocalPath::test_str_getitem tests/test_local.py::TestLocalPath::test_fspath tests/test_local.py::TestLocalMachine::test_getattr tests/test_local.py::TestLocalMachine::test_imports tests/test_local.py::TestLocalMachine::test_get tests/test_local.py::TestLocalMachine::test_shadowed_by_dir tests/test_local.py::TestLocalMachine::test_repr_command tests/test_local.py::TestLocalMachine::test_cwd tests/test_local.py::TestLocalMachine::test_mixing_chdir tests/test_local.py::TestLocalMachine::test_contains tests/test_local.py::TestLocalMachine::test_path tests/test_local.py::TestLocalMachine::test_glob_spaces tests/test_local.py::TestLocalMachine::test_env tests/test_local.py::TestLocalMachine::test_local tests/test_local.py::TestLocalMachine::test_piping tests/test_local.py::TestLocalMachine::test_redirection tests/test_local.py::TestLocalMachine::test_popen tests/test_local.py::TestLocalMachine::test_run tests/test_local.py::TestLocalMachine::test_timeout tests/test_local.py::TestLocalMachine::test_pipe_stderr tests/test_local.py::TestLocalMachine::test_fair_error_attribution tests/test_local.py::TestLocalMachine::test_iter_lines_timeout tests/test_local.py::TestLocalMachine::test_iter_lines_buffer_size tests/test_local.py::TestLocalMachine::test_iter_lines_timeout_by_type tests/test_local.py::TestLocalMachine::test_iter_lines_error tests/test_local.py::TestLocalMachine::test_iter_lines_line_timeout tests/test_local.py::TestLocalMachine::test_modifiers tests/test_local.py::TestLocalMachine::test_tee_modifier tests/test_local.py::TestLocalMachine::test_tee_race tests/test_local.py::TestLocalMachine::test_logger_pipe tests/test_local.py::TestLocalMachine::test_logger_pipe_line_timeout tests/test_local.py::TestLocalMachine::test_arg_expansion tests/test_local.py::TestLocalMachine::test_session tests/test_local.py::TestLocalMachine::test_quoting tests/test_local.py::TestLocalMachine::test_exception_pickling tests/test_local.py::TestLocalMachine::test_tempdir tests/test_local.py::TestLocalMachine::test_direct_open_tmpdir tests/test_local.py::TestLocalMachine::test_read_write_str tests/test_local.py::TestLocalMachine::test_read_write_unicode tests/test_local.py::TestLocalMachine::test_read_write_bin tests/test_local.py::TestLocalMachine::test_links tests/test_local.py::TestLocalMachine::test_list_processes tests/test_local.py::TestLocalMachine::test_pgrep tests/test_local.py::TestLocalMachine::test_local_daemon tests/test_local.py::TestLocalMachine::test_atomic_file tests/test_local.py::TestLocalMachine::test_atomic_file2 tests/test_local.py::TestLocalMachine::test_pid_file tests/test_local.py::TestLocalMachine::test_atomic_counter tests/test_local.py::TestLocalMachine::test_atomic_counter2 tests/test_local.py::TestLocalMachine::test_bound_env tests/test_local.py::TestLocalMachine::test_nesting_lists_as_argv tests/test_local.py::TestLocalMachine::test_contains_ls tests/test_local.py::TestLocalMachine::test_issue_139 tests/test_local.py::TestLocalMachine::test_pipeline_failure tests/test_local.py::TestLocalMachine::test_cmd tests/test_local.py::TestLocalMachine::test_pipeline_retcode tests/test_local.py::TestLocalMachine::test_pipeline_stdin tests/test_local.py::TestLocalMachine::test_run_bg tests/test_local.py::TestLocalMachine::test_run_fg tests/test_local.py::TestLocalMachine::test_run_tee tests/test_local.py::TestLocalMachine::test_run_tf tests/test_local.py::TestLocalMachine::test_run_retcode tests/test_local.py::TestLocalMachine::test_run_nohup tests/test_local.py::TestLocalEncoding::test_inout_rich tests/test_local.py::TestLocalEncoding::test_out_rich tests/test_local.py::TestLocalEncoding::test_runfile_rich tests/test_local.py::test_local_glob_path