Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Use Python's logging framework for INFO diagnostic messages (fixes #353)#651

Open
shivamtiwari3 wants to merge 1 commit intogoogle:masterfrom
shivamtiwari3:fix/use-logging-for-info-messages
Open

Use Python's logging framework for INFO diagnostic messages (fixes #353)#651
shivamtiwari3 wants to merge 1 commit intogoogle:masterfrom
shivamtiwari3:fix/use-logging-for-info-messages

Conversation

@shivamtiwari3
Copy link

Summary

Fixes #353.

The two INFO: Showing help … messages that Python Fire prints to stderr
were emitted via bare print() calls. This made it impossible for users
to suppress or redirect the messages using Python's standard logging
machinery.


Problem

import fire

# There was no way to suppress this line before this fix:
# INFO: Showing help with the command 'mytool -- --help'.
fire.Fire(MyClass, command=['--help'])

A common workaround was the fragile monkey-patch described in #353:

def _print(*args, **kwargs):
    args = [a for a in args if not (isinstance(a, str) and a.startswith('INFO:'))]
    if args:
        print(*args, **kwargs)
fire.core.print = _print

Solution

Three changes in fire/core.py:

1. _LazyStderrStreamHandler

A thin logging.StreamHandler subclass that resolves sys.stderr
at emit time via a @property rather than capturing it at
construction time. This is essential for correctness in tests that
replace sys.stderr with an in-memory StringIO buffer using
unittest.mock.patch.object(sys, 'stderr', ...).

2. _logger

_logger = logging.getLogger(__name__)   # → 'fire.core'

The handler is added only when _logger has no handlers yet (idempotent
on repeated imports / reloads). propagate is set to False so that
users who configure the root logger do not receive duplicate output.

3. Level on the parent 'fire' logger

The level is set to INFO on the 'fire' parent logger (not on
'fire.core' directly). Because 'fire.core' inherits its effective
level from the hierarchy, users can suppress all fire messages with a
single call to the parent:

import logging
logging.getLogger('fire').setLevel(logging.WARNING)   # suppress INFO

or target just this module:

logging.getLogger('fire.core').setLevel(logging.WARNING)

or redirect to a custom destination:

handler = logging.FileHandler('fire.log')
logging.getLogger('fire').handlers = [handler]

Default behaviour is unchanged

For a user who does not configure logging, the INFO: line continues
to appear on stderr exactly as before. The format is preserved:

INFO: Showing help with the command 'mytool -- --help'.

Tests added (LoggingTest in fire/core_test.py)

Test What it verifies
testInfoMessageAppearsOnStderrByDefault Default stderr output is preserved (no regression)
testInfoMessageUsesFireLogger core._logger is a logging.Logger named 'fire.core'
testInfoCanBeSuppressedViaLogging Setting fire.core level to WARNING hides the INFO line
testInfoCanBeSuppressedViaParentLogger Setting fire level to WARNING hides the INFO line
testInfoCanBeRedirectedViaCustomHandler Replacing handlers redirects INFO to a custom stream

Test results

265 passed in 0.25s

All pre-existing tests pass without modification. The four INFO:.*SYNOPSIS
assertions in CoreTest continue to match because the logging output format
(INFO: <message>) is identical to what print() produced before.


Checklist

  • Fixes the reported issue (Logging messages don't using logging #353)
  • Default user-visible behaviour is unchanged
  • Uses logging.getLogger(__name__) (library best practice)
  • No new runtime dependencies
  • Fully covered by new and existing tests
  • All 265 tests pass

@google-cla
Copy link

google-cla bot commented Mar 9, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@shivamtiwari3 shivamtiwari3 force-pushed the fix/use-logging-for-info-messages branch from 39f9021 to 8c7d905 Compare March 9, 2026 19:46
Previously, the two 'INFO: Showing help …' diagnostics were emitted via
bare print() calls to sys.stderr. This made it impossible for users to
suppress or redirect the messages using the standard logging machinery.

This commit replaces those calls with a proper Python logging setup:

* Adds _LazyStderrStreamHandler – a StreamHandler subclass that resolves
  sys.stderr at emit time (via a property) rather than at construction time.
  This is required so that unit-test code that patches sys.stderr with an
  in-memory buffer continues to work correctly.

* Adds _logger = logging.getLogger('fire.core') and installs the lazy
  handler on it with level NOTSET, so the effective level is inherited from
  the parent 'fire' logger.

* Sets the parent 'fire' logger to INFO (only if it has not already been
  configured) so that the default user experience is identical to before:
  the INFO line still appears on stderr without any logging configuration.

* Users can now suppress the message with a single line:
      import logging; logging.getLogger('fire').setLevel(logging.WARNING)
  or redirect it to an arbitrary destination by adding their own handler to
  logging.getLogger('fire') or logging.getLogger('fire.core').

Adds four new tests in LoggingTest covering:
  - Default stderr output is preserved.
  - _logger is a logging.Logger named 'fire.core'.
  - INFO can be suppressed via the fire.core logger level.
  - INFO can be suppressed via the parent fire logger level.
  - INFO can be redirected through a custom handler.

All 265 existing tests continue to pass.

Fixes: google#353
@shivamtiwari3 shivamtiwari3 force-pushed the fix/use-logging-for-info-messages branch from 8c7d905 to 4722f5e Compare March 9, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Logging messages don't using logging

1 participant