audioarxiv 0.1.2rc62.post1__tar.gz → 0.1.2rc68.post1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/PKG-INFO +1 -1
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/installation.rst +2 -12
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/__init__.py +6 -6
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/tools/main.py +31 -33
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/tests/audio/test_base.py +74 -2
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/tests/resources/test_paper.py +64 -0
- audioarxiv-0.1.2rc68.post1/tests/test_logging.py +86 -0
- audioarxiv-0.1.2rc68.post1/tests/tools/test_main.py +358 -0
- audioarxiv-0.1.2rc62.post1/tests/tools/test_main.py +0 -101
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.devcontainer/Dockerfile +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.devcontainer/devcontainer.json +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/dependabot.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/template-sync.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/workflows/CI.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/workflows/publish.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/workflows/schedule-update-actions.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/workflows/semantic-pr-check.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/workflows/sphinx.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/workflows/template-sync.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.gitignore +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.pre-commit-config.yaml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.pypirc +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.vscode/launch.json +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.vscode/settings.json +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/CODE_OF_CONDUCT.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/CONTRIBUTING.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/LICENSE +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/README.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/SECURITY.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/SUPPORT.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/codecov.yml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/cspell.json +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/.gitignore +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/Makefile +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/conf.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/devcontainer.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/developer.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/index.rst +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/make.bat +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/pre-commit-config.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/pylint.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/pyproject.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/release_notes.rst +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/requirements.txt +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/templates/custom-class-template.rst +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/templates/custom-module-template.rst +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/user_guide.rst +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/vscode.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/workflows.md +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/pyproject.toml +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/audio/__init__.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/audio/base.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/preprocess/__init__.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/preprocess/article.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/preprocess/math_equation.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/resources/__init__.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/resources/paper.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/tools/__init__.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/tests/preprocess/test_article.py +0 -0
- {audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/tests/preprocess/test_math_equation.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: audioarxiv
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2rc68.post1
|
4
4
|
Summary: Turn arXiv papers into audio. audioarxiv lets you fetch the research papers from arXiv and read them aloud.
|
5
5
|
Author-email: "Isaac C. F. Wong" <isaac.cf.wong@gmail.com>
|
6
6
|
Requires-Python: >=3.9
|
@@ -3,19 +3,9 @@ Installation
|
|
3
3
|
|
4
4
|
To install audioarxiv, you need Python 3.9 or higher. You can install the package using pip:
|
5
5
|
|
6
|
-
..
|
7
|
-
|
8
|
-
.. tab:: Conda
|
9
|
-
|
10
|
-
.. code-block:: console
|
11
|
-
|
12
|
-
$ conda install -c conda-forge audioarxiv
|
13
|
-
|
14
|
-
.. tab:: Pip
|
15
|
-
|
16
|
-
.. code-block:: console
|
6
|
+
.. code-block:: console
|
17
7
|
|
18
|
-
|
8
|
+
$ pip install audioarxiv
|
19
9
|
|
20
10
|
Requirements
|
21
11
|
------------
|
@@ -24,7 +24,7 @@ from pandas import DataFrame
|
|
24
24
|
|
25
25
|
from . import audio, preprocess, resources
|
26
26
|
|
27
|
-
__version__ = "0.1.2-
|
27
|
+
__version__ = "0.1.2-rc68-post1"
|
28
28
|
|
29
29
|
|
30
30
|
def get_version_information() -> str:
|
@@ -111,11 +111,11 @@ def env_package_list(as_dataframe: bool = False) -> list | DataFrame:
|
|
111
111
|
|
112
112
|
Returns:
|
113
113
|
Union[list, DataFrame]:
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
114
|
+
If ``as_dataframe=False`` is given, the output is a `list` of `dict`,
|
115
|
+
one for each package, at least with ``'name'`` and ``'version'`` keys
|
116
|
+
(more if `conda` is used).
|
117
|
+
If ``as_dataframe=True`` is given, the output is a `DataFrame`
|
118
|
+
created from the `list` of `dicts`.
|
119
119
|
"""
|
120
120
|
prefix = sys.prefix
|
121
121
|
pkgs = []
|
@@ -57,7 +57,6 @@ def initialize_configuration(args: configargparse.Namespace) -> tuple:
|
|
57
57
|
os.makedirs(config_dir, exist_ok=True)
|
58
58
|
config_file = 'config.json'
|
59
59
|
config_path = os.path.join(config_dir, config_file)
|
60
|
-
|
61
60
|
# Default settings.
|
62
61
|
settings = {
|
63
62
|
'audio': {
|
@@ -159,36 +158,35 @@ def main():
|
|
159
158
|
paper = Paper(**settings['paper'])
|
160
159
|
|
161
160
|
# Search the paper.
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
161
|
+
# Print the information
|
162
|
+
logger.info('Configuration file: %s', config_path)
|
163
|
+
logger.info('Audio settings')
|
164
|
+
for key, value in settings['audio'].items():
|
165
|
+
logger.info('%s: %s', key, value)
|
166
|
+
|
167
|
+
logger.info('Paper settings')
|
168
|
+
for key, value in settings['paper'].items():
|
169
|
+
logger.info('%s: %s', key, value)
|
170
|
+
|
171
|
+
logger.info('Searching arxiv: %s...', args.id)
|
172
|
+
paper.search_by_arxiv_id(arxiv_id=args.id)
|
173
|
+
# Get the sections
|
174
|
+
sections = paper.sections
|
175
|
+
if args.output is None:
|
176
|
+
for section in sections:
|
177
|
+
audio.read_article(section['header'])
|
178
|
+
time.sleep(1)
|
179
|
+
for content in section['content']:
|
180
|
+
audio.read_article(content)
|
180
181
|
time.sleep(1)
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
logger.info('Saving audio...')
|
193
|
-
audio.save_article(filename=args.output, article=article)
|
194
|
-
logger.info('Audio is saved to %s.', args.output)
|
182
|
+
else:
|
183
|
+
article = []
|
184
|
+
for section in sections:
|
185
|
+
if section['header'] is not None:
|
186
|
+
article.append(section['header'])
|
187
|
+
if section['content'] is not None:
|
188
|
+
article += section['content']
|
189
|
+
article = " ".join(article)
|
190
|
+
logger.info('Saving audio...')
|
191
|
+
audio.save_article(filename=args.output, article=article)
|
192
|
+
logger.info('Audio is saved to %s.', args.output)
|
@@ -186,10 +186,10 @@ def test_read_article_with_non_string_input(mock_init, caplog):
|
|
186
186
|
assert "is not str. Skipping." in caplog.text
|
187
187
|
|
188
188
|
|
189
|
-
@patch(
|
189
|
+
@patch("audioarxiv.audio.base.pyttsx3.init")
|
190
190
|
def test_audio_stop(mock_init):
|
191
191
|
mock_engine = MagicMock()
|
192
|
-
mock_init.return_value = mock_engine
|
192
|
+
mock_init.return_value = mock_engine
|
193
193
|
|
194
194
|
# Create the Audio instance, which should use the mocked engine
|
195
195
|
audio = Audio()
|
@@ -199,3 +199,75 @@ def test_audio_stop(mock_init):
|
|
199
199
|
|
200
200
|
# Verify that the stop method was called on the mocked engine
|
201
201
|
mock_engine.stop.assert_called_once()
|
202
|
+
|
203
|
+
|
204
|
+
@patch("audioarxiv.audio.base.pyttsx3.init")
|
205
|
+
def test_validate_arguments_enabled(mock_init):
|
206
|
+
mock_engine = MagicMock()
|
207
|
+
mock_init.return_value = mock_engine
|
208
|
+
|
209
|
+
# Arrange
|
210
|
+
with patch("audioarxiv.audio.base.validate_audio_arguments") as mock_validate:
|
211
|
+
mock_validate.return_value = {
|
212
|
+
"rate": 150,
|
213
|
+
"volume": 0.8,
|
214
|
+
"voice": "voice_id",
|
215
|
+
"pause_seconds": 0.2
|
216
|
+
}
|
217
|
+
|
218
|
+
# Act
|
219
|
+
audio = Audio(rate=150, # noqa: F841 # pylint: disable=unused-variable
|
220
|
+
volume=0.8,
|
221
|
+
voice="voice_id",
|
222
|
+
pause_seconds=0.2,
|
223
|
+
validate_arguments=True)
|
224
|
+
|
225
|
+
# Assert
|
226
|
+
mock_validate.assert_called_once()
|
227
|
+
mock_engine.setProperty.assert_any_call('rate', 150)
|
228
|
+
mock_engine.setProperty.assert_any_call('volume', 0.8)
|
229
|
+
mock_engine.setProperty.assert_any_call('voice', 'voice_id')
|
230
|
+
|
231
|
+
|
232
|
+
@patch("audioarxiv.audio.base.pyttsx3.init")
|
233
|
+
def test_validate_arguments_disabled(mock_init):
|
234
|
+
mock_engine = MagicMock()
|
235
|
+
mock_init.return_value = mock_engine
|
236
|
+
# Should not call `validate_audio_arguments`
|
237
|
+
with patch("audioarxiv.audio.base.validate_audio_arguments") as mock_validate:
|
238
|
+
audio = Audio(rate=150, # noqa: F841 # pylint: disable=unused-variable
|
239
|
+
volume=0.8,
|
240
|
+
voice="voice_id",
|
241
|
+
pause_seconds=0.2,
|
242
|
+
validate_arguments=False)
|
243
|
+
|
244
|
+
mock_validate.assert_not_called()
|
245
|
+
mock_engine.setProperty.assert_any_call('rate', 150)
|
246
|
+
mock_engine.setProperty.assert_any_call('volume', 0.8)
|
247
|
+
mock_engine.setProperty.assert_any_call('voice', 'voice_id')
|
248
|
+
|
249
|
+
|
250
|
+
@pytest.mark.parametrize("rate", [100, None])
|
251
|
+
@patch("audioarxiv.audio.base.pyttsx3.init")
|
252
|
+
def test_rate_handling(mock_init, rate):
|
253
|
+
mock_engine = MagicMock()
|
254
|
+
mock_init.return_value = mock_engine
|
255
|
+
audio = Audio(rate=rate, volume=0.8, validate_arguments=False) # noqa: F841 # pylint: disable=unused-variable
|
256
|
+
if rate is not None:
|
257
|
+
mock_engine.setProperty.assert_any_call('rate', rate)
|
258
|
+
else:
|
259
|
+
for call in mock_engine.setProperty.call_args_list:
|
260
|
+
assert call[0][0] != 'rate'
|
261
|
+
|
262
|
+
|
263
|
+
@pytest.mark.parametrize("volume", [0.5, None])
|
264
|
+
@patch("audioarxiv.audio.base.pyttsx3.init")
|
265
|
+
def test_volume_handling(mock_init, volume):
|
266
|
+
mock_engine = MagicMock()
|
267
|
+
mock_init.return_value = mock_engine
|
268
|
+
audio = Audio(rate=140, volume=volume, validate_arguments=False) # noqa: F841 # pylint: disable=unused-variable
|
269
|
+
if volume is not None:
|
270
|
+
mock_engine.setProperty.assert_any_call('volume', volume)
|
271
|
+
else:
|
272
|
+
for call in mock_engine.setProperty.call_args_list:
|
273
|
+
assert call[0][0] != 'volume'
|
@@ -181,3 +181,67 @@ def test_sections_when_no_paper(mock_client_class, caplog):
|
|
181
181
|
# Assertions
|
182
182
|
assert 'Paper is None. Cannot download PDF.' in caplog.text
|
183
183
|
assert len(sections) == 0 # No sections should be found since paper is None
|
184
|
+
|
185
|
+
|
186
|
+
@patch("fitz.open")
|
187
|
+
@patch.object(Paper, "download_pdf")
|
188
|
+
def test_sections_extraction_logic(download_pdf_mock, fitz_open_mock):
|
189
|
+
# Setup fake PDF text blocks
|
190
|
+
mock_page = MagicMock()
|
191
|
+
mock_page.get_text.return_value = [
|
192
|
+
(0, 0, 100, 100, "1 Introduction", 0, 0, 0),
|
193
|
+
(0, 0, 100, 100, "This is the first paragraph.", 0, 0, 0),
|
194
|
+
(0, 0, 100, 100, "2 Related Work", 0, 0, 0),
|
195
|
+
(0, 0, 100, 100, "Some related work goes here.", 0, 0, 0),
|
196
|
+
]
|
197
|
+
|
198
|
+
mock_doc = [mock_page]
|
199
|
+
fitz_open_mock.return_value = mock_doc
|
200
|
+
|
201
|
+
paper = Paper()
|
202
|
+
paper.paper = MagicMock()
|
203
|
+
download_pdf_mock.return_value = "mock_path"
|
204
|
+
|
205
|
+
sections = paper.sections
|
206
|
+
|
207
|
+
assert len(sections) == 2 # ✅ tests `if len(self._sections) == 0`
|
208
|
+
assert sections[0]["header"] == "1 Introduction"
|
209
|
+
assert sections[0]["content"] == ["This is the first paragraph."]
|
210
|
+
assert sections[1]["header"] == "2 Related Work"
|
211
|
+
assert sections[1]["content"] == ["Some related work goes here."]
|
212
|
+
|
213
|
+
|
214
|
+
@patch("fitz.open")
|
215
|
+
@patch.object(Paper, "download_pdf")
|
216
|
+
def test_sections_only_appends_nonempty_sections(download_pdf_mock, fitz_open_mock):
|
217
|
+
# Only content, no section header detected
|
218
|
+
mock_page = MagicMock()
|
219
|
+
mock_page.get_text.return_value = [
|
220
|
+
(0, 0, 100, 100, "Just some content without a header", 0, 0, 0)
|
221
|
+
]
|
222
|
+
|
223
|
+
mock_doc = [mock_page]
|
224
|
+
fitz_open_mock.return_value = mock_doc
|
225
|
+
|
226
|
+
paper = Paper()
|
227
|
+
paper.paper = MagicMock()
|
228
|
+
download_pdf_mock.return_value = "mock_path"
|
229
|
+
|
230
|
+
sections = paper.sections
|
231
|
+
|
232
|
+
# Tests `if current_section["header"] or current_section["content"]`
|
233
|
+
assert len(sections) == 1
|
234
|
+
assert sections[0]["header"] is None
|
235
|
+
assert sections[0]["content"] == ["Just some content without a header"]
|
236
|
+
|
237
|
+
|
238
|
+
@patch("fitz.open")
|
239
|
+
@patch.object(Paper, "download_pdf")
|
240
|
+
def test_sections_empty_when_no_paper(download_pdf_mock, fitz_open_mock):
|
241
|
+
paper = Paper()
|
242
|
+
paper.paper = None # simulate not setting a paper
|
243
|
+
|
244
|
+
sections = paper.sections
|
245
|
+
|
246
|
+
# Triggers early return due to missing paper
|
247
|
+
assert sections == []
|
@@ -0,0 +1,86 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
import tempfile
|
6
|
+
from pathlib import Path
|
7
|
+
from unittest.mock import patch
|
8
|
+
|
9
|
+
from audioarxiv import (env_package_list, get_version_information,
|
10
|
+
loaded_modules_dict, setup_logger)
|
11
|
+
|
12
|
+
|
13
|
+
def test_get_version_information():
|
14
|
+
version = get_version_information()
|
15
|
+
assert isinstance(version, str)
|
16
|
+
assert len(version) > 0
|
17
|
+
|
18
|
+
|
19
|
+
def test_setup_logger_creates_handlers():
|
20
|
+
logger = logging.getLogger("test_logger")
|
21
|
+
logger.handlers = [] # Clear any existing handlers
|
22
|
+
|
23
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
24
|
+
setup_logger(logger, outdir=tmpdir, label="test", log_level="INFO", print_version=True)
|
25
|
+
|
26
|
+
# Verify handlers
|
27
|
+
assert any(isinstance(h, logging.StreamHandler) for h in logger.handlers)
|
28
|
+
assert any(isinstance(h, logging.FileHandler) for h in logger.handlers)
|
29
|
+
|
30
|
+
# Verify log file content
|
31
|
+
log_path = Path(tmpdir) / "test.log"
|
32
|
+
assert log_path.exists()
|
33
|
+
with open(log_path) as f:
|
34
|
+
content = f.read()
|
35
|
+
assert "audioarxiv version" in content.lower()
|
36
|
+
|
37
|
+
|
38
|
+
def test_loaded_modules_dict_structure():
|
39
|
+
modules = loaded_modules_dict()
|
40
|
+
assert isinstance(modules, dict)
|
41
|
+
for k, v in modules.items():
|
42
|
+
assert isinstance(k, str)
|
43
|
+
assert isinstance(v, str)
|
44
|
+
|
45
|
+
|
46
|
+
@patch("audioarxiv.subprocess.check_output")
|
47
|
+
def test_env_package_list_conda(mock_sub_proc):
|
48
|
+
mock_pkgs = [
|
49
|
+
{"name": "numpy", "version": "1.24.0"},
|
50
|
+
{"name": "pandas", "version": "2.0.0"},
|
51
|
+
]
|
52
|
+
mock_sub_proc.return_value = json.dumps(mock_pkgs).encode("utf-8")
|
53
|
+
|
54
|
+
with patch("pathlib.Path.is_dir", return_value=True): # Simulate conda-meta directory
|
55
|
+
result = env_package_list()
|
56
|
+
assert isinstance(result, list)
|
57
|
+
assert all("name" in pkg and "version" in pkg for pkg in result)
|
58
|
+
|
59
|
+
|
60
|
+
@patch("audioarxiv.subprocess.check_output")
|
61
|
+
def test_env_package_list_pip(mock_sub_proc):
|
62
|
+
mock_pkgs = [
|
63
|
+
{"name": "requests", "version": "2.31.0"},
|
64
|
+
{"name": "flask", "version": "3.0.0"},
|
65
|
+
]
|
66
|
+
mock_sub_proc.return_value = json.dumps(mock_pkgs).encode("utf-8")
|
67
|
+
|
68
|
+
with patch("pathlib.Path.is_dir", return_value=False): # Simulate no conda-meta
|
69
|
+
result = env_package_list()
|
70
|
+
assert isinstance(result, list)
|
71
|
+
assert all("name" in pkg and "version" in pkg for pkg in result)
|
72
|
+
|
73
|
+
|
74
|
+
@patch("audioarxiv.subprocess.check_output")
|
75
|
+
def test_env_package_list_as_dataframe(mock_sub_proc):
|
76
|
+
mock_pkgs = [
|
77
|
+
{"name": "torch", "version": "2.1.0"},
|
78
|
+
{"name": "scipy", "version": "1.11.0"},
|
79
|
+
]
|
80
|
+
mock_sub_proc.return_value = json.dumps(mock_pkgs).encode("utf-8")
|
81
|
+
|
82
|
+
with patch("pathlib.Path.is_dir", return_value=False):
|
83
|
+
df = env_package_list(as_dataframe=True)
|
84
|
+
assert df.shape[0] == 2 # type: ignore[attr-defined]
|
85
|
+
assert "name" in df.columns # type: ignore[attr-defined]
|
86
|
+
assert "version" in df.columns # type: ignore[attr-defined]
|
@@ -0,0 +1,358 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
import os
|
6
|
+
import signal
|
7
|
+
import tempfile
|
8
|
+
from datetime import datetime
|
9
|
+
from unittest import mock
|
10
|
+
from unittest.mock import MagicMock, patch
|
11
|
+
|
12
|
+
import pytest
|
13
|
+
import pyttsx3
|
14
|
+
|
15
|
+
from audioarxiv.tools.main import (handle_exit, initialize_configuration, main,
|
16
|
+
save_settings)
|
17
|
+
|
18
|
+
|
19
|
+
@pytest.fixture
|
20
|
+
def mock_pyttsx3_init(monkeypatch):
|
21
|
+
mock_engine = MagicMock()
|
22
|
+
monkeypatch.setattr(pyttsx3, "init", lambda: mock_engine)
|
23
|
+
return mock_engine
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.fixture
|
27
|
+
def mock_paper_object():
|
28
|
+
mock_paper = MagicMock()
|
29
|
+
mock_paper.title = "Test Title"
|
30
|
+
mock_paper.summary = "This is a test abstract."
|
31
|
+
|
32
|
+
author1 = MagicMock()
|
33
|
+
author1.name = "Alice"
|
34
|
+
author2 = MagicMock()
|
35
|
+
author2.name = "Bob"
|
36
|
+
mock_paper.authors = [author1, author2]
|
37
|
+
|
38
|
+
mock_paper.published = datetime(2022, 1, 1)
|
39
|
+
mock_paper.updated = datetime(2022, 1, 2)
|
40
|
+
return mock_paper
|
41
|
+
|
42
|
+
|
43
|
+
# Patch where classes/functions are used, not defined
|
44
|
+
@pytest.mark.integration
|
45
|
+
@patch("audioarxiv.tools.main.Audio")
|
46
|
+
@patch("audioarxiv.tools.main.Paper")
|
47
|
+
@patch("audioarxiv.tools.main.configargparse.ArgParser.parse_args")
|
48
|
+
def test_main_with_id_and_output(mock_parse_args, mock_Paper, mock_Audio):
|
49
|
+
mock_args = MagicMock()
|
50
|
+
mock_args.id = "1234.5678"
|
51
|
+
mock_args.output = "output.mp3"
|
52
|
+
mock_args.list_voices = False
|
53
|
+
mock_args.rate = None
|
54
|
+
mock_args.volume = None
|
55
|
+
mock_args.voice = None
|
56
|
+
mock_args.pause_seconds = None
|
57
|
+
mock_args.page_size = None
|
58
|
+
mock_args.delay_seconds = None
|
59
|
+
mock_args.num_retries = None
|
60
|
+
mock_parse_args.return_value = mock_args
|
61
|
+
|
62
|
+
mock_audio = mock_Audio.return_value
|
63
|
+
mock_paper = mock_Paper.return_value
|
64
|
+
mock_paper.sections = [
|
65
|
+
{'header': "Introduction", 'content': ["This is content."]},
|
66
|
+
{'header': None, 'content': ["More content."]}
|
67
|
+
]
|
68
|
+
|
69
|
+
with patch("audioarxiv.tools.main.initialize_configuration") as mock_init_config:
|
70
|
+
mock_init_config.return_value = ({"audio": {}, "paper": {}}, "mock/config/path")
|
71
|
+
|
72
|
+
main()
|
73
|
+
|
74
|
+
mock_audio.save_article.assert_called_once()
|
75
|
+
assert mock_audio.save_article.call_args[1]["filename"] == "output.mp3"
|
76
|
+
|
77
|
+
|
78
|
+
@pytest.mark.integration
|
79
|
+
@patch("audioarxiv.tools.main.Audio")
|
80
|
+
@patch("audioarxiv.tools.main.configargparse.ArgParser.parse_args")
|
81
|
+
def test_main_list_voices(mock_parse_args, mock_Audio):
|
82
|
+
mock_args = MagicMock()
|
83
|
+
mock_args.list_voices = True
|
84
|
+
mock_parse_args.return_value = mock_args
|
85
|
+
|
86
|
+
mock_audio = mock_Audio.return_value
|
87
|
+
|
88
|
+
main()
|
89
|
+
|
90
|
+
mock_audio.list_voices.assert_called_once()
|
91
|
+
|
92
|
+
|
93
|
+
@pytest.mark.integration
|
94
|
+
@patch("audioarxiv.tools.main.validate_audio_arguments")
|
95
|
+
@patch("audioarxiv.tools.main.validate_paper_arguments")
|
96
|
+
def test_initialize_configuration_defaults(mock_validate_paper, mock_validate_audio):
|
97
|
+
mock_validate_audio.return_value = {'rate': 140, 'volume': 0.9, 'voice': None, 'pause_seconds': 0.1}
|
98
|
+
mock_validate_paper.return_value = {'page_size': 100, 'delay_seconds': 3.0, 'num_retries': 3}
|
99
|
+
|
100
|
+
dummy_args = MagicMock()
|
101
|
+
for attr in ['rate', 'volume', 'voice', 'pause_seconds', 'page_size', 'delay_seconds', 'num_retries']:
|
102
|
+
setattr(dummy_args, attr, None)
|
103
|
+
|
104
|
+
with tempfile.TemporaryDirectory() as tmp_dir_name:
|
105
|
+
config_path = os.path.join(tmp_dir_name, 'config.json') # noqa: F841 # pylint: disable=unused-variable
|
106
|
+
|
107
|
+
with patch("audioarxiv.tools.main.user_config_dir", return_value=tmp_dir_name):
|
108
|
+
settings, path = initialize_configuration(dummy_args)
|
109
|
+
assert settings['audio']['rate'] == 140
|
110
|
+
assert os.path.exists(path)
|
111
|
+
|
112
|
+
|
113
|
+
@pytest.mark.integration
|
114
|
+
@patch("builtins.open", new_callable=mock.mock_open)
|
115
|
+
def test_save_settings(mock_open_func):
|
116
|
+
settings = {"audio": {"rate": 150}, "paper": {"page_size": 50}}
|
117
|
+
save_settings("config.json", settings)
|
118
|
+
mock_open_func.assert_called_once_with("config.json", 'w', encoding='utf-8')
|
119
|
+
handle = mock_open_func()
|
120
|
+
handle.write.assert_called()
|
121
|
+
|
122
|
+
|
123
|
+
@pytest.mark.integration
|
124
|
+
@patch("audioarxiv.tools.main.sys.exit")
|
125
|
+
def test_handle_exit(mock_exit):
|
126
|
+
with patch("audioarxiv.tools.main.logger.info") as mock_logger:
|
127
|
+
handle_exit(signal.SIGINT, None)
|
128
|
+
mock_logger.assert_called_once()
|
129
|
+
mock_exit.assert_called_once_with(0)
|
130
|
+
|
131
|
+
|
132
|
+
@patch('builtins.open', side_effect=IOError('Mocked IOError during file open'))
|
133
|
+
def test_save_settings_throws_exception(mock_open, caplog):
|
134
|
+
config_path = "test_config.json"
|
135
|
+
settings = {
|
136
|
+
'audio': {
|
137
|
+
'rate': 140,
|
138
|
+
'volume': 0.9,
|
139
|
+
'voice': None,
|
140
|
+
'pause_seconds': 0.1
|
141
|
+
},
|
142
|
+
'paper': {
|
143
|
+
'page_size': 100,
|
144
|
+
'delay_seconds': 3.0,
|
145
|
+
'num_retries': 3
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
logger = logging.getLogger('audioarxiv')
|
150
|
+
logger.setLevel(logging.ERROR)
|
151
|
+
logger.propagate = True
|
152
|
+
|
153
|
+
with caplog.at_level(logging.ERROR, logger='audioarxiv'):
|
154
|
+
# Call save_settings, which should raise an exception
|
155
|
+
save_settings(config_path, settings)
|
156
|
+
|
157
|
+
# Assert that the open function was called (even though it will raise an exception)
|
158
|
+
mock_open.assert_called_once_with(config_path, 'w', encoding="utf-8")
|
159
|
+
|
160
|
+
assert 'Error saving settings: Mocked IOError during file open' in caplog.text
|
161
|
+
|
162
|
+
|
163
|
+
@patch('audioarxiv.tools.main.user_config_dir', return_value='.') # Mock user_config_dir
|
164
|
+
@patch('os.path.exists')
|
165
|
+
@patch('builtins.open', new_callable=MagicMock) # Mock the open function to read the file
|
166
|
+
@patch('json.load') # Mock json.load to simulate loading settings
|
167
|
+
@patch('audioarxiv.audio.base.validate_audio_arguments') # Mock the validate_audio_arguments function
|
168
|
+
@patch('audioarxiv.resources.paper.validate_paper_arguments') # Mock the validate_paper_arguments function
|
169
|
+
@patch("audioarxiv.resources.paper.arxiv.Client")
|
170
|
+
@patch("configargparse.ArgParser.parse_args")
|
171
|
+
def test_load_settings(mock_parse_args,
|
172
|
+
mock_client_class,
|
173
|
+
mock_validate_paper,
|
174
|
+
mock_validate_audio,
|
175
|
+
mock_json_load,
|
176
|
+
mock_open,
|
177
|
+
mock_exists,
|
178
|
+
mock_user_config_dir,
|
179
|
+
mock_pyttsx3_init,
|
180
|
+
mock_paper_object,
|
181
|
+
caplog):
|
182
|
+
# Set up mock arguments
|
183
|
+
mock_args = MagicMock()
|
184
|
+
mock_args.id = "1234.5678"
|
185
|
+
mock_args.output = None
|
186
|
+
mock_args.list_voices = False
|
187
|
+
mock_args.rate = None
|
188
|
+
mock_args.volume = None
|
189
|
+
mock_args.voice = None
|
190
|
+
mock_args.pause_seconds = None
|
191
|
+
mock_args.page_size = None
|
192
|
+
mock_args.delay_seconds = None
|
193
|
+
mock_args.num_retries = None
|
194
|
+
mock_parse_args.return_value = mock_args
|
195
|
+
|
196
|
+
mock_engine = mock_pyttsx3_init() # noqa: F841
|
197
|
+
|
198
|
+
mock_client = MagicMock()
|
199
|
+
mock_client.results.return_value = iter([mock_paper_object])
|
200
|
+
mock_client_class.return_value = mock_client
|
201
|
+
|
202
|
+
mock_paper_object.download_pdf.return_value = "path/to/pdf"
|
203
|
+
|
204
|
+
# Set up the logger
|
205
|
+
logger = logging.getLogger('audioarxiv')
|
206
|
+
logger.setLevel(logging.DEBUG)
|
207
|
+
logger.propagate = True
|
208
|
+
|
209
|
+
# Sample settings to be loaded
|
210
|
+
config_dir = mock_user_config_dir('audioarxiv') # Assuming this function is defined correctly
|
211
|
+
config_file = 'config.json'
|
212
|
+
config_path = os.path.join(config_dir, config_file)
|
213
|
+
settings_from_file = {
|
214
|
+
'audio': {
|
215
|
+
'rate': 150,
|
216
|
+
'volume': 1.0,
|
217
|
+
'voice': None,
|
218
|
+
'pause_seconds': 0.2
|
219
|
+
},
|
220
|
+
'paper': {
|
221
|
+
'page_size': 50,
|
222
|
+
'delay_seconds': 2.0,
|
223
|
+
'num_retries': 5
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
# Mock file reading
|
228
|
+
mock_file = MagicMock()
|
229
|
+
mock_file.read.return_value = b'mocked file content'
|
230
|
+
mock_open.return_value.__enter__.return_value = mock_file
|
231
|
+
mock_json_load.return_value = settings_from_file
|
232
|
+
|
233
|
+
# Mock validation functions
|
234
|
+
mock_validate_audio.return_value = settings_from_file['audio']
|
235
|
+
mock_validate_paper.return_value = settings_from_file['paper']
|
236
|
+
|
237
|
+
# Mock file existence
|
238
|
+
mock_exists.return_value = True
|
239
|
+
|
240
|
+
with caplog.at_level(logging.ERROR, logger='audioarxiv'):
|
241
|
+
# Mock the `download_pdf` and `fitz.open` methods
|
242
|
+
with patch('fitz.open') as mock_fitz_open:
|
243
|
+
mock_page = MagicMock()
|
244
|
+
mock_page.get_text.return_value = [[None, None, None, None, "SECTION HEADER\n"],
|
245
|
+
[None, None, None, None, "Section Content"]]
|
246
|
+
mock_fitz_open.return_value = [mock_page]
|
247
|
+
main()
|
248
|
+
|
249
|
+
# Verify config_path was checked
|
250
|
+
mock_exists.assert_any_call(config_path)
|
251
|
+
mock_json_load.assert_called_once_with(mock_file)
|
252
|
+
|
253
|
+
|
254
|
+
@patch('audioarxiv.tools.main.user_config_dir', return_value='.') # Mock user_config_dir
|
255
|
+
@patch('os.path.exists')
|
256
|
+
@patch('builtins.open', new_callable=MagicMock) # Mock the open function to read the file
|
257
|
+
@patch('json.load') # Mock json.load to simulate loading settings
|
258
|
+
@patch('audioarxiv.audio.base.validate_audio_arguments') # Mock the validate_audio_arguments function
|
259
|
+
@patch('audioarxiv.resources.paper.validate_paper_arguments') # Mock the validate_paper_arguments function
|
260
|
+
@patch("audioarxiv.resources.paper.arxiv.Client")
|
261
|
+
@patch("configargparse.ArgParser.parse_args")
|
262
|
+
def test_load_settings_error(mock_parse_args,
|
263
|
+
mock_client_class,
|
264
|
+
mock_validate_paper,
|
265
|
+
mock_validate_audio,
|
266
|
+
mock_json_load,
|
267
|
+
mock_open,
|
268
|
+
mock_exists,
|
269
|
+
mock_user_config_dir,
|
270
|
+
mock_pyttsx3_init,
|
271
|
+
mock_paper_object,
|
272
|
+
caplog):
|
273
|
+
# Set up mock arguments
|
274
|
+
mock_args = MagicMock()
|
275
|
+
mock_args.id = "1234.5678"
|
276
|
+
mock_args.output = None
|
277
|
+
mock_args.list_voices = False
|
278
|
+
mock_args.rate = None
|
279
|
+
mock_args.volume = None
|
280
|
+
mock_args.voice = None
|
281
|
+
mock_args.pause_seconds = None
|
282
|
+
mock_args.page_size = None
|
283
|
+
mock_args.delay_seconds = None
|
284
|
+
mock_args.num_retries = None
|
285
|
+
mock_parse_args.return_value = mock_args
|
286
|
+
|
287
|
+
mock_engine = mock_pyttsx3_init() # noqa: F841
|
288
|
+
|
289
|
+
mock_client = MagicMock()
|
290
|
+
mock_client.results.return_value = iter([mock_paper_object])
|
291
|
+
mock_client_class.return_value = mock_client
|
292
|
+
|
293
|
+
mock_paper_object.download_pdf.return_value = "path/to/pdf"
|
294
|
+
|
295
|
+
# Set up the logger
|
296
|
+
logger = logging.getLogger('audioarxiv')
|
297
|
+
logger.setLevel(logging.DEBUG)
|
298
|
+
logger.propagate = True
|
299
|
+
|
300
|
+
# Sample settings to be loaded
|
301
|
+
config_dir = mock_user_config_dir('audioarxiv') # Assuming this function is defined correctly
|
302
|
+
config_file = 'config.json'
|
303
|
+
config_path = os.path.join(config_dir, config_file)
|
304
|
+
|
305
|
+
settings_from_file = {
|
306
|
+
'audio': {
|
307
|
+
'rate': 150,
|
308
|
+
'volume': 1.0,
|
309
|
+
'voice': None,
|
310
|
+
'pause_seconds': 0.2
|
311
|
+
},
|
312
|
+
'paper': {
|
313
|
+
'page_size': 50,
|
314
|
+
'delay_seconds': 2.0,
|
315
|
+
'num_retries': 5
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
# Mock validation functions
|
320
|
+
mock_validate_audio.return_value = settings_from_file['audio']
|
321
|
+
mock_validate_paper.return_value = settings_from_file['paper']
|
322
|
+
|
323
|
+
# Set up the mock to simulate file reading failure (File not found error)
|
324
|
+
mock_exists.return_value = False # Simulate that the file doesn't exist
|
325
|
+
with caplog.at_level(logging.INFO, logger='audioarxiv'):
|
326
|
+
with patch('fitz.open') as mock_fitz_open:
|
327
|
+
mock_page = MagicMock()
|
328
|
+
mock_page.get_text.return_value = [[None, None, None, None, "SECTION HEADER\n"],
|
329
|
+
[None, None, None, None, "Section Content"]]
|
330
|
+
mock_fitz_open.return_value = [mock_page]
|
331
|
+
main()
|
332
|
+
|
333
|
+
# Ensure the file existence check was called and failed
|
334
|
+
mock_exists.assert_any_call(config_path)
|
335
|
+
# Check for the error in logs
|
336
|
+
assert 'Saving default settings to ./config.json...' in caplog.text
|
337
|
+
|
338
|
+
# Now simulate an invalid JSON error
|
339
|
+
mock_exists.return_value = True
|
340
|
+
mock_file = MagicMock()
|
341
|
+
mock_file.read.return_value = b'mocked file content'
|
342
|
+
mock_open.return_value.__enter__.return_value = mock_file
|
343
|
+
mock_json_load.side_effect = json.JSONDecodeError("Expecting value", "doc", 0) # Simulate JSONDecodeError
|
344
|
+
mock_client.results.return_value = iter([mock_paper_object])
|
345
|
+
|
346
|
+
caplog.clear()
|
347
|
+
with caplog.at_level(logging.ERROR, logger='audioarxiv'):
|
348
|
+
with patch('fitz.open') as mock_fitz_open:
|
349
|
+
mock_page = MagicMock()
|
350
|
+
mock_page.get_text.return_value = [[None, None, None, None, "SECTION HEADER\n"],
|
351
|
+
[None, None, None, None, "Section Content"]]
|
352
|
+
mock_fitz_open.return_value = [mock_page]
|
353
|
+
main()
|
354
|
+
|
355
|
+
# Ensure json.load was called
|
356
|
+
mock_json_load.assert_called_once_with(mock_file)
|
357
|
+
# Check for the JSON error in logs
|
358
|
+
assert 'Error loading settings: Expecting value' in caplog.text
|
@@ -1,101 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import os
|
4
|
-
import signal
|
5
|
-
import tempfile
|
6
|
-
from unittest import mock
|
7
|
-
from unittest.mock import MagicMock, patch
|
8
|
-
|
9
|
-
import pytest
|
10
|
-
|
11
|
-
from audioarxiv.tools.main import (handle_exit, initialize_configuration, main,
|
12
|
-
save_settings)
|
13
|
-
|
14
|
-
|
15
|
-
# Patch where classes/functions are used, not defined
|
16
|
-
@pytest.mark.integration
|
17
|
-
@patch("audioarxiv.tools.main.Audio")
|
18
|
-
@patch("audioarxiv.tools.main.Paper")
|
19
|
-
@patch("audioarxiv.tools.main.configargparse.ArgParser.parse_args")
|
20
|
-
def test_main_with_id_and_output(mock_parse_args, mock_Paper, mock_Audio):
|
21
|
-
mock_args = MagicMock()
|
22
|
-
mock_args.id = "1234.5678"
|
23
|
-
mock_args.output = "output.mp3"
|
24
|
-
mock_args.list_voices = False
|
25
|
-
mock_args.rate = None
|
26
|
-
mock_args.volume = None
|
27
|
-
mock_args.voice = None
|
28
|
-
mock_args.pause_seconds = None
|
29
|
-
mock_args.page_size = None
|
30
|
-
mock_args.delay_seconds = None
|
31
|
-
mock_args.num_retries = None
|
32
|
-
mock_parse_args.return_value = mock_args
|
33
|
-
|
34
|
-
mock_audio = mock_Audio.return_value
|
35
|
-
mock_paper = mock_Paper.return_value
|
36
|
-
mock_paper.sections = [
|
37
|
-
{'header': "Introduction", 'content': ["This is content."]},
|
38
|
-
{'header': None, 'content': ["More content."]}
|
39
|
-
]
|
40
|
-
|
41
|
-
with patch("audioarxiv.tools.main.initialize_configuration") as mock_init_config:
|
42
|
-
mock_init_config.return_value = ({"audio": {}, "paper": {}}, "mock/config/path")
|
43
|
-
|
44
|
-
main()
|
45
|
-
|
46
|
-
mock_audio.save_article.assert_called_once()
|
47
|
-
assert mock_audio.save_article.call_args[1]["filename"] == "output.mp3"
|
48
|
-
|
49
|
-
|
50
|
-
@pytest.mark.integration
|
51
|
-
@patch("audioarxiv.tools.main.Audio")
|
52
|
-
@patch("audioarxiv.tools.main.configargparse.ArgParser.parse_args")
|
53
|
-
def test_main_list_voices(mock_parse_args, mock_Audio):
|
54
|
-
mock_args = MagicMock()
|
55
|
-
mock_args.list_voices = True
|
56
|
-
mock_parse_args.return_value = mock_args
|
57
|
-
|
58
|
-
mock_audio = mock_Audio.return_value
|
59
|
-
|
60
|
-
main()
|
61
|
-
|
62
|
-
mock_audio.list_voices.assert_called_once()
|
63
|
-
|
64
|
-
|
65
|
-
@pytest.mark.integration
|
66
|
-
@patch("audioarxiv.tools.main.validate_audio_arguments")
|
67
|
-
@patch("audioarxiv.tools.main.validate_paper_arguments")
|
68
|
-
def test_initialize_configuration_defaults(mock_validate_paper, mock_validate_audio):
|
69
|
-
mock_validate_audio.return_value = {'rate': 140, 'volume': 0.9, 'voice': None, 'pause_seconds': 0.1}
|
70
|
-
mock_validate_paper.return_value = {'page_size': 100, 'delay_seconds': 3.0, 'num_retries': 3}
|
71
|
-
|
72
|
-
dummy_args = MagicMock()
|
73
|
-
for attr in ['rate', 'volume', 'voice', 'pause_seconds', 'page_size', 'delay_seconds', 'num_retries']:
|
74
|
-
setattr(dummy_args, attr, None)
|
75
|
-
|
76
|
-
with tempfile.TemporaryDirectory() as tmp_dir_name:
|
77
|
-
config_path = os.path.join(tmp_dir_name, 'config.json') # noqa: F841 # pylint: disable=unused-variable
|
78
|
-
|
79
|
-
with patch("audioarxiv.tools.main.user_config_dir", return_value=tmp_dir_name):
|
80
|
-
settings, path = initialize_configuration(dummy_args)
|
81
|
-
assert settings['audio']['rate'] == 140
|
82
|
-
assert os.path.exists(path)
|
83
|
-
|
84
|
-
|
85
|
-
@pytest.mark.integration
|
86
|
-
@patch("builtins.open", new_callable=mock.mock_open)
|
87
|
-
def test_save_settings(mock_open_func):
|
88
|
-
settings = {"audio": {"rate": 150}, "paper": {"page_size": 50}}
|
89
|
-
save_settings("config.json", settings)
|
90
|
-
mock_open_func.assert_called_once_with("config.json", 'w', encoding='utf-8')
|
91
|
-
handle = mock_open_func()
|
92
|
-
handle.write.assert_called()
|
93
|
-
|
94
|
-
|
95
|
-
@pytest.mark.integration
|
96
|
-
@patch("audioarxiv.tools.main.sys.exit")
|
97
|
-
def test_handle_exit(mock_exit):
|
98
|
-
with patch("audioarxiv.tools.main.logger.info") as mock_logger:
|
99
|
-
handle_exit(signal.SIGINT, None)
|
100
|
-
mock_logger.assert_called_once()
|
101
|
-
mock_exit.assert_called_once_with(0)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/workflows/semantic-pr-check.yml
RENAMED
File without changes
|
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/.github/workflows/template-sync.yml
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/templates/custom-class-template.rst
RENAMED
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/docs/templates/custom-module-template.rst
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/preprocess/__init__.py
RENAMED
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/preprocess/article.py
RENAMED
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/preprocess/math_equation.py
RENAMED
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/src/audioarxiv/resources/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{audioarxiv-0.1.2rc62.post1 → audioarxiv-0.1.2rc68.post1}/tests/preprocess/test_math_equation.py
RENAMED
File without changes
|