Skip to content

Commit

Permalink
Merge branch 'main' into instr_sequence
Browse files Browse the repository at this point in the history
  • Loading branch information
iritkatriel authored Apr 4, 2024
2 parents 1d64659 + df912c9 commit fb972af
Show file tree
Hide file tree
Showing 115 changed files with 5,390 additions and 4,374 deletions.
68 changes: 64 additions & 4 deletions Doc/howto/logging-cookbook.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1915,14 +1915,15 @@ In a similar way to the above section, we can implement a listener and handler
using `pynng <https://pypi.org/project/pynng/>`_, which is a Python binding to
`NNG <https://nng.nanomsg.org/>`_, billed as a spiritual successor to ZeroMQ.
The following snippets illustrate -- you can test them in an environment which has
``pynng`` installed. Juat for variety, we present the listener first.
``pynng`` installed. Just for variety, we present the listener first.


Subclass ``QueueListener``
^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python
# listener.py
import json
import logging
import logging.handlers
Expand Down Expand Up @@ -1955,7 +1956,7 @@ Subclass ``QueueListener``
break
except pynng.Timeout:
pass
except pynng.Closed: # sometimes hit when you hit Ctrl-C
except pynng.Closed: # sometimes happens when you hit Ctrl-C
break
if data is None:
return None
Expand Down Expand Up @@ -1988,6 +1989,7 @@ Subclass ``QueueHandler``

.. code-block:: python
# sender.py
import json
import logging
import logging.handlers
Expand Down Expand Up @@ -2015,9 +2017,10 @@ Subclass ``QueueHandler``
logging.getLogger('pynng').propagate = False
handler = NNGSocketHandler(DEFAULT_ADDR)
# Make sure the process ID is in the output
logging.basicConfig(level=logging.DEBUG,
handlers=[logging.StreamHandler(), handler],
format='%(levelname)-8s %(name)10s %(message)s')
format='%(levelname)-8s %(name)10s %(process)6s %(message)s')
levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR,
logging.CRITICAL)
logger_names = ('myapp', 'myapp.lib1', 'myapp.lib2')
Expand All @@ -2031,7 +2034,64 @@ Subclass ``QueueHandler``
delay = random.random() * 2 + 0.5
time.sleep(delay)
You can run the above two snippets in separate command shells.
You can run the above two snippets in separate command shells. If we run the
listener in one shell and run the sender in two separate shells, we should see
something like the following. In the first sender shell:

.. code-block:: console
$ python sender.py
DEBUG myapp 613 Message no. 1
WARNING myapp.lib2 613 Message no. 2
CRITICAL myapp.lib2 613 Message no. 3
WARNING myapp.lib2 613 Message no. 4
CRITICAL myapp.lib1 613 Message no. 5
DEBUG myapp 613 Message no. 6
CRITICAL myapp.lib1 613 Message no. 7
INFO myapp.lib1 613 Message no. 8
(and so on)
In the second sender shell:

.. code-block:: console
$ python sender.py
INFO myapp.lib2 657 Message no. 1
CRITICAL myapp.lib2 657 Message no. 2
CRITICAL myapp 657 Message no. 3
CRITICAL myapp.lib1 657 Message no. 4
INFO myapp.lib1 657 Message no. 5
WARNING myapp.lib2 657 Message no. 6
CRITICAL myapp 657 Message no. 7
DEBUG myapp.lib1 657 Message no. 8
(and so on)
In the listener shell:

.. code-block:: console
$ python listener.py
Press Ctrl-C to stop.
DEBUG myapp 613 Message no. 1
WARNING myapp.lib2 613 Message no. 2
INFO myapp.lib2 657 Message no. 1
CRITICAL myapp.lib2 613 Message no. 3
CRITICAL myapp.lib2 657 Message no. 2
CRITICAL myapp 657 Message no. 3
WARNING myapp.lib2 613 Message no. 4
CRITICAL myapp.lib1 613 Message no. 5
CRITICAL myapp.lib1 657 Message no. 4
INFO myapp.lib1 657 Message no. 5
DEBUG myapp 613 Message no. 6
WARNING myapp.lib2 657 Message no. 6
CRITICAL myapp 657 Message no. 7
CRITICAL myapp.lib1 613 Message no. 7
INFO myapp.lib1 613 Message no. 8
DEBUG myapp.lib1 657 Message no. 8
(and so on)
As you can see, the logging from the two sender processes is interleaved in the
listener's output.


An example dictionary-based configuration
Expand Down
15 changes: 8 additions & 7 deletions Doc/library/logging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,13 @@ If you run *myapp.py*, you should see this in *myapp.log*:
INFO:mylib:Doing something
INFO:__main__:Finished
The key features of this idiomatic usage is that the majority of code is simply
The key feature of this idiomatic usage is that the majority of code is simply
creating a module level logger with ``getLogger(__name__)``, and using that
logger to do any needed logging. This is concise while allowing downstream code
fine grained control if needed. Logged messages to the module-level logger get
forwarded up to handlers of loggers in higher-level modules, all the way up to
the root logger; for this reason this approach is known as hierarchical logging.
logger to do any needed logging. This is concise, while allowing downstream
code fine-grained control if needed. Logged messages to the module-level logger
get forwarded to handlers of loggers in higher-level modules, all the way up to
the highest-level logger known as the root logger; this approach is known as
hierarchical logging.

For logging to be useful, it needs to be configured: setting the levels and
destinations for each logger, potentially changing how specific modules log,
Expand All @@ -82,8 +83,8 @@ The module provides a lot of functionality and flexibility. If you are
unfamiliar with logging, the best way to get to grips with it is to view the
tutorials (**see the links above and on the right**).

The basic classes defined by the module, together with their functions, are
listed below.
The basic classes defined by the module, together with their attributes and
methods, are listed in the sections below.

* Loggers expose the interface that application code directly uses.
* Handlers send the log records (created by loggers) to the appropriate
Expand Down
15 changes: 10 additions & 5 deletions Doc/library/unittest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1880,8 +1880,8 @@ Loading and running tests
Python identifiers) will be loaded.

All test modules must be importable from the top level of the project. If
the start directory is not the top level directory then the top level
directory must be specified separately.
the start directory is not the top level directory then *top_level_dir*
must be specified separately.

If importing a module fails, for example due to a syntax error, then
this will be recorded as a single error and discovery will continue. If
Expand All @@ -1901,9 +1901,11 @@ Loading and running tests
package.

The pattern is deliberately not stored as a loader attribute so that
packages can continue discovery themselves. *top_level_dir* is stored so
``load_tests`` does not need to pass this argument in to
``loader.discover()``.
packages can continue discovery themselves.

*top_level_dir* is stored internally, and used as a default to any
nested calls to ``discover()``. That is, if a package's ``load_tests``
calls ``loader.discover()``, it does not need to pass this argument.

*start_dir* can be a dotted module name as well as a directory.

Expand All @@ -1930,6 +1932,9 @@ Loading and running tests
*start_dir* can not be a :term:`namespace packages <namespace package>`.
It has been broken since Python 3.7 and Python 3.11 officially remove it.

.. versionchanged:: 3.13
*top_level_dir* is only stored for the duration of *discover* call.


The following attributes of a :class:`TestLoader` can be configured either by
subclassing or assignment on an instance:
Expand Down
Loading

0 comments on commit fb972af

Please sign in to comment.