Skip to content

Commit

Permalink
💡 write-the update docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
Wytamma committed Mar 21, 2024
1 parent 0f6cceb commit 61dc689
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 72 deletions.
16 changes: 15 additions & 1 deletion src/snk_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@
class CLI(DynamicTyper):
"""
Constructor for the dynamic Snk CLI class.
Args:
workflow_dir_path (Path): Path to the workflow directory.
Side Effects:
Initializes the CLI class.
Examples:
>>> CLI(Path('/path/to/workflow'))
"""
Expand Down Expand Up @@ -185,10 +188,14 @@ def _create_logo(
):
"""
Create a logo for the CLI.
Args:
font (str): The font to use for the logo.
tagline (str, optional): The tagline to include in the logo. Defaults to "A Snakemake workflow CLI generated with snk".
font (str, optional): The font to use for the logo. Defaults to "small".
Returns:
str: The logo.
Examples:
>>> CLI._create_logo()
"""
Expand All @@ -203,8 +210,13 @@ def _create_logo(
def _find_snakefile(self):
"""
Search possible snakefile locations.
Returns:
Path: The path to the snakefile.
Raises:
FileNotFoundError: If the snakefile is not found.
Examples:
>>> CLI._find_snakefile()
"""
Expand All @@ -216,8 +228,10 @@ def _find_snakefile(self):
def info(self):
"""
Display information about current workflow install.
Returns:
str: A JSON string containing information about the current workflow install.
Examples:
>>> CLI.info()
"""
Expand Down
45 changes: 42 additions & 3 deletions src/snk_cli/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,57 @@


class SnkConfigError(Exception):
"""Base class for all SNK config exceptions"""
"""
Base class for all SNK config exceptions.
"""

class InvalidSnkConfigError(SnkConfigError, ValueError):
"""Thrown if the given SNK config appears to have an invalid format."""
"""
Thrown if the given SNK config appears to have an invalid format.
"""

class MissingSnkConfigError(SnkConfigError, FileNotFoundError):
"""Thrown if the given SNK config file cannot be found."""
"""
Thrown if the given SNK config file cannot be found.
"""

@dataclass
class SnkConfig:
"""
A dataclass for storing Snakemake workflow configuration.
Attributes:
art (str, optional): The art to display in the CLI. Defaults to None.
logo (str, optional): The logo to display in the CLI. Defaults to None.
tagline (str): The tagline to display in the CLI. Defaults to "A Snakemake workflow CLI generated with Snk".
font (str): The font size for the CLI. Defaults to "small".
version (Optional[str], optional): The version of the workflow. Defaults to None.
conda (bool): Whether to use conda for managing environments. Defaults to True.
resources (List[Path]): List of paths to additional resources. Defaults to an empty list.
skip_missing (bool): Whether to skip missing CLI options. Defaults to False.
additional_snakemake_args (List[str]): List of additional Snakemake command-line arguments. Defaults to an empty list.
cli (dict): Dictionary of CLI options and their values. Defaults to an empty dictionary.
symlink_resources (bool): Whether to symlink resources instead of copying them. Defaults to False.
_snk_config_path (Path): The path to the SNK config file. Defaults to None.
Methods:
from_path(snk_config_path: Path) -> SnkConfig:
Load and validate Snk config from file.
from_workflow_dir(workflow_dir_path: Path, create_if_not_exists: bool = False) -> SnkConfig:
Load and validate SNK config from workflow directory.
validate_resources(resources: List[Path]) -> None:
Validate resources.
add_resources(resources: List[Path], workflow_dir_path: Path = None) -> None:
Add resources to the SNK config.
to_yaml(path: Path) -> None:
Write SNK config to YAML file.
save() -> None:
Save SNK config.
"""

art: str = None
Expand Down
19 changes: 13 additions & 6 deletions src/snk_cli/config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@

def get_version_from_config(config_path: Path, config_dict: dict = None) -> str:
"""
Get the version from config. If dict not provided, load from file.
If the version is a path to a __about__ file, load the version from the file.
Path must be relative to the config file.
Get the version from the config file or config dictionary.
Args:
config_path (Path): Path to the config file.
config_dict (dict): Config dict.
config_dict (dict, optional): Config dictionary. Defaults to None.
Returns:
str: Version.
str: The version.
Raises:
FileNotFoundError: If the version file (__about__.py) is not found.
KeyError: If the __version__ key is not found in the version file.
Examples:
>>> get_version_from_config_dict({"version": "0.1.0"})
>>> get_version_from_config(Path("config.yaml"))
'0.1.0'
>>> get_version_from_config(Path("config.yaml"), {"version": "0.2.0"})
'0.2.0'
"""
if not config_dict:
config_dict = load_configfile(config_path)
Expand Down
52 changes: 42 additions & 10 deletions src/snk_cli/dynamic_typer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ def __init_subclass__(cls, **kwargs):
def __call__(self):
"""
Invoke the CLI.
Side Effects:
Invokes the CLI.
Examples:
>>> CLI(Path('/path/to/workflow'))()
"""
Expand All @@ -27,10 +29,13 @@ def __call__(self):
def register_default_command(self, command: Callable, **command_kwargs) -> None:
"""
Register a default command to the CLI.
Args:
command (Callable): The command to register.
Side Effects:
Registers the command to the CLI.
Examples:
>>> CLI.register_default_command(my_command)
"""
Expand Down Expand Up @@ -65,12 +70,17 @@ def register_command(
) -> None:
"""
Register a command to the CLI.
Args:
command (Callable): The command to register.
dynamic_options (List[Option], optional): A list of dynamic options to add to the command.
Side Effects:
Registers the command to the CLI.
Examples:
>>> CLI.register_command(my_command)
>>> CLI.register_command(my_command, dynamic_options=[option1, option2])
"""
if dynamic_options is not None:
command = self.add_dynamic_options(command, dynamic_options)
Expand All @@ -82,34 +92,43 @@ def register_command(
def register_callback(self, command: Callable, **command_kwargs) -> None:
"""
Register a callback to the CLI.
Args:
command (Callable): The callback to register.
Side Effects:
Registers the callback to the CLI.
Examples:
>>> CLI.register_callback(my_callback)
"""
self.app.callback(**command_kwargs)(command)

def register_group(self, group: "DynamicTyper", **command_kwargs) -> None:
"""
Register a subcommand group group to the CLI.
Register a subcommand group to the CLI.
Args:
group (DynamicTyper): The subcommand group to register.
Side Effects:
Registers the subcommand group to the CLI.
Examples:
>>> CLI.register_app(my_group)
>>> CLI.register_group(my_group)
"""
self.app.add_typer(group.app, **command_kwargs)

def _create_cli_parameter(self, option: Option):
"""
Creates a parameter for a CLI option.
Args:
option (Option): An Option object containing the option's name, type, required status, default value, and help message.
Returns:
Parameter: A parameter object for the CLI option.
Examples:
>>> option = Option(name='foo', type='int', required=True, default=0, help='A number')
>>> create_cli_parameter(option)
Expand All @@ -131,8 +150,10 @@ def _create_cli_parameter(self, option: Option):
def check_if_option_passed_via_command_line(self, option: Option):
"""
Check if an option is passed via the command line.
Args:
option (Option): An Option object containing the option's name, type, required status, default value, and help message.
Returns:
bool: Whether the option is passed via the command line.
"""
Expand All @@ -148,15 +169,17 @@ def check_if_option_passed_via_command_line(self, option: Option):
def add_dynamic_options(self, func: Callable, options: List[Option]):
"""
Function to add dynamic options to a command.
Args:
command (Callable): The command to which the dynamic options should be added.
options (List[dict]): A list of dictionaries containing the option's name, type, required status, default value, and help message.
func (Callable): The command to which the dynamic options should be added.
options (List[Option]): A list of Option objects containing the options to add.
Returns:
Callable: A function with the dynamic options added.
Examples:
>>> my_func = add_dynamic_options_to_function(my_func, [{'name': 'foo', 'type': 'int', 'required': True, 'default': 0, 'help': 'A number'}])
>>> my_func = add_dynamic_options_to_function(my_func, [option1, option2])
>>> my_func
<function my_func at 0x7f8f9f9f9f90>
"""
func_sig = signature(func)
params = list(func_sig.parameters.values())
Expand All @@ -168,13 +191,16 @@ def add_dynamic_options(self, func: Callable, options: List[Option]):
def func_wrapper(*args, **kwargs):
"""
Wraps a function with dynamic options.
Args:
*args: Variable length argument list.
**kwargs: Arbitrary keyword arguments.
*args: Variable length argument list.
**kwargs: Arbitrary keyword arguments.
Returns:
Callable: A wrapped function with the dynamic options added.
Callable: A wrapped function with the dynamic options added.
Notes:
This function is used in the `add_dynamic_options_to_function` function.
This function is used in the `add_dynamic_options_to_function` function.
"""
flat_config = None

Expand Down Expand Up @@ -216,6 +242,7 @@ def add_option_to_args():
def error(self, msg, exit=True):
"""
Logs an error message (red) and exits (optional).
Args:
msg (str): The error message to log.
exit (bool): Whether to exit after logging the error message.
Expand All @@ -227,6 +254,7 @@ def error(self, msg, exit=True):
def success(self, msg):
"""
Logs a success message (green).
Args:
msg (str): The success message to log.
"""
Expand All @@ -235,14 +263,18 @@ def success(self, msg):
def log(self, msg, color="yellow", stderr=True):
"""
Logs a message (yellow).
Args:
msg (str): The message to log.
color (str, optional): The color of the log message. Defaults to "yellow".
stderr (bool, optional): Whether to log the message to stderr. Defaults to True.
"""
typer.secho(msg, fg=color, err=stderr)

def echo(self, msg):
"""
Prints a message.
Args:
msg (str): The message to print.
"""
Expand Down
15 changes: 10 additions & 5 deletions src/snk_cli/options/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@ def create_option_from_annotation(
) -> Option:
"""
Create an Option object from a given annotation.
Args:
annotation_key: The key in the annotations.
annotation_values: The dictionary of annotation values.
default_values: default value from config.
annotation_key (str): The key in the annotations.
annotation_values (dict): The dictionary of annotation values.
default_values (dict): Default value from config.
from_annotation (bool, optional): Whether the option is from an annotation. Defaults to False.
Returns:
An Option object.
"""
Option: An Option object.
"""
config_default = default_values.get(annotation_key, None)

default = annotation_values.get(f"{annotation_key}:default", config_default)
Expand Down Expand Up @@ -83,9 +86,11 @@ def build_dynamic_cli_options(
) -> List[dict]:
"""
Builds a list of options from a snakemake config and a snk config.
Args:
snakemake_config (dict): A snakemake config.
snk_config (SnkConfig): A snk config.
Returns:
List[dict]: A list of options.
"""
Expand Down
15 changes: 10 additions & 5 deletions src/snk_cli/subcommands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ class ConfigApp(DynamicTyper):
def __init__(self, workflow: Workflow, options: List[Option]):
"""
Initializes the ConfigApp class.
Args:
workflow (Workflow): The workflow to configure.
workflow (Workflow): The workflow to configure.
"""
self.options = options
self.workflow = workflow
Expand All @@ -22,13 +23,17 @@ def config(
):
"""
Prints the configuration for the workflow.
Args:
pretty (bool, optional): Whether to print the configuration in a pretty format. Defaults to False.
ctx (typer.Context): The Typer context.
pretty (bool, optional): Whether to print the configuration in a pretty format. Defaults to False.
Returns:
None
None
Examples:
>>> ConfigApp.show(pretty=True)
# Pretty printed configuration
>>> ConfigApp.show(pretty=True)
# Pretty printed configuration
"""
import yaml
from collections import defaultdict
Expand Down
Loading

0 comments on commit 61dc689

Please sign in to comment.