Skip to content

Improved logging#252

Open
rozyczko wants to merge 7 commits into
developfrom
improved_logging
Open

Improved logging#252
rozyczko wants to merge 7 commits into
developfrom
improved_logging

Conversation

@rozyczko

Copy link
Copy Markdown
Member

Switched from direct warnings.warn and print statements to Python's standard logging module throughout the codebase.

  • Replaced all uses of warnings.warn and print for runtime and deprecation warnings with appropriate calls to the logging module, using named loggers under the easyscience hierarchy for better control and filtering.

  • Added a new user guide page, controlling-log-output.md, detailing how to suppress, filter, or redirect EasyScience log messages.

  • No global reconfiguration, no default stream handlers, and all loggers inherit configuration, giving users and downstream libraries full control over log output.

@rozyczko rozyczko added [scope] enhancement Adds/improves features (major.MINOR.patch) [priority] medium Normal/default priority [area] base classes Changes to or creation of new base classes labels May 21, 2026
@codecov

codecov Bot commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.61538% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.92%. Comparing base (d4a4c23) to head (5f27b96).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
src/easyscience/global_object/hugger/property.py 10.00% 9 Missing ⚠️
src/easyscience/fitting/available_minimizers.py 25.00% 3 Missing ⚠️
src/easyscience/legacy/xml.py 0.00% 2 Missing ⚠️
src/easyscience/global_object/undo_redo.py 75.00% 1 Missing ⚠️
src/easyscience/utils/decorators.py 0.00% 1 Missing ⚠️
src/easyscience/variable/parameter.py 0.00% 1 Missing ⚠️
...yscience/variable/parameter_dependency_resolver.py 75.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #252      +/-   ##
===========================================
+ Coverage    82.55%   82.92%   +0.36%     
===========================================
  Files           62       62              
  Lines         4946     5082     +136     
===========================================
+ Hits          4083     4214     +131     
- Misses         863      868       +5     
Flag Coverage Δ
integration 43.95% <42.73%> (+0.46%) ⬆️
unittests 82.40% <83.76%> (+1.13%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/easyscience/base_classes/collection_base.py 100.00% <100.00%> (ø)
src/easyscience/base_classes/easy_list.py 96.20% <100.00%> (ø)
src/easyscience/base_classes/obj_base.py 100.00% <100.00%> (ø)
...syscience/fitting/calculators/interface_factory.py 97.27% <100.00%> (+0.02%) ⬆️
src/easyscience/fitting/fitter.py 97.29% <100.00%> (+0.39%) ⬆️
.../easyscience/fitting/minimizers/minimizer_bumps.py 95.95% <100.00%> (-3.52%) ⬇️
...rc/easyscience/fitting/minimizers/minimizer_dfo.py 96.51% <100.00%> (ø)
.../easyscience/fitting/minimizers/minimizer_lmfit.py 97.22% <100.00%> (ø)
src/easyscience/global_object/logger.py 100.00% <100.00%> (+30.00%) ⬆️
src/easyscience/legacy/collection_base.py 97.65% <100.00%> (ø)
... and 8 more

... and 1 file with indirect coverage changes

@rozyczko rozyczko marked this pull request as ready for review May 25, 2026 19:20

@damskii9992 damskii9992 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have an ADR on the logger to explain why we implemented it, and instructions on how to use it, both when writing to it, and when ignoring it (I guess the last one can simply refer to the .md file you wrote).

Comment on lines +143 to +146
@pytest.fixture(autouse=True)
def _quiet_easyscience():
with global_object.log.at_level(logging.ERROR):
yield

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this work? You're only muting the log in the context, so how would this actually mute the log in a test? Wouldn't

global_object.log.set_level('ERROR')

Be better?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

autouse fixture yields inside with at_level(logging.ERROR), so the test body runs within the context and is muted. Your set_level('ERROR') also works and is marginally simpler, but the code is correct


```python
from easyscience import global_object
import logging

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to import logging, right?

Comment on lines +189 to +191
- **No default stream handlers** — EasyScience does not attach handlers
that write to `stdout` or `stderr`. It only creates log records.
Applications and test frameworks decide where those records go.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this bad? What about users that use easyscience as a standalone fitting package? Do they not see any messages without configuring it? They should . . .

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python's logging.lastResort handler emits WARNINGs and above to stderr when no handlers are
configured, so standalone easyscience users do see warnings/errors out of the box.
Only INFO/DEBUG are hidden without change of setup.

Comment thread src/easyscience/global_object/logger.py Outdated
import contextlib
import logging
import os
from typing import Optional

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ehh, no Optional typing, use |

Comment thread src/easyscience/global_object/logger.py Outdated
upper = stripped.upper()
if upper in _LEVEL_NAME_MAP:
return _LEVEL_NAME_MAP[upper]
return default

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should probably raise in the end. If you try to set a logging level, but make a spelling mistake, we shouldn't silently return the default.
Also, why the hell is the default a separate parameter? Why not just the default of raw? Which probably also should have a better parameter name (level?)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this method probably shouldn't have any defaults . . .

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

silently falling back to a default on a bad value is the right behaviour. You don't want import to crash on a typo'd env var. This is for env var parsing.
FOr public API (set_level/at_level) tthis should raise. I think we need to split the concerns here
Strict parse for the API
lenient parse (default) for env var

Comment on lines -99 to +102
print(f'Unable to auto generate bindings.\n{e}')
logging.getLogger('easyscience.fitting.calculators').warning(
'Unable to auto generate bindings.\n%s', e
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you will ctrl+f to replace this everywhere, but just adding another comment here so you don't forget.

f'Fit did not converge within the maximum optimizer steps of {max_evaluations} '
f'({n_evaluations} objective evaluations). '
'Consider increasing the maximum number of evaluations or adjusting the tolerance.',
UserWarning,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a problem that we lose the categories?

# TODO make this a proper message (use logging?)
warnings.warn(
'LMFit minimization is not available. Probably lmfit has not been installed.',
ImportWarning,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another category lost

logging.getLogger('easyscience.deprecated').warning(
'Call to deprecated function %s.',
func.__name__,
stacklevel=3,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stacklevel? Is that a feature in the logging module? You haven't used this elsewhere.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stacklevel is a supported kwarg on logging methods and here it correctly points the warning at the caller of the deprecated function.
Not having it elsewhere is a fair point. I'll add it to other deprecation log locations for consistency

Comment thread pyproject.toml Outdated
'COM812', # https://docs.astral.sh/ruff/rules/missing-trailing-comma/
# The following is replaced by 'D'/[tool.ruff.lint.pydocstyle] and [tool.pydoclint]
'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc
# The following is replaced by 'D'/[tool.ruff.lint.pydocstyle] and [tool.pydoclint] 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also accidentally reverted this to comment out the DOC rule :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cause pixi run check is messing it up!!!
I need to change it once and for all

@rozyczko

Copy link
Copy Markdown
Member Author

I think we should have an ADR on the logger to explain why we implemented it, and instructions on how to use it, both when writing to it, and when ignoring it (I guess the last one can simply refer to the .md file you wrote).

#277

@rozyczko rozyczko requested a review from damskii9992 June 16, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[area] base classes Changes to or creation of new base classes [priority] medium Normal/default priority [scope] enhancement Adds/improves features (major.MINOR.patch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants