Skip to content

replace all print statements with proper logging #130

Closed

Description

@simontaurus

We should take the chance of the current refactoring to also replace noisy print statements with the loggin module

https://docs.python.org/3/library/logging.html

Activity

  1. LukasGold commented on Sep 2, 2026

    @LukasGold
    Contributor

    Scope

    An AST pass over src/osw finds 123 print() calls in real code, spread over 15 modules, led by core.py (29), wtsite.py (23), utils/wikitext.py (17), ontology.py (12) and wiki_tools.py (12). A further 22 sit in the if __name__ == "__main__": demo block of utils/util.py and 47 are already commented out. Both of those groups stay as they are.

    Only two modules use logging today, and neither consistently: core.py:71 has a proper _logger = logging.getLogger(__name__) used for five error calls while the same file still has 29 prints, and utils/workflow.py imports logging inside function bodies at lines 195 and 654.

    Decisions

    Logging is on by default. src/osw/__init__.py attaches a StreamHandler to the osw logger at INFO, writing to sys.stdout, which is where the prints it replaces went. A library that silently swallows its own output would break every existing notebook and script at once, so the default keeps today's behaviour visible.

    A one-time notice on import says so and says how to change it:

    [INFO] osw: osw logs at INFO. Use osw.set_log_level('WARNING') to see less,
    osw.disable_logging() to switch it off, or set OSW_LOG_LEVEL=OFF before import.
    

    New public API: osw.set_log_level(level), osw.disable_logging(), osw.enable_logging(level=None, stream=None), and the OSW_LOG_LEVEL environment variable, which accepts a level name, a level number, or OFF. The environment variable is the only way to pre-empt the notice itself, since it is read before the notice is emitted.

    propagate is set to False while the default handler is attached, so callers who have already run logging.basicConfig() do not see every message twice. disable_logging() removes only the handler osw attached itself and restores propagation, which hands control back to the calling application.

    Level mapping per call site, from the message content: error for reported failures, warning for retries and skipped or tolerated items, debug for verbose detail and anything behind a debug flag, info for ordinary progress, which is most of them. Messages keep f-string style, matching the existing _logger.error(f"...") at src/osw/core.py:1294.

    The parallelize interaction

    #154 had just rebuilt output capture in parallelize around ThreadRoutedStdout, which replaces sys.stdout for the duration of a call and gives each worker thread its own buffer, replayed in input order when flush_at_end is set. That works because print() resolves sys.stdout at call time. Converting the workers to logging would have bypassed it completely and silently undone that fix, since a StreamHandler writes to a stream it captured at construction and defaults to stderr.

    So this change adds ThreadRoutedLogHandler, the log counterpart, and parallelize now buffers and replays LogRecords alongside the printed text. The stdout router stays, because an external caller passing its own printing function to parallelize should keep working.

    One detail worth recording: the routing has to be installed as a handler on the osw logger, not as a filter. A logger's filters run only for records logged on that logger itself, so a filter on osw would never see records from osw.wtsite and the other child loggers, which is exactly the traffic that needs capturing.

    Out of scope

    • tests/, examples/ and scripts/.
    • Any change to what the messages say. This is a transport change, not an editing pass.
    • The dask remnants in utils/util.py.

    Sequencing

    The sweep touches 15 files and conflicts with most of the open PR queue, so it is held until the rest of the queue drains, then rebased onto main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions