Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

class Foldernames and user guide snippet #669

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion examples/user_guide/Parameter_Types.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"- [Path](#paths): A POSIX-style string specifying the location of a local file or folder\n",
" * [Filename](#paths): A POSIX-style string specifying the location of a local file\n",
" * [Foldername](#paths): A POSIX-style string specifying the location of a local folder\n",
" - [Foldernames](#paths): A POSIX-style string or a list of such strings specifying the location of one or more folder(s)\n",
"- [SelectorBase](#selectors): Abstract superclass covering various selector parameter types\n",
" * [Selector](#selectors): One object selected out of a provided ordered list of objects\n",
" - [FileSelector](#selectors): One filename selected out of those matching a provided glob\n",
Expand Down Expand Up @@ -647,12 +648,13 @@
"- `param.Path`: A POSIX-style string specifying the location of a local file or folder\n",
"- `param.Filename`: A POSIX-style string specifying the location of a local file\n",
"- `param.Foldername`: A POSIX-style string specifying the location of a local folder\n",
"- `param.Foldernames`: A POSIX-style string or a list of such strings specifying the location of one or more folder(s)\n",
"\n",
"A Path can be set to a string specifying the path of a file or folder. In code, the string should be specified in POSIX (UNIX) style, using forward slashes / and starting from / if absolute or some other character if relative. When retrieved, the string will be in the format of the user's operating system. \n",
"\n",
"Relative paths are converted to absolute paths by searching for a matching filename on the filesystem. If `search_paths` is provided and not empty, the folders in that list are searched for the given filename, in order, returning the absolute path for the first match found by appending the provided path to the search path. An IOError is raised if the file or folder is not found. If `search_paths` is empty (the default), the file or folder is expected to be in the current working directory.\n",
"\n",
"Either a file or a folder name is accepted by `param.Path`, while `param.Filename` accepts only file names and `param.Foldername` accepts only folder names."
"Either a file or a folder name is accepted by `param.Path`, while `param.Filename` accepts only file names, and `param.Foldername` and `param.Foldernames` accept only folder names."
]
},
{
Expand All @@ -665,6 +667,7 @@
" p = param.Path('Parameter_Types.ipynb')\n",
" f = param.Filename('Parameter_Types.ipynb')\n",
" d = param.Foldername('lib', search_paths=['/','/usr','/share'])\n",
" ds = param.Foldernames(['lib', 'opt'], search_paths=['/','/usr','/share'])\n",
" \n",
"p = P()\n",
"p.p"
Expand Down Expand Up @@ -718,6 +721,35 @@
" p.d='Parameter_Types.ipynb'"
]
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"p.ds"
],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"p.ds = '/lib'\n",
"p.ds"
],
"metadata": {}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"with param.exceptions_summarized():\n",
" p.ds='Parameter_Types.ipynb'"
],
"metadata": {}
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down
50 changes: 50 additions & 0 deletions param/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
Parameters and Parameterized classes.
"""

import typing
import os.path
import pathlib
import sys
import copy
import glob
Expand All @@ -38,6 +40,9 @@
from collections import OrderedDict
from numbers import Real

FlexPath = typing.Union[str, pathlib.Path] # a string or a Path object
FlexPaths = typing.Union[FlexPath, typing.List[FlexPath]] # one or a list of FLexPath

# Determine up-to-date version information, if possible, but with a
# safe fallback to ensure that this file and parameterized.py are the
# only two required files.
Expand Down Expand Up @@ -1817,6 +1822,51 @@ def _resolve(self, path):
return resolve_path(path, path_to_file=False, search_paths=self.search_paths)


class Foldernames(Foldername):
r"""
Parameter that can be set to a string specifying the path of a folder,
a pathlib.Path object, or a list of such strings and/or pathlib.Path objects.

On accessing, the parameter always returns a list of paths, even for a single
jmborr marked this conversation as resolved.
Show resolved Hide resolved
folder.

The string(s) should be specified in UNIX style, but they will be
returned in the format of the user's operating system.

The specified path(s) can be absolute, or relative to either:

* any of the paths specified in the search_paths attribute (if
search_paths is not None);

or

* any of the paths searched by resolve_dir_path() (if search_paths
is None).
"""

@staticmethod
def _cast_to_list(obj):
r"""Cast an iterable onto a list, or insert the non-iterable into a one-item list.

Validation is deferred to _resolve().
"""
if obj is None:
return obj
elif hasattr(obj, "__iter__") and not isinstance(obj, str): # store items in list
return list(obj)
jmborr marked this conversation as resolved.
Show resolved Hide resolved
else:
return [obj] # "my_input_dir" becomes ["my_input_dir"]

def __init__(self, default=None, search_paths=None, **params):
super(Foldernames, self).__init__(self._cast_to_list(default), search_paths, **params)

def __set__(self, param_owner, obj):
super(Foldernames, self).__set__(param_owner, self._cast_to_list(obj))

def _resolve(self, paths: FlexPaths):
r"""Resolve and validate each folder item"""
return [super(Foldernames, self)._resolve(p) for p in paths]


def abbreviate_paths(pathspec,named_paths):
"""
Expand Down