code-annotations 1.6.0__tar.gz → 2.2.0__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.
- {code-annotations-1.6.0 → code_annotations-2.2.0}/CHANGELOG.rst +28 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/PKG-INFO +49 -8
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/__init__.py +1 -1
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/base.py +2 -2
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/cli.py +117 -68
- code_annotations-2.2.0/code_annotations/contrib/config/__init__.py +14 -0
- code_annotations-2.2.0/code_annotations/contrib/config/openedx_events_annotations.yaml +20 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/contrib/sphinx/extensions/featuretoggles.py +1 -1
- code_annotations-2.2.0/code_annotations/contrib/sphinx/extensions/openedx_events.py +182 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/find_django.py +169 -102
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/generate_docs.py +1 -1
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations.egg-info/PKG-INFO +49 -8
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations.egg-info/SOURCES.txt +2 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations.egg-info/requires.txt +4 -4
- {code-annotations-1.6.0 → code_annotations-2.2.0}/setup.py +4 -3
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_django_generate_safelist.py +2 -2
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_find_django.py +17 -0
- code-annotations-1.6.0/code_annotations/contrib/config/__init__.py +0 -15
- {code-annotations-1.6.0 → code_annotations-2.2.0}/LICENSE.txt +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/MANIFEST.in +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/NOTICE.txt +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/README.rst +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/annotation_errors.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/contrib/config/feature_toggle_annotations.yaml +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/contrib/config/setting_annotations.yaml +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/contrib/sphinx/extensions/__init__.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/contrib/sphinx/extensions/base.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/contrib/sphinx/extensions/settings.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/exceptions.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/extensions/__init__.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/extensions/base.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/extensions/javascript.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/extensions/python.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/find_static.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations/helpers.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations.egg-info/dependency_links.txt +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations.egg-info/entry_points.txt +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations.egg-info/not-zip-safe +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/code_annotations.egg-info/top_level.txt +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/requirements/base.in +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/setup.cfg +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_base.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_django_coverage.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_django_list_local_models.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_find_static.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_generate_docs.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_search.py +0 -0
- {code-annotations-1.6.0 → code_annotations-2.2.0}/tests/test_sphinx.py +0 -0
|
@@ -14,6 +14,34 @@ Change Log
|
|
|
14
14
|
Unreleased
|
|
15
15
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
16
16
|
|
|
17
|
+
[2.2.0] - 2025-01-15
|
|
18
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
19
|
+
|
|
20
|
+
* Add support for optional Open edX Event trigger in-line annotation.
|
|
21
|
+
|
|
22
|
+
[2.1.0] - 2024-12-12
|
|
23
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
24
|
+
|
|
25
|
+
* Add support for optional Open edX Event warning for in-line annotation.
|
|
26
|
+
|
|
27
|
+
[2.0.0] - 2024-10-18
|
|
28
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
29
|
+
|
|
30
|
+
* Drop support for python 3.8
|
|
31
|
+
* Adds support for python 3.11 & 3.12
|
|
32
|
+
|
|
33
|
+
[1.8.1] - 2024-07-11
|
|
34
|
+
~~~~~~~~~~~~~~~~~~~~
|
|
35
|
+
|
|
36
|
+
* Fix elapsed-time calculations to always use UTC. Other clocks can be altered partway through by Django config settings being loaded while the timer is running, resulting in reporting elapsed time of "-17999.895582 seconds" or similar.
|
|
37
|
+
* Fix report filename to use year-month-day order, not year-day-month. (Also more compact, now.)
|
|
38
|
+
|
|
39
|
+
[1.8.0] - 2024-03-31
|
|
40
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
41
|
+
|
|
42
|
+
* Added python3.11 and 3.12 support. Dropped django32 support.
|
|
43
|
+
|
|
44
|
+
|
|
17
45
|
[1.6.0] - 2024-01-31
|
|
18
46
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
19
47
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: code-annotations
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Extensible tools for parsing annotations in codebases
|
|
5
5
|
Home-page: https://github.com/openedx/code-annotations
|
|
6
6
|
Author: edX
|
|
@@ -9,24 +9,37 @@ License: Apache Software License 2.0
|
|
|
9
9
|
Keywords: edx pii code annotations
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: Framework :: Django
|
|
12
|
-
Classifier: Framework :: Django :: 3.2
|
|
13
12
|
Classifier: Framework :: Django :: 4.2
|
|
14
13
|
Classifier: Intended Audience :: Developers
|
|
15
14
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
15
|
Classifier: Natural Language :: English
|
|
17
16
|
Classifier: Programming Language :: Python
|
|
18
17
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.11
|
|
20
21
|
Description-Content-Type: text/x-rst
|
|
21
22
|
License-File: LICENSE.txt
|
|
22
23
|
License-File: NOTICE.txt
|
|
23
|
-
Requires-Dist: stevedore
|
|
24
|
-
Requires-Dist: python-slugify
|
|
25
|
-
Requires-Dist: click
|
|
26
24
|
Requires-Dist: pyyaml
|
|
25
|
+
Requires-Dist: click
|
|
27
26
|
Requires-Dist: Jinja2
|
|
27
|
+
Requires-Dist: stevedore
|
|
28
|
+
Requires-Dist: python-slugify
|
|
28
29
|
Provides-Extra: django
|
|
29
|
-
Requires-Dist: Django
|
|
30
|
+
Requires-Dist: Django>=4.2; extra == "django"
|
|
31
|
+
Dynamic: author
|
|
32
|
+
Dynamic: author-email
|
|
33
|
+
Dynamic: classifier
|
|
34
|
+
Dynamic: description
|
|
35
|
+
Dynamic: description-content-type
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: keywords
|
|
38
|
+
Dynamic: license
|
|
39
|
+
Dynamic: provides-extra
|
|
40
|
+
Dynamic: requires-dist
|
|
41
|
+
Dynamic: requires-python
|
|
42
|
+
Dynamic: summary
|
|
30
43
|
|
|
31
44
|
code-annotations
|
|
32
45
|
=============================
|
|
@@ -128,6 +141,34 @@ Change Log
|
|
|
128
141
|
Unreleased
|
|
129
142
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
130
143
|
|
|
144
|
+
[2.2.0] - 2025-01-15
|
|
145
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
146
|
+
|
|
147
|
+
* Add support for optional Open edX Event trigger in-line annotation.
|
|
148
|
+
|
|
149
|
+
[2.1.0] - 2024-12-12
|
|
150
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
151
|
+
|
|
152
|
+
* Add support for optional Open edX Event warning for in-line annotation.
|
|
153
|
+
|
|
154
|
+
[2.0.0] - 2024-10-18
|
|
155
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
156
|
+
|
|
157
|
+
* Drop support for python 3.8
|
|
158
|
+
* Adds support for python 3.11 & 3.12
|
|
159
|
+
|
|
160
|
+
[1.8.1] - 2024-07-11
|
|
161
|
+
~~~~~~~~~~~~~~~~~~~~
|
|
162
|
+
|
|
163
|
+
* Fix elapsed-time calculations to always use UTC. Other clocks can be altered partway through by Django config settings being loaded while the timer is running, resulting in reporting elapsed time of "-17999.895582 seconds" or similar.
|
|
164
|
+
* Fix report filename to use year-month-day order, not year-day-month. (Also more compact, now.)
|
|
165
|
+
|
|
166
|
+
[1.8.0] - 2024-03-31
|
|
167
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
168
|
+
|
|
169
|
+
* Added python3.11 and 3.12 support. Dropped django32 support.
|
|
170
|
+
|
|
171
|
+
|
|
131
172
|
[1.6.0] - 2024-01-31
|
|
132
173
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
133
174
|
|
|
@@ -616,9 +616,9 @@ class BaseSearch(metaclass=ABCMeta):
|
|
|
616
616
|
"""
|
|
617
617
|
self.echo.echo_vv(yaml.dump(all_results, default_flow_style=False))
|
|
618
618
|
|
|
619
|
-
now = datetime.datetime.
|
|
619
|
+
now = datetime.datetime.utcnow()
|
|
620
620
|
report_filename = os.path.join(self.config.report_path, '{}{}.yaml'.format(
|
|
621
|
-
report_prefix, now.strftime('%Y
|
|
621
|
+
report_prefix, now.strftime('%Y%m%d-%H%M%S')
|
|
622
622
|
))
|
|
623
623
|
|
|
624
624
|
formatted_results = self._format_results_for_report(all_results)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Command line interface for code annotation tools.
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
import datetime
|
|
5
6
|
import sys
|
|
6
7
|
import traceback
|
|
@@ -21,53 +22,88 @@ def entry_point():
|
|
|
21
22
|
"""
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
@entry_point.command(
|
|
25
|
+
@entry_point.command("django_find_annotations")
|
|
26
|
+
@click.option(
|
|
27
|
+
"--config_file",
|
|
28
|
+
default=".annotations",
|
|
29
|
+
help="Path to the configuration file",
|
|
30
|
+
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--seed_safelist/--no_safelist",
|
|
34
|
+
default=False,
|
|
35
|
+
show_default=True,
|
|
36
|
+
help="Generate an initial safelist file based on the current Django environment.",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--list_local_models/--no_list_models",
|
|
40
|
+
default=False,
|
|
41
|
+
show_default=True,
|
|
42
|
+
help="List all locally defined models (in the current repo) that require annotations.",
|
|
43
|
+
)
|
|
44
|
+
@click.option(
|
|
45
|
+
"--app_name",
|
|
46
|
+
default=None,
|
|
47
|
+
help="(Optional) App name for which coverage is generated.",
|
|
48
|
+
)
|
|
49
|
+
@click.option("--report_path", default=None, help="Location to write the report")
|
|
50
|
+
@click.option("-v", "--verbosity", count=True, help="Verbosity level (-v through -vvv)")
|
|
25
51
|
@click.option(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
52
|
+
"--lint/--no_lint",
|
|
53
|
+
help="Enable or disable linting checks",
|
|
54
|
+
default=False,
|
|
55
|
+
show_default=True,
|
|
30
56
|
)
|
|
31
57
|
@click.option(
|
|
32
|
-
|
|
58
|
+
"--report/--no_report",
|
|
59
|
+
help="Enable or disable writing the report",
|
|
33
60
|
default=False,
|
|
34
61
|
show_default=True,
|
|
35
|
-
help='Generate an initial safelist file based on the current Django environment.',
|
|
36
62
|
)
|
|
37
63
|
@click.option(
|
|
38
|
-
|
|
64
|
+
"--coverage/--no_coverage",
|
|
65
|
+
help="Enable or disable coverage checks",
|
|
39
66
|
default=False,
|
|
40
67
|
show_default=True,
|
|
41
|
-
help='List all locally defined models (in the current repo) that require annotations.',
|
|
42
68
|
)
|
|
43
|
-
@click.option('--app_name', default='', help='(Optional) App name for which coverage is generated.')
|
|
44
|
-
@click.option('--report_path', default=None, help='Location to write the report')
|
|
45
|
-
@click.option('-v', '--verbosity', count=True, help='Verbosity level (-v through -vvv)')
|
|
46
|
-
@click.option('--lint/--no_lint', help='Enable or disable linting checks', default=False, show_default=True)
|
|
47
|
-
@click.option('--report/--no_report', help='Enable or disable writing the report', default=False, show_default=True)
|
|
48
|
-
@click.option('--coverage/--no_coverage', help='Enable or disable coverage checks', default=False, show_default=True)
|
|
49
69
|
def django_find_annotations(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
config_file,
|
|
71
|
+
seed_safelist,
|
|
72
|
+
list_local_models,
|
|
73
|
+
app_name,
|
|
74
|
+
report_path,
|
|
75
|
+
verbosity,
|
|
76
|
+
lint,
|
|
77
|
+
report,
|
|
78
|
+
coverage,
|
|
59
79
|
):
|
|
60
80
|
"""
|
|
61
81
|
Subcommand for dealing with annotations in Django models.
|
|
62
82
|
"""
|
|
63
83
|
try:
|
|
64
|
-
start_time = datetime.datetime.
|
|
84
|
+
start_time = datetime.datetime.utcnow()
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
not coverage
|
|
88
|
+
and not seed_safelist
|
|
89
|
+
and not list_local_models
|
|
90
|
+
and not lint
|
|
91
|
+
and not report
|
|
92
|
+
):
|
|
93
|
+
click.echo(
|
|
94
|
+
"No actions specified. Please specify one or more of --seed_safelist, --list_local_models, "
|
|
95
|
+
"--lint, --report, or --coverage"
|
|
96
|
+
)
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
65
99
|
config = AnnotationConfig(config_file, report_path, verbosity)
|
|
66
|
-
searcher = DjangoSearch(config)
|
|
100
|
+
searcher = DjangoSearch(config, app_name)
|
|
67
101
|
|
|
68
102
|
# Early out if we're trying to do coverage, but a coverage target is not configured
|
|
69
103
|
if coverage and not config.coverage_target:
|
|
70
|
-
raise ConfigurationException(
|
|
104
|
+
raise ConfigurationException(
|
|
105
|
+
"Please add 'coverage_target' to your configuration before running --coverage"
|
|
106
|
+
)
|
|
71
107
|
|
|
72
108
|
if seed_safelist:
|
|
73
109
|
searcher.seed_safelist()
|
|
@@ -106,38 +142,51 @@ def django_find_annotations(
|
|
|
106
142
|
for filename in annotated_models:
|
|
107
143
|
annotation_count += len(annotated_models[filename])
|
|
108
144
|
|
|
109
|
-
elapsed = datetime.datetime.
|
|
110
|
-
click.echo(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
elapsed = datetime.datetime.utcnow() - start_time
|
|
146
|
+
click.echo(
|
|
147
|
+
"Search found {} annotations in {} seconds.".format(
|
|
148
|
+
annotation_count, elapsed.total_seconds()
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
except Exception as exc:
|
|
115
152
|
click.echo(traceback.print_exc())
|
|
116
153
|
fail(str(exc))
|
|
117
154
|
|
|
118
155
|
|
|
119
|
-
@entry_point.command(
|
|
156
|
+
@entry_point.command("static_find_annotations")
|
|
120
157
|
@click.option(
|
|
121
|
-
|
|
122
|
-
default=
|
|
123
|
-
help=
|
|
124
|
-
type=click.Path(exists=True, dir_okay=False, resolve_path=True)
|
|
158
|
+
"--config_file",
|
|
159
|
+
default=".annotations",
|
|
160
|
+
help="Path to the configuration file",
|
|
161
|
+
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
|
|
125
162
|
)
|
|
126
163
|
@click.option(
|
|
127
|
-
|
|
128
|
-
help=
|
|
129
|
-
type=click.Path(exists=True, dir_okay=True, resolve_path=True)
|
|
164
|
+
"--source_path",
|
|
165
|
+
help="Location of the source code to search",
|
|
166
|
+
type=click.Path(exists=True, dir_okay=True, resolve_path=True),
|
|
130
167
|
)
|
|
131
|
-
@click.option(
|
|
132
|
-
@click.option(
|
|
133
|
-
@click.option(
|
|
134
|
-
|
|
135
|
-
|
|
168
|
+
@click.option("--report_path", default=None, help="Location to write the report")
|
|
169
|
+
@click.option("-v", "--verbosity", count=True, help="Verbosity level (-v through -vvv)")
|
|
170
|
+
@click.option(
|
|
171
|
+
"--lint/--no_lint",
|
|
172
|
+
help="Enable or disable linting checks",
|
|
173
|
+
default=True,
|
|
174
|
+
show_default=True,
|
|
175
|
+
)
|
|
176
|
+
@click.option(
|
|
177
|
+
"--report/--no_report",
|
|
178
|
+
help="Enable or disable writing the report file",
|
|
179
|
+
default=True,
|
|
180
|
+
show_default=True,
|
|
181
|
+
)
|
|
182
|
+
def static_find_annotations(
|
|
183
|
+
config_file, source_path, report_path, verbosity, lint, report
|
|
184
|
+
):
|
|
136
185
|
"""
|
|
137
186
|
Subcommand to find annotations via static file analysis.
|
|
138
187
|
"""
|
|
139
188
|
try:
|
|
140
|
-
start_time = datetime.datetime.
|
|
189
|
+
start_time = datetime.datetime.utcnow()
|
|
141
190
|
config = AnnotationConfig(config_file, report_path, verbosity, source_path)
|
|
142
191
|
searcher = StaticSearch(config)
|
|
143
192
|
all_results = searcher.search()
|
|
@@ -161,7 +210,7 @@ def static_find_annotations(config_file, source_path, report_path, verbosity, li
|
|
|
161
210
|
report_filename = searcher.report(all_results)
|
|
162
211
|
click.echo(f"Report written to {report_filename}.")
|
|
163
212
|
|
|
164
|
-
elapsed = datetime.datetime.
|
|
213
|
+
elapsed = datetime.datetime.utcnow() - start_time
|
|
165
214
|
annotation_count = 0
|
|
166
215
|
|
|
167
216
|
for filename in all_results:
|
|
@@ -169,49 +218,49 @@ def static_find_annotations(config_file, source_path, report_path, verbosity, li
|
|
|
169
218
|
|
|
170
219
|
click.echo(f"Search found {annotation_count} annotations in {elapsed}.")
|
|
171
220
|
|
|
172
|
-
except Exception as exc:
|
|
221
|
+
except Exception as exc:
|
|
173
222
|
click.echo(traceback.print_exc())
|
|
174
223
|
fail(str(exc))
|
|
175
224
|
|
|
176
225
|
|
|
177
226
|
@entry_point.command("generate_docs")
|
|
178
227
|
@click.option(
|
|
179
|
-
|
|
180
|
-
default=
|
|
181
|
-
help=
|
|
182
|
-
type=click.Path(exists=True, dir_okay=False)
|
|
228
|
+
"--config_file",
|
|
229
|
+
default=".annotations",
|
|
230
|
+
help="Path to the configuration file",
|
|
231
|
+
type=click.Path(exists=True, dir_okay=False),
|
|
183
232
|
)
|
|
184
|
-
@click.option(
|
|
185
|
-
@click.argument("report_files", type=click.File(
|
|
186
|
-
def generate_docs(
|
|
187
|
-
config_file,
|
|
188
|
-
verbosity,
|
|
189
|
-
report_files
|
|
190
|
-
):
|
|
233
|
+
@click.option("-v", "--verbosity", count=True, help="Verbosity level (-v through -vvv)")
|
|
234
|
+
@click.argument("report_files", type=click.File("r"), nargs=-1)
|
|
235
|
+
def generate_docs(config_file, verbosity, report_files):
|
|
191
236
|
"""
|
|
192
237
|
Generate documentation from a code annotations report.
|
|
193
238
|
"""
|
|
194
|
-
start_time = datetime.datetime.
|
|
239
|
+
start_time = datetime.datetime.utcnow()
|
|
195
240
|
|
|
196
241
|
try:
|
|
197
242
|
config = AnnotationConfig(config_file, verbosity)
|
|
198
243
|
|
|
199
244
|
for key in (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
245
|
+
"report_template_dir",
|
|
246
|
+
"rendered_report_dir",
|
|
247
|
+
"rendered_report_file_extension",
|
|
248
|
+
"rendered_report_source_link_prefix",
|
|
204
249
|
):
|
|
205
250
|
if not getattr(config, key):
|
|
206
251
|
raise ConfigurationException(f"No {key} key in {config_file}")
|
|
207
252
|
|
|
208
|
-
config.echo(
|
|
253
|
+
config.echo(
|
|
254
|
+
"Rendering the following reports: \n{}".format(
|
|
255
|
+
"\n".join([r.name for r in report_files])
|
|
256
|
+
)
|
|
257
|
+
)
|
|
209
258
|
|
|
210
259
|
renderer = ReportRenderer(config, report_files)
|
|
211
260
|
renderer.render()
|
|
212
261
|
|
|
213
|
-
elapsed = datetime.datetime.
|
|
262
|
+
elapsed = datetime.datetime.utcnow() - start_time
|
|
214
263
|
click.echo(f"Report rendered in {elapsed.total_seconds()} seconds.")
|
|
215
|
-
except Exception as exc:
|
|
264
|
+
except Exception as exc:
|
|
216
265
|
click.echo(traceback.print_exc())
|
|
217
266
|
fail(str(exc))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Expose contrib configuration file paths as Python variables, for use in 3rd-party utilities.
|
|
3
|
+
"""
|
|
4
|
+
import importlib.resources
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
FEATURE_TOGGLE_ANNOTATIONS_CONFIG_PATH = importlib.resources.files(
|
|
8
|
+
"code_annotations") / os.path.join("contrib", "config", "feature_toggle_annotations.yaml")
|
|
9
|
+
|
|
10
|
+
SETTING_ANNOTATIONS_CONFIG_PATH = importlib.resources.files(
|
|
11
|
+
"code_annotations") / os.path.join("contrib", "config", "setting_annotations.yaml")
|
|
12
|
+
|
|
13
|
+
OPENEDX_EVENTS_ANNOTATIONS_CONFIG_PATH = importlib.resources.files(
|
|
14
|
+
"code_annotations") / os.path.join("contrib", "config", "openedx_events_annotations.yaml")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# This code-annotations configuration file supports openedx-events
|
|
2
|
+
|
|
3
|
+
source_path: ./
|
|
4
|
+
report_path: reports
|
|
5
|
+
safelist_path: .annotation_safe_list.yml
|
|
6
|
+
coverage_target: 100.0
|
|
7
|
+
annotations:
|
|
8
|
+
feature_toggle:
|
|
9
|
+
# See annotation format documentation: https://docs.openedx.org/projects/openedx-events/en/latest/reference/in-line-code-annotations-for-an-event.html
|
|
10
|
+
- ".. event_type:":
|
|
11
|
+
- ".. event_name:":
|
|
12
|
+
- ".. event_description:":
|
|
13
|
+
- ".. event_data:":
|
|
14
|
+
- ".. event_key_field:":
|
|
15
|
+
- ".. event_trigger_repository:":
|
|
16
|
+
- ".. event_warning:":
|
|
17
|
+
extensions:
|
|
18
|
+
python:
|
|
19
|
+
- py
|
|
20
|
+
rst_template: doc.rst.j2
|
|
@@ -111,7 +111,7 @@ class FeatureToggles(SphinxDirective):
|
|
|
111
111
|
for opt in optional_attrs:
|
|
112
112
|
if toggle.get(f".. toggle_{opt}:") not in (None, "None", "n/a", "N/A"):
|
|
113
113
|
toggle_section += nodes.paragraph(
|
|
114
|
-
text=f'{opt.title().replace("_"," ")}: {toggle[f".. toggle_{opt}:"]}',
|
|
114
|
+
text=f'{opt.title().replace("_", " ")}: {toggle[f".. toggle_{opt}:"]}',
|
|
115
115
|
ids=[f"{opt}-{toggle_name}"],
|
|
116
116
|
)
|
|
117
117
|
yield toggle_section
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sphinx extension for viewing openedx events annotations.
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from docutils import nodes
|
|
7
|
+
from sphinx.util.docutils import SphinxDirective
|
|
8
|
+
|
|
9
|
+
from code_annotations.contrib.config import OPENEDX_EVENTS_ANNOTATIONS_CONFIG_PATH
|
|
10
|
+
|
|
11
|
+
from .base import find_annotations
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def find_events(source_path):
|
|
15
|
+
"""
|
|
16
|
+
Find the events as defined in the configuration file.
|
|
17
|
+
|
|
18
|
+
Return:
|
|
19
|
+
events (dict): found events indexed by event type.
|
|
20
|
+
"""
|
|
21
|
+
return find_annotations(
|
|
22
|
+
source_path, OPENEDX_EVENTS_ANNOTATIONS_CONFIG_PATH, ".. event_type:"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OpenedxEvents(SphinxDirective):
|
|
27
|
+
"""
|
|
28
|
+
Sphinx directive to list the events in a single documentation page.
|
|
29
|
+
|
|
30
|
+
Use this directive as follows::
|
|
31
|
+
|
|
32
|
+
.. openedxevents::
|
|
33
|
+
|
|
34
|
+
This directive supports the following configuration parameters:
|
|
35
|
+
|
|
36
|
+
- ``openedxevents_source_path``: absolute path to the repository file tree. E.g:
|
|
37
|
+
|
|
38
|
+
openedxevents_source_path = os.path.join(os.path.dirname(__file__), "..", "..")
|
|
39
|
+
|
|
40
|
+
- ``openedxevents_repo_url``: Github repository where the code is hosted. E.g:
|
|
41
|
+
|
|
42
|
+
openedxevents_repo_url = "https://github.com/openedx/myrepo"
|
|
43
|
+
|
|
44
|
+
- ``openedxevents_repo_version``: current version of the git repository. E.g:
|
|
45
|
+
|
|
46
|
+
import git
|
|
47
|
+
try:
|
|
48
|
+
repo = git.Repo(search_parent_directories=True)
|
|
49
|
+
openedxevents_repo_version = repo.head.object.hexsha
|
|
50
|
+
except git.InvalidGitRepositoryError:
|
|
51
|
+
openedxevents_repo_version = "main"
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
required_arguments = 0
|
|
55
|
+
optional_arguments = 0
|
|
56
|
+
option_spec = {}
|
|
57
|
+
|
|
58
|
+
def run(self):
|
|
59
|
+
"""
|
|
60
|
+
Public interface of the Directive class.
|
|
61
|
+
|
|
62
|
+
Return:
|
|
63
|
+
nodes (list): nodes to be appended to the resulting document.
|
|
64
|
+
"""
|
|
65
|
+
return list(self.iter_nodes())
|
|
66
|
+
|
|
67
|
+
def iter_nodes(self):
|
|
68
|
+
"""
|
|
69
|
+
Iterate on the docutils nodes generated by this directive.
|
|
70
|
+
"""
|
|
71
|
+
events = find_events(self.env.config.openedxevents_source_path)
|
|
72
|
+
|
|
73
|
+
current_domain = ""
|
|
74
|
+
domain_header = None
|
|
75
|
+
current_subject = ""
|
|
76
|
+
subject_header = None
|
|
77
|
+
|
|
78
|
+
for event_type in sorted(events):
|
|
79
|
+
domain = event_type.split(".")[2]
|
|
80
|
+
subject = event_type.split(".")[3]
|
|
81
|
+
if domain != current_domain:
|
|
82
|
+
if domain_header:
|
|
83
|
+
yield domain_header
|
|
84
|
+
|
|
85
|
+
current_domain = domain
|
|
86
|
+
domain_header = nodes.section("", ids=[f"openedxevent-domain-{domain}"])
|
|
87
|
+
domain_header += nodes.title(text=f"Architectural subdomain: {domain}")
|
|
88
|
+
if subject != current_subject:
|
|
89
|
+
current_subject = subject
|
|
90
|
+
subject_header = nodes.section("", ids=[f"openedxevent-subject"
|
|
91
|
+
f"-{subject}"])
|
|
92
|
+
subject_header += nodes.title(text=f"Subject: {subject}")
|
|
93
|
+
domain_header += subject_header
|
|
94
|
+
|
|
95
|
+
event = events[event_type]
|
|
96
|
+
event_name = event[".. event_name:"]
|
|
97
|
+
event_name_literal = nodes.literal(text=event_name)
|
|
98
|
+
event_data = event[".. event_data:"]
|
|
99
|
+
event_data_literal = nodes.literal(text=event_data)
|
|
100
|
+
event_key_field = event.get(".. event_key_field:", "")
|
|
101
|
+
event_key_literal = nodes.literal(text=event_key_field)
|
|
102
|
+
event_description = event[".. event_description:"]
|
|
103
|
+
event_trigger_repository = event.get(".. event_trigger_repository:")
|
|
104
|
+
|
|
105
|
+
event_section = nodes.section("", ids=[f"openedxevent-{event_type}"])
|
|
106
|
+
event_section += nodes.title(text=event_type, ids=[f"title-{event_type}"])
|
|
107
|
+
event_section += nodes.paragraph(text=f"Description: "
|
|
108
|
+
f"{event_description}")
|
|
109
|
+
event_section += nodes.paragraph("", "Signal name: ", event_name_literal)
|
|
110
|
+
if event_key_field:
|
|
111
|
+
event_section += nodes.paragraph(
|
|
112
|
+
"",
|
|
113
|
+
"Event key field: ",
|
|
114
|
+
event_key_literal
|
|
115
|
+
)
|
|
116
|
+
event_section += nodes.paragraph("", "Event data: ", event_data_literal)
|
|
117
|
+
event_section += nodes.paragraph(
|
|
118
|
+
"",
|
|
119
|
+
"Defined at: ",
|
|
120
|
+
nodes.reference(
|
|
121
|
+
text="{} (line {})".format(
|
|
122
|
+
event["filename"], event["line_number"]
|
|
123
|
+
),
|
|
124
|
+
refuri="{}/blob/{}/{}#L{}".format(
|
|
125
|
+
self.env.config.openedxevents_repo_url,
|
|
126
|
+
self.env.config.openedxevents_repo_version,
|
|
127
|
+
event["filename"],
|
|
128
|
+
event["line_number"],
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
ids=[f"definition-{event_name}"],
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if event_trigger_repository:
|
|
135
|
+
event_trigger_repository = event_trigger_repository.split(" ")
|
|
136
|
+
event_section += nodes.paragraph(text="Triggered by:", ids=[f"triggers-{event_name}"])
|
|
137
|
+
triggers_bullet_list = nodes.bullet_list()
|
|
138
|
+
for repository in event_trigger_repository:
|
|
139
|
+
search_url = f"https://github.com/search?q=repo:{repository}+{event_name}.send_event&type=code"
|
|
140
|
+
triggers_bullet_list += nodes.list_item(
|
|
141
|
+
"",
|
|
142
|
+
nodes.paragraph(
|
|
143
|
+
"",
|
|
144
|
+
"",
|
|
145
|
+
nodes.reference(
|
|
146
|
+
text=repository,
|
|
147
|
+
refuri=search_url,
|
|
148
|
+
),
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
event_section += triggers_bullet_list
|
|
153
|
+
|
|
154
|
+
if event.get(".. event_warning:") not in (None, "None", "n/a", "N/A"):
|
|
155
|
+
event_section += nodes.warning(
|
|
156
|
+
"", nodes.paragraph("", event[".. event_warning:"]), ids=[f"warning-{event_name}"]
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
subject_header += event_section
|
|
160
|
+
|
|
161
|
+
if domain_header:
|
|
162
|
+
yield domain_header
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def setup(app):
|
|
166
|
+
"""
|
|
167
|
+
Declare the Sphinx extension.
|
|
168
|
+
"""
|
|
169
|
+
app.add_config_value(
|
|
170
|
+
"openedxevents_source_path",
|
|
171
|
+
os.path.abspath(".."),
|
|
172
|
+
"env",
|
|
173
|
+
)
|
|
174
|
+
app.add_config_value("openedxevents_repo_url", "", "env")
|
|
175
|
+
app.add_config_value("openedxevents_repo_version", "main", "env")
|
|
176
|
+
app.add_directive("openedxevents", OpenedxEvents)
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
"version": "0.1",
|
|
180
|
+
"parallel_read_safe": True,
|
|
181
|
+
"parallel_write_safe": True,
|
|
182
|
+
}
|