-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Conversation
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? |
There was a problem hiding this 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.
src/_pytest/logging.py
Outdated
@@ -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) |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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
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' |
There was a problem hiding this 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( |
There was a problem hiding this comment.
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
. 😉
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably for historical reasons.
testing/logging/test_reporting.py
Outdated
@@ -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): |
There was a problem hiding this comment.
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. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops yes 😁
src/_pytest/logging.py
Outdated
@@ -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) |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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' ?
There was a problem hiding this comment.
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
.
testing/logging/test_reporting.py
Outdated
""" | ||
) | ||
p.move(sub.join(p.basename)) | ||
os.chdir(sub.strpath) |
There was a problem hiding this comment.
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. 👍
There was a problem hiding this comment.
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
testing/logging/test_reporting.py
Outdated
""" | ||
) | ||
p.move(sub.join(p.basename)) | ||
os.chdir(sub.strpath) |
There was a problem hiding this comment.
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. 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL, thanks
testing/logging/test_reporting.py
Outdated
""" | ||
) | ||
p.move(sub.join(p.basename)) | ||
os.chdir(sub.strpath) |
There was a problem hiding this comment.
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. 👍
changelog/7336.bugfix.rst
Outdated
@@ -0,0 +1 @@ | |||
The pytest ``--log-file`` cli or ``log_file`` ini marker is now relative to the configs ``inifile`` directory. |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
changelog/7336.bugfix.rst
Outdated
@@ -0,0 +1 @@ | |||
The pytest ``--log-file`` cli or ``log_file`` ini marker is now relative to the configs ``inifile`` directory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
dd98247
to
45d6a10
Compare
@@ -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]]]]: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this 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:
-
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. -
The
set_log_path
is usingrootdir
as the base path for relative input, this seems inconsistent (maybe it's OK though).
Thanks @bluetech I will revisit based on your insights |
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 |
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