Updated to release 0.0.2 including tests and documentation
M .hgignore +8 -1
@@ 1,3 1,10 @@ 
 syntax: glob
 .idea
-docs/build
  No newline at end of file
+.pypirc
+docs/build
+build
+dist
+simple_log.egg-info
+.DS_Store
+*.pyc
+*.log

          
M README.md +28 -9
@@ 1,15 1,15 @@ 
 Simple Logging
 ===============================
 
-* version number: 0.0.1
-* date: 2016.11.10
+* version number: 0.0.2
+* date: 2016.11.23
 * author: Andrew J. Todd esq.
 
 Overview
 --------
 
 A python module to allow you to log. It's a wrapper around the standard logging module with sensible defaults and 
-as little configuration as possible.
+as little configuration as possible. Documentation is on [halfcooked.com](https://halfcooked.com/code/simple_log/)
 
 Installation 
 ------------

          
@@ 36,22 36,41 @@ We try and stay true to the name and mak
     
 If you want to have multiple logs just pass a name to `get_log`
 
-    >>> second_log = get_log('two')
+    >>> first_log = get_log("first")
+    >>> first_log.info("Information message to first log")
+    2016.11.10 22:27:30 INFO:: Information message to first log
+    >>> second_log = get_log("two")
     >>> second_log.debug("This is a debug message")
+    2016.11.10 22:28:02 DEBUG:: This is a debug message
 
-By default the logging level is set to `'INFO'` (the standard module defaults to `'WARN'`). See the
+By default the logging level is set to `'INFO'` (the standard module defaults to `'WARNING'`). See the
 [logging tutorial](https://docs.python.org/3/howto/logging.html#logging-basic-tutorial) for information on logging 
-levels. If you would like to change the logging level, for instance to display 'DEBUG' messages use the `set_level`
-function
+levels. If you would like to change the logging level, for instance to display 'DEBUG' messages use the `setLevel`
+method on your log object
 
-    >>> from simple_log import get_log, set_level
+    >>> from simple_log import get_log
     >>> my_log = get_log('test_log')
     >>> my_log.debug('This is the first debug message')
     ...
-    >>> set_level('test_log', 'DEBUG')
+    >>> my_log.setLevel('DEBUG')
     >>> my_log.debug('This is the second debug message')
     2016.11.10 22:34:55 DEBUG:: This is the second debug message
 
+The log level values that you can pass to `setLevel` are `'DEBUG'`, `'INFO'` (the default), `'WARNING'`, `'ERROR'` or 
+`'CRITICAL'`.
+
+If you would like your log messages written to a file as well as the screen use the `add_file` function
+
+    >>> from simple_log import get_log, add_file
+    >>> my_log = get_log('test_log')
+    >>> my_log.info('This is an information message')
+    2016.11.12 22:48:10 INFO:: This is an information message
+    >>> add_file('test_log', 'test_log.log')
+    >>> my_log.info('This is another information message')
+    2016.11.12 22:49:30 INFO:: This is another information message
+    >>> cat test_log.log
+    2016.11.12 22:49:30 INFO:: This is another information message
+    
 Contributing
 ------------
 

          
M docs/source/conf.py +6 -2
@@ 31,8 31,12 @@ sys.path.insert(0, os.path.abspath('../.
 # ones.
 extensions = [
     'sphinx.ext.autodoc',
+    'sphinx.ext.intersphinx',
 ]
 
+# Directive to get the Python standard documents
+intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
+
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 

          
@@ 57,9 61,9 @@ author = 'Andy Todd'
 # built documents.
 #
 # The short X.Y version.
-version = '0.0.1'
+version = '0.0.2'
 # The full version, including alpha/beta/rc tags.
-release = '0.0.1'
+release = '0.0.2'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.

          
A => docs/source/distributing.rst +19 -0
@@ 0,0 1,19 @@ 
+Distributing
+============
+
+These are the steps to package and upload simple_log to `pypi <https://pypi.python.org/pypi>`_
+
+1. Make changes and commit to mercurial
+
+2. Produce source and wheel distribution packages::
+
+    $ python setup.py sdist
+    $ python setup.py bdist_wheel
+
+3. Upload to pypi::
+
+    $ python setup.py upload -r pypi
+
+Or use twine::
+
+    $ twine upload -r pypi dist/*
  No newline at end of file

          
M docs/source/index.rst +1 -0
@@ 9,6 9,7 @@ Contents:
    :maxdepth: 2
 
    simplelog
+   distributing
 
 
 Indices and tables

          
M docs/source/simplelog.rst +40 -7
@@ 26,25 26,58 @@ We try and stay true to the name and mak
 
 If you want to have multiple logs just pass a name to `get_log`
 
+    >>> first_log = get_log("first")
+    >>> first_log.info("Information message to first log")
+    2016.11.10 22:27:30 INFO:: Information message to first log
     >>> second_log = get_log('two')
     >>> second_log.debug("This is a debug message")
+    2016.11.10 22:28:02 DEBUG:: This is a debug message
 
-By default the logging level is set to `'INFO'` (the standard module defaults to `'WARN'`). See the
+By default the logging level is set to `'INFO'` (the standard module defaults to `'WARNING'`). See the
 `logging tutorial <https://docs.python.org/3/howto/logging.html#logging-basic-tutorial>`_ for information on logging
-levels. If you would like to change the logging level, for instance to display 'DEBUG' messages use the `set_level`
-function
+levels. If you would like to change the logging level, for instance to display 'DEBUG' messages use the `setLevel`
+method on your log object
 
-    >>> from simple_log import get_log, set_level
+    >>> from simple_log import get_log
     >>> my_log = get_log('test_log')
     >>> my_log.debug('This is the first debug message')
     ...
-    >>> set_level('test_log', 'DEBUG')
+    >>> my_log.setLevel('test_log', 'DEBUG')
     >>> my_log.debug('This is the second debug message')
     2016.11.10 22:34:55 DEBUG:: This is the second debug message
 
+If you would like your log messages written to a file as well as the screen use the `add_file` function
+
+    >>> from simple_log import get_log, add_file
+    >>> my_log = get_log('test_log')
+    >>> my_log.info('This is an information message')
+    2016.11.12 22:48:10 INFO:: This is an information message
+    >>> add_file('test_log', 'test_log.log')
+    >>> my_log.info('This is another information message')
+    2016.11.12 22:49:30 INFO:: This is another information message
+    >>> cat test_log.log
+    2016.11.12 22:49:30 INFO:: This is another information message
+
+the `add_file` function tries to be clever and should work if you pass it either the name of a log that was created
+using `get_log` or if you pass in the log object itself.
+
+    >>> from simple_log import get_log, add_file
+    >>> my_log = get_log('test_log')
+    >>> add_file(my_log, 'test_log.log')
+    >>> my_log.info('Information message to file')
+    2016.11.23 19:54:10 INFO:: Information message to file
+    >>> cat test_log.log
+    2016.11.23 19:54:10 INFO:: Information message to file
+
 Code Structure
 --------------
 
-This package is made up of one Python module called `simple_log.py`. It has two functions `get_log` and `set_level`.
+The only dependency for `simple_log` is the Python standard library :py:mod:`logging` module
+
+This package is made up of one Python module called `log_utilities.py` within the `simple_log` module. It has two
+functions
 
-The only dependency is the Python standard library `logging module <https://docs.python.org/3/library/logging.html>`_
+.. autofunction:: simple_log.get_log
+
+.. autofunction:: simple_log.add_file
+

          
M requirements.txt +5 -5
@@ 1,6 1,6 @@ 
 # Dev/Deployment
-sphinx
-sphinx_rtd_theme
-pytest
-coverage
-pypi-publisher
  No newline at end of file
+# sphinx
+# sphinx_rtd_theme
+# pytest
+# coverage
+# pypi-publisher
  No newline at end of file

          
M setup.py +4 -4
@@ 2,7 2,7 @@ from setuptools import setup, find_packa
 from codecs import open
 from os import path
 
-__version__ = '0.0.1'
+__version__ = '0.0.2'
 
 here = path.abspath(path.dirname(__file__))
 

          
@@ 22,9 22,9 @@ setup(
     version=__version__,
     description='A python module with sensible defaults for logging',
     long_description=long_description,
-    url='https://github.com/andy47/simple_log',
-    download_url='https://github.com/andy47/simple_log/tarball/' + __version__,
-    license='BSD',
+    url='https://bitbucket.org/andy47/simplelog',
+    download_url='https://bitbucket.org/andy47/simplelog/downloads/',
+    license='MIT',
     classifiers=[
       'Development Status :: 3 - Alpha',
       'Intended Audience :: Developers',

          
M simple_log/__init__.py +1 -1
@@ 1,1 1,1 @@ 
-from simple_log.log_utilities import get_log, set_level
  No newline at end of file
+from simple_log.log_utilities import get_log, add_file
  No newline at end of file

          
M simple_log/log_utilities.py +20 -10
@@ 5,8 5,8 @@ Written in and tested against Python 3 o
 
 Licensed under the MIT license - https://opensource.org/licenses/MIT
 """
-__version__ = (0, 1, 0)
-__date__ = (2016, 9, 21)
+__version__ = (0, 0, 2)
+__date__ = (2016, 11, 11)
 __author__ = "Andrew J Todd esq. <andy47@halfcooked.com>"
 
 import logging

          
@@ 18,6 18,14 @@ logs = {}
 
 
 def get_log(logname=None):
+    """Return a log object called `<logname>`
+
+    If you don't specify a `<logname>` your returned object will have the name `default`
+
+    Log objects returned by this function have a logging level of `'INFO'` and a
+    sensible message output format including the current date and time and then the
+    log level followed by the log message.
+    """
     if not logname:
         log_name = 'default'
     else:

          
@@ 35,13 43,15 @@ def get_log(logname=None):
         return logger
 
 
-def set_level(log_name, level):
-    """Set the level on <log_name> to <level>
+def add_file(log_name, file_name):
+    """Add a file handler to log messages directed to `<log_name>` to `<file_name>`
 
-    See the standard documentation for the valid list of levels. They are all implemented as module attributes so
-    we just getattr in the call to setLevel
+    We can accept either a log object or the name of a log object in the `<log_name>` parameter
     """
-    if log_name in logs:
-        logs[log_name].setLevel(getattr(logging, level))
-    else:
-        raise AttributeError('{} is not a valid log'.format(log_name))
+    file_handler = logging.FileHandler(file_name, "a")
+    file_formatter = logging.Formatter(MESSAGE_FORMAT, DATE_FORMAT)
+    file_handler.setFormatter(file_formatter)
+    if type(log_name) is logging.Logger:
+        log_name.addHandler(file_handler)
+    elif log_name in logs:
+        logs[log_name].addHandler(file_handler)

          
M tests/test_simple_log.py +33 -4
@@ 6,8 6,10 @@ These unit tests are designed to be run 
 __author__ = "Andrew J Todd esq. <andy47@halfcooked.com>"
 
 import logging
-import py.test
-from simple_log import get_log, set_level
+import os
+import tempfile
+
+from simple_log import get_log, add_file
 
 
 def test_get_log_returns_something():

          
@@ 35,9 37,36 @@ def test_repeat_returns_same_log():
     assert first is second
 
 
-def skip_test_set_level():
+def test_file_output_with_log():
+    """If we specify a file name log messages should be written to it"""
+    tdir = tempfile.mkdtemp()
+    log_name = 'TEST_LOG'
+    log_file_name = os.path.join(tdir, log_name + '.log')
+    # log_file_name = log_name + '.log'
+    test_log = get_log()
+    add_file(test_log, log_file_name)
+    test_log.info("Information test message")
+    with open(log_file_name, 'r') as f:
+        contents = f.readlines()
+    assert "Information test message" in ''.join(contents)
+
+
+def test_file_output_with_log_name():
+    tdir = tempfile.mkdtemp()
+    log_name = 'TEST_LOG'
+    log_file_name = os.path.join(tdir, log_name + '.log')
+    # log_file_name = log_name + '.log'
+    test_log = get_log(log_name)
+    add_file(log_name, log_file_name)
+    test_log.info("Information test message")
+    with open(log_file_name, 'r') as f:
+        contents = f.readlines()
+    assert "Information test message" in ''.join(contents)
+
+
+def test_set_level():
     log_name = 'TEST_LOG'
     test_log = get_log(log_name)
-    set_level(log_name, 'DEBUG')
+    test_log.setLevel('DEBUG')
     assert test_log.level == logging.DEBUG