forked from speechbrain/speechbrain
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfetching.py
More file actions
176 lines (158 loc) · 6.12 KB
/
Copy pathfetching.py
File metadata and controls
176 lines (158 loc) · 6.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
"""Downloads or otherwise fetches pretrained models
Authors:
* Aku Rouhe 2021
* Samuele Cornell 2021
* Andreas Nautsch 2022, 2023
"""
import urllib.request
import urllib.error
import pathlib
import logging
from enum import Enum
import huggingface_hub
from collections import namedtuple
from requests.exceptions import HTTPError
logger = logging.getLogger(__name__)
def _missing_ok_unlink(path):
# missing_ok=True was added to Path.unlink() in Python 3.8
# This does the same.
try:
path.unlink()
except FileNotFoundError:
pass
class FetchFrom(Enum):
"""Designator where to fetch models/audios from.
Note: HuggingFace repository sources and local folder sources may be confused if their source type is undefined.
"""
LOCAL = 1
HUGGING_FACE = 2
URI = 3
# For easier use
FetchSource = namedtuple("FetchSource", ["FetchFrom", "path"])
FetchSource.__doc__ = (
"""NamedTuple describing a source path and how to fetch it"""
)
FetchSource.__hash__ = lambda self: hash(self.path)
FetchSource.encode = lambda self, *args, **kwargs: "_".join(
(str(self.path), str(self.FetchFrom))
).encode(*args, **kwargs)
# FetchSource.__str__ = lambda self: str(self.path)
def fetch(
filename,
source,
savedir="./pretrained_model_checkpoints",
overwrite=False,
save_filename=None,
use_auth_token=False,
revision=None,
huggingface_cache_dir=None,
):
"""Ensures you have a local copy of the file, returns its path
In case the source is an external location, downloads the file. In case
the source is already accessible on the filesystem, creates a symlink in
the savedir. Thus, the side effects of this function always look similar:
savedir/save_filename can be used to access the file. And save_filename
defaults to the filename arg.
Arguments
---------
filename : str
Name of the file including extensions.
source : str or FetchSource
Where to look for the file. This is interpreted in special ways:
First, if the source begins with "http://" or "https://", it is
interpreted as a web address and the file is downloaded.
Second, if the source is a valid directory path, a symlink is
created to the file.
Otherwise, the source is interpreted as a Huggingface model hub ID, and
the file is downloaded from there.
savedir : str
Path where to save downloads/symlinks.
overwrite : bool
If True, always overwrite existing savedir/filename file and download
or recreate the link. If False (as by default), if savedir/filename
exists, assume it is correct and don't download/relink. Note that
Huggingface local cache is always used - with overwrite=True we just
relink from the local cache.
save_filename : str
The filename to use for saving this file. Defaults to filename if not
given.
use_auth_token : bool (default: False)
If true Huggingface's auth_token will be used to load private models from the HuggingFace Hub,
default is False because majority of models are public.
revision : str
The model revision corresponding to the HuggingFace Hub model revision.
This is particularly useful if you wish to pin your code to a particular
version of a model hosted at HuggingFace.
huggingface_cache_dir: str
Path to HuggingFace cache; if None -> "~/.cache/huggingface" (default: None)
Returns
-------
pathlib.Path
Path to file on local file system.
Raises
------
ValueError
If file is not found
"""
if save_filename is None:
save_filename = filename
savedir = pathlib.Path(savedir)
savedir.mkdir(parents=True, exist_ok=True)
fetch_from = None
if isinstance(source, FetchSource):
fetch_from, source = source
sourcefile = f"{source}/{filename}"
destination = savedir / save_filename
if destination.exists() and not overwrite:
MSG = f"Fetch {filename}: Using existing file/symlink in {str(destination)}."
logger.info(MSG)
return destination
if pathlib.Path(source).is_dir() and fetch_from not in [
FetchFrom.HUGGING_FACE,
FetchFrom.URI,
]:
# Interpret source as local directory path & create a link and return it as destination
sourcepath = pathlib.Path(sourcefile).absolute()
_missing_ok_unlink(destination)
destination.symlink_to(sourcepath)
MSG = f"Destination {filename}: local file in {str(sourcepath)}."
logger.info(MSG)
return destination
if (
str(source).startswith("http:") or str(source).startswith("https:")
) or fetch_from is FetchFrom.URI: # Interpret source as web address.
MSG = (
f"Fetch {filename}: Downloading from normal URL {str(sourcefile)} ."
)
logger.info(MSG)
# Download
try:
urllib.request.urlretrieve(sourcefile, destination)
except urllib.error.URLError:
raise ValueError(
f"Interpreted {source} as web address, but could not download."
)
else: # FetchFrom.HUGGING_FACE check is spared (no other option right now)
# Interpret source as huggingface hub ID
# Use huggingface hub's fancy cached download.
MSG = f"Fetch {filename}: Delegating to Huggingface hub, source {str(source)}."
logger.info(MSG)
try:
fetched_file = huggingface_hub.hf_hub_download(
repo_id=source,
filename=filename,
use_auth_token=use_auth_token,
revision=revision,
cache_dir=huggingface_cache_dir,
)
logger.info(f"HF fetch: {fetched_file}")
except HTTPError as e:
if "404 Client Error" in str(e):
raise ValueError("File not found on HF hub")
else:
raise
# Huggingface hub downloads to etag filename, symlink to the expected one:
sourcepath = pathlib.Path(fetched_file).absolute()
_missing_ok_unlink(destination)
destination.symlink_to(sourcepath)
return destination