Skip to content
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

Allow configuration of the log message #311

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
28 changes: 18 additions & 10 deletions sh.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,17 @@ def friendly_truncate(s, max_len):
return s


def default_logger_str(cmd, call_args):
friendly_cmd = friendly_truncate(cmd, 20)
friendly_call_args = friendly_truncate(str(call_args), 20)

# we're setting up the logger string here, instead of __repr__ because
# we reserve __repr__ to behave as if it was evaluating the child
# process's output
return "<Command %r call_args %s>" % (friendly_cmd, friendly_call_args)



class RunningCommand(object):
""" this represents an executing Command object. it is returned as the
result of __call__() being executed on a Command instance. this creates a
Expand All @@ -420,17 +431,10 @@ def __init__(self, cmd, call_args, stdin, stdout, stderr):
else:
self.ran = " ".join(cmd)


friendly_cmd = friendly_truncate(self.ran, 20)
friendly_call_args = friendly_truncate(str(call_args), 20)

# we're setting up the logger string here, instead of __repr__ because
# we reserve __repr__ to behave as if it was evaluating the child
# process's output
logger_str = "<Command %r call_args %s>" % (friendly_cmd,
friendly_call_args)

log_msg_cmd = call_args.get('log_msg') or default_logger_str
logger_str = log_msg_cmd(self.ran, call_args)
self.log = Logger("command", logger_str)

self.call_args = call_args
self.cmd = cmd

Expand Down Expand Up @@ -745,6 +749,10 @@ class Command(object):
# a tuple (rows, columns) of the desired size of both the stdout and
# stdin ttys, if ttys are being used
"tty_size": (20, 80),

# a callable that produces a log message from an argument tuple of the
# command and the args
"log_msg": None
}

# these are arguments that cannot be called together, because they wouldn't
Expand Down
69 changes: 49 additions & 20 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from os.path import exists, join, realpath
import unittest
import tempfile
import fnmatch
import logging
import sys
import sh
import platform
Expand All @@ -17,8 +19,26 @@
if IS_PY3:
unicode = str
python = sh.Command(sh.which("python%d.%d" % sys.version_info[:2]))
from io import StringIO
from io import BytesIO as cStringIO
else:
from sh import python
from StringIO import StringIO
from cStringIO import StringIO as cStringIO


if hasattr(logging, 'NullHandler'):
NullHandler = logging.NullHandler
else:
class NullHandler(logging.Handler):
def handle(self, record):
pass

def emit(self, record):
pass

def createLock(self):
self.lock = None


THIS_DIR = os.path.dirname(os.path.abspath(__file__))
Expand Down Expand Up @@ -1217,12 +1237,6 @@ def test_tty_output(self):

def test_stringio_output(self):
from sh import echo
if IS_PY3:
from io import StringIO
from io import BytesIO as cStringIO
else:
from StringIO import StringIO
from cStringIO import StringIO as cStringIO

out = StringIO()
echo("-n", "testing 123", _out=out)
Expand All @@ -1236,13 +1250,6 @@ def test_stringio_output(self):
def test_stringio_input(self):
from sh import cat

if IS_PY3:
from io import StringIO
from io import BytesIO as cStringIO
else:
from StringIO import StringIO
from cStringIO import StringIO as cStringIO

input = StringIO()
input.write("herpderp")
input.seek(0)
Expand Down Expand Up @@ -1502,13 +1509,6 @@ def s(fn): str(fn())
def test_shared_secial_args(self):
import sh

if IS_PY3:
from io import StringIO
from io import BytesIO as cStringIO
else:
from StringIO import StringIO
from cStringIO import StringIO as cStringIO

out1 = sh.ls('.')
out2 = StringIO()
sh_new = sh(_out=out2)
Expand Down Expand Up @@ -1923,6 +1923,31 @@ def test_signal_exception_aliases(self):

self.assertEqual(sig, SignalException_SIGQUIT)

def test_change_log_message(self):
py = create_tmp_test("""
print("cool")
""")
def log_msg(cmd, call_args):
return 'Hi! I ran <COMMAND %s>' % (cmd,)

buf = StringIO()
handler = logging.StreamHandler(buf)
logger = logging.getLogger('sh')
try:
logger.addHandler(handler)
with sh.args(log_msg=log_msg):
python(py.name, "meow", "bark")
finally:
logger.removeHandler(handler)

loglines = buf.getvalue().split('\n')
self.assertTrue(loglines, 'Log handler captured no messages?')
self.assertTrue(
fnmatch.fnmatch(loglines[0],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fnmatch confused me, but I guess it is because <Command will contain the temp file path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that was why, and it neatly works around the python vs python3.5 problem, too.

'Hi! I ran <COMMAND *python* meow bark>: starting process'),

'Log line did not match: %r' % (loglines[0],))


class StreamBuffererTests(unittest.TestCase):
def test_unbuffered(self):
Expand Down Expand Up @@ -1953,6 +1978,10 @@ def test_chunk_buffered(self):


if __name__ == "__main__":
root = logging.getLogger()
root.setLevel(logging.DEBUG)
root.addHandler(NullHandler())

# if we're running a specific test, we can let unittest framework figure out
# that test and run it itself. it will also handle setting the return code
# of the process if any tests error or fail
Expand Down