pyOpenSourceProjects 0.1.2__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/.github/workflows/build.yml +2 -4
  2. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/PKG-INFO +4 -2
  3. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/README.md +2 -1
  4. pyopensourceprojects-0.2.1/mkdocs.yml +18 -0
  5. pyopensourceprojects-0.2.1/osprojects/__init__.py +1 -0
  6. pyopensourceprojects-0.2.1/osprojects/checkos.py +320 -0
  7. pyopensourceprojects-0.2.1/osprojects/editor.py +132 -0
  8. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/osprojects/osproject.py +175 -45
  9. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/pyproject.toml +9 -4
  10. pyopensourceprojects-0.2.1/scripts/doc +88 -0
  11. pyopensourceprojects-0.2.1/scripts/release +8 -0
  12. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/tests/basetest.py +1 -0
  13. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/tests/test_osproject.py +72 -1
  14. pyopensourceprojects-0.1.2/osprojects/__init__.py +0 -1
  15. pyopensourceprojects-0.1.2/scripts/doc +0 -88
  16. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/.github/workflows/upload-to-pypi.yml +0 -0
  17. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/.gitignore +0 -0
  18. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/.project +0 -0
  19. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/.pydevproject +0 -0
  20. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/LICENSE +0 -0
  21. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/scripts/blackisort +0 -0
  22. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/scripts/install +0 -0
  23. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/scripts/installAndTest +0 -0
  24. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/scripts/test +0 -0
  25. {pyopensourceprojects-0.1.2 → pyopensourceprojects-0.2.1}/tests/__init__.py +0 -0
@@ -20,7 +20,7 @@ jobs:
20
20
  matrix:
21
21
  os: [ubuntu-latest, macos-latest, windows-latest]
22
22
  #os: [ubuntu-latest]
23
- python-version: [3.9, "3.10", "3.11", "3.12"]
23
+ python-version: [ 3.9, '3.10', '3.11', '3.12' ]
24
24
  #python-version: ["3.10"]
25
25
 
26
26
  steps:
@@ -32,10 +32,8 @@ jobs:
32
32
  - name: Install dependencies
33
33
  run: |
34
34
  python -m pip install --upgrade pip
35
- pip install sphinx
36
- pip install sphinx_rtd_theme
37
35
  scripts/install
38
36
  scripts/doc
39
37
  - name: Run tests
40
38
  run: |
41
- scripts/installAndTest
39
+ scripts/test
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyOpenSourceProjects
3
- Version: 0.1.2
3
+ Version: 0.2.1
4
4
  Dynamic: Summary
5
5
  Project-URL: Home, https://github.com/WolfgangFahl/pyOpenSourceProjects
6
6
  Project-URL: Documentation, http://wiki.bitplan.com/index.php/pyOpenSourceProjects
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.10
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Requires-Python: >=3.9
18
+ Requires-Dist: packaging>=24.1
18
19
  Requires-Dist: py-3rdparty-mediawiki>=0.11.3
19
20
  Requires-Dist: pylodstorage>=0.11.6
20
21
  Requires-Dist: python-dateutil>=2.8.2
@@ -25,10 +26,11 @@ Description-Content-Type: text/markdown
25
26
  Helper Library to organize open source Projects
26
27
 
