Skip to content

Commit 17ca212

Browse files
committed
types(workspace-imports) Example typings via NotRequried
1 parent 7237fe6 commit 17ca212

3 files changed

Lines changed: 102 additions & 27 deletions

File tree

src/tmuxp/_internal/types.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,64 @@ class PluginConfigSchema(TypedDict):
3838
tmuxp_min_version: NotRequired[str]
3939
tmuxp_max_version: NotRequired[str]
4040
tmuxp_version_incompatible: NotRequired[list[str]]
41+
42+
43+
class ShellCommandConfig(TypedDict):
44+
"""Shell command configuration."""
45+
46+
cmd: str
47+
enter: NotRequired[bool]
48+
suppress_history: NotRequired[bool]
49+
50+
51+
ShellCommandValue = str | ShellCommandConfig | list[str | ShellCommandConfig]
52+
53+
54+
class PaneConfig(TypedDict, total=False):
55+
"""Pane configuration."""
56+
57+
shell_command: NotRequired[ShellCommandValue]
58+
shell_command_before: NotRequired[ShellCommandValue]
59+
start_directory: NotRequired[str]
60+
environment: NotRequired[dict[str, str]]
61+
focus: NotRequired[str | bool]
62+
suppress_history: NotRequired[bool]
63+
target: NotRequired[str]
64+
65+
66+
PaneValue = str | PaneConfig | None
67+
68+
69+
class WindowConfig(TypedDict, total=False):
70+
"""Window configuration."""
71+
72+
window_name: str
73+
start_directory: NotRequired[str]
74+
shell_command_before: NotRequired[ShellCommandValue]
75+
shell_command_after: NotRequired[ShellCommandValue]
76+
layout: NotRequired[str]
77+
clear: NotRequired[bool]
78+
options: NotRequired[dict[str, t.Any]]
79+
options_after: NotRequired[dict[str, t.Any]]
80+
environment: NotRequired[dict[str, str]]
81+
focus: NotRequired[str | bool]
82+
suppress_history: NotRequired[bool]
83+
panes: NotRequired[list[PaneValue]]
84+
85+
86+
class WorkspaceConfig(TypedDict, total=False):
87+
"""Complete tmuxp workspace configuration."""
88+
89+
session_name: str | None # Can be None during import
90+
start_directory: NotRequired[str]
91+
before_script: NotRequired[str]
92+
shell_command_before: NotRequired[ShellCommandValue]
93+
shell_command: NotRequired[ShellCommandValue] # Used in import
94+
environment: NotRequired[dict[str, str]]
95+
global_options: NotRequired[dict[str, t.Any]]
96+
options: NotRequired[dict[str, t.Any]]
97+
config: NotRequired[str] # tmux config file path
98+
socket_name: NotRequired[str] # tmux socket name
99+
plugins: NotRequired[list[str | PluginConfigSchema]]
100+
suppress_history: NotRequired[bool]
101+
windows: list[WindowConfig]

src/tmuxp/cli/import_config.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545

4646
CLIColorModeLiteral: TypeAlias = t.Literal["auto", "always", "never"]
4747

48+
from tmuxp._internal.types import WorkspaceConfig
49+
4850

