From e770c502d71dab7b511a8bb449b3c435fc6e78c7 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Wed, 28 Dec 2022 19:42:01 -0600 Subject: [PATCH 1/8] feat(WorkspaceBuilder): Require server in constructor --- CHANGES | 2 + src/tmuxp/workspace/builder.py | 10 ++--- tests/workspace/test_builder.py | 70 +++++++++++++++++++-------------- tests/workspace/test_freezer.py | 2 +- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/CHANGES b/CHANGES index 3b252a3b684..a85eff48f2c 100644 --- a/CHANGES +++ b/CHANGES @@ -29,6 +29,8 @@ $ pipx install --suffix=@next 'tmuxp' --pip-args '\--pre' --force - Fix resolution of direcotries +- WorkspaceBuilder: Require `Server` in constructor (#857) + ## tmuxp 1.23.0 (_yanked_, 2022-12-28) _Yanked release: `tmuxp load` issues, see #856_ diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py index 619e67f86d0..db4d6e9e3e4 100644 --- a/src/tmuxp/workspace/builder.py +++ b/src/tmuxp/workspace/builder.py @@ -130,20 +130,20 @@ class WorkspaceBuilder: their panes, returning full :class:`libtmux.Window` and :class:`libtmux.Pane` objects each step of the way:: - workspace = WorkspaceBuilder(sconf=sconf) + workspace = WorkspaceBuilder(sconf=sconf, server=server) It handles the magic of cases where the user may want to start a session inside tmux (when `$TMUX` is in the env variables). """ - server: t.Optional["Server"] + server: "Server" session: t.Optional["Session"] def __init__( self, sconf: t.Dict[str, t.Any], + server: Server, plugins: t.List[t.Any] = [], - server: t.Optional[Server] = None, ) -> None: """ Initialize workspace loading. @@ -170,8 +170,8 @@ def __init__( # validation.validate_schema(sconf) - if isinstance(server, Server): - self.server = server + assert isinstance(server, Server) + self.server = server self.sconf = sconf diff --git a/tests/workspace/test_builder.py b/tests/workspace/test_builder.py index 44e577f5c76..2100a461b37 100644 --- a/tests/workspace/test_builder.py +++ b/tests/workspace/test_builder.py @@ -31,7 +31,7 @@ def test_split_windows(session): test_utils.get_workspace_file("workspace/builder/two_pane.yaml") ) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) window_count = len(session.windows) # current window count assert len(session.windows) == window_count @@ -51,7 +51,7 @@ def test_split_windows_three_pane(session): test_utils.get_workspace_file("workspace/builder/three_pane.yaml") ) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) window_count = len(session.windows) # current window count assert len(session.windows) == window_count @@ -75,7 +75,7 @@ def test_focus_pane_index(session): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) @@ -154,7 +154,7 @@ def test_suppress_history(session): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) inHistoryWindow = session.windows.get(window_name="inHistory") @@ -207,7 +207,7 @@ def test_session_options(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) _default_shell = session.show_option("default-shell") @@ -225,7 +225,7 @@ def test_global_options(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) _status_position = session.show_option("status-position", _global=True) @@ -247,7 +247,7 @@ def test_global_session_env_options(session, monkeypatch): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) _visual_silence = session.show_option("visual-silence", _global=True) @@ -268,7 +268,7 @@ def test_window_options(session): if has_gte_version("2.3"): workspace["windows"][0]["options"]["pane-border-format"] = " #P " - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) window_count = len(session.windows) # current window count assert len(session.windows) == window_count @@ -294,7 +294,7 @@ def test_window_options_after(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) def assert_last_line(p, s): @@ -332,7 +332,7 @@ def test_window_shell(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) for w, wconf in builder.iter_create_windows(session): if "window_shell" in wconf: @@ -356,7 +356,7 @@ def test_environment_variables(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session) # Give slow shells some time to settle as otherwise tests might fail. time.sleep(0.3) @@ -398,7 +398,7 @@ def test_environment_variables_logs(session: Session, caplog: pytest.LogCaptureF ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session) # environment on sessions should work as this is done using set-environment @@ -486,7 +486,7 @@ def test_blank_pane_count(session): test_config = ConfigReader._from_file(yaml_workspace_file) test_config = loader.expand(test_config) - builder = WorkspaceBuilder(sconf=test_config) + builder = WorkspaceBuilder(sconf=test_config, server=session.server) builder.build(session=session) assert session == builder.session @@ -521,7 +521,7 @@ def test_start_directory(session, tmp_path: pathlib.Path): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) assert session == builder.session @@ -569,7 +569,7 @@ def test_start_directory_relative(session, tmp_path: pathlib.Path): assert os.path.exists(config_dir) assert os.path.exists(test_dir) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) assert session == builder.session @@ -630,7 +630,7 @@ def test_pane_order(session): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) window_count = len(session.windows) # current window count assert len(session.windows) == window_count @@ -671,7 +671,7 @@ def test_window_index(session): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) for window, _ in builder.iter_create_windows(session): expected_index = name_index_map[window.window_name] @@ -690,7 +690,7 @@ def test_before_load_throw_error_if_retcode_error(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=server) with temp_session(server) as sess: session_name = sess.name @@ -713,7 +713,7 @@ def test_before_load_throw_error_if_file_not_exists(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=server) with temp_session(server) as session: session_name = session.name @@ -738,7 +738,7 @@ def test_before_load_true_if_test_passes(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=server) with temp_session(server) as session: builder.build(session=session) @@ -757,7 +757,7 @@ def test_before_load_true_if_test_passes_with_args(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=server) with temp_session(server) as session: builder.build(session=session) @@ -771,7 +771,9 @@ def test_plugin_system_before_workspace_builder( ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, plugins=load_plugins(workspace)) + builder = WorkspaceBuilder( + sconf=workspace, plugins=load_plugins(workspace), server=session.server + ) assert len(builder.plugins) > 0 builder.build(session=session) @@ -786,7 +788,9 @@ def test_plugin_system_on_window_create(monkeypatch_plugin_test_packages, sessio ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, plugins=load_plugins(workspace)) + builder = WorkspaceBuilder( + sconf=workspace, plugins=load_plugins(workspace), server=session.server + ) assert len(builder.plugins) > 0 builder.build(session=session) @@ -801,7 +805,9 @@ def test_plugin_system_after_window_finished(monkeypatch_plugin_test_packages, s ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, plugins=load_plugins(workspace)) + builder = WorkspaceBuilder( + sconf=workspace, plugins=load_plugins(workspace), server=session.server + ) assert len(builder.plugins) > 0 builder.build(session=session) @@ -818,7 +824,9 @@ def test_plugin_system_on_window_create_multiple_windows(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, plugins=load_plugins(workspace)) + builder = WorkspaceBuilder( + sconf=workspace, plugins=load_plugins(workspace), server=session.server + ) assert len(builder.plugins) > 0 builder.build(session=session) @@ -838,7 +846,9 @@ def test_plugin_system_after_window_finished_multiple_windows( ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, plugins=load_plugins(workspace)) + builder = WorkspaceBuilder( + sconf=workspace, plugins=load_plugins(workspace), server=session.server + ) assert len(builder.plugins) > 0 builder.build(session=session) @@ -856,7 +866,9 @@ def test_plugin_system_multiple_plugins(monkeypatch_plugin_test_packages, sessio ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, plugins=load_plugins(workspace)) + builder = WorkspaceBuilder( + sconf=workspace, plugins=load_plugins(workspace), server=session.server + ) assert len(builder.plugins) > 0 builder.build(session=session) @@ -1245,7 +1257,7 @@ def test_first_pane_start_directory(session, tmp_path: pathlib.Path): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) assert session == builder.session @@ -1270,7 +1282,7 @@ def test_layout_main_horizontal(session): yaml_workspace = test_utils.get_workspace_file("workspace/builder/three_pane.yaml") workspace = ConfigReader._from_file(path=yaml_workspace) - builder = WorkspaceBuilder(sconf=workspace) + builder = WorkspaceBuilder(sconf=workspace, server=session.server) builder.build(session=session) assert session.windows diff --git a/tests/workspace/test_freezer.py b/tests/workspace/test_freezer.py index 7725caec2f7..2c41aee1c82 100644 --- a/tests/workspace/test_freezer.py +++ b/tests/workspace/test_freezer.py @@ -18,7 +18,7 @@ def test_freeze_config(session): test_utils.get_workspace_file("workspace/freezer/sample_workspace.yaml") ) - builder = WorkspaceBuilder(sconf=session_config) + builder = WorkspaceBuilder(sconf=session_config, server=session.server) builder.build(session=session) assert session == builder.session From b7fe6b62e790df00647f506e9a2297c7137303a1 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 29 Dec 2022 09:30:18 -0600 Subject: [PATCH 2/8] chore(WorkspaceBuilder): Remove unneeded newlines, improve docs --- src/tmuxp/workspace/builder.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py index db4d6e9e3e4..0146c2261bb 100644 --- a/src/tmuxp/workspace/builder.py +++ b/src/tmuxp/workspace/builder.py @@ -27,12 +27,10 @@ class WorkspaceBuilder: + """Load workspace from workspace :py:obj:`dict` object. - """ - Load workspace from session :py:obj:`dict`. - - Build tmux workspace from a configuration. Creates and names windows, sets - options, splits windows into panes. + Build tmux workspace from a configuration. Creates and names windows, sets options, + splits windows into panes. Examples -------- @@ -145,8 +143,7 @@ def __init__( server: Server, plugins: t.List[t.Any] = [], ) -> None: - """ - Initialize workspace loading. + """Initialize workspace loading. Parameters ---------- @@ -192,14 +189,12 @@ def session_exists(self, session_name: str) -> bool: return True def build(self, session: t.Optional[Session] = None, append: bool = False) -> None: - """ - Build tmux workspace in session. + """Build tmux workspace in session. Optionally accepts ``session`` to build with only session object. - Without ``session``, it will use :class:`libmtux.Server` at - ``self.server`` passed in on initialization to create a new Session - object. + Without ``session``, it will use :class:`libmtux.Server` at ``self.server`` + passed in on initialization to create a new Session object. Parameters ---------- @@ -318,8 +313,7 @@ def build(self, session: t.Optional[Session] = None, append: bool = False) -> No def iter_create_windows( self, session: Session, append: bool = False ) -> t.Iterator[t.Any]: - """ - Return :class:`libtmux.Window` iterating through session config dict. + """Return :class:`libtmux.Window` iterating through session config dict. Generator yielding :class:`libtmux.Window` by iterating through ``sconf['windows']``. @@ -417,8 +411,7 @@ def iter_create_windows( def iter_create_panes( self, w: Window, wconf: t.Dict[str, t.Any] ) -> t.Iterator[t.Any]: - """ - Return :class:`libtmux.Pane` iterating through window config dict. + """Return :class:`libtmux.Pane` iterating through window config dict. Run ``shell_command`` with ``$ tmux send-keys``. From aab30b50abfbe555848275ec1d40b4ad19f9e4e0 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 29 Dec 2022 09:11:20 -0600 Subject: [PATCH 3/8] refactor(WorkspaceBuilder): Refactor .session --- src/tmuxp/workspace/builder.py | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py index 0146c2261bb..7878da6d4c9 100644 --- a/src/tmuxp/workspace/builder.py +++ b/src/tmuxp/workspace/builder.py @@ -135,7 +135,8 @@ class WorkspaceBuilder: """ server: "Server" - session: t.Optional["Session"] + _session: t.Optional["Session"] + session_name: str def __init__( self, @@ -171,9 +172,29 @@ def __init__( self.server = server self.sconf = sconf - self.plugins = plugins + if self.server is not None and self.session_exists( + session_name=self.sconf["session_name"] + ): + try: + session = self.server.sessions.get( + session_name=self.sconf["session_name"] + ) + assert session is not None + self._session = session + except ObjectDoesNotExist: + pass + + @property + def session(self): + if self._session is None: + raise ObjectDoesNotExist( + "No session object exists for WorkspaceBuilder. " + "Tip: Add session_name in constructor or run WorkspaceBuilder.build()" + ) + return self._session + def session_exists(self, session_name: str) -> bool: assert session_name is not None assert isinstance(session_name, str) @@ -213,9 +234,12 @@ def build(self, session: t.Optional[Session] = None, append: bool = False) -> No if self.server.has_session(self.sconf["session_name"]): try: - self.session = self.server.sessions.get( + session = self.server.sessions.get( session_name=self.sconf["session_name"] ) + assert session is not None + assert isinstance(session, Session) + self._session = session raise TmuxSessionExists( "Session name %s is already running." @@ -241,7 +265,7 @@ def build(self, session: t.Optional[Session] = None, append: bool = False) -> No assert session is not None assert session.name is not None - self.session: "Session" = session + self._session = session assert session.server is not None From cfd0496c297f667d43cd6f27d65a1f3f1de33162 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 29 Dec 2022 11:03:22 -0600 Subject: [PATCH 4/8] chore(WorkspaceBuilder): Capitalize exception --- src/tmuxp/workspace/builder.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py index 7878da6d4c9..f1bbb6e4cbc 100644 --- a/src/tmuxp/workspace/builder.py +++ b/src/tmuxp/workspace/builder.py @@ -10,7 +10,6 @@ from libtmux._internal.query_list import ObjectDoesNotExist from libtmux.common import has_lt_version -from libtmux.exc import TmuxSessionExists from libtmux.pane import Pane from libtmux.server import Server from libtmux.session import Session @@ -164,7 +163,7 @@ def __init__( """ if not sconf: - raise exc.EmptyWorkspaceException("session configuration is empty.") + raise exc.EmptyWorkspaceException("Session configuration is empty.") # validation.validate_schema(sconf) @@ -231,22 +230,6 @@ def build(self, session: t.Optional[Session] = None, append: bool = False) -> No "WorkspaceBuilder.build requires server to be passed " + "on initialization, or pass in session object to here." ) - - if self.server.has_session(self.sconf["session_name"]): - try: - session = self.server.sessions.get( - session_name=self.sconf["session_name"] - ) - assert session is not None - assert isinstance(session, Session) - self._session = session - - raise TmuxSessionExists( - "Session name %s is already running." - % self.sconf["session_name"] - ) - except ObjectDoesNotExist: - pass else: new_session_kwargs = {} if "start_directory" in self.sconf: From 6581b389a5641e34758b8daca77b2bf400ef4626 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 29 Dec 2022 11:13:38 -0600 Subject: [PATCH 5/8] fix(WorkspaceBuilder): Fix logging usage --- src/tmuxp/workspace/builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py index f1bbb6e4cbc..ab6e29661b7 100644 --- a/src/tmuxp/workspace/builder.py +++ b/src/tmuxp/workspace/builder.py @@ -387,7 +387,7 @@ def iter_create_windows( target = "panes" else: target = "windows" - logging.warning( + logger.warning( f"Cannot set environment for new {target}. " "You need tmux 3.0 or newer for this." ) @@ -470,7 +470,7 @@ def get_pane_shell(): # configuration as a warning for the window was already issued when # the window was created. if pconf.get("environment"): - logging.warning( + logger.warning( "Cannot set environment for new panes. " "You need tmux 3.0 or newer for this." ) From 15b1ae3a2d1cf6e181c6a842bda4ae66ac713b08 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 29 Dec 2022 15:36:52 -0600 Subject: [PATCH 6/8] refactor(WorkspaceBuilder): Remove acronym / initialization of variables --- src/tmuxp/cli/load.py | 10 +- src/tmuxp/workspace/builder.py | 251 +++++++++++++++++--------------- tests/cli/test_cli.py | 8 +- tests/cli/test_load.py | 28 ++-- tests/workspace/test_builder.py | 84 +++++------ tests/workspace/test_freezer.py | 2 +- 6 files changed, 202 insertions(+), 181 deletions(-) diff --git a/src/tmuxp/cli/load.py b/src/tmuxp/cli/load.py index 41fbf4a869a..46862bb4238 100644 --- a/src/tmuxp/cli/load.py +++ b/src/tmuxp/cli/load.py @@ -107,13 +107,13 @@ def set_layout_hook(session: Session, hook_name: str) -> None: session.cmd(*cmd) -def load_plugins(sconf: t.Dict[str, t.Any]) -> t.List[t.Any]: +def load_plugins(session_config: t.Dict[str, t.Any]) -> t.List[t.Any]: """ Load and return plugins in workspace """ plugins = [] - if "plugins" in sconf: - for plugin in sconf["plugins"]: + if "plugins" in session_config: + for plugin in session_config["plugins"]: try: module_name = plugin.split(".") module_name = ".".join(module_name[:-1]) @@ -396,7 +396,9 @@ def load_workspace( try: # load WorkspaceBuilder object for tmuxp workspace / tmux server builder = WorkspaceBuilder( - sconf=expanded_workspace, plugins=load_plugins(expanded_workspace), server=t + session_config=expanded_workspace, + plugins=load_plugins(expanded_workspace), + server=t, ) except exc.EmptyWorkspaceException: tmuxp_echo("%s is empty or parsed no workspace data" % workspace_file) diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py index ab6e29661b7..366e828cf5f 100644 --- a/src/tmuxp/workspace/builder.py +++ b/src/tmuxp/workspace/builder.py @@ -59,7 +59,7 @@ class WorkspaceBuilder: ... - cmd: htop ... ''', Loader=yaml.Loader) - >>> builder = WorkspaceBuilder(sconf=session_config, server=server) + >>> builder = WorkspaceBuilder(session_config=session_config, server=server) **New session:** @@ -99,7 +99,7 @@ class WorkspaceBuilder: 1. Load JSON / YAML file via via :class:`pathlib.Path`:: from tmuxp import config_reader - sconf = config_reader.ConfigReader._load(raw_yaml) + session_config = config_reader.ConfigReader._load(raw_yaml) The reader automatically detects the file type from :attr:`pathlib.suffix`. @@ -108,26 +108,26 @@ class WorkspaceBuilder: import pathlib from tmuxp import config_reader - sconf = config_reader.ConfigReader._from_file( + session_config = config_reader.ConfigReader._from_file( pathlib.Path('path/to/config.yaml') ) - 2. :meth:`config.expand` sconf inline shorthand:: + 2. :meth:`config.expand` session_config inline shorthand:: from tmuxp import config - sconf = config.expand(sconf) + session_config = config.expand(session_config) 3. :meth:`config.trickle` passes down default values from session -> window -> pane if applicable:: - sconf = config.trickle(sconf) + session_config = config.trickle(session_config) 4. (You are here) We will create a :class:`libtmux.Session` (a real ``tmux(1)`` session) and iterate through the list of windows, and their panes, returning full :class:`libtmux.Window` and :class:`libtmux.Pane` objects each step of the way:: - workspace = WorkspaceBuilder(sconf=sconf, server=server) + workspace = WorkspaceBuilder(session_config=session_config, server=server) It handles the magic of cases where the user may want to start a session inside tmux (when `$TMUX` is in the env variables). @@ -139,7 +139,7 @@ class WorkspaceBuilder: def __init__( self, - sconf: t.Dict[str, t.Any], + session_config: t.Dict[str, t.Any], server: Server, plugins: t.List[t.Any] = [], ) -> None: @@ -147,7 +147,7 @@ def __init__( Parameters ---------- - sconf : dict + session_config : dict session config, includes a :py:obj:`list` of ``windows``. plugins : list @@ -162,23 +162,23 @@ def __init__( ``self.session``. """ - if not sconf: + if not session_config: raise exc.EmptyWorkspaceException("Session configuration is empty.") - # validation.validate_schema(sconf) + # validation.validate_schema(session_config) assert isinstance(server, Server) self.server = server - self.sconf = sconf + self.session_config = session_config self.plugins = plugins if self.server is not None and self.session_exists( - session_name=self.sconf["session_name"] + session_name=self.session_config["session_name"] ): try: session = self.server.sessions.get( - session_name=self.sconf["session_name"] + session_name=self.session_config["session_name"] ) assert session is not None self._session = session @@ -232,18 +232,18 @@ def build(self, session: t.Optional[Session] = None, append: bool = False) -> No ) else: new_session_kwargs = {} - if "start_directory" in self.sconf: - new_session_kwargs["start_directory"] = self.sconf[ + if "start_directory" in self.session_config: + new_session_kwargs["start_directory"] = self.session_config[ "start_directory" ] session = self.server.new_session( - session_name=self.sconf["session_name"], + session_name=self.session_config["session_name"], **new_session_kwargs, ) assert session is not None - assert self.sconf["session_name"] == session.name - assert len(self.sconf["session_name"]) > 0 + assert self.session_config["session_name"] == session.name + assert len(self.session_config["session_name"]) > 0 assert session is not None assert session.name is not None @@ -264,52 +264,54 @@ def build(self, session: t.Optional[Session] = None, append: bool = False) -> No focus = None - if "before_script" in self.sconf: + if "before_script" in self.session_config: try: cwd = None # we want to run the before_script file cwd'd from the # session start directory, if it exists. - if "start_directory" in self.sconf: - cwd = self.sconf["start_directory"] - run_before_script(self.sconf["before_script"], cwd=cwd) + if "start_directory" in self.session_config: + cwd = self.session_config["start_directory"] + run_before_script(self.session_config["before_script"], cwd=cwd) except Exception as e: self.session.kill_session() raise e - if "options" in self.sconf: - for option, value in self.sconf["options"].items(): + if "options" in self.session_config: + for option, value in self.session_config["options"].items(): self.session.set_option(option, value) - if "global_options" in self.sconf: - for option, value in self.sconf["global_options"].items(): + + if "global_options" in self.session_config: + for option, value in self.session_config["global_options"].items(): self.session.set_option(option, value, _global=True) - if "environment" in self.sconf: - for option, value in self.sconf["environment"].items(): + + if "environment" in self.session_config: + for option, value in self.session_config["environment"].items(): self.session.set_environment(option, value) - for w, wconf in self.iter_create_windows(session, append): - assert isinstance(w, Window) + for window, window_config in self.iter_create_windows(session, append): + assert isinstance(window, Window) for plugin in self.plugins: - plugin.on_window_create(w) + plugin.on_window_create(window) focus_pane = None - for p, pconf in self.iter_create_panes(w, wconf): - assert isinstance(p, Pane) - p = p + for pane, pane_config in self.iter_create_panes(window, window_config): + assert isinstance(pane, Pane) + pane = pane - if "layout" in wconf: - w.select_layout(wconf["layout"]) + if "layout" in window_config: + window.select_layout(window_config["layout"]) - if "focus" in pconf and pconf["focus"]: - focus_pane = p + if "focus" in pane_config and pane_config["focus"]: + focus_pane = pane - if "focus" in wconf and wconf["focus"]: - focus = w + if "focus" in window_config and window_config["focus"]: + focus = window - self.config_after_window(w, wconf) + self.config_after_window(window, window_config) for plugin in self.plugins: - plugin.after_window_finished(w) + plugin.after_window_finished(window) if focus_pane: focus_pane.select_pane() @@ -323,7 +325,7 @@ def iter_create_windows( """Return :class:`libtmux.Window` iterating through session config dict. Generator yielding :class:`libtmux.Window` by iterating through - ``sconf['windows']``. + ``session_config['windows']``. Applies ``window_options`` to window. @@ -336,51 +338,55 @@ def iter_create_windows( Returns ------- - tuple of (:class:`libtmux.Window`, ``wconf``) + tuple of (:class:`libtmux.Window`, ``window_config``) Newly created window, and the section from the tmuxp configuration that was used to create the window. """ - for i, wconf in enumerate(self.sconf["windows"], start=1): - if "window_name" not in wconf: + for window_iterator, window_config in enumerate( + self.session_config["windows"], start=1 + ): + if "window_name" not in window_config: window_name = None else: - window_name = wconf["window_name"] + window_name = window_config["window_name"] - is_first_window_pass = self.first_window_pass(i, session, append) + is_first_window_pass = self.first_window_pass( + window_iterator, session, append + ) w1 = None if is_first_window_pass: # if first window, use window 1 w1 = session.attached_window w1.move_window("99") - if "start_directory" in wconf: - sd = wconf["start_directory"] + if "start_directory" in window_config: + start_directory = window_config["start_directory"] else: - sd = None + start_directory = None # If the first pane specifies a start_directory, use that instead. - panes = wconf["panes"] + panes = window_config["panes"] if panes and "start_directory" in panes[0]: - sd = panes[0]["start_directory"] + start_directory = panes[0]["start_directory"] - if "window_shell" in wconf: - ws = wconf["window_shell"] + if "window_shell" in window_config: + window_shell = window_config["window_shell"] else: - ws = None + window_shell = None # If the first pane specifies a shell, use that instead. try: - if wconf["panes"][0]["shell"] != "": - ws = wconf["panes"][0]["shell"] + if window_config["panes"][0]["shell"] != "": + window_shell = window_config["panes"][0]["shell"] except (KeyError, IndexError): pass - environment = panes[0].get("environment", wconf.get("environment")) + environment = panes[0].get("environment", window_config.get("environment")) if environment and has_lt_version("3.0"): # Falling back to use the environment of the first pane for the window # creation is nice but yields misleading error messages. pane_env = panes[0].get("environment") - win_env = wconf.get("environment") + win_env = window_config.get("environment") if pane_env and win_env: target = "panes and windows" elif pane_env: @@ -393,30 +399,32 @@ def iter_create_windows( ) environment = None - w = session.new_window( + window = session.new_window( window_name=window_name, - start_directory=sd, + start_directory=start_directory, attach=False, # do not move to the new window - window_index=wconf.get("window_index", ""), - window_shell=ws, + window_index=window_config.get("window_index", ""), + window_shell=window_shell, environment=environment, ) if is_first_window_pass: # if first window, use window 1 session.attached_window.kill_window() - assert isinstance(w, Window) - if "options" in wconf and isinstance(wconf["options"], dict): - for key, val in wconf["options"].items(): - w.set_window_option(key, val) + assert isinstance(window, Window) + if "options" in window_config and isinstance( + window_config["options"], dict + ): + for key, val in window_config["options"].items(): + window.set_window_option(key, val) - if "focus" in wconf and wconf["focus"]: - w.select_window() + if "focus" in window_config and window_config["focus"]: + window.select_window() - yield w, wconf + yield window, window_config def iter_create_panes( - self, w: Window, wconf: t.Dict[str, t.Any] + self, window: Window, window_config: t.Dict[str, t.Any] ) -> t.Iterator[t.Any]: """Return :class:`libtmux.Pane` iterating through window config dict. @@ -424,83 +432,88 @@ def iter_create_panes( Parameters ---------- - w : :class:`libtmux.Window` + window : :class:`libtmux.Window` window to create panes for - wconf : dict + window_config : dict config section for window Returns ------- - tuple of (:class:`libtmux.Pane`, ``pconf``) + tuple of (:class:`libtmux.Pane`, ``pane_config``) Newly created pane, and the section from the tmuxp configuration that was used to create the pane. """ - assert isinstance(w, Window) + assert isinstance(window, Window) - pane_base_index_str = w.show_window_option("pane-base-index", g=True) + pane_base_index_str = window.show_window_option("pane-base-index", g=True) assert pane_base_index_str is not None pane_base_index = int(pane_base_index_str) - p = None + pane = None - for pindex, pconf in enumerate(wconf["panes"], start=pane_base_index): - if pindex == int(pane_base_index): - p = w.attached_pane + for pane_index, pane_config in enumerate( + window_config["panes"], start=pane_base_index + ): + if pane_index == int(pane_base_index): + pane = window.attached_pane else: def get_pane_start_directory(): - if "start_directory" in pconf: - return pconf["start_directory"] - elif "start_directory" in wconf: - return wconf["start_directory"] + if "start_directory" in pane_config: + return pane_config["start_directory"] + elif "start_directory" in window_config: + return window_config["start_directory"] else: return None def get_pane_shell(): - if "shell" in pconf: - return pconf["shell"] - elif "window_shell" in wconf: - return wconf["window_shell"] + if "shell" in pane_config: + return pane_config["shell"] + elif "window_shell" in window_config: + return window_config["window_shell"] else: return None - environment = pconf.get("environment", wconf.get("environment")) + environment = pane_config.get( + "environment", window_config.get("environment") + ) if environment and has_lt_version("3.0"): # Just issue a warning when the environment comes from the pane # configuration as a warning for the window was already issued when # the window was created. - if pconf.get("environment"): + if pane_config.get("environment"): logger.warning( "Cannot set environment for new panes. " "You need tmux 3.0 or newer for this." ) environment = None - assert p is not None + assert pane is not None - p = w.split_window( + pane = window.split_window( attach=True, start_directory=get_pane_start_directory(), shell=get_pane_shell(), - target=p.id, + target=pane.id, environment=environment, ) - assert isinstance(p, Pane) - if "layout" in wconf: - w.select_layout(wconf["layout"]) + assert isinstance(pane, Pane) + + if "layout" in window_config: + window.select_layout(window_config["layout"]) - if "suppress_history" in pconf: - suppress = pconf["suppress_history"] - elif "suppress_history" in wconf: - suppress = wconf["suppress_history"] + if "suppress_history" in pane_config: + suppress = pane_config["suppress_history"] + elif "suppress_history" in window_config: + suppress = window_config["suppress_history"] else: suppress = True - enter = pconf.get("enter", True) - sleep_before = pconf.get("sleep_before", None) - sleep_after = pconf.get("sleep_after", None) - for cmd in pconf["shell_command"]: + enter = pane_config.get("enter", True) + sleep_before = pane_config.get("sleep_before", None) + sleep_after = pane_config.get("sleep_after", None) + for cmd in pane_config["shell_command"]: enter = cmd.get("enter", enter) sleep_before = cmd.get("sleep_before", sleep_before) sleep_after = cmd.get("sleep_after", sleep_after) @@ -508,18 +521,20 @@ def get_pane_shell(): if sleep_before is not None: time.sleep(sleep_before) - p.send_keys(cmd["cmd"], suppress_history=suppress, enter=enter) + pane.send_keys(cmd["cmd"], suppress_history=suppress, enter=enter) if sleep_after is not None: time.sleep(sleep_after) - if "focus" in pconf and pconf["focus"]: - assert p.pane_id is not None - w.select_pane(p.pane_id) + if "focus" in pane_config and pane_config["focus"]: + assert pane.pane_id is not None + window.select_pane(pane.pane_id) - yield p, pconf + yield pane, pane_config - def config_after_window(self, w: Window, wconf: t.Dict[str, t.Any]) -> None: + def config_after_window( + self, window: Window, window_config: t.Dict[str, t.Any] + ) -> None: """Actions to apply to window after window and pane finished. When building a tmux session, sometimes its easier to postpone things @@ -528,14 +543,16 @@ def config_after_window(self, w: Window, wconf: t.Dict[str, t.Any]) -> None: Parameters ---------- - w : :class:`libtmux.Window` + window : :class:`libtmux.Window` window to create panes for - wconf : dict + window_config : dict config section for window """ - if "options_after" in wconf and isinstance(wconf["options_after"], dict): - for key, val in wconf["options_after"].items(): - w.set_window_option(key, val) + if "options_after" in window_config and isinstance( + window_config["options_after"], dict + ): + for key, val in window_config["options_after"].items(): + window.set_window_option(key, val) def find_current_attached_session(self) -> Session: assert self.server is not None diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py index 87c76fce6bf..1358491f29d 100644 --- a/tests/cli/test_cli.py +++ b/tests/cli/test_cli.py @@ -119,12 +119,14 @@ def test_reattach_plugins( ) -> None: config_plugins = test_utils.read_workspace_file("workspace/builder/plugin_r.yaml") - sconfig = ConfigReader._load(format="yaml", content=config_plugins) - sconfig = loader.expand(sconfig) + session_configig = ConfigReader._load(format="yaml", content=config_plugins) + session_configig = loader.expand(session_configig) # open it detached builder = WorkspaceBuilder( - sconf=sconfig, plugins=load_plugins(sconfig), server=server + session_config=session_configig, + plugins=load_plugins(session_configig), + server=server, ) builder.build() diff --git a/tests/cli/test_load.py b/tests/cli/test_load.py index 5c65ff16a40..6a42ce1ef75 100644 --- a/tests/cli/test_load.py +++ b/tests/cli/test_load.py @@ -430,10 +430,10 @@ def test_load_plugins(monkeypatch_plugin_test_packages: None) -> None: plugins_config = test_utils.read_workspace_file("workspace/builder/plugin_bwb.yaml") - sconfig = ConfigReader._load(format="yaml", content=plugins_config) - sconfig = loader.expand(sconfig) + session_config = ConfigReader._load(format="yaml", content=plugins_config) + session_config = loader.expand(session_config) - plugins = load_plugins(sconfig) + plugins = load_plugins(session_config) assert len(plugins) == 1 @@ -541,9 +541,9 @@ def test_load_attached( attach_session_mock.return_value.stderr = None yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") - sconfig = ConfigReader._load(format="yaml", content=yaml_config) + session_config = ConfigReader._load(format="yaml", content=yaml_config) - builder = WorkspaceBuilder(sconf=sconfig, server=server) + builder = WorkspaceBuilder(session_config=session_config, server=server) _load_attached(builder, False) @@ -560,9 +560,9 @@ def test_load_attached_detached( attach_session_mock.return_value.stderr = None yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") - sconfig = ConfigReader._load(format="yaml", content=yaml_config) + session_config = ConfigReader._load(format="yaml", content=yaml_config) - builder = WorkspaceBuilder(sconf=sconfig, server=server) + builder = WorkspaceBuilder(session_config=session_config, server=server) _load_attached(builder, True) @@ -579,9 +579,9 @@ def test_load_attached_within_tmux( switch_client_mock.return_value.stderr = None yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") - sconfig = ConfigReader._load(format="yaml", content=yaml_config) + session_config = ConfigReader._load(format="yaml", content=yaml_config) - builder = WorkspaceBuilder(sconf=sconfig, server=server) + builder = WorkspaceBuilder(session_config=session_config, server=server) _load_attached(builder, False) @@ -598,9 +598,9 @@ def test_load_attached_within_tmux_detached( switch_client_mock.return_value.stderr = None yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") - sconfig = ConfigReader._load(format="yaml", content=yaml_config) + session_config = ConfigReader._load(format="yaml", content=yaml_config) - builder = WorkspaceBuilder(sconf=sconfig, server=server) + builder = WorkspaceBuilder(session_config=session_config, server=server) _load_attached(builder, True) @@ -611,9 +611,9 @@ def test_load_append_windows_to_current_session( server: "Server", monkeypatch: pytest.MonkeyPatch ) -> None: yaml_config = test_utils.read_workspace_file("workspace/builder/two_pane.yaml") - sconfig = ConfigReader._load(format="yaml", content=yaml_config) + session_config = ConfigReader._load(format="yaml", content=yaml_config) - builder = WorkspaceBuilder(sconf=sconfig, server=server) + builder = WorkspaceBuilder(session_config=session_config, server=server) builder.build() assert len(server.sessions) == 1 @@ -623,7 +623,7 @@ def test_load_append_windows_to_current_session( assert server.panes[0].pane_id monkeypatch.setenv("TMUX_PANE", server.panes[0].pane_id) - builder = WorkspaceBuilder(sconf=sconfig, server=server) + builder = WorkspaceBuilder(session_config=session_config, server=server) _load_append_windows_to_current_session(builder) assert len(server.sessions) == 1 diff --git a/tests/workspace/test_builder.py b/tests/workspace/test_builder.py index 2100a461b37..cd1a0d832ba 100644 --- a/tests/workspace/test_builder.py +++ b/tests/workspace/test_builder.py @@ -31,7 +31,7 @@ def test_split_windows(session): test_utils.get_workspace_file("workspace/builder/two_pane.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) window_count = len(session.windows) # current window count assert len(session.windows) == window_count @@ -51,7 +51,7 @@ def test_split_windows_three_pane(session): test_utils.get_workspace_file("workspace/builder/three_pane.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) window_count = len(session.windows) # current window count assert len(session.windows) == window_count @@ -75,7 +75,7 @@ def test_focus_pane_index(session): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) @@ -154,7 +154,7 @@ def test_suppress_history(session): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) inHistoryWindow = session.windows.get(window_name="inHistory") @@ -207,7 +207,7 @@ def test_session_options(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) _default_shell = session.show_option("default-shell") @@ -225,7 +225,7 @@ def test_global_options(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) _status_position = session.show_option("status-position", _global=True) @@ -247,7 +247,7 @@ def test_global_session_env_options(session, monkeypatch): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) _visual_silence = session.show_option("visual-silence", _global=True) @@ -268,7 +268,7 @@ def test_window_options(session): if has_gte_version("2.3"): workspace["windows"][0]["options"]["pane-border-format"] = " #P " - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) window_count = len(session.windows) # current window count assert len(session.windows) == window_count @@ -294,7 +294,7 @@ def test_window_options_after(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) def assert_last_line(p, s): @@ -332,7 +332,7 @@ def test_window_shell(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) for w, wconf in builder.iter_create_windows(session): if "window_shell" in wconf: @@ -356,7 +356,7 @@ def test_environment_variables(session): ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session) # Give slow shells some time to settle as otherwise tests might fail. time.sleep(0.3) @@ -398,7 +398,7 @@ def test_environment_variables_logs(session: Session, caplog: pytest.LogCaptureF ) workspace = loader.expand(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session) # environment on sessions should work as this is done using set-environment @@ -450,7 +450,7 @@ def test_automatic_rename_option( if " " in portable_command: portable_command = portable_command.split(" ")[0] - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() assert builder.session is not None session: Session = builder.session @@ -486,7 +486,7 @@ def test_blank_pane_count(session): test_config = ConfigReader._from_file(yaml_workspace_file) test_config = loader.expand(test_config) - builder = WorkspaceBuilder(sconf=test_config, server=session.server) + builder = WorkspaceBuilder(session_config=test_config, server=session.server) builder.build(session=session) assert session == builder.session @@ -521,7 +521,7 @@ def test_start_directory(session, tmp_path: pathlib.Path): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) assert session == builder.session @@ -569,7 +569,7 @@ def test_start_directory_relative(session, tmp_path: pathlib.Path): assert os.path.exists(config_dir) assert os.path.exists(test_dir) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) assert session == builder.session @@ -599,7 +599,7 @@ def test_start_directory_sets_session_path(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() session = builder.session @@ -630,7 +630,7 @@ def test_pane_order(session): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) window_count = len(session.windows) # current window count assert len(session.windows) == window_count @@ -671,7 +671,7 @@ def test_window_index(session): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) for window, _ in builder.iter_create_windows(session): expected_index = name_index_map[window.window_name] @@ -690,7 +690,7 @@ def test_before_load_throw_error_if_retcode_error(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) with temp_session(server) as sess: session_name = sess.name @@ -713,7 +713,7 @@ def test_before_load_throw_error_if_file_not_exists(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) with temp_session(server) as session: session_name = session.name @@ -738,7 +738,7 @@ def test_before_load_true_if_test_passes(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) with temp_session(server) as session: builder.build(session=session) @@ -757,7 +757,7 @@ def test_before_load_true_if_test_passes_with_args(server): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) with temp_session(server) as session: builder.build(session=session) @@ -772,7 +772,7 @@ def test_plugin_system_before_workspace_builder( workspace = loader.expand(workspace) builder = WorkspaceBuilder( - sconf=workspace, plugins=load_plugins(workspace), server=session.server + session_config=workspace, plugins=load_plugins(workspace), server=session.server ) assert len(builder.plugins) > 0 @@ -789,7 +789,7 @@ def test_plugin_system_on_window_create(monkeypatch_plugin_test_packages, sessio workspace = loader.expand(workspace) builder = WorkspaceBuilder( - sconf=workspace, plugins=load_plugins(workspace), server=session.server + session_config=workspace, plugins=load_plugins(workspace), server=session.server ) assert len(builder.plugins) > 0 @@ -806,7 +806,7 @@ def test_plugin_system_after_window_finished(monkeypatch_plugin_test_packages, s workspace = loader.expand(workspace) builder = WorkspaceBuilder( - sconf=workspace, plugins=load_plugins(workspace), server=session.server + session_config=workspace, plugins=load_plugins(workspace), server=session.server ) assert len(builder.plugins) > 0 @@ -825,7 +825,7 @@ def test_plugin_system_on_window_create_multiple_windows(session): workspace = loader.expand(workspace) builder = WorkspaceBuilder( - sconf=workspace, plugins=load_plugins(workspace), server=session.server + session_config=workspace, plugins=load_plugins(workspace), server=session.server ) assert len(builder.plugins) > 0 @@ -847,7 +847,7 @@ def test_plugin_system_after_window_finished_multiple_windows( workspace = loader.expand(workspace) builder = WorkspaceBuilder( - sconf=workspace, plugins=load_plugins(workspace), server=session.server + session_config=workspace, plugins=load_plugins(workspace), server=session.server ) assert len(builder.plugins) > 0 @@ -867,7 +867,7 @@ def test_plugin_system_multiple_plugins(monkeypatch_plugin_test_packages, sessio workspace = loader.expand(workspace) builder = WorkspaceBuilder( - sconf=workspace, plugins=load_plugins(workspace), server=session.server + session_config=workspace, plugins=load_plugins(workspace), server=session.server ) assert len(builder.plugins) > 0 @@ -889,7 +889,7 @@ def test_load_configs_same_session(server): path=test_utils.get_workspace_file("workspace/builder/three_windows.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() assert len(server.sessions) == 1 @@ -899,7 +899,7 @@ def test_load_configs_same_session(server): path=test_utils.get_workspace_file("workspace/builder/two_windows.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() assert len(server.sessions) == 2 assert len(server.sessions[1].windows) == 2 @@ -908,7 +908,7 @@ def test_load_configs_same_session(server): path=test_utils.get_workspace_file("workspace/builder/two_windows.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build(server.sessions[1], True) assert len(server.sessions) == 2 @@ -920,7 +920,7 @@ def test_load_configs_separate_sessions(server): path=test_utils.get_workspace_file("workspace/builder/three_windows.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() assert len(server.sessions) == 1 @@ -930,7 +930,7 @@ def test_load_configs_separate_sessions(server): path=test_utils.get_workspace_file("workspace/builder/two_windows.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() assert len(server.sessions) == 2 @@ -943,14 +943,14 @@ def test_find_current_active_pane(server, monkeypatch): path=test_utils.get_workspace_file("workspace/builder/three_windows.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() workspace = ConfigReader._from_file( path=test_utils.get_workspace_file("workspace/builder/two_windows.yaml") ) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() assert len(server.sessions) == 2 @@ -960,7 +960,7 @@ def test_find_current_active_pane(server, monkeypatch): first_pane_on_second_session_id = second_session.windows[0].panes[0].pane_id monkeypatch.setenv("TMUX_PANE", first_pane_on_second_session_id) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) assert builder.find_current_attached_session() == second_session @@ -1099,7 +1099,7 @@ def test_load_workspace_enter( workspace = ConfigReader._from_file(yaml_workspace) workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) builder.build() session = builder.session @@ -1221,7 +1221,7 @@ def test_load_workspace_sleep( workspace = ConfigReader._from_file(yaml_workspace) workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) start_time = time.process_time() @@ -1257,7 +1257,7 @@ def test_first_pane_start_directory(session, tmp_path: pathlib.Path): workspace = loader.expand(workspace) workspace = loader.trickle(workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) assert session == builder.session @@ -1282,7 +1282,7 @@ def test_layout_main_horizontal(session): yaml_workspace = test_utils.get_workspace_file("workspace/builder/three_pane.yaml") workspace = ConfigReader._from_file(path=yaml_workspace) - builder = WorkspaceBuilder(sconf=workspace, server=session.server) + builder = WorkspaceBuilder(session_config=workspace, server=session.server) builder.build(session=session) assert session.windows @@ -1380,7 +1380,7 @@ def test_issue_800_default_size_many_windows( if TMUXP_DEFAULT_SIZE is not None: monkeypatch.setenv("TMUXP_DEFAULT_SIZE", TMUXP_DEFAULT_SIZE) - builder = WorkspaceBuilder(sconf=workspace, server=server) + builder = WorkspaceBuilder(session_config=workspace, server=server) if raises: with pytest.raises(Exception): diff --git a/tests/workspace/test_freezer.py b/tests/workspace/test_freezer.py index 2c41aee1c82..4848f1e3f11 100644 --- a/tests/workspace/test_freezer.py +++ b/tests/workspace/test_freezer.py @@ -18,7 +18,7 @@ def test_freeze_config(session): test_utils.get_workspace_file("workspace/freezer/sample_workspace.yaml") ) - builder = WorkspaceBuilder(sconf=session_config, server=session.server) + builder = WorkspaceBuilder(session_config=session_config, server=session.server) builder.build(session=session) assert session == builder.session From 6870528b607299873487313e6a8aebf0a471b057 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 29 Dec 2022 15:51:27 -0600 Subject: [PATCH 7/8] build(deps): Typings for Freezer --- src/tmuxp/workspace/freezer.py | 73 ++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/src/tmuxp/workspace/freezer.py b/src/tmuxp/workspace/freezer.py index aed0bf44389..b9b4ab9ddb3 100644 --- a/src/tmuxp/workspace/freezer.py +++ b/src/tmuxp/workspace/freezer.py @@ -1,3 +1,8 @@ +from libtmux.pane import Pane +from libtmux.session import Session +import typing as t + + def inline(workspace_dict): """Return workspace with inlined shorthands. Opposite of :meth:`loader.expand`. @@ -40,7 +45,7 @@ def inline(workspace_dict): return workspace_dict -def freeze(session): +def freeze(session: Session) -> t.Dict[str, t.Any]: """Freeze live tmux session into a tmuxp workspacee. Parameters @@ -53,53 +58,61 @@ def freeze(session): dict tmuxp compatible workspace """ - sconf = {"session_name": session.session_name, "windows": []} - - for w in session.windows: - wconf = { - "options": w.show_window_options(), - "window_name": w.name, - "layout": w.window_layout, + session_config: t.Dict[str, t.Any] = { + "session_name": session.session_name, + "windows": [], + } + + for window in session.windows: + window_config: t.Dict[str, t.Any] = { + "options": window.show_window_options(), + "window_name": window.name, + "layout": window.window_layout, "panes": [], } - if getattr(w, "window_active", "0") == "1": - wconf["focus"] = "true" + + if getattr(window, "window_active", "0") == "1": + window_config["focus"] = "true" # If all panes have same path, set 'start_directory' instead # of using 'cd' shell commands. - def pane_has_same_path(p): - return w.panes[0].pane_current_path == p.pane_current_path + def pane_has_same_path(pane: Pane) -> bool: + return window.panes[0].pane_current_path == pane.pane_current_path - if all(pane_has_same_path(p) for p in w.panes): - wconf["start_directory"] = w.panes[0].pane_current_path + if all(pane_has_same_path(pane=pane) for pane in window.panes): + window_config["start_directory"] = window.panes[0].pane_current_path - for p in w.panes: - pconf = {"shell_command": []} + for pane in window.panes: + pane_config: t.Union[str, t.Dict[str, t.Any]] = {"shell_command": []} + assert isinstance(pane_config, dict) - if "start_directory" not in wconf: - pconf["shell_command"].append("cd " + p.pane_current_path) + if "start_directory" not in window_config and pane.pane_current_path: + pane_config["shell_command"].append("cd " + pane.pane_current_path) - if getattr(p, "pane_active", "0") == "1": - pconf["focus"] = "true" + if getattr(pane, "pane_active", "0") == "1": + pane_config["focus"] = "true" - current_cmd = p.pane_current_command + current_cmd = pane.pane_current_command - def filter_interpretters_and_shells(): - return current_cmd.startswith("-") or any( - current_cmd.endswith(cmd) for cmd in ["python", "ruby", "node"] + def filter_interpretters_and_shells() -> bool: + return current_cmd is not None and ( + current_cmd.startswith("-") + or any( + current_cmd.endswith(cmd) for cmd in ["python", "ruby", "node"] + ) ) if filter_interpretters_and_shells(): current_cmd = None if current_cmd: - pconf["shell_command"].append(current_cmd) + pane_config["shell_command"].append(current_cmd) else: - if not len(pconf["shell_command"]): - pconf = "pane" + if not len(pane_config["shell_command"]): + pane_config = "pane" - wconf["panes"].append(pconf) + window_config["panes"].append(pane_config) - sconf["windows"].append(wconf) + session_config["windows"].append(window_config) - return sconf + return session_config From 5cbafd679be759205370625ebdd37c8ed6733c13 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Thu, 29 Dec 2022 17:34:37 -0600 Subject: [PATCH 8/8] chore(WorkspaceBulder): Newlines, move assertion --- src/tmuxp/workspace/builder.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tmuxp/workspace/builder.py b/src/tmuxp/workspace/builder.py index 366e828cf5f..1af6026d6d8 100644 --- a/src/tmuxp/workspace/builder.py +++ b/src/tmuxp/workspace/builder.py @@ -276,6 +276,7 @@ def build(self, session: t.Optional[Session] = None, append: bool = False) -> No except Exception as e: self.session.kill_session() raise e + if "options" in self.session_config: for option, value in self.session_config["options"].items(): self.session.set_option(option, value) @@ -407,11 +408,11 @@ def iter_create_windows( window_shell=window_shell, environment=environment, ) + assert isinstance(window, Window) if is_first_window_pass: # if first window, use window 1 session.attached_window.kill_window() - assert isinstance(window, Window) if "options" in window_config and isinstance( window_config["options"], dict ):