27
28
  [![pypi](https://img.shields.io/pypi/pyversions/pyOpenSourceProjects)](https://pypi.org/project/pyOpenSourceProjects/)
28
- [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/workflows/Build/badge.svg?branch=main)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions?query=workflow%3ABuild+branch%3Amain)
29
+ [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml)
29
30
  [![PyPI Status](https://img.shields.io/pypi/v/pyOpenSourceProjects.svg)](https://pypi.python.org/pypi/pyOpenSourceProjects/)
30
31
  [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues)
31
32
  [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues/?q=is%3Aissue+is%3Aclosed)
33
+ [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyOpenSourceProjects/)
32
34
  [![License](https://img.shields.io/github/license/WolfgangFahl/pyOpenSourceProjects.svg)](https://www.apache.org/licenses/LICENSE-2.0)
33
35
 
34
36
 
@@ -2,10 +2,11 @@
2
2
  Helper Library to organize open source Projects
3
3
 
4
4
  [![pypi](https://img.shields.io/pypi/pyversions/pyOpenSourceProjects)](https://pypi.org/project/pyOpenSourceProjects/)
5
- [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/workflows/Build/badge.svg?branch=main)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions?query=workflow%3ABuild+branch%3Amain)
5
+ [![Github Actions Build](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml/badge.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/actions/workflows/build.yml)
6
6
  [![PyPI Status](https://img.shields.io/pypi/v/pyOpenSourceProjects.svg)](https://pypi.python.org/pypi/pyOpenSourceProjects/)
7
7
  [![GitHub issues](https://img.shields.io/github/issues/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues)
8
8
  [![GitHub closed issues](https://img.shields.io/github/issues-closed/WolfgangFahl/pyOpenSourceProjects.svg)](https://github.com/WolfgangFahl/pyOpenSourceProjects/issues/?q=is%3Aissue+is%3Aclosed)
9
+ [![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://WolfgangFahl.github.io/pyOpenSourceProjects/)
9
10
  [![License](https://img.shields.io/github/license/WolfgangFahl/pyOpenSourceProjects.svg)](https://www.apache.org/licenses/LICENSE-2.0)
10
11
 
11
12
 
@@ -0,0 +1,18 @@
1
+ site_name: pyOpenSourceProjects API Documentation
2
+ theme:
3
+ name: material
4
+ plugins:
5
+ - search
6
+ - mkdocstrings:
7
+ handlers:
8
+ python:
9
+ setup_commands:
10
+ - import sys
11
+ - import os
12
+ - sys.path.insert(0, os.path.abspath("."))
13
+ selection:
14
+ docstring_style: google
15
+ rendering:
16
+ show_source: true
17
+ nav:
18
+ - API: index.md
@@ -0,0 +1 @@
1
+ __version__ = "0.2.1"
@@ -0,0 +1,320 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Created on 2024-07-30
4
+
5
+ @author: wf
6
+ """
7
+ import argparse
8
+ import os
9
+ from argparse import Namespace
10
+ from dataclasses import dataclass
11
+ from typing import List
12
+ # original at ngwidgets - use redundant local copy ...
13
+ from osprojects.editor import Editor
14
+ from osprojects.osproject import GitHub, OsProject
15
+ import tomllib
16
+ import traceback
17
+ from packaging import version
18
+
19
+ @dataclass
20
+ class Check:
21
+ ok: bool = False
22
+ path: str = None
23
+ msg: str = ""
24
+ content: str = None
25
+
26
+ @property
27
+ def marker(self) -> str:
28
+ return f"✅" if self.ok else f"❌"
29
+
30
+ @classmethod
31
+ def file_exists(cls, path) -> "Check":
32
+ ok = os.path.exists(path)
33
+ content = None
34
+ if ok and os.path.isfile(path):
35
+ with open(path, "r") as f:
36
+ content = f.read()
37
+ check = Check(ok, path, msg=path, content=content)
38
+ return check
39
+
40
+ class CheckOS:
41
+ """
42
+ check the open source projects
43
+ """
44
+
45
+ def __init__(self, args: Namespace, project: OsProject):
46
+ self.args = args
47
+ self.verbose = args.verbose
48
+ self.workspace = args.workspace
49
+ self.project = project
50
+ self.project_path = os.path.join(self.workspace, project.id)
51
+ self.checks = []
52
+
53
+ @property
54
+ def total(self) -> int:
55
+ return len(self.checks)
56
+
57
+ @property
58
+ def ok_checks(self) -> List[Check]:
59
+ ok_checks = [check for check in self.checks if check.ok]
60
+ return ok_checks
61
+
62
+ @property
63
+ def failed_checks(self) -> List[Check]:
64
+ failed_checks = [check for check in self.checks if not check.ok]
65
+ return failed_checks
66
+
67
+ def add_check(self, ok, msg:str="",path: str=None,negative:bool=False) -> Check:
68
+ if not path:
69
+ raise ValueError("path parameter missing")
70
+ marker=""
71
+ if negative:
72
+ ok=not ok
73
+ marker="⚠ ️"
74
+ check = Check(ok=ok, path=path, msg=f"{marker}{msg}{path}")
75
+ self.checks.append(check)
76
+ return check
77
+
78
+ def add_content_check(self, content: str, needle: str, path: str, negative:bool=False) -> Check:
79
+ ok=needle in content
80
+ check=self.add_check(ok, msg=f"{needle} in ", path=path,negative=negative)
81
+ return check
82
+
83
+ def add_path_check(self, path) -> Check:
84
+ # Check if path exists
85
+ path_exists = Check.file_exists(path)
86
+ self.checks.append(path_exists)
87
+ return path_exists
88
+
89
+ def check_local(self) -> Check:
90
+ local = Check.file_exists(self.project_path)
91
+ return local
92
+
93
+ def check_github_workflows(self):
94
+ workflows_path = os.path.join(self.project_path, ".github", "workflows")
95
+ workflows_exist = self.add_path_check(workflows_path)
96
+
97
+ if workflows_exist.ok:
98
+ required_files = ["build.yml", "upload-to-pypi.yml"]
99
+ for file in required_files:
100
+ file_path = os.path.join(workflows_path, file)
101
+ file_exists = self.add_path_check(file_path)
102
+
103
+ if file_exists.ok:
104
+ content = file_exists.content
105
+
106
+ if file == "build.yml":
107
+ min_version = int(self.requires_python.split('.')[-1])
108
+ python_versions = f"""python-version: [ {', '.join([f"'3.{i}'" for i in range(min_version, 13)])} ]"""
109
+ self.add_content_check(
110
+ content,
111
+ python_versions,
112
+ file_path,
113
+ )
114
+ self.add_content_check(
115
+ content,
116
+ "os: [ubuntu-latest, macos-latest, windows-latest]",
117
+ file_path,
118
+ )
119
+ self.add_content_check(content, "uses: actions/checkout@v4", file_path)
120
+ self.add_content_check(
121
+ content,
122
+ "uses: actions/setup-python@v5",
123
+ file_path,
124
+ )
125
+
126
+ self.add_content_check(
127
+ content,
128
+ "sphinx",
129
+ file_path,
130
+ negative=True
131
+ )
132
+ scripts_ok="scripts/install" in content and "scripts/test" in content or "scripts/installAndTest" in content
133
+ self.add_check(scripts_ok,"install and test", file_path)
134
+
135
+ elif file == "upload-to-pypi.yml":
136
+ self.add_content_check(content, "id-token: write", file_path)
137
+ self.add_content_check(content, "uses: actions/checkout@v4", file_path)
138
+ self.add_content_check(
139
+ content,
140
+ "uses: actions/setup-python@v5",
141
+ file_path,
142
+ )
143
+ self.add_content_check(
144
+ content,
145
+ "uses: pypa/gh-action-pypi-publish@release/v1",
146
+ file_path,
147
+ )
148
+
149
+ def check_scripts(self):
150
+ scripts_path = os.path.join(self.project_path, "scripts")
151
+ scripts_exist = self.add_path_check(scripts_path)
152
+ if scripts_exist.ok:
153
+ required_files = ["blackisort", "test", "install", "doc", "release"]
154
+ for file in required_files:
155
+ file_path = os.path.join(scripts_path, file)
156
+ file_exists = self.add_path_check(file_path)
157
+ if file_exists.ok:
158
+ content = file_exists.content
159
+ if file=="doc":
160
+ self.add_content_check(content, "sphinx", file_path, negative=True)
161
+ self.add_content_check(content,"WF 2024-07-30 - updated",file_path)
162
+ if file=="release":
163
+ self.add_content_check(content, "scripts/docs -d", file_path, negative=True)
164
+
165
+ def check_readme(self):
166
+ readme_path = os.path.join(self.project_path, "README.md")
167
+ readme_exists = self.add_path_check(readme_path)
168
+ if readme_exists.ok:
169
+ readme_content = readme_exists.content
170
+ badge_lines = [
171
+ "[![pypi](https://img.shields.io/pypi/pyversions/{self.project_name})](https://pypi.org/project/{self.project_name}/)",
172
+ "[![Github Actions Build](https://github.com/{self.project.fqid}/actions/workflows/build.yml/badge.svg)](https://github.com/{self.project.fqid}/actions/workflows/build.yml)",
173
+ "[![PyPI Status](https://img.shields.io/pypi/v/{self.project_name}.svg)](https://pypi.python.org/pypi/{self.project_name}/)",
174
+ "[![GitHub issues](https://img.shields.io/github/issues/{self.project.fqid}.svg)](https://github.com/{self.project.fqid}/issues)",
175
+ "[![GitHub closed issues](https://img.shields.io/github/issues-closed/{self.project.fqid}.svg)](https://github.com/{self.project.fqid}/issues/?q=is%3Aissue+is%3Aclosed)",
176
+ "[![API Docs](https://img.shields.io/badge/API-Documentation-blue)](https://{self.project.owner}.github.io/{self.project.id}/)",
177
+ "[![License](https://img.shields.io/github/license/{self.project.fqid}.svg)](https://www.apache.org/licenses/LICENSE-2.0)",
178
+ ]
179
+ for line in badge_lines:
180
+ formatted_line = line.format(self=self)
181
+ self.add_content_check(
182
+ content=readme_content,
183
+ needle=formatted_line,
184
+ path=readme_path,
185
+ )
186
+ self.add_content_check(readme_content, "readthedocs", readme_path, negative=True)
187
+
188
+ def check_pyproject_toml(self):
189
+ toml_path = os.path.join(self.project_path, "pyproject.toml")
190
+ toml_exists = self.add_path_check(toml_path)
191
+ if toml_exists.ok:
192
+ content=toml_exists.content
193
+ toml_dict = tomllib.loads(content)
194
+ project_check=self.add_check("project" in toml_dict, "[project]", toml_path)
195
+ if project_check.ok:
196
+ self.project_name=toml_dict["project"]["name"]
197
+ requires_python_check=self.add_check("requires-python" in toml_dict["project"], "requires-python", toml_path)
198
+ if requires_python_check.ok:
199
+ self.requires_python = toml_dict["project"]["requires-python"]
200
+ self.min_python_version = version.parse(self.requires_python.replace(">=", ""))
201
+ min_version_needed="3.9"
202
+ version_ok=self.min_python_version >= version.parse(min_version_needed)
203
+ self.add_check(version_ok, f"requires-python>={min_version_needed}", toml_path)
204
+ self.add_content_check(content, "hatchling", toml_path)
205
+ self.add_content_check(content,"[tool.hatch.build.targets.wheel.sources]",toml_path)
206
+
207
+ def check(self):
208
+ """
209
+ Check the given project and print results
210
+ """
211
+ self.check_local()
212
+ self.check_pyproject_toml()
213
+ self.check_readme()
214
+ self.check_scripts()
215
+ self.check_github_workflows()
216
+
217
+ # ok_count=len(ok_checks)
218
+ failed_count = len(self.failed_checks)
219
+ summary = f"❌ {failed_count}/{self.total}" if failed_count > 0 else f"✅ {self.total}/{self.total}"
220
+ print(f"{self.project} {summary}: {self.project.url}")
221
+ if failed_count > 0:
222
+ # Sort checks by path
223
+ sorted_checks = sorted(self.checks, key=lambda c: c.path or "")
224
+
225
+ # Group checks by path
226
+ checks_by_path = {}
227
+ for check in sorted_checks:
228
+ if check.path not in checks_by_path:
229
+ checks_by_path[check.path] = []
230
+ checks_by_path[check.path].append(check)
231
+
232
+ # Display results
233
+ for path, path_checks in checks_by_path.items():
234
+ path_failed = sum(1 for c in path_checks if not c.ok)
235
+ if path_failed > 0 or self.args.debug:
236
+ print(f"❌ {path}: {path_failed}")
237
+ i=0
238
+ for check in path_checks:
239
+ show=not check.ok or self.args.debug
240
+ if show:
241
+ i+=1
242
+ print(f" {i:3}{check.marker}:{check.msg}")
243
+
244
+ if self.args.editor and path_failed > 0:
245
+ if os.path.isfile(path):
246
+ # @TODO Make editor configurable
247
+ Editor.open(path,default_editor_cmd="/usr/local/bin/atom")
248
+ else:
249
+ Editor.open_filepath(path)
250
+
251
+
252
+ def main(_argv=None):
253
+ """
254
+ main command line entry point
255
+ """
256
+ parser = argparse.ArgumentParser(description="Check open source projects")
257
+ parser.add_argument(
258
+ "-d",
259
+ "--debug",
260
+ action="store_true",
261
+ help="add debug output",
262
+ )
263
+ parser.add_argument(
264
+ "-e",
265
+ "--editor",
266
+ action="store_true",
267
+ help="open default editor on failed files",
268
+ )
269
+ parser.add_argument(
270
+ "-o", "--owner", help="project owner or organization", required=True
271
+ )
272
+ parser.add_argument("-p", "--project", help="name of the project")
273
+ parser.add_argument("-l", "--language", help="filter projects by language")
274
+ parser.add_argument(
275
+ "--local", action="store_true", help="check only locally available projects"
276
+ )
277
+ parser.add_argument(
278
+ "-v", "--verbose", action="store_true", help="show verbose output"
279
+ )
280
+ parser.add_argument(
281
+ "-ws",
282
+ "--workspace",
283
+ help="(Eclipse) workspace directory",
284
+ default=os.path.expanduser("~/py-workspace"),
285
+ )
286
+
287
+ args = parser.parse_args(args=_argv)
288
+
289
+ try:
290
+ github = GitHub()
291
+ if args.project:
292
+ # Check specific project
293
+ projects = github.list_projects_as_os_projects(
294
+ args.owner, project_name=args.project
295
+ )
296
+ else:
297
+ # Check all projects
298
+ projects = github.list_projects_as_os_projects(args.owner)
299
+
300
+ if args.language:
301
+ projects = [p for p in projects if p.language == args.language]
302
+
303
+ if args.local:
304
+ local_projects = []
305
+ for project in projects:
306
+ checker = CheckOS(args=args, project=project)
307
+ if checker.check_local().ok:
308
+ local_projects.append(project)
309
+ projects = local_projects
310
+
311
+ for project in projects:
312
+ checker = CheckOS(args=args, project=project)
313
+ checker.check()
314
+ except Exception as ex:
315
+ if args.debug:
316
+ print(traceback.format_exc())
317
+ raise ex
318
+
319
+ if __name__ == "__main__":
320
+ main()
@@ -0,0 +1,132 @@
1
+ """
2
+ Created on 2022-11-27
3
+
4
+ @author: wf
5
+ """
6
+
7
+ import os
8
+ import platform
9
+ import subprocess
10
+ import tempfile
11
+ from pathlib import Path
12
+ from urllib.request import urlopen
13
+
14
+ from bs4 import BeautifulSoup
15
+
16
+
17
+ class Editor:
18
+ """
19
+ helper class to open the system defined editor
20
+
21
+ see https://stackoverflow.com/questions/1442841/lauch-default-editor-like-webbrowser-module
22
+ """
23
+
24
+ @classmethod
25
+ def open_filepath(cls, filepath: str):
26
+ if platform.system() == "Darwin": # macOS
27
+ subprocess.call(("open", filepath))
28
+ elif platform.system() == "Windows": # Windows
29
+ os.startfile(filepath, "open")
30
+ else: # linux variants
31
+ subprocess.call(("xdg-open", filepath))
32
+
33
+ @classmethod
34
+ def extract_text(cls, html_text: str) -> str:
35
+ """
36
+ extract the text from the given html_text
37
+
38
+ Args:
39
+ html_text(str): the input for the html text
40
+
41
+ Returns:
42
+ str: the plain text
43
+ """
44
+ soup = BeautifulSoup(html_text, features="html.parser")
45
+
46
+ # kill all script and style elements
47
+ for script in soup(["script", "style"]):
48
+ script.extract() # rip it out
49
+
50
+ # get text
51
+ text = soup.get_text()
52
+
53
+ # break into lines and remove leading and trailing space on each
54
+ lines = (line.strip() for line in text.splitlines())
55
+ # break multi-headlines into a line each
56
+ chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
57
+ # drop blank lines
58
+ text = "\n".join(chunk for chunk in chunks if chunk)
59
+ return text
60
+
61
+ @classmethod
62
+ def open(
63
+ cls,
64
+ file_source: str,
65
+ extract_text: bool = True,
66
+ default_editor_cmd: str = "/usr/local/bin/atom",
67
+ ) -> str:
68
+ """
69
+ open an editor for the given file_source
70
+
71
+ Args:
72
+ file_source(str): the path to the file
73
+ extract_text(bool): if True extract the text from html sources
74
+
75
+ Returns:
76
+ str: the path to the file e.g. a temporary file if the file_source points to an url
77
+ """
78
+ # handle urls
79
+ # https://stackoverflow.com/a/45886824/1497139
80
+ if file_source.startswith("http"):
81
+ url_source = urlopen(file_source)
82
+ # https://stackoverflow.com/a/19156107/1497139
83
+ charset = url_source.headers.get_content_charset()
84
+ # if charset fails here you might want to set it to utf-8 as a default!
85
+ text = url_source.read().decode(charset)
86
+ if extract_text:
87
+ # https://stackoverflow.com/a/24618186/1497139
88
+ text = cls.extract_text(text)
89
+
90
+ return cls.open_tmp_text(text)
91
+
92
+ editor_cmd = None
93
+ editor_env = os.getenv("EDITOR")
94
+ if editor_env:
95
+ editor_cmd = editor_env
96
+ if platform.system() == "Darwin":
97
+ if not editor_env:
98
+ # https://stackoverflow.com/questions/22390709/how-can-i-open-the-atom-editor-from-the-command-line-in-os-x
99
+ editor_cmd = default_editor_cmd
100
+ if editor_cmd:
101
+ os_cmd = f"{editor_cmd} {file_source}"
102
+ os.system(os_cmd)
103
+ return file_source
104
+
105
+ @classmethod
106
+ def open_tmp_text(cls, text: str, file_name: str = None) -> str:
107
+ """
108
+ open an editor for the given text in a newly created temporary file
109
+
110
+ Args:
111
+ text(str): the text to write to a temporary file and then open
112
+ file_name(str): the name to use for the file
113
+
114
+ Returns:
115
+ str: the path to the temp file
116
+ """
117
+ # see https://stackoverflow.com/a/8577226/1497139
118
+ # https://stackoverflow.com/a/3924253/1497139
119
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
120
+ with open(tmp.name, "w") as tmp_file:
121
+ tmp_file.write(text)
122
+ tmp_file.close()
123
+ if file_name is None:
124
+ file_path = tmp.name
125
+ else:
126
+ # https://stackoverflow.com/questions/3167154/how-to-split-a-dos-path-into-its-components-in-python
127
+ path = Path(tmp.name)
128
+ # https://stackoverflow.com/a/49798311/1497139
129
+ file_path = path.parent / file_name
130
+ os.rename(tmp.name, file_path)
131
+
132
+ return cls.open(str(file_path))
@@ -3,15 +3,17 @@ Created on 2022-01-24
3
3
 
4
4
  @author: wf
5
5
  """
6
+
6
7
  from __future__ import annotations
7
- import os
8
+
8
9
  import argparse
9
10
  import datetime
10
11
  import json
12
+ import os
11
13
  import re
12
14
  import subprocess
13
15
  import sys
14
- from typing import List, Type
16
+ from typing import List, Optional, Type
15
17
 
16
18
  import requests
17
19
  from dateutil.parser import parse
@@ -55,23 +57,26 @@ class GitHub(TicketSystem):
55
57
  """
56
58
  wrapper for the GitHub api
57
59
  """
60
+
58
61
  @classmethod
59
- def load_access_token(cls)->str:
62
+ def load_access_token(cls) -> str:
60
63
  """
61
64
  if $HOME/.github/access_token.json exists read the token from there
62
65
  """
63
66
  # Specify the path to the access token file
64
- token_file_path = os.path.join(os.getenv('HOME'), '.github', 'access_token.json')
65
-
67
+ token_file_path = os.path.join(
68
+ os.getenv("HOME"), ".github", "access_token.json"
69
+ )
70
+
66
71
  # Check if the file exists and read the token
67
72
  if os.path.exists(token_file_path):
68
- with open(token_file_path, 'r') as token_file:
73
+ with open(token_file_path, "r") as token_file:
69
74
  token_data = json.load(token_file)
70
- return token_data.get('access_token')
71
-
75
+ return token_data.get("access_token")
76
+
72
77
  # Return None if no token file is found
73
78
  return None
74
-
79
+
75
80
  @classmethod
76
81
  def prepare_headers(cls, access_token: str = None) -> dict:
77
82
  """
@@ -80,15 +85,108 @@ class GitHub(TicketSystem):
80
85
  if access_token is None:
81
86
  access_token = cls.load_access_token()
82
87
 
83
- headers = {'Authorization': f'token {access_token}'} if access_token else {}
88
+ headers = {"Authorization": f"token {access_token}"} if access_token else {}
84
89
  return headers
85
90
 
86
91
  @classmethod
87
- def getIssues(cls,
88
- project: OsProject,
89
- access_token:str=None,
90
- limit: int = None,
91
- **params) -> List[Ticket]:
92
+ def list_projects_as_os_projects(
93
+ cls, owner: str, access_token: str = None, project_name: Optional[str] = None
94
+ ) -> List[OsProject]:
95
+ """
96
+ List all public repositories for a given owner and return them as OsProject instances.
97
+
98
+ Args:
99
+ owner (str): The GitHub username or organization name.
100
+ access_token (str, optional): GitHub personal access token for authentication.
101
+ project_name (str, optional): If provided, return only this specific project.
102
+
103
+ Returns:
104
+ List[OsProject]: A list of OsProject instances representing the repositories.
105
+ """
106
+ headers = cls.prepare_headers(access_token)
107
+
108
+ if project_name:
109
+ url = f"https://api.github.com/repos/{owner}/{project_name}"
110
+ response = requests.get(url, headers=headers)
111
+ if response.status_code != 200:
112
+ raise Exception(
113
+ f"Failed to fetch repository: {response.status_code} - {response.text}"
114
+ )
115
+ repos = [response.json()]
116
+ else:
117
+ url = f"https://api.github.com/users/{owner}/repos"
118
+ params = {
119
+ "type": "all",
120
+ "per_page": 100,
121
+ } # Include all repo types, 100 per page
122
+ all_repos = []
123
+ page = 1
124
+
125
+ while True:
126
+ params["page"] = page
127
+ response = requests.get(url, headers=headers, params=params)
128
+
129
+ if response.status_code != 200:
130
+ raise Exception(
131
+ f"Failed to fetch repositories: {response.status_code} - {response.text}"
132
+ )
133
+
134
+ repos = response.json()
135
+ if not repos:
136
+ break # No more repositories to fetch
137
+
138
+ all_repos.extend(repos)
139
+ page += 1
140
+
141
+ repos = all_repos
142
+
143
+ return [
144
+ OsProject(
145
+ owner=owner,
146
+ id=repo["name"],
147
+ ticketSystem=cls,
148
+ title=repo["name"],
149
+ url=repo["html_url"],
150
+ description=repo["description"],
151
+ language=repo["language"],
152
+ created_at=datetime.datetime.fromisoformat(
153
+ repo["created_at"].rstrip("Z")
154
+ ),
155
+ updated_at=datetime.datetime.fromisoformat(
156
+ repo["updated_at"].rstrip("Z")
157
+ ),
158
+ stars=repo["stargazers_count"],
159
+ forks=repo["forks_count"],
160
+ )
161
+ for repo in repos
162
+ ]
163
+
164
+ @classmethod
165
+ def get_project(
166
+ cls, owner: str, project_id: str, access_token: str = None
167
+ ) -> OsProject:
168
+ """
169
+ Get a specific project as an OsProject instance.
170
+
171
+ Args:
172
+ owner (str): The GitHub username or organization name.
173
+ project_id (str): The name of the project.
174
+ access_token (str, optional): GitHub personal access token for authentication.
175
+
176
+ Returns:
177
+ OsProject: An OsProject instance representing the repository.
178
+ """
179
+ projects = cls.list_projects_as_os_projects(
180
+ owner, access_token, project_name=project_id
181
+ )
182
+ if projects:
183
+ return projects[0]
184
+ raise Exception(f"Project {owner}/{project_id} not found")
185
+
186
+ @classmethod
187
+ def getIssues(
188
+ cls, project: OsProject, access_token: str = None, limit: int = None, **params
189
+ ) -> List[Ticket]:
92
190
  payload = {}
93
191
  headers = cls.prepare_headers(access_token)
94
192
  issues = []
@@ -104,20 +202,24 @@ class GitHub(TicketSystem):
104
202
  data=payload,
105
203
  params=params,
106
204
  )
107
- if response.status_code == 403 and 'rate limit' in response.text:
205
+ if response.status_code == 403 and "rate limit" in response.text:
108
206
  raise Exception("rate limit - you might want to use an access token")
109
207
  issue_records = json.loads(response.text)
110
208
  for record in issue_records:
111
209
  tr = {
112
210
  "project": project,
113
211
  "title": record.get("title"),
114
- "body": record.get("body", ""),
115
- "createdAt": parse(record.get("created_at"))
116
- if record.get("created_at")
117
- else "",
118
- "closedAt": parse(record.get("closed_at"))
119
- if record.get("closed_at")
120
- else "",
212
+ "body": record.get("body", ""),
213
+ "createdAt": (
214
+ parse(record.get("created_at"))
215
+ if record.get("created_at")
216
+ else ""
217
+ ),
218
+ "closedAt": (
219
+ parse(record.get("closed_at"))
220
+ if record.get("closed_at")
221
+ else ""
222
+ ),
121
223
  "state": record.get("state"),
122
224
  "number": record.get("number"),
123
225
  "url": f"{cls.projectUrl(project)}/issues/{record.get('number')}",
@@ -126,7 +228,7 @@ class GitHub(TicketSystem):
126
228
  fetched_count += 1
127
229
  # Check if we have reached the limit
128
230
  if limit is not None and fetched_count >= limit:
129
- nextResults=False
231
+ nextResults = False
130
232
  break
131
233
 
132
234
  if len(issue_records) < 100:
@@ -134,9 +236,11 @@ class GitHub(TicketSystem):
134
236
  else:
135
237
  params["page"] += 1
136
238
  return issues
137
-
239
+
138
240
  @classmethod
139
- def getComments(cls, project: OsProject, issue_number: int, access_token: str = None) -> List[dict]:
241
+ def getComments(
242
+ cls, project: OsProject, issue_number: int, access_token: str = None
243
+ ) -> List[dict]:
140
244
  """
141
245
  Fetch all comments for a specific issue number from GitHub.
142
246
  """
@@ -146,7 +250,9 @@ class GitHub(TicketSystem):
146
250
  if response.status_code == 200:
147
251
  return response.json()
148
252
  else:
149
- raise Exception(f"Failed to fetch comments: {response.status_code} - {response.text}")
253
+ raise Exception(
254
+ f"Failed to fetch comments: {response.status_code} - {response.text}"
255
+ )
150
256
  return []
151
257
 
152
258
  @staticmethod
@@ -160,7 +266,7 @@ class GitHub(TicketSystem):
160
266
  @staticmethod
161
267
  def commitUrl(project: OsProject, id: str):
162
268
  return f"{GitHub.projectUrl(project)}/commit/{id}"
163
-
269
+
164
270
  @staticmethod
165
271
  def commentUrl(project: OsProject, issue_number: int):
166
272
  """
@@ -168,7 +274,6 @@ class GitHub(TicketSystem):
168
274
  """
169
275
  return f"https://api.github.com/repos/{project.owner}/{project.id}/issues/{issue_number}/comments"
170
276
 
171
-
172
277
  @staticmethod
173
278
  def resolveProjectUrl(url: str) -> (str, str):
174
279
  """
@@ -201,29 +306,53 @@ class OsProject(object):
201
306
  self,
202
307
  owner: str = None,
203
308
  id: str = None,
204
- ticketSystem: Type[TicketSystem] = GitHub,
309
+ ticketSystem: Type[TicketSystem] = None,
310
+ title: str = None,
311
+ url: str = None,
312
+ description: str = None,
313
+ language: str = None,
314
+ created_at: datetime.datetime = None,
315
+ updated_at: datetime.datetime = None,
316
+ stars: int = 0,
317
+ forks: int = 0,
205
318
  ):
206
319
  """
207
320
  Constructor
208
321
  """
209
322
  self.owner = owner
210
323
  self.id = id
211
- self.ticketSystem = ticketSystem
324
+ self.ticketSystem = ticketSystem or GitHub
325
+ self.title = title
326
+ self.url = url
327
+ self.description = description
328
+ self.language = language
329
+ self.created_at = created_at
330
+ self.updated_at = updated_at
331
+ self.stars = stars
332
+ self.forks = forks
333
+
334
+ @property
335
+ def fqid(self):
336
+ fqid = f"{self.owner}/{self.id}"
337
+ return fqid
338
+
339
+ def __str__(self):
340
+ return self.fqid
212
341
 
213
342
  @staticmethod
214
343
  def getSamples():
215
344
  samples = [
216
345
  {
217
346
  "id": "pyOpenSourceProjects",
218
- "state": "",
219
347
  "owner": "WolfgangFahl",
220
348
  "title": "pyOpenSourceProjects",
221
349
  "url": "https://github.com/WolfgangFahl/pyOpenSourceProjects",
222
- "version": "",
223
- "desciption": "Helper Library to organize open source Projects",
224
- "date": datetime.datetime(year=2022, month=1, day=24),
225
- "since": "",
226
- "until": "",
350
+ "description": "Helper Library to organize open source Projects",
351
+ "language": "Python",
352
+ "created_at": datetime.datetime(year=2022, month=1, day=24),
353
+ "updated_at": datetime.datetime(year=2022, month=1, day=24),
354
+ "stars": 5,
355
+ "forks": 2,
227
356
  }
228
357
  ]
229
358
  return samples
@@ -235,8 +364,7 @@ class OsProject(object):
235
364
  """
236
365
  url = subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
237
366
  url = url.decode().strip("\n")
238
- osProject = cls.fromUrl(url)
239
- return osProject
367
+ return cls.fromUrl(url)
240
368
 
241
369
  @classmethod
242
370
  def fromUrl(cls, url: str) -> OsProject:
@@ -244,9 +372,11 @@ class OsProject(object):
244
372
  Init OsProject from given url
245
373
  """
246
374
  if "github.com" in url:
247
- owner, project = GitHub.resolveProjectUrl(url)
248
- if owner and project:
249
- return OsProject(owner=owner, id=project, ticketSystem=GitHub)
375
+ owner, project_id = GitHub.resolveProjectUrl(url)
376
+ if owner and project_id:
377
+ github = GitHub()
378
+ project = github.get_project(owner, project_id)
379
+ return project
250
380
  raise Exception(f"Could not resolve the url '{url}' to a OsProject object")
251
381
 
252
382
  def getIssues(self, **params) -> list:
@@ -254,14 +384,14 @@ class OsProject(object):
254
384
  tickets.sort(key=lambda r: getattr(r, "number"), reverse=True)
255
385
  return tickets
256
386
 
257
- def getAllTickets(self, limit:int=None,**params):
387
+ def getAllTickets(self, limit: int = None, **params):
258
388
  """
259
389
  Get all Tickets of the project - closed and open ones
260
-
390
+
261
391
  Args:
262
392
  limit(int): if set limit the number of tickets retrieved
263
393
  """
264
- issues= self.getIssues(state="all",limit=limit, **params)
394
+ issues = self.getIssues(state="all", limit=limit, **params)
265
395
  return issues
266
396
 
267
397
  def getCommits(self) -> List[Commit]:
@@ -18,7 +18,9 @@ dependencies = [
18
18
  "pyLodStorage>=0.11.6",
19
19
  "py-3rdparty-mediawiki>=0.11.3",
20
20
  # https://pypi.org/project/python-dateutil/
21
- "python-dateutil>=2.8.2"
21
+ "python-dateutil>=2.8.2",
22
+ # https://github.com/pypa/packaging
23
+ "packaging>=24.1"
22
24
  ]
23
25
  requires-python = ">=3.9"
24
26
 
@@ -44,10 +46,13 @@ path = "osprojects/__init__.py"
44
46
  test = []
45
47
 
46
48
  [tool.hatch.build.targets.wheel]
47
- packages = [
48
- "osprojects",
49
- ]
49
+ only-include = ["osprojects"]
50
+
51
+ [tool.hatch.build.targets.wheel.sources]
52
+ "osprojects" = "osprojects"
53
+
50
54
 
51
55
  [project.scripts]
52
56
  issue2ticket = "osprojects.osproject:main"
53
57
  gitlog2wiki = "osprojects.osproject:gitlog2wiki"
58
+ checkos = "osprojects.checkos:main"
@@ -0,0 +1,88 @@
1
+ #!/bin/bash
2
+ # create docs for a configurable project
3
+ # WF 2024-07-30 - updated
4
+
5
+ # Extract project name from pyproject.toml
6
+ PROJECT_NAME=$(grep "\[project\]" pyproject.toml -A1 | grep name | cut -d '=' -f2 | tr -d ' "')
7
+ PACKAGE_NAME=$(grep "\[tool.hatch.build.targets.wheel.sources\]" pyproject.toml -A1 | tail -1 | cut -d '=' -f2 | tr -d ' "')
8
+
9
+
10
+ # Function to print usage information
11
+ print_usage() {
12
+ echo "Usage: $0 [OPTIONS]"
13
+ echo "Options:"
14
+ echo " -pr, --project NAME Set the project name (default: $PROJECT_NAME)"
15
+ echo " -pa, --package NAME Set the package name (default: $PACKAGE_NAME)"
16
+ echo " -d, --deploy Deploy the documentation after building"
17
+ echo " -h, --help Display this help message"
18
+ }
19
+
20
+ # Parse command line arguments
21
+ DEPLOY=false
22
+ while [[ "$#" -gt 0 ]]; do
23
+ case $1 in
24
+ -pr|--project) PROJECT_NAME="$2"; shift ;;
25
+ -pa|--package) PACKAGE_NAME="$2"; shift ;;
26
+ -d|--deploy) DEPLOY=true ;;
27
+ -h|--help) print_usage; exit 0 ;;
28
+ *) echo "Unknown parameter: $1"; print_usage; exit 1 ;;
29
+ esac
30
+ shift
31
+ done
32
+
33
+ # Ensure we're in the correct directory
34
+ if [[ ! -d "$PACKAGE_NAME" ]]; then
35
+ echo "Error: $PACKAGE_NAME package directory not found. Are you in the correct directory?"
36
+ exit 1
37
+ fi
38
+
39
+ # Check if mkdocs is installed
40
+ if ! command -v mkdocs &> /dev/null; then
41
+ pip install mkdocs mkdocs-material mkdocstrings[python]
42
+ fi
43
+
44
+ # Create or update mkdocs.yml
45
+ cat << EOF > mkdocs.yml
46
+ site_name: $PROJECT_NAME API Documentation
47
+ theme:
48
+ name: material
49
+ plugins:
50
+ - search
51
+ - mkdocstrings:
52
+ handlers:
53
+ python:
54
+ setup_commands:
55
+ - import sys
56
+ - import os
57
+ - sys.path.insert(0, os.path.abspath("."))
58
+ selection:
59
+ docstring_style: google
60
+ rendering:
61
+ show_source: true
62
+ nav:
63
+ - API: index.md
64
+ EOF
65
+
66
+ # Create or update index.md
67
+ index_md=docs/index.md
68
+ mkdir -p docs
69
+ cat << EOF > $index_md
70
+ # $PROJECT_NAME API Documentation
71
+
72
+ ::: $PACKAGE_NAME
73
+ options:
74
+ show_submodules: true
75
+ EOF
76
+
77
+ # Ignore DeprecationWarnings during build
78
+ export PYTHONWARNINGS="ignore::DeprecationWarning"
79
+
80
+ # Build the documentation
81
+ mkdocs build --config-file ./mkdocs.yml
82
+
83
+ # Deploy if requested
84
+ if [ "$DEPLOY" = true ]; then
85
+ mkdocs gh-deploy --force --config-file ./mkdocs.yml
86
+ fi
87
+
88
+ echo "Documentation process completed for $PROJECT_NAME."
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+ # WF 2024-07-31
3
+ # prepare a release
4
+ scripts/doc -d
5
+
6
+ # Commit with a message that includes the current ISO timestamp
7
+ git commit -a -m "release commit"
8
+ git push
@@ -3,6 +3,7 @@ Created on 2021-08-19
3
3
 
4
4
  @author: wf
5
5
  """
6
+
6
7
  import getpass
7
8
  import io
8
9
  import os
@@ -3,6 +3,7 @@ Created on 2022-01-24
3
3
 
4
4
  @author: wf
5
5
  """
6
+
6
7
  import unittest
7
8
 
8
9
  from osprojects.osproject import Commit, GitHub, OsProject, Ticket, gitlog2wiki, main
@@ -23,7 +24,7 @@ class TestOsProject(BaseTest):
23
24
  expectedTicket = self.getSampleById(Ticket, "number", 2)
24
25
  expectedTicket.project = osProject
25
26
  comparison_ticket_dict = tickets[-2].__dict__
26
- comparison_ticket_dict.pop('body', None)
27
+ comparison_ticket_dict.pop("body", None)
27
28
  self.assertDictEqual(expectedTicket.__dict__, comparison_ticket_dict)
28
29
  commit = Commit()
29
30
  ticket = Ticket()
@@ -102,6 +103,76 @@ class TestGitHub(BaseTest):
102
103
  self.assertEqual(expectedOwner, owner)
103
104
  self.assertEqual(expectedProject, project)
104
105
 
106
+ def testListProjects(self):
107
+ """
108
+ tests the list_projects_as_os_projects method
109
+ """
110
+ owner = "WolfgangFahl"
111
+ github = GitHub()
112
+
113
+ # Test list_projects_as_os_projects
114
+ projects = github.list_projects_as_os_projects(owner)
115
+ debug = self.debug
116
+ debug = True
117
+ if debug:
118
+ for project in projects:
119
+ print(project)
120
+ self.assertIsInstance(projects, list)
121
+ self.assertTrue(len(projects) > 0, "No projects found for WolfgangFahl")
122
+
123
+ # Check if pyOpenSourceProjects is in the list
124
+ pyosp_found = any(project.id == "pyOpenSourceProjects" for project in projects)
125
+ self.assertTrue(
126
+ pyosp_found, "pyOpenSourceProjects not found in the list of projects"
127
+ )
128
+
129
+ # Test a sample project's structure
130
+ sample_project = projects[0]
131
+ expected_attributes = {
132
+ "id",
133
+ "owner",
134
+ "title",
135
+ "url",
136
+ "description",
137
+ "language",
138
+ "created_at",
139
+ "updated_at",
140
+ "stars",
141
+ "forks",
142
+ }
143
+ self.assertTrue(
144
+ all(hasattr(sample_project, attr) for attr in expected_attributes),
145
+ "OsProject instance is missing expected attributes",
146
+ )
147
+
148
+ # Check if all items are OsProject instances
149
+ self.assertTrue(
150
+ all(isinstance(project, OsProject) for project in projects),
151
+ "Not all items are OsProject instances",
152
+ )
153
+
154
+ # Test a sample OsProject
155
+ sample_os_project = projects[0]
156
+ self.assertEqual(sample_os_project.owner, owner)
157
+ self.assertIsInstance(sample_os_project.id, str)
158
+ self.assertEqual(sample_os_project.ticketSystem, GitHub)
159
+
160
+ def testGetSpecificProject(self):
161
+ """
162
+ tests getting a specific project
163
+ """
164
+ owner = "WolfgangFahl"
165
+ project_name = "pyOpenSourceProjects"
166
+ github = GitHub()
167
+
168
+ project = github.list_projects_as_os_projects(owner, project_name=project_name)[
169
+ 0
170
+ ]
171
+ self.assertIsInstance(project, OsProject)
172
+ self.assertEqual(project.id, project_name)
173
+ self.assertEqual(project.owner, owner)
174
+ self.assertEqual(project.ticketSystem, GitHub)
175
+
105
176
 
106
177
  class TestCommit(BaseTest):
107
178
  """
@@ -1 +0,0 @@
1
- __version__ = "0.1.2"
@@ -1,88 +0,0 @@
1
- #!/bin/bash
2
- # WF 2020-01-31
3
-
4
- #
5
- # check whether the given command is installed
6
- #
7
- checkinstalled() {
8
- local l_cmd="$1"
9
- which $l_cmd > /dev/null
10
- if [ $? -ne 0 ]
11
- then
12
- echo "$l_cmd need to be installed" 1>&2
13
- exit 1
14
- fi
15
- }
16
-
17
- fixconf() {
18
- local l_year="$1"
19
- local l_author="$2"
20
- conf=conf.py
21
- # fix sys path
22
- # https://stackoverflow.com/questions/10324393/sphinx-build-fail-autodoc-cant-import-find-module
23
- grep "# sys.path" $conf
24
- if [ $? -eq 0 ]
25
- then
26
- tmpconf=/tmp/conf$$.py
27
- cat $conf | awk -v author="$l_author" -v year="$l_year" '
28
- BEGIN {
29
- quote="\x27"
30
- squote="\047"
31
- }
32
- /# import os/ { next }
33
- /# import sys/ { next }
34
- /copyright/ {
35
- printf "copyright = %s%s, %s%s\n",squote,year,author,squote
36
- next
37
- }
38
- /author/ {
39
- printf "author = %s%s%s\n",squote,author,squote
40
- next
41
- }
42
- /html_theme = / {
43
- # html_theme = 'alabaster'
44
- printf "html_theme = %ssphinx_rtd_theme%s\n",squote,squote
45
- printf "master_doc = %sindex%s\n",squote,squote
46
- next
47
- }
48
- # add sphinx_rtd extension
49
- /extensions = / {
50
- print $0
51
- printf "\t%ssphinx_rtd_theme%s,\n",squote,squote
52
- printf "\t%ssphinx.ext.napoleon%s,\n",squote,squote
53
- next
54
- }
55
- /# sys.path/ {
56
- print("#https://stackoverflow.com/a/44980548/1497139")
57
- print("import os")
58
- print("import sys")
59
- print("import sphinx_rtd_theme")
60
- printf("basepath=os.path.abspath(%s../..%s)\n",squote,squote)
61
- printf("print(%sadding basepath %%s%s %% (basepath))\n",squote,squote)
62
- print("sys.path.insert(0, basepath)")
63
- printf("print(%ssys.path is now: %%s%s %% (sys.path))\n",squote,squote)
64
- next
65
- }
66
- { print }
67
- END {
68
- print ("#additional settings")
69
- print ("#https://stackoverflow.com/a/5599712/1497139")
70
- print ("autoclass_content = '\''both'\''")
71
- }' > $tmpconf
72
- #diff $tmpconf $conf
73
- mv $tmpconf $conf
74
- echo "$src/conf.py has been fixed"
75
- fi
76
- }
77
-
78
- src=docs/source
79
- checkinstalled sphinx-apidoc
80
- sphinx-apidoc --full -f -o $src .
81
- cd $src
82
-
83
- fixconf 2020-2021 "Wolfgang Fahl"
84
- make clean html
85
- if [ "$GHACTIONS" != "ACTIVE" ]
86
- then
87
- open _build/html/index.html
88
- fi