plainx-sentry 0.10.0__tar.gz → 0.11.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.
Files changed (29) hide show
  1. plainx_sentry-0.11.0/.claude/rules/plain-code.md +33 -0
  2. plainx_sentry-0.11.0/.claude/rules/plain-models.md +30 -0
  3. plainx_sentry-0.11.0/.claude/rules/plain-templates.md +27 -0
  4. plainx_sentry-0.11.0/.claude/rules/plain-test.md +13 -0
  5. plainx_sentry-0.11.0/.claude/rules/plain.md +101 -0
  6. plainx_sentry-0.11.0/.claude/skills/plain-install/SKILL.md +26 -0
  7. plainx_sentry-0.11.0/.claude/skills/plain-upgrade/SKILL.md +35 -0
  8. plainx_sentry-0.11.0/.github/workflows/release.yml +32 -0
  9. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/PKG-INFO +23 -2
  10. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/README.md +21 -0
  11. plainx_sentry-0.11.0/plainx/sentry/CHANGELOG.md +24 -0
  12. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/plainx/sentry/config.py +1 -1
  13. plainx_sentry-0.11.0/plainx/sentry/middleware.py +73 -0
  14. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/plainx/sentry/templates/sentry/js.html +1 -1
  15. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/plainx/sentry/templates.py +13 -5
  16. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/pyproject.toml +3 -3
  17. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/tests/test_sentry.py +54 -24
  18. plainx_sentry-0.11.0/uv.lock +739 -0
  19. plainx_sentry-0.10.0/plainx/sentry/CHANGELOG.md +0 -11
  20. plainx_sentry-0.10.0/uv.lock +0 -1003
  21. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/.gitignore +0 -0
  22. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/plainx/sentry/__init__.py +0 -0
  23. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/plainx/sentry/default_settings.py +0 -0
  24. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/scripts/install +0 -0
  25. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/scripts/release +0 -0
  26. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/scripts/test +0 -0
  27. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/tests/settings.py +0 -0
  28. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/tests/templates/index.html +0 -0
  29. {plainx_sentry-0.10.0 → plainx_sentry-0.11.0}/tests/urls.py +0 -0
@@ -0,0 +1,33 @@
1
+ ---
2
+ paths:
3
+ - "**/*.py"
4
+ ---
5
+
6
+ # Code Quality
7
+
8
+ ## Fix Formatting and Linting
9
+
10
+ ```
11
+ uv run plain fix [path]
12
+ ```
13
+
14
+ Automatically fixes formatting and linting issues using ruff and biome.
15
+
16
+ Options:
17
+
18
+ - `--unsafe-fixes` - Apply ruff unsafe fixes
19
+ - `--add-noqa` - Add noqa comments to suppress errors
20
+
21
+ ## Check Without Fixing
22
+
23
+ ```
24
+ uv run plain code check [path]
25
+ ```
26
+
27
+ Runs ruff, ty (type checking), biome, and annotation coverage checks without auto-fixing.
28
+
29
+ ## Code Style
30
+
31
+ - Add `from __future__ import annotations` at the top of Python files
32
+ - Keep imports at the top of the file unless avoiding circular imports
33
+ - Don't include args/returns in docstrings if already type annotated
@@ -0,0 +1,30 @@
1
+ # Database & Models
2
+
3
+ ## Creating Migrations
4
+
5
+ ```
6
+ uv run plain makemigrations
7
+ ```
8
+
9
+ Only write migrations by hand if they are custom data migrations.
10
+
11
+ ## Running Migrations
12
+
13
+ ```
14
+ uv run plain migrate --backup
15
+ ```
16
+
17
+ The `--backup` flag creates a database backup before applying migrations.
18
+
19
+ Run `uv run plain docs models --source` for detailed model and migration documentation.
20
+
21
+ ## Querying
22
+
23
+ Use `Model.query` to build querysets:
24
+
25
+ - `User.query.all()`
26
+ - `User.query.filter(is_active=True)`
27
+ - `User.query.get(pk=1)`
28
+ - `User.query.exclude(role="admin")`
29
+
30
+ Run `uv run plain docs models --api` for the full query API.
@@ -0,0 +1,27 @@
1
+ ---
2
+ paths:
3
+ - "**/*.html"
4
+ ---
5
+
6
+ # Templates
7
+
8
+ ## Long Lines
9
+
10
+ When an HTML tag has many attributes or a long class list, break it into multiple lines with each attribute indented. The closing `>` goes on its own line.
11
+
12
+ **Good:**
13
+
14
+ ```html
15
+ <div
16
+ class="flex items-center justify-between gap-4 rounded-lg border bg-white p-4 shadow-sm"
17
+ data-active="{{ is_active }}"
18
+ >
19
+ ...
20
+ </div>
21
+ ```
22
+
23
+ **Avoid:**
24
+
25
+ ```html
26
+ <div class="flex items-center justify-between gap-4 rounded-lg border bg-white p-4 shadow-sm" data-active="{{ is_active }}">...</div>
27
+ ```
@@ -0,0 +1,13 @@
1
+ # Testing
2
+
3
+ ```
4
+ uv run plain test [pytest options]
5
+ ```
6
+
7
+ - `uv run plain test` - Run all tests
8
+ - `uv run plain test -k test_name` - Filter by test name
9
+ - `uv run plain test --pdb` - Drop into debugger on failure
10
+ - `uv run plain test -x` - Stop on first failure
11
+ - `uv run plain test -v` - Verbose output
12
+
13
+ Use pytest fixtures and conventions. Place tests in `tests/` directory. Use `plain.test.Client` for HTTP request testing.
@@ -0,0 +1,101 @@
1
+ # Plain Framework
2
+
3
+ Plain is a Python web framework.
4
+
5
+ - Always use `uv run` to execute commands — never use bare `python` or `plain` directly.
6
+ - Plain is a Django fork but has different APIs — never assume Django patterns will work.
7
+ - When unsure about an API or something doesn't work, run `uv run plain docs <package>` first. Add `--api` if you need the full API surface.
8
+ - Use the `/plain-install` skill to add new Plain packages.
9
+ - Use the `/plain-upgrade` skill to upgrade Plain packages.
10
+
11
+ ## Key Differences from Django
12
+
13
+ Claude's training data contains a lot of Django code. These are the most common patterns that differ in Plain:
14
+
15
+ - **Querysets**: Use `Model.query` not `Model.objects` (e.g., `User.query.filter(is_active=True)`)
16
+ - **Field types**: Import from `plain.models.types` not `plain.models.fields`
17
+ - **Templates**: Plain uses Jinja2, not Django's template engine. Most syntax is similar but filters use `|` with function call syntax (e.g., `{{ name|title }}` works, but custom filters differ)
18
+ - **URLs**: Use `Router` with `urls` list, not Django's `urlpatterns`
19
+ - **Tests**: Use `plain.test.Client`, not `django.test.Client`
20
+ - **Settings**: Use `plain.runtime.settings`, not `django.conf.settings`
21
+
22
+ When in doubt, run `uv run plain docs <package> --api` to check the actual API.
23
+
24
+ ## Documentation
25
+
26
+ Run `uv run plain docs --list` to see all official packages (installed and uninstalled) with descriptions.
27
+ Run `uv run plain docs <package>` for markdown documentation (installed packages only).
28
+ Run `uv run plain docs <package> --api` for the symbolicated API surface.
29
+ For uninstalled packages, the CLI shows the install command and an online docs URL.
30
+
31
+ Online docs URL pattern: `https://plainframework.com/docs/<pip-name>/<module/path>/README.md`
32
+ Example: `https://plainframework.com/docs/plain-models/plain/models/README.md`
33
+
34
+ Examples:
35
+
36
+ - `uv run plain docs models` - Models and database docs
37
+ - `uv run plain docs models --api` - Models API surface
38
+ - `uv run plain docs templates` - Jinja2 templates
39
+ - `uv run plain docs assets` - Static assets
40
+
41
+ ### All official packages
42
+
43
+ - **plain** — Web framework core
44
+ - **plain-admin** — Backend admin interface
45
+ - **plain-api** — Class-based API views
46
+ - **plain-auth** — User authentication and authorization
47
+ - **plain-cache** — Database-backed cache with optional expiration
48
+ - **plain-code** — Preconfigured code formatting and linting
49
+ - **plain-dev** — Local development server with auto-reload
50
+ - **plain-elements** — HTML template components
51
+ - **plain-email** — Send email
52
+ - **plain-esbuild** — Build JavaScript with esbuild
53
+ - **plain-flags** — Feature flags via database models
54
+ - **plain-htmx** — HTMX integration for templates and views
55
+ - **plain-jobs** — Background jobs with a database-driven queue
56
+ - **plain-loginlink** — Link-based authentication
57
+ - **plain-models** — Model data and store it in a database
58
+ - **plain-oauth** — OAuth provider login
59
+ - **plain-observer** — On-page telemetry and observability
60
+ - **plain-pages** — Serve static pages, markdown, and assets
61
+ - **plain-pageviews** — Client-side pageview tracking
62
+ - **plain-passwords** — Password authentication
63
+ - **plain-pytest** — Test with pytest
64
+ - **plain-redirection** — URL redirection with admin and logging
65
+ - **plain-scan** — Test for production best practices
66
+ - **plain-sessions** — Database-backed sessions
67
+ - **plain-start** — Bootstrap a new project from templates
68
+ - **plain-support** — Support forms for your application
69
+ - **plain-tailwind** — Tailwind CSS without JavaScript or npm
70
+ - **plain-toolbar** — Debug toolbar
71
+ - **plain-tunnel** — Remote access to local dev server
72
+ - **plain-vendor** — Vendor CDN scripts and styles
73
+
74
+ ## Shell
75
+
76
+ `uv run plain shell` opens an interactive Python shell with Plain configured and database access.
77
+
78
+ Run a one-off command:
79
+
80
+ ```
81
+ uv run plain shell -c "from app.users.models import User; print(User.query.count())"
82
+ ```
83
+
84
+ Run a script:
85
+
86
+ ```
87
+ uv run plain run script.py
88
+ ```
89
+
90
+ ## HTTP Requests
91
+
92
+ Use `uv run plain request` to make test HTTP requests against the dev database.
93
+
94
+ ```
95
+ uv run plain request /path
96
+ uv run plain request /path --user 1
97
+ uv run plain request /path --header "Accept: application/json"
98
+ uv run plain request /path --method POST --data '{"key": "value"}'
99
+ uv run plain request /path --no-body # Headers only
100
+ uv run plain request /path --no-headers # Body only
101
+ ```
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: plain-install
3
+ description: Installs Plain packages and guides through setup steps. Use when adding new packages to a project.
4
+ ---
5
+
6
+ # Install Plain Packages
7
+
8
+ ## 1. Install the package(s)
9
+
10
+ ```
11
+ uv run plain install <package-name> [additional-packages...]
12
+ ```
13
+
14
+ ## 2. Complete setup for each package
15
+
16
+ 1. Run `uv run plain docs <package>` and read the installation instructions
17
+ 2. If the docs indicate it's a dev tool, move it: `uv remove <package> && uv add <package> --dev`
18
+ 3. Complete any code modifications from the installation instructions
19
+
20
+ ## Guidelines
21
+
22
+ - DO NOT commit any changes
23
+ - Report back with:
24
+ - Whether setup completed successfully
25
+ - Any manual steps the user needs to complete
26
+ - Any issues or errors encountered
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: plain-upgrade
3
+ description: Upgrades Plain packages and applies required migration changes. Use when updating to newer package versions.
4
+ ---
5
+
6
+ # Upgrade Plain Packages
7
+
8
+ ## 1. Run the upgrade
9
+
10
+ ```
11
+ uv run plain upgrade [package-names...]
12
+ ```
13
+
14
+ This will show which packages were upgraded (e.g., `plain-models: 0.1.0 -> 0.2.0`).
15
+
16
+ ## 2. Apply code changes for each upgraded package
17
+
18
+ For each package that was upgraded:
19
+
20
+ 1. Run `uv run plain changelog <package> --from <old-version> --to <new-version>`
21
+ 2. Read the "Upgrade instructions" section
22
+ 3. If it says "No changes required", skip to next package
23
+ 4. Apply any required code changes
24
+
25
+ ## 3. Validate
26
+
27
+ 1. Run `uv run plain fix` to fix formatting
28
+ 2. Run `uv run plain preflight` to validate configuration
29
+
30
+ ## Guidelines
31
+
32
+ - Process ALL packages before testing
33
+ - DO NOT commit any changes
34
+ - Keep code changes minimal and focused
35
+ - Report any issues or conflicts encountered
@@ -0,0 +1,32 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v5
16
+ - run: uv python install
17
+
18
+ # https://docs.pypi.org/trusted-publishers/using-a-publisher/
19
+ - name: Mint API token
20
+ id: mint-token
21
+ run: |
22
+ resp=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
23
+ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi")
24
+ oidc_token=$(jq -r '.value' <<< "${resp}")
25
+ resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}")
26
+ api_token=$(jq -r '.token' <<< "${resp}")
27
+ echo "::add-mask::${api_token}"
28
+ echo "api-token=${api_token}" >> "${GITHUB_OUTPUT}"
29
+
30
+ - run: uv build && uv publish
31
+ env:
32
+ UV_PUBLISH_TOKEN: ${{ steps.mint-token.outputs.api-token }}
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plainx-sentry
3
- Version: 0.10.0
3
+ Version: 0.11.0
4
4
  Author-email: Dave Gaeddert <dave.gaeddert@gmail.com>
5
- Requires-Python: >=3.11
5
+ Requires-Python: >=3.13
6
6
  Requires-Dist: plain-auth>=0.16.0
7
7
  Requires-Dist: plain-sessions>=0.27.0
8
8
  Requires-Dist: sentry-sdk[opentelemetry]>=2.24.0
@@ -47,6 +47,27 @@ In Heroku, for example:
47
47
  heroku config:set SENTRY_DSN=<your-DSN>
48
48
  ```
49
49
 
50
+ ## User and request context
51
+
52
+ To attach user and request context to errors, add the middleware:
53
+
54
+ ```python
55
+ # settings.py
56
+ MIDDLEWARE = [
57
+ ...
58
+ "plain.sessions.middleware.SessionMiddleware",
59
+ "plainx.sentry.middleware.SentryMiddleware", # After SessionMiddleware
60
+ ...
61
+ ]
62
+ ```
63
+
64
+ This attaches to errors:
65
+
66
+ - **User context**: ID, email, username
67
+ - **Request context**: URL, method, query string, headers, cookies
68
+
69
+ Email, username, headers, and cookies require `SENTRY_PII_ENABLED=True` (the default).
70
+
50
71
  ## Configuration
51
72
 
52
73
  [Look at the `default_settings.py` for all available settings.](./plainx/sentry/default_settings.py)
@@ -37,6 +37,27 @@ In Heroku, for example:
37
37
  heroku config:set SENTRY_DSN=<your-DSN>
38
38
  ```
39
39
 
40
+ ## User and request context
41
+
42
+ To attach user and request context to errors, add the middleware:
43
+
44
+ ```python
45
+ # settings.py
46
+ MIDDLEWARE = [
47
+ ...
48
+ "plain.sessions.middleware.SessionMiddleware",
49
+ "plainx.sentry.middleware.SentryMiddleware", # After SessionMiddleware
50
+ ...
51
+ ]
52
+ ```
53
+
54
+ This attaches to errors:
55
+
56
+ - **User context**: ID, email, username
57
+ - **Request context**: URL, method, query string, headers, cookies
58
+
59
+ Email, username, headers, and cookies require `SENTRY_PII_ENABLED=True` (the default).
60
+
40
61
  ## Configuration
41
62
 
42
63
  [Look at the `default_settings.py` for all available settings.](./plainx/sentry/default_settings.py)
@@ -0,0 +1,24 @@
1
+ # plainx-sentry changelog
2
+
3
+ ## [0.11.0](https://github.com/davegaeddert/plainx-sentry/releases/v0.11.0) (2026-02-05)
4
+
5
+ ### What's changed
6
+
7
+ - Added `SentryMiddleware` for automatic request and user context on Sentry events ([950df07](https://github.com/davegaeddert/plainx-sentry/commit/950df07))
8
+ - Now requires Python 3.13+ ([dbfb0d7](https://github.com/davegaeddert/plainx-sentry/commit/dbfb0d7))
9
+ - Added type annotations throughout the codebase ([dbfb0d7](https://github.com/davegaeddert/plainx-sentry/commit/dbfb0d7))
10
+
11
+ ### Upgrade instructions
12
+
13
+ - Ensure you are running Python 3.13 or higher
14
+ - Add `SentryMiddleware` to your middleware stack after `SessionMiddleware` for automatic request/user context
15
+
16
+ ## [0.7.0](https://github.com/davegaeddert/plainx-sentry/releases/plainx-sentry@0.7.0) (2025-07-19)
17
+
18
+ ### What's changed
19
+
20
+ - Middleware was removed in favor of using the OpenTelemetry integration and new otel instrumentation in Plain.
21
+
22
+ ### Upgrade instructions
23
+
24
+ - Remove `SentryMiddleware` and `SentryWorkerMiddleware` from your `app/settings.py`.
@@ -11,7 +11,7 @@ from sentry_sdk.integrations.opentelemetry import SentryPropagator, SentrySpanPr
11
11
  class PlainxSentryConfig(PackageConfig):
12
12
  label = "plainxsentry"
13
13
 
14
- def ready(self):
14
+ def ready(self) -> None:
15
15
  if settings.SENTRY_DSN and settings.SENTRY_AUTO_INIT:
16
16
  sentry_sdk.init(
17
17
  settings.SENTRY_DSN,
@@ -0,0 +1,73 @@
1
+ from typing import Any
2
+
3
+ import sentry_sdk
4
+ from plain.auth import get_request_user
5
+ from plain.http import HttpMiddleware
6
+ from plain.http.request import Request
7
+ from plain.http.response import Response
8
+ from sentry_sdk.scope import should_send_default_pii
9
+
10
+
11
+ def _build_request_info(request: Request) -> dict[str, Any]:
12
+ """Build request context dictionary for Sentry events."""
13
+ try:
14
+ url = request.build_absolute_uri()
15
+ except Exception:
16
+ url = request.path
17
+
18
+ request_info = {
19
+ "method": request.method,
20
+ "url": url,
21
+ "query_string": request.query_string,
22
+ }
23
+
24
+ if should_send_default_pii():
25
+ request_info["headers"] = dict(request.headers)
26
+ request_info["cookies"] = dict(request.cookies)
27
+
28
+ return request_info
29
+
30
+
31
+ def _build_user_info(user: Any) -> dict[str, Any]:
32
+ """Build user context dictionary for Sentry events."""
33
+ user_info = {"id": str(user.id)}
34
+
35
+ if should_send_default_pii():
36
+ if email := getattr(user, "email", None):
37
+ user_info["email"] = email
38
+ if username := getattr(user, "username", None):
39
+ user_info["username"] = username
40
+
41
+ return user_info
42
+
43
+
44
+ class SentryMiddleware(HttpMiddleware):
45
+ """
46
+ Middleware that registers a Sentry event processor for request/user context.
47
+
48
+ Add this to your MIDDLEWARE setting after SessionMiddleware:
49
+
50
+ MIDDLEWARE = [
51
+ ...
52
+ "plain.sessions.middleware.SessionMiddleware",
53
+ "plainx.sentry.middleware.SentryMiddleware",
54
+ ...
55
+ ]
56
+ """
57
+
58
+ def process_request(self, request: Request) -> Response:
59
+ def event_processor(
60
+ event: dict[str, Any], hint: dict[str, Any]
61
+ ) -> dict[str, Any]:
62
+ if "request" not in event:
63
+ event["request"] = _build_request_info(request)
64
+
65
+ if "user" not in event:
66
+ user = get_request_user(request)
67
+ if user:
68
+ event["user"] = _build_user_info(user)
69
+
70
+ return event
71
+
72
+ sentry_sdk.get_current_scope().add_event_processor(event_processor)
73
+ return self.get_response(request)
@@ -1,5 +1,5 @@
1
1
  {% if sentry_public_key|default("") -%}
2
- <script src="https://js.sentry-cdn.com/{{ sentry_public_key }}.min.js" crossorigin="anonymous"></script>
2
+ <script src="https://js.sentry-cdn.com/{{ sentry_public_key }}.min.js" crossorigin="anonymous" nonce="{{ csp_nonce }}"></script>
3
3
  {{ sentry_init|json_script("sentry_init", csp_nonce) }}
4
4
  <script nonce="{{ csp_nonce }}">
5
5
  var sentryInit = JSON.parse(document.getElementById("sentry_init").textContent);
@@ -1,4 +1,7 @@
1
+ from typing import Any
2
+
1
3
  import sentry_sdk
4
+ from jinja2.runtime import Context
2
5
  from plain.auth import get_request_user
3
6
  from plain.runtime import settings
4
7
  from plain.templates import register_template_extension
@@ -10,7 +13,9 @@ class SentryJSExtension(InclusionTagExtension):
10
13
  tags = {"sentry_js"}
11
14
  template_name = "sentry/js.html"
12
15
 
13
- def get_context(self, context, *args, **kwargs):
16
+ def get_context(
17
+ self, context: Context, *args: Any, **kwargs: Any
18
+ ) -> Context | dict[str, Any]:
14
19
  if not settings.SENTRY_DSN:
15
20
  return {}
16
21
 
@@ -45,7 +50,10 @@ class SentryJSExtension(InclusionTagExtension):
45
50
  class SentryFeedbackExtension(SentryJSExtension):
46
51
  tags = {"sentry_feedback"}
47
52
 
48
- def get_context(self, context, *args, **kwargs):
49
- context = super().get_context(context, *args, **kwargs)
50
- context["sentry_dialog_event_id"] = sentry_sdk.last_event_id()
51
- return context
53
+ def get_context(
54
+ self, context: Context, *args: Any, **kwargs: Any
55
+ ) -> dict[str, Any]:
56
+ parent_result = super().get_context(context, *args, **kwargs)
57
+ result: dict[str, Any] = dict(parent_result)
58
+ result["sentry_dialog_event_id"] = sentry_sdk.last_event_id()
59
+ return result
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "plainx-sentry"
3
- version = "0.10.0"
3
+ version = "0.11.0"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  authors = [
7
7
  { name = "Dave Gaeddert", email = "dave.gaeddert@gmail.com" }
8
8
  ]
9
- requires-python = ">=3.11"
9
+ requires-python = ">=3.13"
10
10
  dependencies = [
11
11
  "plain-auth>=0.16.0",
12
12
  "plain-sessions>=0.27.0",
@@ -26,7 +26,7 @@ packages = ["plainx"]
26
26
 
27
27
 
28
28
  [tool.plain.code.biome]
29
- version = "2.1.2"
29
+ enabled = false
30
30
  [build-system]
31
31
  requires = ["hatchling"]
32
32
  build-backend = "hatchling.build"