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

ntpath.py: ValueError: path is on mount 'C:', start on mount 'D:' error shows after calling get_full_context() from a different drive on Windows 10 #55

Closed
kr1zo opened this issue Feb 18, 2021 · 5 comments
Labels

Comments

@kr1zo
Copy link
Contributor

kr1zo commented Feb 18, 2021

Summary:

The check_methods.get_full_context(level) function does not work correctly on Windows 10, error in determining filename on line 195: filename = os.path.relpath(filename)

Detailed description:

If the function was called from a file on the D drive and filename is on the C drive, then os.path.relpath will give an error:
ValueError: path is on mount 'C:', start on mount 'D:'

Pytest will failed on the ValueError exception from module ntpath.py line 703.

This behavior is possible if we override the call of any pytest_check function not from the test file, but from the module file, which is located on a different disk than the test.
What I mean is function overrides can be getted through Handlers and inheritance.

I have prepared a vivid example with overriding the behavior of the logging function.
When calling logging.info(), pytest_check.is_none() will also be called to check the assertion which will always fail.

We will also intercept all pytest logging during pytest_runtest_call()
This is where we can initialize pytest_check.is_none() from one drive while the test is on another a drive.

Perhaps there are inaccuracies in terminology in my description, I think it will be clear from the code below what I mean.

For example:

We have Python 3.9.1 with modules (logging, pytest, pytest_check) installed on disc C:/Python391/
We have tests with conftest installed on disc D:/test/ with this code:

D:/test/conftest.py:

import logging
import pytest_check


class MyHandler(logging.Handler):
    def emit(self, record):
        pytest_check.is_none(record.getMessage())


def pytest_runtest_call():
    logger = logging.getLogger()
    my_handler = MyHandler()

    logger.addHandler(my_handler)

D:/test/test_logging_assert.py:

import logging


def test_logging_assert(request):
    logging.info('not none')

Run the command line from the test folder on the D drive:

D:\test> C:/Python391/python.exe -m pytest --log-cli-level=info test_logging_assert.py

Actual result:

D:\test> C:/Python391/python.exe -m pytest --log-cli-level=info test_logging_assert.py
================================================= test session starts =================================================
platform win32 -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\test
plugins: allure-pytest-2.8.33, pytest_check-1.0.1, rerunfailures-9.1.1
collected 1 item

test_logging_assert.py::test_logging_assert
---------------------------------------------------- live log call ----------------------------------------------------
INFO     root:test_logging_assert.py:5 not none
FAILED                                                                                                           [100%]

====================================================== FAILURES =======================================================
_________________________________________________ test_logging_assert _________________________________________________

args = ('not none',), kwds = {}, __tracebackhide__ = True

    @functools.wraps(func)
    def wrapper(*args, **kwds):
        __tracebackhide__ = True
        try:
>           func(*args, **kwds)

C:\Python391\lib\site-packages\pytest_check\check_methods.py:84:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

x = 'not none', msg = ''

    @check_func
    def is_none(x, msg=""):
>       assert x is None, msg
E       AssertionError:
E       assert 'not none' is None

C:\Python391\lib\site-packages\pytest_check\check_methods.py:127: AssertionError

During handling of the above exception, another exception occurred:

request = <FixtureRequest for <Function test_logging_assert>>

    def test_logging_assert(request):
>       logging.info('not none')

test_logging_assert.py:5:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Python391\lib\logging\__init__.py:2085: in info
    root.info(msg, *args, **kwargs)
C:\Python391\lib\logging\__init__.py:1434: in info
    self._log(INFO, msg, args, **kwargs)
C:\Python391\lib\logging\__init__.py:1577: in _log
    self.handle(record)
C:\Python391\lib\logging\__init__.py:1587: in handle
    self.callHandlers(record)
C:\Python391\lib\logging\__init__.py:1649: in callHandlers
    hdlr.handle(record)
C:\Python391\lib\logging\__init__.py:948: in handle
    self.emit(record)
conftest.py:7: in emit
    pytest_check.is_none(record.getMessage())
C:\Python391\lib\site-packages\pytest_check\check_methods.py:195: in get_full_context
    filename = os.path.relpath(filename)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

path = 'C:\\Python391\\lib\\logging\\__init__.py', start = '.'

    def relpath(path, start=None):
        """Return a relative version of a path"""
        path = os.fspath(path)
        if isinstance(path, bytes):
            sep = b'\\'
            curdir = b'.'
            pardir = b'..'
        else:
            sep = '\\'
            curdir = '.'
            pardir = '..'

        if start is None:
            start = curdir

        if not path:
            raise ValueError("no path specified")

        start = os.fspath(start)
        try:
            start_abs = abspath(normpath(start))
            path_abs = abspath(normpath(path))
            start_drive, start_rest = splitdrive(start_abs)
            path_drive, path_rest = splitdrive(path_abs)
            if normcase(start_drive) != normcase(path_drive):
>               raise ValueError("path is on mount %r, start on mount %r" % (
                    path_drive, start_drive))
E                   ValueError: path is on mount 'C:', start on mount 'D:'

C:\Python391\lib\ntpath.py:703: ValueError
-------------------------------------------------- Captured log call --------------------------------------------------
INFO     root:test_logging_assert.py:5 not none
=============================================== short test summary info ===============================================
FAILED test_logging_assert.py::test_logging_assert - ValueError: path is on mount 'C:', start on mount 'D:'
================================================== 1 failed in 0.27s ==================================================

D:\test> 

Expected Result:

D:\test> C:/Python391/python.exe -m pytest --log-cli-level=info test_logging_assert.py
================================================= test session starts =================================================
platform win32 -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\test
plugins: allure-pytest-2.8.33, pytest_check-1.0.1, rerunfailures-9.1.1
collected 1 item

test_logging_assert.py::test_logging_assert
---------------------------------------------------- live log call ----------------------------------------------------
INFO     root:test_logging_assert.py:5 not none
FAILED                                                                                                           [100%]

====================================================== FAILURES =======================================================
_________________________________________________ test_logging_assert _________________________________________________
FAILURE:
assert 'not none' is None
D:\test\test_logging_assert.py:5 in test_logging_assert() -> logging.info('not none')
C:\Python391\lib\logging\__init__.py:2085 in info() -> root.info(msg, *args, **kwargs)
C:\Python391\lib\logging\__init__.py:1434 in info() -> self._log(INFO, msg, args, **kwargs)
C:\Python391\lib\logging\__init__.py:1577 in _log() -> self.handle(record)
C:\Python391\lib\logging\__init__.py:1587 in handle() -> self.callHandlers(record)
C:\Python391\lib\logging\__init__.py:1649 in callHandlers() -> hdlr.handle(record)
C:\Python391\lib\logging\__init__.py:948 in handle() -> self.emit(record)
D:\test\conftest.py:7 in emit() -> pytest_check.is_none(record.getMessage())
------------------------------------------------------------
Failed Checks: 1
-------------------------------------------------- Captured log call --------------------------------------------------
INFO     root:test_logging_assert.py:5 not none
=============================================== short test summary info ===============================================
FAILED test_logging_assert.py::test_logging_assert
================================================== 1 failed in 0.17s ==================================================

D:\test>

Possible solution:

Change finding os.path.relpath to finding os.path.abspath:
filename = os.path.abspath(filename)

Environment:

OS: Windows 10
Python: 3.9.1
Pytest: 6.2.2
Pytest check: 1.0.1

Pull request #54

@okken
Copy link
Owner

okken commented Mar 30, 2021

This seems like a defect against relpath, not against check.

@okken okken closed this as completed Mar 30, 2021
@rloutrel
Copy link

@okken : Can we consider, it is not expected that os.path return an absolute path when calling relpath and the current behvior is not really a bug, but that the function caller should handle the exception?

May I work on a fix for this where the exception is caught and fallback in abspath, like:

try:    
    filename = os.path.relpath(filename)
except ValueError as error:
    filename = os.path.abspath(filename)

it would produce the long path ONLY when the relative path is not possible to be built (like on wind**s system with multiple drives)

rloutrel pushed a commit to rloutrel/pytest-check that referenced this issue Aug 24, 2022
…lback if the relpath did not work (baase on the exception produced on window with several drives)
@rloutrel
Copy link

Hi @okken , actually, I did already produce a first fix proposition, so that you can directly accept/reject my offer. By rejection, please give me a reason, so that I can offer a more appropriate solution.

I hope it will be an acceptable compromise for you by using the relative path as default (I understood that you did not like the first fix because you do not like the absolute path structure, what is understandable), but providing a cleaner fallback solution based on absolute path when the relative path does not work. It will avoid testers on windows to get "unreadable" feedback with exception cascading (I am Linux fan, but unfortunatly my clients do not...).

@okken
Copy link
Owner

okken commented Aug 24, 2022

I'll take a look as soon-ish, life permitting.
My apologies for the delay

@okken okken reopened this Aug 24, 2022
okken added a commit that referenced this issue Aug 25, 2022
Alternative fix proposition for #55 : the abspath is used as fallback…
@okken okken added the bug label Aug 25, 2022
@okken
Copy link
Owner

okken commented Aug 25, 2022

fixed with 1.0.9
Thanks to both @rloutrel and @kr1zo for patience and persistence.
I hope it's ok that I gave both of you shoutouts in the changelog.

@okken okken closed this as completed Aug 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants