From 50246ba92e3a2c851a775d831333d7d9d624d3a7 Mon Sep 17 00:00:00 2001 From: s01st Date: Fri, 21 Nov 2025 09:14:08 -0600 Subject: [PATCH 1/7] patched NumpyDocString class to support parameter formated sections besides just Parameters e.g. Attributes. --- fastcore/docscrape.py | 50 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/fastcore/docscrape.py b/fastcore/docscrape.py index 74e854ee..b0c3b550 100644 --- a/fastcore/docscrape.py +++ b/fastcore/docscrape.py @@ -25,6 +25,7 @@ from warnings import warn from collections import namedtuple from collections.abc import Mapping +from typing import Any __all__ = ['Parameter', 'NumpyDocString', 'dedent_lines'] @@ -93,6 +94,20 @@ def __str__(self): SECTIONS = 'Summary Extended Yields Receives Other Raises Warns Warnings See Also Notes References Examples Attributes Methods'.split() +'''Named `numpydoc` sections see: https://numpydoc.readthedocs.io/en/latest/format.html#sections''' + +PARAM_SECTIONS = { + "Parameters", + "Other Parameters", + "Attributes", + "Methods", + "Raises", + "Warns", + "Yields", + "Receives" +} +'''Set of `numpydoc` sections which should support parameters via `Parameter`.''' + class NumpyDocString(Mapping): "Parses a numpydoc string to an abstract representation" @@ -102,14 +117,26 @@ class NumpyDocString(Mapping): sections['Parameters'] = [] sections['Returns'] = [] - def __init__(self, docstring, config=None): + def __init__(self, docstring, config=None, supports_params: set[str] = PARAM_SECTIONS): + # --- original initialization --- docstring = textwrap.dedent(docstring).split('\n') self._doc = Reader(docstring) self._parsed_data = copy.deepcopy(self.sections) self._parse() + + # --- fastcore default normalization --- self['Parameters'] = {o.name:o for o in self['Parameters']} if self['Returns']: self['Returns'] = self['Returns'][0] - for section in SECTIONS: self[section] = dedent_lines(self[section], split=False) + + # --- our patch: normalize ALL parameter-like sections --- + for sec in supports_params: + if sec in self._parsed_data: + self._parsed_data[sec] = self._normalize_param_section(self._parsed_data[sec]) + + + # --- continue normal fastcore behavior --- + for section in SECTIONS: + self[section] = dedent_lines(self[section], split=False) def __iter__(self): return iter(self._parsed_data) def __len__(self): return len(self._parsed_data) @@ -174,6 +201,25 @@ def _parse_param_list(self, content, single_element_is_type=False): params.append(Parameter(arg_name, arg_type, desc)) return params + def _normalize_param_section(self, val: list[Parameter] | Any) -> dict[Parameter] | Any: + """ + Convert lists of `Parameter` objects into a dict or clean list. + """ + # Not a list? Then noop. + if not isinstance(val, list): + return val + + # Falsy value i.e. empty list? Then noop. + if not val: + return val + + # Lazy check, assumes if first value is a Parameter, all are. + if not isinstance(val[0], Parameter): + return val + + # Convert to dict[name -> Parameter] + return {p.name: p for p in val} + def _parse_summary(self): """Grab signature (if given) and summary""" if self._is_at_section(): return From ea9bbc7d047f9bbde37610bc797188919e408d05 Mon Sep 17 00:00:00 2001 From: s01st Date: Fri, 21 Nov 2025 09:29:30 -0600 Subject: [PATCH 2/7] added some configuration for which sections support parameters and exposed them via __init__ --- fastcore/docscrape.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/fastcore/docscrape.py b/fastcore/docscrape.py index b0c3b550..27f52a8f 100644 --- a/fastcore/docscrape.py +++ b/fastcore/docscrape.py @@ -112,12 +112,41 @@ def __str__(self): class NumpyDocString(Mapping): "Parses a numpydoc string to an abstract representation" # See the NumPy Doc Manual https://numpydoc.readthedocs.io/en/latest/format.html> + # TODO: flushout docstring + + # NOTE: unclear why these are class variables sections = {o:[] for o in SECTIONS} sections['Summary'] = [''] + + # NOTE: unclear why these are not included in `SECTIONS` given initialization above creates lists sections['Parameters'] = [] sections['Returns'] = [] - def __init__(self, docstring, config=None, supports_params: set[str] = PARAM_SECTIONS): + # NOTE: following above style, adding `param_sections` as class variable + param_sections: set[str] = set(PARAM_SECTIONS) + + def __init__( + self, docstring, + config=None, # TODO: figure this out + supported_sections: list[str] | None = SECTIONS, + supports_params: set[str] | None = PARAM_SECTIONS + ): + + # If None, set to default supported set + if supports_params is None: supports_params = set(PARAM_SECTIONS) + else: + # add missing to class variable + missing = set(supports_params) - set(self.param_sections) + for sec in missing: self.param_sections.add(sec) + + # If None, set to default supported set + if supported_sections is None: supported_sections = set(SECTIONS) + else: + # add missing to class variable + missing = set(supported_sections) - set(self.sections.keys()) + for sec in missing: self.sections[sec] = [] + + # --- original initialization --- docstring = textwrap.dedent(docstring).split('\n') self._doc = Reader(docstring) From 772dcd4b6e14108d42811a323968c2651300bcc5 Mon Sep 17 00:00:00 2001 From: s01st Date: Mon, 24 Nov 2025 08:32:26 -0600 Subject: [PATCH 3/7] removed todos and commnets per request --- fastcore/docscrape.py | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/fastcore/docscrape.py b/fastcore/docscrape.py index 27f52a8f..5b81a6f6 100644 --- a/fastcore/docscrape.py +++ b/fastcore/docscrape.py @@ -94,48 +94,31 @@ def __str__(self): SECTIONS = 'Summary Extended Yields Receives Other Raises Warns Warnings See Also Notes References Examples Attributes Methods'.split() -'''Named `numpydoc` sections see: https://numpydoc.readthedocs.io/en/latest/format.html#sections''' PARAM_SECTIONS = { - "Parameters", - "Other Parameters", - "Attributes", - "Methods", - "Raises", - "Warns", - "Yields", - "Receives" + "Parameters", "Other Parameters", "Attributes", "Methods", + "Raises", "Warns", "Yields", "Receives" } -'''Set of `numpydoc` sections which should support parameters via `Parameter`.''' - class NumpyDocString(Mapping): "Parses a numpydoc string to an abstract representation" # See the NumPy Doc Manual https://numpydoc.readthedocs.io/en/latest/format.html> - # TODO: flushout docstring - # NOTE: unclear why these are class variables sections = {o:[] for o in SECTIONS} sections['Summary'] = [''] - - # NOTE: unclear why these are not included in `SECTIONS` given initialization above creates lists sections['Parameters'] = [] sections['Returns'] = [] - - # NOTE: following above style, adding `param_sections` as class variable param_sections: set[str] = set(PARAM_SECTIONS) def __init__( self, docstring, - config=None, # TODO: figure this out + config=None, supported_sections: list[str] | None = SECTIONS, supports_params: set[str] | None = PARAM_SECTIONS ): - # If None, set to default supported set if supports_params is None: supports_params = set(PARAM_SECTIONS) else: - # add missing to class variable missing = set(supports_params) - set(self.param_sections) for sec in missing: self.param_sections.add(sec) @@ -157,7 +140,7 @@ def __init__( self['Parameters'] = {o.name:o for o in self['Parameters']} if self['Returns']: self['Returns'] = self['Returns'][0] - # --- our patch: normalize ALL parameter-like sections --- + # --- normalize ALL parameter-like sections --- for sec in supports_params: if sec in self._parsed_data: self._parsed_data[sec] = self._normalize_param_section(self._parsed_data[sec]) From 9cc2c120eac8b48bf880fe1e65bbe4bee9d38f74 Mon Sep 17 00:00:00 2001 From: s01st Date: Tue, 25 Nov 2025 08:23:31 -0600 Subject: [PATCH 4/7] reduced number of lines to adhere to prefered style --- fastcore/docscrape.py | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/fastcore/docscrape.py b/fastcore/docscrape.py index 5b81a6f6..4b29fe3f 100644 --- a/fastcore/docscrape.py +++ b/fastcore/docscrape.py @@ -94,11 +94,7 @@ def __str__(self): SECTIONS = 'Summary Extended Yields Receives Other Raises Warns Warnings See Also Notes References Examples Attributes Methods'.split() - -PARAM_SECTIONS = { - "Parameters", "Other Parameters", "Attributes", "Methods", - "Raises", "Warns", "Yields", "Receives" -} +PARAM_SECTIONS = {"Parameters", "Other Parameters", "Attributes", "Methods", "Raises", "Warns", "Yields", "Receives"} class NumpyDocString(Mapping): "Parses a numpydoc string to an abstract representation" @@ -110,12 +106,7 @@ class NumpyDocString(Mapping): sections['Returns'] = [] param_sections: set[str] = set(PARAM_SECTIONS) - def __init__( - self, docstring, - config=None, - supported_sections: list[str] | None = SECTIONS, - supports_params: set[str] | None = PARAM_SECTIONS - ): + def __init__(self, docstring, config=None, supported_sections: list[str] | None = SECTIONS, supports_params: set[str] | None = PARAM_SECTIONS): if supports_params is None: supports_params = set(PARAM_SECTIONS) else: @@ -145,7 +136,6 @@ def __init__( if sec in self._parsed_data: self._parsed_data[sec] = self._normalize_param_section(self._parsed_data[sec]) - # --- continue normal fastcore behavior --- for section in SECTIONS: self[section] = dedent_lines(self[section], split=False) @@ -214,23 +204,11 @@ def _parse_param_list(self, content, single_element_is_type=False): return params def _normalize_param_section(self, val: list[Parameter] | Any) -> dict[Parameter] | Any: - """ - Convert lists of `Parameter` objects into a dict or clean list. - """ - # Not a list? Then noop. - if not isinstance(val, list): - return val - - # Falsy value i.e. empty list? Then noop. - if not val: - return val - - # Lazy check, assumes if first value is a Parameter, all are. - if not isinstance(val[0], Parameter): - return val - - # Convert to dict[name -> Parameter] - return {p.name: p for p in val} + """Convert lists of `Parameter` objects into a dict or clean list.""" + if not isinstance(val, list): return val # Not a list? Then noop. + if not val: return val # Falsy value i.e. empty list? Then noop. + if not isinstance(val[0], Parameter): return val # Lazy check, assumes if first value is a Parameter, all are. + return {p.name: p for p in val} # Convert to dict[name -> Parameter] def _parse_summary(self): """Grab signature (if given) and summary""" From e20bbe4d1f56688eb84bf40da8e9cbff0102ee22 Mon Sep 17 00:00:00 2001 From: s01st Date: Tue, 25 Nov 2025 08:26:03 -0600 Subject: [PATCH 5/7] remove type annotations --- fastcore/docscrape.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/fastcore/docscrape.py b/fastcore/docscrape.py index 4b29fe3f..f414db72 100644 --- a/fastcore/docscrape.py +++ b/fastcore/docscrape.py @@ -25,7 +25,6 @@ from warnings import warn from collections import namedtuple from collections.abc import Mapping -from typing import Any __all__ = ['Parameter', 'NumpyDocString', 'dedent_lines'] @@ -106,37 +105,32 @@ class NumpyDocString(Mapping): sections['Returns'] = [] param_sections: set[str] = set(PARAM_SECTIONS) - def __init__(self, docstring, config=None, supported_sections: list[str] | None = SECTIONS, supports_params: set[str] | None = PARAM_SECTIONS): + def __init__(self, docstring, config=None, supported_sections = SECTIONS, supports_params = PARAM_SECTIONS): if supports_params is None: supports_params = set(PARAM_SECTIONS) else: missing = set(supports_params) - set(self.param_sections) for sec in missing: self.param_sections.add(sec) - # If None, set to default supported set - if supported_sections is None: supported_sections = set(SECTIONS) + + if supported_sections is None: supported_sections = set(SECTIONS) # If None, set to default supported set else: - # add missing to class variable - missing = set(supported_sections) - set(self.sections.keys()) + missing = set(supported_sections) - set(self.sections.keys()) # add missing to class variable for sec in missing: self.sections[sec] = [] - # --- original initialization --- docstring = textwrap.dedent(docstring).split('\n') self._doc = Reader(docstring) self._parsed_data = copy.deepcopy(self.sections) self._parse() - # --- fastcore default normalization --- self['Parameters'] = {o.name:o for o in self['Parameters']} if self['Returns']: self['Returns'] = self['Returns'][0] - # --- normalize ALL parameter-like sections --- for sec in supports_params: if sec in self._parsed_data: self._parsed_data[sec] = self._normalize_param_section(self._parsed_data[sec]) - # --- continue normal fastcore behavior --- for section in SECTIONS: self[section] = dedent_lines(self[section], split=False) @@ -203,7 +197,7 @@ def _parse_param_list(self, content, single_element_is_type=False): params.append(Parameter(arg_name, arg_type, desc)) return params - def _normalize_param_section(self, val: list[Parameter] | Any) -> dict[Parameter] | Any: + def _normalize_param_section(self, val): """Convert lists of `Parameter` objects into a dict or clean list.""" if not isinstance(val, list): return val # Not a list? Then noop. if not val: return val # Falsy value i.e. empty list? Then noop. From 7afff83d6852413fb0164180f3a87ef058d8748d Mon Sep 17 00:00:00 2001 From: s01st Date: Tue, 25 Nov 2025 08:26:27 -0600 Subject: [PATCH 6/7] remove lingering comments --- fastcore/docscrape.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fastcore/docscrape.py b/fastcore/docscrape.py index f414db72..11898c26 100644 --- a/fastcore/docscrape.py +++ b/fastcore/docscrape.py @@ -113,9 +113,9 @@ def __init__(self, docstring, config=None, supported_sections = SECTIONS, suppor for sec in missing: self.param_sections.add(sec) - if supported_sections is None: supported_sections = set(SECTIONS) # If None, set to default supported set + if supported_sections is None: supported_sections = set(SECTIONS) else: - missing = set(supported_sections) - set(self.sections.keys()) # add missing to class variable + missing = set(supported_sections) - set(self.sections.keys()) for sec in missing: self.sections[sec] = [] @@ -199,10 +199,10 @@ def _parse_param_list(self, content, single_element_is_type=False): def _normalize_param_section(self, val): """Convert lists of `Parameter` objects into a dict or clean list.""" - if not isinstance(val, list): return val # Not a list? Then noop. - if not val: return val # Falsy value i.e. empty list? Then noop. - if not isinstance(val[0], Parameter): return val # Lazy check, assumes if first value is a Parameter, all are. - return {p.name: p for p in val} # Convert to dict[name -> Parameter] + if not isinstance(val, list): return val + if not val: return val + if not isinstance(val[0], Parameter): return val + return {p.name: p for p in val} def _parse_summary(self): """Grab signature (if given) and summary""" From 459f7dbccb1686e8d4ef96514850c780636cc2a3 Mon Sep 17 00:00:00 2001 From: s01st Date: Tue, 25 Nov 2025 08:26:55 -0600 Subject: [PATCH 7/7] Futher reduced normalize param section function --- fastcore/docscrape.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fastcore/docscrape.py b/fastcore/docscrape.py index 11898c26..57ee053c 100644 --- a/fastcore/docscrape.py +++ b/fastcore/docscrape.py @@ -199,8 +199,7 @@ def _parse_param_list(self, content, single_element_is_type=False): def _normalize_param_section(self, val): """Convert lists of `Parameter` objects into a dict or clean list.""" - if not isinstance(val, list): return val - if not val: return val + if not isinstance(val, list) or not val: return val if not isinstance(val[0], Parameter): return val return {p.name: p for p in val}