chore(lint): more linting fixes

This commit is contained in:
2025-04-30 17:22:21 +03:00
parent af085bf9bb
commit d0669e4dd0
6 changed files with 98 additions and 61 deletions

View File

@@ -20,7 +20,6 @@ indent_style = tab
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false
max_line_length = 100
[*.yml] [*.{yml,toml,md}]
max_line_length = 100 max_line_length = 200

View File

@@ -6,22 +6,22 @@ This guide will help you get started.
## 🛠 Project Setup ## 🛠 Project Setup
1. Clone the repository: 1. Clone the repository:
```bash ```bash
git clone https://github.com/your-username/aeonview.git git clone https://github.com/your-username/aeonview.git
cd aeonview cd aeonview
``` ```
2. Set up your environment: 2. Set up your environment:
```bash ```bash
python3 -m venv venv python3 -m venv venv
source venv/bin/activate source venv/bin/activate
pip install -r dev-requirements.txt pip install -r dev-requirements.txt
``` ```
3. Install pre-commit hooks: 3. Install pre-commit hooks:
```bash ```bash
pre-commit install pre-commit install
``` ```
## ✅ Development Workflow ## ✅ Development Workflow
@@ -46,20 +46,20 @@ make lint # check for lint errors
## ✅ Submitting a Pull Request ## ✅ Submitting a Pull Request
1. Create a feature branch: 1. Create a feature branch:
```bash ```bash
git checkout -b feature/my-new-feature git checkout -b feature/my-new-feature
``` ```
2. Commit your changes: 2. Commit your changes:
```bash ```bash
git add . git add .
git commit -m "feat: add support for X" git commit -m "feat: add support for X"
``` ```
3. Push and open a pull request: 3. Push and open a pull request:
```bash ```bash
git push origin feature/my-new-feature git push origin feature/my-new-feature
``` ```
4. Follow the PR template and link any relevant issues. 4. Follow the PR template and link any relevant issues.

View File

@@ -1,8 +1,8 @@
```markdown ```markdown
_) # _)
_` | -_) _ \ \ \ \ / | -_) \ \ \ / # _` | -_) _ \ \ \ \ / | -_) \ \ \ /
\__,_| \___| \___/ _| _| \_/ _| \___| \_/\_/ # \__,_| \___| \___/ _| _| \_/ _| \___| \_/\_/
aeonview - a simple timelapse tool # aeonview - a simple timelapse tool
``` ```
**aeonview** is a Python-based tool for generating timelapse videos **aeonview** is a Python-based tool for generating timelapse videos

View File

@@ -365,12 +365,12 @@ class AeonViewHelpers:
if url.endswith(".png"): if url.endswith(".png"):
return ".png" return ".png"
elif url.endswith(".gif"): if url.endswith(".gif"):
return ".gif" return ".gif"
elif url.endswith(".webp"): if url.endswith(".webp"):
return ".webp" return ".webp"
else:
return ".jpg" return ".jpg"
@staticmethod @staticmethod
def setup_logger(verbose: bool): def setup_logger(verbose: bool):

View File

