コード例 #1
0
def _load_attached(builder, detached):
    """
    Load config in new session

    Parameters
    ----------
    builder: :class:`workspacebuilder.WorkspaceBuilder`
    detached : bool
    """
    builder.build()

    if 'TMUX' in os.environ:  # tmuxp ran from inside tmux
        # unset TMUX, save it, e.g. '/tmp/tmux-1000/default,30668,0'
        tmux_env = os.environ.pop('TMUX')

        if has_gte_version('2.6'):
            set_layout_hook(builder.session, 'client-session-changed')

        builder.session.switch_client()  # switch client to new session

        os.environ['TMUX'] = tmux_env  # set TMUX back again
    else:
        if has_gte_version('2.6'):
            # if attaching for first time
            set_layout_hook(builder.session, 'client-attached')

            # for cases where user switches client for first time
            set_layout_hook(builder.session, 'client-session-changed')

        if not detached:
            builder.session.attach_session()
コード例 #2
0
def test_window_options(session):
    yaml_config = loadfixture("workspacebuilder/window_options.yaml")
    s = session
    sconfig = kaptan.Kaptan(handler='yaml')
    sconfig = sconfig.import_config(yaml_config).get()
    sconfig = config.expand(sconfig)

    if has_gte_version('2.3'):
        sconfig['windows'][0]['options']['pane-border-format'] = ' #P '

    builder = WorkspaceBuilder(sconf=sconfig)

    window_count = len(session._windows)  # current window count
    assert len(s._windows) == window_count
    for w, wconf in builder.iter_create_windows(s):
        for p in builder.iter_create_panes(w, wconf):
            w.select_layout('tiled')  # fix glitch with pane size
            p = p
            assert len(s._windows) == window_count
        assert isinstance(w, Window)
        assert w.show_window_option('main-pane-height') == 5
        if has_gte_version('2.3'):
            assert w.show_window_option('pane-border-format') == ' #P '

        assert len(s._windows) == window_count
        window_count += 1
        w.select_layout(wconf['layout'])
コード例 #3
0
def test_window_options(session):
    yaml_config = test_utils.read_config_file(
        "workspacebuilder/window_options.yaml")
    s = session
    sconfig = kaptan.Kaptan(handler="yaml")
    sconfig = sconfig.import_config(yaml_config).get()
    sconfig = config.expand(sconfig)

    if has_gte_version("2.3"):
        sconfig["windows"][0]["options"]["pane-border-format"] = " #P "

    builder = WorkspaceBuilder(sconf=sconfig)

    window_count = len(session._windows)  # current window count
    assert len(s._windows) == window_count
    for w, wconf in builder.iter_create_windows(s):
        for p in builder.iter_create_panes(w, wconf):
            w.select_layout("tiled")  # fix glitch with pane size
            p = p
            assert len(s._windows) == window_count
        assert isinstance(w, Window)
        assert w.show_window_option("main-pane-height") == 5
        if has_gte_version("2.3"):
            assert w.show_window_option("pane-border-format") == " #P "

        assert len(s._windows) == window_count
        window_count += 1
        w.select_layout(wconf["layout"])
コード例 #4
0
ファイル: test_common.py プロジェクト: tony/libtmux
def test_has_gte_version():
    assert has_gte_version('1.6')
    assert has_gte_version('1.6b')
    assert has_gte_version(str(get_version()))

    assert not has_gte_version('4.0')
    assert not has_gte_version('4.0b')
コード例 #5
0
def test_window_options(session):
    yaml_config = loadfixture("workspacebuilder/window_options.yaml")
    s = session
    sconfig = kaptan.Kaptan(handler='yaml')
    sconfig = sconfig.import_config(yaml_config).get()
    sconfig = config.expand(sconfig)

    if has_gte_version('2.3'):
        sconfig['windows'][0]['options']['pane-border-format'] = ' #P '

    builder = WorkspaceBuilder(sconf=sconfig)

    window_count = len(session._windows)  # current window count
    assert len(s._windows) == window_count
    for w, wconf in builder.iter_create_windows(s):
        for p in builder.iter_create_panes(w, wconf):
            w.select_layout('tiled')  # fix glitch with pane size
            p = p
            assert len(s._windows) == window_count
        assert isinstance(w, Window)
        assert w.show_window_option('main-pane-height') == 5
        if has_gte_version('2.3'):
            assert w.show_window_option('pane-border-format') == ' #P '

        assert len(s._windows) == window_count
        window_count += 1
        w.select_layout(wconf['layout'])
コード例 #6
0
def test_has_gte_version():
    assert has_gte_version('1.6')
    assert has_gte_version('1.6b')
    assert has_gte_version(str(get_version()))

    assert not has_gte_version('4.0')
    assert not has_gte_version('4.0b')
コード例 #7
0
ファイル: test_common.py プロジェクト: tmux-python/libtmux
def test_has_gte_version():
    assert has_gte_version("1.6")
    assert has_gte_version("1.6b")
    assert has_gte_version(str(get_version()))

    assert not has_gte_version("4.0")
    assert not has_gte_version("4.0b")
コード例 #8
0
def test_show_option_unknown(session):
    """Session.show_option raises UnknownOption for invalid option."""
    cmd_exception = exc.UnknownOption
    if has_gte_version('3.0'):
        cmd_exception = exc.InvalidOption
    with pytest.raises(cmd_exception):
        session.show_option('moooz')
コード例 #9
0
ファイル: test_session.py プロジェクト: sampathP/libtmux
def test_has_session(server, session):
    """Server.has_session returns True if has session_name exists."""
    TEST_SESSION_NAME = session.get('session_name')
    assert server.has_session(TEST_SESSION_NAME)
    if has_gte_version('2.1'):
        assert not server.has_session(TEST_SESSION_NAME[:-2])
        assert server.has_session(TEST_SESSION_NAME[:-2], exact=False)
    assert not server.has_session('asdf2314324321')
コード例 #10
0
ファイル: test_session.py プロジェクト: sampathP/libtmux
def test_set_option_invalid(session):
    """Session.set_option raises UnknownOption for invalid option."""
    if has_gte_version('2.4'):
        with pytest.raises(exc.InvalidOption):
            session.set_option('afewewfew', 43)
    else:
        with pytest.raises(exc.UnknownOption):
            session.set_option('afewewfew', 43)
コード例 #11
0
ファイル: test_session.py プロジェクト: tony/libtmux
def test_set_option_invalid(session):
    """Session.set_option raises UnknownOption for invalid option."""
    if has_gte_version('2.4'):
        with pytest.raises(exc.InvalidOption):
            session.set_option('afewewfew', 43)
    else:
        with pytest.raises(exc.UnknownOption):
            session.set_option('afewewfew', 43)
コード例 #12
0
ファイル: test_session.py プロジェクト: tony/libtmux
def test_has_session(server, session):
    """Server.has_session returns True if has session_name exists."""
    TEST_SESSION_NAME = session.get('session_name')
    assert server.has_session(TEST_SESSION_NAME)
    if has_gte_version('2.1'):
        assert not server.has_session(TEST_SESSION_NAME[:-2])
        assert server.has_session(TEST_SESSION_NAME[:-2], exact=False)
    assert not server.has_session('asdf2314324321')
コード例 #13
0
ファイル: test_window.py プロジェクト: tmux-python/libtmux
def test_show_window_option_unknown(session):
    """Window.show_window_option raises UnknownOption for bad option key."""
    window = session.new_window(window_name="test_window")

    cmd_exception = exc.UnknownOption
    if has_gte_version("3.0"):
        cmd_exception = exc.InvalidOption
    with pytest.raises(cmd_exception):
        window.show_window_option("moooz")
コード例 #14
0
ファイル: test_window.py プロジェクト: tony/libtmux
def test_set_window_option_invalid(session):
    """Window.set_window_option raises ValueError for invalid option key."""

    window = session.new_window(window_name='test_window')

    if has_gte_version('2.4'):
        with pytest.raises(exc.InvalidOption):
            window.set_window_option('afewewfew', 43)
    else:
        with pytest.raises(exc.UnknownOption):
            window.set_window_option('afewewfew', 43)
コード例 #15
0
ファイル: test_window.py プロジェクト: tmux-python/libtmux
def test_set_window_option_invalid(session):
    """Window.set_window_option raises ValueError for invalid option key."""

    window = session.new_window(window_name="test_window")

    if has_gte_version("2.4"):
        with pytest.raises(exc.InvalidOption):
            window.set_window_option("afewewfew", 43)
    else:
        with pytest.raises(exc.UnknownOption):
            window.set_window_option("afewewfew", 43)
コード例 #16
0
def _load_append_windows_to_current_session(builder):
    """
    Load config as new windows in current session

    Parameters
    ----------
    builder: :class:`workspacebuilder.WorkspaceBuilder`
    """
    current_attached_session = builder.find_current_attached_session()
    builder.build(current_attached_session, append=True)
    if has_gte_version('2.6'):  # prepare for both cases
        set_layout_hook(builder.session, 'client-attached')
        set_layout_hook(builder.session, 'client-session-changed')
コード例 #17
0
ファイル: test_window.py プロジェクト: tmux-python/libtmux
def test_split_window_shell(session):
    """Window.split_window() splits window, returns new Pane, vertical."""
    window_name = "test split window"
    cmd = "sleep 1m"
    window = session.new_window(window_name=window_name, attach=True)
    pane = window.split_window(shell=cmd)
    assert len(window.panes) == 2
    assert isinstance(pane, Pane)
    assert float(window.panes[0].height) <= ((float(window.width) + 1) / 2)
    if has_gte_version("3.2"):
        assert pane.get("pane_start_command").replace('"', "") == cmd
    else:
        assert pane.get("pane_start_command") == cmd
コード例 #18
0
ファイル: test_common.py プロジェクト: wing8279/libtmux
def test_allows_next_version(monkeypatch):
    def mock_tmux_cmd(param):
        class Hi(object):
            stdout = ['tmux next-2.9']
            stderr = None

        return Hi()

    monkeypatch.setattr(libtmux.common, 'tmux_cmd', mock_tmux_cmd)

    assert has_minimum_version()
    assert has_gte_version(TMUX_MIN_VERSION)
    assert has_gt_version(TMUX_MAX_VERSION), "Greater than the max-supported version"
    assert '2.9' == get_version()
コード例 #19
0
ファイル: test_window.py プロジェクト: sampathP/libtmux
def test_set_show_window_options(session):
    """Set option then Window.show_window_options(key)."""
    window = session.new_window(window_name='test_window')

    window.set_window_option('main-pane-height', 20)
    assert window.show_window_options('main-pane-height') == 20

    window.set_window_option('main-pane-height', 40)
    assert window.show_window_options('main-pane-height') == 40
    assert window.show_window_options()['main-pane-height'] == 40

    if has_gte_version('2.3'):
        window.set_window_option('pane-border-format', ' #P ')
        assert window.show_window_options('pane-border-format') == ' #P '
コード例 #20
0
ファイル: test_server.py プロジェクト: tmux-python/libtmux
def test_new_session_shell(server):
    """Server.new_session creates and returns valid session running with
    specified command"""
    cmd = "sleep 1m"
    mysession = server.new_session("test_new_session", window_command=cmd)
    window = mysession.list_windows()[0]
    pane = window.list_panes()[0]
    assert mysession.get("session_name") == "test_new_session"
    assert server.has_session("test_new_session")

    if has_gte_version("3.2"):
        assert pane.get("pane_start_command").replace('"', "") == cmd
    else:
        assert pane.get("pane_start_command") == cmd
コード例 #21
0
ファイル: test_window.py プロジェクト: tony/libtmux
def test_set_show_window_options(session):
    """Set option then Window.show_window_options(key)."""
    window = session.new_window(window_name='test_window')

    window.set_window_option('main-pane-height', 20)
    assert window.show_window_options('main-pane-height') == 20

    window.set_window_option('main-pane-height', 40)
    assert window.show_window_options('main-pane-height') == 40
    assert window.show_window_options()['main-pane-height'] == 40

    if has_gte_version('2.3'):
        window.set_window_option('pane-border-format', ' #P ')
        assert window.show_window_options('pane-border-format') == ' #P '
コード例 #22
0
ファイル: test_window.py プロジェクト: tmux-python/libtmux
def test_set_show_window_options(session):
    """Set option then Window.show_window_options(key)."""
    window = session.new_window(window_name="test_window")

    window.set_window_option("main-pane-height", 20)
    assert window.show_window_options("main-pane-height") == 20

    window.set_window_option("main-pane-height", 40)
    assert window.show_window_options("main-pane-height") == 40
    assert window.show_window_options()["main-pane-height"] == 40

    if has_gte_version("2.3"):
        window.set_window_option("pane-border-format", " #P ")
        assert window.show_window_options("pane-border-format") == " #P "
コード例 #23
0
def test_get_version_openbsd(monkeypatch):
    def mock_tmux_cmd(param):
        class Hi(object):
            stderr = ['tmux: unknown option -- V']

        return Hi()

    monkeypatch.setattr(libtmux.common, 'tmux_cmd', mock_tmux_cmd)
    monkeypatch.setattr(sys, 'platform', 'openbsd 5.2')
    assert has_minimum_version()
    assert has_gte_version(TMUX_MIN_VERSION)
    assert has_gt_version(TMUX_MAX_VERSION), (
        "Greater than the max-supported version")
    assert '%s-openbsd' % TMUX_MAX_VERSION == get_version(), (
        "Is the latest supported version with -openbsd appended")
コード例 #24
0
ファイル: test_common.py プロジェクト: tmux-python/libtmux
def test_get_version_openbsd(monkeypatch):
    def mock_tmux_cmd(param):
        class Hi:
            stderr = ["tmux: unknown option -- V"]

        return Hi()

    monkeypatch.setattr(libtmux.common, "tmux_cmd", mock_tmux_cmd)
    monkeypatch.setattr(sys, "platform", "openbsd 5.2")
    assert has_minimum_version()
    assert has_gte_version(TMUX_MIN_VERSION)
    assert has_gt_version(
        TMUX_MAX_VERSION), "Greater than the max-supported version"
    assert ("%s-openbsd" % TMUX_MAX_VERSION == get_version()
            ), "Is the latest supported version with -openbsd appended"
コード例 #25
0
ファイル: test_common.py プロジェクト: tony/libtmux
def test_get_version_openbsd(monkeypatch):
    def mock_tmux_cmd(param):
        class Hi(object):
            stderr = ['tmux: unknown option -- V']

        return Hi()

    monkeypatch.setattr(libtmux.common, 'tmux_cmd', mock_tmux_cmd)
    monkeypatch.setattr(sys, 'platform', 'openbsd 5.2')
    assert has_minimum_version()
    assert has_gte_version(TMUX_MIN_VERSION)
    assert has_gt_version(TMUX_MAX_VERSION), "Greater than the max-supported version"
    assert (
        '%s-openbsd' % TMUX_MAX_VERSION == get_version()
    ), "Is the latest supported version with -openbsd appended"
コード例 #26
0
def _load_detached(builder):
    """
    Load config in new session but don't attach

    Parameters
    ----------
    builder: :class:`workspacebuilder.WorkspaceBuilder`
    """
    builder.build()

    if has_gte_version('2.6'):  # prepare for both cases
        set_layout_hook(builder.session, 'client-attached')
        set_layout_hook(builder.session, 'client-session-changed')

    print('Session created in detached state.')
コード例 #27
0
def test_allows_master_version(monkeypatch):
    def mock_tmux_cmd(param):
        class Hi(object):
            stdout = ['tmux master']
            stderr = None

        return Hi()

    monkeypatch.setattr(libtmux.common, 'tmux_cmd', mock_tmux_cmd)

    assert has_minimum_version()
    assert has_gte_version(TMUX_MIN_VERSION)
    assert has_gt_version(TMUX_MAX_VERSION), (
        "Greater than the max-supported version")
    assert '%s-master' % TMUX_MAX_VERSION == get_version(), (
        "Is the latest supported version with -master appended")
コード例 #28
0
ファイル: test_common.py プロジェクト: tmux-python/libtmux
def test_allows_master_version(monkeypatch):
    def mock_tmux_cmd(param):
        class Hi:
            stdout = ["tmux master"]
            stderr = None

        return Hi()

    monkeypatch.setattr(libtmux.common, "tmux_cmd", mock_tmux_cmd)

    assert has_minimum_version()
    assert has_gte_version(TMUX_MIN_VERSION)
    assert has_gt_version(
        TMUX_MAX_VERSION), "Greater than the max-supported version"
    assert ("%s-master" % TMUX_MAX_VERSION == get_version()
            ), "Is the latest supported version with -master appended"
コード例 #29
0
ファイル: test_common.py プロジェクト: tony/libtmux
def test_allows_master_version(monkeypatch):
    def mock_tmux_cmd(param):
        class Hi(object):
            stdout = ['tmux master']
            stderr = None

        return Hi()

    monkeypatch.setattr(libtmux.common, 'tmux_cmd', mock_tmux_cmd)

    assert has_minimum_version()
    assert has_gte_version(TMUX_MIN_VERSION)
    assert has_gt_version(TMUX_MAX_VERSION), "Greater than the max-supported version"
    assert (
        '%s-master' % TMUX_MAX_VERSION == get_version()
    ), "Is the latest supported version with -master appended"
コード例 #30
0
ファイル: cli.py プロジェクト: digitalsatori/tmuxp
def load_workspace(
    config_file,
    socket_name=None,
    socket_path=None,
    colors=None,
    detached=False,
    answer_yes=False,
):
    """
    Load a tmux "workspace" session via tmuxp file.

    Parameters
    ----------
    config_file : str
        absolute path to config file
    socket_name : str, optional
        ``tmux -L <socket-name>``
    socket_path: str, optional
        ``tmux -S <socket-path>``
    colors : str, optional
        '-2'
            Force tmux to support 256 colors
    detached : bool
        Force detached state. default False.
    answer_yes : bool
        Assume yes when given prompt. default False.

    Notes
    -----

    tmuxp will check and load a configuration file. The file will use kaptan
    to load a JSON/YAML into a :py:obj:`dict`. Then :func:`config.expand` and
    :func:`config.trickle` will be used to expand any shorthands, template
    variables, or file paths relative to where the config/script is executed
    from.

    :func:`config.expand` accepts the directory of the config file, so the
    user's configuration can resolve absolute paths relative to where the
    config file is. In otherwords, if a config file at */var/moo/hi.yaml*
    has *./* in its configs, we want to be sure any file path with *./* is
    relative to */var/moo*, not the user's PWD.

    A :class:`libtmux.Server` object is created. No tmux server is started yet,
    just the object.

    The prepared configuration and the server object is passed into an instance
    of :class:`~tmuxp.workspacebuilder.WorkspaceBuilder`.

    A sanity check against :meth:`libtmux.common.which` is ran. It will raise
    an exception if tmux isn't found.

    If a tmux session under the same name as ``session_name`` in the tmuxp
    configuration exists, tmuxp offers to attach the session. Currently, tmuxp
    does not allow appending a workspace / incremental building on top of a
    current session (pull requests are welcome!).

    :meth:`~tmuxp.workspacebuilder.WorkspaceBuilder.build` will build the session in
    the background via using tmux's detached state (``-d``).

    After the session (workspace) is built, unless the user decided to load
    the session in the background via ``tmuxp -d`` (which is in the spirit
    of tmux's ``-d``), we need to prompt the user to attach the session.

    If the user is already inside a tmux client, which we detect via the
    ``TMUX`` environment variable bring present, we will prompt the user to
    switch their current client to it.

    If they're outside of tmux client - in a plain-old PTY - we will
    automatically ``attach``.

    If an exception is raised during the building of the workspace, tmuxp will
    prompt to cleanup (``$ tmux kill-session``) the session on the user's
    behalf. An exception raised during this process means it's not easy to
    predict how broken the session is.

    .. versionchanged:: tmux 2.6+

        In tmux 2.6, the way layout and proportion's work when interfacing
        with tmux in a detached state (outside of a client) changed. Since
        tmuxp builds workspaces in a detached state, the WorkspaceBuilder isn't
        able to rely on functionality requiring awarness of session geometry,
        e.g. ``set-layout``.

        Thankfully, tmux is able to defer commands to run after the user
        performs certain actions, such as loading a client via
        ``attach-session`` or ``switch-client``.

        Upon client switch, ``client-session-changed`` is triggered [1]_.

    References
    ----------
    .. [1] cmd-switch-client.c hook. GitHub repo for tmux.
       https://github.com/tmux/tmux/blob/2.6/cmd-switch-client.c#L132.
       Accessed April 8th, 2018.
    """
    # get the canonical path, eliminating any symlinks
    config_file = os.path.realpath(config_file)

    # kaptan allows us to open a yaml or json file as a dict
    sconfig = kaptan.Kaptan()
    sconfig = sconfig.import_config(config_file).get()
    # shapes configurations relative to config / profile file location
    sconfig = config.expand(sconfig, os.path.dirname(config_file))
    # propagate config inheritance (e.g. session -> window, window -> pane)
    sconfig = config.trickle(sconfig)

    t = Server(  # create tmux server object
        socket_name=socket_name, socket_path=socket_path, colors=colors
    )

    which('tmux')  # raise exception if tmux not found

    try:  # load WorkspaceBuilder object for tmuxp config / tmux server
        builder = WorkspaceBuilder(sconf=sconfig, server=t)
    except exc.EmptyConfigException:
        click.echo('%s is empty or parsed no config data' % config_file, err=True)
        return

    session_name = sconfig['session_name']

    # if the session already exists, prompt the user to attach. tmuxp doesn't
    # support incremental session building or appending (yet, PR's welcome!)
    if builder.session_exists(session_name):
        if not detached and (
            answer_yes
            or click.confirm(
                '%s is already running. Attach?'
                % click.style(session_name, fg='green'),
                default=True,
            )
        ):
            _reattach(builder.session)
        return

    try:
        click.echo(
            click.style('[Loading] ', fg='green')
            + click.style(config_file, fg='blue', bold=True)
        )

        builder.build()  # load tmux session via workspace builder

        if 'TMUX' in os.environ:  # tmuxp ran from inside tmux
            if not detached and (
                answer_yes or click.confirm('Already inside TMUX, switch to session?')
            ):
                # unset TMUX, save it, e.g. '/tmp/tmux-1000/default,30668,0'
                tmux_env = os.environ.pop('TMUX')

                if has_gte_version('2.6'):
                    set_layout_hook(builder.session, 'client-session-changed')

                builder.session.switch_client()  # switch client to new session

                os.environ['TMUX'] = tmux_env  # set TMUX back again
                return builder.session
            else:  # session created in the background, from within tmux
                if has_gte_version('2.6'):  # prepare for both cases
                    set_layout_hook(builder.session, 'client-attached')
                    set_layout_hook(builder.session, 'client-session-changed')

                sys.exit('Session created in detached state.')
        else:  # tmuxp ran from inside tmux
            if has_gte_version('2.6'):
                # if attaching for first time
                set_layout_hook(builder.session, 'client-attached')

                # for cases where user switches client for first time
                set_layout_hook(builder.session, 'client-session-changed')

            if not detached:
                builder.session.attach_session()

    except exc.TmuxpException as e:
        import traceback

        click.echo(traceback.format_exc(), err=True)
        click.echo(e, err=True)

        choice = click.prompt(
            'Error loading workspace. (k)ill, (a)ttach, (d)etach?',
            value_proc=_validate_choices(['k', 'a', 'd']),
            default='k',
        )

        if choice == 'k':
            builder.session.kill_session()
            click.echo('Session killed.')
        elif choice == 'a':
            if 'TMUX' in os.environ:
                builder.session.switch_client()
            else:
                builder.session.attach_session()
        else:
            sys.exit()

    return builder.session
コード例 #31
0
ファイル: cli.py プロジェクト: aurieh/tmuxp
def load_workspace(
    config_file,
    socket_name=None,
    socket_path=None,
    colors=None,
    detached=False,
    answer_yes=False,
):
    """
    Load a tmux "workspace" session via tmuxp file.

    Parameters
    ----------
    config_file : str
        absolute path to config file
    socket_name : str, optional
        ``tmux -L <socket-name>``
    socket_path: str, optional
        ``tmux -S <socket-path>``
    colors : str, optional
        '-2'
            Force tmux to support 256 colors
    detached : bool
        Force detached state. default False.
    answer_yes : bool
        Assume yes when given prompt. default False.

    Notes
    -----

    tmuxp will check and load a configuration file. The file will use kaptan
    to load a JSON/YAML into a :py:obj:`dict`. Then :func:`config.expand` and
    :func:`config.trickle` will be used to expand any shorthands, template
    variables, or file paths relative to where the config/script is executed
    from.

    :func:`config.expand` accepts the directory of the config file, so the
    user's configuration can resolve absolute paths relative to where the
    config file is. In otherwords, if a config file at */var/moo/hi.yaml*
    has *./* in its configs, we want to be sure any file path with *./* is
    relative to */var/moo*, not the user's PWD.

    A :class:`libtmux.Server` object is created. No tmux server is started yet,
    just the object.

    The prepared configuration and the server object is passed into an instance
    of :class:`~tmuxp.workspacebuilder.WorkspaceBuilder`.

    A sanity check against :meth:`libtmux.common.which` is ran. It will raise
    an exception if tmux isn't found.

    If a tmux session under the same name as ``session_name`` in the tmuxp
    configuration exists, tmuxp offers to attach the session. Currently, tmuxp
    does not allow appending a workspace / incremental building on top of a
    current session (pull requests are welcome!).

    :meth:`~tmuxp.workspacebuilder.WorkspaceBuilder.build` will build the session in
    the background via using tmux's detached state (``-d``).

    After the session (workspace) is built, unless the user decided to load
    the session in the background via ``tmuxp -d`` (which is in the spirit
    of tmux's ``-d``), we need to prompt the user to attach the session.

    If the user is already inside a tmux client, which we detect via the
    ``TMUX`` environment variable bring present, we will prompt the user to
    switch their current client to it.

    If they're outside of tmux client - in a plain-old PTY - we will
    automatically ``attach``.

    If an exception is raised during the building of the workspace, tmuxp will
    prompt to cleanup (``$ tmux kill-session``) the session on the user's
    behalf. An exception raised during this process means it's not easy to
    predict how broken the session is.

    .. versionchanged:: tmux 2.6+

        In tmux 2.6, the way layout and proportion's work when interfacing
        with tmux in a detached state (outside of a client) changed. Since
        tmuxp builds workspaces in a detached state, the WorkspaceBuilder isn't
        able to rely on functionality requiring awarness of session geometry,
        e.g. ``set-layout``.

        Thankfully, tmux is able to defer commands to run after the user
        performs certain actions, such as loading a client via
        ``attach-session`` or ``switch-client``.

        Upon client switch, ``client-session-changed`` is triggered [1]_.

    References
    ----------
    .. [1] cmd-switch-client.c hook. GitHub repo for tmux.
       https://github.com/tmux/tmux/blob/2.6/cmd-switch-client.c#L132.
       Accessed April 8th, 2018.
    """
    # here we ensure COLS and LINES is set for percentage config
    try:
        curses.initscr()
    finally:
        curses.endwin()

    # get the canonical path, eliminating any symlinks
    config_file = os.path.realpath(config_file)

    # kaptan allows us to open a yaml or json file as a dict
    sconfig = kaptan.Kaptan()
    sconfig = sconfig.import_config(config_file).get()
    # shapes configurations relative to config / profile file location
    sconfig = config.expand(sconfig, os.path.dirname(config_file))
    # propagate config inheritance (e.g. session -> window, window -> pane)
    sconfig = config.trickle(sconfig)

    t = Server(  # create tmux server object
        socket_name=socket_name, socket_path=socket_path, colors=colors
    )

    which('tmux')  # raise exception if tmux not found

    try:  # load WorkspaceBuilder object for tmuxp config / tmux server
        builder = WorkspaceBuilder(sconf=sconfig, server=t)
    except exc.EmptyConfigException:
        click.echo('%s is empty or parsed no config data' % config_file, err=True)
        return

    session_name = sconfig['session_name']

    # if the session already exists, prompt the user to attach. tmuxp doesn't
    # support incremental session building or appending (yet, PR's welcome!)
    if builder.session_exists(session_name):
        if not detached and (
            answer_yes
            or click.confirm(
                '%s is already running. Attach?'
                % click.style(session_name, fg='green'),
                default=True,
            )
        ):
            _reattach(builder.session)
        return

    try:
        click.echo(
            click.style('[Loading] ', fg='green')
            + click.style(config_file, fg='blue', bold=True)
        )

        builder.build()  # load tmux session via workspace builder

        if 'TMUX' in os.environ:  # tmuxp ran from inside tmux
            if not detached and (
                answer_yes or click.confirm('Already inside TMUX, switch to session?')
            ):
                # unset TMUX, save it, e.g. '/tmp/tmux-1000/default,30668,0'
                tmux_env = os.environ.pop('TMUX')

                if has_gte_version('2.6'):
                    set_layout_hook(builder.session, 'client-session-changed')

                builder.session.switch_client()  # switch client to new session

                os.environ['TMUX'] = tmux_env  # set TMUX back again
                return builder.session
            else:  # session created in the background, from within tmux
                if has_gte_version('2.6'):  # prepare for both cases
                    set_layout_hook(builder.session, 'client-attached')
                    set_layout_hook(builder.session, 'client-session-changed')

                sys.exit('Session created in detached state.')
        else:  # tmuxp ran from inside tmux
            if has_gte_version('2.6'):
                # if attaching for first time
                set_layout_hook(builder.session, 'client-attached')

                # for cases where user switches client for first time
                set_layout_hook(builder.session, 'client-session-changed')

            if not detached:
                builder.session.attach_session()

    except exc.TmuxpException as e:
        import traceback

        click.echo(traceback.format_exc(), err=True)
        click.echo(e, err=True)

        choice = click.prompt(
            'Error loading workspace. (k)ill, (a)ttach, (d)etach?',
            value_proc=_validate_choices(['k', 'a', 'd']),
            default='k',
        )

        if choice == 'k':
            builder.session.kill_session()
            click.echo('Session killed.')
        elif choice == 'a':
            if 'TMUX' in os.environ:
                builder.session.switch_client()
            else:
                builder.session.attach_session()
        else:
            sys.exit()

    return builder.session
コード例 #32
0
def load_workspace(config_file,
                   socket_name=None,
                   socket_path=None,
                   colors=None,
                   attached=None,
                   detached=None,
                   answer_yes=False):
    """Build config workspace.

    :param config_file: full path to config file
    :param type: str

    """
    # get the canonical path, eliminating any symlinks
    config_file = os.path.realpath(config_file)

    sconfig = kaptan.Kaptan()
    sconfig = sconfig.import_config(config_file).get()
    # expands configurations relative to config / profile file location
    sconfig = config.expand(sconfig, os.path.dirname(config_file))
    sconfig = config.trickle(sconfig)

    t = Server(socket_name=socket_name, socket_path=socket_path, colors=colors)

    try:
        builder = WorkspaceBuilder(sconf=sconfig, server=t)
    except exc.EmptyConfigException:
        click.echo('%s is empty or parsed no config data' % config_file,
                   err=True)
        return

    which('tmux')

    def reattach(session):
        if 'TMUX' in os.environ:
            session.switch_client()

        else:
            session.attach_session()

    session_name = sconfig['session_name']
    if builder.session_exists(session_name):
        if not detached and (answer_yes or click.confirm(
                '%s is already running. Attach?' %
                click.style(session_name, fg='green'),
                default=True)):
            reattach(builder.session)
        return

    try:
        click.echo(
            click.style('[Loading] ', fg='green') +
            click.style(config_file, fg='blue', bold=True))
        builder.build()

        if 'TMUX' in os.environ:  # tmuxp ran from inside tmux
            if not detached and (
                    answer_yes or
                    click.confirm('Already inside TMUX, switch to session?')):
                tmux_env = os.environ.pop('TMUX')

                if has_gte_version('2.6'):
                    # if using -d from inside tmux session + switching inside
                    # https://github.com/tmux/tmux/blob/2.6/cmd-switch-client.c#L132
                    set_layout_hook(builder.session, 'client-session-changed')

                builder.session.switch_client()

                os.environ['TMUX'] = tmux_env
                return builder.session
            else:  # session created in the background, from within tmux
                if has_gte_version('2.6'):  # prepare for both cases
                    set_layout_hook(builder.session, 'client-attached')
                    set_layout_hook(builder.session, 'client-session-changed')

                sys.exit('Session created in detached state.')

        # below: tmuxp ran outside of tmux

        if has_gte_version('2.6'):
            # if attaching for first time
            set_layout_hook(builder.session, 'client-attached')

            # for cases where user switches client for first time
            set_layout_hook(builder.session, 'client-session-changed')

        if not detached:
            builder.session.attach_session()

    except exc.TmuxpException as e:
        import traceback

        click.echo(traceback.format_exc(), err=True)
        click.echo(e, err=True)

        choice = click.prompt(
            'Error loading workspace. (k)ill, (a)ttach, (d)etach?',
            value_proc=_validate_choices(['k', 'a', 'd']),
            default='k')

        if choice == 'k':
            builder.session.kill_session()
            click.echo('Session killed.')
        elif choice == 'a':
            if 'TMUX' in os.environ:
                builder.session.switch_client()
            else:
                builder.session.attach_session()
        else:
            sys.exit()

    return builder.session