Source code for ansys.dyna.core.pre.examples.download_utilities
import os
from threading import Lock
from typing import Optional
from urllib.parse import urljoin
import urllib.request
from ansys.dyna.core.pre.internals.defaults import EXAMPLES_PATH
__all__ = ["DownloadManager"]
class DownloadManagerMeta(type):
"""
Provides a thread-safe implementation of ``Singleton`` from
https://refactoring.guru/design-patterns/singleton/python/example#example-1.
"""
_instances = {}
_lock: Lock = Lock()
def __call__(cls, *args, **kwargs):
"""
Possible changes to the value of the ``__init__`` argument do not affect
the returned instance.
"""
with cls._lock:
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
[docs]
class DownloadManager(metaclass=DownloadManagerMeta):
"""Manages downloads of example files.
Local paths are saved in this class so that a global cleanup
of example files can be performed when the client is closed.
"""
def __init__(self):
[docs]
self.downloads_list = []
[docs]
def add_file(self, file_path: str):
"""Add the path for a downloaded example file to a list.
This list keeps track of where example files are
downloaded so that a global cleanup of these files can be
performed when the client is closed.
Parameters
----------
file_path : str
Local path of the downloaded example file.
"""
self.downloads_list.append(file_path)
[docs]
def clear_download_cache(self):
"""Remove downloaded example files from the local path."""
for file in self.downloads_list:
os.remove(file)
self.downloads_list.clear()
[docs]
def download_file(
self, filename: str, *directory: str, destination: Optional[str] = None, force: bool = False
) -> str:
"""Download an example file from the PyPrimeMesh repository.
Parameters
----------
filename : str
Name of the example file to download.
destination : str, optional
Path to download the example file to. The default
is ``None``, in which case the default path for app data
is used.
force : bool, optional
Whether to always download the example file. The default is
``False``, in which case if the example file is cached, it
is reused.
directory : tuple[str]
Path under the PyAnsys Github examples repository.
Returns
-------
tuple[str, str]
Tuple containing the filepath to use and the local filepath of the downloaded
directory. The two are different in case of containers.
"""
# if destination is not a dir create it
if destination is not None and not os.path.isdir(destination):
os.mkdir(destination)
# check if it was able to create the dir
if destination is not None and not os.path.isdir(destination):
raise ValueError("destination directory provided does not exist")
url = self._get_filepath_on_default_server(filename, *directory)
local_path = self._retrieve_data(url, filename, dest=destination, force=force)
# add path to downloaded files
self.add_file(local_path)
return local_path
def _joinurl(self, base, *paths):
for path in paths:
if base[-1] != "/":
base += "/"
base = urljoin(base, path)
return base
def _get_default_server_and_joiner(self):
return "https://github.com/ansys/example-data/raw/master", self._joinurl
def _get_filepath_on_default_server(self, filename: str, *directory: str):
server, joiner = self._get_default_server_and_joiner()
if directory:
return joiner(server, *directory, filename)
else:
return joiner(server, filename)
def _retrieve_url(self, url, dest):
saved_file, _ = urllib.request.urlretrieve(url, filename=dest)
return saved_file
def _retrieve_data(self, url: str, filename: str, dest: str = None, force: bool = False):
if dest == None:
dest = EXAMPLES_PATH
local_path = os.path.join(dest, os.path.basename(filename))
if not force and os.path.isfile(local_path):
return local_path
local_path = self._retrieve_url(url, local_path)
return local_path