Skip to content

Commit

Permalink
Merge pull request #11 from mrf345/testing
Browse files Browse the repository at this point in the history
Fix Markup import error and add gh pipeline
  • Loading branch information
mrf345 authored Jul 4, 2024
2 parents 70c572b + 1c07df9 commit 8eafc17
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 85 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Build
on:
push:
schedule:
# runs a new build everyday
- cron: 0 0 * * *

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, windows-latest]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '>=1.18.1'
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: '**/requirements/test.txt'
- name: Install dependencies
run: pip install -r requirements/test.txt
- name: Style check
run: |
python -m flake8
- name: Tests
run: |
python -m pytest
35 changes: 35 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Generate Coverage Badge

on:
push:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: pip install -r requirements/test.txt
- name: Set coverage percentage
run: |
echo "COVERAGE=$(pytest | awk '$1 == "TOTAL" {print $NF+0}')%" >> $GITHUB_ENV
REF=${{ github.ref }}
IFS='/' read -ra PATHS <<< "$REF"
BRANCH_NAME="${PATHS[1]}_${PATHS[2]}"
echo "BRANCH=$(echo ${BRANCH_NAME})" >> $GITHUB_ENV
- name: Generate coverage badge
uses: schneegans/[email protected]
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: bc746d7bfe356b54fbb93b2ea5d0d2a4
filename: flask_gtts__${{ env.BRANCH }}.json
label: coverage
message: ${{ env.COVERAGE }}
color: green
namedLogo: pytest
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
<h1 align='center'>Flask-gTTS</h1>
<p align='center'>
<a href='https://travis-ci.com/mrf345/flask_gtts'>
<img src='https://travis-ci.com/mrf345/flask_gtts.svg?branch=master' alt='Build Status' />
</a>
<a href='https://github.com/mrf345/flask_gtts/releases'>
<img src='https://img.shields.io/github/v/tag/mrf345/flask_gtts' alt='Latest Release' />
</a>
<a href='https://coveralls.io/github/mrf345/flask_gtts?branch=master'>
<img src='https://coveralls.io/repos/github/mrf345/flask_gtts/badge.svg?branch=master' alt='Coverage Status' />
</a>
<a href='https://www.python.org/dev/peps/pep-0008/'>
<img src='https://img.shields.io/badge/code%20style-PEP8-orange.svg' alt='Code Style' />
</a>
<a href='https://pypi.org/project/Flask-gTTS/'>
<img src='https://img.shields.io/github/v/tag/mrf345/flask_gtts' alt='Latest Release' />
</a>
<a href='https://github.com/mrf345/flask_gtts/actions/workflows/ci.yml'>
<img src='https://github.com/mrf345/flask_gtts/actions/workflows/ci.yml/badge.svg'>
</a>
<a href='https://github.com/mrf345/flask_gtts/actions/workflows/ci.yml'>
<img src='https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/mrf345/bc746d7bfe356b54fbb93b2ea5d0d2a4/raw/flask_gtts__heads_master.json' alt='Coverage Percentage' />
</a>
<br />
<img src='https://img.shields.io/pypi/pyversions/flask_gtts' alt='Supported versions' />
<br />
</p>

<h3 align='center'>A Flask extension to add support for Google Text-To-Speech (TTS).</h3>

## Install:
Expand Down
2 changes: 1 addition & 1 deletion flask_gtts/about.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '0.18'
__version__ = '0.19'
__doc__ = 'A Flask extension to help in generating Google Text-To-Speech files.'
__license__ = 'MIT'
__author__ = 'Mohamed Feddad'
Expand Down
3 changes: 0 additions & 3 deletions flask_gtts/constants.py

This file was deleted.

23 changes: 8 additions & 15 deletions flask_gtts/main.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import os
from functools import wraps
import atexit as at_exit
from functools import wraps
from shutil import rmtree
from uuid import uuid4
from flask import url_for, jsonify, Markup
from gtts import gTTS

