Skip to content

feat: major improvement - #89

Merged
j03-dev merged 6 commits into
mainfrom
feat/wrap_with_mid
Jul 29, 2026
Merged

feat: major improvement#89
j03-dev merged 6 commits into
mainfrom
feat/wrap_with_mid

Conversation

@j03-dev

@j03-dev j03-dev commented Jul 29, 2026

Copy link
Copy Markdown
Owner
  • chore: remove catcher
  • chore: add wrapper
  • feat: wraper & registe function to template

Summary by CodeRabbit

  • New Features

    • Added a global request wrapper (wrap) to customize request/response handling end-to-end.
  • Changes

    • Removed status-based catcher support and the related server API; catcher bindings are no longer exposed.
    • Template rendering is now Tera-only: Jinja/minijinja and engine selection were removed, and templating exports were simplified.
  • Release

    • Bumped crate/package version to 0.10.0 and updated the README version badge.

j03-dev added 3 commits July 26, 2026 12:39
- remove cors
- remplace catcher with wrappe function
- allows user to inject function in template engine
- only tera is available
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@j03-dev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 56dbe61c-219d-41b9-963a-160696dcfa92

📥 Commits

Reviewing files that changed from the base of the PR and between 0b5587f and 2e0f7fe.

📒 Files selected for processing (2)
  • oxapy/__init__.pyi
  • src/templating.rs
📝 Walkthrough

Walkthrough

The 0.10.0 release removes catcher and Minijinja support, adds global request wrappers, changes middleware chains to borrow slices, consolidates templating on Tera, updates stub generation, broadens JSON conversion input, and synchronizes package metadata.

Changes

Core API and runtime refactor

Layer / File(s) Summary
Tera-only templating
Cargo.toml, src/templating/*
Template now uses Tera exclusively, supports Python-backed Tera functions, and no longer exports separate Jinja or Tera engine classes.
Request wrapper pipeline
src/lib.rs, src/request.rs, src/response.rs, src/middleware.rs
Catcher and CORS plumbing is replaced by HttpServer.wrap(...) and response-level Python wrappers; middleware chains now borrow middleware slices.
Release and build updates
Cargo.toml, README.md, build.sh, oxapy/__init__.py, src/json.rs
The version is updated to 0.10.0, stub generation enables stub_gen, the typing suppression is removed, and JSON conversion accepts PyAny input.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HttpServer
  participant Request
  participant Response
  participant PythonWrapper
  HttpServer->>Request: create context with wrapper
  Request->>Response: process route or not-found response
  Response->>PythonWrapper: call wrapper(request, response)
  PythonWrapper-->>Response: return converted response
Loading

Possibly related PRs

  • j03-dev/oxapy#44: Adds the catcher feature that this PR removes from the server API and response pipeline.
  • j03-dev/oxapy#22: Adds the Python Tera bindings later consolidated into the Tera-only Template implementation.
  • j03-dev/oxapy#84: Modifies the same catcher and CORS request-handling flow replaced here by the global wrapper.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and generic, so it does not clearly describe the removal of catchers, addition of a wrapper, or template function registration. Use a concise, specific title that names the main change, such as removing catcher support and adding request wrapping/template function registration.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/wrap_with_mid

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/templating/mod.rs (1)

202-207: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Remove the stale templat stub for Tera.

oxapy/templating/__init__.pyi still exports Tera with __new__/render, but the extension only registers Template via templating_submodule. Regenerate/remove that generated stub to avoid exposing a Python class that no longer exists in the compiled module.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/templating/mod.rs` around lines 202 - 207, Remove or regenerate the stale
Tera declaration from oxapy/templating/__init__.pyi, including its __new__ and
render members, so the stub matches templating_submodule’s registered Template
class and render function without exposing a nonexistent Tera class.
🧹 Nitpick comments (4)
src/templating/mod.rs (3)

133-143: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Exception type fully-qualified instead of imported; error message wording is awkward.

pyo3::exceptions::PyRuntimeError is referenced inline rather than via an import (compare PyValueError, which is imported at the top of the file). Also, "Tera engine is already shared cloned copies" reads oddly and could be clearer, e.g. "Cannot register function: the Template's Tera engine is already shared (register functions before attaching the Template to the server)."

As per coding guidelines, "Use pyo3::exceptions::* for standard Python exceptions in Rust code."

♻️ Suggested fix
-use pyo3::{
-    Bound, PyResult,
-    exceptions::PyValueError,
-    prelude::*,
-    types::{PyDict, PyModule, PyModuleMethods},
-};
+use pyo3::{
+    Bound, PyResult,
+    exceptions::*,
+    prelude::*,
+    types::{PyDict, PyModule, PyModuleMethods},
+};
-            Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
-                "Cannot register function: Tera engine is already shared cloned copies",
+            Err(PyErr::new::<PyRuntimeError, _>(
+                "Cannot register function: the Tera engine is already shared; register functions before attaching this Template to the server",
             ))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/templating/mod.rs` around lines 133 - 143, Update register_function to
use the imported PyRuntimeError type instead of the fully qualified
pyo3::exceptions path, and revise the error text to clearly state that the
Template’s Tera engine is already shared and functions must be registered before
attaching the Template to the server.

Source: Coding guidelines


114-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

render and register_function (both public pymethods) have no doc comments.

Neither method documents Args/Returns/Example, unlike new above them. register_function in particular is a new public API central to this PR's stated goal of letting users register template functions — worth documenting its behavior, especially the "must be called before the Template is shared with the server, or it errors" constraint (see Arc::get_mut check below), since that's a non-obvious usage requirement.

As per coding guidelines, "Use doc comments /// for public APIs in Rust" and "Include Args, Returns, and Example sections in Rust docstrings for public APIs."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/templating/mod.rs` around lines 114 - 143, Add Rust doc comments to the
public pymethods render and register_function, including Args, Returns, and
Example sections. Document render’s template/context inputs and rendered-string
result, and document register_function’s callable registration behavior plus its
requirement to run before the Template engine is shared, including the resulting
error when that requirement is violated.

Source: Coding guidelines


90-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Docstring still describes removed engine-selection behavior.

Raises: PyException: If an invalid engine type is specified. and the # Use Jinja with default template directory / templating.Template() example are stale now that Template::new only takes dir and Jinja/minijinja support has been removed entirely. This will surface in the generated .pyi stub docs and mislead users.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/templating/mod.rs` around lines 90 - 101, Update the documentation for
Template::new to remove the obsolete invalid-engine PyException description and
Jinja-specific example, including templating.Template() without arguments.
Revise the example and surrounding text to reflect the current dir-only
constructor and supported engine behavior.
README.md (1)

12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add alt text to the version badge image.

markdownlint flags this <img> for missing alternate text.

♻️ Suggested fix
-<a href='https://github.com/j03-dev/oxapy/#'><img src='https://img.shields.io/badge/version-0.10.0-%23b7410e'/></a>
+<a href='https://github.com/j03-dev/oxapy/#'><img src='https://img.shields.io/badge/version-0.10.0-%23b7410e' alt='version'/></a>
As per static analysis hints, "Images should have alternate text (alt text)" (MD045, no-alt-text).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 12, Add descriptive alt text to the version badge img
element in README.md, preserving its existing source, link, and displayed badge
behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib.rs`:
- Around line 345-357: Update the documentation for the public wrap method to
remove obsolete catchers terminology, status-code catcher behavior, and any
catchers argument references. Document the current single response wrapper
parameter and its behavior so generated stubs and help reflect wrap’s actual
API.

In `@src/response.rs`:
- Around line 234-244: Resolve the inert CORS configuration exposed alongside
Response::call_wrapper: either propagate the configured CORS policy through
call_wrapper and apply its headers, or remove the CORS field, HttpServer::cors
method, public export, and generated documentation as one consistent API change.
Ensure no publicly configurable CORS path remains without corresponding
behavior.

In `@src/templating/mod.rs`:
- Around line 41-43: Update the is_safe method on the templating wrapper to
return false, matching Tera’s default escaping behavior. Ensure functions
registered through register_function() remain autoescaped unless an explicit
raw-output contract is intentionally introduced.
- Around line 26-39: Update PyTeraFunction::call to convert the Python callable
result through the existing JSON-to-Tera conversion path instead of wrapping
result.to_string() in Value::String. Preserve booleans, numbers, lists, and
dictionaries as their corresponding tera::Value types, propagating conversion
errors through tera::Error.

---

Outside diff comments:
In `@src/templating/mod.rs`:
- Around line 202-207: Remove or regenerate the stale Tera declaration from
oxapy/templating/__init__.pyi, including its __new__ and render members, so the
stub matches templating_submodule’s registered Template class and render
function without exposing a nonexistent Tera class.

---

Nitpick comments:
In `@README.md`:
- Line 12: Add descriptive alt text to the version badge img element in
README.md, preserving its existing source, link, and displayed badge behavior.

In `@src/templating/mod.rs`:
- Around line 133-143: Update register_function to use the imported
PyRuntimeError type instead of the fully qualified pyo3::exceptions path, and
revise the error text to clearly state that the Template’s Tera engine is
already shared and functions must be registered before attaching the Template to
the server.
- Around line 114-143: Add Rust doc comments to the public pymethods render and
register_function, including Args, Returns, and Example sections. Document
render’s template/context inputs and rendered-string result, and document
register_function’s callable registration behavior plus its requirement to run
before the Template engine is shared, including the resulting error when that
requirement is violated.
- Around line 90-101: Update the documentation for Template::new to remove the
obsolete invalid-engine PyException description and Jinja-specific example,
including templating.Template() without arguments. Revise the example and
surrounding text to reflect the current dir-only constructor and supported
engine behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 61cca9e3-2573-487d-8641-a650f0a25043

📥 Commits

Reviewing files that changed from the base of the PR and between bbda3ac and d6bf8bc.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • Cargo.toml
  • README.md
  • build.sh
  • oxapy/__init__.py
  • src/catcher.rs
  • src/lib.rs
  • src/middleware.rs
  • src/request.rs
  • src/response.rs
  • src/templating/minijinja.rs
  • src/templating/mod.rs
  • src/templating/tera.rs
💤 Files with no reviewable changes (3)
  • src/templating/minijinja.rs
  • src/catcher.rs
  • src/templating/tera.rs

Comment thread src/lib.rs
Comment on lines +345 to +357
/// def global_middleware(request, response):
/// if response.status.code == 200:
/// return Response("<h1>Page Not Found</h1>", content_type="text/html")
/// return response
///
/// server.catchers([not_found])
/// server.wrap(global_middleware)
/// ```
fn catchers<'py>(
fn wrap<'py>(
mut slf: PyRefMut<'py, Self>,
catchers: Vec<PyRef<Catcher>>,
wrapper: Py<PyAny>,
py: Python<'py>,
) -> PyRefMut<'py, Self> {
let map = catchers
.into_iter()
.map(|c| (c.status, c.handler.clone_ref(py)))
.collect();
slf.catchers = Some(Arc::new(map));
slf.wrapper = Some(Arc::new(wrapper));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the obsolete catchers documentation.

This public method still documents status-code catchers and a catchers argument, while it now accepts one response wrapper. Generated stubs/help will expose the wrong API.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib.rs` around lines 345 - 357, Update the documentation for the public
wrap method to remove obsolete catchers terminology, status-code catcher
behavior, and any catchers argument references. Document the current single
response wrapper parameter and its behavior so generated stubs and help reflect
wrap’s actual API.

Comment thread src/response.rs
Comment on lines +234 to 244
pub(crate) fn call_wrapper(mut self, req: &ProcessRequest) -> Self {
if let Some(wrapper) = &req.wrapper {
let request = req.request.as_ref().clone();
self = Python::attach(|py| {
let result = handler.call(py, (request, self), None)?;
let result = wrapper.call(py, (request, self), None)?;
convert_to_response(result, py)
})
.unwrap_or_else(Response::from);
}
self
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove or restore the now-inert CORS API.

call_wrapper no longer applies CORS, while HttpServer::cors(...) remains publicly configurable. Existing users can configure CORS successfully but receive no CORS headers. Either thread CORS through this flow or remove the field, method, export, and generated documentation together.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/response.rs` around lines 234 - 244, Resolve the inert CORS configuration
exposed alongside Response::call_wrapper: either propagate the configured CORS
policy through call_wrapper and apply its headers, or remove the CORS field,
HttpServer::cors method, public export, and generated documentation as one
consistent API change. Ensure no publicly configurable CORS path remains without
corresponding behavior.

Comment thread src/templating.rs
Comment thread src/templating.rs
Comment on lines +41 to +43
fn is_safe(&self) -> bool {
true
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate templating mod and relevant files =="
git ls-files | rg '(^|/)src/templating/mod\.rs$|Cargo\.toml|pyproject\.toml|oxapy/templating/__init__\.pyi$' || true

echo
echo "== src/templating/mod.rs outline =="
ast-grep outline src/templating/mod.rs || true

echo
echo "== relevant lines in src/templating/mod.rs =="
cat -n src/templating/mod.rs | sed -n '1,160p'

echo
echo "== dependency version declarations for tera =="
rg -n 'tera|minijinja|pyo3' Cargo.toml pyproject.toml -S || true

Repository: j03-dev/oxapy

Length of output: 7108


🌐 Web query:

Tera Rust Function trait is_safe default return value documentation

💡 Result:

In the Tera template engine for Rust, the is_safe method provided by the Function trait (and similarly by the Filter trait) has a default return value of false [1][2]. This method is used to determine whether the output of a custom function or filter should be treated as safe content [1][2]. When is_safe returns false (the default), Tera will automatically escape the output to prevent injection attacks, consistent with its default behavior of escaping content [1][2][3]. If a developer intends for their function or filter to return raw, safe content that should not be escaped, they must explicitly override is_safe to return true [1][4][2].

Citations:


Default is_safe() to Tera’s normal escaping behavior.

tera::Function::is_safe() defaults to false, but this wrapper always returns true, so every Python function registered through register_function() can bypass autoescaping in HTML/XML templates. Return false unless a specific raw-output contract is documented and intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/templating/mod.rs` around lines 41 - 43, Update the is_safe method on the
templating wrapper to return false, matching Tera’s default escaping behavior.
Ensure functions registered through register_function() remain autoescaped
unless an explicit raw-output contract is intentionally introduced.

@j03-dev
j03-dev merged commit cb54f93 into main Jul 29, 2026
15 of 16 checks passed
@j03-dev
j03-dev deleted the feat/wrap_with_mid branch July 29, 2026 19: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