Skip to content

Commit

Permalink
Merge pull request #82 from tony/project-dirs-relativity
Browse files Browse the repository at this point in the history
Merge pull request for loading projects outside of current working directory. Support for expanding env variables and tildes in ``start_directory``.
  • Loading branch information
tony committed Aug 6, 2014
2 parents 3887bdc + f994684 commit 8a2f21a
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 12 deletions.
9 changes: 9 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ Here you can find the recent changes to tmuxp.

- [config] :meth:`config.expand` now resolves directories in configuration
via :py:func:`os.path.expanduser` and :py:func:`os.path.expandvars`.
- [config] :meth:`config.expandpath` for helping resolve paths.
- [builder] [cli] improved support for loading tmuxp project files from
outside current working directory. e.g.

.. code-block:: bash
$ tmuxp load /path/to/my/project/.tmuxp.yaml
Will behave better with relative directories.

0.1.11
------
Expand Down
2 changes: 2 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ Import and export

.. automethod:: tmuxp.config.validate_schema

.. automethod:: tmuxp.config.expandpath

.. automethod:: tmuxp.config.expand

.. automethod:: tmuxp.config.inline
Expand Down
28 changes: 25 additions & 3 deletions tmuxp/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ def in_cwd():

return configs

def expandpath(_path):
"""Return expanded path based on user's ``$HOME`` and ``env``.
:py:func:`os.path.expanduser` and :py:func:`os.path.expandvars`
:param _path: path to expand
:type _path: string
:returns: expanded path
:rtype: string
"""
return os.path.expandvars(os.path.expanduser(_path))

def inline(sconf):
""" Return config in inline form, opposite of :meth:`config.expand`.
Expand Down Expand Up @@ -139,7 +151,7 @@ def inline(sconf):
return sconf


def expand(sconf, cwd=None):
def expand(sconf, cwd=None, parent=None):
"""Return config with shorthand and inline properties expanded.
This is necessary to keep the code in the :class:`WorkspaceBuilder` clean
Expand Down Expand Up @@ -172,11 +184,21 @@ def expand(sconf, cwd=None):
# Any config section, session, window, pane that can contain the
# 'shell_command' value
if 'start_directory' in sconf:
sconf['start_directory'] = expandpath(sconf['start_directory'])
start_path = sconf['start_directory']
if any(start_path.startswith(a) for a in ['.', './']):
# if window has a session, or pane has a window with a
# start_directory of . or ./, make sure the start_directory can be
# relative to the parent.
#
# This is for the case where you may be loading a config from
# outside your shell current directory.
if parent:
cwd = parent['start_directory']
start_path = os.path.normpath(os.path.join(cwd, start_path))
sconf['start_directory'] = start_path


if 'before_script' in sconf:
before_script = sconf['before_script']
if any(before_script.startswith(a) for a in ['.', './']):
Expand All @@ -198,7 +220,7 @@ def expand(sconf, cwd=None):
# recurse into window and pane config items
if 'windows' in sconf:
sconf['windows'] = [
expand(window) for window in sconf['windows']
expand(window, parent=sconf) for window in sconf['windows']
]
elif 'panes' in sconf:

Expand Down Expand Up @@ -235,7 +257,7 @@ def expand(sconf, cwd=None):
p['shell_command'] = []

pconf.update(p)
sconf['panes'] = [expand(pane) for pane in sconf['panes']]
sconf['panes'] = [expand(pane, parent=sconf) for pane in sconf['panes']]

return sconf

Expand Down
18 changes: 10 additions & 8 deletions tmuxp/testsuite/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class ExpandTest(TestCase):

