Skip to content

Conversation

fsbraun
Copy link
Member

@fsbraun fsbraun commented Aug 4, 2025

Description

This prepares the 4.1.0 release featuring CSP. It also removes test files from the distributed wheel fixing #59

Related resources

Checklist

  • I have opened this pull request against master
  • I have added or modified the tests when changing logic
  • I have followed the conventional commits guidelines to add meaningful information into the changelog
  • I have read the contribution guidelines and I have joined #workgroup-pr-review on
    Slack to find a “pr review buddy” who is going to review my pull request.

Summary by Sourcery

Prepare for the 4.1.0 release by adding CSP-friendly widget media loading, updating packaging metadata, refining CI tests, and bumping the version.

New Features:

  • Support optional inline injection of widget CSS and JS for CSP compliance when the app isn’t installed.

Enhancements:

  • Move widget JS and CSS into static distributable assets and simplify the widget.media logic.
  • Exclude test files from the distributed wheel package.

Build:

  • Modernize packaging with pyproject.toml (remove setup.py) and update package metadata for version 4.1.0.

CI:

  • Expand GitHub Actions matrix to cover additional Django/Django CMS combinations and switch to ubuntu-latest runner.

Tests:

  • Add tests for the media property to verify both inline and static asset loading.

Chores:

  • Bump package version to 4.1.0.

Copy link
Contributor

sourcery-ai bot commented Aug 4, 2025

Reviewer's Guide

This PR prepares the 4.1.0 release by refactoring widget media loading to support CSPs (inlining CSS/JS or falling back to external files), adding static assets, updating packaging to PEP 621 (pyproject.toml), bumping the version, and updating tests and CI matrix accordingly.

Class diagram for refactored AttributesWidget media loading

classDiagram
    class AttributesWidget {
        +__init__(*args, **kwargs)
        +media
        +_render_row(key, value, field_name, key_attrs, val_attrs)
        +render(name, value, attrs=None, renderer=None)
        +value_from_datadict(data, files, name)
    }
    class Widget
    AttributesWidget --|> Widget
    class Media
    AttributesWidget ..> Media : uses
    class apps
    AttributesWidget ..> apps : uses
    class os
    AttributesWidget ..> os : uses
    class _read_inline_code {
        +_read_inline_code()
    }
    AttributesWidget ..> _read_inline_code : calls
Loading

File-Level Changes

Change Details Files
Refactored widget media handling for CSP support
  • Introduced _read_inline_code to load CSS/JS from static files at startup
  • Added _inline_code cache and logic to toggle between inline and external assets
  • Updated AttributesWidget.media property to return inline Media or external references
  • Simplified render() to append inline code placeholder instead of hardcoded style/script
djangocms_attributes_field/widgets.py
Added static asset files for the widget
  • Created widget.js and widget.css under static/djangocms_attributes_field
  • Migrated previously inlined styles and scripts into these files
djangocms_attributes_field/static/djangocms_attributes_field/widget.js
djangocms_attributes_field/static/djangocms_attributes_field/widget.css
Migrated to pyproject.toml packaging and bumped version
  • Replaced setup.py with PEP 621-compliant pyproject.toml including metadata and package data
  • Updated version, pyproject.toml version, changelog, and removed setup.py
pyproject.toml
djangocms_attributes_field/__init__.py
CHANGELOG.rst
setup.py
Extended tests and CI configuration
  • Added tests for media property behavior with and without INSTALLED_APPS
  • Expanded test requirement files and matrix in GitHub Actions workflow
  • Ensured static files are packaged in distribution
tests/test_widgets.py
.github/workflows/test.yml
tests/requirements/

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @fsbraun - I've reviewed your changes - here's some feedback:

  • Reading widget CSS and JS directly from the filesystem can break when the package is installed as a zipped wheel or in environments without a local file path—consider using pkg_resources or Django’s staticfiles finder API instead.
  • The module‐level _inline_code cache may hold stale data across different settings configurations in long‐running processes; consider using a per‐widget cached_property or clearing the cache more explicitly to avoid unexpected reuse.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Reading widget CSS and JS directly from the filesystem can break when the package is installed as a zipped wheel or in environments without a local file path—consider using pkg_resources or Django’s staticfiles finder API instead.
- The module‐level _inline_code cache may hold stale data across different settings configurations in long‐running processes; consider using a per‐widget cached_property or clearing the cache more explicitly to avoid unexpected reuse.

## Individual Comments

### Comment 1
<location> `djangocms_attributes_field/widgets.py:19` </location>
<code_context>
+# djangocms_attributes_field as an app, but still want to use the widget.
+_inline_code = None
+
+def _read_inline_code():
+    if apps.is_installed('djangocms_attributes_field'):
+        _inline_code = ""
+    else:
+        def _read_static_files():
+            base_dir = os.path.dirname(os.path.abspath(__file__))
+            with open(os.path.join(base_dir, 'static/djangocms_attributes_field/widget.js'), 'r', encoding='utf-8') as f:
+                js_code = f.read()
+            with open(os.path.join(base_dir, 'static/djangocms_attributes_field/widget.css'), 'r', encoding='utf-8') as f:
+                css_code = f.read()
+            return css_code, js_code
+
+        _inline_code = "<style>{}</style><script>{}</script>".format(*_read_static_files())
+    return _inline_code
+

</code_context>

<issue_to_address>
Assignment to _inline_code inside _read_inline_code() does not use the 'global' keyword.

Without declaring 'global _inline_code', assignments in _read_inline_code() will not update the module-level variable, breaking caching and causing unnecessary file reads.
</issue_to_address>

### Comment 2
<location> `djangocms_attributes_field/widgets.py:142` </location>
<code_context>
-            }(django.jQuery));
-        </script>
-        """
+        output += f'</div>{_inline_code}'
         return mark_safe(output)

</code_context>

<issue_to_address>
Directly appending _inline_code to output may result in None being rendered.

If _inline_code is None, 'None' will appear in the HTML. Ensure _inline_code is always a string or default to an empty string when it's None.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 19 to 28
def _read_inline_code():
if apps.is_installed('djangocms_attributes_field'):
_inline_code = ""
else:
def _read_static_files():
base_dir = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(base_dir, 'static/djangocms_attributes_field/widget.js'), 'r', encoding='utf-8') as f:
js_code = f.read()
with open(os.path.join(base_dir, 'static/djangocms_attributes_field/widget.css'), 'r', encoding='utf-8') as f:
css_code = f.read()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Assignment to _inline_code inside _read_inline_code() does not use the 'global' keyword.

Without declaring 'global _inline_code', assignments in _read_inline_code() will not update the module-level variable, breaking caching and causing unnecessary file reads.

}(django.jQuery));
</script>
"""
output += f'</div>{_inline_code}'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Directly appending _inline_code to output may result in None being rendered.

If _inline_code is None, 'None' will appear in the HTML. Ensure _inline_code is always a string or default to an empty string when it's None.

Comment on lines 3 to 10
function fixUpIds (fieldGroup) {
fieldGroup.find('.attributes-pair').each(function (idx, value) {
$(value).find('.attributes-key').attr('id', 'field-key-row-' + idx)
.siblings('label').attr('for', 'field-key-row-' + idx);
$(value).find('.attributes-value').attr('id', 'field-value-row-' + idx)
.siblings('label').attr('for', 'field-value-row-' + idx);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (code-quality): Avoid function declarations, favouring function assignment expressions, inside blocks. (avoid-function-declarations-in-blocks)

ExplanationFunction declarations may be hoisted in Javascript, but the behaviour is inconsistent between browsers. Hoisting is generally confusing and should be avoided. Rather than using function declarations inside blocks, you should use function expressions, which create functions in-scope.

Copy link

codecov bot commented Aug 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.44%. Comparing base (41d95f8) to head (db5ff2d).

Additional details and impacted files
@@           Coverage Diff           @@
##           master      #60   +/-   ##
=======================================
  Coverage   91.44%   91.44%           
=======================================
  Files           4        4           
  Lines         187      187           
  Branches       29       29           
=======================================
  Hits          171      171           
  Misses         10       10           
  Partials        6        6           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fsbraun fsbraun merged commit 49e0791 into master Aug 4, 2025
31 checks passed
@fsbraun fsbraun deleted the feat/csp-support branch August 4, 2025 16:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant