import os
import platform
import stat
import sys
import time
import pytest
import audeer
@pytest.fixture(scope="function", autouse=False)
def tree(tmpdir, request):
r"""Create file tree."""
files = request.param
paths = []
for path in files:
if os.name == "nt":
path = path.replace("/", os.path.sep)
if path.endswith(os.path.sep):
path = audeer.path(tmpdir, path)
path = audeer.mkdir(path)
path = path + os.path.sep
paths.append(path)
else:
path = audeer.path(tmpdir, path)
audeer.mkdir(os.path.dirname(path))
path = audeer.touch(path)
paths.append(path)
yield paths
@pytest.mark.parametrize(
"tree, root, files, archive_create, archive_extract, destination, " "expected",
[
( # empty
[],
".",
[],
"archive.zip",
"archive.zip",
".",
[],
),
(
[],
".",
None,
"archive.zip",
"archive.zip",
".",
[],
),
( # single file
["file.txt"],
".",
"file.txt",
"archive.zip",
"archive.zip",
".",
["file.txt"],
),
( # archive folder does not exist
["file.txt"],
".",
"file.txt",
"sub/archive.zip",
"sub/archive.zip",
".",
["file.txt"],
),
( # file in sub folder
["file.txt", "sub/a/b/file.txt"],
".",
["sub/a/b/file.txt", "file.txt"],
"archive.zip",
"archive.zip",
".",
["sub/a/b/file.txt", "file.txt"],
),
(
["file.txt", "sub/a/b/file.txt"],
".",
["sub/a/b/file.txt"],
"archive.zip",
"archive.zip",
".",
["sub/a/b/file.txt"],
),
(
["file.txt", "sub/a/b/file.txt"],
".",
["file.txt"],
"archive.zip",
"archive.zip",
".",
["file.txt"],
),
( # include all files
["file.txt", "sub/a/b/file.txt", ".hidden"],
".",
None,
"archive.zip",
"archive.zip",
".",
[".hidden", "file.txt", "sub/a/b/file.txt"],
),
( # exclude empty folder
["file.txt", "sub/a/b/file.txt", ".hidden", "empty/"],
".",
None,
"archive.zip",
"archive.zip",
".",
[".hidden", "file.txt", "sub/a/b/file.txt"],
),
( # tar
["file.txt", "sub/a/b/file.txt"],
".",
["sub/a/b/file.txt", "file.txt"],
"archive.tar",
"archive.tar",
".",
["sub/a/b/file.txt", "file.txt"],
),
( # tar.gz
["file.txt", "sub/a/b/file.txt"],
".",
["sub/a/b/file.txt", "file.txt"],
"archive.tar.gz",
"archive.tar.gz",
".",
["sub/a/b/file.txt", "file.txt"],
),
( # tar.bz2
["file.txt", "sub/a/b/file.txt"],
".",
["sub/a/b/file.txt", "file.txt"],
"archive.tar.bz2",
"archive.tar.bz2",
".",
["sub/a/b/file.txt", "file.txt"],
),
( # tar.xz
["file.txt", "sub/a/b/file.txt"],
".",
["sub/a/b/file.txt", "file.txt"],
"archive.tar.xz",
"archive.tar.xz",
".",
["sub/a/b/file.txt", "file.txt"],
),
( # root is sub folder
["sub/file.txt"],
"./sub",
["file.txt"],
"archive.zip",
"archive.zip",
".",
["file.txt"],
),
(
["sub/file.txt"],
"./sub",
None,
"archive.zip",
"archive.zip",
".",
["file.txt"],
),
( # destitation is sub folder
["file.txt"],
".",
["file.txt"],
"archive.zip",
"archive.zip",
"./sub",
["file.txt"],
),
( # relative path with ../
["sub/file.txt"],
"./sub",
["../sub/file.txt"],
"archive.zip",
"archive.zip",
".",
["file.txt"],
),
pytest.param( # root does not exit
[],
"./sub",
None,
"archive.zip",
"archive.zip",
".",
None,
marks=pytest.mark.xfail(raises=FileNotFoundError),
),
pytest.param( # root is not a directory
["file.txt"],
"file.txt",
None,
"archive.zip",
"archive.zip",
".",
None,
marks=pytest.mark.xfail(raises=NotADirectoryError),
),
pytest.param( # destination is not a directory
["file.txt"],
".",
[],
"archive.zip",
"archive.zip",
"file.txt",
[],
marks=pytest.mark.xfail(raises=NotADirectoryError),
),
pytest.param( # archive to be extracted does not exit
[],
".",
[],
"archive.zip",
"bad.zip",
".",
None,
marks=pytest.mark.xfail(raises=FileNotFoundError),
),
pytest.param( # archive to be extracted is a directory
["sub/"],
".",
[],
"archive.zip",
"sub",
".",
None,
marks=pytest.mark.xfail(raises=IsADirectoryError),
),
pytest.param( # file does not exit
[],
".",
["file.txt"],
"archive.zip",
"archive.zip",
".",
None,
marks=pytest.mark.xfail(raises=FileNotFoundError),
),
pytest.param( # file not below root
["file.txt", "sub/"],
"./sub",
["../file.txt"],
"archive.zip",
"archive.zip",
".",
None,
marks=pytest.mark.xfail(raises=RuntimeError),
),
pytest.param( # archive type not supported
[],
".",
[],
"archive.bad",
"archive.zip",
".",
None,
marks=pytest.mark.xfail(raises=RuntimeError),
),
pytest.param(
["archive.bad"],
".",
[],
"archive.zip",
"archive.bad",
".",
None,
marks=pytest.mark.xfail(raises=RuntimeError),
),
pytest.param( # invalid .rar format
["file.txt", "sub/a/b/file.txt"],
".",
["sub/a/b/file.txt", "file.txt"],
"archive.rar",
"archive.rar",
".",
None,
marks=pytest.mark.xfail(raises=RuntimeError),
),
pytest.param( # invalid .7z format
["file.txt", "sub/a/b/file.txt"],
".",
["sub/a/b/file.txt", "file.txt"],
"archive.7z",
"archive.7z",
".",
None,
marks=pytest.mark.xfail(raises=RuntimeError),
),
pytest.param( # broken archive
["archive.zip"],
".",
[],
"archive.tar.gz",
"archive.zip",
".",
None,
marks=pytest.mark.xfail(raises=RuntimeError),
),
],
indirect=["tree"],
)
def test_archives(
tmpdir, tree, root, files, archive_create, archive_extract, destination, expected
):
root = audeer.path(tmpdir, root)
destination = audeer.path(tmpdir, destination)
archive_create = audeer.path(tmpdir, archive_create)
archive_extract = audeer.path(tmpdir, archive_extract)
if os.name == "nt":
if expected is not None:
expected = [file.replace("/", os.path.sep) for file in expected]
if isinstance(files, str):
files = files.replace("/", os.path.sep)
elif files is not None:
files = [file.replace("/", os.path.sep) for file in files]
# relative path
audeer.create_archive(
root,
files,
archive_create,
)
result = audeer.extract_archive(
archive_extract,
root,
keep_archive=True,
)
assert result == expected
for file in result:
assert os.path.exists(audeer.path(root, file))
assert os.path.exists(archive_extract)
# list of archives
result = audeer.extract_archives(
[archive_extract, archive_extract],
destination,
keep_archive=True,
)
assert result == expected + expected
for file in result:
assert os.path.exists(audeer.path(root, file))
assert os.path.exists(archive_extract)
# absolute path
if files is not None:
if isinstance(files, str):
files = audeer.path(root, files)
else:
files = [audeer.path(root, file) for file in files]
audeer.create_archive(
root,
files,
archive_create,
)
result = audeer.extract_archive(
archive_extract,
destination,
keep_archive=True,
)
assert result == expected
for file in result:
assert os.path.exists(audeer.path(root, file))
assert os.path.exists(archive_extract)
# delete archive
audeer.extract_archive(
archive_extract,
destination,
keep_archive=False,
)
assert not os.path.exists(archive_extract)
@pytest.mark.parametrize(
"path,ext,basename",
[
("~/.bashrc", None, ".bashrc"),
("file.tar.gz", None, "file.tar"),
("/a/c.d/g", None, "g"),
(b"/a/c.d/g", None, "g"),
("/a/c.d/g.exe", "exe", "g"),
("../.././README.md", ".md", "README"),
("folder/file.txt", None, "file"),
("folder/file.txt", "txt", "file"),
(b"folder/file.txt", "txt", "file"),
],
)
def test_basename_wo_ext(path, ext, basename):
b = audeer.basename_wo_ext(path, ext=ext)
assert b == basename
assert isinstance(b, str)
@pytest.mark.parametrize(
"dirs,expected",
[
([], ""),
(
[
"/home/user/tmp/coverage/test",
"/home/user/tmp/covert/operator",
"/home/user/tmp/coven/members",
],
"/home/user/tmp",
),
(
[
"/home/user/tmp/coverage/test",
"/home/user/tmp/covert/operator",
"/home/user/tmp",
],
"/home/user/tmp",
),
(
[
"~/tmp/coverage/test",
"~/tmp/covert/operator",
"~/tmp/coven/members",
],
f'{os.path.expanduser("~")}/tmp',
),
(
[
"/etc/tmp/coverage/test",
"/home/user/tmp/covert/operator",
"/home/user/tmp/coven/members",
],
"",
),
(
[
"/home/user/tmp",
"/home/user/tmp",
],
"/home/user/tmp",
),
(
[
"/home1/user/tmp",
"/home2/user/tmp",
],
"",
),
],
)
def test_common_directory(dirs, expected):
common = audeer.common_directory(dirs)
# Change paths always to Linux syntax
_, common = os.path.splitdrive(common)
_, expected = os.path.splitdrive(expected)
common = common.replace("\\", "/")
expected = expected.replace("\\", "/")
# On MacOS we get a '/System/Volumes/Data' in front
common = common.replace("/System/Volumes/Data", "")
assert common == expected
def test_download_url(tmpdir):
url = "https://audeering.github.io/audeer/_static/favicon.png"
audeer.download_url(url, tmpdir)
audeer.download_url(url, tmpdir)
dst = audeer.download_url(url, tmpdir, force_download=True)
assert dst == os.path.join(tmpdir, os.path.basename(url))
@pytest.mark.parametrize(
"path,extension",
[
("", ""),
("~/.bashrc", ""),
("file.tar.gz", "gz"),
("/a/c.d/g", ""),
("/a/c.d/g.exe", "exe"),
("../.././README.md", "md"),
(b"../.././README.md", "md"),
("folder/file.txt", "txt"),
(b"folder/file.txt", "txt"),
("test.WAV", "WAV"),
("test.WaV", "WaV"),
],
)
def test_file_extension(path, extension):
ext = audeer.file_extension(path)
assert ext == extension
assert isinstance(ext, str)
@pytest.mark.parametrize(
"dir_list,expected,recursive,hidden",
[
([], [], False, False),
([], [], True, False),
(["a", "b", "c"], ["a", "b", "c"], False, False),
(["a", "b", "c"], ["a", "b", "c"], True, False),
(["a"], ["a"], False, False),
(["a"], ["a"], True, False),
(
["a", os.path.join("a", "b"), os.path.join("a", "b", "c")],
["a", os.path.join("a", "b"), os.path.join("a", "b", "c")],
True,
False,
),
# hidden
(["a", ".b"], ["a"], True, False),
(["a", ".b"], [".b", "a"], True, True),
(
["a", ".b", os.path.join("a", ".b"), os.path.join("a", ".b", "c")],
["a"],
True,
False,
),
(
["a", ".b", os.path.join("a", ".b"), os.path.join("a", ".b", "c")],
[".b", "a", os.path.join("a", ".b"), os.path.join("a", ".b", "c")],
True,
True,
),
],
)
def test_list_dir_names(tmpdir, dir_list, expected, recursive, hidden):
dir_tmp = tmpdir.mkdir("folder")
directories = []
for directory in dir_list:
directory = os.path.join(str(dir_tmp), directory)
directories.append(audeer.mkdir(directory))
for directory in directories:
assert os.path.isdir(directory)
path = os.path.join(str(dir_tmp), ".")
dirs = audeer.list_dir_names(
path,
basenames=False,
recursive=recursive,
hidden=hidden,
)
assert dirs == [audeer.path(dir_tmp, d) for d in expected]
assert isinstance(dirs, list)
# test basenames
dirs = audeer.list_dir_names(
path,
basenames=True,
recursive=recursive,
hidden=hidden,
)
assert dirs == expected
def test_list_dir_names_errors(tmpdir):
with pytest.raises(NotADirectoryError):
file = audeer.touch(tmpdir, "file.txt")
audeer.list_dir_names(file)
with pytest.raises(FileNotFoundError):
audeer.list_dir_names("not-existent")
@pytest.mark.parametrize(
"files,path,filetype,expected,recursive,hidden",
[
# empty
([], ".", "", [], False, False),
([], ".", "", [], True, False),
([], ".", "wav", [], False, False),
([], ".", "wav", [], True, False),
# file
(
["file.txt"],
"file.txt",
"",
["file.txt"],
False,
False,
),
pytest.param(
[os.path.join("sub", "file.txt")],
"file.txt",
"",
[],
False,
False,
marks=pytest.mark.xfail(raises=NotADirectoryError),
),
(
[os.path.join("sub", "file.txt")],
"file.txt",
"",
[os.path.join("sub", "file.txt")],
True,
False,
),
pytest.param(
[],
"file",
"",
None,
False,
False,
marks=pytest.mark.xfail(raises=NotADirectoryError),
),
(
[
"t1.wav",
"t2.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", "t2.wav"),
],
"t1.wav",
"",
[os.path.join("sub", "t1.wav"), "t1.wav"],
True,
False,
),
# folder
(
["t3.ogg", "t2.wav", "t1.wav"],
".",
"",
["t1.wav", "t2.wav", "t3.ogg"],
False,
False,
),
(
[
"t3.ogg",
"t2.wav",
"t1.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", "t1.ogg"),
],
".",
"",
["t1.wav", "t2.wav", "t3.ogg"],
False,
False,
),
(
[
"t3.ogg",
"t2.wav",
"t1.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", "sub", "t1.ogg"),
],
".",
"",
[
os.path.join("sub", "sub", "t1.ogg"),
os.path.join("sub", "t1.wav"),
"t1.wav",
"t2.wav",
"t3.ogg",
],
True,
False,
),
pytest.param(
[],
"does-not-exist",
"",
None,
False,
False,
marks=pytest.mark.xfail(raises=NotADirectoryError),
),
pytest.param(
[],
os.path.join("does", "not", "exist"),
"",
None,
False,
False,
marks=pytest.mark.xfail(raises=NotADirectoryError),
),
# filetype
(
[
"t3.ogg",
"t2.wav",
"t1.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", "sub", "t1.ogg"),
],
".",
"ogg",
["t3.ogg"],
False,
False,
),
(
[
"t3.ogg",
"t2.wav",
"t1.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", "sub", "t1.ogg"),
],
".",
"ogg",
[
os.path.join("sub", "sub", "t1.ogg"),
"t3.ogg",
],
True,
False,
),
(
[
"t1.wav",
os.path.join("sub", "t1.wav"),
],
"t1.wav",
"",
["t1.wav"],
False,
False,
),
# pattern
(
[
"t1.wav",
"t2.ogg",
"s3.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", "t2.ogg"),
os.path.join("sub", "s3.wav"),
],
"t*",
"",
[
os.path.join("sub", "t1.wav"),
os.path.join("sub", "t2.ogg"),
"t1.wav",
"t2.ogg",
],
True,
False,
),
(
[
os.path.join("sub", "t1.wav"),
os.path.join("sub", "t2.ogg"),
os.path.join("sub", "s3.wav"),
],
"t*",
"",
[
os.path.join("sub", "t1.wav"),
os.path.join("sub", "t2.ogg"),
],
True,
False,
),
(
[
"t1.wav",
"t2.ogg",
"s3.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", "t2.ogg"),
os.path.join("sub", "s3.wav"),
],
"x*",
"",
[],
True,
False,
),
(
[
"t1.wav",
"t2.wav",
"s1.wav",
"s2.wav",
],
"t?.wav",
"",
["t1.wav", "t2.wav"],
False,
False,
),
(
[
"t1.wav",
"t2.wav",
"s1.wav",
"s2.wav",
],
"[ts]1.wav",
"",
["s1.wav", "t1.wav"],
False,
False,
),
(
[
"t1.wav",
"t2.wav",
"s1.wav",
"s.wav",
],
"[ts]?.wav",
"",
["s1.wav", "t1.wav", "t2.wav"],
False,
False,
),
(
[
"t1.wav",
"t2.wav",
"s1.wav",
"s.wav",
"x.wav",
],
"[ts]*.wav",
"",
["s.wav", "s1.wav", "t1.wav", "t2.wav"],
False,
False,
),
(
[],
"file*",
"",
[],
False,
False,
),
(
[],
"?ile",
"",
[],
False,
False,
),
pytest.param(
[],
os.path.join("does", "not", "exist", "file*"),
"",
None,
False,
False,
marks=pytest.mark.xfail(raises=NotADirectoryError),
),
pytest.param(
[],
os.path.join("not!a[pattern"),
"",
None,
False,
False,
marks=pytest.mark.xfail(raises=NotADirectoryError),
),
# pattern + filetype
(
[
"t1.wav",
"t2.ogg",
"s3.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", "t2.ogg"),
os.path.join("sub", "s3.wav"),
],
"t*",
"ogg",
[os.path.join("sub", "t2.ogg"), "t2.ogg"],
True,
False,
),
# hidden
(
[".file.txt"],
".file.txt",
"",
[],
False,
False,
),
(
[".file.txt"],
".file.txt",
"",
[".file.txt"],
False,
True,
),
(
[os.path.join("sub", ".file.txt")],
".file.txt",
"",
[],
True,
False,
),
(
[os.path.join("sub", ".file.txt")],
".file.txt",
"",
[os.path.join("sub", ".file.txt")],
True,
True,
),
(
[
"t1.wav",
".t2.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", ".t2.wav"),
],
"",
"",
[
"t1.wav",
],
False,
False,
),
(
[
"t1.wav",
".t2.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", ".t2.wav"),
],
"",
"",
[
os.path.join("sub", "t1.wav"),
"t1.wav",
],
True,
False,
),
(
[
"t1.wav",
".t2.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", ".t2.wav"),
],
"",
"",
[
".t2.wav",
"t1.wav",
],
False,
True,
),
(
[
"t1.wav",
".t2.wav",
os.path.join("sub", "t1.wav"),
os.path.join("sub", ".t2.wav"),
],
"",
"",
[
".t2.wav",
os.path.join("sub", ".t2.wav"),
os.path.join("sub", "t1.wav"),
"t1.wav",
],
True,
True,
),
],
)
def test_list_file_names(tmpdir, files, path, filetype, expected, recursive, hidden):
dir_tmp = tmpdir.mkdir("folder")
dir_tmp.mkdir("subfolder")
path = os.path.join(str(dir_tmp), path)
for file in files:
# Create the files
file_tmp = dir_tmp.join(file)
audeer.mkdir(os.path.dirname(file_tmp))
file_tmp.write("")
f = audeer.list_file_names(
path,
filetype=filetype,
basenames=False,
recursive=recursive,
hidden=hidden,
)
# test full path
assert f == [audeer.path(dir_tmp, f) for f in expected]
assert isinstance(f, list)
# test basenames
f = audeer.list_file_names(
path,
filetype=filetype,
basenames=True,
recursive=recursive,
hidden=hidden,
)
assert f == expected
def test_list_file_names_symlinks(tmpdir):
# symlinks in folder
folder = audeer.mkdir(tmpdir, "folder")
file = audeer.touch(folder, "file.txt")
link = audeer.path(folder, "link.txt")
os.symlink(file, link)
files = audeer.list_file_names(folder, basenames=True)
assert files == ["file.txt", "link.txt"]
os.remove(link)
# symlinks to sub-folders
sub_folder = audeer.mkdir(folder, "sub-folder")
audeer.touch(sub_folder, "file2.txt")
link = audeer.path(folder, "link")
os.symlink(sub_folder, link)
files = audeer.list_file_names(folder, basenames=True)
assert files == ["file.txt"]
files = audeer.list_file_names(folder, basenames=True, recursive=True)
assert files == [
"file.txt",
os.path.join("link", "file2.txt"),
os.path.join("sub-folder", "file2.txt"),
]
def test_md5_errors():
with pytest.raises(FileNotFoundError):
audeer.md5("does/not/exist")
@pytest.mark.parametrize(
"file, content, expected",
[
( # empty file
"file.txt",
None,
"d41d8cd98f00b204e9800998ecf8427e",
),
( # different content
"file.txt",
"hello world",
"5eb63bbbe01eeed093cb22bb8f5acdc3",
),
(
"file.txt",
"Hello World",
"b10a8db164e0754105b7a99be72e3fe5",
),
( # different filename
"file.TXT",
"Hello World",
"b10a8db164e0754105b7a99be72e3fe5",
),
],
)
def test_md5_file(tmpdir, file, content, expected):
path = audeer.path(tmpdir, file)
with open(path, "w") as fp:
if content is not None:
fp.write(content)
assert audeer.md5(path) == expected
@pytest.mark.parametrize(
"tree, content, expected",
[
( # empty folder
[],
None,
"d41d8cd98f00b204e9800998ecf8427e",
),
( # folder with different content
["f"],
None,
"8fa14cdd754f91cc6554c9e71929cce7",
),
(
["sub/f"],
None,
"1af042d5a4ec129583f6093f98f64118",
),
(
["f", "sub/f"],
None,
"b540f38948f445622adc657a757f4b0d",
),
(
["f", "sub/g"],
None,
"305107efbb15f9334d22ae4fbeec4de6",
),
(
["f", "sub/g"],
"hello world",
"47829eb8ef287d0d72e0fed9b96d258d",
),
(
["f", "sub/g"],
"Hello World",
"442d96d7c43bb18f247888408e5d6977",
),
( # with empty sub folder
["f", "sub/g", "sub/"],
None,
"305107efbb15f9334d22ae4fbeec4de6",
),
( # with hidden file
["f", "sub/g", ".hidden"],
None,
"97490b233a7717aec19023e28443a1bf",
),
( # umlaute
["ä", "ö", "ü", "ß"],
None,
"622165ad36122984c6b2c7ba466aa262",
),
],
indirect=["tree"],
)
def test_md5_folder(tmpdir, tree, content, expected):
if content is not None:
for path in tree:
with open(path, "w") as fp:
fp.write(content)
assert audeer.md5(tmpdir) == expected
def test_md5_symbolic_link(tmpdir):
# Link to a file
file = audeer.touch(tmpdir, "file.txt")
link = audeer.path(tmpdir, "link.txt")
os.symlink(file, link)
assert audeer.md5(file) == audeer.md5(link)
os.remove(file)
os.remove(link)
# Link to a folder
folder = audeer.mkdir(tmpdir, "folder")
file = audeer.touch(folder, "file.txt")
link = audeer.path(tmpdir, "link")
os.symlink(folder, link)
assert audeer.md5(folder) == audeer.md5(link)
os.remove(link)
# Link to file in folder
md5_single_file = audeer.md5(folder)
link = audeer.path(folder, "link.txt")
os.symlink(file, link)
md5_link = audeer.md5(folder)
os.remove(link)
audeer.touch(folder, "link.txt")
md5_file = audeer.md5(folder)
assert md5_link == md5_file
assert md5_file != md5_single_file
def test_mkdir(tmpdir):
# New dir
path = str(tmpdir.mkdir("folder1"))
p = audeer.mkdir(path)
assert os.path.isdir(p) is True
assert p == path
# Existing dir
p = audeer.mkdir(path)
assert os.path.isdir(p) is True
assert p == path
# Existing dir with content
dir_tmp = tmpdir.mkdir("folder2")
f = dir_tmp.join("file.txt")
f.write("")
path = str(dir_tmp)
p = audeer.mkdir(path)
assert os.path.isdir(p) is True
assert p == path
# Relative path
path = str(tmpdir.mkdir("folder3"))
current_path = os.getcwd()
os.chdir(path)
p = audeer.mkdir("folder4")
os.chdir(current_path)
assert os.path.isdir(p) is True
assert p == os.path.join(path, "folder4")
# Subdirectories
os.chdir(path)
p = audeer.mkdir("folder5", "folder6")
os.chdir(current_path)
assert os.path.isdir(p) is True
assert p == os.path.join(path, "folder5", "folder6")
# Path in bytes
path = str(tmpdir.mkdir("folder7"))
path = bytes(path, "utf8")
p = audeer.mkdir(path)
assert os.path.isdir(p) is True
assert p == path.decode("utf8")
# Empty dir
path = ""
p = audeer.mkdir(path)
assert p == path
# Mode, see https://stackoverflow.com/a/705088
# Default mode
os.umask(0)
p = audeer.mkdir(tmpdir, "folder8", "sub-folder")
mode = stat.S_IMODE(os.stat(p).st_mode)
expected_mode = int("777", 8)
assert mode == expected_mode
# Non-default modes
# Under Windows, changing permissions does not work,
# there we always expect 777
os.umask(0)
p = audeer.mkdir(tmpdir, "folder9", "sub-folder", mode=0o775)
expected_mode = "775"
if platform.system() == "Windows":
expected_mode = "777"
mode = stat.S_IMODE(os.stat(p).st_mode)
assert mode == int(expected_mode, 8)
assert mode != int("755", 8)
os.umask(0)
p = audeer.mkdir(tmpdir, "folder10", "sub-folder", mode=0o755)
expected_mode = "755"
if platform.system() == "Windows":
expected_mode = "777"
mode = stat.S_IMODE(os.stat(p).st_mode)
assert mode == int(expected_mode, 8)
assert mode != int("775", 8)
@pytest.mark.parametrize(
"src_path, dst_path",
[
(
"path1",
"path1",
),
(
"path1",
"path2",
),
],
)
def test_move(tmpdir, src_path, dst_path):
system = platform.system()
tmp_dir = audeer.mkdir(tmpdir, "folder")
# src: file
# dst: new file
audeer.touch(tmp_dir, src_path)
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
if src_path != dst_path:
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
# src: file
# dst: existing file
audeer.rmdir(tmp_dir)
audeer.mkdir(tmp_dir)
audeer.touch(tmp_dir, src_path)
audeer.touch(tmp_dir, dst_path)
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
if src_path != dst_path:
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
# src: folder
# dst: new folder
audeer.rmdir(tmp_dir)
audeer.mkdir(tmp_dir, src_path)
audeer.touch(tmp_dir, src_path, "file.txt")
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
if src_path != dst_path:
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path, "file.txt"))
assert os.path.isdir(os.path.join(tmp_dir, dst_path))
# src: non-empty folder
# dst: existing non-empty folder
audeer.rmdir(tmp_dir)
audeer.mkdir(tmp_dir, src_path)
audeer.touch(tmp_dir, src_path, "file.txt")
if src_path != dst_path:
audeer.mkdir(tmp_dir, dst_path)
audeer.touch(tmp_dir, dst_path, "file.txt")
if src_path != dst_path:
if system == "Windows":
error_msg = "Access is denied"
else:
error_msg = "Directory not empty"
with pytest.raises(OSError, match=error_msg):
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
# src: non-empty folder
# dst: existing empty folder
os.remove(os.path.join(tmp_dir, dst_path, "file.txt"))
if system == "Windows":
# Only under Windows
# we get an error
# if destination is an empty folder
with pytest.raises(OSError, match="Access is denied"):
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
else:
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
if src_path != dst_path:
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path, "file.txt"))
assert os.path.isdir(os.path.join(tmp_dir, dst_path))
# src: non-empty folder
# dst: identical to src
if src_path == dst_path:
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
if src_path != dst_path:
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path, "file.txt"))
assert os.path.isdir(os.path.join(tmp_dir, dst_path))
# src: empty folder
# dst: existing non-empty folder
audeer.rmdir(tmp_dir)
audeer.mkdir(tmp_dir, src_path)
if src_path != dst_path:
audeer.mkdir(tmp_dir, dst_path)
audeer.touch(tmp_dir, dst_path, "file.txt")
if src_path != dst_path:
if system == "Windows":
error_msg = "Access is denied"
else:
error_msg = "Directory not empty"
with pytest.raises(OSError, match=error_msg):
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
# src: empty folder
# dst: existing empty folder
os.remove(os.path.join(tmp_dir, dst_path, "file.txt"))
if system == "Windows":
# Only under Windows
# we get an error
# if destination is an empty folder
with pytest.raises(OSError, match="Access is denied"):
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
else:
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
if src_path != dst_path:
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
assert not os.path.exists(os.path.join(tmp_dir, dst_path, "file.txt"))
assert os.path.isdir(os.path.join(tmp_dir, dst_path))
# src: empty folder
# dst: identical to src
if src_path == dst_path:
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
if src_path != dst_path:
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
assert not os.path.exists(os.path.join(tmp_dir, dst_path, "file.txt"))
assert os.path.isdir(os.path.join(tmp_dir, dst_path))
if src_path != dst_path:
# src: file
# dst: non-empty folder
audeer.rmdir(tmp_dir)
audeer.mkdir(tmp_dir)
audeer.touch(tmp_dir, src_path)
audeer.mkdir(tmp_dir, dst_path)
audeer.touch(tmp_dir, dst_path, "file.txt")
if system == "Windows":
error_msg = "Access is denied"
else:
error_msg = "Is a directory"
with pytest.raises(OSError, match=error_msg):
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
# src: file
# dst: empty folder
os.remove(os.path.join(tmp_dir, dst_path, "file.txt"))
with pytest.raises(OSError, match=error_msg):
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
# src: non-empty folder
# dst: file
audeer.rmdir(tmp_dir)
audeer.mkdir(tmp_dir)
audeer.mkdir(tmp_dir, src_path)
audeer.touch(tmp_dir, src_path, "file.txt")
audeer.touch(tmp_dir, dst_path)
if system != "Windows":
error_msg = "Not a directory"
with pytest.raises(OSError, match=error_msg):
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
else:
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path, "file.txt"))
assert os.path.isdir(os.path.join(tmp_dir, dst_path))
# src: empty folder
# dst: file
audeer.rmdir(tmp_dir)
audeer.mkdir(tmp_dir, src_path)
audeer.touch(tmp_dir, dst_path)
if system != "Windows":
error_msg = "Not a directory"
with pytest.raises(OSError, match=error_msg):
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
else:
audeer.move(
os.path.join(tmp_dir, src_path),
os.path.join(tmp_dir, dst_path),
)
assert not os.path.exists(os.path.join(tmp_dir, src_path))
assert os.path.exists(os.path.join(tmp_dir, dst_path))
assert not os.path.exists(os.path.join(tmp_dir, dst_path, "file.txt"))
assert os.path.isdir(os.path.join(tmp_dir, dst_path))
@pytest.mark.parametrize(
"src_file, dst_file",
[
(
"file1",
"file1",
),
(
"file1",
"file2",
),
],
)
def test_move_file(tmpdir, src_file, dst_file):
tmp_path = audeer.mkdir(tmpdir, "folder")
src_path = audeer.touch(tmp_path, src_file)
dst_path = os.path.join(tmp_path, dst_file)
audeer.move_file(src_path, dst_path)
if src_file != dst_file:
assert not os.path.exists(src_path)
assert os.path.exists(dst_path)
@pytest.mark.parametrize(
"path, new_extension, ext, expected_path",
[
("", "", None, ""),
("", "txt", None, ""),
("", "", "rst", ""),
("", "txt", "rst", ""),
("file", "", None, "file"),
("file", "txt", None, "file.txt"),
("file.txt", "wav", None, "file.wav"),
("test/file.txt", "wav", None, "test/file.wav"),
("a/b/../file.txt", "wav", None, "a/b/../file.wav"),
("file.txt", "wav", "txt", "file.wav"),
("file.txt", "wav", ".txt", "file.wav"),
("file.txt", ".wav", "txt", "file.wav"),
("file.txt", ".wav", ".txt", "file.wav"),
("file.a.b", "wav", "a.b", "file.wav"),
("file.a.b", "wav", ".a.b", "file.wav"),
("file", "wav", "ext", "file"),
("file.txt", "wav", "ext", "file.txt"),
("file.txt", "wav", "t", "file.txt"),
],
)
def test_replace_file_extension(path, new_extension, ext, expected_path):
new_path = audeer.replace_file_extension(path, new_extension, ext=ext)
assert new_path == expected_path
def test_rmdir(tmpdir):
# Non existing dir
audeer.rmdir("non-esitent")
# Folder with file content
dir_tmp = tmpdir.mkdir("folder")
f = dir_tmp.join("file.txt")
f.write("")
path = str(dir_tmp)
p = audeer.mkdir(path)
with pytest.raises(NotADirectoryError):
audeer.rmdir(os.path.join(p, "file.txt"))
audeer.rmdir(p)
assert not os.path.exists(p)
# Folder with folder content
p = audeer.mkdir(tmpdir, "folder", "sub-folder")
audeer.rmdir(tmpdir, "folder")
assert not os.path.exists(p)
assert not os.path.exists(os.path.dirname(p))
# Relative path
path = str(tmpdir.mkdir("folder"))
current_path = os.getcwd()
os.chdir(os.path.dirname(path))
assert os.path.exists(path)
audeer.rmdir("folder")
assert not os.path.exists(path)
os.chdir(current_path)
# Symbolic link
path = audeer.mkdir(tmpdir, "folder")
link = os.path.join(tmpdir, "link")
os.symlink(path, link)
# Error message is broken
# for newer version of Python 3.12
# under MacOS and Linux
python_version = (
sys.version_info.major,
sys.version_info.minor,
sys.version_info.micro,
)
if python_version >= (3, 12, 4) and platform.system() != "Windows":
error_msg = "None"
else:
error_msg = "symbolic link"
with pytest.raises(OSError, match=error_msg):
audeer.rmdir(link, follow_symlink=False)
assert os.path.exists(link)
assert os.path.exists(path)
audeer.rmdir(link, follow_symlink=True)
assert not os.path.exists(link)
assert not os.path.exists(path)
def test_script_dir(tmpdir):
r"""Test estimation of current directory of caller.
See https://stackoverflow.com/a/5137509.
Args:
tmpdir: tmpdir fixture
"""
expected_script_dir = os.path.dirname(os.path.realpath(__file__))
assert audeer.script_dir() == expected_script_dir
current_dir = os.getcwd()
os.chdir(tmpdir)
assert audeer.script_dir() == expected_script_dir
os.chdir(current_dir)
def test_touch(tmpdir):
path = audeer.mkdir(tmpdir, "folder1")
path = os.path.join(path, "file")
assert not os.path.exists(path)
audeer.touch(path)
assert os.path.exists(path)
stat = os.stat(path)
time.sleep(0.1)
audeer.touch(path)
assert os.path.exists(path)
new_stat = os.stat(path)
assert stat.st_atime < new_stat.st_atime
assert stat.st_mtime < new_stat.st_mtime