Difficulty accessing Google Text-to-Speech API from within Flask app - python

I am having difficulties accessing Google's texttospeech Python API from within a Flask app. The app is running in a container with Debian 10, Nginx/Gunicorn, and Python 3.7 installed. In the code below, the client connects successfully, but the synthesize_speech request hangs indefinitely (without any error message). When I run the same code from a Python script in the same container without Flask, the speech synthesis request is successful. However, I can call other external APIs, such as those at AWS, from my Flask app without any problems.
What could be causing this or how I could go about diagnosing the problem? I have tried switching to version 1.0.1 of the texttospeech library, but without success. Presumably the problem isn't my credentials, which I believe I have set up correctly, as otherwise the connection request wouldn't be successful.
from google.cloud import texttospeech
# Connect (this is successful)
client = texttospeech.TextToSpeechClient()
input_text = texttospeech.SynthesisInput(text="God dag")
voice_parameters = texttospeech.VoiceSelectionParams(
language_code="sv-SE",
name="sv-SE-Wavenet-A"
)
audio_config = texttospeech.AudioConfig(
audio_encoding=texttospeech.AudioEncoding.MP3
)
# Synthesize speech (this never completes)
response = client.synthesize_speech(
request={
"input": input_text,
"voice": voice_parameters,
"audio_config": audio_config
}
)
pip freeze
google-api-core==1.22.1
google-auth==1.20.1
google-cloud-texttospeech==2.2.0
googleapis-common-protos==1.52.0
grpcio==1.31.0
...

It turns out the issue was caused by monkey_patching in gunicorn's gevent worker class. I have managed to resolve the issue by changing the worker_class in gunicorn to "sync", as suggested on this page for a similar issue:
https://github.com/googleapis/google-cloud-python/issues/5848

Related

Flask-Socketio emitting from an external process

I've been working on a project using flask, flask-socketio and redis
I have a server, and some modules I would like to be able to emit from outside of the server file.
server.py
from flask import Flask, Response, request, json
from flask_socketio import SocketIO, join_room, leave_room, emit
app = Flask(__name__)
socketio = SocketIO()
socketio.init_app(
app,
cors_allowed_origins="*",
message_que="redis://127.0.0.1:6379/0"
)
#socketio.on('ready')
def ready(data):
socketio.emit('rollCall', { 'message': 'Ive got work for you' }, room='Ready')
...
jobque.py
from modules.server.server import socketio
...
socketio.emit('rollCall', { 'message': 'Ive got work for you' }, room='Ready')
As it's currently configured, emits from the server file all work, the clients respond and they can talk back and forth. But when jobque goes to emit the same message, nothing happens. There's no error, and the client doesn't hear it.
I'm also using redis for things other than the websockets, I can get and set from it with no problem, in both files.
What do I need to do to get this external process to emit? I've looked through the flask-socketio documentation and this is exactly how they have it setup.
I've also tries creating a new SocketIO object inside jobque.py instead of importing the one form the server, but the results are the same
socket = SocketIO(message_queue="redis://127.0.0.1:6379/0")
socketio.emit('rollCall', { 'message': 'Ive got work for you' }, room='Ready')
I also went and checked if I could see the websocket traffic in redis with the message que setup using redis-cli > MONITOR, but I don't see any. I only see the operations I'm using redis for directly with the redis module. This makes me think the message que isn't actually being used, but I can't know for sure.
Unfortunately spelled message_queue as message_que was the issue.
Creating a new SocketIO instance without the app works now.

Python slackclient oauth.access call returning 'invalid_code' error

