Mastering Python Second Edition Release Code
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
Chapter 10, Testing and Logging
|
||||
##############################################################################
|
||||
|
||||
| Preparing for Bugs explains how code can be tested and how logging can be added to enable easy debugging in the case of bugs at a later time.
|
||||
@@ -0,0 +1,34 @@
|
||||
def square(n: int) -> int:
|
||||
'''
|
||||
Returns the input number, squared
|
||||
|
||||
>>> square(0)
|
||||
0
|
||||
>>> square(1)
|
||||
1
|
||||
>>> square(2)
|
||||
4
|
||||
>>> square(3)
|
||||
9
|
||||
>>> square()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: square() missing 1 required positional argument: 'n'
|
||||
>>> square('x')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: can't multiply sequence by non-int of type 'str'
|
||||
|
||||
Args:
|
||||
n (int): The number to square
|
||||
|
||||
Returns:
|
||||
int: The squared result
|
||||
'''
|
||||
return n * n
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
def square(n: int) -> int:
|
||||
'''
|
||||
>>> square('x')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: unsupported operand type(s) for ** or pow(): ...
|
||||
'''
|
||||
return n ** 2
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod(optionflags=doctest.ELLIPSIS)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -0,0 +1,61 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'doctest support'
|
||||
copyright = '2020, Rick van Hattem'
|
||||
author = 'Rick van Hattem'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.napoleon',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.doctest',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/', None),
|
||||
'sphinx': ('https://www.sphinx-doc.org/', None),
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
.. doctest support documentation master file, created by
|
||||
sphinx-quickstart on Sun Oct 4 17:57:12 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to doctest support's documentation!
|
||||
===========================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
square
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
@@ -0,0 +1,35 @@
|
||||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
@@ -0,0 +1,34 @@
|
||||
square module
|
||||
=============
|
||||
|
||||
.. automodule:: square
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Examples:
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from square import square
|
||||
|
||||
.. doctest::
|
||||
|
||||
# pytest does not recognize testsetup
|
||||
>>> from square import square
|
||||
>>> square(100)
|
||||
10000
|
||||
>>> square(0)
|
||||
0
|
||||
>>> square(1)
|
||||
1
|
||||
>>> square(3)
|
||||
9
|
||||
>>> square()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: square() missing 1 required positional argument: 'n'
|
||||
>>> square('x')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: can't multiply sequence by non-int of type 'str'
|
||||
@@ -0,0 +1,11 @@
|
||||
'''
|
||||
>>> False
|
||||
0
|
||||
>>> True
|
||||
1
|
||||
'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
doctest.testmod(optionflags=doctest.DONT_ACCEPT_TRUE_FOR_1)
|
||||
@@ -0,0 +1,15 @@
|
||||
'''
|
||||
>>> [list(range(5)) for i in range(3)]
|
||||
[[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
|
||||
|
||||
>>> # doctest: +NORMALIZE_WHITESPACE
|
||||
... [list(range(5)) for i in range(3)]
|
||||
[[0, 1, 2, 3, 4],
|
||||
[0, 1, 2, 3, 4],
|
||||
[0, 1, 2, 3, 4]]
|
||||
'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
'''
|
||||
>>> {10: 'a', 20: 'b'} # doctest: +ELLIPSIS
|
||||
{...}
|
||||
>>> [True, 1, 'a'] # doctest: +ELLIPSIS
|
||||
[...]
|
||||
>>> True, # doctest: +ELLIPSIS
|
||||
(...)
|
||||
>>> [1, 2, 3, 4] # doctest: +ELLIPSIS
|
||||
[1, ..., 4]
|
||||
>>> [1, 0, 0, 0, 0, 0, 4] # doctest: +ELLIPSIS
|
||||
[1, ..., 4]
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> class Spam(object):
|
||||
... pass
|
||||
>>> Spam() # doctest: +ELLIPSIS
|
||||
<...Spam object at 0x...>
|
||||
'''
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
>>> import pprint
|
||||
|
||||
>>> data = dict.fromkeys('spam')
|
||||
>>> pprint.pprint(data)
|
||||
{'a': None, 'm': None, 'p': None, 's': None}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> data = dict.fromkeys('spam')
|
||||
>>> sorted(data.items())
|
||||
[('a', None), ('m', None), ('p', None), ('s', None)]
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> data = dict.fromkeys('spam')
|
||||
>>> data == {'a': None, 'm': None, 'p': None, 's': None}
|
||||
True
|
||||
@@ -0,0 +1,11 @@
|
||||
>>> 1/3 # doctest: +ELLIPSIS
|
||||
0.333...
|
||||
>>> f'{1/3:.3f}'
|
||||
'0.333'
|
||||
>>> '{:.3f}'.format(1/3)
|
||||
'0.333'
|
||||
>>> round(1/3, 3)
|
||||
0.333
|
||||
>>> 0.333 < 1/3 < 0.334
|
||||
True
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
>>> import time
|
||||
|
||||
>>> a = time.time()
|
||||
>>> b = time.time()
|
||||
>>> (b - a) < 0.01
|
||||
True
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
>>> import datetime
|
||||
|
||||
>>> a = datetime.datetime.now()
|
||||
>>> b = datetime.datetime.now()
|
||||
>>> str(b - a) # doctest: +ELLIPSIS
|
||||
'0:00:00.000...
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import unittest
|
||||
import cube
|
||||
|
||||
|
||||
class TestCube(unittest.TestCase):
|
||||
|
||||
def test_0(self):
|
||||
self.assertEqual(cube.cube(0), 0)
|
||||
|
||||
def test_1(self):
|
||||
self.assertEqual(cube.cube(1), 1)
|
||||
|
||||
def test_2(self):
|
||||
self.assertEqual(cube.cube(2), 8)
|
||||
|
||||
def test_3(self):
|
||||
self.assertEqual(cube.cube(3), 27)
|
||||
|
||||
def test_no_arguments(self):
|
||||
with self.assertRaises(TypeError):
|
||||
cube.cube()
|
||||
|
||||
def test_exception_str(self):
|
||||
with self.assertRaises(TypeError):
|
||||
cube.cube('x')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,29 @@
|
||||
import unittest
|
||||
import cube
|
||||
|
||||
n = 2
|
||||
expected = 8
|
||||
|
||||
|
||||
# Regular unit test
|
||||
class TestCube(unittest.TestCase):
|
||||
|
||||
def test_2(self):
|
||||
self.assertEqual(cube.cube(n), expected)
|
||||
|
||||
def test_no_arguments(self):
|
||||
with self.assertRaises(TypeError):
|
||||
cube.cube()
|
||||
|
||||
|
||||
# py.test class
|
||||
class TestPyCube:
|
||||
|
||||
def test_2(self):
|
||||
assert cube.cube(n) == expected
|
||||
|
||||
|
||||
# py.test functions
|
||||
def test_2():
|
||||
assert cube.cube(n) == expected
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
class User:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name
|
||||
|
||||
|
||||
def test_user_equal():
|
||||
a = User('Rick')
|
||||
b = User('Guido')
|
||||
|
||||
assert a == b
|
||||
@@ -0,0 +1,13 @@
|
||||
class User:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name
|
||||
|
||||
|
||||
def test_user_equal():
|
||||
a = User('Rick')
|
||||
b = User('Guido')
|
||||
|
||||
assert a == b
|
||||
@@ -0,0 +1,16 @@
|
||||
import pytest
|
||||
import cube
|
||||
|
||||
|
||||
cubes = (
|
||||
(0, 0),
|
||||
(1, 1),
|
||||
(2, 8),
|
||||
(3, 27),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('n,expected', cubes)
|
||||
def test_cube(n, expected):
|
||||
assert cube.cube(n) == expected
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
##################################################################
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def name():
|
||||
return 'Rick'
|
||||
|
||||
|
||||
def test_something(name):
|
||||
assert name == 'Rick'
|
||||
|
||||
|
||||
##################################################################
|
||||
|
||||
def test_cache(cache):
|
||||
counter = cache.get('counter', 0) + 1
|
||||
assert counter
|
||||
cache.set('counter', counter)
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def some_yield_fixture():
|
||||
with open(__file__ + '.txt', 'w') as fh:
|
||||
# Before the function
|
||||
yield fh
|
||||
# After the function
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def some_regular_fixture():
|
||||
# Do something here
|
||||
return 'some_value_to_pass_as_parameter'
|
||||
|
||||
|
||||
def some_test(some_yield_fixture, some_regular_fixture):
|
||||
some_yield_fixture.write(some_regular_fixture)
|
||||
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
import pytest
|
||||
import sqlite3
|
||||
|
||||
|
||||
@pytest.fixture(params=[':memory:'])
|
||||
def connection(request):
|
||||
return sqlite3.connect(request.param)
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
def transaction(connection):
|
||||
with connection:
|
||||
yield connection
|
||||
|
||||
|
||||
def test_insert(transaction):
|
||||
transaction.execute('create table test (id integer)')
|
||||
for i in range(3):
|
||||
transaction.execute('insert into test values (?)', (i,))
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
|
||||
def test_print():
|
||||
print('Printing to stdout')
|
||||
print('Printing to stderr', file=sys.stderr)
|
||||
logging.debug('Printing to debug')
|
||||
logging.info('Printing to info')
|
||||
logging.warning('Printing to warning')
|
||||
logging.error('Printing to error')
|
||||
# We don't want to display os.environ so hack around it
|
||||
fail = 'FAIL' in os.environ
|
||||
assert not fail
|
||||
@@ -0,0 +1,16 @@
|
||||
import pytest
|
||||
import cube_root
|
||||
|
||||
|
||||
cubes = (
|
||||
(0, 0),
|
||||
(1, 1),
|
||||
(8, 2),
|
||||
(27, 3),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('n,expected', cubes)
|
||||
def test_cube_root(n, expected):
|
||||
assert cube_root.cube_root(n) == expected
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import pytest
|
||||
import cube_root
|
||||
|
||||
|
||||
cubes = (
|
||||
(0, 0),
|
||||
(1, 1),
|
||||
(8, 2),
|
||||
(27, 3),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('n,expected', cubes)
|
||||
def test_cube_root(n, expected):
|
||||
assert cube_root.cube_root(n) == expected
|
||||
|
||||
|
||||
def test_cube_root_below_zero():
|
||||
with pytest.raises(ValueError):
|
||||
cube_root.cube_root(-1)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import os
|
||||
def test(a,b):
|
||||
return c
|
||||
@@ -0,0 +1,16 @@
|
||||
[pytest]
|
||||
python_files =
|
||||
your_project_source/*.py
|
||||
tests/*.py
|
||||
|
||||
addopts =
|
||||
--doctest-modules
|
||||
--cov your_project_source
|
||||
--cov-report term-missing
|
||||
--cov-report html
|
||||
--flake8
|
||||
--mypy
|
||||
|
||||
# W391 is the error about blank lines at the end of a file
|
||||
flake8-ignore =
|
||||
*.py W391
|
||||
@@ -0,0 +1,38 @@
|
||||
from unittest import mock
|
||||
import random
|
||||
|
||||
|
||||
@mock.patch('random.random')
|
||||
def test_random(mock_random):
|
||||
# Specify our mock return value
|
||||
mock_random.return_value = 0.1
|
||||
# Test for the mock return value
|
||||
assert random.random() == 0.1
|
||||
assert mock_random.call_count == 1
|
||||
|
||||
|
||||
def test_random_with():
|
||||
with mock.patch('random.random') as mock_random:
|
||||
mock_random.return_value = 0.1
|
||||
assert random.random() == 0.1
|
||||
|
||||
##############################################################################
|
||||
|
||||
|
||||
import os
|
||||
from unittest import mock
|
||||
|
||||
|
||||
def delete_file(filename):
|
||||
while os.path.exists(filename):
|
||||
os.unlink(filename)
|
||||
|
||||
|
||||
@mock.patch('os.path.exists', side_effect=(True, False, False))
|
||||
@mock.patch('os.unlink')
|
||||
def test_delete_file(mock_exists, mock_unlink):
|
||||
# First try:
|
||||
delete_file('some non-existing file')
|
||||
|
||||
# Second try:
|
||||
delete_file('some non-existing file')
|
||||
@@ -0,0 +1,16 @@
|
||||
import os
|
||||
|
||||
|
||||
def test_chdir_monkeypatch(monkeypatch):
|
||||
monkeypatch.chdir('/')
|
||||
assert os.getcwd() == '/'
|
||||
|
||||
|
||||
def test_chdir():
|
||||
original_directory = os.getcwd()
|
||||
try:
|
||||
os.chdir('/')
|
||||
assert os.getcwd() == '/'
|
||||
finally:
|
||||
os.chdir(original_directory)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
def test_dict_merge():
|
||||
a = dict(a=123)
|
||||
b = dict(b=456)
|
||||
assert a | b
|
||||
@@ -0,0 +1,9 @@
|
||||
[tox]
|
||||
envlist = py3{8,9}
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
deps =
|
||||
pytest
|
||||
commands =
|
||||
pytest test.py
|
||||
@@ -0,0 +1,7 @@
|
||||
import logging
|
||||
|
||||
logging.debug('debug')
|
||||
logging.info('info')
|
||||
logging.warning('warning')
|
||||
logging.error('error')
|
||||
logging.critical('critical')
|
||||
@@ -0,0 +1,25 @@
|
||||
import logging
|
||||
|
||||
log_format = (
|
||||
'%(levelname)-8s %(name)-12s %(message)s')
|
||||
|
||||
logging.basicConfig(
|
||||
filename='debug.log',
|
||||
format=log_format,
|
||||
level=logging.DEBUG,
|
||||
)
|
||||
|
||||
formatter = logging.Formatter(log_format)
|
||||
handler = logging.StreamHandler()
|
||||
handler.setLevel(logging.WARNING)
|
||||
handler.setFormatter(formatter)
|
||||
logging.getLogger().addHandler(handler)
|
||||
|
||||
|
||||
logging.debug('debug')
|
||||
logging.info('info')
|
||||
some_logger = logging.getLogger('some')
|
||||
some_logger.warning('warning')
|
||||
some_logger.error('error')
|
||||
other_logger = some_logger.getChild('other')
|
||||
other_logger.critical('critical')
|
||||
@@ -0,0 +1,30 @@
|
||||
from logging import config
|
||||
|
||||
config.dictConfig({
|
||||
'version': 1,
|
||||
'formatters': {
|
||||
'standard': {
|
||||
'format': '%(levelname)-8s %(name)-12s %(message)s',
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'file': {
|
||||
'filename': 'debug.log',
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.FileHandler',
|
||||
'formatter': 'standard',
|
||||
},
|
||||
'stream': {
|
||||
'level': 'WARNING',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'standard',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'handlers': ['file', 'stream'],
|
||||
'level': 'DEBUG',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": 1,
|
||||
"formatters": {
|
||||
"standard": {
|
||||
"format": "%(levelname)-8s %(name)-12s %(message)s"
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"file": {
|
||||
"filename": "debug.log",
|
||||
"level": "DEBUG",
|
||||
"class": "logging.FileHandler",
|
||||
"formatter": "standard"
|
||||
},
|
||||
"stream": {
|
||||
"level": "WARNING",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "standard"
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"": {
|
||||
"handlers": ["file", "stream"],
|
||||
"level": "DEBUG"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import os
|
||||
import json
|
||||
from logging import config
|
||||
|
||||
|
||||
name = os.path.splitext(__file__)[0]
|
||||
json_filename = os.path.join(os.path.dirname(__file__),
|
||||
f'{name}.json')
|
||||
with open(json_filename) as fh:
|
||||
config.dictConfig(json.load(fh))
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[formatters]
|
||||
keys=standard
|
||||
|
||||
[handlers]
|
||||
keys=file,stream
|
||||
|
||||
[loggers]
|
||||
keys=root
|
||||
|
||||
[formatter_standard]
|
||||
format=%(levelname)-8s %(name)-12s %(message)s
|
||||
|
||||
[handler_file]
|
||||
level=DEBUG
|
||||
class=FileHandler
|
||||
formatter=standard
|
||||
args=('debug.log',)
|
||||
|
||||
[handler_stream]
|
||||
level=WARNING
|
||||
class=StreamHandler
|
||||
formatter=standard
|
||||
args=(sys.stderr,)
|
||||
|
||||
[logger_root]
|
||||
handlers=file,stream
|
||||
level=DEBUG
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import os
|
||||
from logging import config
|
||||
|
||||
name = os.path.splitext(__file__)[0]
|
||||
|
||||
config.fileConfig(os.path.join(os.path.dirname(__file__),
|
||||
f'{name}.ini'))
|
||||
@@ -0,0 +1,33 @@
|
||||
[formatters]
|
||||
keys=standard
|
||||
|
||||
[handlers]
|
||||
keys=file,stream
|
||||
|
||||
[loggers]
|
||||
keys=root,some
|
||||
|
||||
[formatter_standard]
|
||||
format=%(levelname)-8s %(name)-12s %(message)s
|
||||
|
||||
[handler_file]
|
||||
level=DEBUG
|
||||
class=FileHandler
|
||||
formatter=standard
|
||||
args=('debug.log',)
|
||||
|
||||
[handler_stream]
|
||||
level=WARNING
|
||||
class=StreamHandler
|
||||
formatter=standard
|
||||
args=(sys.stderr,)
|
||||
|
||||
[logger_root]
|
||||
handlers=file,stream
|
||||
level=DEBUG
|
||||
|
||||
[logger_some]
|
||||
level=DEBUG
|
||||
qualname=some
|
||||
handlers=
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import sys
|
||||
|
||||
|
||||
def receive():
|
||||
import time
|
||||
import logging
|
||||
from logging import config
|
||||
|
||||
listener = config.listen()
|
||||
listener.start()
|
||||
|
||||
try:
|
||||
while True:
|
||||
logging.debug('debug')
|
||||
logging.info('info')
|
||||
some_logger = logging.getLogger('some')
|
||||
some_logger.warning('warning')
|
||||
some_logger.error('error')
|
||||
other_logger = some_logger.getChild('other')
|
||||
other_logger.critical('critical')
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
# Stop listening and finish the listening thread
|
||||
config.stopListening()
|
||||
listener.join()
|
||||
|
||||
|
||||
def send():
|
||||
import os
|
||||
import struct
|
||||
import socket
|
||||
from logging import config
|
||||
|
||||
ini_filename = os.path.splitext(__file__)[0] + '.ini'
|
||||
with open(ini_filename, 'rb') as fh:
|
||||
data = fh.read()
|
||||
|
||||
# Open the socket
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
# Connect to the server
|
||||
sock.connect(('127.0.0.1', config.DEFAULT_LOGGING_CONFIG_PORT))
|
||||
# Send the magic logging packet
|
||||
sock.send(struct.pack('>L', len(data)))
|
||||
# Send the config
|
||||
sock.send(data)
|
||||
# And close the connection again
|
||||
sock.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.argv[-1] == 'send':
|
||||
send()
|
||||
elif sys.argv[-1] == 'receive':
|
||||
receive()
|
||||
else:
|
||||
print(f'Usage: {sys.argv[0]} [send/receive]')
|
||||
@@ -0,0 +1,35 @@
|
||||
import logging
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MyClass(object):
|
||||
|
||||
def __init__(self, count):
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('main_module.sub_module')
|
||||
logger.addHandler(logging.FileHandler('sub_module.log'))
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('main_module.sub_module')
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger()
|
||||
exception = 'Oops...'
|
||||
logger.error('Some horrible error: %r', exception)
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.error('simple error', extra=dict(some_variable='my value'))
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
logging.basicConfig(format='%(some_variable)s: %(message)s')
|
||||
logger = logging.getLogger()
|
||||
logger.error('the message', extra=dict(some_variable='my value'))
|
||||
@@ -0,0 +1,12 @@
|
||||
import logging
|
||||
|
||||
logging.basicConfig()
|
||||
logger = logging.getLogger()
|
||||
|
||||
try:
|
||||
raise RuntimeError('some runtime error')
|
||||
except Exception as exception:
|
||||
logger.exception('Got an exception: %s', exception)
|
||||
|
||||
logger.error('And an error')
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import logging
|
||||
|
||||
|
||||
formatter = logging.Formatter('{levelname} {message}', style='{')
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
logging.error('formatted message?')
|
||||
@@ -0,0 +1,23 @@
|
||||
import logging
|
||||
|
||||
|
||||
class FormattingMessage:
|
||||
|
||||
def __init__(self, message, kwargs):
|
||||
self.message = message
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __str__(self):
|
||||
return self.message.format(**self.kwargs)
|
||||
|
||||
|
||||
class FormattingAdapter(logging.LoggerAdapter):
|
||||
|
||||
def process(self, msg, kwargs):
|
||||
msg, kwargs = super().process(msg, kwargs)
|
||||
return FormattingMessage(msg, kwargs), dict()
|
||||
|
||||
|
||||
logger = FormattingAdapter(logging.root, dict())
|
||||
logger.error('Hi {name}', name='Rick')
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import logging
|
||||
|
||||
|
||||
a = logging.getLogger('a')
|
||||
ab = logging.getLogger('a.b')
|
||||
|
||||
ab.error('before setting level')
|
||||
a.setLevel(logging.CRITICAL)
|
||||
ab.error('after setting level')
|
||||
@@ -0,0 +1,13 @@
|
||||
import logging
|
||||
|
||||
|
||||
ab = logging.getLogger('a.b')
|
||||
ab.setLevel(logging.ERROR)
|
||||
ab.propagate = False
|
||||
ab.addHandler(logging.StreamHandler())
|
||||
|
||||
a = logging.getLogger('a')
|
||||
|
||||
ab.error('before setting level')
|
||||
a.setLevel(logging.CRITICAL)
|
||||
ab.error('after setting level')
|
||||
@@ -0,0 +1,47 @@
|
||||
import logging
|
||||
|
||||
|
||||
def get_handlers(logger):
|
||||
handlers = []
|
||||
# Walk through the loggers and their parents recursively to
|
||||
# fetch the handlers
|
||||
while logger:
|
||||
handlers += logger.handlers
|
||||
|
||||
if logger.propagate:
|
||||
logger = logger.parent
|
||||
else:
|
||||
break
|
||||
|
||||
# Python has a lastResort handler in case no handlers are
|
||||
# defined
|
||||
if not handlers and logging.lastResort:
|
||||
handlers.append(logging.lastResort)
|
||||
|
||||
return handlers
|
||||
|
||||
|
||||
def debug_loggers():
|
||||
logger: logging.Logger
|
||||
for name, logger in logging.root.manager.loggerDict.items():
|
||||
# Placeholders are loggers without settings
|
||||
if isinstance(logger, logging.PlaceHolder):
|
||||
print('skipping', name)
|
||||
continue
|
||||
|
||||
level = logging.getLevelName(logger.getEffectiveLevel())
|
||||
handlers = get_handlers(logger)
|
||||
print(f'{name}@{level}: {handlers}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
a = logging.getLogger('a')
|
||||
a.setLevel(logging.INFO)
|
||||
|
||||
handler = logging.StreamHandler()
|
||||
handler.setLevel(logging.INFO)
|
||||
ab = logging.getLogger('a.b')
|
||||
ab.setLevel(logging.DEBUG)
|
||||
ab.addHandler(handler)
|
||||
|
||||
debug_loggers()
|
||||
@@ -0,0 +1,37 @@
|
||||
[report]
|
||||
# The test coverage you require, keeping to 100% is not easily
|
||||
# possible for all projects but it’s a good default for new projects.
|
||||
fail_under = 100
|
||||
|
||||
# These functions are generally only needed for debugging and/or
|
||||
# extra safety so we want to ignore them from the coverage
|
||||
# requirements
|
||||
exclude_lines =
|
||||
# Make it possible to ignore blocks of code
|
||||
pragma: no cover
|
||||
|
||||
# Generally only debug code uses this
|
||||
def __repr__
|
||||
|
||||
# If a debug setting is set, skip testing
|
||||
if self\.debug:
|
||||
if settings.DEBUG
|
||||
|
||||
# Don’t worry about safety checks and expected errors
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
|
||||
# Do not complain about code that will never run
|
||||
if 0:
|
||||
if __name__ == .__main__.:
|
||||
@abc.abstractmethod
|
||||
|
||||
[run]
|
||||
# Make sure we require that all branches of the code is covered.
|
||||
# So both the if and the else
|
||||
branch = True
|
||||
|
||||
# No need to require coverage of testing code
|
||||
omit =
|
||||
test_*.py
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import sys
|
||||
import pathlib
|
||||
|
||||
# Little hack to add the current directory to sys.path so we can
|
||||
# find the imports
|
||||
path = pathlib.Path(__file__).parent
|
||||
sys.path.append(str(path.resolve()))
|
||||
|
||||
|
||||
from T_12_assert_representation import User
|
||||
|
||||
|
||||
def is_user(value):
|
||||
return isinstance(value, User)
|
||||
|
||||
|
||||
def pytest_assertrepr_compare(config, op, left, right):
|
||||
if is_user(left) and is_user(right) and op == '==':
|
||||
return [
|
||||
'Comparing User instances:',
|
||||
f' name: {left.name} != {right.name}',
|
||||
]
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
def cube(n: int) -> int:
|
||||
'''
|
||||
Returns the input number, cubed
|
||||
|
||||
Args:
|
||||
n (int): The number to cube
|
||||
|
||||
Returns:
|
||||
int: The cubed result
|
||||
'''
|
||||
return n ** 3
|
||||
@@ -0,0 +1,15 @@
|
||||
def cube_root(n: int) -> int:
|
||||
'''
|
||||
Returns the cube root of the input number
|
||||
|
||||
Args:
|
||||
n (int): The number to cube root
|
||||
|
||||
Returns:
|
||||
int: The cube root result
|
||||
'''
|
||||
if n >= 0:
|
||||
return n ** (1 / 3)
|
||||
else:
|
||||
raise ValueError('A number larger than 0 was expected')
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Welcome to Mastering Python's documentation!
|
||||
============================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
README
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
def square(n: int) -> int:
|
||||
'''
|
||||
Returns the input number, squared
|
||||
|
||||
>>> square(2)
|
||||
4
|
||||
|
||||
Args:
|
||||
n (int): The number to square
|
||||
|
||||
Returns:
|
||||
int: The squared result
|
||||
'''
|
||||
return n * n
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
Reference in New Issue
Block a user