mirror of
https://github.com/kennethreitz/python-guide.git
synced 2026-06-05 23:00:18 +00:00
@@ -32,6 +32,7 @@ This part of the guide focuses on best practices for writing Python code.
|
|||||||
writing/reading
|
writing/reading
|
||||||
writing/documentation
|
writing/documentation
|
||||||
writing/tests
|
writing/tests
|
||||||
|
writing/logging
|
||||||
writing/gotchas
|
writing/gotchas
|
||||||
writing/license
|
writing/license
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ different scenarios.
|
|||||||
scenarios/scientific
|
scenarios/scientific
|
||||||
scenarios/imaging
|
scenarios/imaging
|
||||||
scenarios/xml
|
scenarios/xml
|
||||||
|
scenarios/json
|
||||||
scenarios/crypto
|
scenarios/crypto
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ who choose to create a command-line interface because it is quick and simple.
|
|||||||
Cliff
|
Cliff
|
||||||
------
|
------
|
||||||
|
|
||||||
`Cliff <https://cliff.readthedocs.org/en/latest>`_ is a framework for
|
`Cliff <http://docs.openstack.org/developer/cliff/>`_ is a framework for
|
||||||
building command-line programs. It uses setuptools entry points to provide
|
building command-line programs. It uses setuptools entry points to provide
|
||||||
subcommands, output formatters, and other extensions. The framework is meant
|
subcommands, output formatters, and other extensions. The framework is meant
|
||||||
to be used to create multi-level commands such as subversion and git, where
|
to be used to create multi-level commands such as subversion and git, where
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
JSON
|
||||||
|
====
|
||||||
|
|
||||||
|
The `json <https://docs.python.org/2/library/json.html>`_ library can parse JSON from strings or files. When parsing, the library converts the JSON into a Python dictionary or list. It can also parse Python dictionaries or lists into JSON strings.
|
||||||
|
|
||||||
|
Parsing JSON
|
||||||
|
------------
|
||||||
|
|
||||||
|
Take the following string containing JSON data:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
json_string = '{"first_name": "Guido", "last_name":"Rossum"}'
|
||||||
|
|
||||||
|
It can be parsed like this:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import json
|
||||||
|
parsed_json = json.loads(json_string)
|
||||||
|
|
||||||
|
and can now be used as a normal dictionary:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
print(parsed_json['first_name'])
|
||||||
|
"Guido"
|
||||||
|
|
||||||
|
You can also convert a the following to JSON:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
d = {
|
||||||
|
'first_name': 'Guido',
|
||||||
|
'second_name': 'Rossum',
|
||||||
|
'titles': ['BDFL', 'Developer'],
|
||||||
|
}
|
||||||
|
|
||||||
|
print(json.dumps(d))
|
||||||
|
'{"first_name": "Guido", "last_name": "Rossum", "titles": ["BDFL", "Developer"]}'
|
||||||
|
|
||||||
|
|
||||||
|
simplejson
|
||||||
|
----------
|
||||||
|
|
||||||
|
`simplejson <https://simplejson.readthedocs.org/en/latest/>`_ is the externally maintained development version of the json library.
|
||||||
|
|
||||||
|
simplejson mimics the json standard library, it is available so that developers that use an older version of python can use the latest features available in the json lib.
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
Logging
|
||||||
|
=======
|
||||||
|
|
||||||
|
The :mod:`logging` module has been a part of Python's Standard Library since
|
||||||
|
version 2.3. It is succinctly described in :pep:`282`. The documentation
|
||||||
|
is notoriously hard to read, except for the `basic logging tutorial`_.
|
||||||
|
|
||||||
|
Logging serves two purposes:
|
||||||
|
|
||||||
|
- **Diagnostic logging** records events related to the application's
|
||||||
|
operation. If a user calls in to report an error, for example, the logs
|
||||||
|
can be searched for context.
|
||||||
|
- **Audit logging** records events for business analysis. A user's
|
||||||
|
transactions can be extracted and combined with other user details for
|
||||||
|
reports or to optimize a business goal.
|
||||||
|
|
||||||
|
|
||||||
|
... or Print?
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The only time that ``print`` is a better option than logging is when
|
||||||
|
the goal is to display a help statement for a command line application.
|
||||||
|
Other reasons why logging is better than ``print``:
|
||||||
|
|
||||||
|
- The `log record`_, which is created with every logging event, contains
|
||||||
|
readily available diagnostic information such as the file name,
|
||||||
|
full path, function, and line number of the logging event.
|
||||||
|
- Events logged in included modules are automatically accessible via the
|
||||||
|
root logger
|
||||||
|
to your application's logging stream, unless you filter them out.
|
||||||
|
- Logging can be selectively silenced by using the method
|
||||||
|
:meth:`logging.Logger.setLevel` or disabled by setting the attribute
|
||||||
|
:attr:`logging.Logger.disabled` to ``True``.
|
||||||
|
|
||||||
|
|
||||||
|
Logging in a Library
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Notes for `configuring logging for a library`_ are in the
|
||||||
|
`logging tutorial`_. Because the *user*, not the library, should
|
||||||
|
dictate what happens when a logging event occurs, one admonition bears
|
||||||
|
repeating:
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
It is strongly advised that you do not add any handlers other than
|
||||||
|
NullHandler to your library’s loggers.
|
||||||
|
|
||||||
|
|
||||||
|
Best practice when instantiating loggers in a library is to only create them
|
||||||
|
using the ``__name__`` global variable: the :mod:`logging` module creates a
|
||||||
|
hierarchy of loggers using dot notation, so using ``__name__`` ensures
|
||||||
|
no name collisions.
|
||||||
|
|
||||||
|
Here is an example of best practice from the `requests source`_ -- place
|
||||||
|
this in your ``__init__.py``
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
# Set default logging handler to avoid "No handler found" warnings.
|
||||||
|
import logging
|
||||||
|
try: # Python 2.7+
|
||||||
|
from logging import NullHandler
|
||||||
|
except ImportError:
|
||||||
|
class NullHandler(logging.Handler):
|
||||||
|
def emit(self, record):
|
||||||
|
pass
|
||||||
|
|
||||||
|
logging.getLogger(__name__).addHandler(NullHandler())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Logging in an Application
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The `twelve factor app <http://12factor.net>`_, an authoritative reference
|
||||||
|
for good practice in application development, contains a section on
|
||||||
|
`logging best practice <http://12factor.net/logs>`_. It emphatically
|
||||||
|
advocates for treating log events as an event stream, and for
|
||||||
|
sending that event stream to standard output to be handled by the
|
||||||
|
application environment.
|
||||||
|
|
||||||
|
|
||||||
|
There are at least three ways to configure a logger:
|
||||||
|
|
||||||
|
- Using an INI-formatted file:
|
||||||
|
- **Pro**: possible to update configuration while running
|
||||||
|
using the function :func:`logging.config.listen` to listen
|
||||||
|
on a socket.
|
||||||
|
- **Con**: less control (*e.g.* custom subclassed filters or loggers)
|
||||||
|
than possible when configuring a logger in code.
|
||||||
|
- Using a dictionary or a JSON-formatted file:
|
||||||
|
- **Pro**: in addition to updating while running, it is possible to
|
||||||
|
load from a file using the :mod:`json` module, in the standard
|
||||||
|
library since Python 2.6.
|
||||||
|
- **Con**: less control than when configuring a logger in code.
|
||||||
|
- Using code:
|
||||||
|
- **Pro**: complete control over the configuration.
|
||||||
|
- **Con**: modifications require a change to source code.
|
||||||
|
|
||||||
|
|
||||||
|
Example Configuration via an INI File
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Let us say the file is named ``logging_config.ini``.
|
||||||
|
More details for the file format are in the `logging configuration`_
|
||||||
|
section of the `logging tutorial`_.
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[loggers]
|
||||||
|
keys=root
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys=stream_handler
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys=formatter
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=stream_handler
|
||||||
|
|
||||||
|
[handler_stream_handler]
|
||||||
|
class=StreamHandler
|
||||||
|
level=DEBUG
|
||||||
|
formatter=formatter
|
||||||
|
args=(sys.stderr,)
|
||||||
|
|
||||||
|
[formatter_formatter]
|
||||||
|
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s
|
||||||
|
|
||||||
|
|
||||||
|
Then use :meth:`logging.config.fileConfig` in the code:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from logging.config import fileConfig
|
||||||
|
|
||||||
|
fileConfig('logging_config.ini')
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.debug('often makes a very good meal of %s', 'visiting tourists')
|
||||||
|
|
||||||
|
|
||||||
|
Example Configuration via a Dictionary
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
As of Python 2.7, you can use a dictionary with configuration details.
|
||||||
|
:pep:`319` contains a list of the mandatory and optional elements in
|
||||||
|
the configuration dictionary.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from logging.config import dictConfig
|
||||||
|
|
||||||
|
logging_config = dict(
|
||||||
|
version = 1,
|
||||||
|
formatters = {
|
||||||
|
'f': {'format':
|
||||||
|
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
|
||||||
|
},
|
||||||
|
handlers = {
|
||||||
|
'h': {'class': 'logging.StreamHandler',
|
||||||
|
'formatter': 'f',
|
||||||
|
'level': logging.DEBUG}
|
||||||
|
},
|
||||||
|
loggers = {
|
||||||
|
root : {'handlers': ['h'],
|
||||||
|
'level': logging.DEBUG}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
dictConfig(logging_config)
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.debug('often makes a very good meal of %s', 'visiting tourists')
|
||||||
|
|
||||||
|
|
||||||
|
Example Configuration Directly in Code
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
logger.addHandler(handler)
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
logger.debug('often makes a very good meal of %s', 'visiting tourists')
|
||||||
|
|
||||||
|
|
||||||
|
.. _basic logging tutorial: http://docs.python.org/howto/logging.html#logging-basic-tutorial
|
||||||
|
.. _logging configuration: https://docs.python.org/howto/logging.html#configuring-logging
|
||||||
|
.. _logging tutorial: http://docs.python.org/howto/logging.html
|
||||||
|
.. _configuring logging for a library: https://docs.python.org/howto/logging.html#configuring-logging-for-a-library
|
||||||
|
.. _log record: https://docs.python.org/library/logging.html#logrecord-attributes
|
||||||
|
.. _requests source: https://github.com/kennethreitz/requests
|
||||||
@@ -58,7 +58,7 @@ Some general rules of testing:
|
|||||||
good set of tests, you or other maintainers will rely largely on the
|
good set of tests, you or other maintainers will rely largely on the
|
||||||
testing suite to fix the problem or modify a given behavior. Therefore
|
testing suite to fix the problem or modify a given behavior. Therefore
|
||||||
the testing code will be read as much as or even more than the running
|
the testing code will be read as much as or even more than the running
|
||||||
code. A unit test whose purpose is unclear is not very helpful is this
|
code. A unit test whose purpose is unclear is not very helpful in this
|
||||||
case.
|
case.
|
||||||
|
|
||||||
- Another use of the testing code is as an introduction to new developers. When
|
- Another use of the testing code is as an introduction to new developers. When
|
||||||
|
|||||||
Reference in New Issue
Block a user