Source code for aiida.cmdline.params.types.path
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved. #
# This file is part of the AiiDA code. #
# #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida-core #
# For further information on the license, see the LICENSE.txt file #
# For further information please visit http://www.aiida.net #
###########################################################################
"""Click parameter types for paths."""
import os
import pathlib
import click
__all__ = ('AbsolutePathParamType', 'FileOrUrl', 'PathOrUrl')
URL_TIMEOUT_SECONDS = 10
[docs]
def check_timeout_seconds(timeout_seconds):
"""Raise if timeout is not within range [0;60]"""
try:
timeout_seconds = int(timeout_seconds)
except ValueError:
raise TypeError(f'timeout_seconds should be an integer but got: {type(timeout_seconds)}')
if timeout_seconds < 0 or timeout_seconds > 60:
raise ValueError('timeout_seconds needs to be in the range [0;60].')
return timeout_seconds
[docs]
class AbsolutePathParamType(click.Path):
"""The ParamType for identifying absolute Paths (derived from click.Path)."""
name = 'AbsolutePath'
[docs]
def convert(self, value, param, ctx):
value = os.path.expanduser(value)
newval = super().convert(value, param, ctx)
if not os.path.isabs(newval):
raise click.BadParameter('path must be absolute')
return newval
[docs]
def __repr__(self):
return 'ABSOLUTEPATH'
[docs]
class AbsolutePathOrEmptyParamType(AbsolutePathParamType):
"""The ParamType for identifying absolute Paths, accepting also empty paths."""
name = 'AbsolutePathEmpty'
[docs]
def convert(self, value, param, ctx):
if not value:
return value
return super().convert(value, param, ctx)
[docs]
def __repr__(self):
return 'ABSOLUTEPATHEMPTY'
[docs]
def convert_possible_url(value: str, timeout: int):
"""If ``value`` does not correspond to a path on disk, try to open it as a URL.
:param value: Potential path to file on disk or URL.
:param timeout: The timeout in seconds when opening the URL.
:param return_handle: Return the ``value`` as is. When set to ``True`` return an open file handle instead.
:returns: The URL if ``value`` could be opened as a URL
"""
import socket
import urllib.error
import urllib.request
filepath = pathlib.Path(value)
# Check whether the path actually corresponds to a file on disk, in which case the exception is reraised.
if filepath.exists():
raise click.BadParameter(f'The path `{value}` exists but could not be read.')
try:
return urllib.request.urlopen(value, timeout=timeout)
except urllib.error.URLError:
raise click.BadParameter(f'The URL `{value}` could not be reached.')
except socket.timeout:
raise click.BadParameter(f'The URL `{value}` could not be reached within {timeout} seconds.')
except ValueError as exception_url:
raise click.BadParameter(
f'The path `{value}` does not correspond to a file and also could not be reached as a URL.\n'
'Please check the spelling for typos and if it is a URL, make sure to include the protocol, e.g., http://'
) from exception_url
[docs]
class PathOrUrl(click.Path):
"""Parameter type that accepts a path on the local file system or a URL.
:param timeout_seconds: Maximum timeout accepted for URL response. Must be an integer in the range [0;60].
:returns: The path or URL.
"""
name = 'PathOrUrl'
[docs]
def __init__(self, timeout_seconds=URL_TIMEOUT_SECONDS, **kwargs):
super().__init__(**kwargs)
self.timeout_seconds = check_timeout_seconds(timeout_seconds)
[docs]
def convert(self, value, param, ctx):
try:
return super().convert(value, param, ctx)
except click.exceptions.BadParameter:
convert_possible_url(value, self.timeout_seconds)
return value
[docs]
class FileOrUrl(click.File):
"""Parameter type that accepts a path on the local file system or a URL.
:param timeout_seconds: Maximum timeout accepted for URL response. Must be an integer in the range [0;60].
:returns: The file or URL.
"""
name = 'FileOrUrl'
[docs]
def __init__(self, timeout_seconds=URL_TIMEOUT_SECONDS, **kwargs):
super().__init__(**kwargs)
self.timeout_seconds = check_timeout_seconds(timeout_seconds)
[docs]
def convert(self, value, param, ctx):
try:
return super().convert(value, param, ctx)
except click.exceptions.BadParameter:
return convert_possible_url(value, self.timeout_seconds)