Skip to content

Commit e96927d

Browse files
committed
support python 3.12
1 parent 95961d8 commit e96927d

File tree

11 files changed

+92
-24
lines changed

11 files changed

+92
-24
lines changed

docker/Dockerfile-Dev

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ FROM ubuntu:20.04
33

44
ENV DEBIAN_FRONTEND=noninteractive
55
ENV TZ=Europe/London
6+
ENV SETUPTOOLS_SCM_PRETEND_VERSION_FOR_SUBALIGNER=0.0.0-dev
67

78
RUN mkdir -p /subaligner && chmod 755 /subaligner
89
COPY subaligner /subaligner/subaligner

pyproject.toml

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
[build-system]
2-
requires = ["setuptools<65.0.0", "setuptools-scm", "wheel"]
2+
requires = [
3+
"setuptools<65.0.0; python_version < '3.12'",
4+
"setuptools>=65.0.0; python_version >= '3.12'",
5+
"setuptools-scm",
6+
"wheel",
7+
]
38
build-backend = "setuptools.build_meta"
49

510
[project]
@@ -11,13 +16,14 @@ authors = [
1116
description = "Automatically synchronize and translate subtitles, or create new ones by transcribing, using pre-trained DNNs, Forced Alignments and Transformers."
1217
readme = { file = "README.md", content-type = "text/markdown" }
1318
urls = { "Homepage" = "https://github.com/baxtree/subaligner", "Documentation" = "https://subaligner.readthedocs.io/en/latest/", "Source" = "https://github.com/baxtree/subaligner" }
14-
requires-python = ">=3.8,<3.12"
19+
requires-python = ">=3.8,<3.13"
1520
classifiers = [
1621
"License :: OSI Approved :: MIT License",
17-
"Programming Language :: Python :: 3.8",
18-
"Programming Language :: Python :: 3.9",
19-
"Programming Language :: Python :: 3.10",
22+
"Programming Language :: Python :: 3.12",
2023
"Programming Language :: Python :: 3.11",
24+
"Programming Language :: Python :: 3.10",
25+
"Programming Language :: Python :: 3.9",
26+
"Programming Language :: Python :: 3.8",
2127
"Intended Audience :: Developers",
2228
"Topic :: Utilities"
2329
]
@@ -71,9 +77,13 @@ dependencies = [
7177
"PyYAML>=4.2b1",
7278
"rsa==4.7",
7379
"scipy<1.12.0",
74-
"scikit-learn<1.2.0",
75-
"six~=1.15.0",
76-
"tensorflow>=1.15.5,<2.16.0",
80+
"scikit-learn<1.2.0; python_version < '3.12'",
81+
"scikit-learn>=1.2.0; python_version >= '3.12'",
82+
"six~=1.15.0; python_version < '3.12'",
83+
"six~=1.17.0; python_version >= '3.12'",
84+
"tf-keras~=2.19.0; python_version >= '3.12'",
85+
"tensorflow>=1.15.5,<2.16.0; python_version < '3.12'",
86+
"tensorflow~=2.19.0; python_version >= '3.12'",
7787
"termcolor==1.1.0",
7888
"toml==0.10.0",
7989
"toolz==0.9.0",
@@ -85,7 +95,7 @@ dependencies = [
8595

8696
[project.optional-dependencies]
8797
harmony = [
88-
"aeneas~=1.7.3.0",
98+
"aeneas~=1.7.3.0; python_version < '3.12'",
8999
"dtw-python~=1.5.3",
90100
"sentencepiece~=0.1.95",
91101
"torch<2.3.0",
@@ -94,7 +104,7 @@ harmony = [
94104
"openai-whisper==20250625"
95105
]
96106
dev = [
97-
"aeneas~=1.7.3.0",
107+
"aeneas~=1.7.3.0; python_version < '3.12'",
98108
"dtw-python~=1.5.3",
99109
"sentencepiece~=0.1.95",
100110
"torch<2.3.0",
@@ -105,7 +115,7 @@ dev = [
105115
"coverage==5.5",
106116
"tox~=3.23.0",
107117
"pycodestyle==2.12.1",
108-
"twine<4.0.0",
118+
"twine<3.8.0",
109119
"snakeviz==2.1.0",
110120
"line-profiler~=4.1.2",
111121
"scikit-build==0.11.1",
@@ -114,22 +124,24 @@ dev = [
114124
"mypy==1.3.0",
115125
"types-requests==2.27.9",
116126
"types-setuptools==64.0.1",
117-
"typing-extensions==4.5.0",
127+
"typing-extensions==4.8.0",
118128
"parameterized==0.8.1",
119129
"pylint~=2.17.2",
120130
"pygments<3.0.0",
121131
"darglint~=1.8.1"
122132
]
123133
docs = [
124134
"sphinx==6.2.1",
125-
"sphinx-rtd-theme==2.0.0"
135+
"sphinx-rtd-theme==2.0.0",
136+
"docutils<0.21",
126137
]
127138
stretch = [
128-
"aeneas~=1.7.3.0",
139+
"aeneas~=1.7.3.0; python_version < '3.12'",
129140
"dtw-python~=1.5.3"
130141
]
131142
llm = [
132-
"sentencepiece~=0.1.95",
143+
"sentencepiece~=0.1.95; python_version < '3.12'",
144+
"sentencepiece~=0.2.0; python_version >= '3.12'",
133145
"torch<2.3.0",
134146
"torchaudio<2.3.0",
135147
"transformers<4.37.0",
@@ -171,4 +183,4 @@ exclude = "\\.git|\\.tox"
171183
require-return-section-when-returning-nothing = true
172184

173185
[tool.poetry.dependencies]
174-
python = ">=3.8,<3.12"
186+
python = ">=3.8,<3.13"

subaligner/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import os
22
import warnings
33
import logging
4+
import sys
45
import multiprocessing as mp
6+
import tensorflow as tf
57
from ._version import __version__
68

79
__all__ = ["__version__"]
@@ -16,4 +18,9 @@
1618
os.environ["TF_CPP_MIN_VLOG_LEVEL"] = "0"
1719
logging.getLogger("tensorflow").disabled = True
1820

19-
os.environ["TF_USE_LEGACY_KERAS"] = "1"
21+
if tf.__version__ >= "2.16.0":
22+
os.environ["TF_USE_LEGACY_KERAS"] = "1"
23+
import tensorflow._api.v2.compat.v2.__internal__ as tf_internal
24+
if hasattr(tf_internal, 'register_call_context_function'):
25+
tf_internal.register_load_context_function = tf_internal.register_call_context_function
26+
sys.modules["keras"] = tf.keras

subaligner/network.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import psutil
55
import numpy as np
66
import tensorflow as tf
7-
import keras.optimizers as tf_optimizers
7+
import tensorflow.keras.optimizers as tf_optimizers
88

99
from typing import Tuple, Optional, List, Generator
1010
from keras.layers import (

tests/integration/feature/subaligner.feature

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ Feature: Subaligner CLI
9090

9191
@with-mode @script
9292
Scenario Outline: Test alignments with plain texts as input
93-
Given I have a video file <video-in>
93+
Given I skip the scenario if Python version is above 3.11
94+
And I have a video file <video-in>
9495
And I have a subtitle file "test_plain.txt"
9596
When I run the alignment with subaligner on them with script stage and output <subtitle-out>
9697
Then a new subtitle file <subtitle-out> is generated
@@ -181,7 +182,8 @@ Feature: Subaligner CLI
181182

182183
@stretch
183184
Scenario Outline: Test dual-stage alignment with stretch on
184-
Given I have a video file "test.mp4"
185+
Given I skip the scenario if Python version is above 3.11
186+
And I have a video file "test.mp4"
185187
And I have a subtitle file "test.srt"
186188
When I run the alignment with <aligner> on them with <mode> stage and with stretch on
187189
Then a new subtitle file "test_aligned.srt" is generated
@@ -244,11 +246,21 @@ Feature: Subaligner CLI
244246
| aligner | mode | subtitle-in | language-pair | subtitle-out |
245247
| subaligner | single | "test.srt" | eng,zho | "test_aligned.srt" |
246248
| subaligner | dual | "test.srt" | eng,spa | "test_aligned.srt" |
247-
| subaligner | script | "test_plain.txt" | eng,ita | "test_aligned.srt" |
248-
| subaligner | script | "test_plain.txt" | eng,por | "test_aligned.srt" |
249249
| subaligner_1pass | <NULL> | "test.srt" | eng,fra | "test_aligned.srt" |
250250
| subaligner_2pass | <NULL> | "test.srt" | eng,deu | "test_aligned.srt" |
251251

252+
@translation @script
253+
Scenario Outline: Test translation on aligned scripts
254+
Given I skip the scenario if Python version is above 3.11
255+
And I have a video file "test.mp4"
256+
And I have a subtitle file <subtitle-in>
257+
When I run the alignment with <aligner> on them with <mode> stage and <language-pair> for translation
258+
Then a new subtitle file <subtitle-out> is generated
259+
Examples:
260+
| aligner | mode | subtitle-in | language-pair | subtitle-out |
261+
| subaligner | script | "test_plain.txt" | eng,ita | "test_aligned.srt" |
262+
| subaligner | script | "test_plain.txt" | eng,por | "test_aligned.srt" |
263+
252264
@transcription @custom-prompt
253265
Scenario Outline: Test transcription on audiovisual input and subtitle generation
254266
Given I have a video file <video-in>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import warnings
2+
warnings.filterwarnings("ignore", category=DeprecationWarning)

tests/integration/radish/step.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33
import subprocess
4+
import sys
45
import os
56
import tempfile
67
import shutil
@@ -105,6 +106,10 @@ def run_subaligner_manual_shift(step, offset_seconds):
105106

106107
@when("I run the alignment with {aligner:S} on them with {mode:S} stage and {language_pair:S} for translation")
107108
def run_subaligner_with_translation(step, aligner, mode, language_pair):
109+
if hasattr(step.context, "skip_scenario") and step.context.skip_scenario and mode == "script":
110+
step.skip()
111+
return
112+
108113
if mode == "<NULL>":
109114
process = subprocess.Popen([
110115
os.path.join(PWD, "..", "..", "..", "bin", aligner),
@@ -184,6 +189,10 @@ def run_subaligner_with_transcription(step):
184189

185190
@when('I run the alignment with {aligner:S} on them with {mode:S} stage and output "{file_name:S}"')
186191
def run_subaligner_with_output(step, aligner, mode, file_name):
192+
if hasattr(step.context, "skip_scenario") and step.context.skip_scenario and mode == "script":
193+
step.skip()
194+
return
195+
187196
if mode == "<NULL>":
188197
process = subprocess.Popen([
189198
os.path.join(PWD, "..", "..", "..", "bin", aligner),
@@ -235,8 +244,19 @@ def run_subaligner_with_exit_segfail(step, aligner, mode):
235244
step.context.exit_code = process.wait(timeout=WAIT_TIMEOUT_IN_SECONDS)
236245

237246

247+
@given("I skip the scenario if Python version is above {version:S}")
248+
def run_skip_scenario_if_python_version_above(step, version):
249+
if sys.version_info > tuple([int(v) for v in version.split(".")]):
250+
step.context.skip_scenario = True
251+
step.skip()
252+
253+
238254
@when("I run the alignment with {aligner:S} on them with {mode:S} stage and with stretch on")
239255
def run_subaligner_with_stretch(step, aligner, mode):
256+
if hasattr(step.context, "skip_scenario") and step.context.skip_scenario:
257+
step.skip()
258+
return
259+
240260
if mode == "<NULL>":
241261
process = subprocess.Popen([
242262
os.path.join(PWD, "..", "..", "..", "bin", aligner),
@@ -279,6 +299,10 @@ def run_subaligner_with_custom_model(step, aligner, mode):
279299

280300
@then('a new subtitle file "{file_name:S}" is generated')
281301
def expect_result(step, file_name):
302+
if hasattr(step.context, "skip_scenario") and step.context.skip_scenario:
303+
step.skip()
304+
return
305+
282306
output_file_path = os.path.join(PWD, "..", "..", "subaligner", "resource", file_name.replace("[]", " "))
283307
assert step.context.exit_code == 0
284308
assert os.path.isfile(output_file_path) is True

tests/subaligner/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
try:
2+
import aeneas
3+
aeneas_is_available = True
4+
except ImportError:
5+
aeneas_is_available = False

tests/subaligner/test_network.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
from mock import patch
99
from parameterized import parameterized
10-
from keras.models import Model
1110
from subaligner.hyperparameters import Hyperparameters
1211
from subaligner.exception import TerminalException
1312
from subaligner.network import Network as Undertest

tests/subaligner/test_predictor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from subaligner.logger import Logger
99
from subaligner.media_helper import MediaHelper
1010
from subaligner.predictor import Predictor as Undertest
11+
from tests.subaligner import aeneas_is_available
1112

1213

1314
class PredictorTests(unittest.TestCase):
@@ -249,6 +250,7 @@ def test_predict_dual_pass_with_specified_language(self):
249250
self.assertGreater(len(voice_probabilities), 0)
250251
self.assertEqual(24.0, frame_rate)
251252

253+
@unittest.skipIf(not aeneas_is_available, "The aeneas package is not available")
252254
def test_predict_plain_text(self):
253255
subs, audio_file_path, voice_probabilities, frame_rate = Undertest(n_mfcc=20, step_sample=0.02).predict_plain_text(
254256
self.video_file_path, self.plain_text_file_path

0 commit comments

Comments
 (0)