Skip to content

Commit 8423935

Browse files
committed
refactor: Basic typings and namespaces
1 parent f3c61e7 commit 8423935

File tree

12 files changed

+374
-275
lines changed

12 files changed

+374
-275
lines changed

conftest.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
@pytest.mark.skipif(USING_ZSH, reason="Using ZSH")
2727
@pytest.fixture(autouse=USING_ZSH, scope="session")
28-
def zshrc(user_path: pathlib.Path):
28+
def zshrc(user_path: pathlib.Path) -> pathlib.Path:
2929
"""This quiets ZSH default message.
3030
3131
Needs a startup file .zshenv, .zprofile, .zshrc, .zlogin.
@@ -41,7 +41,7 @@ def home_path_default(monkeypatch: pytest.MonkeyPatch, user_path: pathlib.Path)
4141

4242

4343
@pytest.fixture(scope="function")
44-
def monkeypatch_plugin_test_packages(monkeypatch):
44+
def monkeypatch_plugin_test_packages(monkeypatch: pytest.MonkeyPatch) -> None:
4545
paths = [
4646
"tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bwb/",
4747
"tests/fixtures/pluginsystem/plugins/tmuxp_test_plugin_bs/",
@@ -55,7 +55,7 @@ def monkeypatch_plugin_test_packages(monkeypatch):
5555

5656

5757
@pytest.fixture(scope="function")
58-
def socket_name(request) -> str:
58+
def socket_name(request: pytest.FixtureRequest) -> str:
5959
return "tmuxp_test%s" % next(namer)
6060

6161

src/tmuxp/cli/__init__.py

+31-33
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import argparse
88
import logging
99
import os
10+
import pathlib
1011
import sys
12+
import typing as t
1113

1214
from libtmux.__about__ import __version__ as libtmux_version
1315
from libtmux.common import has_minimum_version
@@ -19,19 +21,28 @@
1921
from .convert import command_convert, create_convert_subparser
2022
from .debug_info import command_debug_info, create_debug_info_subparser
2123
from .edit import command_edit, create_edit_subparser
22-
from .freeze import command_freeze, create_freeze_subparser
24+
from .freeze import CLIFreezeNamespace, command_freeze, create_freeze_subparser
2325
from .import_config import (
2426
command_import_teamocil,
2527
command_import_tmuxinator,
2628
create_import_subparser,
2729
)
28-
from .load import command_load, create_load_subparser
30+
from .load import CLILoadNamespace, command_load, create_load_subparser
2931
from .ls import command_ls, create_ls_subparser
30-
from .shell import command_shell, create_shell_subparser
32+
from .shell import CLIShellNamespace, command_shell, create_shell_subparser
3133
from .utils import tmuxp_echo
3234

3335
logger = logging.getLogger(__name__)
3436

37+
if t.TYPE_CHECKING:
38+
from typing_extensions import Literal, TypeAlias
39+
40+
CLIVerbosity: TypeAlias = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
41+
CLISubparserName: TypeAlias = Literal[
42+
"ls", "load", "convert", "edit", "import", "shell", "debug-info"
43+
]
44+
CLIImportSubparserName: TypeAlias = Literal["teamocil", "tmuxinator"]
45+
3546

3647
def create_parser() -> argparse.ArgumentParser:
3748
parser = argparse.ArgumentParser(prog="tmuxp")
@@ -83,7 +94,17 @@ def create_parser() -> argparse.ArgumentParser:
8394
return parser
8495

8596

86-
def cli(args=None):
97+
class CLINamespace(argparse.Namespace):
98+
log_level: "CLIVerbosity"
99+
subparser_name: "CLISubparserName"
100+
import_subparser_name: t.Optional["CLIImportSubparserName"]
101+
version: bool
102+
103+
104+
ns = CLINamespace()
105+
106+
107+
def cli(_args: t.Optional[t.List[str]] = None) -> None:
87108
"""Manage tmux sessions.
88109
89110
Pass the "--help" argument to any command to see detailed help.
@@ -96,11 +117,11 @@ def cli(args=None):
96117
tmuxp_echo("tmux not found. tmuxp requires you install tmux first.")
97118
sys.exit()
98119
except exc.TmuxpException as e:
99-
tmuxp_echo(e, err=True)
120+
tmuxp_echo(str(e))
100121
sys.exit()
101122

102123
parser = create_parser()
103-
args = parser.parse_args(args)
124+
args = parser.parse_args(_args, namespace=ns)
104125

105126
setup_logger(logger=logger, level=args.log_level.upper())
106127