I am using ngrok + flask + python slackclient to respond to Slack API's OAuth Flow and I am receiving an invalid_code error.
I am following steps described in slackclient docs and my code is pretty simple so far:
import os
import slack
from flask import Flask, request
app = Flask(__name__)
SLACK_CLIENT_ID = os.environ['SLACK_CLIENT_ID']
SLACK_CLIENT_SECRET = os.environ['SLACK_CLIENT_SECRET']
#app.route('/install', methods=['GET', 'POST'])
def install():
# Retrieve the auth code from the request params
auth_code = request.args['code']
# An empty string is a valid token for this request
client = slack.WebClient(token='')
# Request the auth tokens from Slack
response = client.oauth_access(
client_id=SLACK_CLIENT_ID,
client_secret=SLACK_CLIENT_SECRET,
code=auth_code
)
print(response)
if __name__ == '__main__':
app.run()
I initiate the App installation from the "Add" button in my Slack workspace's Manage Apps page. I can confirm that I am receiving a code as expected once the installation is initiated, and it is being correctly passed through to the slack.BaseClient.api_call() function that eventually sends the request to https://slack.com/api/oauth.access.
I expect the response from the oauth_access call to be a JSON object containing my access tokens, however, I get:
slack.errors.SlackApiError: The request to the Slack API failed.
The server responded with: {'ok': False, 'error': 'invalid_code', 'warning': 'superfluous_charset', 'response_metadata': {'warnings': ['superfluous_charset']}}
I tried to send a POST with curl to Slack's endpoint with the required parameters and it worked as expected. I also tried with requests.post() and that also worked as expected. So I suspect that I am using slackclient incorrectly or have misunderstood something. Can anyone help point me in the right direction?
It seems to be a problem with the Python SDK. I think this pull request fixes this
https://github.com/slackapi/python-slackclient/pull/527
In the meantime it may be easier to revert to version 2.1.0
This issue is solved in v2.2.1 of slackclient
See changelog:
[WebClient] Oauth previously failed to pass along credentials properly. This is fixed now. #527

401 Unauthorized making REST Call to Azure API App using Bearer token

I created 2 applications in my Azure directory, 1 for my API Server and one for my API client. I am using the Python ADAL Library and can successfully obtain a token using the following code:
tenant_id = "abc123-abc123-abc123"
context = adal.AuthenticationContext('https://login.microsoftonline.com/' + tenant_id)
token = context.acquire_token_with_username_password(
'https://myapiserver.azurewebsites.net/',
'myuser',
'mypassword',
'my_apiclient_client_id'
)
I then try to send a request to my API app using the following method but keep getting 'unauthorized':
at = token['accessToken']
id_token = "Bearer {0}".format(at)
response = requests.get('https://myapiserver.azurewebsites.net/', headers={"Authorization": id_token})
I am able to successfully login using myuser/mypass from the loginurl. I have also given the client app access to the server app in Azure AD.
Although the question was posted a long time ago, I'll try to provide an answer. I stumbled across the question because we had the exact same problem here. We could successfully obtain a token with the adal library but then we were not able to access the resource I obtained the token for.
To make things worse, we sat up a simple console app in .Net, used the exact same parameters, and it was working. We could also copy the token obtained through the .Net app and use it in our Python request and it worked (this one is kind of obvious, but made us confident that the problem was not related to how I assemble the request).
The source of the problem was in the end in the oauth2_client of the adal python package. When I compared the actual HTTP requests sent by the .Net and the python app, a subtle difference was that the python app sent a POST request explicitly asking for api-version=1.0.
POST https://login.microsoftonline.com/common//oauth2/token?api-version=1.0
Once I changed the following line in oauth2_client.py in the adal library, I could access my resource.
Changed
return urlparse('{}?{}'.format(self._token_endpoint, urlencode(parameters)))
in the method _create_token_url, to
return urlparse(self._token_endpoint)
We are working on a pull request to patch the library in github.
For the current release of Azure Python SDK, it support authentication with a service principal. It does not support authentication using an ADAL library yet. Maybe it will in future releases.
See https://azure-sdk-for-python.readthedocs.io/en/latest/resourcemanagement.html#authentication for details.
See also Azure Active Directory Authentication Libraries for the platforms ADAL is available on.
#Derek,
Could you set your Issue URL on Azure Portal? If I set the wrong Issue URL, I could get the same error with you. It seems that your code is right.
Base on my experience, you need add your application into Azure AD and get a client ID.(I am sure you have done this.) And then you can get the tenant ID and input into Issue URL textbox on Azure portal.
NOTE:
On old portal(manage.windowsazure.com),in the bottom command bar, click View Endpoints, and then copy the Federation Metadata Document URL and download that document or navigate to it in a browser.
Within the root EntityDescriptor element, there should be an entityID attribute of the form https://sts.windows.net/ followed by a GUID specific to your tenant (called a "tenant ID"). Copy this value - it will serve as your Issuer URL. You will configure your application to use this later.
My demo is as following:
import adal
import requests
TenantURL='https://login.microsoftonline.com/*******'
context = adal.AuthenticationContext(TenantURL)
RESOURCE = 'http://wi****.azurewebsites.net'
ClientID='****'
ClientSect='7****'
token_response = context.acquire_token_with_client_credentials(
RESOURCE,
ClientID,
ClientSect
)
access_token = token_response.get('accessToken')
print(access_token)
id_token = "Bearer {0}".format(access_token)
response = requests.get(RESOURCE, headers={"Authorization": id_token})
print(response)
Please try to modified it. Any updates, please let me know.

Cannot get authentication with google api from python script to work - Error: redirect_uri_mismatch

I am trying to create a trivial python script that allows me to bulk add a list of youtube videos to a playlist of mine.
The problem I am having is getting this script to get authenticated to the google api with my apps credentials.
I am basically just using the sample authentication script at https://developers.google.com/google-apps/calendar/quickstart/python
and this stackoverflow question (Adding youtube video to playlist using Python)
The main stopping point is that I keep getting an Error: redirect_uri_mismatch. Since I am calling the script from the commandline on my laptop, the error is saying: The redirect URI in the request: http://localhost:8080/ did not match a registered redirect URI.
I have set http://localhost:8080 as the JavaScript origins and http://localhost:8080/oauth2callback as the Redirect URIs
And i am using the following (as run from the python shell):
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client import tools
from oauth2client.tools import argparser, run_flow
import argparse, sys, os
flow = flow_from_clientsecrets('path to my CLIENT_SECRETS.json file', scope='https://www.googleapis.com/auth/youtube')
store = Storage('config/%s-oauth2.json' % sys.argv[0])
parser = argparse.ArgumentParser(parents=[tools.argparser])
flags = parser.parse_args()
credentials = run_flow(flow, store, flags)
then the terminal opens my browser and in the browser I get the 400 error. The following gets printed to the terminal:
Your browser has been opened to visit:
https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&response_type=code&client_id=my-CLIENT-ID&access_type=offline
If your browser is on a different machine then exit and re-run this
application with the command-line parameter
--noauth_local_webserver
I am not sure what I am really supposed to be putting as the redirect URIs and javascript origins, but I don't intend to run this anywhere else other than as a python script from the terminal of my laptop. The documentation provides https://example.com/ and https://example.com/oauth2callback as pre populated values, but clearly that's not where I am running this "app" from and I know that's just placeholder stuff.
UPDATE: not sure why, but i realize that the url the app is sending me to has the redirect_uri parameter set to just http://localhost:8080/, if i add oauth2callback to the uri than i get sent to the screen where it asks me to accept management of the youtube account. so that's odd.
UPDATE 2: and if I change my redirect uris to just http://localhost:8080/ via the developer console, i can get my credentials, but I am sure this is not the best way to be going about this.
If you are just running this through you terminal than you can use the native host by creating a client ID as an installed app and then selecting other. If you are trying to do this using the web app client ID and only want it to be local then you would use these:
JavaScript origins: http://localhost:your_port_here/
Redirect URIs: http://localhost:your_port_here/url_path
where url_path is the place you want google to send you after authentication.
Edit: the tutorial code only works if you are using a native app. If you are indeed planning to create a web app there are separate instructions.
The redirect_uri parameter set to just http://localhost:8080/ is by design. When you call tools.run_flow() it is spinning up a server of it's own behind the scenes and initiating the oauth flow. It is then expecting the redirect_uri to redirect back to the server it spun up (which is listening on port 8080) so that it can use the access_token it receives to create the credentials and put them in storage.

Flask streaming doesn't work on my machine

Related to this question.
I'm trying to use Flask streaming and having difficulty doing so, it seems my example works on any machine I try it on other than my own. I'm running Flask 0.10.1, Werkzeug 0.9.4, and Python 2.7.6. I have reinstalled Flask and Werkzeug with no effect. If someone could suggest a way to debug this I would be very grateful.
The problem I experience is that data doesnt get sent to the client while the stream is open, it is only sent once the stream is closed (eg in the example below when the generator function returns).
#!/usr/bin/env python
from flask import Flask, Response
from time import sleep
def stream():
n = 10
while n > 0:
yield "data: hi\n\n"
sleep(0.5)
n = n - 1
app = Flask(__name__)
#app.route("/events")
def streamSessionEvents():
return Response(
stream(),
mimetype="text/event-stream"
)
#...also has a route to output html/js client to consume the above
app.run(threaded=True)
Revisited this recently, it was indeed the virus scanner messing with my network traffic.
I'm using Sophos, and found that if I add the address of the machine hosting my Flask application to the virus scanner's "allowed sites" list then my SSE events are received correctly.

Categories

Resources