From e0b6798742af8f6914ad1d66f80dc29f8d637408 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 5 Aug 2026 14:43:41 +0200 Subject: [PATCH 1/2] Catch error of missing _version.py --- src/copapy/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/copapy/__init__.py b/src/copapy/__init__.py index 808dd4e..6a7791b 100644 --- a/src/copapy/__init__.py +++ b/src/copapy/__init__.py @@ -42,8 +42,15 @@ from ._math import sqrt, abs, sign, sin, cos, tan, asin, acos, atan, atan2, log, from ._nn import relu, sigmoid from ._autograd import grad from ._tensors import tensor as matrix -from ._version import __version__ # Run "pip install -e ." to generate _version.py +from typing import TYPE_CHECKING +if TYPE_CHECKING: + __version__: str +else: + try: + from ._version import __version__ + except ImportError: + __version__ = "0.0.0" # Run "pip install -e ." to generate _version.py __all__ = [ "__version__", From 70b916cc34b25f11f80ab3b3b4bc410b821e670e Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 5 Aug 2026 17:08:04 +0200 Subject: [PATCH 2/2] Copapy logo added to doc page and readme --- README.md | 4 +++ docs/source/conf.py | 8 ++++-- docs/source/extract_section.py | 41 ++++++++++++++++++++++------ docs/source/media/logo.svg | 49 ++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 docs/source/media/logo.svg diff --git a/README.md b/README.md index eae753b..84e9f03 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +
+Copapy logo +
+ # Copapy Copapy is a Python framework for deterministic, low-latency realtime computation with automatic differentiation support, targeting hardware applications - for example in the fields of robotics, aerospace, SDR, embedded systems and control systems in general. diff --git a/docs/source/conf.py b/docs/source/conf.py index c3defd1..ace4171 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,13 +25,17 @@ exclude_patterns = [] # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -# html_theme = 'alabaster' html_theme = 'pydata_sphinx_theme' html_static_path = ['_static'] +html_title = "Python framework for deterministic, low-latency realtime computation" +html_short_title = "Copapy" +html_logo = 'media/logo.svg' +html_favicon = 'media/logo.svg' html_css_files = ['custom.css'] html_theme_options = { "secondary_sidebar_items": ["page-toc"], - "footer_start": ["copyright"] + "footer_start": ["copyright"], + "logo": {"text": "Copapy"} } html_theme_options["footer_end"] = [] diff --git a/docs/source/extract_section.py b/docs/source/extract_section.py index 177a32c..6b85605 100644 --- a/docs/source/extract_section.py +++ b/docs/source/extract_section.py @@ -2,11 +2,32 @@ import re import argparse import os -def extract_sections(md_text: str) -> dict[str, str]: + +def strip_leading_html_tag(text: str) -> str: + """Remove a leading HTML element if the first non-space character is '<'.""" + trimmed = text.lstrip() + #if not trimmed.startswith('<'): + # return text + + match = re.match( + r'^\s*<\s*(?P[A-Za-z][A-Za-z0-9:-]*)(?:\s+[^<>]*)?>' + r'(?P.*?)(?:)?', + trimmed, + re.DOTALL, + ) + if not match: + return text + + return trimmed[match.end():] + + +def extract_sections(md_text: str) -> dict[str, tuple[str, str]]: """ Extracts sections based on headings (#...). Returns {heading_text: section_content} Works for simple Markdown, not fully strict. + + If strip_first_heading is True, omit the first heading/section from the output. """ # regex captures: heading marks (###...), heading text, and the following content @@ -17,13 +38,17 @@ def extract_sections(md_text: str) -> dict[str, str]: re.MULTILINE | re.DOTALL ) - sections: dict[str, str] = {} - for _, title, content in pattern.findall(md_text): + sections: dict[str, tuple[str, str]] = {} + matched = list(pattern.findall(md_text)) + + for prefix, title, content in matched: assert isinstance(content, str) - sections[title] = content.strip().replace('](docs/source/media/', '](media/') + content = strip_leading_html_tag(content) + sections[title] = (prefix + ' ' + title, content.strip().replace('](docs/source/media/', '](media/')) return sections + if __name__ == '__main__': parser = argparse.ArgumentParser(description='Extract sections from README.md and generate documentation files') parser.add_argument('--readme', type=str, default='README.md', help='README.md path') @@ -37,9 +62,9 @@ if __name__ == '__main__': readme = extract_sections(f.read()) with open(os.path.join(build_dir, 'start.md'), 'wt') as f: - f.write('\n'.join(f"{s}\n" + readme[s.strip(' #')] for s in [ - '# Copapy', '## Current state', '## Install', '## Examples', - '### Basic example', '### Inverse kinematics', '## License'])) + f.write('\n'.join('\n'.join(readme[s]) if s != 'Copapy' else readme[s][1] for s in [ + 'Copapy', 'Current state', 'Install', 'Examples', + 'Basic example', 'Inverse kinematics', 'License'])) with open(os.path.join(build_dir, 'compiler.md'), 'wt') as f: - f.write('\n'.join(readme[s] for s in ['How it works'])) + f.write('\n'.join(readme[s][1] for s in ['How it works'])) diff --git a/docs/source/media/logo.svg b/docs/source/media/logo.svg new file mode 100644 index 0000000..a4df3f8 --- /dev/null +++ b/docs/source/media/logo.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + +