Closed euri10 closed 1 year ago
Looks like escaping issue. Will check soon
@euri10 could you please PR a test suite? It works for me. There is something special in your case that I don't know about.
I can trigger it in my project very easily but have issues replicating on a simpler template, I think it happens on the stack frame trying to access an asyncpg Record attribute that does not exists, the generated FrameInfo might be causing it I'll try a MRE later
ok I have troubles to come up with an MRE but hopefuly this manual debug will help @alex-oleshkevich
in generate_html
the limit param is set at 15, so I put a breakpoint and incrementally set it manually to 1, 2 etc until it breaks the template.
it happens to break the template at 5 so there might be something in the lines of the frame code context that is not properly escaped (limit parameter s passed down getinnerframes
and retrieve limit lines of context)
My exception has 28 frames, error seems to happen on the 5th line of code context, so the below is[z.code_context[4] for z in frames]
when setting limit to 5.
Escaping issue might happen in one of those 28 items.
pprint([z.code_context[4] for z in frames])
[' if not response_started:\n',
'\n',
' def should_autoload(self, connection: HTTPConnection) -> bool:\n',
' if response_started:\n',
' handler = None\n',
' # This exception was possibly handled by the dependency but '
'it should\n',
' dependency_exception = e\n',
' elif match == Match.PARTIAL and partial is None:\n',
' def __eq__(self, other: typing.Any) -> bool:\n',
' response = await run_in_threadpool(func, request)\n',
' )\n',
' return await run_in_threadpool(dependant.call, **values)\n',
' {\n',
' context,\n',
'\n',
' return wrapper\n',
'\n',
' async def render_async(self, *args: t.Any, **kwargs: t.Any) -> str:\n',
' def join_path(self, template: str, parent: str) -> str:\n',
' <link href="{{ url_for(\'static\', path=\'/orders.css\') }}" '
'rel="stylesheet">\n',
' </div>\n',
' unread\n',
' loader = jinja2.FileSystemLoader(directory)\n',
'\n',
' pass\n',
' )\n',
' path_params.pop(key)\n',
'\n']
here the full code context for the 28 frames, the previous is the 5th and last element of all lines code context
pprint([z.code_context for z in frames])
[['\n',
' try:\n',
' await self.app(scope, receive, _send)\n',
' except Exception as exc:\n',
' if not response_started:\n'],
[' await send(message)\n',
'\n',
' await self.app(scope, receive, send_wrapper)\n',
'\n',
'\n'],
[' await load_session(connection)\n',
'\n',
' await self.app(scope, receive, send)\n',
'\n',
' def should_autoload(self, connection: HTTPConnection) -> bool:\n'],
['\n',
' if handler is None:\n',
' raise exc\n',
'\n',
' if response_started:\n'],
['\n',
' try:\n',
' await self.app(scope, receive, sender)\n',
' except Exception as exc:\n',
' handler = None\n'],
[' except Exception as e:\n',
' dependency_exception = e\n',
' raise e\n',
' if dependency_exception:\n',
' # This exception was possibly handled by the dependency but '
'it should\n'],
[' scope[self.context_name] = stack\n',
' try:\n',
' await self.app(scope, receive, send)\n',
' except Exception as e:\n',
' dependency_exception = e\n'],
[' if match == Match.FULL:\n',
' scope.update(child_scope)\n',
' await route.handle(scope, receive, send)\n',
' return\n',
' elif match == Match.PARTIAL and partial is None:\n'],
[' await response(scope, receive, send)\n',
' else:\n',
' await self.app(scope, receive, send)\n',
'\n',
' def __eq__(self, other: typing.Any) -> bool:\n'],
[' request = Request(scope, receive=receive, send=send)\n',
' if is_coroutine:\n',
' response = await func(request)\n',
' else:\n',
' response = await run_in_threadpool(func, request)\n'],
[' raise RequestValidationError(errors, body=body)\n',
' else:\n',
' raw_response = await run_endpoint_function(\n',
' dependant=dependant, values=values, '
'is_coroutine=is_coroutine\n',
' )\n'],
['\n',
' if is_coroutine:\n',
' return await dependant.call(**values)\n',
' else:\n',
' return await run_in_threadpool(dependant.call, **values)\n'],
[' buyer_username=session["username"],\n',
' )\n',
' return templates.TemplateResponse(\n',
' "orders/orders.html",\n',
' {\n'],
[' raise ValueError(\'context must include a "request" key\')\n',
' template = self.get_template(name)\n',
' return _TemplateResponse(\n',
' template,\n',
' context,\n'],
[' self.template = template\n',
' self.context = context\n',
' content = template.render(context)\n',
' super().__init__(content, status_code, headers, media_type, '
'background)\n',
'\n'],
[' def _with_tracer(tracer):\n',
' def wrapper(wrapped, instance, args, kwargs):\n',
' return func(tracer, wrapped, instance, args, kwargs)\n',
'\n',
' return wrapper\n'],
[' template_name = instance.name or DEFAULT_TEMPLATE_NAME\n',
' span.set_attribute(ATTRIBUTE_JINJA2_TEMPLATE_NAME, '
'template_name)\n',
' return wrapped(*args, **kwargs)\n',
'\n',
'\n'],
[' return self.environment.concat(self.root_render_func(ctx)) # '
'type: ignore\n',
' except Exception:\n',
' self.environment.handle_exception()\n',
'\n',
' async def render_async(self, *args: t.Any, **kwargs: t.Any) -> str:\n'],
[' from .debug import rewrite_traceback_stack\n',
'\n',
' raise rewrite_traceback_stack(source=source)\n',
'\n',
' def join_path(self, template: str, parent: str) -> str:\n'],
['{% extends "base.html" %}\n',
'{% block title %}Order{% endblock %}\n',
'{% block head %}\n',
' {{ super() }}\n',
' <link href="{{ url_for(\'static\', path=\'/orders.css\') }}" '
'rel="stylesheet">\n'],
[' <div>\n',
' <div id="content" class="content">\n',
' {% block content %}\n',
' {% endblock %}\n',
' </div>\n'],
[' <td><a href="{{ url_for(\'orders_get\', '
'order_uuid=order.orders_uuid) }}">{{ order.orders_uuid }}</a></td>\n',
' <td class="{% if not order.message_count '
'%}order-conversation-alert{% endif %}">\n',
' <a href="{{ url_for("conversation_get", '
'uuid=order.converstions_uuid ) }}">\n',
' {{ order.conversations_uuid }}\n',
' unread\n'],
[' def url_for(context: dict, name: str, **path_params: typing.Any) -> '
'str:\n',
' request = context["request"]\n',
' return request.url_for(name, **path_params)\n',
'\n',
' loader = jinja2.FileSystemLoader(directory)\n'],
[' def url_for(self, name: str, **path_params: typing.Any) -> str:\n',
' router: Router = self.scope["router"]\n',
' url_path = router.url_path_for(name, **path_params)\n',
' return url_path.make_absolute_url(base_url=self.base_url)\n',
'\n'],
[' for route in self.routes:\n',
' try:\n',
' return route.url_path_for(name, **path_params)\n',
' except NoMatchFound:\n',
' pass\n'],
[' raise NoMatchFound(name, path_params)\n',
'\n',
' path, remaining_params = replace_params(\n',
' self.path_format, self.param_convertors, path_params\n',
' )\n'],
[' if "{" + key + "}" in path:\n',
' convertor = param_convertors[key]\n',
' value = convertor.to_string(value)\n',
' path = path.replace("{" + key + "}", value)\n',
' path_params.pop(key)\n'],
[' value = str(value)\n',
' assert "/" not in value, "May not contain path separators"\n',
' assert value, "Must not be empty"\n',
' return value\n',
'\n']]
it happens on the 21th frame, 5th code context line ie manually setting this:
frames = inspect.getinnerframes(exc.__traceback__, 5)[:21] if exc.__traceback__ else []
stack = [
StackItem(
exc=exc,
solution=getattr(exc, 'solution', ''),
frames=frames,
has_vendor_frames=any(is_vendor(f) for f in frames),
)
]
frames[-1].code_context
[' <div>\n', ' <div id="content" class="content">\n', ' {% block content %}\n', ' {% endblock %}\n', ' </div>\n']
and I've got the template failing and redering like this
can you install pygments
and test if the bug still exists?
also, test if escaping this line solves it: https://github.com/alex-oleshkevich/starception/blob/master/starception/templates/code_snippet.html#L21
or email me the page as HTML to i can inspect where the problem is
can you install
pygments
and test if the bug still exists?also, test if escaping this line solves it:
master
/starception/templates/code_snippet.html#L21
pygments solves it, it is not in the dependencies ?
replacing the code
class by foobar also, same as commenting the line I suppose
roo I should rtfm, missed https://github.com/alex-oleshkevich/starception#with-syntax-highlight-support
Fixed in update. Please confirm. ;)
Fixed in update. Please confirm. ;)
upgraded to 0.6.2 and it's working fine ! thanks for the quick fix
I think this should work, it's easily reproducible using url_for it seems
I have an endpoint that renders a template
If I make a mistake in the template on the line below (it should be
order.conversations_uuid
with ana
and notconverstions_uuid
), then starception template is not correctly displayed (see screen)<a href="{{ url_for("conversation_get", uuid=order.converstions_uuid ) }}">