Source code for xdev.desktop_interaction

"""
Functions related to interacting with data via an OS Desktop GUI.
"""
from os.path import normpath
from os.path import exists
from os.path import sys
import types
import os
import warnings
import ubelt as ub


[docs] def _coerce_editable_fpath(target): """ Rules for coercing inputs to ``editfile`` into a path. Args: target (str | PathLike | ModuleType | ClassType | FunctionType): something coercable to a path or module path Returns: ub.Path """ fpath = None if isinstance(target, str): # String inputs are ambiguous. Ambiguous case, is this a module name or a full path? if exists(target): fpath = ub.Path(target) else: # Perhaps this is a module name we want to edit? modpath = ub.modname_to_modpath(target) if modpath is not None: fpath = ub.Path(modpath) elif isinstance(target, types.ModuleType): fpath = ub.Path(target.__file__) elif isinstance(target, os.PathLike): fpath = ub.Path(target) elif hasattr(target, '__module__'): fpath = ub.Path(sys.modules[target.__module__].__file__) else: raise TypeError(f"Unable to coerce {target} into a file path") if fpath is None: raise Exception(f"Unable to interpret {target} as a module name or file path") # Resolve a pyc file to a py file if possible. if fpath.suffix == '.pyc': fpath_py = fpath.augment(ext='.py') if fpath_py.exists(): fpath = fpath_py return fpath
[docs] def _find_editor(): """ Try to find an editor program. """ editor_name = os.environ.get('VISUAL', None) editor_fpath = editor_name and ub.find_exe(editor_name) if editor_fpath is None: if editor_name is not None: warnings.warn('User specified VISUAL={editor_name}, but it does not exist.', UserWarning) # Try and fallback on commonly installed editor # TODO: add more editors in an opinionated order editor_candidates = [ 'gvim', 'code', # visual studio code 'gedit', 'TextEdit' 'Notepad', ] for cand_name in editor_candidates: cand_fpath = ub.find_exe(cand_name) if cand_fpath: editor_fpath = cand_fpath break if editor_fpath is None: raise IOError('Unable to find an existing VISUAL editor') return editor_fpath
[docs] def editfile(fpath, verbose=True): """ Opens a file or code corresponding to a live python object in your preferred visual editor. This function is mainly useful in an interactive IPython session. The visual editor is determined by the ``VISUAL`` environment variable. If this is not specified it defaults to gvim. Args: fpath (PathLike | ModuleType | str): a file path or python module / function. If the input is a string it will interpret it either as a Path or a module name. Ambiguity is resolved by choosing a path if the string resolves to an existing path, and then checking if the string corresponds to a module name. verbose (int): verbosity Example: >>> # xdoctest: +SKIP >>> # This test interacts with a GUI frontend, not sure how to test. >>> import xdev >>> ub.editfile(xdev.misc.__file__) >>> ub.editfile(xdev) >>> ub.editfile(xdev.editfile) """ fpath = _coerce_editable_fpath(fpath) if verbose: print('[xdev] editfile("{}")'.format(fpath)) editor_fpath = _find_editor() if not exists(fpath): raise IOError('Cannot start nonexistant file: %r' % fpath) if verbose: print('[xdev] using "{}"'.format(editor_fpath)) ub.cmd([editor_fpath, fpath], fpath, detach=True)
[docs] def view_directory(dpath=None, verbose=False): """ View a directory in the operating system file browser. Currently supports windows explorer, mac open, and linux nautlius. Args: dpath (PathLike | None): directory name verbose (bool): verbosity """ if dpath is None: dpath = os.getcwd() dpath = os.path.normpath(dpath) if verbose: print('[xdev] view_directory({!r}) '.format(dpath)) if not exists(dpath): raise Exception('Cannot view nonexistant directory: {!r}'.format(dpath)) if False: try: import vimtk.xctrl import vimtk.cplat_ctrl if vimtk.xctrl.is_directory_open(dpath): if verbose: print('[xdev] dpath={!r} is already open'.format(dpath)) win = vimtk.cplat_ctrl.Window.find('Nautilus.*' + os.path.basename(dpath)) win.focus() return except Exception: pass if ub.LINUX: info = ub.cmd(('nautilus', dpath), detach=True, verbose=verbose) elif ub.DARWIN: info = ub.cmd(('open', dpath), detach=True, verbose=verbose) elif ub.WIN32: info = ub.cmd(('explorer.exe', dpath), detach=True, verbose=verbose) else: raise RuntimeError('Unknown Platform') if info is not None: if not info['proc']: raise Exception('startfile failed')
[docs] def startfile(fpath, verbose=True): """ Uses default program defined by the system to open a file. This is done via `os.startfile` on windows, `open` on mac, and `xdg-open` on linux. Args: fpath (PathLike): a file to open using the program associated with the files extension type. verbose (int): verbosity References: http://stackoverflow.com/questions/2692873/quote-posix Example: >>> # xdoctest: +SKIP >>> # This test interacts with a GUI frontend, not sure how to test. >>> import ubelt as ub >>> base = ub.ensure_app_cache_dir('ubelt') >>> fpath1 = join(base, 'test_open.txt') >>> ub.touch(fpath1) >>> proc = ub.startfile(fpath1) """ if verbose: print('[xdev] startfile("{}")'.format(fpath)) fpath = normpath(fpath) if not exists(fpath): raise Exception('Cannot start nonexistant file: {!r}'.format(fpath)) if not ub.WIN32: import shlex fpath = shlex.quote(fpath) if ub.LINUX: info = ub.cmd(('xdg-open', fpath), detach=True, verbose=verbose) elif ub.DARWIN: info = ub.cmd(('open', fpath), detach=True, verbose=verbose) elif ub.WIN32: os.startfile(fpath) info = None else: raise RuntimeError('Unknown Platform') if info is not None: if not info['proc']: raise Exception('startfile failed')