Python script works as pure python but not with Flask - python

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.

Related

passing a website as a parameter in REST API python; error with "/" [duplicate]

This question already has an answer here:
Capture arbitrary path in Flask route
(1 answer)
Closed 1 year ago.
I am trying to pass a website as a parameter. It works if the website does not have a "/" in it. For example: http://192.168.1.156:2434/www.cookinglight.com scrapes cooking light for all the images on it's page; however, if I pass in http://192.168.1.156:2434/https://www.cookinglight.com/recipes/chicken-apple-butternut-squash-soup then an I get an invalid response. Here is my current code:
import json
from flask import Flask, render_template
from imagescraper import image_scraper
app = Flask(__name__)
#app.route("/", methods = ['GET'])
def home():
return render_template('index.html')
#app.route("/<site>", methods = ['GET'])
def get_image(site):
return json.dumps(image_scraper(site))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=2434, debug=True)
import requests
from bs4 import BeautifulSoup
def image_scraper(site):
"""scrapes user inputed url for all images on a website and
:param http url ex. https://www.cookinglight.com
:return dictionary key:alt text; value: source link"""
search = site.strip()
search = search.replace(' ', '+')
website = 'https://' + search
response = requests.get(website)
soup = BeautifulSoup(response.text, 'html.parser')
img_tags = soup.find_all('img')
# create dictionary to add image alt tag and source link
images = {}
for img in img_tags:
try:
name = img['alt']
link = img['src']
images[name] = link
except:
pass
return images
I tried urrllib but did not have any success. Any help would be greatly appreciated! I am a student so still learning!!
UPDATE:
I believe this is the issue as described in the stackoverflow post
Need to allow encoded slashes on Apache
Flask uses / as separate between arguments in url - so you can create route("/<arg1>/<arg2>/<arg3>") (or popular in blogs route("/<year>/<month>/<day>")) and you can get values in variables arg1, arg2, arg3 - and when you try to use your url with / then it also treat it as "/<arg1>/<arg2>/<arg3>" and it tries to find route like route("/<arg1>/<arg2>/<arg3>") and it can't find it and it gives error 404.
route("/<site>") can match only string without /. site is only variable name - it doesn't mean that it will treat it as url with /
If you want to use / as part of single argument, not as separator between arguments, then you need <path:site>.
from flask import Flask
app = Flask(__name__)
#app.route("/")
def home():
return "Hello World"
#app.route("/<path:site>")
def get_image(site):
return f"OK: {site}"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=2434)#, debug=True)
See also Variable Rules
EDIT:
It has nothing to do with issue. Flask was specially created to use / as special char to separate values.

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: How to use url_for() outside the app context?

I'm writing a script to collect the emails of those users that didn't receive an email confirmation email and resend it to them. The script works obviously outside of flask app context. I would like to use url_for() but can't get it right.
def resend(self, csv_path):
self.ctx.push()
with open(csv_path) as csv_file:
csv_reader = csv.reader(csv_file)
for row in csv_reader:
email = row[0]
url_token = AccountAdmin.generate_confirmation_token(email)
confirm_url = url_for('confirm_email', token=url_token, _external=True)
...
self.ctx.pop()
The first thing I had to do was to set SERVER_NAME in config. But then I get this error message:
werkzeug.routing.BuildError: Could not build url for endpoint
'confirm_email' with values ['token']. Did you mean 'static' instead?
This is how it's defined, but I don't think it can even find this, because it's not registered when ran as script:
app.add_url_rule('/v5/confirm_email/<token>', view_func=ConfirmEmailV5.as_view('confirm_email'))
Is there a way to salvage url_for() or do I have to build my own url?
Thanks
It is much easier and proper to get the URL from the application context.
You can either import the application and manually push context with app_context
https://flask.palletsprojects.com/en/2.0.x/appcontext/#manually-push-a-context
from flask import url_for
from whereyoudefineapp import application
application.config['SERVER_NAME'] = 'example.org'
with application.app_context():
url_for('yourblueprint.yourpage')
Or you can redefine your application and register the wanted blueprint.
from flask import Flask, url_for
from whereyoudefineyourblueprint import myblueprint
application = Flask(__name__)
application.config['SERVER_NAME'] = 'example.org'
application.register_blueprint(myblueprint)
with application.app_context():
url_for('myblueprint.mypage')
We can also imagine different ways to do it without the application, but I don't see any adequate / proper solution.
Despite everything, I will still suggest this dirty solution.
Let's say you have the following blueprint with the following routes inside routes.py.
from flask import Blueprint
frontend = Blueprint('frontend', __name__)
#frontend.route('/mypage')
def mypage():
return 'Hello'
#frontend.route('/some/other/page')
def someotherpage():
return 'Hi'
#frontend.route('/wow/<a>')
def wow(a):
return f'Hi {a}'
You could use the library inspect to get the source code and then parse it in order to build the URL.
import inspect
import re
BASE_URL = "https://example.org"
class FailToGetUrlException(Exception):
pass
def get_url(function, complete_url=True):
source = inspect.getsource(function)
lines = source.split("\n")
for line in lines:
r = re.match(r'^\#[a-zA-Z]+\.route\((["\'])([^\'"]+)\1', line)
if r:
if complete_url:
return BASE_URL + r.group(2)
else:
return r.group(2)
raise FailToGetUrlException
from routes import *
print(get_url(mypage))
print(get_url(someotherpage))
print(get_url(wow).replace('<a>', '456'))
Output:
https://example.org/mypage
https://example.org/some/other/page
https://example.org/wow/456

Adding a view.py to a flask web application

I just started working with web tools and flask.I have a python script with 9 functions and I am trying to make a flask application. the main view of this application would do the same thing as my python script (in which some functions are intermediate meaning they do not produce the final output and 2 functions produce the final output). since for one route I have 9 functions, what do you suggest? shall I rename my original script as view.py and call it in the app.py (under the corresponding route) or there is better way?
Creating a separate file is always a better choice. It will make the code more readable and understandable.
Simply create a file and call the method in app.py like this.
from flask import Flask, jsonify
from views import your_method_name
# Initialize the app
app = Flask(__name__)
# Route (e.g. http://127.0.0.1:5000/my-url)
#app.route("/my-url", methods=['POST'])
def parse():
response = your_method_name() # call the method
return jsonify(response)
if __name__ == '__main__':
app.run()

send data to html dashboard in python flask while having dictionary in a function parameter

I want to send data to html in flask framework, i have a function which receives dictionary as a parameter, then there are several functions applied on dictionary. after that final result i want to render in to html page. Its been 48 hours i am trying from different blogs but didn't get precise solution.
imports ...
from other file import other_functions
from other file import other_functions_2
from other file import other_functions_3
app = Flask(__name__, template_folder='templates/')
#app.route("/dashboard")
def calculate_full_eva_web(input:dict):
calculate_gap = other_functions(input)
calculate_matrix = other_functions_2(input)
average = other_functions_3(input)
data = dict{'calculate_gap':calculate_gap, 'calculate_matrix':calculate_matrix,'average':average}
return render_template('pages/dashboard.html', data = data)
if __name__ == "__main__":
app.run(debug=True)
TRACEBACK
Methods that Flask can route to don't take dictionaries as inputs, and the arguments they do take need to be matches by a pattern in the route. (See https://flask.palletsprojects.com/en/1.1.x/api/#url-route-registrations)
You'd get the same error if you changed
#app.route("/dashboard")
def calculate_full_eva_web(input:dict):
to
#app.route("/dashboard")
def calculate_full_eva_web(input):
Your path forward depends on how you want to pass data when you make the request. You can pass key/value pairs via URL parameters and retrieve them via the request.args object. That might be close enough to what you want. (You'll need to remove the argument declaration from calculate_full_eva_web())
Something like
from flask import request
#app.route('/dashboard')
def calculate_full_eva_web():
input = request.args
...

Categories

Resources