@@ -92,27 +92,35 @@ def test_get_image_paths_valid():
date = datetime(2025, 4, 10, 12, 30, 45) date = datetime(2025, 4, 10, 12, 30, 45)
aeon_view_images = AeonViewImages(destination_base, url) aeon_view_images = AeonViewImages(destination_base, url)
paths = aeon_view_images.get_image_paths(url, destination_base, date) paths = aeon_view_images.get_image_paths(url, destination_base, date)
a_p = destination_base / "2025-04" / "10" / "12-30-45.jpg"
assert paths["url"] == url assert paths["url"] == url
assert paths["file"] == "12-30-45.jpg" assert paths["file"] == "12-30-45.jpg"
assert paths["destinations"][ assert paths["destinations"]["file"] == a_p
"file"] == destination_base / "2025-04" / "10" / "12-30-45.jpg"
def test_get_image_paths_invalid_url(): def test_get_image_paths_invalid_url():
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log: with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
aeon_view_images = AeonViewImages(default_test_path, "invalid-url") aeon_view_images = AeonViewImages(default_test_path, "invalid-url")
aeon_view_images.get_image_paths("invalid-url", default_test_path, aeon_view_images.get_image_paths(
datetime(2025, 4, 10)) "invalid-url",
default_test_path,
datetime(2025, 4, 10)
)
assert AeonViewMessages.INVALID_URL in log.call_args[0][0] assert AeonViewMessages.INVALID_URL in log.call_args[0][0]
def test_get_image_paths_invalid_date(): def test_get_image_paths_invalid_date():
with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log: with pytest.raises(SystemExit), mock.patch("aeonview.logging.error") as log:
aeon_view_images = AeonViewImages(default_test_path, aeon_view_images = AeonViewImages(
f"{default_image_domain}.jpg") default_test_path,
aeon_view_images.get_image_paths(f"{default_image_domain}.jpg", f"{default_image_domain}.jpg"
default_test_path, )
"invalid-date") # pyright: ignore [reportArgumentType] aeon_view_images.get_image_paths(
f"{default_image_domain}.jpg",
default_test_path,
"invalid-date" # pyright: ignore [reportArgumentType]
)
assert AeonViewMessages.INVALID_DATE in log.call_args[0][0] assert AeonViewMessages.INVALID_DATE in log.call_args[0][0]
@@ -138,8 +146,11 @@ def test_download_image_success(mock_get):
with tempfile.NamedTemporaryFile(delete=True) as temp_file: with tempfile.NamedTemporaryFile(delete=True) as temp_file:
destination = Path(temp_file.name) destination = Path(temp_file.name)
avi.download_image(destination) avi.download_image(destination)
mock_get.assert_called_once_with(f"{default_image_domain}.jpg", mock_get.assert_called_once_with(
stream=True, timeout=10) f"{default_image_domain}.jpg",
stream=True,
timeout=10
)
assert destination.exists() assert destination.exists()
@@ -161,8 +172,13 @@ def test_download_image_failure(mock_get):
@mock.patch("aeonview.AeonViewHelpers.mkdir_p") @mock.patch("aeonview.AeonViewHelpers.mkdir_p")
@mock.patch("subprocess.run") @mock.patch("subprocess.run")
def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p): def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", args = argparse.Namespace(
year="2025") simulate=False,
fps=10,
day="01",
month="04",
year="2025"
)
avv = AeonViewVideos(default_test_path, args) avv = AeonViewVideos(default_test_path, args)
with mock.patch("aeonview.logging.info") as log: with mock.patch("aeonview.logging.info") as log:
avv.generate_daily_video() avv.generate_daily_video()
@@ -177,31 +193,46 @@ def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
@mock.patch("aeonview.AeonViewHelpers.mkdir_p") @mock.patch("aeonview.AeonViewHelpers.mkdir_p")
def test_generate_daily_video_simulate(mock_mkdir_p): def test_generate_daily_video_simulate(mock_mkdir_p):
args = argparse.Namespace(simulate=True, fps=10, day="01", month="04", args = argparse.Namespace(
year="2025") simulate=True,
fps=10,
day="01",
month="04",
year="2025"
)
avv = AeonViewVideos(default_test_path, args) avv = AeonViewVideos(default_test_path, args)
avv.generate_daily_video() avv.generate_daily_video()
mock_mkdir_p.assert_not_called() mock_mkdir_p.assert_not_called()
def test_generate_monthly_video_not_implemented(): def test_generate_monthly_video_not_implemented():
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", args = argparse.Namespace(
year="2025") simulate=False,
fps=10,
day="01",
month="04",
year="2025"
)
avv = AeonViewVideos(default_test_path, args) avv = AeonViewVideos(default_test_path, args)
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
avv.generate_monthly_video(Path("/tmp")) avv.generate_monthly_video(Path("/tmp"))
def test_generate_yearly_video_not_implemented(): def test_generate_yearly_video_not_implemented():
args = argparse.Namespace(simulate=False, fps=10, day="01", month="04", args = argparse.Namespace(
year="2025") simulate=False,
fps=10,
day="01",
month="04",
year="2025"
)
avv = AeonViewVideos(default_test_path, args) avv = AeonViewVideos(default_test_path, args)
with pytest.raises(NotImplementedError): with pytest.raises(NotImplementedError):
avv.generate_yearly_video(Path("/tmp")) avv.generate_yearly_video(Path("/tmp"))
@mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--url", @mock.patch("sys.argv", ["aeonview.py", "--mode", "image", "--url",
f"{default_image_domain}.jpg"]) f"{default_image_domain}.jpg"])
def test_parse_arguments_image_mode(): def test_parse_arguments_image_mode():
args, _ = AeonViewHelpers.parse_arguments() args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "image" assert args.mode == "image"
@@ -210,7 +241,7 @@ def test_parse_arguments_image_mode():
@mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--project", @mock.patch("sys.argv", ["aeonview.py", "--mode", "video", "--project",
f"{default_project}"]) f"{default_project}"])
def test_parse_arguments_video_mode(): def test_parse_arguments_video_mode():
args, _ = AeonViewHelpers.parse_arguments() args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "video" assert args.mode == "video"
@@ -234,8 +265,10 @@ def test_parse_arguments_fps():
assert args.fps == 30 assert args.fps == 30
@mock.patch("sys.argv", @mock.patch(
["aeonview.py", "--mode", "video", "--generate", "2023-10-01"]) "sys.argv",
["aeonview.py", "--mode", "video", "--generate", "2023-10-01"]
)
def test_parse_arguments_generate_date(): def test_parse_arguments_generate_date():
args, _ = AeonViewHelpers.parse_arguments() args, _ = AeonViewHelpers.parse_arguments()
assert args.mode == "video" assert args.mode == "video"
@@ -268,7 +301,10 @@ def test_image_simulation(mock_download_image, mock_mkdir_p):
args.simulate = True args.simulate = True
args.date = "2025-04-10 12:30:45" args.date = "2025-04-10 12:30:45"
avi = AeonViewImages( avi = AeonViewImages(
default_test_path, f"{default_image_domain}.jpg", args) default_test_path,
f"{default_image_domain}.jpg",
args
)
with mock.patch("aeonview.logging.info") as log: with mock.patch("aeonview.logging.info") as log:
avi.get_current_image() avi.get_current_image()
mock_mkdir_p.assert_not_called() mock_mkdir_p.assert_not_called()
@@ -302,7 +338,8 @@ def test_setup_logger_verbose(mock_basic_config):
AeonViewHelpers.setup_logger(verbose=True) AeonViewHelpers.setup_logger(verbose=True)
mock_basic_config.assert_called_once_with( mock_basic_config.assert_called_once_with(
level=logging.DEBUG, level=logging.DEBUG,
format="[%(levelname)s] %(message)s") format="[%(levelname)s] %(message)s"
)
@mock.patch("logging.basicConfig") @mock.patch("logging.basicConfig")
@@ -310,4 +347,5 @@ def test_setup_logger_non_verbose(mock_basic_config):
AeonViewHelpers.setup_logger(verbose=False) AeonViewHelpers.setup_logger(verbose=False)
mock_basic_config.assert_called_once_with( mock_basic_config.assert_called_once_with(
level=logging.INFO, level=logging.INFO,
format="[%(levelname)s] %(message)s") format="[%(levelname)s] %(message)s"
)

View File

@@ -21,4 +21,4 @@ python_files = ["*_test.py"]
ignore-patterns='^\.#' ignore-patterns='^\.#'
ignore-paths='^\.#' ignore-paths='^\.#'
ignore='CVS,.venv' ignore='CVS,.venv'
disable='attribute-defined-outside-init,invalid-name,missing-docstring,protected-access,too-few-public-methods,format' disable='attribute-defined-outside-init,invalid-name,missing-docstring,protected-access,too-many-instance-attributes,too-few-public-methods,format'