code-annotations 2.2.0__tar.gz → 2.3.2__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 (57) hide show
  1. {code_annotations-2.2.0 → code_annotations-2.3.2}/CHANGELOG.rst +5 -0
  2. {code_annotations-2.2.0 → code_annotations-2.3.2}/MANIFEST.in +1 -1
  3. {code_annotations-2.2.0 → code_annotations-2.3.2}/PKG-INFO +11 -32
  4. {code_annotations-2.2.0 → code_annotations-2.3.2}/README.rst +0 -28
  5. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/__init__.py +1 -1
  6. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/base.py +14 -4
  7. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/cli.py +6 -1
  8. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/generate_docs.py +20 -10
  9. code_annotations-2.3.2/code_annotations/report_templates/html/annotation.tpl +14 -0
  10. code_annotations-2.3.2/code_annotations/report_templates/html/annotation_data.tpl +8 -0
  11. code_annotations-2.3.2/code_annotations/report_templates/html/annotation_list.tpl +27 -0
  12. code_annotations-2.3.2/code_annotations/report_templates/html/base.tpl +92 -0
  13. code_annotations-2.3.2/code_annotations/report_templates/rst/annotation.tpl +13 -0
  14. code_annotations-2.3.2/code_annotations/report_templates/rst/annotation_data.tpl +8 -0
  15. code_annotations-2.3.2/code_annotations/report_templates/rst/annotation_group.tpl +2 -0
  16. code_annotations-2.3.2/code_annotations/report_templates/rst/annotation_list.tpl +34 -0
  17. code_annotations-2.3.2/code_annotations/report_templates/rst/base.tpl +41 -0
  18. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations.egg-info/PKG-INFO +11 -32
  19. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations.egg-info/SOURCES.txt +9 -0
  20. code_annotations-2.3.2/setup.py +111 -0
  21. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_generate_docs.py +35 -1
  22. code_annotations-2.2.0/setup.py +0 -110
  23. {code_annotations-2.2.0 → code_annotations-2.3.2}/LICENSE.txt +0 -0
  24. {code_annotations-2.2.0 → code_annotations-2.3.2}/NOTICE.txt +0 -0
  25. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/annotation_errors.py +0 -0
  26. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/config/__init__.py +0 -0
  27. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/config/feature_toggle_annotations.yaml +0 -0
  28. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/config/openedx_events_annotations.yaml +0 -0
  29. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/config/setting_annotations.yaml +0 -0
  30. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/sphinx/extensions/__init__.py +0 -0
  31. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/sphinx/extensions/base.py +0 -0
  32. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/sphinx/extensions/featuretoggles.py +0 -0
  33. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/sphinx/extensions/openedx_events.py +0 -0
  34. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/contrib/sphinx/extensions/settings.py +0 -0
  35. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/exceptions.py +0 -0
  36. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/extensions/__init__.py +0 -0
  37. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/extensions/base.py +0 -0
  38. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/extensions/javascript.py +0 -0
  39. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/extensions/python.py +0 -0
  40. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/find_django.py +0 -0
  41. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/find_static.py +0 -0
  42. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations/helpers.py +0 -0
  43. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations.egg-info/dependency_links.txt +0 -0
  44. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations.egg-info/entry_points.txt +0 -0
  45. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations.egg-info/not-zip-safe +0 -0
  46. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations.egg-info/requires.txt +2 -2
  47. {code_annotations-2.2.0 → code_annotations-2.3.2}/code_annotations.egg-info/top_level.txt +0 -0
  48. {code_annotations-2.2.0 → code_annotations-2.3.2}/requirements/base.in +0 -0
  49. {code_annotations-2.2.0 → code_annotations-2.3.2}/setup.cfg +0 -0
  50. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_base.py +0 -0
  51. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_django_coverage.py +0 -0
  52. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_django_generate_safelist.py +0 -0
  53. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_django_list_local_models.py +0 -0
  54. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_find_django.py +0 -0
  55. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_find_static.py +0 -0
  56. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_search.py +0 -0
  57. {code_annotations-2.2.0 → code_annotations-2.3.2}/tests/test_sphinx.py +0 -0
@@ -14,6 +14,11 @@ Change Log
14
14
  Unreleased
15
15
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16
16
 
17
+ [2.3.2] - 2026-03-02
18
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19
+
20
+ * Dot release to capture many requirements updates.
21
+
17
22
  [2.2.0] - 2025-01-15
18
23
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19
24
 
@@ -4,4 +4,4 @@ include CONTRIBUTING.rst
4
4
  include LICENSE.txt
5
5
  include README.rst
6
6
  include requirements/base.in
7
- recursive-include code_annotations *.html *.png *.gif *js *.css *jpg *jpeg *svg *py *.yaml *.yml
7
+ recursive-include code_annotations *.tpl *.html *.png *.gif *js *.css *jpg *jpeg *svg *py *.yaml *.yml
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: code-annotations
3
- Version: 2.2.0
3
+ Version: 2.3.2
4
4
  Summary: Extensible tools for parsing annotations in codebases
5
5
  Home-page: https://github.com/openedx/code-annotations
6
6
  Author: edX
@@ -10,6 +10,7 @@ Keywords: edx pii code annotations
10
10
  Classifier: Development Status :: 3 - Alpha
11
11
  Classifier: Framework :: Django
12
12
  Classifier: Framework :: Django :: 4.2
13
+ Classifier: Framework :: Django :: 5.2
13
14
  Classifier: Intended Audience :: Developers
14
15
  Classifier: License :: OSI Approved :: Apache Software License
15
16
  Classifier: Natural Language :: English
@@ -21,11 +22,11 @@ Requires-Python: >=3.11
21
22
  Description-Content-Type: text/x-rst
22
23
  License-File: LICENSE.txt
23
24
  License-File: NOTICE.txt
24
- Requires-Dist: pyyaml
25
25
  Requires-Dist: click
26
+ Requires-Dist: python-slugify
26
27
  Requires-Dist: Jinja2
28
+ Requires-Dist: pyyaml
27
29
  Requires-Dist: stevedore
28
- Requires-Dist: python-slugify
29
30
  Provides-Extra: django
30
31
  Requires-Dist: Django>=4.2; extra == "django"
31
32
  Dynamic: author
@@ -36,6 +37,7 @@ Dynamic: description-content-type
36
37
  Dynamic: home-page
37
38
  Dynamic: keywords
38
39
  Dynamic: license
40
+ Dynamic: license-file
39
41
  Dynamic: provides-extra
40
42
  Dynamic: requires-dist
41
43
  Dynamic: requires-python
@@ -44,9 +46,6 @@ Dynamic: summary
44
46
  code-annotations
45
47
  =============================
46
48
 
47
- |pypi-badge| |CI| |codecov-badge| |doc-badge| |pyversions-badge|
48
- |license-badge|
49
-
50
49
  Extensible tools for parsing annotations in codebases
51
50
 
52
51
  Overview
@@ -100,31 +99,6 @@ refer to this `list of resources`_ if you need any assistance.
100
99
  .. _list of resources: https://open.edx.org/getting-help
101
100
 
102
101
 
103
- .. |pypi-badge| image:: https://img.shields.io/pypi/v/code-annotations.svg
104
- :target: https://pypi.python.org/pypi/code-annotations/
105
- :alt: PyPI
106
-
107
- .. |CI| image:: https://github.com/openedx/code-annotations/workflows/Python%20CI/badge.svg?branch=master
108
- :target: https://github.com/openedx/code-annotations/actions?query=workflow%3A%22Python+CI%22
109
- :alt: CI
110
-
111
- .. |codecov-badge| image:: http://codecov.io/github/edx/code-annotations/coverage.svg?branch=master
112
- :target: http://codecov.io/github/edx/code-annotations?branch=master
113
- :alt: Codecov
114
-
115
- .. |doc-badge| image:: https://readthedocs.org/projects/code-annotations/badge/?version=latest
116
- :target: http://code-annotations.readthedocs.io/en/latest/
117
- :alt: Documentation
118
-
119
- .. |pyversions-badge| image:: https://img.shields.io/pypi/pyversions/code-annotations.svg
120
- :target: https://pypi.python.org/pypi/code-annotations/
121
- :alt: Supported Python versions
122
-
123
- .. |license-badge| image:: https://img.shields.io/github/license/edx/code-annotations.svg
124
- :target: https://github.com/openedx/code-annotations/blob/master/LICENSE.txt
125
- :alt: License
126
-
127
-
128
102
  Change Log
129
103
  ----------
130
104
 
@@ -141,6 +115,11 @@ Change Log
141
115
  Unreleased
142
116
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
143
117
 
118
+ [2.3.2] - 2026-03-02
119
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
120
+
121
+ * Dot release to capture many requirements updates.
122
+
144
123
  [2.2.0] - 2025-01-15
145
124
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
146
125
 
@@ -1,9 +1,6 @@
1
1
  code-annotations
2
2
  =============================
3
3
 
4
- |pypi-badge| |CI| |codecov-badge| |doc-badge| |pyversions-badge|
5
- |license-badge|
6
-
7
4
  Extensible tools for parsing annotations in codebases
8
5
 
9
6
  Overview
@@ -55,28 +52,3 @@ Have a question about this repository, or about Open edX in general? Please
55
52
  refer to this `list of resources`_ if you need any assistance.
56
53
 
57
54
  .. _list of resources: https://open.edx.org/getting-help
58
-
59
-
60
- .. |pypi-badge| image:: https://img.shields.io/pypi/v/code-annotations.svg
61
- :target: https://pypi.python.org/pypi/code-annotations/
62
- :alt: PyPI
63
-
64
- .. |CI| image:: https://github.com/openedx/code-annotations/workflows/Python%20CI/badge.svg?branch=master
65
- :target: https://github.com/openedx/code-annotations/actions?query=workflow%3A%22Python+CI%22
66
- :alt: CI
67
-
68
- .. |codecov-badge| image:: http://codecov.io/github/edx/code-annotations/coverage.svg?branch=master
69
- :target: http://codecov.io/github/edx/code-annotations?branch=master
70
- :alt: Codecov
71
-
72
- .. |doc-badge| image:: https://readthedocs.org/projects/code-annotations/badge/?version=latest
73
- :target: http://code-annotations.readthedocs.io/en/latest/
74
- :alt: Documentation
75
-
76
- .. |pyversions-badge| image:: https://img.shields.io/pypi/pyversions/code-annotations.svg
77
- :target: https://pypi.python.org/pypi/code-annotations/
78
- :alt: Supported Python versions
79
-
80
- .. |license-badge| image:: https://img.shields.io/github/license/edx/code-annotations.svg
81
- :target: https://github.com/openedx/code-annotations/blob/master/LICENSE.txt
82
- :alt: License
@@ -2,4 +2,4 @@
2
2
  Extensible tools for parsing annotations in codebases.
3
3
  """
4
4
 
5
- __version__ = '2.2.0'
5
+ __version__ = "2.3.2"
@@ -14,6 +14,9 @@ from code_annotations import annotation_errors
14
14
  from code_annotations.exceptions import ConfigurationException
15
15
  from code_annotations.helpers import VerboseEcho
16
16
 
17
+ PACKAGE_DIR = os.path.dirname(os.path.realpath(__file__))
18
+ DEFAULT_TEMPLATE_DIR = os.path.join(PACKAGE_DIR, "report_templates")
19
+
17
20
 
18
21
  class AnnotationConfig:
19
22
  """
@@ -58,10 +61,17 @@ class AnnotationConfig:
58
61
  self.echo(f"Configured for source path: {self.source_path}")
59
62
 
60
63
  self._configure_coverage(raw_config.get('coverage_target', None))
61
- self.report_template_dir = raw_config.get('report_template_dir')
62
- self.rendered_report_dir = raw_config.get('rendered_report_dir')
63
- self.rendered_report_file_extension = raw_config.get('rendered_report_file_extension')
64
- self.rendered_report_source_link_prefix = raw_config.get('rendered_report_source_link_prefix')
64
+
65
+ self.rendered_report_format = raw_config.get('rendered_report_format', 'rst')
66
+ self.report_template_dir = raw_config.get(
67
+ 'report_template_dir',
68
+ os.path.join(DEFAULT_TEMPLATE_DIR, self.rendered_report_format)
69
+ )
70
+ self.rendered_report_dir = raw_config.get('rendered_report_dir', 'annotation_reports')
71
+ self.rendered_report_source_link_prefix = raw_config.get('rendered_report_source_link_prefix', None)
72
+ self.trim_filename_prefixes = raw_config.get('trim_filename_prefixes', [])
73
+ self.third_party_package_location = raw_config.get('third_party_package_location', "site-packages")
74
+ self.rendered_report_file_extension = f".{self.rendered_report_format}"
65
75
 
66
76
  self._configure_annotations(raw_config)
67
77
  self._configure_extensions()
@@ -241,10 +241,15 @@ def generate_docs(config_file, verbosity, report_files):
241
241
  try:
242
242
  config = AnnotationConfig(config_file, verbosity)
243
243
 
244
+ if not report_files:
245
+ raise ConfigurationException(
246
+ "No report files provided. Please provide one or more report files to generate docs from."
247
+ )
248
+
244
249
  for key in (
245
250
  "report_template_dir",
246
251
  "rendered_report_dir",
247
- "rendered_report_file_extension",
252
+ "rendered_report_format",
248
253
  "rendered_report_source_link_prefix",
249
254
  ):
250
255
  if not getattr(config, key):
@@ -27,8 +27,7 @@ class ReportRenderer:
27
27
  self.config = config
28
28
  self.echo = self.config.echo
29
29
  self.report_files = report_files
30
- self.create_time = datetime.datetime.utcnow().isoformat()
31
-
30
+ self.create_time = datetime.datetime.now(tz=datetime.timezone.utc)
32
31
  self.full_report = self._aggregate_reports()
33
32
 
