1"""Utility and helper methods for tmuxp.
2
3tmuxp.shell
4~~~~~~~~~~~
5
6"""
7import logging
8import os
9
10logger = logging.getLogger(__name__)
11
12
13def has_ipython():
14    try:
15        from IPython import start_ipython  # NOQA F841
16    except ImportError:
17        try:
18            from IPython.Shell import IPShell  # NOQA F841
19        except ImportError:
20            return False
21
22    return True
23
24
25def has_ptpython():
26    try:
27        from ptpython.repl import embed, run_config  # NOQA F841
28    except ImportError:
29        try:
30            from prompt_toolkit.contrib.repl import embed, run_config  # NOQA F841
31        except ImportError:
32            return False
33
34    return True
35
36
37def has_ptipython():
38    try:
39        from ptpython.ipython import embed  # NOQA F841
40        from ptpython.repl import run_config  # NOQA F841
41    except ImportError:
42        try:
43            from prompt_toolkit.contrib.ipython import embed  # NOQA F841
44            from prompt_toolkit.contrib.repl import run_config  # NOQA F841
45        except ImportError:
46            return False
47
48    return True
49
50
51def has_bpython():
52    try:
53        from bpython import embed  # NOQA F841
54    except ImportError:
55        return False
56    return True
57
58
59def detect_best_shell():
60    if has_ptipython():
61        return 'ptipython'
62    elif has_ptpython():
63        return 'ptpython'
64    elif has_ipython():
65        return 'ipython'
66    elif has_bpython():
67        return 'bpython'
68    return 'code'
69
70
71def get_bpython(options, extra_args=None):
72    if extra_args is None:
73        extra_args = {}
74
75    from bpython import embed  # NOQA F841
76
77    def launch_bpython():
78        imported_objects = get_launch_args(**options)
79        kwargs = {}
80        if extra_args:
81            kwargs['args'] = extra_args
82        embed(imported_objects, **kwargs)
83
84    return launch_bpython
85
86
87def get_ipython_arguments():
88    ipython_args = 'IPYTHON_ARGUMENTS'
89    return os.environ.get(ipython_args, '').split()
90
91
92def get_ipython(options, **extra_args):
93    try:
94        from IPython import start_ipython
95
96        def launch_ipython():
97            imported_objects = get_launch_args(**options)
98            ipython_arguments = extra_args or get_ipython_arguments()
99            start_ipython(argv=ipython_arguments, user_ns=imported_objects)
100
101        return launch_ipython
102    except ImportError:
103        # IPython < 0.11
104        # Explicitly pass an empty list as arguments, because otherwise
105        # IPython would use sys.argv from this script.
106        # Notebook not supported for IPython < 0.11.
107        from IPython.Shell import IPShell
108
109        def launch_ipython():
110            imported_objects = get_launch_args(**options)
111            shell = IPShell(argv=[], user_ns=imported_objects)
112            shell.mainloop()
113
114        return launch_ipython
115
116
117def get_ptpython(options, vi_mode=False):
118    try:
119        from ptpython.repl import embed, run_config
120    except ImportError:
121        from prompt_toolkit.contrib.repl import embed, run_config
122
123    def launch_ptpython():
124        imported_objects = get_launch_args(**options)
125        history_filename = os.path.expanduser('~/.ptpython_history')
126        embed(
127            globals=imported_objects,
128            history_filename=history_filename,
129            vi_mode=vi_mode,
130            configure=run_config,
131        )
132
133    return launch_ptpython
134
135
136def get_ptipython(options, vi_mode=False):
137    """Based on django-extensions
138
139    Run renamed to launch, get_imported_objects renamed to get_launch_args
140    """
141    try:
142        from ptpython.ipython import embed
143        from ptpython.repl import run_config
144    except ImportError:
145        # prompt_toolkit < v0.27
146        from prompt_toolkit.contrib.ipython import embed
147        from prompt_toolkit.contrib.repl import run_config
148
149    def launch_ptipython():
150        imported_objects = get_launch_args(**options)
151        history_filename = os.path.expanduser('~/.ptpython_history')
152        embed(
153            user_ns=imported_objects,
154            history_filename=history_filename,
155            vi_mode=vi_mode,
156            configure=run_config,
157        )
158
159    return launch_ptipython
160
161
162def get_launch_args(**kwargs):
163    import libtmux
164
165    return {
166        'libtmux': libtmux,
167        'Server': libtmux.Server,
168        'Session': libtmux.Session,
169        'Window': libtmux.Window,
170        'Pane': libtmux.Pane,
171        'server': kwargs.get('server'),
172        'session': kwargs.get('session'),
173        'window': kwargs.get('window'),
174        'pane': kwargs.get('pane'),
175    }
176
177
178def get_code(use_pythonrc, imported_objects):
179    import code
180
181    try:
182        # Try activating rlcompleter, because it's handy.
183        import readline
184    except ImportError:
185        pass
186    else:
187        # We don't have to wrap the following import in a 'try', because
188        # we already know 'readline' was imported successfully.
189        import rlcompleter
190
191        readline.set_completer(rlcompleter.Completer(imported_objects).complete)
192        # Enable tab completion on systems using libedit (e.g. macOS).
193        # These lines are copied from Lib/site.py on Python 3.4.
194        readline_doc = getattr(readline, '__doc__', '')
195        if readline_doc is not None and 'libedit' in readline_doc:
196            readline.parse_and_bind("bind ^I rl_complete")
197        else:
198            readline.parse_and_bind("tab:complete")
199
200    # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
201    # conventions and get $PYTHONSTARTUP first then .pythonrc.py.
202    if use_pythonrc:
203        for pythonrc in set(
204            [os.environ.get("PYTHONSTARTUP"), os.path.expanduser('~/.pythonrc.py')]
205        ):
206            if not pythonrc:
207                continue
208            if not os.path.isfile(pythonrc):
209                continue
210            with open(pythonrc) as handle:
211                pythonrc_code = handle.read()
212            # Match the behavior of the cpython shell where an error in
213            # PYTHONSTARTUP prints an exception and continues.
214            exec(compile(pythonrc_code, pythonrc, 'exec'), imported_objects)
215
216    def launch_code():
217        code.interact(local=imported_objects)
218
219    return launch_code
220
221
222def launch(shell='best', use_pythonrc=False, use_vi_mode=False, **kwargs):
223    # Also allowing passing shell='code' to force using code.interact
224    imported_objects = get_launch_args(**kwargs)
225
226    if shell == 'best':
227        shell = detect_best_shell()
228
229    if shell == 'ptipython':
230        launch = get_ptipython(options=kwargs, vi_mode=use_vi_mode)
231    elif shell == 'ptpython':
232        launch = get_ptpython(options=kwargs, vi_mode=use_vi_mode)
233    elif shell == 'ipython':
234        launch = get_ipython(options=kwargs)
235    elif shell == 'bpython':
236        launch = get_bpython(options=kwargs)
237    else:
238        launch = get_code(use_pythonrc=use_pythonrc, imported_objects=imported_objects)
239
240    launch()
241