Skip to content

Commit

Permalink
Adds support for editing multiple files (pallets#2068)
Browse files Browse the repository at this point in the history
Co-authored-by: Andreas Backx <[email protected]>
  • Loading branch information
yashrathi-git and AndreasBackx authored Nov 7, 2024
1 parent d791537 commit 0d69b6c
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ Unreleased
- Contexts created during shell completion are closed properly, fixing
``ResourceWarning``s when using ``click.File``. :issue:`2644` :pr:`2800`
:pr:`2767`
- ``click.edit(filename)`` now supports passing an iterable of filenames in
case the editor supports editing multiple files at once. Its return type
is now also typed: ``AnyStr`` if ``text`` is passed, otherwise ``None``.
:issue:`2067` :pr:`2068`


Version 8.1.8
Expand Down
10 changes: 7 additions & 3 deletions src/click/_termui_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ def get_editor(self) -> str:
return editor
return "vi"

def edit_file(self, filename: str) -> None:
def edit_files(self, filenames: cabc.Iterable[str]) -> None:
import subprocess

editor = self.get_editor()
Expand All @@ -515,8 +515,12 @@ def edit_file(self, filename: str) -> None:
environ = os.environ.copy()
environ.update(self.env)

exc_filename = " ".join(f'"{filename}"' for filename in filenames)

try:
c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True)
c = subprocess.Popen(
args=f"{editor} {exc_filename}", env=environ, shell=True
)
exit_code = c.wait()
if exit_code != 0:
raise ClickException(
Expand Down Expand Up @@ -559,7 +563,7 @@ def edit(self, text: t.AnyStr | None) -> t.AnyStr | None:
# recorded, so get the new recorded value.
timestamp = os.path.getmtime(name)

self.edit_file(name)
self.edit_files((name,))

if self.require_save and os.path.getmtime(name) == timestamp:
return None
Expand Down
39 changes: 36 additions & 3 deletions src/click/termui.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,13 +643,34 @@ def secho(
return echo(message, file=file, nl=nl, err=err, color=color)


@t.overload
def edit(
text: t.AnyStr,
editor: str | None = None,
env: cabc.Mapping[str, str] | None = None,
require_save: bool = True,
extension: str = ".txt",
) -> t.AnyStr: ...


@t.overload
def edit(
text: None = None,
editor: str | None = None,
env: cabc.Mapping[str, str] | None = None,
require_save: bool = True,
extension: str = ".txt",
filename: str | cabc.Iterable[str] | None = None,
) -> None: ...


def edit(
text: t.AnyStr | None = None,
editor: str | None = None,
env: cabc.Mapping[str, str] | None = None,
require_save: bool = True,
extension: str = ".txt",
filename: str | None = None,
filename: str | cabc.Iterable[str] | None = None,
) -> t.AnyStr | None:
r"""Edits the given text in the defined editor. If an editor is given
(should be the full path to the executable but the regular operating
Expand All @@ -676,7 +697,16 @@ def edit(
highlighting.
:param filename: if provided it will edit this file instead of the
provided text contents. It will not use a temporary
file as an indirection in that case.
file as an indirection in that case. If the editor supports
editing multiple files at once, a sequence of files may be
passed as well. Invoke `click.file` once per file instead
if multiple files cannot be managed at once or editing the
files serially is desired.
.. versionchanged:: 8.2.0
``filename`` now accepts any ``Iterable[str]`` in addition to a ``str``
if the ``editor`` supports editing multiple files at once.
"""
from ._termui_impl import Editor

Expand All @@ -685,7 +715,10 @@ def edit(
if filename is None:
return ed.edit(text)

ed.edit_file(filename)
if isinstance(filename, str):
filename = (filename,)

ed.edit_files(filenames=filename)
return None


Expand Down
15 changes: 15 additions & 0 deletions tests/test_termui.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import platform
import tempfile
import time

import pytest
Expand Down Expand Up @@ -380,6 +381,20 @@ def test_fast_edit(runner):
assert result == "aTest\nbTest\n"


@pytest.mark.skipif(platform.system() == "Windows", reason="No sed on Windows.")
def test_edit(runner):
with tempfile.NamedTemporaryFile(mode="w") as named_tempfile:
named_tempfile.write("a\nb")
named_tempfile.flush()

result = click.edit(filename=named_tempfile.name, editor="sed -i~ 's/$/Test/'")
assert result is None

# We need ot reopen the file as it becomes unreadable after the edit.
with open(named_tempfile.name) as reopened_file:
assert reopened_file.read() == "aTest\nbTest"


@pytest.mark.parametrize(
("prompt_required", "required", "args", "expect"),
[
Expand Down

0 comments on commit 0d69b6c

Please sign in to comment.