problem with `os.stat` within `eyed3.load` when running multiple tests using `fs` based fixture
Maintainers usually reply within 2 days
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 35/100
Research direction
Reproduce the sequential fixture case from conftest.py and test_copy_audio_with_fs, then trace the failing path through pyfakefs/fake_os.py and fake_filesystem.py alongside eyed3.load and eyed3/mp3/init.py. Done means the interaction has a confirmed root cause and an agreed regression test or fix.
Written by the indexing model from the issue text.
Description
Describe the bug
Hello together, I have hard times finding out what causes the following error: I am using the fs (not fs_module etc. -> fs) inside a fixture. There is one test, that, when ran in isolation, succeeds. But when it runs together (sequentially) with any other test that uses the fs based fixture it fails.
TLDR
Of course the first thought is, are there any other filesystem actions colliding with the test but I think i ruled those out:
1: fs is used on a per test basis (as far as I can read from the docs only fs_module etc. are scoped broader than function, right?)
2: I added some debugging lines that verify the existence of the file which a code line later causes a fail. Also, before the failing line os.stat is called within eyed3.load method, eyed3 performs some internal checks like if not path.is_file() that are passed. So the pure existance of the file should be given I think. more can be read here: https://github.com/nicfit/eyeD3/blob/2cdef0d7542e65cc2756d63c2afffbdf48061eea/eyed3/mp3/__init__.py#L93
How To Reproduce
This is the test setup and test code up to the point where it raises:
# conftest.py
@pytest.fixture(scope="function")
def test_fs(fs):
yield fs
# testmethod
@e2e
def test_copy_audio_with_fs(
self,
sut: AudioFileManager,
test_assets_dir: str,
test_fs,
):
# GIVEN
test_fs.add_real_directory(str(test_assets_dir))
source_file_path = os.path.join(test_assets_dir, "0001.mp3")
target_file_path = os.path.join(
test_assets_dir,
"target_repo",
"9999.mp3",
)
os.makedirs(
os.path.join(test_assets_dir, "target_repo"),
)
# WHEN
sut.copy_audio( # <-- this line calls, besides others, the function _read_audio() that raises
source_file_path,
target_file_path,
)
The following code is where it leaves my codebase and raises the error within eyed3:
def _read_audio(self, file_path: str) -> eyed3.AudioFile:
# some debugging lines ...
print(f"file_path: {file_path}")
print(f"files in parent dir: {os.listdir(os.path.dirname(file_path))}")
print(f"is parent a dir: {os.path.isdir(os.path.dirname(file_path))}")
print(f"is file_path a file: {os.path.isfile(file_path)}")
print(f"size_bytes: {os.stat(file_path)[stat.ST_SIZE]}") # <-- this is the line used in eyed3.load internally that throws, see down below the stack trace
audio = eyed3.load(file_path) # <-- this line throws when reaching the code line equal to the above one, see down below the stack trace
# more stuff here that is irrelevant, because the line before fails
return audio
The resulting debugging output is this:
file_path: [...]\tests\test_assets\target_repo\9999.mp3
files in parent dir: ['9999.mp3']
is parent a dir: True
is file_path a file: True
size_bytes: 3650
The error thrown/stacktrace is:
sut.copy_audio(
[...]\src\mp3\audio_file_manager.py:52: in copy_audio
audio = self._read_audio(target_path)
[...]\src\mp3\audio_file_manager.py:81: in _read_audio
audio = eyed3.load(file_path)
[...]\.venv\Lib\site-packages\eyed3\core.py:452: in load
return mp3.Mp3AudioFile(path, tag_version)
[...]\.venv\Lib\site-packages\eyed3\mp3\__init__.py:146: in __init__
super().__init__(path)
[...]\.venv\Lib\site-packages\eyed3\core.py:239: in __init__
self._read()
[...]\.venv\Lib\site-packages\eyed3\mp3\__init__.py:164: in _read
self._info = Mp3AudioInfo(file_obj, mp3_offset, self._tag)
[...]\.venv\Lib\site-packages\eyed3\mp3\__init__.py:93: in __init__
size_bytes = os.stat(file_obj.name)[stat.ST_SIZE]
[...]\.venv\Lib\site-packages\pyfakefs\fake_os.py:1426: in wrapped
return f(*args, **kwargs)
[...]\.venv\Lib\site-packages\pyfakefs\fake_os.py:693: in stat
return self.filesystem.stat(path, follow_symlinks)
[...]\.venv\Lib\site-packages\pyfakefs\fake_filesystem.py:673: in stat
file_object = self.resolve(
[...]\.venv\Lib\site-packages\pyfakefs\fake_filesystem.py:1760: in resolve
return self.get_object_from_normpath(
[...]\.venv\Lib\site-packages\pyfakefs\fake_filesystem.py:1689: in get_object_from_normpath
self.raise_os_error(errno.ENOENT, path)
[...]\.venv\Lib\site-packages\pyfakefs\fake_filesystem.py:430: in raise_os_error
raise OSError(err_no, message, filename)
E FileNotFoundError: [Errno 2] No such file or directory in the fake filesystem: '[...]\\tests\\test_assets\\target_repo\\9999.mp3'
Your environment
Windows-11-10.0.22000-SP0
Python 3.12.3 (tags/v3.12.3:f6650f9, Apr 9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)]
pyfakefs 5.6.0
pytest 8.2.1
does such a scenario ring a bell of someone? What can I do to further track down the issue?
Kind regards!
- Dominant language
- Python
- Stars
- 756
- Forks
- 111
- Avg merge
- 3d 21h
- Merged PRs (30d)
- 10
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from pytest-dev/pyfakefs
-
`patched_packages.py` fail: `AttributeError: module 'django' has no attribute 'conf'`Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
pytest-dev/pyfakefs#1367 · 1 comment ·
Maintainers usually reply within 2 days
-
Real Path objects keep a lowercased _str_normcase after a fake filesystem session on Python 3.14Open
Difficulty 3/5 1-2 days Newbie friendliness 58/100
pytest-dev/pyfakefs#1359 · 1 comment ·
Maintainers usually reply within 2 days
-
fnmatch.filter and glob don't match uppercase names under Windows simulationPossibly taken @sricharanreddycheruku claimed this 26 days ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 68/100
pytest-dev/pyfakefs#1345 · 4 comments ·
Maintainers usually reply within 2 days
-
`Path` is replaced with `FakePathlibPathModule` instead of `FakePath` when imported directlyPossibly taken @sunxiuguo claimed this 19 days ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 70/100
pytest-dev/pyfakefs#1334 · 1 comment ·
Maintainers usually reply within 2 days
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
pytest-dev/pyfakefs#1321 · 4 comments ·
Maintainers usually reply within 2 days
All issues in pytest-dev/pyfakefs
Similar issues
-
cost:cheap practicality:seen-in-the-wild severity:medium
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
fairagro/m4.2_advanced_middleware_api#620 ·
Maintainers usually reply within 1 day
-
area:security effort:S priority:P2 provider:local-libvirt status:ready type:docs
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
randomparity/kdive#3495 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
microsoft/markitdown#2820 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 2 days