Skip to content

Add --log-file option to load command #647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 22, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Here you can find the recent changes to tmuxp

current
-------
- Adding option to dump `load` output to log file
- *Insert changes/features/fixes for next release here*

tmuxp 1.6.2 (2020-11-08)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ skip-string-normalization = true

[tool.poetry]
name = "tmuxp"
version = "1.6.2"
version = "1.6.3"
description = "tmux session manager"
license = "MIT"
authors = ["Tony Narlock <[email protected]>"]
Expand Down
27 changes: 27 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,33 @@ def test_load_zsh_autotitle_warning(cli_args, tmpdir, monkeypatch):
assert 'Please set' not in result.output


@pytest.mark.parametrize(
"cli_args",
[
(['load', '.', '--log-file', 'log.txt']),
],
)
def test_load_log_file(cli_args, tmpdir, monkeypatch):
# create dummy tmuxp yaml that breaks to prevent actually loading tmux
tmpdir.join('.tmuxp.yaml').write(
"""
session_name: hello
"""
)
tmpdir.join('.oh-my-zsh').ensure(dir=True)
monkeypatch.setenv('HOME', str(tmpdir))

with tmpdir.as_cwd():
print('tmpdir: {0}'.format(tmpdir))
runner = CliRunner()

# If autoconfirm (-y) no need to prompt y
input_args = 'y\ny\n' if '-y' not in cli_args else ''

runner.invoke(cli.cli, cli_args, input=input_args)
assert 'Loading' in tmpdir.join('log.txt').open().read()


@pytest.mark.parametrize("cli_cmd", ['shell', ('shell', '--pdb')])
@pytest.mark.parametrize(
"cli_args,inputs,env,expected_output",
Expand Down
2 changes: 1 addition & 1 deletion tmuxp/__about__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__title__ = 'tmuxp'
__package_name__ = 'tmuxp'
__version__ = '1.6.2'
__version__ = '1.6.3'
__description__ = 'tmux session manager'
__email__ = '[email protected]'
__author__ = 'Tony Narlock'
Expand Down
73 changes: 45 additions & 28 deletions tmuxp/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ def get_cwd():
return os.getcwd()


def tmuxp_echo(message=None, log_level='INFO', **click_kwargs):
"""
Combines logging.log and click.echo
"""
logger.log(log.LOG_LEVELS[log_level], click.unstyle(message))
click.echo(message, **click_kwargs)


def get_config_dir():
"""
Return tmuxp configuration directory.
Expand Down Expand Up @@ -246,8 +254,8 @@ def scan_config_argument(ctx, param, value, config_dir=None):
config_dir = config_dir()

if not config:
click.echo("Enter at least one CONFIG")
click.echo(ctx.get_help(), color=ctx.color)
tmuxp_echo("Enter at least one CONFIG")
tmuxp_echo(ctx.get_help(), color=ctx.color)
ctx.exit()

if isinstance(value, string_types):
Expand Down Expand Up @@ -357,11 +365,14 @@ def scan_config(config, config_dir=None):
]

if len(candidates) > 1:
click.secho(
'Multiple .tmuxp.{yml,yaml,json} configs in %s' % dirname(config),
fg="red",
tmuxp_echo(
click.style(
'Multiple .tmuxp.{yml,yaml,json} configs in %s'
% dirname(config),
fg="red",
)
)
click.echo(
tmuxp_echo(
click.wrap_text(
'This is undefined behavior, use only one. '
'Use file names e.g. myproject.json, coolproject.yaml. '
Expand Down Expand Up @@ -505,6 +516,11 @@ def load_workspace(
# get the canonical path, eliminating any symlinks
config_file = os.path.realpath(config_file)

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

# kaptan allows us to open a yaml or json file as a dict
sconfig = kaptan.Kaptan()
sconfig = sconfig.import_config(config_file).get()
Expand All @@ -525,7 +541,7 @@ def load_workspace(
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)
tmuxp_echo('%s is empty or parsed no config data' % config_file, err=True)
return

session_name = sconfig['session_name']
Expand All @@ -545,11 +561,6 @@ def load_workspace(
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
Expand Down Expand Up @@ -586,8 +597,8 @@ def load_workspace(
except exc.TmuxpException as e:
import traceback

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

choice = click.prompt(
'Error loading workspace. (k)ill, (a)ttach, (d)etach?',
Expand All @@ -597,7 +608,7 @@ def load_workspace(

if choice == 'k':
builder.session.kill_session()
click.echo('Session killed.')
tmuxp_echo('Session killed.')
elif choice == 'a':
if 'TMUX' in os.environ:
builder.session.switch_client()
Expand Down Expand Up @@ -625,12 +636,12 @@ def cli(log_level):
try:
has_minimum_version()
except TmuxCommandNotFound:
click.echo('tmux not found. tmuxp requires you install tmux first.')
tmuxp_echo('tmux not found. tmuxp requires you install tmux first.')
sys.exit()
except exc.TmuxpException as e:
click.echo(e, err=True)
tmuxp_echo(e, err=True)
sys.exit()
setup_logger(level=log_level.upper())
setup_logger(logger=logger, level=log_level.upper())


def setup_logger(logger=None, level='INFO'):
Expand All @@ -649,12 +660,12 @@ def setup_logger(logger=None, level='INFO'):
logger = logging.getLogger()

if not logger.handlers: # setup logger handlers
channel = logging.StreamHandler()
channel.setFormatter(log.DebugLogFormatter())

# channel = logging.StreamHandler()
# channel.setFormatter(log.DebugLogFormatter())
# channel.setFormatter(log.LogFormatter())

logger.setLevel(level)
logger.addHandler(channel)
# logger.addHandler(channel)


def startup(config_dir):
Expand Down Expand Up @@ -875,6 +886,7 @@ def command_freeze(session_name, socket_name, socket_path, force):
flag_value=88,
help='Like -2, but indicates that the terminal supports 88 colours.',
)
@click.option('--log-file', help='File to log errors/output to')
def command_load(
ctx,
config,
Expand All @@ -884,6 +896,7 @@ def command_load(
answer_yes,
detached,
colors,
log_file,
):
"""Load a tmux workspace from each CONFIG.

Expand All @@ -908,6 +921,10 @@ def command_load(
detached mode.
"""
util.oh_my_zsh_auto_title()
if log_file:
logfile_handler = logging.FileHandler(log_file)
logfile_handler.setFormatter(log.LogFormatter())
logger.addHandler(logfile_handler)

tmux_options = {
'socket_name': socket_name,
Expand All @@ -919,8 +936,8 @@ def command_load(
}

if not config:
click.echo("Enter at least one CONFIG")
click.echo(ctx.get_help(), color=ctx.color)
tmuxp_echo("Enter at least one CONFIG")
tmuxp_echo(ctx.get_help(), color=ctx.color)
ctx.exit()

if isinstance(config, string_types):
Expand Down Expand Up @@ -962,7 +979,7 @@ def import_config(configfile, importfunc):
else:
sys.exit('Unknown config format.')

click.echo(
tmuxp_echo(
newconfig + '---------------------------------------------------------------'
'\n'
'Configuration import does its best to convert files.\n'
Expand All @@ -984,9 +1001,9 @@ def import_config(configfile, importfunc):
buf.write(newconfig)
buf.close()

click.echo('Saved to %s.' % dest)
tmuxp_echo('Saved to %s.' % dest)
else:
click.echo(
tmuxp_echo(
'tmuxp has examples in JSON and YAML format at '
'<http://tmuxp.git-pull.com/examples.html>\n'
'View tmuxp docs at <http://tmuxp.git-pull.com/>'
Expand Down Expand Up @@ -1125,4 +1142,4 @@ def format_tmux_resp(std_resp):
% format_tmux_resp(tmux_cmd('show-window-options', '-g')),
]

click.echo('\n'.join(output))
tmuxp_echo('\n'.join(output))
12 changes: 11 additions & 1 deletion tmuxp/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@
'CRITICAL': Fore.RED,
}

LOG_LEVELS = {
'CRITICAL': 50,
'ERROR': 40,
'WARNING': 30,
'INFO': 20,
'DEBUG': 10,
'NOTSET': 0,
}


def default_log_template(self, record):
"""
Expand Down Expand Up @@ -89,8 +98,9 @@ def format(self, record):

prefix = self.template(record) % record.__dict__

parts = prefix.split(record.message)
formatted = prefix + " " + record.message
return formatted.replace("\n", "\n ")
return formatted.replace("\n", "\n" + parts[0] + " ")


def debug_log_template(self, record):
Expand Down