summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/logging/__init__.py60
-rw-r--r--Lib/subprocess.py71
-rw-r--r--Lib/test/test_logging.py31
-rw-r--r--Lib/test/test_subprocess.py34
4 files changed, 179 insertions, 17 deletions
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 499c954..cce6ea0 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -23,7 +23,8 @@ Copyright (C) 2001-2008 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging' and log away!
"""
-import sys, os, time, io, traceback
+import sys, os, time, io, traceback, warnings
+
__all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
'FATAL', 'FileHandler', 'Filter', 'Filterer', 'Formatter', 'Handler',
'INFO', 'LogRecord', 'Logger', 'Manager', 'NOTSET', 'PlaceHolder',
@@ -42,8 +43,8 @@ except ImportError:
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
__status__ = "production"
-__version__ = "0.5.0.5"
-__date__ = "24 January 2008"
+__version__ = "0.5.0.6"
+__date__ = "03 December 2008"
#---------------------------------------------------------------------------
# Miscellaneous module data
@@ -1483,3 +1484,56 @@ except ImportError: # for Python versions < 2.0
old_exit(status)
sys.exit = exithook
+
+# Null handler
+
+class NullHandler(Handler):
+ """
+ This handler does nothing. It's intended to be used to avoid the
+ "No handlers could be found for logger XXX" one-off warning. This is
+ important for library code, which may contain code to log events. If a user
+ of the library does not configure logging, the one-off warning might be
+ produced; to avoid this, the library developer simply needs to instantiate
+ a NullHandler and add it to the top-level logger of the library module or
+ package.
+ """
+ def emit(self, record):
+ pass
+
+# Warnings integration
+
+_warnings_showwarning = None
+
+def _showwarning(message, category, filename, lineno, file=None, line=None):
+ """
+ Implementation of showwarnings which redirects to logging, which will first
+ check to see if the file parameter is None. If a file is specified, it will
+ delegate to the original warnings implementation of showwarning. Otherwise,
+ it will call warnings.formatwarning and will log the resulting string to a
+ warnings logger named "py.warnings" with level logging.WARNING.
+ """
+ if file is not None:
+ if _warnings_showwarning is not None:
+ _warnings_showwarning(message, category, filename, lineno, file, line)
+ else:
+ s = warnings.formatwarning(message, category, filename, lineno, line)
+ logger = getLogger("py.warnings")
+ if not logger.handlers:
+ logger.addHandler(NullHandler())
+ logger.warning("%s", s)
+
+def captureWarnings(capture):
+ """
+ If capture is true, redirect all warnings to the logging package.
+ If capture is False, ensure that warnings are not redirected to logging
+ but to their original destinations.
+ """
+ global _warnings_showwarning
+ if capture:
+ if _warnings_showwarning is None:
+ _warnings_showwarning = warnings.showwarning
+ warnings.showwarning = _showwarning
+ else:
+ if _warnings_showwarning is not None:
+ warnings.showwarning = _warnings_showwarning
+ _warnings_showwarning = None
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index ac92185..b47ebb8 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -104,7 +104,7 @@ appearance of the main window and priority for the new process.
(Windows only)
-This module also defines four shortcut functions:
+This module also defines some shortcut functions:
call(*popenargs, **kwargs):
Run command with arguments. Wait for command to complete, then
@@ -151,6 +151,17 @@ getoutput(cmd):
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'
+check_output(*popenargs, **kwargs):
+ Run command with arguments and return its output as a byte string.
+
+ If the exit code was non-zero it raises a CalledProcessError. The
+ CalledProcessError object will have the return code in the returncode
+ attribute and output in the output attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ output = subprocess.check_output(["ls", "-l", "/dev/null"])
+
Exceptions
----------
@@ -166,8 +177,8 @@ should prepare for OSErrors.
A ValueError will be raised if Popen is called with invalid arguments.
-check_call() will raise CalledProcessError, if the called process
-returns a non-zero return code.
+check_call() and check_output() will raise CalledProcessError, if the
+called process returns a non-zero return code.
Security
@@ -321,12 +332,15 @@ import signal
# Exception classes used by this module.
class CalledProcessError(Exception):
- """This exception is raised when a process run by check_call() returns
- a non-zero exit status. The exit status will be stored in the
- returncode attribute."""
- def __init__(self, returncode, cmd):
+ """This exception is raised when a process run by check_call() or
+ check_output() returns a non-zero exit status.
+ The exit status will be stored in the returncode attribute;
+ check_output() will also store the output in the output attribute.
+ """
+ def __init__(self, returncode, cmd, output=None):
self.returncode = returncode
self.cmd = cmd
+ self.output = output
def __str__(self):
return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
@@ -364,7 +378,7 @@ else:
import pickle
__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput",
- "getoutput", "CalledProcessError"]
+ "getoutput", "check_output", "CalledProcessError"]
try:
MAXFD = os.sysconf("SC_OPEN_MAX")
@@ -410,12 +424,45 @@ def check_call(*popenargs, **kwargs):
check_call(["ls", "-l"])
"""
retcode = call(*popenargs, **kwargs)
- cmd = kwargs.get("args")
- if cmd is None:
- cmd = popenargs[0]
if retcode:
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
raise CalledProcessError(retcode, cmd)
- return retcode
+ return 0
+
+
+def check_output(*popenargs, **kwargs):
+ """Run command with arguments and return its output as a byte string.
+
+ If the exit code was non-zero it raises a CalledProcessError. The
+ CalledProcessError object will have the return code in the returncode
+ attribute and output in the output attribute.
+
+ The arguments are the same as for the Popen constructor. Example:
+
+ >>> check_output(["ls", "-l", "/dev/null"])
+ 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
+
+ The stdout argument is not allowed as it is used internally.
+ To capture standard error in the result, use stderr=subprocess.STDOUT.
+
+ >>> check_output(["/bin/sh", "-c",
+ "ls -l non_existant_file ; exit 0"],
+ stderr=subprocess.STDOUT)
+ 'ls: non_existant_file: No such file or directory\n'
+ """
+ if 'stdout' in kwargs:
+ raise ValueError('stdout argument not allowed, it will be overridden.')
+ process = Popen(*popenargs, stdout=PIPE, **kwargs)
+ output, unused_err = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ raise CalledProcessError(retcode, cmd, output=output)
+ return output
def list2cmdline(seq):
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index c99a650..c179d14 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -18,7 +18,7 @@
"""Test harness for the logging module. Run all tests.
-Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved.
+Copyright (C) 2001-2008 Vinay Sajip. All Rights Reserved.
"""
import logging
@@ -44,6 +44,7 @@ import threading
import time
import types
import unittest
+import warnings
import weakref
@@ -885,6 +886,32 @@ class EncodingTest(BaseTest):
if os.path.isfile(fn):
os.remove(fn)
+class WarningsTest(BaseTest):
+ def test_warnings(self):
+ logging.captureWarnings(True)
+ warnings.filterwarnings("always", category=UserWarning)
+ try:
+ file = io.StringIO()
+ h = logging.StreamHandler(file)
+ logger = logging.getLogger("py.warnings")
+ logger.addHandler(h)
+ warnings.warn("I'm warning you...")
+ logger.removeHandler(h)
+ s = file.getvalue()
+ h.close()
+ self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
+
+ #See if an explicit file uses the original implementation
+ file = io.StringIO()
+ warnings.showwarning("Explicit", UserWarning, "dummy.py", 42, file,
+ "Dummy line")
+ s = file.getvalue()
+ file.close()
+ self.assertEqual(s, "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
+ finally:
+ warnings.resetwarnings()
+ logging.captureWarnings(False)
+
# Set the locale to the platform-dependent default. I have no idea
# why the test does this, but in any case we save the current locale
# first and restore it at the end.
@@ -893,7 +920,7 @@ def test_main():
run_unittest(BuiltinLevelsTest, BasicFilterTest,
CustomLevelsAndFiltersTest, MemoryHandlerTest,
ConfigFileTest, SocketHandlerTest, MemoryTest,
- EncodingTest)
+ EncodingTest, WarningsTest)
if __name__ == "__main__":
test_main()
diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
index f4f1cd5..eb4e759 100644
--- a/Lib/test/test_subprocess.py
+++ b/Lib/test/test_subprocess.py
@@ -73,6 +73,40 @@ class ProcessTestCase(unittest.TestCase):
else:
self.fail("Expected CalledProcessError")
+ def test_check_output(self):
+ # check_output() function with zero return code
+ output = subprocess.check_output(
+ [sys.executable, "-c", "print('BDFL')"])
+ self.assertTrue(b'BDFL' in output)
+
+ def test_check_output_nonzero(self):
+ # check_call() function with non-zero return code
+ try:
+ subprocess.check_output(
+ [sys.executable, "-c", "import sys; sys.exit(5)"])
+ except subprocess.CalledProcessError as e:
+ self.assertEqual(e.returncode, 5)
+ else:
+ self.fail("Expected CalledProcessError")
+
+ def test_check_output_stderr(self):
+ # check_output() function stderr redirected to stdout
+ output = subprocess.check_output(
+ [sys.executable, "-c", "import sys; sys.stderr.write('BDFL')"],
+ stderr=subprocess.STDOUT)
+ self.assertTrue(b'BDFL' in output)
+
+ def test_check_output_stdout_arg(self):
+ # check_output() function stderr redirected to stdout
+ try:
+ output = subprocess.check_output(
+ [sys.executable, "-c", "print('will not be run')"],
+ stdout=sys.stdout)
+ except ValueError as e:
+ self.assertTrue('stdout' in e.args[0])
+ else:
+ self.fail("Expected ValueError when stdout arg supplied.")
+
def test_call_kwargs(self):
# call() function with keyword args
newenv = os.environ.copy()