pytest-catchlog icon indicating copy to clipboard operation
pytest-catchlog copied to clipboard

filter_record_tuples() implementation

Open TBeijen opened this issue 8 years ago • 6 comments

I find myself often using the following construct when asserting if a certain log message has been recorded:

import logging

def test_things(caplog):
    # things
    assert list(filter(lambda r: r[0] == 'my.module' and r[1] == logging.WARNING, caplog.record_tuples))

Some of the drawbacks:

  • Lot of repetition of constructs not part of the test, e.g. the need to wrap the filter result in list to be able to assert the length
  • Need to import logging merely to obtain the log level

I propose an extension to caplog that looks like this:

def test_things(caplog):
    # things
    assert caplog.filter_record_tuples('my.module', caplog.WARNING)

Please let me know your thoughts. Do I need to create an issue for this proposal?

Edit:

  • Changed caplogs.levels.WARNING into caplog.WARNING
  • Removed references to deprecated 'WARN' (Will not include it in levels exposed)
  • Removed 'Work in Progress' from PR

TBeijen avatar Jan 02 '17 11:01 TBeijen

@TBeijen Thanks for submitting this and happy New Year! :christmas_tree:

I like the idea in general, and I though about adding somewhat similar too. And if we finally agree on introducing this, I would also suggest to add a caplog.filter_records() method too, to be consistent with the caplog.records and caplog.record_tuples properties.

abusalimov avatar Jan 02 '17 21:01 abusalimov

@abusalimov Happy NY too!

Thanks for the swift reply. Implementing caplog.filter_records() had crossed my mind also. I doubted a bit as caplog.record_tuples seems intended for use in test asserts and wasn't sure of typical use cases of caplog.records. Will add.

On a sidenote: I'm now removing the .levels part so it will be caplog.WARNING etc.. The levels part didn't seem to add much and logging has it levels exposed as direct attributes also so this might be more intuitive.

TBeijen avatar Jan 03 '17 07:01 TBeijen

Done. See Readme change for features implemented.

I've disabled travis builds on pypy3, see (https://github.com/travis-ci/travis-ci/issues/6277#issuecomment-265128578)

I must say that after all I'm not so sure of the need to add filter_records as well. I couldn't come up with a use case where it would have any advantages over filter_record_tuples so I'd rather remove it (keeping things simple, YAGNI).

TBeijen avatar Jan 03 '17 15:01 TBeijen

I notice it's still marked as 'changes requested'. I think https://github.com/eisensheng/pytest-catchlog/pull/54/commits/385c440b475c7970a3b80b255c6eb53fcfd62db3 fixes that. Anything I can do to move this PR forward?

TBeijen avatar Apr 02 '17 09:04 TBeijen

@TBeijen ~~Please notice my comments above/~~ Sorry, they have been left pending for all these months. =\

abusalimov avatar Apr 03 '17 13:04 abusalimov

N/p that it takes some time, life can be busy.

Recursion was indeed not handled well. Fixed implementation and added a attribute lookup that will not be retrieved from logging module. (Sorry for polluting the history, should have run tox locally. Python 2.6 coding has been ages ago)

The combination of .isalpha() and .isupper() basically allows access to all log levels, but not to other attributes that don't fall into that pattern, like ._STYLES or .BASIC_FORMAT.

Custom loglevels, added for example via logging.setLevelName(13, 'custom'), are stored in logging._levelNames (python2) or logging._nameToLevel and logging._levelToName (python3).

The logging module itself uses .getLevelName() as described here to get a textual representation for a level. Illustrated by doing logging.getLevelName(123456) which returns Level 123456.

I would not go down the rabbit hole of looking up custom log levels. It's discouraged by python and adds complexity. If people insist on custom log levels they can always import logging itself in the tests.

Looking back at previous commits I actually think the explicit list of levels was better (because: explicit) than the isalpha(), isupper() combo (or lvl == lvl.upper() for that matter). Those levels haven't changed ever and are based on good practices.

TBeijen avatar Apr 08 '17 12:04 TBeijen