Source code for emlib.filetools
"""
Utilities to work with files and filenames
"""
from __future__ import annotations
import datetime
import os
from . import misc
[docs]
def modifiedDate(filename: str) -> datetime.date:
"""
get the modified time of f as a datetime.date
Args:
filename: the file name for which to query the modified data
Returns:
the modification date, as a datetime.date
"""
t = os.path.getmtime(filename)
return datetime.date.fromtimestamp(t)
[docs]
def filesBetween(files: list[str], start, end) -> list[str]:
"""
Returns files between the given times
Args:
files: a list of files
start: a tuple as would be passed to datetime.date
end : a tuple as would be passed to datetime.date
Returns:
a list of files
"""
t0 = datetime.date(*start) if not isinstance(start, datetime.date) else start
t1 = datetime.date(*end) if not isinstance(end, datetime.date) else end
return [f for f in files if t0 <= modifiedDate(f) < t1]
[docs]
def fixLineEndings(filename:str) -> None:
"""
Convert any windows line endings (\r\n) to unix line endings (\n)
"""
f = open(filename, 'rb')
# read the beginning, look if there are CR
data = f.read(100)
if b'\r' in data:
# read all in memory, this files should not be so big anyway
data = data + f.read()
data = data.replace(b'\r\n', b'\n')
f.close()
# write it back, now with LF
open(filename, 'wb').write(data)
else:
f.close()
[docs]
def findFile(path: str, file: str) -> str | None:
"""
Look for file recursively starting at path.
If file is found in path or any subdir, the complete path is returned
else None
Args:
path: the path to start searching
file: the file to find
Returns:
the absolute path or None if the file was not found
"""
dir_cache = set()
for directory in os.walk(path):
if directory[0] in dir_cache:
continue
dir_cache.add(directory[0])
if file in directory[2]:
return '/'.join((directory[0], file))
return None
[docs]
def addSuffix(filename: str, suffix: str) -> str:
"""
Add a suffix between the name and the extension
Args:
filename: the filename to add a suffix to
suffix: the suffix to add
Returns:
the modified filename
Example
-------
>>> name = "test.txt"
>>> newname = addSuffix(name, "-OLD")
>>> newname
test-OLD.txt
>>> os.rename(name, newname)
This does NOT rename the file, it merely returns the string
"""
name, ext = os.path.splitext(filename)
return ''.join((name, suffix, ext))
[docs]
def withExtension(filename: str, extension: str) -> str:
"""
Return a filename with its extension replaced
Args:
filename: the filename to modify
extension: the new extension
Returns:
a filename with the given extension in place of the old extension
============ ========== =============
filename extension output
============ ========== =============
foo.txt .md foo.md
foo.txt md foo.md
foo.bar.baz zip foo.bar.zip
============ ========== =============
"""
if not extension.startswith("."):
extension = "." + extension
base = os.path.splitext(filename)[0]
return base + extension
[docs]
def increaseSuffix(filename: str) -> str:
"""
Given a filename, return a new filename with an increased suffix if
the filename already has a suffix, or a suffix if it hasn't
============= ===========
input output
============= ===========
foo.txt foo-01.txt
foo-01.txt foo-02.txt
foo-2.txt foo-03.txt
============= ===========
"""
name, ext = os.path.splitext(filename)
tokens = name.split("-")
def increase_number(number_as_string):
n = int(number_as_string) + 1
s = "%0" + str(len(number_as_string)) + "d"
return s % n
if len(tokens) > 1:
suffix = tokens[-1]
if misc.asnumber(suffix) is not None:
new_suffix = increase_number(suffix)
new_name = name[:-len(suffix)] + new_suffix
else:
new_name = name + '-01'
else:
new_name = name + '-01'
return new_name + ext
[docs]
def normalizePath(path: str) -> str:
"""
Convert `path` to an absolute path with user expanded
(something that can be safely passed to a subprocess)
"""
return os.path.abspath(os.path.expandvars(os.path.expanduser(path)))