def test_numpy(self): # Docstring taken from Napoleon's example (plus a keyword argument). doc = """ One line summary. Extended description. Parameters ---------- arg1 : int Description of arg1 arg2 : str Description of arg2 Keyword Arguments ----------------- arg3 : float Description of arg3 Returns ------- str Description of return value. Examples -------- >>> print("hello, world") """ doc = defopt._parse_docstring(inspect.cleandoc(doc)) self._check_doc(doc)
def test_parse_doubles(self): doc = """ Test function :param int param: the parameter :type param: int """ with self.assertRaises(ValueError): defopt._parse_docstring(inspect.cleandoc(doc)) doc = """Test function :type param: int :param int param: the parameter """ with self.assertRaises(ValueError): defopt._parse_docstring(inspect.cleandoc(doc))
def test_literal_block(self): doc = """ :: Literal block Multiple lines """ doc = defopt._parse_docstring(inspect.cleandoc(doc)) self.assertEqual(doc.text, ' Literal block\n Multiple lines')
def test_sphinx(self): doc = """ One line summary. Extended description. :param int arg1: Description of arg1 :param str arg2: Description of arg2 :keyword float arg3: Description of arg3 :returns: Description of return value. :rtype: str .. rubric:: examples >>> print("hello, world") """ doc = defopt._parse_docstring(inspect.cleandoc(doc)) self._check_doc(doc)
def test_parse_params(self): doc = """ Test function :param first: first param :parameter int second: second param :arg third: third param :argument float fourth: fourth param :key fifth: fifth param :keyword str sixth: sixth param """ doc = defopt._parse_docstring(inspect.cleandoc(doc)) self.assertEqual(doc.params['first'].text, 'first param') self.assertEqual(doc.params['second'].text, 'second param') self.assertEqual(doc.params['third'].text, 'third param') self.assertEqual(doc.params['fourth'].text, 'fourth param') self.assertEqual(doc.params['fifth'].text, 'fifth param') self.assertEqual(doc.params['sixth'].text, 'sixth param')
def test_parse_docstring(self): doc = """ Test function :param one: first param :type one: int :param float two: second param :returns: str :junk one two: nothing """ doc = defopt._parse_docstring(inspect.cleandoc(doc)) self.assertEqual(doc.text, 'Test function') one = doc.params['one'] self.assertEqual(one.text, 'first param') self.assertEqual(one.type, 'int') two = doc.params['two'] self.assertEqual(two.text, 'second param') self.assertEqual(two.type, 'float')
def test_newlines(self): doc = """ Bar Baz .. a comment - bar - baz quux:: hello 1. bar #. baz ii. bar #. baz """ doc = defopt._parse_docstring(inspect.cleandoc(doc)) # Use inspect.cleandoc and not textwrap.dedent, as we want to keep # whitespace in lines that contain more than the common leading # whitespace. self.assertEqual(doc.text, inspect.cleandoc("""\ Bar Baz - bar - baz quux: hello 1. bar 2. baz ii. bar iii. baz"""))
def test_google(self): # Docstring taken from Napoleon's example (plus a keyword argument). doc = """ One line summary. Extended description. Args: arg1(int): Description of arg1 arg2(str): Description of arg2 Keyword Arguments: arg3(float): Description of arg3 Returns: str: Description of return value. Examples: >>> print("hello, world") """ doc = defopt._parse_docstring(inspect.cleandoc(doc)) self._check_doc(doc)
def test_explicit_role(self): doc = defopt._parse_docstring("""start :py:class:`int` end""") self.assertEqual(doc.text, 'start int end')
def test_implicit_role(self): doc = defopt._parse_docstring("""start `int` end""") self.assertEqual(doc.text, 'start \033[4mint\033[0m end')
def test_param_only(self): doc = defopt._parse_docstring(""":param int param: test""") self.assertEqual(doc.text, '') param = doc.params['param'] self.assertEqual(param.text, 'test') self.assertEqual(param.type, 'int')
def test_no_doc(self): doc = defopt._parse_docstring(None) self.assertEqual(doc.text, '') self.assertEqual(doc.params, {})
def function_to_form(func, *, config: dict = None, name: str = None) -> Type[forms.Form]: """Convert a function to a Django Form. Args: func: the function to be changed config: A dictionary with keys ``widgets`` and ``fields`` each mapping types/specific arguments to custom fields """ name = name or func.__qualname__ sig = signature(func) # i.e., class body for form fields = {} defaults = {} for parameter in sig.parameters.values(): field = param_to_field(parameter, config) fields[parameter.name] = field if parameter.default is not Parameter.empty: defaults[parameter.name] = parameter.default if isinstance(field, forms.TypedChoiceField): field._parameter_name = parameter.name field._func_name = name if parameter.default and parameter.default is not Parameter.empty: for potential_default in [ parameter.default.name, parameter.default.value ]: if any(potential_default == x[0] for x in field.choices): defaults[parameter.name] = potential_default break else: raise ValueError( f"Cannot figure out how to assign default for {parameter.name}: {parameter.default}" ) fields["__doc__"] = re.sub("\n+", "\n", _parse_docstring(inspect.getdoc(func)).text) form_name = "".join(part.capitalize() for part in func.__name__.split("_")) class BaseForm(forms.Form): _func = func _input_defaults = defaults # use this for ignoring extra args from createview and such def __init__(self, *a, instance=None, user=None, **k): from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit super().__init__(*a, **k) self.user = user self.helper = FormHelper(self) self.helper.add_input(Submit("submit", "Execute!")) def execute_function(self): # TODO: reconvert back to enum type! :( return func(**self.cleaned_data) def save(self): from .models import ExecutionResult obj = ExecutionResult(func_name=name, input_json=self.cleaned_data, user=self.user) obj.save() return obj return type(form_name, (BaseForm, ), fields)