34
33
  self.jinja_environment = jinja2.Environment(
@@ -62,6 +61,12 @@ class ReportRenderer:
62
61
  loaded_report = yaml.safe_load(report_file)
63
62
 
64
63
  for filename in loaded_report:
64
+ trimmed_filename = filename
65
+ for prefix in self.config.trim_filename_prefixes:
66
+ if filename.startswith(prefix):
67
+ trimmed_filename = filename[len(prefix):]
68
+ break
69
+
65
70
  if filename in report:
66
71
  for loaded_annotation in loaded_report[filename]:
67
72
  found = False
@@ -74,9 +79,9 @@ class ReportRenderer:
74
79
  break
75
80
 
76
81
  if not found:
77
- report[filename].append(loaded_annotation)
82
+ report[trimmed_filename].append(loaded_annotation)
78
83
  else:
79
- report[filename] = loaded_report[filename]
84
+ report[trimmed_filename] = loaded_report[filename]
80
85
 
81
86
  def _aggregate_reports(self):
82
87
  """
@@ -91,11 +96,12 @@ class ReportRenderer:
91
96
 
92
97
  return report
93
98
 
94
- def _write_doc_file(self, doc_filename, doc_data):
99
+ def _write_doc_file(self, doc_title, doc_filename, doc_data):
95
100
  """
96
101
  Write out a single report file with the given data. This is rendered using the configured top level template.
97
102
 
98
103
  Args:
104
+ doc_title: Title to use for the document.
99
105
  doc_filename: Filename to write to.
100
106
  doc_data: Dict of reporting data to use, in the {'file name': [list, of, annotations,]} style.
101
107
  """
@@ -110,14 +116,16 @@ class ReportRenderer:
110
116
 
111
117
  with open(full_doc_filename, 'w') as output:
112
118
  output.write(self.top_level_template.render(
119
+ doc_title=doc_title,
113
120
  create_time=self.create_time,
114
121
  report=doc_data,
115
122
  all_choices=self.all_choices,
116
123
  all_annotations=self.config.annotation_tokens,
117
124
  group_mapping=self.group_mapping,
118
125
  slugify=slugify,
119
- source_link_prefix=self.config.rendered_report_source_link_prefix)
120
- )
126
+ source_link_prefix=self.config.rendered_report_source_link_prefix,
127
+ third_party_package_location=self.config.third_party_package_location,
128
+ ))
121
129
 
122
130
  def _generate_per_choice_docs(self):
123
131
  """
@@ -130,7 +138,7 @@ class ReportRenderer:
130
138
  if isinstance(annotation['annotation_data'], list) and choice in annotation['annotation_data']:
131
139
  choice_report[filename].append(annotation)
132
140
 
133
- self._write_doc_file(f'choice_{choice}', choice_report)
141
+ self._write_doc_file(f"All References to Choice '{choice}'", f'choice_{choice}', choice_report)
134
142
 
135
143
  def _generate_per_annotation_docs(self):
136
144
  """
@@ -143,13 +151,15 @@ class ReportRenderer:
143
151
  if report_annotation['annotation_token'] == annotation:
144
152
  annotation_report[filename].append(report_annotation)
145
153
 
146
- self._write_doc_file(f'annotation_{annotation}', annotation_report)
154
+ self._write_doc_file(
155
+ f"All References to Annotation '{annotation}'", f'annotation_{annotation}', annotation_report
156
+ )
147
157
 
148
158
  def render(self):
149
159
  """
150
160
  Perform the rendering of all documentation using the configured Jinja2 templates.
151
161
  """
152
162
  # Generate the top level list of all annotations
153
- self._write_doc_file('index', self.full_report)
163
+ self._write_doc_file("Complete Annotation List", 'index', self.full_report)
154
164
  self._generate_per_choice_docs()
155
165
  self._generate_per_annotation_docs()
@@ -0,0 +1,14 @@
1
+ {% if is_third_party %}
2
+ {# no links for third party code since we don't know where to link to #}
3
+ {% if annotation.extra and annotation.extra.object_id %}
4
+ {{ annotation.extra.object_id }} {% if annotation.line_number > 0 %}line {{ annotation.line_number }}{% endif %}: {{ annotation.annotation_token }} {% include "annotation_data.tpl" %}
5
+ {% else %}
6
+ {% if loop.changed(annotation.line_number)%}{{ filename }}:{{ annotation.line_number }}<br />{% endif %}:
7
+ {{ annotation.annotation_token }} {% include "annotation_data.tpl" %}
8
+ {% endif %}
9
+
10
+ {% elif annotation.extra and annotation.extra.object_id %}
11
+ <a href="{{ source_link_prefix }}{{ filename }}#L{{ annotation.line_number }}" target="_blank">{{ annotation.extra.object_id }} {% if annotation.line_number > 0 %}line {{ annotation.line_number }}{% endif %}</a>: {{ annotation.annotation_token }} {% include "annotation_data.tpl" %}
12
+ {% else %}
13
+ <a href="{{ source_link_prefix }}{{ filename }}#L{{ annotation.line_number }}" target="_blank">`{{ filename }}:{{ annotation.line_number }}: {{ annotation.annotation_token }} {% include "annotation_data.tpl" %}
14
+ {% endif %}
@@ -0,0 +1,8 @@
1
+ {% if annotation.annotation_data is sequence and annotation.annotation_data is not string %}
2
+ {% for a in annotation.annotation_data %}
3
+ <a href="choice-{{ slugify(a) }}.html">{{ a }}</a>{% if not loop.last %}, {% endif %}
4
+ {% endfor %}
5
+
6
+ {% else %}
7
+ {{ annotation.annotation_data }}
8
+ {% endif %}
@@ -0,0 +1,27 @@
1
+ {% extends "base.tpl" %}
2
+ {% block content %}
3
+ Annotations found in {{ report|length }} files.
4
+
5
+ {% for filename in report %}
6
+ {% set is_third_party = third_party_package_location in filename %}
7
+
8
+ <h2 id="file-{{ slugify(filename) }}">{{ filename }}</h2>
9
+ <div class="file-annotations">
10
+ {{ report[filename]|length }} annotations {% if is_third_party %}(installed package){% endif %}<br />
11
+ </div>
12
+
13
+ {% for annotation in report[filename] %}
14
+ {% if loop.changed(annotation.report_group_id) %}
15
+ {% if not loop.first %}</ul></div>{% endif %}
16
+ <div class="group-annotations"><ul>
17
+ {% endif %}
18
+ <li>{% include 'annotation.tpl' %}</li>
19
+ {% if loop.last %}
20
+ </ul></div>
21
+ {% endif %}
22
+ {% endfor %}
23
+
24
+
25
+ {% endfor %}
26
+
27
+ {% endblock %}
@@ -0,0 +1,92 @@
1
+ <html>
2
+ <head>
3
+ <title>{{ doc_title }}</title>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5
+ <style>
6
+ body {
7
+ font-family: 'Trebuchet MS', sans-serif;
8
+ }
9
+
10
+ .title {
11
+ text-align: center;
12
+ }
13
+
14
+ .table {
15
+ display: table;
16
+ border-spacing: 12px;
17
+ }
18
+
19
+ .row {
20
+ display: table-row;
21
+ margin-bottom: 0;
22
+ margin-top: 0;
23
+ width: 100%;
24
+ }
25
+
26
+ .cell1 {
27
+ display: table-cell;
28
+ width: 20%;
29
+ margin-right: 1%;
30
+ border: 1px solid #ccc;
31
+ margin 12px;
32
+ background-color: #ffffee;
33
+ }
34
+
35
+ .cell2 {
36
+ display: table-cell;
37
+ width: 79%;
38
+ margin-right: 1%;
39
+ margin 12px;
40
+ }
41
+
42
+ .group-annotations {
43
+ border: 1px solid #ccc;
44
+ margin: 10px;
45
+ }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <h1 class="title">{{ doc_title }}</h1>
50
+
51
+ <div class="table">
52
+ <div class="row">
53
+ <div class="cell1">
54
+ <h3><a href="index.html">Home</a></h3>
55
+
56
+ <h3>Annotations</h3>
57
+
58
+ <ul>
59
+ {% for a in all_annotations %}
60
+ <li><a href="annotation-{{ slugify(a) }}.html">annotation_{{ slugify(a) }}</a></li>
61
+ {% endfor %}
62
+ </ul>
63
+
64
+ <h3>Choices</h3>
65
+
66
+ <ul>
67
+ {% for choice in all_choices %}
68
+ <li><a href="choice-{{ slugify(choice) }}.html">choice_{{ slugify(choice) }}</a></li>
69
+ {% endfor %}
70
+ </ul>
71
+ </div>
72
+ <div class="cell2">
73
+ <h2>Files in this page</h2>
74
+ <ul>
75
+ {% for filename in report %}
76
+ <li><a href="#file-{{ slugify(filename) }}">{{ filename }}</a></li>
77
+ {% endfor %}
78
+ </ul>
79
+
80
+ {% block content %}{% endblock %}
81
+ </div>
82
+ </div>
83
+ </div>
84
+ {% block footer %}
85
+ <div class="footer">
86
+ <br /><br />
87
+ <hr />
88
+ Built at {{ create_time.strftime('%Y-%m-%d %H:%M:%S %Z') }}
89
+ </div>
90
+ {% endblock %}
91
+ </body>
92
+ </html>
@@ -0,0 +1,13 @@
1
+ {% if is_third_party %}
2
+ {# no links for third party code since we don't know where to link to #}
3
+ {% if annotation.extra and annotation.extra.object_id %}
4
+ {{ annotation.extra.object_id }} {% if annotation.line_number > 0 %}line {{ annotation.line_number }}{% endif %}: {{ annotation.annotation_token }} {% include "annotation_data.tpl" %}
5
+ {% else %}
6
+ {{ filename }}:{{ annotation.line_number }}: {{ annotation.annotation_token }} {% include "annotation_data.tpl" %}
7
+ {% endif %}
8
+
9
+ {% elif annotation.extra and annotation.extra.object_id %}
10
+ `{{ annotation.extra.object_id }} {% if annotation.line_number > 0 %}line {{ annotation.line_number }}{% endif %} <{{ source_link_prefix }}{{ filename }}#L{{ annotation.line_number }}>`_: {{ annotation.annotation_token }} {% include "annotation_data.tpl" %}
11
+ {% else %}
12
+ `{{ filename }}:{{ annotation.line_number }} <{{ source_link_prefix }}{{ filename }}#L{{ annotation.line_number }}>`_: {{ annotation.annotation_token }} {% include "annotation_data.tpl" %}
13
+ {% endif %}
@@ -0,0 +1,8 @@
1
+ {% if annotation.annotation_data is sequence and annotation.annotation_data is not string %}
2
+ {% for a in annotation.annotation_data %}
3
+ choice_{{ slugify(a) }}_{% if not loop.last %}, {% endif %}
4
+ {% endfor %}
5
+
6
+ {% else %}
7
+ {{ annotation.annotation_data }}
8
+ {% endif %}
@@ -0,0 +1,2 @@
1
+ .. _index.rst#{{ slugify(filename + '-' + annotation.report_group_id |string) }}:
2
+ .. admonition:: {{ group_mapping[annotation.annotation_token] or annotation.annotation_token }}
@@ -0,0 +1,34 @@
1
+ {% extends "base.tpl" %}
2
+ {% block content %}
3
+ Annotations found in {{ report|length }} files.
4
+
5
+ {% for filename in report %}
6
+ {% set is_third_party = third_party_package_location in filename %}
7
+
8
+ {{ filename }}
9
+ {{ "-" * filename|length }}
10
+
11
+ .. note:: {{ report[filename]|length }} annotations {% if is_third_party %}(installed package){% endif %}
12
+
13
+
14
+ {% for annotation in report[filename] %}
15
+ {% if annotation.report_group_id %}
16
+ {% if loop.changed(annotation.report_group_id) %}
17
+ {% include 'annotation_group.tpl' %}
18
+
19
+
20
+ {% endif %}
21
+ * {% include 'annotation.tpl' %}
22
+
23
+ {% else %}
24
+ {% if loop.changed(annotation.report_group_id) %}
25
+
26
+ {% endif %}
27
+ * {% include 'annotation.tpl' %}
28
+
29
+ {% endif %}
30
+ {% endfor %}
31
+
32
+ {% endfor %}
33
+
34
+ {% endblock %}
@@ -0,0 +1,41 @@
1
+ {{ "#" * doc_title|length }}
2
+ {{ doc_title }}
3
+ {{ "#" * doc_title|length }}
4
+
5
+ .. sidebar:: Table of Contents
6
+
7
+ `Home <index.rst>`_
8
+
9
+ Annotations
10
+
11
+ {% for a in all_annotations %}
12
+ * annotation_{{ slugify(a) }}_
13
+ {% endfor %}
14
+
15
+ Choices
16
+
17
+ {% for choice in all_choices %}
18
+ * choice_{{ slugify(choice) }}_
19
+ {% endfor %}
20
+
21
+
22
+ .. contents::
23
+
24
+ {% block content %}{% endblock %}
25
+
26
+
27
+ {# backlinks for all choices #}
28
+ {% for choice in all_choices %}
29
+ .. _choice_{{ slugify(choice) }}: {{ slugify('choice_' + choice) + '.rst' }}
30
+ {% endfor %}
31
+
32
+
33
+ {# backlinks for all annotations #}
34
+ {% for annotation in all_annotations %}
35
+ .. _annotation_{{ slugify(annotation) }}: {{ slugify('annotation_' + annotation) + '.rst' }}
36
+ {% endfor %}
37
+
38
+
39
+ {% block footer %}
40
+ Built at {{ create_time.strftime('%Y-%m-%d %H:%M:%S %Z') }}
41
+ {% endblock %}
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: code-annotations
3
- Version: 2.2.0
3
+ Version: 2.3.2
4
4
  Summary: Extensible tools for parsing annotations in codebases
5
5
  Home-page: https://github.com/openedx/code-annotations
6
6
  Author: edX
@@ -10,6 +10,7 @@ Keywords: edx pii code annotations
10
10
  Classifier: Development Status :: 3 - Alpha
11
11
  Classifier: Framework :: Django
12
12
  Classifier: Framework :: Django :: 4.2
13
+ Classifier: Framework :: Django :: 5.2
13
14
  Classifier: Intended Audience :: Developers
14
15
  Classifier: License :: OSI Approved :: Apache Software License
15
16
  Classifier: Natural Language :: English
@@ -21,11 +22,11 @@ Requires-Python: >=3.11
21
22
  Description-Content-Type: text/x-rst
22
23
  License-File: LICENSE.txt
23
24
  License-File: NOTICE.txt
24
- Requires-Dist: pyyaml
25
25
  Requires-Dist: click
26
+ Requires-Dist: python-slugify
26
27
  Requires-Dist: Jinja2
28
+ Requires-Dist: pyyaml
27
29
  Requires-Dist: stevedore
28
- Requires-Dist: python-slugify
29
30
  Provides-Extra: django
30
31
  Requires-Dist: Django>=4.2; extra == "django"
31
32
  Dynamic: author
@@ -36,6 +37,7 @@ Dynamic: description-content-type
36
37
  Dynamic: home-page
37
38
  Dynamic: keywords
38
39
  Dynamic: license
40
+ Dynamic: license-file
39
41
  Dynamic: provides-extra
40
42
  Dynamic: requires-dist
41
43
  Dynamic: requires-python
@@ -44,9 +46,6 @@ Dynamic: summary
44
46
  code-annotations
45
47
  =============================
46
48
 
47
- |pypi-badge| |CI| |codecov-badge| |doc-badge| |pyversions-badge|
48
- |license-badge|
49
-
50
49
  Extensible tools for parsing annotations in codebases
51
50
 
52
51
  Overview
@@ -100,31 +99,6 @@ refer to this `list of resources`_ if you need any assistance.
100
99
  .. _list of resources: https://open.edx.org/getting-help
101
100
 
102
101
 
103
- .. |pypi-badge| image:: https://img.shields.io/pypi/v/code-annotations.svg
104
- :target: https://pypi.python.org/pypi/code-annotations/
105
- :alt: PyPI
106
-
107
- .. |CI| image:: https://github.com/openedx/code-annotations/workflows/Python%20CI/badge.svg?branch=master
108
- :target: https://github.com/openedx/code-annotations/actions?query=workflow%3A%22Python+CI%22
109
- :alt: CI
110
-
111
- .. |codecov-badge| image:: http://codecov.io/github/edx/code-annotations/coverage.svg?branch=master
112
- :target: http://codecov.io/github/edx/code-annotations?branch=master
113
- :alt: Codecov
114
-
115
- .. |doc-badge| image:: https://readthedocs.org/projects/code-annotations/badge/?version=latest
116
- :target: http://code-annotations.readthedocs.io/en/latest/
117
- :alt: Documentation
118
-
119
- .. |pyversions-badge| image:: https://img.shields.io/pypi/pyversions/code-annotations.svg
120
- :target: https://pypi.python.org/pypi/code-annotations/
121
- :alt: Supported Python versions
122
-
123
- .. |license-badge| image:: https://img.shields.io/github/license/edx/code-annotations.svg
124
- :target: https://github.com/openedx/code-annotations/blob/master/LICENSE.txt
125
- :alt: License
126
-
127
-
128
102
  Change Log
129
103
  ----------
130
104
 
@@ -141,6 +115,11 @@ Change Log
141
115
  Unreleased
142
116
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
143
117
 
118
+ [2.3.2] - 2026-03-02
119
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
120
+
121
+ * Dot release to capture many requirements updates.
122
+
144
123
  [2.2.0] - 2025-01-15
145
124
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
146
125
 
@@ -34,6 +34,15 @@ code_annotations/extensions/__init__.py
34
34
  code_annotations/extensions/base.py
35
35
  code_annotations/extensions/javascript.py
36
36
  code_annotations/extensions/python.py
37
+ code_annotations/report_templates/html/annotation.tpl
38
+ code_annotations/report_templates/html/annotation_data.tpl
39
+ code_annotations/report_templates/html/annotation_list.tpl
40
+ code_annotations/report_templates/html/base.tpl
41
+ code_annotations/report_templates/rst/annotation.tpl
42
+ code_annotations/report_templates/rst/annotation_data.tpl
43
+ code_annotations/report_templates/rst/annotation_group.tpl
44
+ code_annotations/report_templates/rst/annotation_list.tpl
45
+ code_annotations/report_templates/rst/base.tpl
37
46
  requirements/base.in
38
47
  tests/test_base.py
39
48
  tests/test_django_coverage.py
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Package metadata for code_annotations.
4
+ """
5
+
6
+ import os
7
+ import re
8
+ import sys
9
+
10
+ from setuptools import setup
11
+
12
+
13
+ def get_version(*file_paths):
14
+ """
15
+ Extract the version string from the file at the given relative path fragments.
16
+ """
17
+ filename = os.path.join(os.path.dirname(__file__), *file_paths)
18
+ version_file = open(filename).read()
19
+ version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
20
+ if version_match:
21
+ return version_match.group(1)
22
+ raise RuntimeError("Unable to find version string.")
23
+
24
+
25
+ def load_requirements(*requirements_paths):
26
+ """
27
+ Load all requirements from the specified requirements files.
28
+
29
+ Returns:
30
+ list: Requirements file relative path strings
31
+ """
32
+ requirements = set()
33
+ for path in requirements_paths:
34
+ requirements.update(
35
+ line.split("#")[0].strip()
36
+ for line in open(path).readlines()
37
+ if is_requirement(line.strip())
38
+ )
39
+ return list(requirements)
40
+
41
+
42
+ def is_requirement(line):
43
+ """
44
+ Return True if the requirement line is a package requirement.
45
+
46
+ Returns:
47
+ bool: True if the line is not blank, a comment, a URL, or an included file
48
+ """
49
+ return not (
50
+ line == ""
51
+ or line.startswith("-r")
52
+ or line.startswith("#")
53
+ or line.startswith("-e")
54
+ or line.startswith("git+")
55
+ or line.startswith("-c")
56
+ )
57
+
58
+
59
+ VERSION = get_version("code_annotations", "__init__.py")
60
+
61
+ if sys.argv[-1] == "tag":
62
+ print("Tagging the version on github:")
63
+ os.system("git tag -a %s -m 'version %s'" % (VERSION, VERSION))
64
+ os.system("git push --tags")
65
+ sys.exit()
66
+
67
+ README = open(os.path.join(os.path.dirname(__file__), "README.rst")).read()
68
+ CHANGELOG = open(os.path.join(os.path.dirname(__file__), "CHANGELOG.rst")).read()
69
+
70
+ setup(
71
+ name="code-annotations",
72
+ version=VERSION,
73
+ description="""Extensible tools for parsing annotations in codebases""",
74
+ long_description=README + "\n\n" + CHANGELOG,
75
+ long_description_content_type="text/x-rst",
76
+ author="edX",
77
+ author_email="oscm@edx.org",
78
+ url="https://github.com/openedx/code-annotations",
79
+ packages=[
80
+ "code_annotations",
81
+ ],
82
+ entry_points={
83
+ "console_scripts": [
84
+ "code_annotations = code_annotations.cli:entry_point",
85
+ ],
86
+ "annotation_finder.searchers": [
87
+ "javascript = code_annotations.extensions.javascript:JavascriptAnnotationExtension",
88
+ "python = code_annotations.extensions.python:PythonAnnotationExtension",
89
+ ],
90
+ },
91
+ include_package_data=True,
92
+ install_requires=load_requirements("requirements/base.in"),
93
+ extras_require={"django": ["Django>=4.2"]},
94
+ license="Apache Software License 2.0",
95
+ zip_safe=False,
96
+ keywords="edx pii code annotations",
97
+ python_requires=">=3.11",
98
+ classifiers=[
99
+ "Development Status :: 3 - Alpha",
100
+ "Framework :: Django",
101
+ "Framework :: Django :: 4.2",
102
+ "Framework :: Django :: 5.2",
103
+ "Intended Audience :: Developers",
104
+ "License :: OSI Approved :: Apache Software License",
105
+ "Natural Language :: English",
106
+ "Programming Language :: Python",
107
+ "Programming Language :: Python :: 3",
108
+ "Programming Language :: Python :: 3.11",
109
+ "Programming Language :: Python :: 3.12",
110
+ ],
111
+ )
@@ -49,6 +49,40 @@ def test_generate_report_simple():
49
49
  assert os.path.exists(created_doc)
50
50
 
51
51
 
52
+ def test_generate_report_simple_html():
53
+ find_result = call_script(
54
+ (
55
+ 'static_find_annotations',
56
+ '--config_file',
57
+ 'tests/test_configurations/.annotations_test_python_only',
58
+ '--source_path=tests/extensions/python_test_files/simple_success.pyt',
59
+ '--no_lint',
60
+ ),
61
+ delete_test_reports=False)
62
+
63
+ assert find_result.exit_code == EXIT_CODE_SUCCESS
64
+ assert "Writing report..." in find_result.output
65
+ report_file = get_report_filename_from_output(find_result.output)
66
+
67
+ report_result = call_script(
68
+ (
69
+ 'generate_docs',
70
+ report_file,
71
+ '--config_file',
72
+ 'tests/test_configurations/.annotations_test_success_with_report_docs_html',
73
+ '-vv'
74
+ ),
75
+ delete_test_docs=False
76
+ )
77
+
78
+ assert find_result.exit_code == EXIT_CODE_SUCCESS
79
+ assert "Report rendered in" in report_result.output
80
+
81
+ # All file types are created
82
+ for created_doc in ('test_reports/index.html', 'test_reports/choice-id.html', 'test_reports/annotation-pii.html'):
83
+ assert os.path.exists(created_doc)
84
+
85
+
52
86
  def _do_find(source_path, new_report_path):
53
87
  """
54
88
  Do a static annotation search with report, rename the report to a distinct name.
@@ -167,4 +201,4 @@ def test_generate_report_missing_key():
167
201
  ))
168
202
 
169
203
  assert report_result.exit_code == EXIT_CODE_FAILURE
170
- assert "No report_template_dir key in tests/test_configurations/" in report_result.output
204
+ assert "No rendered_report_source_link_prefix key in" in report_result.output
@@ -1,110 +0,0 @@
1
- #!/usr/bin/env python
2
- """
3
- Package metadata for code_annotations.
4
- """
5
-
6
- import os
7
- import re
8
- import sys
9
-
10
- from setuptools import setup
11
-
12
-
13
- def get_version(*file_paths):
14
- """
15
- Extract the version string from the file at the given relative path fragments.
16
- """
17
- filename = os.path.join(os.path.dirname(__file__), *file_paths)
18
- version_file = open(filename).read()
19
- version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
20
- version_file, re.M)
21
- if version_match:
22
- return version_match.group(1)
23
- raise RuntimeError('Unable to find version string.')
24
-
25
-
26
- def load_requirements(*requirements_paths):
27
- """
28
- Load all requirements from the specified requirements files.
29
-
30
- Returns:
31
- list: Requirements file relative path strings
32
- """
33
- requirements = set()
34
- for path in requirements_paths:
35
- requirements.update(
36
- line.split('#')[0].strip() for line in open(path).readlines()
37
- if is_requirement(line.strip())
38
- )
39
- return list(requirements)
40
-
41
-
42
- def is_requirement(line):
43
- """
44
- Return True if the requirement line is a package requirement.
45
-
46
- Returns:
47
- bool: True if the line is not blank, a comment, a URL, or an included file
48
- """
49
- return not (
50
- line == '' or
51
- line.startswith('-r') or
52
- line.startswith('#') or
53
- line.startswith('-e') or
54
- line.startswith('git+') or
55
- line.startswith('-c')
56
- )
57
-
58
-
59
- VERSION = get_version('code_annotations', '__init__.py')
60
-
61
- if sys.argv[-1] == 'tag':
62
- print("Tagging the version on github:")
63
- os.system("git tag -a %s -m 'version %s'" % (VERSION, VERSION))
64
- os.system("git push --tags")
65
- sys.exit()
66
-
67
- README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
68
- CHANGELOG = open(os.path.join(os.path.dirname(__file__), 'CHANGELOG.rst')).read()
69
-
70
- setup(
71
- name='code-annotations',
72
- version=VERSION,
73
- description="""Extensible tools for parsing annotations in codebases""",
74
- long_description=README + '\n\n' + CHANGELOG,
75
- long_description_content_type='text/x-rst',
76
- author='edX',
77
- author_email='oscm@edx.org',
78
- url='https://github.com/openedx/code-annotations',
79
- packages=[
80
- 'code_annotations',
81
- ],
82
- entry_points={
83
- 'console_scripts': [
84
- 'code_annotations = code_annotations.cli:entry_point',
85
- ],
86
- 'annotation_finder.searchers': [
87
- 'javascript = code_annotations.extensions.javascript:JavascriptAnnotationExtension',
88
- 'python = code_annotations.extensions.python:PythonAnnotationExtension',
89
- ],
90
- },
91
- include_package_data=True,
92
- install_requires=load_requirements('requirements/base.in'),
93
- extras_require={"django": ["Django>=4.2"]},
94
- license="Apache Software License 2.0",
95
- zip_safe=False,
96
- keywords='edx pii code annotations',
97
- python_requires=">=3.11",
98
- classifiers=[
99
- 'Development Status :: 3 - Alpha',
100
- 'Framework :: Django',
101
- 'Framework :: Django :: 4.2',
102
- 'Intended Audience :: Developers',
103
- 'License :: OSI Approved :: Apache Software License',
104
- 'Natural Language :: English',
105
- 'Programming Language :: Python',
106
- 'Programming Language :: Python :: 3',
107
- 'Programming Language :: Python :: 3.11',
108
- 'Programming Language :: Python :: 3.12',
109
- ],
110
- )
@@ -1,8 +1,8 @@
1
- pyyaml
2
1
  click
2
+ python-slugify
3
3
  Jinja2
4
+ pyyaml
4
5
  stevedore
5
- python-slugify
6
6
 
7
7
  [django]
8
8
  Django>=4.2