[Jython-checkins] jython: Route console messages into Java logging (addresses #2778).

jeff.allen jython-checkins at python.org
Thu Aug 8 02:45:47 EDT 2019


https://hg.python.org/jython/rev/5e3bd2044a51
changeset:   8272:5e3bd2044a51
user:        Jeff Allen <ja.py at farowl.co.uk>
date:        Wed Aug 07 22:45:47 2019 +0100
summary:
  Route console messages into Java logging (addresses #2778).

With this change, console messages generated with Py.writeDebug etc.
are converted to java.util.logging messages of corresponding severity,
and so may be controlled or routed using a logging.properties file.

In particular, the path now is open to obtain timestamped messages.

Direct use of Options.verbose is deprecated, but clients that modify it,
then call the classic Py.write* methods end up adjusting the console
logging level and so the effect what they expect (nearly).

files:
  NEWS                             |   10 +
  registry                         |    7 +-
  src/org/python/core/Options.java |  137 +++++++++++-------
  src/org/python/core/PrePy.java   |  141 ++++++++++++++++++-
  src/org/python/util/jython.java  |  119 ++++++++++++++-
  5 files changed, 343 insertions(+), 71 deletions(-)


diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -76,6 +76,16 @@
     - Experimentally, we use Gradle to build a JAR and POM that may be cited as a dependency by
       other projects. The Jython project would like to know if this is being done suitably
       for downstream use.
+    - The registry setting (or Java property) "python.verbose", and direct use of the global
+      org.python.core.Options.verbose are DEPRECATED: use a Java logging properties file instead.
+      Console messages are now produced via java.util.logging (JUL), and the logger "org.python"
+      or its descendants. The jython command configures a ConsoleLogger for "org.python" and by
+      default a format for messages generally similar to that in v2.7.1, preceded by the logger
+      name and JUL severity. The volume of messages is controlled by your logging preferences, but
+      by default is unchanged from v2.7.1. Each -v option on the command line makes the logging
+      one step finer on the JUL scale. Error messages from Python and stack traces are unaffected.
+      When embedded in your application, Jython makes no logging configuration changes: messages
+      from the "org.python" name space merge with those of your application.
 
 Jython 2.7.2a1
   Bugs fixed
diff --git a/registry b/registry
--- a/registry
+++ b/registry
@@ -31,9 +31,10 @@
 #python.packages.directories = java.ext.dirs    # up to Java 8
 #python.packages.directories                    # undefined from Java 9
 
-# Set verbosity to error, warning, message, comment, or debug
-# for varying levels of informative messages from Jython. Normally
-# this option is set from the command line.
+# DEPRECATED way to set the verbosity of messages output by Jython. If
+# specified, "python.verbose" will set logging level for "org.python" when
+# the runtime is initialised. It is better to use java.util.logging
+# preferences (and the -v option) which work from the start of execution.
 #python.verbose = message
 
 # Jython ships with a JLine console (http://jline.sourceforge.net/) out of the
diff --git a/src/org/python/core/Options.java b/src/org/python/core/Options.java
--- a/src/org/python/core/Options.java
+++ b/src/org/python/core/Options.java
@@ -3,35 +3,34 @@
 
 import static org.python.core.RegistryKey.*;
 
+import java.util.logging.Level;
+
 /**
- * A class with static fields for each of the settable options. The options from
- * registry and command line is copied into the fields here and the rest of
- * Jython checks these fields.
+ * A class with static fields for each of the settable options. The options from registry and
+ * command line is copied into the fields here and the rest of Jython checks these fields.
  */
 public class Options {
     // Jython options. Some of these can be set from the command line
     // options, but all can be controlled through the Jython registry
 
     /**
-     * when an exception occurs in Java code, and it is not caught, should the
-     * interpreter print out the Java exception in the traceback?
+     * when an exception occurs in Java code, and it is not caught, should the interpreter print out
+     * the Java exception in the traceback?
      */
     public static boolean showJavaExceptions = false;
 
     /**
-     * If true, exceptions raised from Python code will include a Java stack
-     * trace in addition to the Python traceback.  This can slow raising
-     * considerably.
+     * If true, exceptions raised from Python code will include a Java stack trace in addition to
+     * the Python traceback. This can slow raising considerably.
      *
      * @see org.python.core.RegistryKey#PYTHON_OPTIONS_INCLUDE_JAVA_STACK_IN_EXCEPTIONS
      */
     public static boolean includeJavaStackInExceptions = true;
 
     /**
-     * When true, python exception raised in overridden methods will be shown on
-     * stderr. This option is remarkably useful when python is used for
-     * implementing CORBA server. Some CORBA servers will turn python exception
-     * (say a NameError) into an anonymous user exception without any
+     * When true, python exception raised in overridden methods will be shown on stderr. This option
+     * is remarkably useful when python is used for implementing CORBA server. Some CORBA servers
+     * will turn python exception (say a NameError) into an anonymous user exception without any
      * stacktrace. Setting this option will show the stacktrace.
      *
      * @see org.python.core.RegistryKey#PYTHON_OPTIONS_SHOW_PYTHON_PROXY_EXCEPTIONS
@@ -39,10 +38,9 @@
     public static boolean showPythonProxyExceptions = false;
 
     /**
-     * If true, Jython respects Java the accessibility flag for fields,
-     * methods, and constructors. This means you can only access public members.
-     * Set this to false to access all members by toggling the accessible flag
-     * on the member.
+     * If true, Jython respects Java the accessibility flag for fields, methods, and constructors.
+     * This means you can only access public members. Set this to false to access all members by
+     * toggling the accessible flag on the member.
      *
      * @see org.python.core.RegistryKey#PYTHON_SECURITY_RESPECT_JAVA_ACCESSIBILITY
      */
@@ -73,11 +71,16 @@
     public static boolean no_site = false;
 
     /**
-     * Set verbosity to Py.ERROR, Py.WARNING, Py.MESSAGE, Py.COMMENT, or
-     * Py.DEBUG for varying levels of informative messages from Jython. Normally
-     * this option is set from the command line.
+     * Verbosity of informative messages from Jython, retained as a field for reasons of
+     * backward-compatibility. Normally this option should be allowed to find its value indirectly
+     * through {@code java.util.logging}, as adjusted by the command line {@code -v} option.
+     *
+     * @deprecated Use {@link Py#getLoggingLevel()},
+     *             {@link Py#setLoggingLevel(java.util.logging.Level)}, or {@code java.util.logging}
+     *             preferences to configure logging levels.
      */
-    public static int verbose = PrePy.MESSAGE;
+    @Deprecated
+    public static int verbose = PrePy.verbosityFromLevel(PrePy.getLoggingLevel());
 
     /**
      * Set by the {@code -i} option to the interpreter command, to ask for an interactive session to
@@ -95,16 +98,15 @@
     public static boolean inspect = false;
 
     /**
-     * A directory where the dynamically generated classes are written. Nothing is
-     * ever read from here, it is only for debugging purposes.
+     * A directory where the dynamically generated classes are written. Nothing is ever read from
+     * here, it is only for debugging purposes.
      */
     public static String proxyDebugDirectory;
 
     /**
-     * If true, Jython will use the first module found on sys.path where java
-     * File.isFile() returns true. Setting this to true have no effect on
-     * unix-type filesystems. On Windows/HFS+ systems setting it to true will
-     * enable Jython-2.0 behaviour.
+     * If true, Jython will use the first module found on sys.path where java File.isFile() returns
+     * true. Setting this to true have no effect on unix-type filesystems. On Windows/HFS+ systems
+     * setting it to true will enable Jython-2.0 behaviour.
      *
      * @see org.python.core.RegistryKey#PYTHON_OPTIONS_CASE_OK
      */
@@ -117,8 +119,7 @@
      */
     public static boolean Qnew = false;
 
-    /** Force stdin, stdout and stderr to be unbuffered, and opened in
-     * binary mode */
+    /** Force stdin, stdout and stderr to be unbuffered, and opened in binary mode */
     public static boolean unbuffered = false;
 
     /** Whether -3 (py3k warnings) was enabled via the command line. */
@@ -136,7 +137,7 @@
      */
     public static boolean no_user_site = false;
 
-    //XXX: place holder
+    // XXX: place holder
     public static int bytes_warning = 0;
 
     /**
@@ -157,9 +158,8 @@
     public static int division_warning = 0;
 
     /**
-     * Cache spec for the SRE_STATE code point cache. The value maps to the
-     * CacheBuilderSpec string and affects how the SRE_STATE cache will behave/evict
-     * cached PyString -> int[] code points.
+     * Cache spec for the SRE_STATE code point cache. The value maps to the CacheBuilderSpec string
+     * and affects how the SRE_STATE cache will behave/evict cached PyString -> int[] code points.
      */
     public static final String sreCacheSpecDefault =
             "weakKeys,concurrencyLevel=4,maximumWeight=2621440,expireAfterAccess=30s";
@@ -189,45 +189,82 @@
 
     /** Initialize the static fields from the registry options. */
     public static void setFromRegistry() {
-        // Set the more unusual options
+
         showJavaExceptions =
                 getBooleanOption(PYTHON_OPTIONS_SHOW_JAVA_EXCEPTIONS, showJavaExceptions);
         includeJavaStackInExceptions = getBooleanOption(
                 PYTHON_OPTIONS_INCLUDE_JAVA_STACK_IN_EXCEPTIONS, includeJavaStackInExceptions);
-        showPythonProxyExceptions = getBooleanOption(
-                PYTHON_OPTIONS_SHOW_PYTHON_PROXY_EXCEPTIONS, showPythonProxyExceptions);
-        respectJavaAccessibility = getBooleanOption(
-                PYTHON_SECURITY_RESPECT_JAVA_ACCESSIBILITY, respectJavaAccessibility);
+        showPythonProxyExceptions = getBooleanOption(PYTHON_OPTIONS_SHOW_PYTHON_PROXY_EXCEPTIONS,
+                showPythonProxyExceptions);
+        respectJavaAccessibility = getBooleanOption(PYTHON_SECURITY_RESPECT_JAVA_ACCESSIBILITY,
+                respectJavaAccessibility);
         proxyDebugDirectory =
                 getStringOption(PYTHON_OPTIONS_PROXY_DEBUG_DIRECTORY, proxyDebugDirectory);
 
-        // verbosity is more complicated:
+        // Legacy python.verbose if used may countermand logging.properties::org.python.level
+        setLoggingFromRegistry();
+
+        caseok = getBooleanOption(PYTHON_OPTIONS_CASE_OK, caseok);
+        Qnew = getBooleanOption(PYTHON_OPTIONS_Q_NEW, Qnew);
+
+        setDivisionWarningFromRegistry();
+
+        sreCacheSpec = getStringOption(PYTHON_SRE_CACHESPEC, sreCacheSpec);
+        inspect |= getStringOption(PYTHON_INSPECT, "").length() > 0;
+        importSite = getBooleanOption(PYTHON_IMPORT_SITE, importSite);
+        no_site = !importSite;
+    }
+
+    /**
+     * Set the logging level from {@link RegistryKey#PYTHON_VERBOSE}. We recognise the traditional
+     * Jython names and those used by {@code java.util.logging}. If that key is present in the
+     * Jython registry it will determine the level or the logger named "org.python".
+     */
+    private static void setLoggingFromRegistry() {
         String prop;
         switch ((prop = getStringOption(PYTHON_VERBOSE, "")).toLowerCase()) {
-            case "":
+            case "": // Leave it as it is.
+                break;
+            case "off":
+                PrePy.setLoggingLevel(Level.OFF);
                 break;
             case "error":
-                verbose = PrePy.ERROR;
+            case "severe":
+                PrePy.setLoggingLevel(Level.SEVERE);
                 break;
             case "warning":
-                verbose = PrePy.WARNING;
+            case "warn":
+                PrePy.setLoggingLevel(Level.WARNING);
                 break;
             case "message":
-                verbose = PrePy.MESSAGE;
+            case "info":
+                PrePy.setLoggingLevel(Level.INFO);
                 break;
             case "comment":
-                verbose = PrePy.COMMENT;
+            case "config":
+                PrePy.setLoggingLevel(Level.CONFIG);
                 break;
             case "debug":
-                verbose = PrePy.DEBUG;
+            case "fine":
+                PrePy.setLoggingLevel(Level.FINE);
+                break;
+            case "finer":
+                PrePy.setLoggingLevel(Level.FINER);
+                break;
+            case "finest":
+                PrePy.setLoggingLevel(Level.FINEST);
+                break;
+            case "all":
+                PrePy.setLoggingLevel(Level.ALL);
                 break;
             default:
                 throw new IllegalArgumentException("Invalid verbose option: '" + prop + "'");
         }
+    }
 
-        caseok = getBooleanOption(PYTHON_OPTIONS_CASE_OK, caseok);
-        Qnew = getBooleanOption(PYTHON_OPTIONS_Q_NEW, Qnew);
-
+    /** Set {@link division_warning} from {@link RegistryKey#PYTHON_DIVISION_WARNING}. */
+    private static void setDivisionWarningFromRegistry() {
+        String prop;
         switch ((prop = getStringOption(PYTHON_DIVISION_WARNING, "")).toLowerCase()) {
             case "":
                 break;
@@ -244,10 +281,6 @@
                 throw new IllegalArgumentException(
                         "Invalid division_warning option: '" + prop + "'");
         }
+    }
 
-        sreCacheSpec = getStringOption(PYTHON_SRE_CACHESPEC, sreCacheSpec);
-        inspect |= getStringOption(PYTHON_INSPECT, "").length() > 0;
-        importSite = getBooleanOption(PYTHON_IMPORT_SITE, importSite);
-        no_site = !importSite;
-    }
 }
diff --git a/src/org/python/core/PrePy.java b/src/org/python/core/PrePy.java
--- a/src/org/python/core/PrePy.java
+++ b/src/org/python/core/PrePy.java
@@ -11,6 +11,8 @@
 import java.net.URLConnection;
 import java.security.AccessControlException;
 import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import jnr.posix.util.Platform;
 
@@ -30,7 +32,10 @@
 // Do not refer to any PyObject, Py export or PySystemState in this class.
 public class PrePy {
 
-    // Logging convenience functions are here so they may work before the Jython runtime starts.
+    // Logging functions are here so they may be used without starting the Jython runtime.
+
+    /** Our name-spaced root logger is "org.python". */
+    protected static final Logger logger = Logger.getLogger("org.python");
 
     /** {@link Options#verbose} level indicating an error that prevents correct results. */
     public static final int ERROR = -1;
@@ -43,10 +48,136 @@
     /** {@link Options#verbose} level providing detail in support of debugging or tracing. */
     public static final int DEBUG = 3;
 
-    /** Log a message at a specified level (if that level be not below the threshold). */
-    public static void maybeWrite(String type, String msg, int level) {
-        if (level <= Options.verbose) {
-            System.err.println(type + ": " + msg);
+    static final Level[] LEVELS = {//
+            Level.OFF,      //
+            Level.SEVERE,   // Legacy: ERROR
+            Level.WARNING,  // Legacy: WARNING
+            Level.INFO,     // Legacy: MESSAGE
+            Level.CONFIG,   // Legacy: COMMENT
+            Level.FINE,     // Legacy: DEBUG
+            Level.FINER, Level.FINEST, Level.ALL};
+
+    /**
+     * Translate from the traditional "verbosity" system to JUL Level. We allow Jython verbosity
+     * values beyond the conventional range, treating values <{@link #ERROR} as {@code ERROR}
+     * (that is {@code Level.SEVERE}) and values >{@link #DEBUG} (that is {@code Level.FINE}) as
+     * {@code FINER}, {@code FINEST} and {@code ALL}.
+     *
+     * @param verbosity any integer verbosity, where the runtime default {@link #MESSAGE} = 1
+     * @return a corresponding level where the default {@link #MESSAGE} produces {@code Level.INFO}.
+     */
+    public static Level levelFromVerbosity(int verbosity) {
+        if (verbosity < ERROR) {
+            return Level.OFF;
+        } else if (verbosity >= LEVELS.length + (ERROR - 1)) {
+            return Level.ALL;
+        } else {
+            // Bound the index to the LEVELS array.
+            int index = verbosity - (ERROR - 1);
+            return LEVELS[index];
+        }
+    }
+
+    /**
+     * Translate from JUL Level to equivalent in the traditional "verbosity" system. We return
+     * Jython verbosity values beyond the conventional range, enough to enumerate the Java standard
+     * levels (e.g {@code FINER} returns 4 and {@code ALL} returns 6 ).
+     *
+     * @param level {@code java.util.logging.Level} to translate.
+     * @return integer verbosity, where the runtime default {@code INFO} = 1
+     */
+    public static int verbosityFromLevel(Level level) {
+        /*
+         * Find the least verbose setting v such that events at the given level or above will be
+         * logged by Jython, that is, v such that levelFromVerbosity(v) is a threshold no higher
+         * than the given level. We allow Jython verbosity values beyond the conventional range (e.g
+         * level==FINER), according to the range of values in the LEVELS array.
+         */
+        int intLevel = level.intValue();
+        int index = 0, v = ERROR - 1; // = OFF
+        while (index < LEVELS.length && LEVELS[index].intValue() > intLevel) {
+            assert LEVELS[index] == levelFromVerbosity(v);
+            index += 1;
+            v += 1;
+        }
+        return v;
+    }
+
+    /**
+     * Convenience function to get the effective level of a given Logger, looking up the parent
+     * chain.
+     */
+    private static Level getEffectiveLoggingLevel(Logger logger) {
+        Level level;
+        while ((level = logger.getLevel()) == null) {
+            logger = logger.getParent();
+        }
+        return level;
+    }
+
+    /** Convenience function to get the effective level of Logger "org.python". */
+    public static Level getLoggingLevel() {
+        return getEffectiveLoggingLevel(logger);
+    }
+
+    /**
+     * Used by {@link #maybeWrite(Level, String)}, the terminus of all verbosity-based logging
+     * calls, to detect changes made directly to {@link Options#verbose}.
+     */
+    private static int savedVerbosity = Py.MESSAGE;
+
+    /**
+     * Set the level of the Jython logger "org.python" using the standard {@code java.util.logging}
+     * scale. For backward compatibility with the traditional "verbosity" system, make a
+     * corresponding setting of {@link Options#verbose}.
+     */
+    @SuppressWarnings("deprecation")
+    public static void setLoggingLevel(Level newLevel) {
+        Level currentLevel = getLoggingLevel();
+        if (newLevel != currentLevel) {
+            try {
+                logger.setLevel(newLevel);
+                currentLevel = newLevel;
+            } catch (SecurityException se) {
+                logger.warning("A security manager prevented a change to the logging level.");
+            }
+        }
+        savedVerbosity = Options.verbose = verbosityFromLevel(currentLevel);
+    }
+
+    /**
+     * Adjust the level of the Jython logger "org.python" using the traditional "verbosity" system:
+     * the bigger the number, the lower the logging threshold. This is primarily for the
+     * command-line Jython, where each "-v" increases the verbosity by one, on the
+     * {@code java.util.logging} scale.
+     *
+     * @param n increment on the scale {@code 1=INFO, 2=CONFIG, 3=FINE, ... }
+     */
+    public static void increaseLoggingLevel(int n) {
+        int v = verbosityFromLevel(getLoggingLevel());
+        setLoggingLevel(levelFromVerbosity(v + n));
+    }
+
+    /**
+     * Ensure that the logging system threshold is adjusted to match the legacy
+     * {@link Options#verbose} in the event that that has changed since we last looked.
+     */
+    @SuppressWarnings("deprecation")
+    private static void syncLoggingLevel() {
+        if (Options.verbose != savedVerbosity) {
+            Level level = levelFromVerbosity(savedVerbosity = Options.verbose);
+            setLoggingLevel(level);
+        }
+    }
+
+    /** Log a message at a specified level (if that level is not below the threshold). */
+    @SuppressWarnings("deprecation")
+    public static void maybeWrite(String type, String msg, int verbosity) {
+        // If the caller is using the legacy logging system they may have changed Options.verbose.
+        syncLoggingLevel();
+        if (verbosity <= Options.verbose) {
+            // Formulate the message in legacy style, then as a log message
+            logger.log(levelFromVerbosity(verbosity), "{0}: {1}", new Object[] {type, msg});
         }
     }
 
diff --git a/src/org/python/util/jython.java b/src/org/python/util/jython.java
--- a/src/org/python/util/jython.java
+++ b/src/org/python/util/jython.java
@@ -10,6 +10,10 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Properties;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
@@ -38,6 +42,54 @@
         OK, ERROR, NOT_RUN, NO_FILE
     }
 
+    /** The root of the Jython Logger hierarchy, named "org.python". */
+    public static final Logger logger;// = Logger.getLogger("org.python");
+
+    /**
+     * The default format for console log messages in the command-line Jython. See
+     * {@code java.util.logging.SimpleFormatter} for an explanation of the syntax.
+     * <p>
+     * This format is used in the absence of other logging preferences. Jython tests for definitions
+     * in the system properties of {@code java.util.logging.config.class},
+     * {@code java.util.logging.config.file}, and {@code java.util.logging.SimpleFormatter.format}
+     * and if none of these is defined, it sets the last of them to this value.
+     * <p>
+     * You can choose something else, for example to log with millisecond time stamps, launch Jython
+     * as: <pre>
+     * jython -vv -J-Djava.util.logging.SimpleFormatter.format="[%1$tT.%1$tL] %3$s: (%4$s) %5$s%n"
+     * </pre> Depending on your shell, the argument may need quoting or escaping.
+     */
+    public static final String CONSOLE_LOG_FORMAT = "%3$s %4$s %5$s%n";
+
+    static {
+        SecurityException exception = null;
+        try {
+            // Jython console messages (-v option) are emitted using SimpleFormatter
+            configureSimpleFormatter(CONSOLE_LOG_FORMAT);
+        } catch (SecurityException se) {
+            // Unable to access the necessary system properties. Give up on custom logging.
+            exception = se;
+        }
+
+        // Whether we can configure it or not, we can still _use_ logging.
+        logger = Logger.getLogger("org.python");
+
+        if (exception == null) {
+            try {
+                // Make our "org.python" logger do its own output and not propagate to root.
+                setConsoleHandler(logger);
+            } catch (SecurityException se) {
+                // This probably means no logging finer than INFO (so none enabled by -v)
+                exception = se;
+            }
+        }
+
+        if (exception != null) {
+            logger.log(Level.WARNING, "Unable to format console messages: {0}",
+                    exception.getMessage());
+        }
+    }
+
     // An instance of this class will provide the console (python.console) by default.
     private static final String PYTHON_CONSOLE_CLASS = "org.python.util.JLineConsole";
 
@@ -75,7 +127,7 @@
             + "-u       : unbuffered binary stdout and stderr\n"
             // + "(also PYTHONUNBUFFERED=x)\n"
             // + "           see man page for details on internal buffering relating to '-u'\n"
-            + "-v       : verbose (trace import statements)\n"
+            + "-v       : verbose (emit more \"org.python\" log messages)\n"
             // + "(also PYTHONVERBOSE=x)\n"
             + "           can be supplied multiple times to increase verbosity\n"
             + "-V       : print the Python version number and exit (also --version)\n"
@@ -113,6 +165,45 @@
     }
 
     /**
+     * Try to set the format for SimpleFormatter if no other mechanism has been provided, and
+     * security allows it.
+     *
+     * @param format to set for {@code java.util.logging.SimpleFormatter}
+     * @throws SecurityException if not allowed to read or set necessary properties.
+     */
+    private static void configureSimpleFormatter(String format) throws SecurityException {
+        final String CLASS_KEY = "java.util.logging.config.class";
+        String className = System.getProperty(CLASS_KEY);
+        if (className == null) {
+            final String FILE_KEY = "java.util.logging.config.file";
+            String fileName = System.getProperty(FILE_KEY);
+            if (fileName == null) {
+                final String FORMAT_KEY = "java.util.logging.SimpleFormatter.format";
+                String currentFormat = System.getProperty(FORMAT_KEY);
+                if (currentFormat == null) {
+                    // Note that this sets the format for _all_ console logging
+                    System.setProperty(FORMAT_KEY, format);
+                }
+            }
+        }
+    }
+
+    /**
+     * Customise the logger so that it does not propagate to its parent and has its own
+     * {@code Handler} accepting all messages. The level set on the logger alone therefore controls
+     * whether messages are emitted to the console.
+     *
+     * @param logger to adjust (always "python.org")
+     * @throws SecurityException if no permission to adjust logging
+     */
+    private static void setConsoleHandler(Logger logger) throws SecurityException {
+        logger.setUseParentHandlers(false);
+        Handler handler = new ConsoleHandler();
+        handler.setLevel(Level.ALL);
+        logger.addHandler(handler);
+    }
+
+    /**
      * Runs a JAR file, by executing the code found in the file __run__.py, which should be in the
      * root of the JAR archive. Note that {@code __name__} is set to the base name of the JAR file
      * and not to "__main__" (for historical reasons). This method does <b>not</b> handle
@@ -384,6 +475,8 @@
     public static void main(String[] args) {
         // Parse the command line options
         CommandLineOptions opts = CommandLineOptions.parse(args);
+
+        // Choose the basic action
         switch (opts.action) {
             case VERSION:
                 System.err.printf("Jython %s\n", Version.PY_VERSION);
@@ -397,6 +490,9 @@
                 // Let's run some Python! ...
         }
 
+        // Adjust relative to the level set by java.util.logging.
+        PrePy.increaseLoggingLevel(opts.verbosity);
+
         // Get system properties (or empty set if we're prevented from accessing them)
         Properties preProperties = PrePy.getSystemProperties();
         addDefaultsFromEnvironment(preProperties);
@@ -417,8 +513,7 @@
             // We'll be going interactive eventually. condition an interactive console.
             if (PrePy.haveConsole()) {
                 // Set the default console type if nothing else has
-                addDefault(preProperties, RegistryKey.PYTHON_CONSOLE, 
-                                        PYTHON_CONSOLE_CLASS);
+                addDefault(preProperties, RegistryKey.PYTHON_CONSOLE, PYTHON_CONSOLE_CLASS);
             }
         }
 
@@ -455,7 +550,7 @@
          */
         InteractiveConsole interp = new InteractiveConsole();
 
-        if (Options.verbose > Py.MESSAGE || (!haveScript && stdinIsInteractive)) {
+        if (opts.verbosity > 0 || (!haveScript && stdinIsInteractive)) {
             // Verbose or going interactive immediately: produce sign on messages.
             System.err.println(InteractiveConsole.getDefaultBanner());
             if (Options.importSite) {
@@ -721,6 +816,8 @@
         boolean version = false;
         /** -jar option. */
         boolean jar = false;
+        /** Count of -v options. */
+        int verbosity = 0;
 
         /** Collects definitions made with the -D option directly to Jython (not java -D). */
         Properties properties = new Properties();
@@ -736,12 +833,12 @@
 
         /** Valid long-name options. */
         static final char JAR_OPTION = '\u2615';
-        static final OptionScanner.LongSpec[] PROGRAM_LONG_OPTS = {
-                new OptionScanner.LongSpec("--", OptionScanner.DONE),
-                new OptionScanner.LongSpec("--help", 'h'),
-                new OptionScanner.LongSpec("--version", 'V'),
-                new OptionScanner.LongSpec("-jar", JAR_OPTION, true), // Yes, just one dash.
-        };
+        static final OptionScanner.LongSpec[] PROGRAM_LONG_OPTS =
+                {new OptionScanner.LongSpec("--", OptionScanner.DONE),
+                        new OptionScanner.LongSpec("--help", 'h'),
+                        new OptionScanner.LongSpec("--version", 'V'),
+                        new OptionScanner.LongSpec("-jar", JAR_OPTION, true), // Yes, just one dash.
+                };
 
         /**
          * Parse the arguments into the static {@link Options} and a returned instance of this
@@ -892,7 +989,7 @@
                         break;
 
                     case 'v':
-                        Options.verbose++;
+                        verbosity++;
                         break;
 
                     case 'x':

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list