This repository has been archived by the owner on Jan 30, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
set up print and logging for sage_bootstrap
- Loading branch information
Showing
12 changed files
with
474 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/.tox | ||
/MANIFEST |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/usr/bin/env python | ||
|
||
import sage_bootstrap | ||
|
||
print('done') | ||
|
||
from sage_bootstrap.config import Configuration | ||
|
||
print(Configuration()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
from sage_bootstrap.config import Configuration | ||
config = Configuration() | ||
|
||
from sage_bootstrap.stdio import init_streams | ||
init_streams(config) | ||
|
||
from sage_bootstrap.logger import init_logger | ||
init_logger(config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Set Up Logging | ||
Logging can be customized using the ``SAGE_BOOTSTRAP`` environment | ||
variable. It is a comma-separated list of ``key:value`` pairs. They | ||
are not case sensitive. Valid pairs are: | ||
* ``log:[level]``, where ``[level]`` is one of | ||
* ``debug`` | ||
* ``info`` | ||
* ``warning`` | ||
* ``critical`` | ||
* ``error`` | ||
* ``interactive:true`` or ``interactive:false``, to override isatty detection. | ||
""" | ||
|
||
|
||
#***************************************************************************** | ||
# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com> | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 2 of the License, or | ||
# (at your option) any later version. | ||
# http://www.gnu.org/licenses/ | ||
#***************************************************************************** | ||
|
||
|
||
import sys | ||
import os | ||
import logging | ||
|
||
|
||
LOG_LEVELS = ( | ||
'debug', | ||
'info', | ||
'warning', | ||
'critical', | ||
'error' | ||
) | ||
|
||
|
||
|
||
|
||
class Configuration(object): | ||
|
||
_initialized = False | ||
|
||
log = 'info' | ||
|
||
interactive = os.isatty(sys.stdout.fileno()) | ||
|
||
def __init__(self): | ||
if not Configuration._initialized: | ||
Configuration._init_from_environ() | ||
if self.log not in LOG_LEVELS: | ||
raise ValueError('invalid log level: {0}'.format(self.log)) | ||
assert isinstance(self.interactive, bool) | ||
|
||
@classmethod | ||
def _init_from_environ(cls): | ||
env = os.environ.get('SAGE_BOOTSTRAP', '').lower() | ||
for pair in env.split(','): | ||
if not pair.strip(): | ||
continue | ||
key, value = pair.split(':', 1) | ||
key = key.strip() | ||
value = value.strip() | ||
if key == 'log': | ||
cls.log = value | ||
elif key == 'interactive': | ||
if value == 'true': | ||
cls.interactive = True | ||
elif value == 'false': | ||
cls.interactive = False | ||
else: | ||
raise ValueError('interactive value must be "true" or "false", got "{0}"' | ||
.format(value)) | ||
else: | ||
raise ValueError('unknown key: "{0}"'.format(key)) | ||
cls._initialized = True | ||
|
||
def __repr__(self): | ||
return '\n'.join([ | ||
'Configuration:', | ||
' * log = {0}'.format(self.log), | ||
' * interactive = {0}'.format(self.interactive) | ||
]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Set Up Logging | ||
When using a script interactively, logging should go to stdout and be | ||
human-readable. When using a script as part of a pipe (usually | ||
involving tee), logging should go to stderr. | ||
""" | ||
|
||
|
||
#***************************************************************************** | ||
# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com> | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 2 of the License, or | ||
# (at your option) any later version. | ||
# http://www.gnu.org/licenses/ | ||
#***************************************************************************** | ||
|
||
import sys | ||
import os | ||
import logging | ||
|
||
logger = logging.getLogger() | ||
|
||
|
||
default_formatter = logging.Formatter( | ||
'%(levelname)s [%(module)s.%(funcName)s:%(lineno)s]: %(message)s') | ||
|
||
plain_formatter = logging.Formatter('%(message)s') | ||
|
||
|
||
class ExcludeInfoFilter(logging.Filter): | ||
|
||
def filter(self, record): | ||
return record.levelno != logging.INFO | ||
|
||
class OnlyInfoFilter(logging.Filter): | ||
|
||
def filter(self, record): | ||
return record.levelno == logging.INFO | ||
|
||
|
||
def init_logger(config): | ||
level = getattr(logging, config.log.upper()) | ||
logger.setLevel(level) | ||
ch_stderr = logging.StreamHandler(sys.stderr) | ||
ch_stderr.setLevel(logging.DEBUG) | ||
ch_stderr.setFormatter(default_formatter) | ||
if config.interactive: | ||
ch_stderr.addFilter(ExcludeInfoFilter()) | ||
ch_stdout = logging.StreamHandler(sys.stdout) | ||
ch_stdout.setLevel(logging.DEBUG) | ||
ch_stdout.setFormatter(plain_formatter) | ||
ch_stdout.addFilter(OnlyInfoFilter()) | ||
logger.addHandler(ch_stdout) | ||
logger.addHandler(ch_stderr) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Set Up Input/output | ||
Output should always be unbuffered so that it appears immediately on | ||
the terminal. | ||
""" | ||
|
||
#***************************************************************************** | ||
# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com> | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 2 of the License, or | ||
# (at your option) any later version. | ||
# http://www.gnu.org/licenses/ | ||
#***************************************************************************** | ||
|
||
|
||
import sys | ||
import os | ||
|
||
|
||
class UnbufferedStream(object): | ||
|
||
def __init__(self, stream): | ||
self.stream = stream | ||
|
||
def write(self, data): | ||
self.stream.write(data) | ||
self.stream.flush() | ||
|
||
def __getattr__(self, attr): | ||
return getattr(self.stream, attr) | ||
|
||
|
||
REAL_STDOUT = sys.stdout | ||
REAL_STDERR = sys.stderr | ||
|
||
|
||
def init_streams(config): | ||
if not config.interactive: | ||
sys.stdout = UnbufferedStream(REAL_STDOUT) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/usr/bin/env python | ||
|
||
from distutils.core import setup | ||
|
||
setup( | ||
name='sage_bootstrap', | ||
description='', | ||
author='Volker Braun', | ||
author_email='vbraun.name@gmail.com', | ||
packages=['sage_bootstrap'], | ||
scripts=['sage-bootstrap'], | ||
version='1.0', | ||
url='https://www.sagemath.org', | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Utility to test running with different values for ``SAGE_BOOTSTRAP`` | ||
""" | ||
|
||
#***************************************************************************** | ||
# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com> | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 2 of the License, or | ||
# (at your option) any later version. | ||
# http://www.gnu.org/licenses/ | ||
#***************************************************************************** | ||
|
||
|
||
# This function's line numbers are in unit tests, try to not move it | ||
# up or down. This is why it is up here at the beginning. | ||
def print_log(): | ||
import logging | ||
log = logging.getLogger() | ||
log.debug('This is the debug log level') | ||
log.info('This is the info log level') | ||
log.warning('This is the warning log level') | ||
log.critical('This is the critical log level') | ||
log.error('This is the error log level') | ||
print('This is printed') | ||
|
||
# From here on the line number does not matter | ||
|
||
import sys | ||
import os | ||
import json | ||
import subprocess | ||
|
||
|
||
def run_with(command, SAGE_BOOTSTRAP): | ||
env = dict(os.environ) | ||
env['SAGE_BOOTSTRAP'] = SAGE_BOOTSTRAP | ||
proc = subprocess.Popen( | ||
[sys.executable, __file__, command], | ||
env=env, | ||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, | ||
) | ||
out, err = proc.communicate() | ||
return out.decode('ascii'), err.decode('ascii') | ||
|
||
|
||
def run_config_with(SAGE_BOOTSTRAP): | ||
out, err = run_with('print_config', SAGE_BOOTSTRAP) | ||
assert not err, err | ||
return json.loads(out) | ||
|
||
|
||
def print_config(): | ||
from sage_bootstrap.config import Configuration | ||
from sage_bootstrap.stdio import REAL_STDOUT, REAL_STDERR | ||
config = Configuration() | ||
result = dict( | ||
log=config.log, | ||
interactive=config.interactive, | ||
stdout='default stdout' if sys.stdout == REAL_STDOUT else str(type(sys.stdout)), | ||
stderr='default stderr' if sys.stderr == REAL_STDERR else str(type(sys.stderr)), | ||
) | ||
print(json.dumps(result)) | ||
|
||
|
||
def run_log_with(SAGE_BOOTSTRAP): | ||
return run_with('print_log', SAGE_BOOTSTRAP) | ||
|
||
|
||
commands = dict( | ||
print_config=print_config, | ||
print_log=print_log, | ||
) | ||
|
||
|
||
if __name__ == '__main__': | ||
sys.path.insert(0, | ||
os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) | ||
import sage_bootstrap | ||
commands[sys.argv[1]]() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
#***************************************************************************** | ||
# Copyright (C) 2015 Volker Braun <vbraun.name@gmail.com> | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 2 of the License, or | ||
# (at your option) any later version. | ||
# http://www.gnu.org/licenses/ | ||
#***************************************************************************** | ||
|
||
|
||
import unittest | ||
from sage_bootstrap.config import Configuration, LOG_LEVELS | ||
from .runnable import run_config_with | ||
|
||
|
||
class ConfigurationTestCase(unittest.TestCase): | ||
|
||
def test_default(self): | ||
""" | ||
Test the default configuration | ||
""" | ||
config = Configuration() | ||
self.assertEqual(config.log, 'info') | ||
self.assertTrue(config.interactive) | ||
|
||
def test_example(self): | ||
""" | ||
Test all ``SAGE_BOOTSTRAP`` settings | ||
""" | ||
SAGE_BOOTSTRAP=' loG:CrItIcAl, interactive:TRUE' | ||
result = run_config_with(SAGE_BOOTSTRAP) | ||
self.assertEqual(len(result), 4) | ||
self.assertEqual(result['log'], u'critical') | ||
self.assertTrue(result['interactive']) | ||
self.assertEqual(result['stdout'], 'default stdout') | ||
self.assertEqual(result['stderr'], 'default stderr') | ||
|
||
|
||
def test_logging(self): | ||
""" | ||
Test that the different log levels are understood | ||
""" | ||
for level in LOG_LEVELS: | ||
self.assertEqual( | ||
run_config_with('LOG:{0}'.format(level.upper()))['log'], | ||
level) | ||
|
||
def test_logging(self): | ||
""" | ||
Test that overriding the isatty detection works | ||
""" | ||
interactive = run_config_with('interactive:true') | ||
self.assertTrue(interactive['interactive']) | ||
self.assertEqual(interactive['stdout'], 'default stdout') | ||
self.assertEqual(interactive['stderr'], 'default stderr') | ||
in_pipe = run_config_with('interactive:false') | ||
self.assertFalse(in_pipe['interactive']) | ||
self.assertEqual(in_pipe['stdout'], u"<class 'sage_bootstrap.stdio.UnbufferedStream'>") | ||
self.assertEqual(in_pipe['stderr'], 'default stderr') | ||
|
||
|
Oops, something went wrong.