## About Signatures
* Argnames = Signature - Kwargs; this is a good way to report missing required arg
+## About Singledispatch
+
+* Use `@singledispatch` decorator to mark function`foo` a generic function.
+* Use `@foo.register` decorator to mark a function as an implemntation of `foo`.
+* Dispatch is based on the type of the first argument.
+* Method resolution is from specific to generic.
+* Use `foo.dispatch(<type>)` to determine which function is dispatched by `<type>`.
+* Use `foo.registry` to see all registered functions.
+
# About MainCLI
Purpose:
1. Search for other commands to register with it
+import argparse
from collections.abc import Callable
+from functools import singledispatch
import inspect
-import argparse
+from typing import Union
+
def get_defaults(fn: Callable)->tuple[list, dict]:
""" Returns a dict of required arguments and a dict of kwarg defaults. """
argname = prm.name
# NOTE: If you don't specify type in add_argument(), it will be parsed as a string.
- # HACK: Whenever you see <class 'int'> you can use it as an initializer.
- # It works consistently, but I haven't seen it as defined/supported behavior.
- # Ex: type(42)('36') creates an integer 36.
- if prm.annotation in (int, float, str):
- arg_specs['type'] = prm.annotation
- else:
- pass
+ # Use get_argspecs() to add type-specific information to the arg_spec.
+ arg_specs | get_argspecs(prm.annotation(), arg_specs)
parser.add_argument(argname, **arg_specs)
return parser
+
+# NOTE: When a single dispatch function is invoked, the the first arg is inspected.
+# Based on the type of the first argument, a corresponding implementation is dispatched.
+# Use @foo.register to register an implementation to the single dispatch function foo.
+# The following two hacks are used throughout the get_argspecs implementations:
+# HACK: type(annotation) == type. But type(annotation()) == str | int | whatever.
+# This hack works because of the following hack.
+# HACK: The 'type' type is callable. It behaves like a constructor for it's type.
+# For example: `type(42)('36')` creates an integer 36.
+# It works consistently, but I haven't seen it as defined/supported behavior.
+@singledispatch
+def get_argspecs(annotation: type, arg_specs: dict)->dict:
+ """ Creates a partial argspec dictionary from a parameter annotation. """
+ return arg_specs
+
+@get_argspecs.register
+def scalar_argspecs(annotation: Union[int, float, str], arg_specs)->dict:
+ """ Implements get_argspecs for integers, floats, and strings. """
+ arg_specs['type'] = type(annotation)
+ return arg_specs
+
\ No newline at end of file