4951
def get_tmuxinator_dir() -> pathlib.Path:
5052
"""Return tmuxinator configuration directory.
@@ -160,7 +162,7 @@ def create_import_subparser(
160162
class ImportConfigFn(t.Protocol):
161163
"""Typing for import configuration callback function."""
162164

163-
def __call__(self, workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
165+
def __call__(self, workspace_dict: dict[str, t.Any]) -> WorkspaceConfig:
164166
"""Execute tmuxp import function."""
165167
...
166168

@@ -176,7 +178,9 @@ def import_config(
176178
colors = Colors(ColorMode.AUTO)
177179

178180
existing_workspace_file = ConfigReader._from_file(pathlib.Path(workspace_file))
179-
cfg_reader = ConfigReader(importfunc(existing_workspace_file))
181+
cfg_reader = ConfigReader(
182+
t.cast(dict[t.Any, t.Any], importfunc(existing_workspace_file))
183+
)
180184

181185
workspace_file_format = prompt_choices(
182186
"Convert to",

src/tmuxp/workspace/importers.py

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77

88
logger = logging.getLogger(__name__)
99

10+
if t.TYPE_CHECKING:
11+
from tmuxp._internal.types import WindowConfig, WorkspaceConfig
1012

11-
def import_tmuxinator(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
13+
14+
def import_tmuxinator(workspace_dict: dict[str, t.Any]) -> WorkspaceConfig:
1215
"""Return tmuxp workspace from a `tmuxinator`_ yaml workspace.
1316
1417
.. _tmuxinator: https://github.com/aziz/tmuxinator
@@ -21,6 +24,7 @@ def import_tmuxinator(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
2124
Returns
2225
-------
2326
dict
27+
A dictionary conforming to WorkspaceConfig structure.
2428
"""
2529
logger.debug(
2630
"importing tmuxinator workspace",
@@ -30,7 +34,7 @@ def import_tmuxinator(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
3034
},
3135
)
3236

33-
tmuxp_workspace: dict[str, t.Any] = {}
37+
tmuxp_workspace: WorkspaceConfig = {"session_name": None, "windows": []}
3438

3539
if "project_name" in workspace_dict:
3640
tmuxp_workspace["session_name"] = workspace_dict.pop("project_name")
@@ -62,8 +66,6 @@ def import_tmuxinator(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
6266
if "socket_name" in workspace_dict:
6367
tmuxp_workspace["socket_name"] = workspace_dict["socket_name"]
6468

65-
tmuxp_workspace["windows"] = []
66-
6769
if "tabs" in workspace_dict:
6870
workspace_dict["windows"] = workspace_dict.pop("tabs")
6971

@@ -83,37 +85,44 @@ def import_tmuxinator(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
8385
if "rbenv" in workspace_dict:
8486
if "shell_command_before" not in tmuxp_workspace:
8587
tmuxp_workspace["shell_command_before"] = []
88+
else:
89+
# Ensure shell_command_before is a list
90+
current = tmuxp_workspace["shell_command_before"]
91+
if isinstance(current, (str, dict)):
92+
tmuxp_workspace["shell_command_before"] = [current]
93+
# Now we can safely append
94+
assert isinstance(tmuxp_workspace["shell_command_before"], list)
8695
tmuxp_workspace["shell_command_before"].append(
8796
"rbenv shell {}".format(workspace_dict["rbenv"]),
8897
)
8998

90-
for window_dict in workspace_dict["windows"]:
91-
for k, v in window_dict.items():
92-
window_dict = {"window_name": k}
99+
for window_item in workspace_dict["windows"]:
100+
for k, v in window_item.items():
101+
new_window: WindowConfig = {"window_name": k}
93102

94103
if isinstance(v, str) or v is None:
95-
window_dict["panes"] = [v]
96-
tmuxp_workspace["windows"].append(window_dict)
104+
new_window["panes"] = [v]
105+
tmuxp_workspace["windows"].append(new_window)
97106
continue
98107
if isinstance(v, list):
99-
window_dict["panes"] = v
100-
tmuxp_workspace["windows"].append(window_dict)
108+
new_window["panes"] = v
109+
tmuxp_workspace["windows"].append(new_window)
101110
continue
102111

103112
if "pre" in v:
104-
window_dict["shell_command_before"] = v["pre"]
113+
new_window["shell_command_before"] = v["pre"]
105114
if "panes" in v:
106-
window_dict["panes"] = v["panes"]
115+
new_window["panes"] = v["panes"]
107116
if "root" in v:
108-
window_dict["start_directory"] = v["root"]
117+
new_window["start_directory"] = v["root"]
109118

110119
if "layout" in v:
111-
window_dict["layout"] = v["layout"]
112-
tmuxp_workspace["windows"].append(window_dict)
120+
new_window["layout"] = v["layout"]
121+
tmuxp_workspace["windows"].append(new_window)
113122
return tmuxp_workspace
114123

115124

116-
def import_teamocil(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
125+
def import_teamocil(workspace_dict: dict[str, t.Any]) -> WorkspaceConfig:
117126
"""Return tmuxp workspace from a `teamocil`_ yaml workspace.
118127
119128
.. _teamocil: https://github.com/remiprev/teamocil
@@ -123,6 +132,11 @@ def import_teamocil(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
123132
workspace_dict : dict
124133
python dict for tmuxp workspace
125134
135+
Returns
136+
-------
137+
dict
138+
A dictionary conforming to WorkspaceConfig structure.
139+
126140
Notes
127141
-----
128142
Todos:
@@ -139,7 +153,7 @@ def import_teamocil(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
139153
extra={"tmux_session": _inner.get("name", "")},
140154
)
141155

142-
tmuxp_workspace: dict[str, t.Any] = {}
156+
tmuxp_workspace: WorkspaceConfig = {"session_name": None, "windows": []}
143157

144158
if "session" in workspace_dict:
145159
workspace_dict = workspace_dict["session"]
@@ -149,21 +163,17 @@ def import_teamocil(workspace_dict: dict[str, t.Any]) -> dict[str, t.Any]:
149163
if "root" in workspace_dict:
150164
tmuxp_workspace["start_directory"] = workspace_dict.pop("root")
151165

152-
tmuxp_workspace["windows"] = []
153-
154166
for w in workspace_dict["windows"]:
155-
window_dict = {"window_name": w["name"]}
167+
window_dict: WindowConfig = {"window_name": w["name"]}
156168

157169
if "clear" in w:
158170
window_dict["clear"] = w["clear"]
159171

160172
if "filters" in w:
161173
if "before" in w["filters"]:
162-
for _b in w["filters"]["before"]:
163-
window_dict["shell_command_before"] = w["filters"]["before"]
174+
window_dict["shell_command_before"] = w["filters"]["before"]
164175
if "after" in w["filters"]:
165-
for _b in w["filters"]["after"]:
166-
window_dict["shell_command_after"] = w["filters"]["after"]
176+
window_dict["shell_command_after"] = w["filters"]["after"]
167177

168178
if "root" in w:
169179
window_dict["start_directory"] = w.pop("root")

0 commit comments

Comments
 (0)