from flask_gtts.constants import PY2
from flask import url_for, jsonify
from gtts import gTTS
from markupsafe import Markup


class gtts(object):
Expand Down Expand Up @@ -57,10 +57,7 @@ def init_app(self, app):
self.inject()

if not os.path.isdir(self.tempdir):
if PY2:
os.makedirs(self.tempdir)
else:
os.makedirs(self.tempdir, exist_ok=True)
os.makedirs(self.tempdir, exist_ok=True)

self.route and self.set_route()
self.temporary and at_exit.register(self.teardown)
Expand Down Expand Up @@ -89,13 +86,13 @@ def inject(self):
def inject_vars():
return dict(sayit=self.say, read=self.read)

def say(self, lang='en-us', text='Flask says Hi!'):
def say(self, lang='en', text='Flask says Hi!'):
'''Generate a TTS audio file.
Parameters
----------
lang : str, optional
Language to produce the TTS in, by default 'en-us'
Language to produce the TTS in, by default 'en'
text : str, optional
Text to convert into audio, by default 'Flask says Hi!'
Expand Down Expand Up @@ -163,7 +160,7 @@ def read(self, id='.toRead', mouseover=False):
.replace('{event}', 'mouseover' if mouseover else 'click'))

def set_route(self):
''' Setup a route endpont on `self.route_path/<language>/<text>` '''
''' Setup a route endpoint on `self.route_path/<language>/<text>` '''
def empty_decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
Expand All @@ -175,9 +172,5 @@ def wrapper(*args, **kwargs):
@self.app.route(self.route_path + '/<language>/<text>')
@decorator
def gtts_route(language, text):
if PY2:
language = language.encode('utf8')
text = text.encode('utf8')

mp3_link = self.say(language, text).replace('%5C', '/')
return jsonify(mp3=mp3_link), 200 if mp3_link else 500
1 change: 1 addition & 0 deletions requirements/main.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
flask
gTTs
MarkupSafe
30 changes: 15 additions & 15 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from os import path
from setuptools import setup


supported_versions = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
basedir = path.abspath(path.dirname(__file__))
long_description = ''
requirements = []
test_requirements = []
requirements_path = path.join(basedir, 'requirements')


Expand All @@ -15,15 +14,21 @@
with open(path.join(basedir, 'README.md')) as f:
long_description += f.read()

with open(path.join(requirements_path, 'main.txt')) as f:
requirements += [line for line in f.read().split('\n') if line]
test_requirements += requirements

with open(path.join(requirements_path, 'test.txt')) as f:
test_requirements += [
line for line in f.read().split('\n')
if line and not line.startswith('-r')]
if path.isdir(requirements_path):
with open(path.join(requirements_path, 'main.txt')) as f:
requirements += [line for line in f.read().split('\n') if line]
else:
requires_path = path.join(
path.join(basedir, "Flask_gTTS.egg-info"), "requires.txt"
)

with open(requires_path) as f:
requirements += [line for line in f.read().split("\n") if line]

supported_python_classifiers = [
"Programming Language :: Python :: {0}".format(v) for v in supported_versions
]

setup(
name='Flask-gTTS',
Expand All @@ -41,16 +46,11 @@
zip_safe=False,
platforms='any',
install_requires=requirements,
setup_requires=test_requirements,
package_data={'flask_gtts': ['read.html']},
keywords=['flask', 'extension', 'google', 'text', 'speech',
'gTTS', 'TTS', 'text-to-speech'],
classifiers=[
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
*supported_python_classifiers,
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
Expand Down
55 changes: 21 additions & 34 deletions tests/integration.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import os
from flask_gtts import main
from gtts import gTTS
from flask_gtts import gtts
from pytest import fixture
from importlib import import_module

from .setup import app, language, text, eng, message
import pytest
from flask_gtts import gtts, main

from .setup import app, language, text, extension, message

# workaround for py2 vs py3 mock import
unittest = import_module('unittest')
mock = getattr(unittest, 'mock', None) or import_module('mock')


@fixture
@pytest.fixture
def client():
app.config['TESTING'] = True
app.config['STATIC_FOLDER'] = 'static'
app.config['SERVER_NAME'] = 'localhost'
extension.files = {}
app.logger = mock.MagicMock()
client = app.test_client()
yield client
Expand All @@ -25,59 +25,46 @@ def client():
def test_gtts_remote_error_handling(client, monkeypatch):
mock_gtts = mock.MagicMock()
exception = AttributeError('something went wrong')

def mock_save():
raise exception

mock_gtts().save = mock_save
mock_gtts().save.side_effect = exception
monkeypatch.setattr(main, 'gTTS', mock_gtts)

resp = client.get('%s/%s/%s' % (eng.route_path, language, text))
resp = client.get('%s/%s/%s' % (extension.route_path, language, text))

assert resp.status_code == 500
assert resp.json.get('mp3') == ''
assert app.logger.exception.called_once_with(exception)
app.logger.exception.assert_called_once_with(exception)


def test_false_app_gtts(client):
''' test gtts false app input. '''
try:
gtts(app=None)
except Exception as e1:
assert type(e1) == AttributeError
try:
gtts(app=app, temporary=200)
except Exception as e2:
assert type(e2) == TypeError
assert type(e1) is AttributeError

assert gtts(app=app, tempdir='/').tempdir.endswith('flask_gtts') is True
assert extension.tempdir.endswith('flask_gtts') is True


def test_template_sayit_mp3(client):
''' test sayit function in the template returns .mp3 '''
assert client.get('/say').data.endswith(b'.mp3')
assert eng.say(language, text).endswith('.mp3')
assert extension.say(language, text).endswith('.mp3')


def test_template_sayit_valid(client):
def test_template_sayit_file_exists(client):
''' test validity of mp3 file from template sayit '''
base_dir = app.static_folder
file_name = 'testing.mp3'
file_path = os.path.join(base_dir, file_name)
static_file_relative = client.get('/say').data[1:].decode('utf-8')
static_file_path = os.path.join(eng.tempdir,
extension.files = {}
resp = client.get('/say')
static_file_relative = resp.data[1:].decode('utf-8').split('%5C')[-1]
static_file_path = os.path.join(extension.tempdir,
os.path.basename(static_file_relative))

gTTS(text=text, lang=language).save(file_path)

with open(static_file_path, 'rb') as resp:
with open(file_path, 'rb') as default:
assert resp.read() == default.read()
assert os.path.isfile(static_file_path)


def test_dynamic_route_mp3(client):
''' test dynamic route mp3 response '''
json = client.get('%s/%s/%s' % (eng.route_path, language, text)).json
json = client.get('%s/%s/%s' % (extension.route_path, language, text)).json

assert json.get('mp3', '').endswith('.mp3') is True

Expand All @@ -89,12 +76,12 @@ def test_template_read_js(client):

def test_route_decorator(client):
''' test route decorator response '''
response = client.get('%s/%s/%s' % (eng.route_path, language, text),
response = client.get('%s/%s/%s' % (extension.route_path, language, text),
headers={'prevent': True})

assert response.data.decode('utf-8') == message


def test_cleanup_before_exit(client):
''' test cleanup func before exit '''
assert eng.teardown() is None
assert extension.teardown() is None
10 changes: 5 additions & 5 deletions tests/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ def wrapper(*args, **kwargs):
return wrapper


eng = gtts(app,
route=True,
route_decorator=prevent_header,
failsafe=True)
extension = gtts(app,
route=True,
route_decorator=prevent_header,
failsafe=True)
text = 'something to say'
language = 'en-uk'
language = 'en'


@app.route('/say')
Expand Down

0 comments on commit 8eafc17

Please sign in to comment.