diff --git a/python/.vscode/settings.json b/python/.vscode/settings.json index 23fd35f0e..ee32511e8 100644 --- a/python/.vscode/settings.json +++ b/python/.vscode/settings.json @@ -1,3 +1,7 @@ { - "editor.formatOnSave": true + "editor.formatOnSave": true, + "[python]": { + "editor.defaultFormatter": "ms-python.autopep8" + }, + "python.formatting.provider": "none" } \ No newline at end of file diff --git a/python/docs/_quarto.yml b/python/docs/_quarto.yml index 991d84b6d..e6487141b 100644 --- a/python/docs/_quarto.yml +++ b/python/docs/_quarto.yml @@ -61,8 +61,8 @@ format: - design-style.css include-in-header: _announcement.html toc: true - link-external-icon: true - link-external-newwindow: true + link-external-icon: false + link-external-newwindow: false link-external-filter: '^(?:http:|https:)\/\/kaskada\.io\/' filters: diff --git a/python/docs/_quartodoc.old.yml b/python/docs/_quartodoc.old.yml index a9454e285..9153ef351 100644 --- a/python/docs/_quartodoc.old.yml +++ b/python/docs/_quartodoc.old.yml @@ -16,7 +16,6 @@ quartodoc: sections: - title: Timestream - #flatten: true options: children: embedded package: null @@ -154,6 +153,7 @@ quartodoc: - Since - Sliding - Trailing + - Tumbling - title: Sources options: children: embedded diff --git a/python/docs/_renderer.py b/python/docs/_renderer.py index 149272b8d..c8f191676 100644 --- a/python/docs/_renderer.py +++ b/python/docs/_renderer.py @@ -89,9 +89,21 @@ def _fetch_object_dispname(self, el: "dc.Alias | dc.Object"): parts = el.path.split(".")[1:] name = parts.pop() prefix = ".".join(parts) + dispname = f"**{prefix}.**[**{name}**]{{.red}}" - return f"**{prefix}.**[**{name}**]{{.red}}" - # return ".".join(el.path.split(".")[1:]) + if isinstance(el, dc.Object): + print(f'Object: {el.labels}') + if 'staticmethod' in el.labels: + dispname = "***static*** " + dispname + print(dispname) + + text = [dispname] + print(dispname) + + # if isinstance(el, dc.Object) and el.kind == dc.Kind.CLASS: + # text.append(f"Bases: []({el.parent.name})") + + return "\n\n".join(text) def _fetch_method_parameters(self, el: dc.Function): # adapted from mkdocstrings-python jinja tempalate @@ -177,6 +189,7 @@ def signature( @dispatch def render_header(self, el: layout.Doc): + return self.header(el.name) # render method ----------------------------------------------------------- @@ -199,7 +212,7 @@ def header(self, title: str, order: Optional[str] = None) -> str: if order: text.append(f'order: {order}') text.append("---") - return "\n".join(text) + "\n\n" + return "\n".join(text) @dispatch def render(self, el: layout.Page): @@ -209,76 +222,16 @@ def render(self, el: layout.Page): else: header = [] - if el.flatten: - rows = list([[f'[{entry.name}](`{entry.anchor}`)', self.summarize(entry.obj)] - for entry in el.contents]) - table = self._render_table(rows, ["Method", "Description"]) - return "\n\n".join([*header, table]) - else: - result = map(self.render, el.contents) - return "\n\n".join([*header, *result]) - - @dispatch - def render(self, el: layout.Section): - section_top = f"{'#' * self.crnt_header_level} {el.title}\n\n{el.desc}" - - with self._increment_header(): - body = list(map(self.render, el.contents)) - - return "\n\n".join([section_top, *body]) - - @dispatch - def render(self, el: layout.Interlaced): - # render a sequence of objects with like-sections together. - # restrict its behavior to documenting functions for now ---- - for doc in el.contents: - if not isinstance(doc, (layout.DocFunction, layout.DocAttribute)): - raise NotImplementedError( - "Can only render Interlaced elements if all content elements" - " are function or attribute docs." - f" Found an element of type {type(doc)}, with name {doc.name}" - ) - - # render ---- - # currently, we use everything from the first function, and just render - # the signatures together - first_doc = el.contents[0] - objs = [doc.obj for doc in el.contents] - - if first_doc.obj.docstring is None: - raise ValueError("The first element of Interlaced must have a docstring.") - - str_title = self.render_header(first_doc) - str_sig = "\n\n".join(map(self.signature, objs)) - str_body = [] - - # TODO: we should also interlace parameters and examples - # parsed = map(qast.transform, [x.docstring.parsed for x in objs if x.docstring]) - - # TODO: this is copied from the render method for dc.Object - for section in qast.transform(first_doc.obj.docstring.parsed): - title = section.title or section.kind.value - body = self.render(section) - - if title != "text": - header = f"{'#' * (self.crnt_header_level + 1)} {title.title()}" - str_body.append("\n\n".join([header, body])) - else: - str_body.append(body) - - if self.show_signature: - parts = [str_title, str_sig, *str_body] - else: - parts = [str_title, *str_body] - - return "\n\n".join(parts) + result = map(self.render, el.contents) + return "\n\n".join([*header, *result]) @dispatch def render(self, el: layout.Doc): raise NotImplementedError(f"Unsupported Doc type: {type(el)}") @dispatch - def render(self, el: Union[layout.DocClass, layout.DocModule]): + def render(self, el: Union[layout.DocClass, layout.DocModule], single_page: bool = False): + print("boop") title = self.render_header(el) attr_docs = [] @@ -328,18 +281,19 @@ def render(self, el: Union[layout.DocClass, layout.DocModule]): # method summary table ---- if raw_meths: - _summary_table = "\n".join(map(self.summarize, raw_meths)) - section_name = ( - "Methods" if isinstance(el, layout.DocClass) else "Functions" - ) - objs = f"{sub_header} {section_name}\n\n{header}\n{_summary_table}" - meth_docs.append(objs) + # _summary_table = "\n".join(map(self.summarize, raw_meths)) + # section_name = ( + # "Methods" if isinstance(el, layout.DocClass) else "Functions" + # ) + # objs = f"{sub_header} {section_name}\n\n{header}\n{_summary_table}" + # meth_docs.append(objs) # TODO use context manager, or context variable? n_incr = 1 if el.flat else 2 with self._increment_header(n_incr): meth_docs.extend( - [self.render(x) for x in raw_meths if isinstance(x, layout.Doc)] + [self.render(x, single_page=True) + for x in raw_meths if isinstance(x, layout.Doc)] ) str_sig = self.signature(el) @@ -350,13 +304,14 @@ def render(self, el: Union[layout.DocClass, layout.DocModule]): return "\n\n".join([title, *sig_part, body, *attr_docs, *class_docs, *meth_docs]) @dispatch - def render(self, el: Union[layout.DocFunction, layout.DocAttribute]): - title = self.render_header(el) + def render(self, el: Union[layout.DocFunction, layout.DocAttribute], single_page: bool = False): + title = "" if single_page else self.render_header(el) - str_sig = self.signature(el) - sig_part = [str_sig] if self.show_signature else [] + sig = self.signature(el) if self.show_signature else "" + + body_rows = self.render(el.obj).split("\n") - return "\n\n".join([title, *sig_part, self.render(el.obj)]) + return "\n\n".join([title, sig, self.get_definition_list(body_rows)]) # render griffe objects =================================================== @@ -370,15 +325,7 @@ def render(self, el: Union[dc.Object, dc.Alias]): else: patched_sections = qast.transform(el.docstring.parsed) for section in patched_sections: - title = section.title or section.kind.value - body = self.render(section) - - if title != "text": - # header here is a definition list term - header = title.title() - str_body.append("\n\n".join([header, body])) - else: - str_body.append(body) + str_body.append(self.render(section)) parts = [*str_body] @@ -468,12 +415,19 @@ def render(self, el: ds.DocstringSectionText): # parameters ---- + def get_definition_list(self, items: [str]) -> str: + rows = [] + for item in items: + if len(rows) == 0: + rows.append(f'~ {item}') + else: + rows.append(f' {item}') + return "\n".join(rows) + @dispatch def render(self, el: ds.DocstringSectionParameters): - # render as a definition list item rows = list(map(self.render, el.value)) - text = "\n".join(rows) - return f': {text}' + return f'Parameters:\n{self.get_definition_list(rows)}' @dispatch def render(self, el: ds.DocstringParameter) -> str: @@ -548,11 +502,27 @@ def render(self, el: qast.ExampleText): def render(self, el: Union[ds.DocstringSectionReturns, ds.DocstringSectionRaises]): # render as a definition list rows = list(map(self.render, el.value)) - text = "\n".join(rows) - return f': {text}' + return "\n".join(rows) + + @dispatch + def render(self, el: ds.DocstringReturn): + # similar to DocstringParameter, but no name or default + text = [] + + returns = sanitize(el.description, allow_markdown=True) + if returns: + text.append('Returns:') + text.append(f'~ {returns}') + + return_type = self.render_annotation(el.annotation) + if return_type: + text.append('Return type:') + text.append(f'~ {return_type}') + + return "\n\n".join(text) @dispatch - def render(self, el: Union[ds.DocstringReturn, ds.DocstringRaise]): + def render(self, el: ds.DocstringRaise): # similar to DocstringParameter, but no name or default annotation = self.render_annotation(el.annotation) return f'{annotation} -- {sanitize(el.description, allow_markdown=True)}' diff --git a/python/docs/autosummary.py b/python/docs/autosummary.py index 021c317a4..6b387f793 100644 --- a/python/docs/autosummary.py +++ b/python/docs/autosummary.py @@ -275,6 +275,7 @@ def write_indexes(self, blueprint: layout.Layout): # --- root index text = self.header(self.title) + text += "This is the API Reference" p_index = Path(self.dir) / self.out_index p_index.parent.mkdir(exist_ok=True, parents=True) diff --git a/python/docs/blog/_metadata.yml b/python/docs/blog/_metadata.yml new file mode 100644 index 000000000..954e649d0 --- /dev/null +++ b/python/docs/blog/_metadata.yml @@ -0,0 +1,4 @@ +format: + html: + link-external-icon: true + link-external-newwindow: true diff --git a/python/docs/examples/_metadata.yml b/python/docs/examples/_metadata.yml index 05585a226..dc16c1f14 100644 --- a/python/docs/examples/_metadata.yml +++ b/python/docs/examples/_metadata.yml @@ -1,2 +1,7 @@ filters: - include-code-files + +format: + html: + link-external-icon: true + link-external-newwindow: true diff --git a/python/docs/styles.css b/python/docs/styles.css index ab5f64237..2c7e74f3e 100644 --- a/python/docs/styles.css +++ b/python/docs/styles.css @@ -64,4 +64,19 @@ dd { .red { color: #e83e8c +} + +dl dl dt { + padding-bottom: 4px; + padding-left: 8px; + padding-right: 8px; + padding-top: 2px; +} + +body.quarto-light dl dl dt { + background-color: #f5f5f5; +} + +body.quarto-dark dl dl dt { + background-color: #2f2f2f; } \ No newline at end of file diff --git a/python/pysrc/kaskada/_timestream.py b/python/pysrc/kaskada/_timestream.py index 47e7b6d83..1d32f7bf3 100644 --- a/python/pysrc/kaskada/_timestream.py +++ b/python/pysrc/kaskada/_timestream.py @@ -1174,7 +1174,7 @@ def run_iter( mode: The execution mode to use. Defaults to `'once'` to produce the results from the currently available data. Use `'live'` to start a standing query that continues to process new data until stopped. - results: The results to produce. Defaults to `Histroy()` producing all points. + results: The results to produce. Defaults to `History()` producing all points. row_limit: The maximum number of rows to return. Defaults to `None` for no limit. max_batch_size: The maximum number of rows to return in each batch. Defaults to `None` for no limit. diff --git a/python/pysrc/kaskada/windows.py b/python/pysrc/kaskada/windows.py index 5edaede3e..018ec595e 100644 --- a/python/pysrc/kaskada/windows.py +++ b/python/pysrc/kaskada/windows.py @@ -24,9 +24,6 @@ class Since(Window): Args: predicate: the condition used to determine when the window resets. - - Returns: - Window for aggregating cumulative values since the predicate. """ #: The boolean Timestream to use as predicate for the window. @@ -38,27 +35,47 @@ class Since(Window): @staticmethod def minutely() -> Since: - """Return a window since the start of each calendar minute.""" + """Return a window since the start of each calendar minute. + + Returns: + Window for aggregating cumulative values since the predicate. + """ return Since(predicate=lambda domain: Timestream._call("minutely", domain)) @staticmethod def hourly() -> Since: - """Return a window since the start of each calendar hour.""" + """Return a window since the start of each calendar hour. + + Returns: + Window for aggregating cumulative values since the predicate. + """ return Since(predicate=lambda domain: Timestream._call("hourly", domain)) @staticmethod def daily() -> Since: - """Return a window since the start of each calendar day.""" + """Return a window since the start of each calendar day. + + Returns: + Window for aggregating cumulative values since the predicate. + """ return Since(predicate=lambda domain: Timestream._call("daily", domain)) @staticmethod def monthly() -> Since: - """Return a window since the start of each calendar month.""" + """Return a window since the start of each calendar month. + + Returns: + Window for aggregating cumulative values since the predicate. + """ return Since(predicate=lambda domain: Timestream._call("monthly", domain)) @staticmethod def yearly() -> Since: - """Return a window since the start of each calendar year.""" + """Return a window since the start of each calendar year. + + Returns: + Window for aggregating cumulative values since the predicate. + """ return Since(predicate=lambda domain: Timestream._call("yearly", domain)) @@ -73,10 +90,7 @@ class Tumbling(Window): Args: predicate: the condition used to determine when the window resets. - Returns: - Window for aggregating values since the predicate. - - Note: + Notes: Like other systems, Kaskada treats tumbling windows as non-overlapping. When one window ends the next starts. @@ -94,27 +108,47 @@ class Tumbling(Window): @staticmethod def minutely() -> Tumbling: - """Return a tumbling window that resets at the start of each calendar minute.""" + """Return a tumbling window that resets at the start of each calendar minute. + + Returns: + Window for aggregating values since the predicate. + """ return Tumbling(predicate=lambda domain: Timestream._call("minutely", domain)) @staticmethod def hourly() -> Tumbling: - """Return a tumbling window that resets at the start of each calendar hour.""" + """Return a tumbling window that resets at the start of each calendar hour. + + Returns: + Window for aggregating values since the predicate. + """ return Tumbling(predicate=lambda domain: Timestream._call("hourly", domain)) @staticmethod def daily() -> Tumbling: - """Return a tumbling window that resets at the start of each calendar day.""" + """Return a tumbling window that resets at the start of each calendar day. + + Returns: + Window for aggregating values since the predicate. + """ return Tumbling(predicate=lambda domain: Timestream._call("daily", domain)) @staticmethod def monthly() -> Tumbling: - """Return a tumbling window that resets at the start of each calendar month.""" + """Return a tumbling window that resets at the start of each calendar month. + + Returns: + Window for aggregating values since the predicate. + """ return Tumbling(predicate=lambda domain: Timestream._call("monthly", domain)) @staticmethod def yearly() -> Tumbling: - """Return a tumbling window that resets at the start of each calendar year.""" + """Return a tumbling window that resets at the start of each calendar year. + + Returns: + Window for aggregating values since the predicate. + """ return Tumbling(predicate=lambda domain: Timestream._call("yearly", domain)) @@ -130,9 +164,6 @@ class Sliding(Window): duration: the number of active windows at any given time. predicate: the condition used to determine when the oldest window ends and a new window starts. - - Returns: - Overlapping windows for aggregating values. """ #: The number of sliding intervals to use in the window. @@ -155,6 +186,9 @@ def minutely(duration: int) -> Sliding: Args: duration: The number of minutes to use in the window. + + Returns: + Overlapping windows for aggregating values. """ return Sliding( duration=duration, @@ -167,6 +201,9 @@ def hourly(duration: int) -> Sliding: Args: duration: The number of hours to use in the window. + + Returns: + Overlapping windows for aggregating values. """ return Sliding( duration=duration, @@ -179,6 +216,9 @@ def daily(duration: int) -> Sliding: Args: duration: The number of days to use in the window. + + Returns: + Overlapping windows for aggregating values. """ return Sliding( duration=duration, @@ -191,6 +231,9 @@ def monthly(duration: int) -> Sliding: Args: duration: The number of months to use in the window. + + Returns: + Overlapping windows for aggregating values. """ return Sliding( duration=duration, @@ -203,6 +246,9 @@ def yearly(duration: int) -> Sliding: Args: duration: The number of years to use in the window. + + Returns: + Overlapping windows for aggregating values. """ return Sliding( duration=duration,