after_config = {
'session_name': 'sampleconfig',
'start_directory': '~',
'start_directory': os.path.expanduser('~'),
'windows': [
{
'window_name': 'editor',
Expand Down Expand Up @@ -246,19 +246,19 @@ class ExpandTest(TestCase):
]
},
{
'start_directory': os.path.abspath('./'),
'start_directory': os.path.normpath(os.path.join(os.path.join(os.path.expanduser('~'), './'))),
'panes': [
{'shell_command': ['pwd']}
]
},
{
'start_directory': os.path.abspath('./asdf/'),
'start_directory': os.path.normpath(os.path.join(os.path.join(os.path.expanduser('~'), './asdf'))),
'panes': [
{'shell_command': ['pwd']}
]
},
{
'start_directory': os.path.abspath('../'),
'start_directory': os.path.normpath(os.path.join(os.path.expanduser('~'), '../')),
'panes': [
{'shell_command': ['pwd']}
]
Expand Down Expand Up @@ -317,7 +317,7 @@ def test_no_window_name(self):

expanded_yaml = """
session_name: sampleconfig
start_directory: '~'
start_directory: {HOME}
windows:
- window_name: focused window
layout: main-horizontal
Expand Down Expand Up @@ -348,7 +348,9 @@ def test_no_window_name(self):
focus: true
- shell_command: []
- shell_command: []
"""
""".format(
HOME=os.path.expanduser('~')
)

self.maxDiff = None

Expand Down Expand Up @@ -622,7 +624,7 @@ class ShellCommandBeforeTest(TestCase):
'windows': [
{
'window_name': 'editor',
'start_directory': '~',
'start_directory': os.path.expanduser('~'),
'shell_command_before': ['source .env/bin/activate'],
'panes': [
{
Expand Down Expand Up @@ -676,7 +678,7 @@ class ShellCommandBeforeTest(TestCase):
'windows': [
{
'window_name': 'editor',
'start_directory': '~',
'start_directory': os.path.expanduser('~'),
'shell_command_before': ['source .env/bin/activate'],
'panes': [
{
Expand Down
115 changes: 114 additions & 1 deletion tmuxp/testsuite/workspacebuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@ def tearDown(self):

def test_start_directory(self):

start_directory = os.getcwd()

sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(self.yaml_config).get()
sconfig = config.expand(sconfig)
Expand All @@ -441,7 +443,117 @@ def test_start_directory(self):
builder.build(session=self.session)

assert(self.session == builder.session)
dirs = ['/usr/bin', '/dev', '/tmp/foo bar', '/usr', os.getcwd()]
dirs = ['/usr/bin', '/dev', '/tmp/foo bar', '/usr', '/usr']
for path, window in zip(dirs, self.session.windows):
for p in window.panes:
for i in range(60):
p.server._update_panes()
if p.get('pane_current_path') == path:
break
time.sleep(.2)

self.assertEqual(p.get('pane_current_path'), path)


class StartDirectoryRelativeTest(TmuxTestCase):
"""Same as above test, but with relative start directory, mimicing
loading it from a location of project file. Like::
$ tmuxp load ~/workspace/myproject/.tmuxp.yaml
instead of::
$ cd ~/workspace/myproject/.tmuxp.yaml
$ tmuxp load .
"""

yaml_config = """
session_name: sampleconfig
start_directory: ./
windows:
- window_name: supposed to be /usr/bin
start_directory: '/usr/bin'
layout: main-horizontal
options:
main-pane-height: 50
panes:
- shell_command:
- echo "hey"
- shell_command:
- echo "moo"
- window_name: support to be /dev
start_directory: '/dev'
layout: main-horizontal
panes:
- shell_command:
- pwd
- shell_command:
- echo "hey"
- shell_command:
- echo "moo"
- window_name: cwd containing a space
start_directory: /tmp/foo bar
layout: main-horizontal
panes:
- shell_command:
- pwd
- shell_command:
- echo "hey"
- shell_command:
- echo "moo"
- window_name: testsa3
layout: main-horizontal
panes:
- shell_command:
- pwd
- shell_command:
- echo "hey"
- shell_command:
- echo "moo3"
- window_name: cwd relative to config file
layout: main-horizontal
start_directory: ./
panes:
- shell_command:
- pwd
- shell_command:
- echo "hey"
- shell_command:
- echo "moo3"
"""

def setUp(self):
super(StartDirectoryRelativeTest, self).setUp()
if not os.path.exists('/tmp/foo bar') and not os.path.exists('/tmp/testRelConfigDir'):
os.mkdir('/tmp/foo bar')
os.mkdir('/tmp/testRelConfigDir')
self._temp_dir_created = True
else:
self._temp_dir_created = False

def tearDown(self):
super(StartDirectoryRelativeTest, self).tearDown()
if self._temp_dir_created:
os.rmdir('/tmp/foo bar')
os.rmdir('/tmp/testRelConfigDir')

def test_start_directory(self):

start_directory = os.getcwd()

sconfig = kaptan.Kaptan(handler='yaml')
sconfig = sconfig.import_config(self.yaml_config).get()
# the second argument of os.getcwd() mimics the behavior
# the CLI loader will do, but it passes in the config file's location.
sconfig = config.expand(sconfig, '/tmp/testRelConfigDir')
sconfig = config.trickle(sconfig)

builder = WorkspaceBuilder(sconf=sconfig)
builder.build(session=self.session)

assert(self.session == builder.session)
dirs = ['/usr/bin', '/dev', '/tmp/foo bar', '/tmp/testRelConfigDir']
for path, window in zip(dirs, self.session.windows):
for p in window.panes:
for i in range(60):
Expand Down Expand Up @@ -660,6 +772,7 @@ def suite():
suite.addTest(unittest.makeSuite(FocusAndPaneIndexTest))
suite.addTest(unittest.makeSuite(PaneOrderingTest))
suite.addTest(unittest.makeSuite(StartDirectoryTest))
suite.addTest(unittest.makeSuite(StartDirectoryRelativeTest))
suite.addTest(unittest.makeSuite(ThreePaneTest))
suite.addTest(unittest.makeSuite(TwoPaneTest))
suite.addTest(unittest.makeSuite(WindowAutomaticRename))
Expand Down

0 comments on commit 8a2f21a

Please sign in to comment.