From db364bd0ef16bba067dfa5a91d25e9bb5de82e10 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 22 May 2016 09:48:27 -0500 Subject: [PATCH 1/8] remove vendored libtmux --- libtmux/__init__.py | 4 - libtmux/_compat.py | 91 --------- libtmux/common.py | 393 ------------------------------------- libtmux/exc.py | 21 -- libtmux/formats.py | 102 ---------- libtmux/pane.py | 185 ------------------ libtmux/server.py | 464 ------------------------------------------- libtmux/session.py | 418 --------------------------------------- libtmux/window.py | 466 -------------------------------------------- 9 files changed, 2144 deletions(-) delete mode 100644 libtmux/__init__.py delete mode 100644 libtmux/_compat.py delete mode 100644 libtmux/common.py delete mode 100644 libtmux/exc.py delete mode 100644 libtmux/formats.py delete mode 100644 libtmux/pane.py delete mode 100644 libtmux/server.py delete mode 100644 libtmux/session.py delete mode 100644 libtmux/window.py diff --git a/libtmux/__init__.py b/libtmux/__init__.py deleted file mode 100644 index 17636a7d30..0000000000 --- a/libtmux/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .pane import Pane -from .server import Server -from .session import Session -from .window import Window diff --git a/libtmux/_compat.py b/libtmux/_compat.py deleted file mode 100644 index ca238300d3..0000000000 --- a/libtmux/_compat.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf8 -*- -import sys - -PY2 = sys.version_info[0] == 2 - -_identity = lambda x: x - - -if PY2: - unichr = unichr - text_type = unicode - string_types = (str, unicode) - integer_types = (int, long) - from urllib import urlretrieve - - text_to_native = lambda s, enc: s.encode(enc) - - iterkeys = lambda d: d.iterkeys() - itervalues = lambda d: d.itervalues() - iteritems = lambda d: d.iteritems() - - from cStringIO import StringIO as BytesIO - from StringIO import StringIO - import cPickle as pickle - import ConfigParser as configparser - - from itertools import izip, imap - range_type = xrange - - cmp = cmp - - input = raw_input - from string import lower as ascii_lowercase - import urlparse - - exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') - - def implements_to_string(cls): - cls.__unicode__ = cls.__str__ - cls.__str__ = lambda x: x.__unicode__().encode('utf-8') - return cls - - def console_to_str(s): - return s.decode('utf_8') - -else: - unichr = chr - text_type = str - string_types = (str,) - integer_types = (int, ) - - text_to_native = lambda s, enc: s - - iterkeys = lambda d: iter(d.keys()) - itervalues = lambda d: iter(d.values()) - iteritems = lambda d: iter(d.items()) - - from io import StringIO, BytesIO - import pickle - import configparser - - izip = zip - imap = map - range_type = range - - cmp = lambda a, b: (a > b) - (a < b) - - input = input - from string import ascii_lowercase - import urllib.parse as urllib - import urllib.parse as urlparse - from urllib.request import urlretrieve - - console_encoding = sys.__stdout__.encoding - - implements_to_string = _identity - - def console_to_str(s): - """ From pypa/pip project, pip.backwardwardcompat. License MIT. """ - try: - return s.decode(console_encoding) - except UnicodeDecodeError: - return s.decode('utf_8') - - def reraise(tp, value, tb=None): - if value.__traceback__ is not tb: - raise(value.with_traceback(tb)) - raise value - - -number_types = integer_types + (float,) diff --git a/libtmux/common.py b/libtmux/common.py deleted file mode 100644 index 09853f9f23..0000000000 --- a/libtmux/common.py +++ /dev/null @@ -1,393 +0,0 @@ -# -*- coding: utf-8 -*- -"""Helper methods and mixins. - -libtmux.common -~~~~~~~~~~~~~~ - -""" -import collections -import logging -import os -import re -import subprocess -from distutils.version import StrictVersion - -from . import exc -from ._compat import console_to_str - -logger = logging.getLogger(__name__) - - -class EnvironmentMixin(object): - - """Mixin class for managing session and server level environment - variables in tmux. - - """ - - _add_option = None - - def __init__(self, add_option=None): - self._add_option = add_option - - def set_environment(self, name, value): - """Set environment ``$ tmux set-environment ``. - - :param name: the environment variable name. such as 'PATH'. - :type option: string - :param value: environment value. - :type value: string - - """ - - args = ['set-environment'] - if self._add_option: - args += [self._add_option] - - args += [name, value] - - proc = self.cmd(*args) - - if proc.stderr: - if isinstance(proc.stderr, list) and len(proc.stderr) == int(1): - proc.stderr = proc.stderr[0] - raise ValueError('tmux set-environment stderr: %s' % proc.stderr) - - def unset_environment(self, name): - """Unset environment variable ``$ tmux set-environment -u ``. - - :param name: the environment variable name. such as 'PATH'. - :type option: string - """ - - args = ['set-environment'] - if self._add_option: - args += [self._add_option] - args += ['-u', name] - - proc = self.cmd(*args) - - if proc.stderr: - if isinstance(proc.stderr, list) and len(proc.stderr) == int(1): - proc.stderr = proc.stderr[0] - raise ValueError('tmux set-environment stderr: %s' % proc.stderr) - - def remove_environment(self, name): - """Remove environment variable ``$ tmux set-environment -r ``. - - :param name: the environment variable name. such as 'PATH'. - :type option: string - """ - - args = ['set-environment'] - if self._add_option: - args += [self._add_option] - args += ['-r', name] - - proc = self.cmd(*args) - - if proc.stderr: - if isinstance(proc.stderr, list) and len(proc.stderr) == int(1): - proc.stderr = proc.stderr[0] - raise ValueError('tmux set-environment stderr: %s' % proc.stderr) - - def show_environment(self, name=None): - """Show environment ``$tmux show-environment -t [session] ``. - - Return dict of environment variables for the session or the value of a - specific variable if the name is specified. - - :param name: the environment variable name. such as 'PATH'. - :type option: string - """ - tmux_args = ['show-environment'] - if self._add_option: - tmux_args += [self._add_option] - if name: - tmux_args += [name] - vars = self.cmd(*tmux_args).stdout - vars = [tuple(item.split('=', 1)) for item in vars] - vars_dict = {} - for t in vars: - if len(t) == 2: - vars_dict[t[0]] = t[1] - elif len(t) == 1: - vars_dict[t[0]] = True - else: - raise ValueError('unexpected variable %s', t) - - if name: - return vars_dict.get(name) - - return vars_dict - - -class tmux_cmd(object): - - """:term:`tmux(1)` command via :py:mod:`subprocess`. - - Usage:: - - proc = tmux_cmd('new-session', '-s%' % 'my session') - - if proc.stderr: - raise exc.LibTmuxException( - 'Command: %s returned error: %s' % (proc.cmd, proc.stderr) - ) - - print('tmux command returned %s' % proc.stdout) - - Equivalent to: - - .. code-block:: bash - - $ tmux new-session -s my session - - :versionchanged: 0.8 - Renamed from ``tmux`` to ``tmux_cmd``. - - """ - - def __init__(self, *args, **kwargs): - cmd = [which('tmux')] - cmd += args # add the command arguments to cmd - cmd = [str(c) for c in cmd] - - self.cmd = cmd - - try: - self.process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - self.process.wait() - stdout = self.process.stdout.read() - self.process.stdout.close() - stderr = self.process.stderr.read() - self.process.stderr.close() - except Exception as e: - logger.error( - 'Exception for %s: \n%s' % ( - subprocess.list2cmdline(cmd), - e - ) - ) - - self.stdout = console_to_str(stdout) - self.stdout = self.stdout.split('\n') - self.stdout = list(filter(None, self.stdout)) # filter empty values - - self.stderr = console_to_str(stderr) - self.stderr = self.stderr.split('\n') - self.stderr = list(filter(None, self.stderr)) # filter empty values - - if 'has-session' in cmd and len(self.stderr): - if not self.stdout: - self.stdout = self.stderr[0] - - logging.debug('self.stdout for %s: \n%s' % - (' '.join(cmd), self.stdout)) - - -class TmuxMappingObject(collections.MutableMapping): - - """Base: :py:class:`collections.MutableMapping`. - - Convenience container. Base class for :class:`Pane`, :class:`Window`, - :class:`Session` and :class:`Server`. - - Instance attributes for useful information :term:`tmux(1)` uses for - Session, Window, Pane, stored :attr:`self._TMUX`. For example, a - :class:`Window` will have a ``window_id`` and ``window_name``. - - """ - - def __getitem__(self, key): - return self._TMUX[key] - - def __setitem__(self, key, value): - self._TMUX[key] = value - self.dirty = True - - def __delitem__(self, key): - del self._TMUX[key] - self.dirty = True - - def keys(self): - """Return list of keys.""" - return self._TMUX.keys() - - def __iter__(self): - return self._TMUX.__iter__() - - def __len__(self): - return len(self._TMUX.keys()) - - -class TmuxRelationalObject(object): - - """Base Class for managing tmux object child entities. .. # NOQA - - Manages collection of child objects (a :class:`Server` has a collection of - :class:`Session` objects, a :class:`Session` has collection of - :class:`Window`) - - Children of :class:`TmuxRelationalObject` are going to have a - ``self.children``, ``self.childIdAttribute`` and ``self.list_children``. - - ================ ================== ===================== ============================ - Object ``.children`` ``.childIdAttribute`` method - ================ ================== ===================== ============================ - :class:`Server` ``self._sessions`` 'session_id' :meth:`Server.list_sessions` - :class:`Session` ``self._windows`` 'window_id' :meth:`Session.list_windows` - :class:`Window` ``self._panes`` 'pane_id' :meth:`Window.list_panes` - :class:`Pane` - ================ ================== ===================== ============================ - - """ - - def findWhere(self, attrs): - """Return object on first match. - - Based on `.findWhere()`_ from `underscore.js`_. - - .. _.findWhere(): http://underscorejs.org/#findWhere - .. _underscore.js: http://underscorejs.org/ - - """ - try: - return self.where(attrs)[0] - except IndexError: - return None - - def where(self, attrs, first=False): - """Return objects matching child objects properties. - - Based on `.where()`_ from `underscore.js`_. - - .. _.where(): http://underscorejs.org/#where - .. _underscore.js: http://underscorejs.org/ - - :param attrs: tmux properties to match - :type attrs: dict - :rtype: list - - """ - - # from https://github.com/serkanyersen/underscore.py - def by(val, *args): - for key, value in attrs.items(): - try: - if attrs[key] != val[key]: - return False - except KeyError: - return False - return True - - if first: - return list(filter(by, self.children))[0] - else: - return list(filter(by, self.children)) - - def getById(self, id): - """Return object based on ``childIdAttribute``. - - Based on `.get()`_ from `backbone.js`_. - - .. _backbone.js: http://backbonejs.org/ - .. _.get(): http://backbonejs.org/#Collection-get - - :param id: - :type id: string - :rtype: object - - """ - for child in self.children: - if child[self.childIdAttribute] == id: - return child - else: - continue - - return None - - -def which(exe=None): - """Return path of bin. Python clone of /usr/bin/which. - - from salt.util - https://www.github.com/saltstack/salt - license apache - - :param exe: Application to search PATHs for. - :type exe: string - :rtype: string - - """ - if exe: - if os.access(exe, os.X_OK): - return exe - - # default path based on busybox's default - search_path = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin' - - for path in search_path.split(os.pathsep): - full_path = os.path.join(path, exe) - if os.access(full_path, os.X_OK): - return full_path - raise exc.LibTmuxException( - '{0!r} could not be found in the following search ' - 'path: {1!r}'.format( - exe, search_path - ) - ) - logger.error('No executable was passed to be searched by which') - return None - - -def is_version(version): - """Return True if tmux version installed. - - :param version: version, '1.8' - :param type: string - :rtype: bool - - """ - proc = tmux_cmd('-V') - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - installed_version = proc.stdout[0].split('tmux ')[1] - - return StrictVersion(installed_version) == StrictVersion(version) - - -def has_required_tmux_version(version=None): - """Return if tmux meets version requirement. Version >1.8 or above. - - :versionchanged: 0.1.7 - Versions will now remove trailing letters per `Issue 55`_. - - .. _Issue 55: https://github.com/tony/libtmux/issues/55. - - """ - - if not version: - proc = tmux_cmd('-V') - - if proc.stderr: - if proc.stderr[0] == 'tmux: unknown option -- V': - raise exc.LibTmuxException( - 'libtmux supports tmux 1.8 and greater. This system' - ' is running tmux 1.3 or earlier.') - raise exc.LibTmuxException(proc.stderr) - - version = proc.stdout[0].split('tmux ')[1] - - version = re.sub(r'[a-z]', '', version) - - if StrictVersion(version) <= StrictVersion("1.7"): - raise exc.LibTmuxException( - 'libtmux only supports tmux 1.8 and greater. This system' - ' has %s installed. Upgrade your tmux to use libtmux.' % version - ) - return version diff --git a/libtmux/exc.py b/libtmux/exc.py deleted file mode 100644 index c6c442fe43..0000000000 --- a/libtmux/exc.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -"""libtmux exceptions. - -libtmux.exc -~~~~~~~~~~~ - -""" -from __future__ import (absolute_import, division, print_function, - unicode_literals, with_statement) - - -class LibTmuxException(Exception): - - """Base Exception for libtmux Errors.""" - - -class TmuxSessionExists(LibTmuxException): - - """Session does not exist in the server.""" - - pass diff --git a/libtmux/formats.py b/libtmux/formats.py deleted file mode 100644 index 202382bff3..0000000000 --- a/libtmux/formats.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- -"""Format variables for tmux objects. - -libtmux.formats -~~~~~~~~~~~~~~~ - -For reference: http://sourceforge.net/p/tmux/tmux-code/ci/master/tree/format.c - -""" - -from __future__ import (absolute_import, division, print_function, - unicode_literals, with_statement) - -SESSION_FORMATS = [ - 'session_name', - 'session_windows', - 'session_width', - 'session_height', - 'session_id', - 'session_created', - 'session_created_string', - 'session_attached', - 'session_grouped', - 'session_group', -] - -CLIENT_FORMATS = [ - 'client_cwd', - 'client_height', - 'client_width', - 'client_tty', - 'client_termname', - 'client_created', - 'client_created_string', - 'client_activity', - 'client_activity_string', - 'client_prefix', - 'client_utf8', - 'client_readonly', - 'client_session', - 'client_last_session', -] - -WINDOW_FORMATS = [ - # format_window() - 'window_id', - 'window_name', - 'window_width', - 'window_height', - 'window_layout', - 'window_panes', - # format_winlink() - 'window_index', - 'window_flags', - 'window_active', - 'window_bell_flag', - 'window_activity_flag', - 'window_silence_flag', -] - -PANE_FORMATS = [ - 'history_size', - 'history_limit', - 'history_bytes', - 'pane_index', - 'pane_width', - 'pane_height', - 'pane_title', - 'pane_id', - 'pane_active', - 'pane_dead', - 'pane_in_mode', - 'pane_synchronized', - 'pane_tty', - 'pane_pid', - 'pane_start_command', - 'pane_start_path', - 'pane_current_path', - 'pane_current_command', - - 'cursor_x', - 'cursor_y', - 'scroll_region_upper', - 'scroll_region_lower', - 'saved_cursor_x', - 'saved_cursor_y', - - 'alternate_on', - 'alternate_saved_x', - 'alternate_saved_y', - - 'cursor_flag', - 'insert_flag', - 'keypad_cursor_flag', - 'keypad_flag', - 'wrap_flag', - - 'mouse_standard_flag', - 'mouse_button_flag', - 'mouse_any_flag', - 'mouse_utf8_flag', -] diff --git a/libtmux/pane.py b/libtmux/pane.py deleted file mode 100644 index ec89c14430..0000000000 --- a/libtmux/pane.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*- coding: utf-8 -*- -"""Pythonization of the :ref:`tmux(1)` pane. - -libtmux.pane -~~~~~~~~~~~~ - -""" -from __future__ import (absolute_import, division, print_function, - unicode_literals, with_statement) - -import logging - -from . import exc -from .common import TmuxMappingObject, TmuxRelationalObject - -logger = logging.getLogger(__name__) - - -class Pane(TmuxMappingObject, TmuxRelationalObject): - - """:term:`tmux(1)` :term:`pane`. - - :param window: :class:`Window` - - :versionchanged: 0.8 - Renamed from ``.tmux`` to ``.cmd``. - - """ - - def __init__(self, window=None, **kwargs): - if not window: - raise ValueError('Pane must have \ - ``Window`` object') - - self.window = window - self.session = self.window.session - self.server = self.session.server - - self._pane_id = kwargs['pane_id'] - - self.server._update_panes() - - @property - def _TMUX(self, *args): - - attrs = { - 'pane_id': self._pane_id - } - - # from https://github.com/serkanyersen/underscore.py - def by(val, *args): - for key, value in attrs.items(): - try: - if attrs[key] != val[key]: - return False - except KeyError: - return False - return True - - return list(filter(by, self.server._panes))[0] - - def cmd(self, cmd, *args, **kwargs): - """Return :meth:`Server.cmd` defaulting to ``target_pane`` as target. - - Send command to tmux with :attr:`pane_id` as ``target-pane``. - - Specifying ``('-t', 'custom-target')`` or ``('-tcustom_target')`` in - ``args`` will override using the object's ``pane_id`` as target. - - :rtype: :class:`Server.cmd` - - """ - if not any(arg.startswith('-t') for arg in args): - args = ('-t', self.get('pane_id')) + args - - return self.server.cmd(cmd, *args, **kwargs) - - def send_keys(self, cmd, enter=True, suppress_history=True): - """``$ tmux send-keys`` to the pane. - - A leading space character is added to cmd to avoid polluting the - user's history. - - :param cmd: Text or input into pane - :type cmd: str - :param enter: Send enter after sending the input. - :type enter: bool - :param suppress_history: Don't add these keys to the shell history - :type suppress_history: bool - - """ - prefix = ' ' if suppress_history else '' - self.cmd('send-keys', prefix + cmd) - - if enter: - self.enter() - - def clear(self): - """Clear pane.""" - self.send_keys('reset') - - def reset(self): - """Reset and clear pane history. """ - - self.cmd('send-keys', '-R \; clear-history') - - def split_window(self, attach=False): - """Split window at pane and return newly created :class:`Pane`. - - :param attach: Attach / select pane after creation. - :type attach: bool - :rtype: :class:`Pane`. - - """ - return self.window.split_window( - target=self.get('pane_id'), - attach=attach - ) - - def set_width(self, width): - """Set width of pane. - - :param width: pane width, in cells. - :type width: int - - """ - self.resize_pane(width=width) - - def set_height(self, height): - """Set height of pane. - - :param height: pane height, in cells. - :type height: int - - """ - self.resize_pane(height=height) - - def resize_pane(self, *args, **kwargs): - """``$ tmux resize-pane`` of pane and return ``self``. - - :param target_pane: ``target_pane``, or ``-U``,``-D``, ``-L``, ``-R``. - :type target_pane: string - :rtype: :class:`Pane` - - """ - - if 'height' in kwargs: - proc = self.cmd('resize-pane', '-y%s' % int(kwargs['height'])) - elif 'width' in kwargs: - proc = self.cmd('resize-pane', '-x%s' % int(kwargs['width'])) - else: - proc = self.cmd('resize-pane', args[0]) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - self.server._update_panes() - return self - - def enter(self): - """Send carriage return to pane. - - ``$ tmux send-keys`` send Enter to the pane. - - """ - self.cmd('send-keys', 'Enter') - - def select_pane(self): - """Select pane. Return ``self``. - - To select a window object asynchrously. If a ``pane`` object exists - and is no longer longer the current window, ``w.select_pane()`` - will make ``p`` the current pane. - - :rtype: :class:`pane` - - """ - return self.window.select_pane(self.get('pane_id')) - - def __repr__(self): - return "%s(%s %s)" % ( - self.__class__.__name__, - self.get('pane_id'), - self.window - ) diff --git a/libtmux/server.py b/libtmux/server.py deleted file mode 100644 index 10fd6a12ee..0000000000 --- a/libtmux/server.py +++ /dev/null @@ -1,464 +0,0 @@ -# -*- coding: utf-8 -*- -"""Pythonization of the :term:`tmux(1)` server. - -libtmux.server -~~~~~~~~~~~~~~ - -""" -from __future__ import (absolute_import, division, print_function, - unicode_literals, with_statement) - -import logging -import os - -from . import exc, formats -from .common import EnvironmentMixin, TmuxRelationalObject, tmux_cmd -from .session import Session - -logger = logging.getLogger(__name__) - - -class Server(TmuxRelationalObject, EnvironmentMixin): - - """The :term:`tmux(1)` server. - - - :attr:`Server._sessions` [:class:`Session`, ...] - - - :attr:`Session._windows` [:class:`Window`, ...] - - - :attr:`Window._panes` [:class:`Pane`, ...] - - - :class:`Pane` - - When instantiated, provides the ``t`` global. stores information on live, - running tmux server. - - """ - - #: ``[-L socket-name]`` - socket_name = None - #: ``[-S socket-path]`` - socket_path = None - #: ``[-f file]`` - config_file = None - #: ``-2`` or ``-8`` - colors = None - #: unique child ID key - childIdAttribute = 'session_id' - - def __init__( - self, - socket_name=None, - socket_path=None, - config_file=None, - colors=None, - **kwargs - ): - EnvironmentMixin.__init__(self, '-g') - self._windows = [] - self._panes = [] - - if socket_name: - self.socket_name = socket_name - - if socket_path: - self.socket_path = socket_path - - if config_file: - self.config_file = config_file - - if colors: - self.colors = colors - - def cmd(self, *args, **kwargs): - """Return :class:`util.tmux_cmd` send tmux commands with sockets, colors. - - :rtype: :class:`util.tmux_cmd` - - :versionchanged: 0.8 - Renamed from ``.tmux`` to ``.cmd``. - - """ - - args = list(args) - if self.socket_name: - args.insert(0, '-L{0}'.format(self.socket_name)) - if self.socket_path: - args.insert(0, '-S{0}'.format(self.socket_path)) - if self.config_file: - args.insert(0, '-f{0}'.format(self.config_file)) - if self.colors: - if self.colors == 256: - args.insert(0, '-2') - elif self.colors == 88: - args.insert(0, '-8') - else: - raise ValueError('Server.colors must equal 88 or 256') - - return tmux_cmd(*args, **kwargs) - - def _list_sessions(self): - """Return list of sessions in :py:obj:`dict` form. - - Retrieved from ``$ tmux(1) list-sessions`` stdout. - - The :py:obj:`list` is derived from ``stdout`` in :class:`util.tmux_cmd` - which wraps :py:class:`subprocess.Popen`. - - :rtype: :py:obj:`list` of :py:obj:`dict` - - """ - - sformats = formats.SESSION_FORMATS - tmux_formats = ['#{%s}' % f for f in sformats] - - tmux_args = ( - '-F%s' % '\t'.join(tmux_formats), # output - ) - - proc = self.cmd( - 'list-sessions', - *tmux_args - ) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - sformats = formats.SESSION_FORMATS - tmux_formats = ['#{%s}' % format for format in sformats] - sessions = proc.stdout - - # combine format keys with values returned from ``tmux list-windows`` - sessions = [dict(zip( - sformats, session.split('\t'))) for session in sessions] - - # clear up empty dict - sessions = [ - dict((k, v) for k, v in session.items() if v) - for session in sessions - ] - - return sessions - - @property - def _sessions(self): - """Property / alias to return :meth:`~._list_sessions`.""" - - return self._list_sessions() - - def list_sessions(self): - """Return list of :class:`Session` from the ``tmux(1)`` session. - - :rtype: :py:obj:`list` of :class:`Session` - - """ - return [ - Session(server=self, **s) for s in self._sessions - ] - - @property - def sessions(self): - """Property / alias to return :meth:`~.list_sessions`.""" - return self.list_sessions() - #: Alias of :attr:`sessions`. - children = sessions - - def _list_windows(self): - """Return list of windows in :py:obj:`dict` form. - - Retrieved from ``$ tmux(1) list-windows`` stdout. - - The :py:obj:`list` is derived from ``stdout`` in :class:`util.tmux_cmd` - which wraps :py:class:`subprocess.Popen`. - - :rtype: list - - """ - - wformats = ['session_name', 'session_id'] + formats.WINDOW_FORMATS - tmux_formats = ['#{%s}' % format for format in wformats] - - proc = self.cmd( - 'list-windows', # ``tmux list-windows`` - '-a', - '-F%s' % '\t'.join(tmux_formats), # output - ) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - windows = proc.stdout - - wformats = ['session_name', 'session_id'] + formats.WINDOW_FORMATS - - # combine format keys with values returned from ``tmux list-windows`` - windows = [dict(zip( - wformats, window.split('\t'))) for window in windows] - - # clear up empty dict - windows = [ - dict((k, v) for k, v in window.items() if v) for window in windows - ] - - # tmux < 1.8 doesn't have window_id, use window_name - for w in windows: - if 'window_id' not in w: - w['window_id'] = w['window_name'] - - if self._windows: - self._windows[:] = [] - - self._windows.extend(windows) - - return self._windows - - def _update_windows(self): - """Update internal window data and return ``self`` for chainability. - - :rtype: :class:`Server` - - """ - self._list_windows() - return self - - def _list_panes(self): - """Return list of panes in :py:obj:`dict` form. - - Retrieved from ``$ tmux(1) list-panes`` stdout. - - The :py:obj:`list` is derived from ``stdout`` in :class:`util.tmux_cmd` - which wraps :py:class:`subprocess.Popen`. - - :rtype: list - - """ - - pformats = [ - 'session_name', 'session_id', - 'window_index', 'window_id', - 'window_name' - ] + formats.PANE_FORMATS - tmux_formats = ['#{%s}\t' % f for f in pformats] - - proc = self.cmd( - 'list-panes', - '-a', - '-F%s' % ''.join(tmux_formats), # output - ) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - panes = proc.stdout - - pformats = [ - 'session_name', 'session_id', - 'window_index', 'window_id', 'window_name' - ] + formats.PANE_FORMATS - - # combine format keys with values returned from ``tmux list-panes`` - panes = [dict(zip( - pformats, window.split('\t'))) for window in panes] - - # clear up empty dict - panes = [ - dict((k, v) for k, v in window.items() if v) for window in panes - ] - - if self._panes: - self._panes[:] = [] - - self._panes.extend(panes) - - return self._panes - - def _update_panes(self): - """Update internal pane data and return ``self`` for chainability. - - :rtype: :class:`Server` - - """ - self._list_panes() - return self - - def attached_sessions(self): - """Return active :class:`Session` objects. - - This will not work where multiple tmux sessions are attached. - - :rtype: :py:obj:`list` of :class:`Session` - - """ - - sessions = self._sessions - attached_sessions = list() - - for session in sessions: - if 'session_attached' in session: - # for now session_active is a unicode - if session.get('session_attached') == '1': - logger.debug('session %s attached', session.get( - 'session_name')) - attached_sessions.append(session) - else: - continue - - return [ - Session(server=self, **s) for s in attached_sessions - ] or None - - def has_session(self, target_session): - """Return True if session exists. ``$ tmux has-session``. - - :param: target_session: str of session name. - :rtype: bool - - """ - - proc = self.cmd('has-session', '-t%s' % target_session) - - if not proc.stdout: - return True - if any( - x in proc.stdout for x in - ['failed to connect to server', 'error connecting to'] - ): - return False - elif 'no server running' in proc.stdout: # tmux 2.0 - return False - elif 'can\'t find session' in proc.stdout: # tmux 2.1 - return False - elif 'session not found' in proc.stdout: - return False - else: - return True - - def kill_server(self): - """``$ tmux kill-server``.""" - self.cmd('kill-server') - - def kill_session(self, target_session=None): - """Kill the tmux session with ``$ tmux kill-session``, return ``self``. - - :param: target_session: str. note this accepts ``fnmatch(3)``. 'asdf' - will kill 'asdfasd'. - - :rtype: :class:`Server` - - """ - proc = self.cmd('kill-session', '-t%s' % target_session) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - return self - - def switch_client(self, target_session): - """``$ tmux switch-client``. - - :param: target_session: str. name of the session. fnmatch(3) works. - - """ - - proc = self.cmd('switch-client', '-t%s' % target_session) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - def attach_session(self, target_session=None): - """``$ tmux attach-session`` aka alias: ``$ tmux attach``. - - :param: target_session: str. name of the session. fnmatch(3) works. - - """ - tmux_args = tuple() - if target_session: - tmux_args += ('-t%s' % target_session,) - - proc = self.cmd('attach-session', *tmux_args) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - def new_session(self, - session_name=None, - kill_session=False, - attach=False, - *args, - **kwargs): - """Return :class:`Session` from ``$ tmux new-session``. - - Uses ``-P`` flag to print session info, ``-F`` for return formatting - returns new Session object. - - ``$ tmux new-session -d`` will create the session in the background - ``$ tmux new-session -Ad`` will move to the session name if it already - exists. todo: make an option to handle this. - - :param session_name: session name:: - - $ tmux new-session -s - :type session_name: string - - :param attach: create session in the foreground. ``attach=False`` is - equivalent to:: - - $ tmux new-session -d - :type attach: bool - - :param kill_session: Kill current session if ``$ tmux has-session`` - Useful for testing workspaces. - :type kill_session: bool - :rtype: :class:`Session` - - """ - - if self.has_session(session_name): - if kill_session: - self.cmd('kill-session', '-t%s' % session_name) - logger.info('session %s exists. killed it.' % session_name) - else: - raise exc.TmuxSessionExists( - 'Session named %s exists' % session_name - ) - - logger.debug('creating session %s' % session_name) - - sformats = formats.SESSION_FORMATS - tmux_formats = ['#{%s}' % f for f in sformats] - - env = os.environ.get('TMUX') - - if env: - del os.environ['TMUX'] - - tmux_args = ( - '-s%s' % session_name, - '-P', '-F%s' % '\t'.join(tmux_formats), # output - ) - - if not attach: - tmux_args += ('-d',) - - proc = self.cmd( - 'new-session', - *tmux_args - ) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - session = proc.stdout[0] - - if env: - os.environ['TMUX'] = env - - # combine format keys with values returned from ``tmux list-windows`` - session = dict(zip(sformats, session.split('\t'))) - - # clear up empty dict - session = dict((k, v) for k, v in session.items() if v) - - session = Session(server=self, **session) - - return session diff --git a/libtmux/session.py b/libtmux/session.py deleted file mode 100644 index f87f93bc98..0000000000 --- a/libtmux/session.py +++ /dev/null @@ -1,418 +0,0 @@ -# -*- coding: utf-8 -*- -"""Pythonization of the :term:`tmux(1)` session. - -libtmux.session -~~~~~~~~~~~~~~~ - -""" -from __future__ import (absolute_import, division, print_function, - unicode_literals, with_statement) - -import logging -import os - -from . import exc, formats -from .common import EnvironmentMixin, TmuxMappingObject, TmuxRelationalObject -from .window import Window - -logger = logging.getLogger(__name__) - - -class Session( - TmuxMappingObject, - TmuxRelationalObject, - EnvironmentMixin -): - """:term:`tmux(1)` session. - - Holds :class:`Window` objects. - - """ - - childIdAttribute = 'window_id' - - def __init__(self, server=None, **kwargs): - EnvironmentMixin.__init__(self) - self.server = server - - if 'session_id' not in kwargs: - raise ValueError('Session requires a `session_id`') - self._session_id = kwargs['session_id'] - self.server._update_windows() - - @property - def _TMUX(self, *args): - - attrs = { - 'session_id': str(self._session_id) - } - - # from https://github.com/serkanyersen/underscore.py - def by(val, *args): - for key, value in attrs.items(): - try: - if attrs[key] != val[key]: - return False - except KeyError: - return False - return True - - try: - return list(filter(by, self.server._sessions))[0] - except IndexError as e: - logger.error(e) - - def cmd(self, *args, **kwargs): - """Return :meth:`server.cmd`. - - :rtype: :class:`server.cmd` - - :versionchanged: 0.8 - Renamed from ``.tmux`` to ``.cmd``. - - """ - if '-t' not in kwargs: - kwargs['-t'] = self.get('session_id') - return self.server.cmd(*args, **kwargs) - - def attach_session(self, target_session=None): - """Return ``$ tmux attach-session`` aka alias: ``$ tmux attach``. - - :param: target_session: str. name of the session. fnmatch(3) works. - - """ - proc = self.cmd('attach-session', '-t%s' % self.get('session_id')) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - def kill_session(self): - """``$ tmux kill-session``.""" - - proc = self.cmd('kill-session', '-t%s' % self.get('session_id')) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - def switch_client(self, target_session=None): - """``$ tmux switch-client``. - - :param: target_session: str. note this accepts fnmatch(3). - """ - proc = self.cmd('switch-client', '-t%s' % self.get('session_id')) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - def rename_session(self, new_name): - """Rename session and return new :class:`Session` object. - - :param rename_session: new session name - :type rename_session: string - :rtype: :class:`Session` - - """ - proc = self.cmd( - 'rename-session', - '-t%s' % self.get('session_id'), - new_name - ) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - return self - - def new_window(self, - window_name=None, - start_directory=None, - attach=True, - window_index='', - window_shell=None): - """Return :class:`Window` from ``$ tmux new-window``. - - .. note:: - - By default, this will make the window active. For the new window - to be created and not set to current, pass in ``attach=False``. - - :param window_name: window name. - - .. code-block:: bash - - $ tmux new-window -n -c - - :type window_name: string - :param start_directory: specifies the working directory in which the - new created. - :type start_directory: string - :param attach: make new window the current window after creating it, - default True. - :param window_index: create the new window at the given index position. - Default is empty string which will create the window in the next - available position. - :type window_index: string - :param window_shell: execute a command on starting the window. The - window will close when the command exits. - NOTE: When this command exits the window will close. This feature - is useful for long-running processes where the closing of the - window upon completion is desired. - :type window_command: string - :param type: bool - :rtype: :class:`Window` - - """ - - wformats = ['session_name', 'session_id'] + formats.WINDOW_FORMATS - tmux_formats = ['#{%s}' % f for f in wformats] - - window_args = tuple() - - if not attach: - window_args += ('-d',) - - window_args += ( - '-P', - ) - - if start_directory: - # as of 2014-02-08 tmux 1.9-dev doesn't expand ~ in new-window -c. - start_directory = os.path.expanduser(start_directory) - window_args += ('-c%s' % start_directory,) - - window_args += ( - '-F"%s"' % '\t'.join(tmux_formats), # output - ) - if window_name: - window_args += ('-n%s' % window_name,) - - window_args += ( - # empty string for window_index will use the first one available - '-t%s:%s' % (self.get('session_id'), window_index), - ) - - if window_shell: - window_args += (window_shell,) - - proc = self.cmd('new-window', *window_args) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - window = proc.stdout[0] - - window = dict(zip(wformats, window.split('\t'))) - - # clear up empty dict - window = dict((k, v) for k, v in window.items() if v) - window = Window(session=self, **window) - - self.server._update_windows() - - return window - - def kill_window(self, target_window=None): - """``$ tmux kill-window``. - - Kill the current window or the window at ``target-window``. removing it - from any sessions to which it is linked. - - :param target_window: the ``target window``. - :type target_window: string - - """ - - if target_window: - if isinstance(target_window, int): - target = '-t%s:%d' % (self.get('session_name'), target_window) - else: - target = '-t%s' % target_window - - proc = self.cmd('kill-window', target) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - self.server._update_windows() - - def _list_windows(self): - windows = self.server._update_windows()._windows - - windows = [ - w for w in windows if w['session_id'] == self.get('session_id') - ] - - return windows - - @property - def _windows(self): - """Property / alias to return :meth:`~._list_windows`.""" - - return self._list_windows() - - def list_windows(self): - """Return a list of :class:`Window` from the ``tmux(1)`` session. - - :rtype: :class:`Window` - - """ - windows = [ - w for w in self._windows if w['session_id'] == self._session_id - ] - - return [Window(session=self, **window) for window in windows] - - @property - def windows(self): - """Property / alias to return :meth:`~.list_windows`.""" - return self.list_windows() - - #: Alias of :attr:`windows`. - children = windows - - def attached_window(self): - """Return active :class:`Window` object. - - :rtype: :class:`Window` - - """ - active_windows = [] - for window in self._windows: - if 'window_active' in window: - # for now window_active is a unicode - if window.get('window_active') == '1': - active_windows.append(Window(session=self, **window)) - else: - continue - - if len(active_windows) == int(1): - return active_windows[0] - else: - raise exc.LibTmuxException( - 'multiple active windows found. %s' % active_windows) - - if len(self._windows) == int(0): - raise exc.LibTmuxException('No Windows') - - def select_window(self, target_window): - """Return :class:`Window` selected via ``$ tmux select-window``. - - :param: window: ``target_window`` also 'last-window' (``-l``), - 'next-window' (``-n``), or 'previous-window' (``-p``) - :type window: integer - :rtype: :class:`Window` - - :todo: assure ``-l``, ``-n``, ``-p`` work. - - """ - - target = '-t%s' % target_window - - proc = self.cmd('select-window', target) - - if proc.stderr: - raise exc.LibTmuxException(proc.stderr) - - return self.attached_window() - - def attached_pane(self): - """Return active :class:`Pane` object.""" - - return self.attached_window().attached_pane() - - def set_option(self, option, value): - """Set option ``$ tmux set-option