Skip to content

add append option to LoggingPlugin.set_log_path and return old path #9687

Open
@RolfBippus

Description

@RolfBippus

What's the problem this feature will solve?

set_log_path() currently overwrites existing log-files.
I would like to add fixtures that produce e.g. a logfile per module (scope = 'module') or for selected tests (scope = 'function').
Using set_log_path() is great for this, however the old path (if it existed) cannot be restored when upon fixture teardown. This does not allow the fixture to be e.g. combined with the --log-file commandline option for tests not using the fixture.

Describe the solution you'd like

Thus I propose to return the old path and to add an option to append to the file instead of overwriting it.

Alternative Solutions

I have tried different things, however using the set_log_path() seems the most simple way to achieve this.

Additional context

Here are the proposed changes to set_log_path:

    def set_log_path(self, fname: str, append=False) -> None:
        """Set the filename parameter for Logging.FileHandler().

        Creates parent directory if it does not exist.

        .. warning::
            This is an experimental API.
        """
        fpath = Path(fname)

        if not fpath.is_absolute():
            fpath = self._config.rootpath / fpath

        if not fpath.parent.exists():
            fpath.parent.mkdir(exist_ok=True, parents=True)

        stream = fpath.open(mode="w" if not append else "a", encoding="UTF-8")
        if sys.version_info >= (3, 7):
            old_stream = self.log_file_handler.setStream(stream)
        else:
            old_stream = self.log_file_handler.stream
            self.log_file_handler.acquire()
            try:
                self.log_file_handler.flush()
                self.log_file_handler.stream = stream
            finally:
                self.log_file_handler.release()
        old_path = None
        if old_stream:
            old_path = os.path.abspath(old_stream.name)
            old_stream.close()
        return old_path

Example of application for a fixture generating one log file per test:

It allows to write a specific logfile for selected tests and at the same time still allows the usage of --log-file from the commandline to work on all other tests. (attention, I added a pytestconfig.option.logdir option)

@pytest.fixture(scope='function')
def logfile_per_test(request, pytestconfig):
    """
    A fixture that creates a logfile per test/ function call.
    If the function is parametrized, one logfile per call(!) is created.
    """
    logging_plugin=request.config.pluginmanager.get_plugin("logging-plugin")
    filename=os.path.join(pytestconfig.option.logdir,
                          request.module.__name__ + '__' + request.node.name.replace('[','__').replace(']', '__') +".log")
    oldfile = logging_plugin.set_log_path(filename, append=False) # this overwrites the logfile!
    # write some intitial info to the logfile
    log = logging.getLogger(request.module.__name__)
    log.info(f"========= {request.module.__name__}.{request.node.name} =======================")
    
    yield
    
    # write some closing info to the logfile
    log.info(f"========= finished {request.module.__name__}.{request.node.name} ==============")
    
    # restore the old logfile, in case there had been one, and append to it
    if oldfile:
        logging_plugin.set_log_path(oldfile, append=True) # this further appends to the existing logfile!

Metadata

Metadata

Assignees

No one assigned

    Labels

    plugin: loggingrelated to the logging builtin plugintype: proposalproposal for a new feature, often to gather opinions or design the API around the new feature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions