Source code for django_typer.completers.path
import os
import sys
import typing as t
from functools import partial
from pathlib import Path
from click import Context, Parameter
from click.shell_completion import CompletionItem
from django.conf import settings
def _settings_path(name: str) -> t.Optional[Path]:
s_pth = getattr(settings, name, None)
if s_pth:
return Path(s_pth)
return None
[docs]
def import_paths(
ctx: Context,
param: Parameter,
incomplete: str,
root: t.Union[t.Callable[[], t.Optional[Path]], t.Optional[Path]] = None,
) -> t.List[CompletionItem]:
"""
A completer that completes a python dot import path string based on sys.path.
:param ctx: The click context.
:param param: The click parameter.
:param incomplete: The incomplete string.
:param root: The root path to search for modules.
:return: A list of available matching import paths
"""
import pkgutil
rt = root() if callable(root) else root
incomplete = incomplete.strip()
completions = []
packages = [pkg for pkg in incomplete.split(".") if pkg]
pkg_complete = not incomplete or incomplete.endswith(".")
module_import = ".".join(packages) if pkg_complete else ".".join(packages[:-1])
module_path = Path(module_import.replace(".", "/"))
search_paths = []
if rt and (rt / module_path).exists():
search_paths.append(str(rt / module_path))
else:
for pth in sys.path:
if (Path(pth) / module_path).exists():
search_paths.append(str(Path(pth) / module_path))
prefix = "" if pkg_complete else packages[-1]
for module in pkgutil.iter_modules(path=search_paths):
if module.name.startswith(prefix):
completions.append(
CompletionItem(
f"{module_import}{'.' if module_import else ''}{module.name}",
type="plain",
)
)
if len(completions) == 1 and not completions[0].value.endswith("."):
return import_paths(ctx, param, f"{completions[0].value}.") or completions
return completions
[docs]
def paths(
ctx: Context,
param: Parameter,
incomplete: str,
dir_only: t.Optional[bool] = None,
root: t.Union[t.Callable[[], t.Optional[Path]], t.Optional[Path]] = None,
) -> t.List[CompletionItem]:
"""
A completer that completes a path. Relative incomplete paths are interpreted
relative to the current working directory.
:param ctx: The click context.
:param param: The click parameter.
:param incomplete: The incomplete string.
:param dir_only: Restrict completions to paths to directories only, otherwise
complete directories or files.
:param root: Restrict completions to this root path.
:return: A list of available matching directories
"""
rt = root() if callable(root) else root
def exists(pth: Path) -> bool:
if dir_only:
return pth.is_dir()
return pth.exists() or pth.is_symlink()
separator = os.sep
if "/" in incomplete:
if "\\" not in incomplete:
separator = "/"
elif "\\" in incomplete:
separator = "\\"
completions = []
if rt:
incomplete_path = rt / Path(
incomplete.replace(separator, os.path.sep).lstrip(os.path.sep)
)
else:
incomplete_path = Path(incomplete.replace(separator, os.path.sep))
partial_dir = ""
if not exists(incomplete_path) and not incomplete.endswith(separator):
partial_dir = incomplete_path.name
incomplete_path = incomplete_path.parent
elif incomplete_path.is_file() and not dir_only:
return [CompletionItem(incomplete, type="file")]
if incomplete_path.is_dir():
for child in os.listdir(incomplete_path):
if not exists(incomplete_path / child):
continue
if child.startswith(partial_dir):
to_complete = incomplete[0 : (-len(partial_dir) or None)]
sep = (
""
if not to_complete or to_complete.endswith(separator)
else separator
)
ctype = (
"plain"
if rt
else "dir"
if (incomplete_path / child).is_dir()
else "file"
)
completions.append(
CompletionItem(
f"{to_complete}{sep}{child}",
type=ctype,
)
)
if (
len(completions) == 1
and Path(completions[0].value).is_dir()
and [
child
for child in os.listdir(completions[0].value)
if exists(Path(completions[0].value) / child)
]
):
# recurse because we can go futher
return paths(ctx, param, completions[0].value, dir_only=dir_only)
return completions
directories = partial(paths, dir_only=True)
"""
A completer that completes a directory path (but not files). Relative incomplete paths
are interpreted relative to the current working directory.
:param ctx: The click context.
:param param: The click parameter.
:param incomplete: The incomplete string.
:return: A list of available matching directories
"""
static_paths = partial(paths, root=partial(_settings_path, name="STATIC_ROOT"))
"""
Complete static file paths.
:param ctx: The click context.
:param param: The click parameter.
:param incomplete: The incomplete string.
:param dir_only: Restrict completions to paths to directories only, otherwise
complete directories or files.
:return: A list of available matching directories
"""
media_paths = partial(paths, root=partial(_settings_path, name="MEDIA_ROOT"))
"""
Complete media file paths.
:param ctx: The click context.
:param param: The click parameter.
:param incomplete: The incomplete string.
:param dir_only: Restrict completions to paths to directories only, otherwise
complete directories or files.
:return: A list of available matching directories
"""