@@ -109,28 +130,12 @@ def cli(args=None):
109130
return
110131
elif args.subparser_name == "load":
111132
command_load(
112-
config_file=args.config_file,
113-
socket_name=args.socket_name,
114-
socket_path=args.socket_path,
115-
tmux_config_file=args.tmux_config_file,
116-
new_session_name=args.new_session_name,
117-
answer_yes=args.answer_yes,
118-
detached=args.detached,
119-
append=args.append,
120-
colors=args.colors,
121-
log_file=args.log_file,
133+
args=CLILoadNamespace(**vars(args)),
122134
parser=parser,
123135
)
124136
elif args.subparser_name == "shell":
125137
command_shell(
126-
session_name=args.session_name,
127-
window_name=args.window_name,
128-
socket_name=args.socket_name,
129-
socket_path=args.socket_path,
130-
command=args.command,
131-
shell=args.shell,
132-
use_pythonrc=args.use_pythonrc,
133-
use_vi_mode=args.use_vi_mode,
138+
args=CLIShellNamespace(**vars(args)),
134139
parser=parser,
135140
)
136141
elif args.subparser_name == "import":
@@ -164,21 +169,14 @@ def cli(args=None):
164169
)
165170
elif args.subparser_name == "freeze":
166171
command_freeze(
167-
session_name=args.session_name,
168-
socket_name=args.socket_name,
169-
socket_path=args.socket_path,
170-
config_format=args.config_format,
171-
save_to=args.save_to,
172-
answer_yes=args.answer_yes,
173-
quiet=args.quiet,
174-
force=args.force,
172+
args=CLIFreezeNamespace(**vars(args)),
175173
parser=parser,
176174
)
177175
elif args.subparser_name == "ls":
178176
command_ls(parser=parser)
179177

180178

181-
def startup(config_dir):
179+
def startup(config_dir: pathlib.Path) -> None:
182180
"""
183181
Initialize CLI.
184182

src/tmuxp/cli/convert.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def command_convert(
3131
config_file: t.Union[str, pathlib.Path],
3232
answer_yes: bool,
3333
parser: t.Optional[argparse.ArgumentParser] = None,
34-
):
34+
) -> None:
3535
"""Convert a tmuxp config between JSON and YAML."""
3636
config_file = scan_config(config_file, config_dir=get_config_dir())
3737

src/tmuxp/cli/debug_info.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def create_debug_info_subparser(
2525

2626
def command_debug_info(
2727
parser: t.Optional[argparse.ArgumentParser] = None,
28-
):
28+
) -> None:
2929
"""
3030
Print debug info to submit with Issues.
3131
"""

src/tmuxp/cli/edit.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ def create_edit_subparser(
1212
) -> argparse.ArgumentParser:
1313
parser.add_argument(
1414
dest="config_file",
15+
metavar="config-file",
1516
type=str,
16-
help="checks current tmuxp and current directory for yaml files.",
17+
help="checks current tmuxp and current directory for config files.",
1718
)
1819
return parser
1920

src/tmuxp/cli/freeze.py

+52-31
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,28 @@
1010

1111
from .. import config, util
1212
from ..workspacebuilder import freeze
13-
from .utils import _validate_choices, get_config_dir, prompt, prompt_yes_no
13+
from .utils import get_config_dir, prompt, prompt_choices, prompt_yes_no
14+
15+
if t.TYPE_CHECKING:
16+
from typing_extensions import Literal, TypeAlias, TypeGuard
17+
18+
CLIOutputFormatLiteral: TypeAlias = Literal["yaml", "json"]
19+
20+
21+
class CLIFreezeNamespace(argparse.Namespace):
22+
session_name: str
23+
socket_name: t.Optional[str]
24+
socket_path: t.Optional[str]
25+
config_format: t.Optional["CLIOutputFormatLiteral"]
26+
save_to: t.Optional[str]
27+
answer_yes: t.Optional[bool]
28+
quiet: t.Optional[bool]
29+
force: t.Optional[bool]
1430

1531

1632
def session_completion(ctx, params, incomplete):
17-
t = Server()
18-
choices = [session.name for session in t.list_sessions()]
33+
server = Server()
34+
choices = [session.name for session in server.list_sessions()]
1935
return sorted(str(c) for c in choices if str(c).startswith(incomplete))
2036

2137

@@ -69,28 +85,20 @@ def create_freeze_subparser(
6985

7086

7187
def command_freeze(
72-
session_name: t.Optional[str] = None,
73-
socket_name: t.Optional[str] = None,
74-
config_format: t.Optional[str] = None,
75-
save_to: t.Optional[str] = None,
76-
socket_path: t.Optional[str] = None,
77-
answer_yes: t.Optional[bool] = None,
78-
quiet: t.Optional[bool] = None,
79-
force: t.Optional[bool] = None,
88+
args: CLIFreezeNamespace,
8089
parser: t.Optional[argparse.ArgumentParser] = None,
8190
) -> None:
8291
"""Snapshot a session into a config.
8392
8493
If SESSION_NAME is provided, snapshot that session. Otherwise, use the
8594
current session."""
86-
87-
t = Server(socket_name=socket_name, socket_path=socket_path)
95+
server = Server(socket_name=args.socket_name, socket_path=args.socket_path)
8896

8997
try:
90-
if session_name:
91-
session = t.find_where({"session_name": session_name})
98+
if args.session_name:
99+
session = server.find_where({"session_name": args.session_name})
92100
else:
93-
session = util.get_session(t)
101+
session = util.get_session(server)
94102

95103
if not session:
96104
raise TmuxpException("Session not found.")
@@ -102,54 +110,67 @@ def command_freeze(
102110
newconfig = config.inline(sconf)
103111
configparser = ConfigReader(newconfig)
104112

105-
if not quiet:
113+
if not args.quiet:
106114
print(
107115
"---------------------------------------------------------------"
108116
"\n"
109117
"Freeze does its best to snapshot live tmux sessions.\n"
110118
)
111119
if not (
112-
answer_yes
120+
args.answer_yes
113121
or prompt_yes_no(
114122
"The new config *WILL* require adjusting afterwards. Save config?"
115123
)
116124
):
117-
if not quiet:
125+
if not args.quiet:
118126
print(
119127
"tmuxp has examples in JSON and YAML format at "
120128
"<http://tmuxp.git-pull.com/examples.html>\n"
121129
"View tmuxp docs at <http://tmuxp.git-pull.com/>."
122130
)
123131
sys.exit()
124132

125-
dest = save_to
133+
dest = args.save_to
126134
while not dest:
127135
save_to = os.path.abspath(
128136
os.path.join(
129137
get_config_dir(),
130-
"{}.{}".format(sconf.get("session_name"), config_format or "yaml"),
138+
"{}.{}".format(sconf.get("session_name"), args.config_format or "yaml"),
131139
)
132140
)
133141
dest_prompt = prompt(
134142
"Save to: %s" % save_to,
135143
default=save_to,
136144
)
137-
if not force and os.path.exists(dest_prompt):
145+
if not args.force and os.path.exists(dest_prompt):
138146
print("%s exists. Pick a new filename." % dest_prompt)
139147
continue
140148

141149
dest = dest_prompt
142150
dest = os.path.abspath(os.path.relpath(os.path.expanduser(dest)))
151+
config_format = args.config_format
152+
153+
valid_config_formats: t.List["CLIOutputFormatLiteral"] = ["json", "yaml"]
154+
155+
def is_valid_ext(stem: t.Optional[str]) -> "TypeGuard[CLIOutputFormatLiteral]":
156+
return stem in valid_config_formats
157+
158+
if not is_valid_ext(config_format):
159+
160+
def extract_config_format(val: str) -> t.Optional["CLIOutputFormatLiteral"]:
161+
suffix = pathlib.Path(val).suffix
162+
if isinstance(suffix, str):
163+
suffix = suffix.lower().lstrip(".")
164+
if is_valid_ext(suffix):
165+
return suffix
166+
return None
143167

144-
if config_format is None or config_format == "":
145-
valid_config_formats = ["json", "yaml"]
146-
_, config_format = os.path.splitext(dest)
147-
config_format = config_format[1:].lower()
148-
if config_format not in valid_config_formats:
149-
config_format = prompt(
168+
config_format = extract_config_format(dest)
169+
if not is_valid_ext(config_format):
170+
config_format = prompt_choices(
150171
"Couldn't ascertain one of [%s] from file name. Convert to"
151172
% ", ".join(valid_config_formats),
152-
value_proc=_validate_choices(["yaml", "json"]),
173+
choices=valid_config_formats,
153174
default="yaml",
154175
)
155176

@@ -160,13 +181,13 @@ def command_freeze(
160181
elif config_format == "json":
161182
newconfig = configparser.dump(format="json", indent=2)
162183

163-
if answer_yes or prompt_yes_no("Save to %s?" % dest):
184+
if args.answer_yes or prompt_yes_no("Save to %s?" % dest):
164185
destdir = os.path.dirname(dest)
165186
if not os.path.isdir(destdir):
166187
os.makedirs(destdir)
167188
buf = open(dest, "w")
168189
buf.write(newconfig)
169190
buf.close()
170191

171-
if not quiet:
192+
if not args.quiet:
172193
print("Saved to %s." % dest)

0 commit comments

Comments
 (0)