diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml new file mode 100644 index 0000000..1164fa0 --- /dev/null +++ b/.github/workflows/publish_release.yml @@ -0,0 +1,40 @@ +# This workflow will generate a release distribution and upload it to PyPI + +name: Publish Build and GitHub Release +on: + push: + branches: + - master + +jobs: + tag_release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Get Version + run: | + VERSION=$(python setup.py --version) + echo "VERSION=${VERSION}" >> $GITHUB_ENV + - uses: ncipollo/release-action@v1 + with: + token: ${{secrets.GITHUB_TOKEN}} + tag: ${{env.VERSION}} + build_and_publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install Build Tools + run: | + python -m pip install build wheel + + - name: Build Distribution Packages + run: | + python setup.py bdist_wheel + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@master + with: + password: ${{secrets.PYPI_TOKEN}} diff --git a/.github/workflows/publish_test_build.yml b/.github/workflows/publish_test_build.yml new file mode 100644 index 0000000..61bd63b --- /dev/null +++ b/.github/workflows/publish_test_build.yml @@ -0,0 +1,39 @@ +# This workflow will generate a distribution and upload it to PyPI + +name: Publish Alpha Build +on: + push: + branches: + - dev + paths-ignore: + - 'version.py' + +jobs: + build_and_publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install Build Tools + run: | + python -m pip install build wheel + - name: Increment Version + run: | + VER=$(python setup.py --version) + python version_bump.py + - name: Push Version Change + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Increment Version + - name: Build Distribution Packages + run: | + python setup.py bdist_wheel + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@master + with: + password: ${{secrets.PYPI_TOKEN}} diff --git a/.github/workflows/pull_master.yml b/.github/workflows/pull_master.yml new file mode 100644 index 0000000..aae67da --- /dev/null +++ b/.github/workflows/pull_master.yml @@ -0,0 +1,21 @@ +# This workflow will generate a PR for changes in cert into master + +name: Pull to Master +on: + push: + branches: + - dev + workflow_dispatch: + +jobs: + pull_changes: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: pull-request-action + uses: repo-sync/pull-request@v2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + pr_reviewer: 'neonreviewers' + pr_assignee: 'neondaniel' + pr_draft: true diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 0000000..39d727d --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,48 @@ +# This workflow will run unit tests + +name: Run Unit Tests +on: + pull_request: + workflow_dispatch: + +jobs: + build_tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install Build Tools + run: | + python -m pip install build wheel + - name: Build Distribution Packages + run: | + python setup.py bdist_wheel + unit_tests: + strategy: + matrix: + python-version: [ 3.6, 3.7, 3.8, 3.9 ] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up python ${{ matrix.python-version } + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version } + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements/requirements.txt -r requirements/test_requirements.txt + - name: Test TTS + run: | + pytest tests/test_tts.py --junitxml=tests/tts-test-results.xml + - name: Upload TTS test results + uses: actions/upload-artifact@v2 + with: + name: pytest-results-${{ matrix.python-version } + path: tests/tts-test-results.xml + if: ${{ always() }} diff --git a/.gitignore b/.gitignore index b6e4761..77b0a90 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,6 @@ dmypy.json # Pyre type checker .pyre/ + +# PyCharm project +.idea/ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0f84944 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2021 Neongecko.com Inc. +# BSD-3 License + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ce25faa --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# NeonAI Template TTS Plugin # TODO: Name +[Mycroft](https://mycroft-ai.gitbook.io/docs/mycroft-technologies/mycroft-core/plugins) compatible +TTS Plugin for Template Text-to-Speech. # TODO: Update Name + +# Configuration: +# TODO: Specify any optional or required configuration values +```yaml +tts: + module: tts_module_name # TODO: Unique Entry Point Name + tts_module_name: {} # TODO: Any module config +``` \ No newline at end of file diff --git a/neon_tts_plugin_TODO_NAME/__init__.py b/neon_tts_plugin_TODO_NAME/__init__.py new file mode 100644 index 0000000..d025010 --- /dev/null +++ b/neon_tts_plugin_TODO_NAME/__init__.py @@ -0,0 +1,93 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2022 Neongecko.com Inc. +# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, +# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo +# BSD-3 License +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from typing import Optional +from neon_utils.configuration_utils import get_neon_tts_config +from neon_utils.logger import LOG +from neon_utils.parse_utils import format_speak_tags + +try: + from neon_audio.tts import TTS, TTSValidator +except ImportError: + from ovos_plugin_manager.templates.tts import TTS, TTSValidator +from neon_utils.metrics_utils import Stopwatch + + +class TemplateTTS(TTS): # TODO: Replace 'Template' with TTS name + def __init__(self, lang="en-us", config=None): + config = config or get_neon_tts_config().get("tts_module_name", {}) # TODO: Update name + super(TemplateTTS, self).__init__(lang, config, TemplateTTSValidator(self), + audio_ext="mp3", # TODO: Specify output audio format + ssml_tags=["speak"]) # TODO: Specify valid SSML tags + # TODO: Optionally define any class parameters + + def get_tts(self, sentence: str, output_file: str, speaker: Optional[dict] = None): + stopwatch = Stopwatch() + speaker = speaker or dict() + + # TODO: speaker params are optionally defined and should be handled whenever defined + # # Read utterance data from passed configuration + # request_lang = speaker.get("language", self.lang) + # request_gender = speaker.get("gender", "female") + # request_voice = speaker.get("voice") + + # TODO: Below is an example of a common ambiguous language code; test and implement or remove + # # Catch Chinese alt code + # if request_lang.lower() == "zh-zh": + # request_lang = "cmn-cn" + + to_speak = format_speak_tags(sentence) + LOG.debug(to_speak) + if to_speak: + with stopwatch: + pass + # TODO: Get TTS audio here + + LOG.debug(f"TTS Synthesis time={stopwatch.time}") + + return output_file, None + + +class TemplateTTSValidator(TTSValidator): # TODO: Replace 'Template' with TTS name + def __init__(self, tts): + super(TemplateTTSValidator, self).__init__(tts) + + def validate_lang(self): + # TODO: Add some validation of `self.lang` default language + pass + + def validate_dependencies(self): + # TODO: Optionally check dependencies or raise + pass + + def validate_connection(self): + # TODO: Optionally check connection to remote service or raise + pass + + def get_tts_class(self): + return TemplateTTS diff --git a/requirements/requirements.txt b/requirements/requirements.txt new file mode 100644 index 0000000..e2301c9 --- /dev/null +++ b/requirements/requirements.txt @@ -0,0 +1,3 @@ +neon-utils~=0.5 +ovos-plugin-manager~=0.0.2 +# TODO: Add any dependencies \ No newline at end of file diff --git a/requirements/test_requirements.txt b/requirements/test_requirements.txt new file mode 100644 index 0000000..b9ad906 --- /dev/null +++ b/requirements/test_requirements.txt @@ -0,0 +1,2 @@ +pytest +pytest-timeout \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..81b4c59 --- /dev/null +++ b/setup.py @@ -0,0 +1,85 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2022 Neongecko.com Inc. +# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, +# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo +# BSD-3 License +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from setuptools import setup, find_packages +from os import path, getenv + + +def get_requirements(requirements_filename: str): + requirements_file = path.join(path.abspath(path.dirname(__file__)), "requirements", requirements_filename) + with open(requirements_file, 'r', encoding='utf-8') as r: + requirements = r.readlines() + requirements = [r.strip() for r in requirements if r.strip() and not r.strip().startswith("#")] + + for i in range(0, len(requirements)): + r = requirements[i] + if "@" in r: + parts = [p.lower() if p.strip().startswith("git+http") else p for p in r.split('@')] + r = "@".join(parts) + if getenv("GITHUB_TOKEN"): + if "github.com" in r: + r = r.replace("github.com", f"{getenv('GITHUB_TOKEN')}@github.com") + requirements[i] = r + return requirements + + +PLUGIN_ENTRY_POINT = 'tts_module_name = neon_tts_plugin_TODO_NAME:TemplateTTS' # TODO: Update name and path + +with open("README.md", "r") as f: + long_description = f.read() + +with open("./version.py", "r", encoding="utf-8") as v: + for line in v.readlines(): + if line.startswith("__version__"): + if '"' in line: + version = line.split('"')[1] + else: + version = line.split("'")[1] + +setup( + name='neon-tts-plugin-tts_module_name', # TODO Update `tts_module_name` + version=version, + description='A TTS plugin for Neon', # TODO: Add engine/framework name + long_description=long_description, + long_description_content_type='text/markdown', + url='https://github.com/NeonGeckoCom/template-neon-tts-plugin', # TODO: Update link + author='Neongecko', + author_email='developers@neon.ai', + license='BSD-3.0', + packages=find_packages(), + install_requires=get_requirements("requirements.txt"), + zip_safe=True, + classifiers=[ + 'Intended Audience :: Developers', + 'Topic :: Text Processing :: Linguistic', + + 'Programming Language :: Python :: 3.6', + ], + keywords='mycroft plugin tts', + entry_points={'mycroft.plugin.tts': PLUGIN_ENTRY_POINT} +) diff --git a/tests/test_tts.py b/tests/test_tts.py new file mode 100644 index 0000000..8926ad7 --- /dev/null +++ b/tests/test_tts.py @@ -0,0 +1,66 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2022 Neongecko.com Inc. +# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, +# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo +# BSD-3 License +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import sys +import unittest +from pprint import pprint + +sys.path.append(os.path.join(os.path.dirname(__file__), "res")) +sys.path.append(os.path.dirname(os.path.dirname(__file__))) +from neon_tts_plugin_TODO_NAME import TemplateTTS # TODO: Update Import + + +class TestTTS(unittest.TestCase): + def setUp(self) -> None: + self.tts = TemplateTTS() + + def doCleanups(self) -> None: + try: + os.remove(os.path.join(os.path.dirname(__file__), "test.wav")) + except FileNotFoundError: + pass + try: + self.tts.playback.stop() + self.tts.playback.join() + except AttributeError: + pass + + def test_speak_no_params(self): + out_file = os.path.join(os.path.dirname(__file__), "test.wav") + file, _ = self.tts.get_tts("Hello.", out_file) + self.assertEqual(file, out_file) + + def test_empty_speak(self): + out_file = os.path.join(os.path.dirname(__file__), "test2.wav") + file, _ = self.tts.get_tts("Hello.", out_file) + self.assertFalse(os.path.isfile(out_file)) + + +if __name__ == '__main__': + unittest.main() diff --git a/version.py b/version.py new file mode 100644 index 0000000..ef7228e --- /dev/null +++ b/version.py @@ -0,0 +1,29 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2022 Neongecko.com Inc. +# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, +# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo +# BSD-3 License +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +__version__ = "0.0.1" diff --git a/version_bump.py b/version_bump.py new file mode 100644 index 0000000..5a01c55 --- /dev/null +++ b/version_bump.py @@ -0,0 +1,54 @@ +# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework +# All trademark and other rights reserved by their respective owners +# Copyright 2008-2022 Neongecko.com Inc. +# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds, +# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo +# BSD-3 License +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import fileinput +from os.path import join, dirname + +with open(join(dirname(__file__), "version.py"), "r", encoding="utf-8") as v: + for line in v.readlines(): + if line.startswith("__version__"): + if '"' in line: + version = line.split('"')[1] + else: + version = line.split("'")[1] + +if "a" not in version: + parts = version.split('.') + parts[-1] = str(int(parts[-1]) + 1) + version = '.'.join(parts) + version = f"{version}a0" +else: + post = version.split("a")[1] + new_post = int(post) + 1 + version = version.replace(f"a{post}", f"a{new_post}") + +for line in fileinput.input(join(dirname(__file__), "version.py"), inplace=True): + if line.startswith("__version__"): + print(f"__version__ = \"{version}\"") + else: + print(line.rstrip('\n'))