Repository: pddg/uroboros

Issue:
Google style docstring
reST style docstring is not easy to read and write for me

Discussion:

Patch:
diff --git a/Pipfile b/Pipfile
index 6095ede..b96cc9f 100644
--- a/Pipfile
+++ b/Pipfile
@@ -10,6 +10,7 @@ tox = "*"
 uroboros = {editable = true,path = "."}
 
 [packages]
+zipp = "*"
 
 [requires]
 python_version = "3.7"
diff --git a/Pipfile.lock b/Pipfile.lock
index 5c9f134..b049267 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "cd721bd9293a6a388c3b0c6f647934a81959b0b382f0faafcc08a74b533529a3"
+            "sha256": "a234c0b9b95d35722035205fef2e372c310c40ad57917e0f9f598266ccf153c3"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -15,7 +15,16 @@
             }
         ]
     },
-    "default": {},
+    "default": {
+        "zipp": {
+            "hashes": [
+                "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
+                "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
+            ],
+            "index": "pypi",
+            "version": "==3.1.0"
+        }
+    },
     "develop": {
         "appdirs": {
             "hashes": [
@@ -34,9 +43,10 @@
         },
         "distlib": {
             "hashes": [
-                "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"
+                "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb",
+                "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"
             ],
-            "version": "==0.3.0"
+            "version": "==0.3.1"
         },
         "filelock": {
             "hashes": [
@@ -149,11 +159,11 @@
         },
         "tox": {
             "hashes": [
-                "sha256:a0d36849e59ac4a28664e80951a634b0e920d88047ce3fa8fa7b45216e573f30",
-                "sha256:db12b48359ba2cbc8c8f7ab712706ee67d59f8f9e8e0c795dcc45349b8784e48"
+                "sha256:60c3793f8ab194097ec75b5a9866138444f63742b0f664ec80be1222a40687c5",
+                "sha256:9a746cda9cadb9e1e05c7ab99f98cfcea355140d2ecac5f97520be94657c3bc7"
             ],
             "index": "pypi",
-            "version": "==3.16.0"
+            "version": "==3.16.1"
         },
         "uroboros": {
             "editable": true,
@@ -175,8 +185,12 @@
             "version": "==0.2.5"
         },
         "zipp": {
-            "editable": true,
-            "path": "."
+            "hashes": [
+                "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
+                "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
+            ],
+            "index": "pypi",
+            "version": "==3.1.0"
         }
     }
 }
diff --git a/uroboros/command.py b/uroboros/command.py
index 2188031..e6d14a6 100644
--- a/uroboros/command.py
+++ b/uroboros/command.py
@@ -11,6 +11,7 @@ from uroboros.constants import ExitStatus
 if TYPE_CHECKING:
     from typing import List, Dict, Optional, Union, Set
     from uroboros.option import Option
+    CommandDict = Dict['Command', 'Optional[Command]']
 
 
 class Command(metaclass=abc.ABCMeta):
@@ -43,12 +44,14 @@ class Command(metaclass=abc.ABCMeta):
         self._parser = None  # type: Optional[argparse.ArgumentParser]
 
     def execute(self, argv: 'List[str]' = None) -> int:
-        """
-        Execute `uroboros.command.Command.run` internally.
-        And return exit code (integer).
-        :param argv:    Arguments to parse. If None is given, try to parse
-                        `sys.argv` by the parser of this command.
-        :return:        Exit status (integer only)
+        """Execute the command and return exit code (integer)
+
+        Args:
+            argv (:obj: List[str], optional): Arguments to parse. If None is
+                given (e.g. do not pass any args), try to parse `sys.argv` .
+
+        Returns:
+            int: Exit status code
         """
         assert getattr(self, "name", None) is not None, \
             "{} does not have `name` attribute.".format(
@@ -85,20 +88,43 @@ class Command(metaclass=abc.ABCMeta):
 
     @abc.abstractmethod
     def run(self, args: 'argparse.Namespace') -> 'Union[ExitStatus, int]':
+        """This method must implement user defined action.
+
+        This method is an abstract method of this class. This should be
+        overwitten by the user. Return exit status code after execution.
+
+        Args:
+            args (argparse.Namespace): Parsed arguments.
+
+        Returns:
+            Union[ExitStatus, int]: Exit status code
+        """
         raise NotImplementedError
 
     def build_option(self, parser: 'argparse.ArgumentParser') \
             -> 'argparse.ArgumentParser':
+        """Configure ArgumentParser to add user defined options of this command.
+
+        If you want to add your own option to this command, you can override
+        this function. Then, you can configure the parser given by argument
+        of this function.
+
+        Args:
+            parser (argparse.ArgumentParser): Parsed arguments.
+
+        Returns:
+            argparse.ArgumentParser: Configured argument parser.
+        """
         return parser
 
     def initialize(self, parser: 'Optional[argparse.ArgumentParser]' = None):
-        """
-        Initialize this command and its sub commands recursively.
-        :param parser: `argparse.ArgumentParser` of parent command
-        :return: None
+        """Initialize this command and its sub commands recursively.
+
+        Args:
+            parser (argparse.ArgumentParser): ArgumentParser of parent command
         """
         if parser is None:
-            self._parser = self.create_default_parser()
+            self._parser = self._create_default_parser()
         else:
             self._parser = parser
         # Add validator
@@ -107,9 +133,9 @@ class Command(metaclass=abc.ABCMeta):
         # Add function to execute
         self._parser.set_defaults(func=self.run)
         self.build_option(self._parser)
-        self.initialize_sub_parsers(self._parser)
+        self._initialize_sub_parsers(self._parser)
 
-    def initialize_sub_parsers(self, parser: 'argparse.ArgumentParser'):
+    def _initialize_sub_parsers(self, parser: 'argparse.ArgumentParser'):
         if len(self.sub_commands) == 0:
             return
         parser = parser.add_subparsers(
@@ -125,7 +151,7 @@ class Command(metaclass=abc.ABCMeta):
             )
             cmd.initialize(sub_parser)
 
-    def create_default_parser(self) -> 'argparse.ArgumentParser':
+    def _create_default_parser(self) -> 'argparse.ArgumentParser':
         parser = argparse.ArgumentParser(
             prog=self.name,
             description=self.long_description,
@@ -135,10 +161,13 @@ class Command(metaclass=abc.ABCMeta):
         return parser
 
     def add_command(self, *commands: 'Command') -> 'Command':
-        """
-        Add sub command to this command.
-        :param commands: An instance of `uroboros.command.Command`
-        :return: None
+        """Add sub command to this command.
+
+        Add one or more commands to this command.
+        The added commands are callable as its sub command.
+
+        Args:
+            commands: An instance of sub commands
         """
         for command in commands:
             assert isinstance(command, Command), \
@@ -161,15 +190,27 @@ class Command(metaclass=abc.ABCMeta):
         return {id(cmd) for cmd in self.sub_commands}
 
     def register_parent(self, parent_ids: 'Set[int]'):
+        """Register parent command
+
+        This function is used internaly.
+        Register all parent command ids to check that the command has
+        already been registered.
+
+        Args:
+            parent_ids (Set[int]): Set of parent command instance ids
+        """
         self._parent_ids |= parent_ids
         for cmd in self.sub_commands:
             cmd.register_parent(self._parent_ids)
 
     def increment_nest(self, parent_layer_count: int):
-        """
-        Increment the depth of this command.
-        :param parent_layer_count: Number of nest of parent command.
-        :return: None
+        """Increment the depth of this command and its children.
+
+        This function is used internaly.
+        Increment the nest of this command and its children recursively.
+
+        Args:
+            parent_layer_count (int): Number of nest of parent command.
         """
         self._layer = parent_layer_count + 1
         # Propagate the increment to sub commands
@@ -177,12 +218,14 @@ class Command(metaclass=abc.ABCMeta):
             cmd.increment_nest(self._layer)
 
     def get_sub_commands(self, args: 'argparse.Namespace') -> 'List[Command]':
-        """
-        Get the list of `Command` specified by CLI except myself.
-        if myself is root_cmd and "root_cmd first_cmd second_cmd"
+        """Get the list of `Command` specified by CLI except myself.
+
+        If myself is root_cmd and "root_cmd first_cmd second_cmd"
         is specified in CLI, this may return the instances of first_cmd
         and second_cmd.
-        :return: List of `uroboros.Command`
+
+        Returns:
+            List[Command]: Child commands of this command.
         """
         commands = []
         # Do not include myself
@@ -193,11 +236,10 @@ class Command(metaclass=abc.ABCMeta):
             layer += 1
         return commands
 
-    def get_all_sub_commands(self) -> 'Dict[Command, dict]':
-        """
-        Get the nested dictionary of `Command`.
+    def get_all_sub_commands(self) -> 'Dict[Command, CommandDict]':
+        """Get all child commands of this command including itself.
+
         Traverse all sub commands of this command recursively.
-        :return: Dictionary of command
         {
             self: {
               first_command: {
@@ -213,6 +255,9 @@ class Command(metaclass=abc.ABCMeta):
               ...
             },
         }
+
+        Returns:
+           Dict[Command, CommandDict]: All child commands of this command.
         """
         commands_dict = {}
         for sub_cmd in self.sub_commands:
@@ -222,15 +267,22 @@ class Command(metaclass=abc.ABCMeta):
         }
 
     def get_options(self) -> 'List[Option]':
-        """
-        Get all `Option` instance of this `Command`.
-        :return: List of Option instance
+        """Get all uroboros.`Option instance of this `Command.
+
+        Returns:
+            List[Option]: List of uroboros.Option instance
         """
         return [opt() if type(opt) == type else opt for opt in self.options]
 
     def print_help(self):
-        """
-        Helper method for print the help message of this command.
+        """Helper method for print the help message of this command.
+
+        Raises:
+            errors.CommandNotRegisteredError: If this command has
+                not been initialized.
+
+        Note:
+            This function can be called after initialization.
         """
         self._check_initialized()
         return self._parser.print_help()
@@ -262,14 +314,18 @@ class Command(metaclass=abc.ABCMeta):
     def before_validate(self,
                         unsafe_args: 'argparse.Namespace'
                         ) -> 'argparse.Namespace':
-        """
-        Hook function before validation. This method will be called
-        in order from root command to its children.
+        """Hook function before validation
+
+        This method will be called in order from root command to its children.
         Use `unsafe_args` carefully since it has not been validated yet.
         You can set any value into `unsafe_args` and you must return it
         finally.
-        :param unsafe_args: An instance of argparse.Namespace
-        :return: An instance of argparse.Namespace
+
+        Args:
+            unsafe_args (argparse.Namespace): Parsed arguments which
+                are not validated.
+        Returns:
+            argparse.Namespace: An instance of argparse.Namespace
         """
         return unsafe_args
 
@@ -282,10 +338,13 @@ class Command(metaclass=abc.ABCMeta):
         return exceptions
 
     def validate(self, args: 'argparse.Namespace') -> 'List[Exception]':
-        """
-        Validate parameters of given options.
-        :param args: Parsed arguments
-        :return: The list of exceptions
+        """Validate parameters of given options.
+
+        Args:
+            args (argparse.Namespace): Parsed arguments which
+                are not validated.
+        Returns:
+            List[Exception]: The list of exceptions
         """
         exceptions = []
         for opt in self.options:
@@ -306,17 +365,27 @@ class Command(metaclass=abc.ABCMeta):
     def after_validate(self,
                        safe_args: 'argparse.Namespace'
                        ) -> 'argparse.Namespace':
-        """
-        Hook function after validation. This method will be called
-        in order from root command to its children.
-        Given argument `safe_args` is validated by validation method
-        of your commands. You can set any value into `safe_args` and
-        you must return it finally.
-        :param safe_args: An instance of argparse.Namespace
-        :return: An instance of argparse.Namespace
+        """Hook function after validation.
+
+        This method will be called in order from root command to
+        its children. Given argument `safe_args` is validated by
+        validation method of your commands. You can set any value
+        into `safe_args` and you must return it finally.
+
+        Args:
+            safe_args (argparse.Namespace): Validated arguments
+
+        Returns:
+            argparse.Namespace: An instance of argparse.Namespace
         """
         return safe_args
 
     def _check_initialized(self):
+        """Check that this command has been initialized.
+
+        Raises:
+            errors.CommandNotRegisteredError: If this command has
+                not been initialized.
+        """
         if self._parser is None:
             raise errors.CommandNotRegisteredError(self.name)
diff --git a/uroboros/errors.py b/uroboros/errors.py
index 19b12cc..f106ea3 100644
--- a/uroboros/errors.py
+++ b/uroboros/errors.py
@@ -5,6 +5,7 @@ if TYPE_CHECKING:
 
 
 class CommandNotRegisteredError(Exception):
+    """The command is not initialized"""
 
     def __init__(self, name: str):
         self.name = name
@@ -25,6 +26,7 @@ class NoCommandError(Exception):
 
 
 class CommandDuplicateError(Exception):
+    """Same command instance is added"""
 
     def __init__(self, command: 'Command', parent: 'Command'):
         self.command = command
diff --git a/uroboros/option.py b/uroboros/option.py
index 65bf35f..c98d7a4 100644
--- a/uroboros/option.py
+++ b/uroboros/option.py
@@ -8,6 +8,7 @@ if TYPE_CHECKING:
 
 
 class Option(metaclass=abc.ABCMeta):
+    """Common option class"""
 
     def __init__(self):
         self.parser = argparse.ArgumentParser(add_help=False)
@@ -19,17 +20,64 @@ class Option(metaclass=abc.ABCMeta):
     @abc.abstractmethod
     def build_option(self, parser: 'argparse.ArgumentParser') \
             -> 'argparse.ArgumentParser':
+        """Configure ArgumentParser to add user defined options of this command.
+
+        You must override this function. You should configure the parser given
+        by argument of this function, then return the parser.
+        This method and `uroboros.Command.build_option` method are functionally
+        equivalent.
+
+        Args:
+            parser (argparse.ArgumentParser): Initialized argument parser
+
+        Returns:
+            argparse.ArgumentParser: Configured argument parser.
+        """
         raise NotImplementedError
 
     def before_validate(self,
                         unsafe_args: 'argparse.Namespace'
                         ) -> 'argparse.Namespace':
+        """Hook function before validation
+
+        This method will be called in order from root command to its children.
+        Use `unsafe_args` carefully since it has not been validated yet.
+        You can set any value into `unsafe_args` and you must return it
+        finally.
+
+        Args:
+            unsafe_args (argparse.Namespace): Parsed arguments which
+                are not validated.
+        Returns:
+            argparse.Namespace: An instance of argparse.Namespace
+        """
         return unsafe_args
 
     def validate(self, args: 'argparse.Namespace') -> 'List[Exception]':
+        """Validate parameters of given options.
+
+        Args:
+            args (argparse.Namespace): Parsed arguments which
+                are not validated.
+        Returns:
+            List[Exception]: The list of exceptions
+        """
         return []
 
     def after_validate(self,
                        safe_args: 'argparse.Namespace'
                        ) -> 'argparse.Namespace':
+        """Hook function after validation.
+
+        This method will be called in order from root command to
+        its children. Given argument `safe_args` is validated by
+        validation method of your commands. You can set any value
+        into `safe_args` and you must return it finally.
+
+        Args:
+            safe_args (argparse.Namespace): Validated arguments
+
+        Returns:
+            argparse.Namespace: An instance of argparse.Namespace
+        """
         return safe_args
diff --git a/uroboros/utils.py b/uroboros/utils.py
index 6a90229..ce072ac 100644
--- a/uroboros/utils.py
+++ b/uroboros/utils.py
@@ -1,16 +1,22 @@
 def get_args_command_name(layer: int):
+    """Return the specified layer's command name"""
     return "__layer{layer}_command".format(layer=layer)
 
 
 def get_args_validator_name(layer: int):
+    """Return the specified layer's validator name"""
     return "__layer{layer}_validator".format(layer=layer)
 
 
 def get_args_section_name(layer: int):
+    """Return the specified layer's parser name"""
     return "__layer{layer}_parser".format(layer=layer)
 
 
 def call_one_by_one(objs, method_name: str, args, **kwargs):
+    """
+    Call specified method of given objects with given args in order.
+    """
     for obj in objs:
         assert hasattr(obj, method_name), \
             "'{cmd}' has no method '{method}".format(


Test patch:
diff --git a/tests/test_command.py b/tests/test_command.py
index 05a703b..d3e2e34 100644
--- a/tests/test_command.py
+++ b/tests/test_command.py
@@ -347,7 +347,7 @@ class TestCommand(object):
 
         argv = ["--test", "test"]
         for cmd in [Cmd(), Cmd2()]:
-            cmd_parser = cmd.create_default_parser()
+            cmd_parser = cmd._create_default_parser()
             args = cmd_parser.parse_args(argv)
             assert args.test == 'test'