Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#7336 make --log-file relative to inifile if it exists #7350

Closed

Conversation

symonk
Copy link
Member

@symonk symonk commented Jun 11, 2020

As per #7336, I think the correct behaviour is to keep the --log-file relative to the inifile? From what I gather the inifile can be None; so this PR makes the --log-file relative to that directory in the event that it exists; not sure how to proceed if it is None in a good manner. (assume the current behaviour of putting the log file in the execution directory?)

Close #7336

@symonk symonk marked this pull request as ready for review June 11, 2020 13:13
@symonk symonk requested a review from nicoddemus June 11, 2020 19:12
@gnikonorov
Copy link
Member

While this is technically beyond the scope of the linked issue can we allow for the user to provide an absolute path for a log file, in which case we do not make the log file relative to anything?

Copy link
Member

@bluetech bluetech left a comment

Choose a reason for hiding this comment

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

Hi @symonk, some comments.

Besides __init__, the function set_log_path contains some logic relating to this, and there it is using rootdir. They should probably be reconciled in some way.

@@ -527,6 +527,11 @@ def __init__(self, config: Config) -> None:
# File logging.
self.log_file_level = get_log_level_for_setting(config, "log_file_level")
log_file = get_option_ini(config, "log_file") or os.devnull

# Keep the log file relative to the inidir file (if it exists) (#7336)
inidir = getattr(getattr(config, "inifile", None), "dirname", None)
Copy link
Member

Choose a reason for hiding this comment

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

At this point config.inifile should always be defined (unless I'm mistaken), so getattr(config, "inifile", None) can be replaced with just config.inifile.

inifile can be None, but instead of using getattr on it, I think it is better do use an explicit is None check. getattr() is using a dynamic feature of Python which is not analyzed and type checked, so I for one prefer to avoid it unless the dynamicism is really needed (e.g. when the name is not a constant).

Finally, I think currently there may be some cases where inifile may be an str rather than a py.path.local, but that is more of a bug (I'm just working on that as a follow up for the recent TOML PR).

Copy link
Member Author

@symonk symonk Jun 11, 2020

Choose a reason for hiding this comment

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

Hmm I seem to be mistaken, I thought i was getting a type:str in some cases; and no 'dirname' attr, but it seems if config.inifile is not None, it is always path.local (as far as our current test suite goes)

Copy link
Member Author

Choose a reason for hiding this comment

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

mypy hates:

src\_pytest\logging.py:533: error: "str" has no attribute "dirname"  [attr-defined]
Found 1 error in 1 file (checked 2 source files)

I assume some type hinting on the config obj - will check

@bluetech
Copy link
Member

While this is technically beyond the scope of the linked issue can we allow for the user to provide an absolute path for a log file, in which case we do not make the log file relative to anything?

You usually get this "for free" from the way path-joining works:

>>> import os.path
>>> os.path.join('/the/log/dir', '/an/absolute/path')
'/an/absolute/path'

Copy link
Member

@nicoddemus nicoddemus left a comment

Choose a reason for hiding this comment

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

Thanks @symonk,

Looks good, just left a few comments. 👍

def test_log_file_is_in_pytest_ini_rootdir(testdir):
# as per https://docs.pytest.org/en/5.4.3/reference.html log_file
# the file should be relative to the pytest inifile
testdir.makefile(
Copy link
Member

Choose a reason for hiding this comment

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

No need to change, but you can also use makeinifile. 😉

Copy link
Member Author

Choose a reason for hiding this comment

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

ah i got confused as it made a 'tox' file :)

Copy link
Member

Choose a reason for hiding this comment

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

Probably for historical reasons.

@@ -1152,3 +1152,82 @@ def test_bad_log(monkeypatch):
)
result = testdir.runpytest()
result.assert_outcomes(passed=1)


def test_log_file_is_in_pytest_ini_rootdir(testdir):
Copy link
Member

@nicoddemus nicoddemus Jun 11, 2020

Choose a reason for hiding this comment

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

Please add docstrings to each test referencing #7350, so it is easier to find the issue that lead to the creation of the test later. 👍

Copy link
Member Author

@symonk symonk Jun 11, 2020

Choose a reason for hiding this comment

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

thanks, i assume a typo of #7350/#7336 - will update

Copy link
Member

Choose a reason for hiding this comment

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

oops yes 😁

@@ -527,6 +527,11 @@ def __init__(self, config: Config) -> None:
# File logging.
self.log_file_level = get_log_level_for_setting(config, "log_file_level")
log_file = get_option_ini(config, "log_file") or os.devnull

# Keep the log file relative to the inidir file (if it exists) (#7336)
inidir = getattr(getattr(config, "inifile", None), "dirname", None)
Copy link
Member

Choose a reason for hiding this comment

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

I realize this is a matter of opinion, but this seems a bit contrived, I would instead use this:

if log_file != os.devnull and config.inifile:
    log_file = os.path.join(config.inifile.dirname, log_file)

(No need for the getattr chain)

Copy link
Member Author

@symonk symonk Jun 11, 2020

Choose a reason for hiding this comment

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

Thanks, any advice on fixing:

src\_pytest\logging.py:533: error: "str" has no attribute "dirname"  [attr-defined]
Found 1 error in 1 file (checked 2 source files)

As a result?

I see:

def determine_setup(
    inifile: Optional[str],
    args: List[str],
    rootdir_cmd_arg: Optional[str] = None,
    config: Optional["Config"] = None,
) -> Tuple[py.path.local, Optional[str], Dict[str, Union[str, List[str]]]]:

should this be a path local?

if im not mistaken, dont we want to remove references to 'py' ?

Copy link
Member

@nicoddemus nicoddemus Jun 12, 2020

Choose a reason for hiding this comment

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

That's weird, locate_config returns:

Tuple[
    Optional[py.path.local], Optional[py.path.local], Dict[str, Union[str, List[str]]],
]

(Note that the 2nd item is Optional[py.path.local])

Here's a snippet from determine_setup, which calls locate_config:

def determine_setup(
    inifile: Optional[str],
    args: List[str],
    rootdir_cmd_arg: Optional[str] = None,
    config: Optional["Config"] = None,
) -> Tuple[py.path.local, Optional[str], Dict[str, Union[str, List[str]]]]:
    rootdir = None
    dirs = get_dirs_from_args(args)
    if inifile:  # (1)
        inicfg = load_config_dict_from_file(py.path.local(inifile)) or {}
        if rootdir_cmd_arg is None:
            rootdir = get_common_ancestor(dirs)
    else:  # (2)
        ancestor = get_common_ancestor(dirs)
        rootdir, inifile, inicfg = locate_config([ancestor])
        if rootdir is None and rootdir_cmd_arg is None:
            ...
    return rootdir, inifile, inicfg or {}

In 1, indeed inifile ends up as a string by the time of return, but not when we get into 2, where inifile is a py.path.local (because of the locate_config call).

Shouldn't mypy catch that?

Regardless, I think we should change the return value from determine_setup to always return a py.path.local.

"""
)
p.move(sub.join(p.basename))
os.chdir(sub.strpath)
Copy link
Member

Choose a reason for hiding this comment

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

Use monkeypatch.chdir as it is safer. 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

ah yes, auto revert post-test execution i assume? thanks for the tip

"""
)
p.move(sub.join(p.basename))
os.chdir(sub.strpath)
Copy link
Member

Choose a reason for hiding this comment

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

Use monkeypatch.chdir as it is safer. 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

TIL, thanks

"""
)
p.move(sub.join(p.basename))
os.chdir(sub.strpath)
Copy link
Member

Choose a reason for hiding this comment

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

Use monkeypatch.chdir as it is safer. 👍

@@ -0,0 +1 @@
The pytest ``--log-file`` cli or ``log_file`` ini marker is now relative to the configs ``inifile`` directory.
Copy link
Member

Choose a reason for hiding this comment

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

this is a breaking change, the filename shoudld reflect that

Copy link
Member

Choose a reason for hiding this comment

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

Hmm good point, although the docs say otherwise, indeed this might break users.

@@ -0,0 +1 @@
The pytest ``--log-file`` cli or ``log_file`` ini marker is now relative to the configs ``inifile`` directory.
Copy link
Member

@nicoddemus nicoddemus Jun 11, 2020

Choose a reason for hiding this comment

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

Suggested change
The pytest ``--log-file`` cli or ``log_file`` ini marker is now relative to the configs ``inifile`` directory.
The pytest ``--log-file`` cli or ``log_file`` ini marker is now relative to the ``inifile`` directory, as it was always the intention. It was originally introduced as relative to the current working directory unintentionally.

@symonk symonk force-pushed the log-file-relative-to-inifile-7336 branch from dd98247 to 45d6a10 Compare June 11, 2020 23:09
@@ -161,7 +161,7 @@ def determine_setup(
args: List[str],
rootdir_cmd_arg: Optional[str] = None,
config: Optional["Config"] = None,
) -> Tuple[py.path.local, Optional[str], Dict[str, Union[str, List[str]]]]:
) -> Tuple[py.path.local, Optional[py.path.local], Dict[str, Union[str, List[str]]]]:
Copy link
Member

Choose a reason for hiding this comment

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

Please make sure to convert inifile to a py.path.local inside if inifile: below to honor this type annotation.

Copy link
Member

Choose a reason for hiding this comment

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

I have a PR locally which addresses these issues (by fixing errors after running mypy with pytest-dev/py#232). I'll post it in a bit.

Copy link
Member Author

@symonk symonk Jun 12, 2020

Choose a reason for hiding this comment

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

test_inifilename hates such change(s) - investigating; maybe the test should be just updated:

'../../foo/bar.ini'  # becomes:
C:\Users\sy\AppData\Local\Temp\pytest-of-sy\pytest-972\test_inifilename0\foo\bar.ini 

Copy link
Member

@bluetech bluetech left a comment

Choose a reason for hiding this comment

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

Formally registering two issues I mentioned above:

  1. This makes log_file relative to inidir, however it also affects the --log-file command line argument, where it is expected to be relative to the invocation dir, IMO.

  2. The set_log_path is using rootdir as the base path for relative input, this seems inconsistent (maybe it's OK though).

@symonk
Copy link
Member Author

symonk commented Jun 12, 2020

Thanks @bluetech I will revisit based on your insights

@symonk
Copy link
Member Author

symonk commented Jun 17, 2020

Sorry been busy, going to look at this again; I will assume --log-file should remain untouched and only the ini marker for log_file should be relative to inifile

@Hardy7cc

This comment has been minimized.

@nicoddemus

This comment was marked as resolved.

Base automatically changed from master to main March 9, 2021 20:40
@Zac-HD Zac-HD mentioned this pull request Apr 21, 2022
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.

inaccurate documentation
6 participants