Skip to content

Conversation

@EmptyRabbit
Copy link
Contributor

@EmptyRabbit EmptyRabbit commented Jun 15, 2022

Closes #9877

Add "when" property to LogCaptureHandler in _pytest/logging.py to fix caplog stage records not reset after caplog.clear()

caplog_handler.reset() set LogCaptureHandler.records a new list object, which lost bind to item.stash

As each running stage use one caplog_handler object and it's records object reference in item.stash item.stash[caplog_records_key][when] = caplog_handler.records. So after clear, any log records can not be captured to item.stash

@EmptyRabbit
Copy link
Contributor Author

I am a newer, please give your comment, thanks :)

@EmptyRabbit EmptyRabbit force-pushed the dev_main branch 2 times, most recently from 17775a5 to b0b313f Compare June 15, 2022 19:03
@EmptyRabbit
Copy link
Contributor Author

It seems like ci is blocked, I already forced push again but not useful. Could you help me to check? @nicoddemus

def clear(self) -> None:
"""Reset the list of log records and the captured log text."""
self.handler.reset()
self._item.stash[caplog_records_key][self.handler.when] = self.records
Copy link
Member

Choose a reason for hiding this comment

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

if done there, its already empty
the fix is incorrect as far as i can tell ,the correct fix location needs some extra details

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if done there, its already empty the fix is incorrect as far as i can tell ,the correct fix location needs some extra details

which object is already empty? I'm trying to understand it, thanks help!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if done there, its already empty the fix is incorrect as far as i can tell ,the correct fix location needs some extra details

after handler.reset(), handler.records object id is changed, it lost connection with item.stash[caplog_records_key][when], item.stash[caplog_records_key][when] is not empty

maybe use list.clear when caplog fixture clear the records is better?

class LogCaptureHandler(logging_StreamHandler):
    def clear(self) -> None:
        self.records.clear()
        self.stream = StringIO()

Copy link
Member

Choose a reason for hiding this comment

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

That would destroy data

Copy link
Contributor Author

@EmptyRabbit EmptyRabbit Jun 28, 2022

Choose a reason for hiding this comment

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

That would destroy data

Sorry, I am confused about the destroy data, it's only used for caplog fixture, each running stage will reset new records. My idea is each running stage should always bind unique stash caplog records, as LoggingCaptureHandler is reused. If I missing something else?

@RonnyPfannschmidt
Copy link
Member

@nicoddemus we have a little time bomb here - basically every handler reset ought to stash the outputs/records

however we drop handler state and disconnect the handler state from the stash

i beleive the internals need to change a bit, so that a handler can push new lists onto the stash, so the full set or the last used set can be requested

this requires careful consideration as the log record stashing is incomplete, and a handler clearing will disconnect the details

@nicoddemus
Copy link
Member

You mean Node._stash should hold lists and the last item?

@RonnyPfannschmidt
Copy link
Member

@nicoddemus exactly

@RonnyPfannschmidt
Copy link
Member

@nicoddemus this will also be a help for subtests, which should enable separation, just like capog sections

self.stream = StringIO()

def clear(self) -> None:
self.records.clear()
Copy link
Member

Choose a reason for hiding this comment

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

without moving the cleared items to a storage we cannot perceed

Copy link
Contributor Author

@EmptyRabbit EmptyRabbit Jun 28, 2022

Choose a reason for hiding this comment

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

do you mean cleared items some place is useful?

Copy link
Member

Choose a reason for hiding this comment

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

Basically clear removes all data, afterwards it's done, even from pytest internal store

Copy link
Contributor Author

@EmptyRabbit EmptyRabbit Jun 29, 2022

Choose a reason for hiding this comment

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

yes, caplog data will be discarded when clear, but another caplog_handler seems like storage all log records captured on stage

    def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, None]:
        """Implement the internals of the pytest_runtest_xxx() hooks."""
        with catching_logs(
            self.caplog_handler,
            level=self.log_level,
        ) as caplog_handler, catching_logs(
            self.report_handler,
            level=self.log_level,
        ) as report_handler:
            caplog_handler.reset()
            report_handler.reset()
            item.stash[caplog_records_key][when] = caplog_handler.records
            item.stash[caplog_handler_key] = caplog_handler

            yield

            log = report_handler.stream.getvalue().strip()
            item.add_report_section(when, "log", log)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there a hidden problem if not keep any cleared items in caplog object? I will keep learning the pytest frame to be familiar with the code logic!


def clear(self) -> None:
self.records.clear()
self.stream = StringIO()
Copy link
Member

Choose a reason for hiding this comment

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

do we actually use the streams, and if yes, how do we reconcile their usage

Copy link
Contributor Author

@EmptyRabbit EmptyRabbit Jun 28, 2022

Choose a reason for hiding this comment

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

caplog.text use stream to show content , and another handler is updated to "Captured log call" sections after stage finished. Since stream is not storage in item.stash, so I think it not a must unique.

Thank you for sharing your thoughtful idea, learning a lot


caplog.clear()

assert caplog.get_records("call") == []
Copy link
Member

Choose a reason for hiding this comment

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

i believe we need a distinction between current records and all records

Copy link
Contributor Author

@EmptyRabbit EmptyRabbit Jun 28, 2022

Choose a reason for hiding this comment

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

if want all records, should call caplog.get_records("setup"/"call"/"teardown") three times.....

Copy link
Member

Choose a reason for hiding this comment

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

@EmptyRabbit what i meant is, that right now we throw records from call away on clear, those wont return

Copy link
Contributor Author

@EmptyRabbit EmptyRabbit Jul 5, 2022

Choose a reason for hiding this comment

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

@RonnyPfannschmidt but those wont return records is useless for user, they hope to clear

when call caplog.get_records('setup') on call stage, it should be the same as get caplog.records on setup stage

if use self.records=[], will have bug like this:

def test_caplog_clear(caplog):
    logging.error('captured one log')
    assert caplog.messages == ['captured one log']
    assert [x.getMessage() for x in caplog.get_records('call')] == ['captured one log']

    caplog.clear()

    logging.error('captured two log')
    assert caplog.messages == ['captured two log']
    assert [x.getMessage() for x in caplog.get_records('call')] == ['captured two log']
    # ['captured one log'] != ['captured two log'], second log not be collected to item.stash

if we need keep all handler cleared records, it seems like a new feature.

Copy link
Member

Choose a reason for hiding this comment

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

ok, as im looking at this now again, it seems that i messed up

i missed the relation between the caplog handler and the report handler

so the thing i thought was missing, was in reality supported by the other handler

as such i beleive my concern does no longer stand and the new feature is a followup idea

Copy link
Contributor Author

@EmptyRabbit EmptyRabbit Jul 5, 2022

Choose a reason for hiding this comment

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

approved,Exciting!

find wrong people mentioned to at last comment , updated. sorry..

@EmptyRabbit EmptyRabbit reopened this Jun 29, 2022
@EmptyRabbit EmptyRabbit closed this Jul 6, 2022
@EmptyRabbit EmptyRabbit reopened this Jul 6, 2022
@EmptyRabbit
Copy link
Contributor Author

reopen to run checks

Co-authored-by: Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
@@ -0,0 +1 @@
Ensure ``caplog.get_records(when)`` returns current/correct data after invoking ``caplog.clear()``.
Copy link
Member

Choose a reason for hiding this comment

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

Took the liberty of fixing capitalization and punctuation. 😁

@RonnyPfannschmidt RonnyPfannschmidt merged commit 966d4fb into pytest-dev:main Jul 8, 2022
@EmptyRabbit EmptyRabbit deleted the dev_main branch July 9, 2022 14:37
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.

caplog.get_records and caplog.clear conflict

3 participants