Flask-Misaka can't recognize fenced code in markdown strings - python

I'm using Flask-Misaka with Flask to render a markdown string to html. However, it seems that the Flask-Misaka can't recognize fenced code. It does removed the back-ticks, but no colored block is displayed. I have tried with versions 0.4.0 and 0.4.1.
app.py
from flask import Flask, render_template
from flask_misaka import Misaka, markdown
app = Flask(__name__)
Misaka(app, fenced_code=True)
TEST_MD = markdown("```block```\n", fenced_code=True)
#app.route("/", methods=['GET'])
def index():
return render_template('{{s|markdown}}', s=TEST_MD)

The issue is that you are missing a stylesheet. If you look at the HTML output of Flask it will show <p><code>block</code></p>\n. So the fenced code is seen and the HTML output is rendered correctly.
Short example which directly shows the result when the code is executed:
from flask import Flask, render_template_string
from flask_misaka import markdown
app = Flask(__name__)
with app.app_context():
render_template_string('{{s}}', s=markdown("```block```\n", fenced_code=True))

Related

How would I serve this html file with Flask (nota a template)

I'm using apidoc to generate a documentation website. On running, it creates a folder called doc, which contains an index.html file and accompanying css and js.
I'd like to be able to serve this folder with a Flask server, but can't work out how to do it.
My folder structure looks like this
-root
--- doc/ #contains all of the static stuff
--- server.py
I've tried this, but can't get it to work:
app = Flask(__name__, static_url_path="/doc")
#app.route('/')
def root():
return app.send_from_directory('index.html')
One of the problems is that all of the static files referenced in the index.html generated by apidoc are relative to that page, so /js/etc. doesn't work, since it's actually /doc/js...
It would be great if someone could help me with the syntax here. Thanks.
I spot three problems in code.
a) you do not need to use static_url_path, as send_from_directory is independent of it
b) when I try to run above code, and go to /, I get a AttributeError: 'Flask' object has no attribute 'send_from_directory' - this means translates to app.send_from_directory is wrong - you need to import this function from flask, ie from flask import send_from_directory
c) when I then try to run your code, I get a TypeError: send_from_directory() missing 1 required positional argument: 'filename', which means send_from_directory needs another argument; it needs both a directory and a file
Putting this all together you get something like this:
from flask import Flask
from flask import send_from_directory
app = Flask(__name__)
#app.route("/")
def index():
return send_from_directory("doc", "index.html")
As a takeway (for myself):
reading the documentation helps a lot ( https://flask.palletsprojects.com/en/1.1.x/api/ )
having a close look at the - at first scary - error messages gives really good hints on what to do

flask bokeh server - figure does not render even locally

I can't run bokeh server within flask beind apache so now I'm trying to serve bokeh within flask locally. The figure does not render.
Here is the flask code:
from flask import Flask, render_template
app = Flask(__name__)
from bokeh.embed import server_document
#app.route("/")
def techblog():
try:
tag = server_document(url=r'/bokeh', relative_urls=True)
return render_template('techblog.html', tag=tag)
except Exception as e:
return str(e)
if __name__ == '__main__':
app.run(debug=True)
Here is the bokeh code:
from numpy.random import random
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.layouts import column, widgetbox
from bokeh.models import Button, ColumnDataSource
from bokeh.server.server import Server
def run(doc):
fig = figure(title='random data', width=400, height=200, tools='pan,box_zoom,reset,save')
source = ColumnDataSource(data={'x': [], 'y': []})
fig.line('x', 'y', source=source)
def click(n=100):
source.data = {'x': range(n), 'y': random(n)}
button = Button(label='update', button_type='success')
button.on_click(click)
layout = column(widgetbox(button), fig)
doc.add_root(layout)
click()
# configure and run bokeh server
kws = {'port': 5000, 'prefix':'/bokeh','allow_websocket_origin': ['127.0.0.1']}
server = Server(run, **kws)
server.start()
# if __name__ == '__main__':
server.io_loop.add_callback(server.show, '/')
server.io_loop.start()
Here is my html template:
<h1 style='color:blue'>Hello There!</h1>
</br>
{{ tag|safe }}
</br>
{{ tag }}
I'm running flask app via python. And on a separate command processor I run bokeh app via,
bokeh serve --allow-websocket-origin=localhost:5000 filename.py
I only get the tag without "safe" as
<script src="/bokeh/autoload.js?bokeh-autoload-element=1001&bokeh-app-path=/bokeh" id="1001"></script>
And I have this message on flask console. It is a standart 404:
"GET /bokeh/autoload.js?bokeh-autoload-element=1000&bokeh-app-path=/bokeh HTTP/1.1" 404 -
That'a all. No figure or button is rendered. What should I change to see the figure?
Edit: I've specified the port and the prefix in the bokeh code. The outcome has not changed.
Edit 2: I've add the console msj for 404 error on flask console.
There are a few things wrong:
You have not configured a port, so the Bokeh server will use its default port of 5006 (not 5000)
You have not configured an app path, so the Bokeh server will serve the app from its default location of / (not /bokeh)
It's also worth mentioning that if you whitelist localhost as an allow websocket origin, then it literally must be localhost in the URL bar (i.e. not 127.0.0.1, they are not interchangeable in this context)
Lastly, it's a lot of extra work to put the Bokeh app code in a plain python script that calls Server manually, etc. You could just put the contents of run in a file and call bokeh serve --port=5000 app.py and then the app will be available at localhost:5000/app
I add some details to #bigreddot's answer.
Code didn't work for me too. I even found exactly this code in some tutorial and main problem was that it was using nginx which was converting /bokeh to http://127.0.0.1/bokeh. But on local computer without nginx I have to change all urls.
EDIT: I found tutorial with this code https://rbtechblog.com/blog/deploy_bokeh_app
I start changing code and reduce it to create minimal code which works. I made changes similar to changes mentioned by bigreddot.
Bokeh
I put code directly in file without def and without Server
filename.py
from numpy.random import random
from bokeh.io import curdoc
from bokeh.plotting import figure
from bokeh.layouts import column, widgetbox
from bokeh.models import Button, ColumnDataSource
def click(n=100):
source.data = {'x': range(n), 'y': random(n)}
fig = figure(title='random data', width=800, height=400, tools='pan,box_zoom,reset,save')
source = ColumnDataSource(data={'x': [], 'y': []}) # place for data
fig.line('x', 'y', source=source) # draw plot
button = Button(label='update', button_type='success') # create button
button.on_click(click) # assign function to button
layout = column(fig, widgetbox(button)) # create layout
curdoc().add_root(layout) # add all to document
click() # generate random data at start
Now I can run it in console
bokeh serve filename.py
and I can see it in web browser using url
http://localhost:5006/filename
(bokeh should display this url in console after start - if you will use different file or options then you may see different url)
At this moment I don't need any other options but later I will need --allow-websocket-origin but I will describe it later.
BTW: I not use name bokeh.py because it can make problem to import original bokeh.
Flask
Because I don't use nginx which could convert /bokeh to http://localhost:5006/filename so I have to use full url in serve_document
For test I used render_template_string instead of render_template so I don't have to create templates/index.html so it will easier to copy and test code.
I removed try/except to get more details if there will be error.
app.py
from flask import Flask, render_template, render_template_string
from bokeh.embed import server_document
app = Flask(__name__)
#app.route("/")
def index():
tag = server_document(url='http://localhost:5006/filename')
#return render_template('index.html', tag=tag)
return render_template_string('''<div>{{ tag|safe }}</div>''', tag=tag)
if __name__ == '__main__':
app.run(debug=True)
Now I can run it
python app.py
and I can open page in web browser using standard url
http://localhost:5000/
but I will not see plot and bokeh will display
Refusing websocket connection from Origin 'http://127.0.0.1:5000';
use --allow-websocket-origin=127.0.0.1:5000
or set BOKEH_ALLOW_WS_ORIGIN=127.0.0.1:5000 to permit this;
currently we allow origins {'localhost:5006'}
so I have to restart bokeh with this option
bokeh serve filename.py --allow-websocket-origin=127.0.0.1:5000
(as bigreddot mentioned it has to be 127.0.0.1, not localhost)
And now flask should display plot.
BTW: if I use template without any HTML tag
render_template_string('''{{ tag|safe }}''', tag=tag)
then browser may treat all code (<script ...></scrip>) as part of <head></head> and it will not display it because browser never display elements which are in <head></head> even if there are correct images or plots.

Python script works as pure python but not with Flask

I'm working on some code that pulls course info from Canvas. As pure python, it works fine. If I try to incorporate it with Flask, I get the following error
requests.exceptions.MissingSchema: Invalid URL 'run/api/v1/courses/1234567': No schema supplied. Perhaps you meant http://run/api/v1/courses/1234567?
This is the code in question:
Canvas file
import sys
from canvasapi import Canvas
def getinfo():
canvasurl = "https://canvas.instructure.com/";
canvastoken = #Redacted for this example
try:
canvastoken = sys.argv[1];
canvasurl = sys.argv[2];
except:
print()
#Create a new canvas object passing in the newly aquired url and token
canvas = Canvas(canvasurl, canvastoken);
#print(canv)
# Create a new course oject -- passing in course number as a parameter
# Course number is currently hard coded
print(canvas.get_course(1234567))
Flask file code (the file that I'm trying to run):
from flask import Flask
import canvas
canvas.getinfo()
app = Flask(__name__)
#app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
No schema provided usually means you haven't specified the http:// or https:// in the URL.
In the code you provided, I don't see any reference to a run/api/v1/courses/1234567. One possibility is if you are using the url_for method from requests anywhere in your code, try setting _external=True:
url = url_for('relativeURL', _external=True)
This allows Flask to construct an absolute URL (i.e., a URL with domain included).
If you aren't using url_for, check other places in your code where you might be omitting the http or https from the URL.
If you update your question to include the part that refers to the offending URL, we might be able to provide more specific help.

python (flask) how to render html links from plain text input

I'm using python, flask, sqlalchemy/sqllite, jinja for my workflow.
I have a HEAP of text stored in the database and it's got paragraphs of plain text and in that text will be things like email#address.com, somedomain.com, www.somedomain.com, https://somedomain.com, https://www.somedomain.com, etc etc.
Is there a solution that exists for parsing the data as I load it and rendering it out as html links when I draw to the browser? Currently it just renders as text without links being active.
Thanks so much
A quick solution would be to use an existing library, python-textile, built for such a task.
For example:
import textile
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def index():
plain_text = 'This text contains a link to https://stackoverflow.com'
marked_up_text = textile.textile(plain_text)
return render_template('layout.html', body=marked_up_text)
layout.html
{{body | safe}}

How to use a Jinja2 extension direct from a Flask render_template

I'm working on a Flask app that is working just fine, until I try to add the following line to a template to be rendered:
{% do totals.update({tier: 0}) %}
The current code for rendering the template uses Flask's render_template():
from flask import Flask, Response, request, session
from flask import render_template
app = Flask(__name__)
..
return render_template(<template.htlm>,...)
This fails with with following error:
TemplateSyntaxError: Encountered unknown tag 'do'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'.
The obvious fix is to add the jinja2.ext.do extension to jinja. I have been able to do so successfully using Jinja2 directly, as per:
from jinja2 import Environment, PackageLoader
ENV = Environment(loader=PackageLoader('ticket_app', 'templates'), extensions=['jinja2.ext.do'])
...
TEMP = ENV.get_template('div_' + div_id + '.html')
return TEMP.render(sales_new=sales_new, event_config=event_config)
However, I would prefer to not use Jinja2 directly... The app was only using Flask and render_template() before, and as render_template() uses Jinja2 under the hood (as far as I understand) it seems that it should be possible to make render_template() understand the jinja2.ext.do extension (or any other extension for that matter).
So far, I've tried the following:
app = Flask(__name__)
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config['EXPLAIN_TEMPLATE_LOADING'] = True
env = app.jinja_env
env.add_extension('jinja2.ext.do')
While the above does not throw an error, it is also not causing it to make render_template() understand the jinja2.ext.do extension.
Any suggestions? Should this be possible? If so, how?
Update for Flask v.2.0 :
Since v.2.0, the below solution raise a KeyError: 'extensions' exception. Try this instead.
For Flask v.1.1, you can directly access to the Jinja extensions loaded by Flask with the Flask.jinja_options dictionary. In your case, adding just this line should do the trick:
app = Flask(__name__)
app.jinja_options['extensions'].append('jinja2.ext.do')
Make sure you update your Flask (using pip: pip install -U Flask).
I actually found that the option that Zatiranyk outlined broke sometimes (specifically when I tried to use Flask-SocketIO.
app.jinja_options['extensions'].append('jinja2.ext.do')
The solution that I found was to use the below option instead:
app.jinja_env.add_extension('jinja2.ext.do')

Categories

Resources