Skip to content

Commit

Permalink
fix missing exception #836 (#839)
Browse files Browse the repository at this point in the history
* bump mypy 1.7.0->1.10.0

* fix missing exception #836

* add hidden imports pyinstaller/pyinstaller#8554

* bump pyinstaller 5.13.2 -> 6.7.0
  • Loading branch information
AndreyNikiforov authored May 23, 2024
1 parent ccb995c commit 29039b5
Show file tree
Hide file tree
Showing 43 changed files with 427 additions and 375 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/quality-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ jobs:
icloudpd_changelog: ${{steps.get_version.outputs.icloudpd_changelog}}

build_package:
needs: [get_version, skip_check]
needs: [get_version, skip_check, type_check, lint, test]
if: needs.skip_check.outputs.should_skip != 'true'
uses: ./.github/workflows/build-package.yml
with:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- fix: missing exception [#836](https://github.com/icloud-photos-downloader/icloud_photos_downloader/issues/836)

## 1.17.5 (2024-04-27)

- experimental: fix errors in npm packages
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies = [
"tqdm==4.66.0",
"piexif==1.1.3",
"urllib3==1.26.16",
"typing_extensions==4.11.0",
# from pyicloud_ipd
"six==1.16.0",
"tzlocal==4.3.1",
Expand All @@ -42,7 +43,7 @@ dependencies = [
[project.optional-dependencies]
dev = [
"twine==4.0.2",
"pyinstaller==5.13.2",
"pyinstaller==6.7.0",
"wheel==0.42.0",
]
devlinux = [
Expand All @@ -61,7 +62,7 @@ test = [
"autopep8==2.0.2",
"pytest-timeout==2.1.0",
"pytest-xdist==3.3.1",
"mypy==1.7.0",
"mypy==1.10.0",
"types-pytz==2022.7.1.2",
"types-tzlocal==4.3.0.0",
"types-requests==2.31.0.2",
Expand Down
6 changes: 3 additions & 3 deletions scripts/build_bin_linux
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ set -euo pipefail
# expects python with installed dependencies
# required params: version plat

pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd.py src/starters/icloud.py --name icloudpd-$1-linux-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloud.py --name icloud-$1-linux-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd_ex.py --name icloudpd-ex-$1-linux-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd.py src/starters/icloud.py --name icloudpd-$1-linux-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloud.py --name icloud-$1-linux-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd_ex.py --name icloudpd-ex-$1-linux-$2
6 changes: 3 additions & 3 deletions scripts/build_bin_macos
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ set -euo pipefail
# expects python with installed dependencies
# required params: version arch

pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd.py src/starters/icloud.py --name icloudpd-$1-macos-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloud.py --name icloud-$1-macos-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd_ex.py --name icloudpd-ex-$1-macos-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd.py src/starters/icloud.py --name icloudpd-$1-macos-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloud.py --name icloud-$1-macos-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd_ex.py --name icloudpd-ex-$1-macos-$2
6 changes: 3 additions & 3 deletions scripts/build_bin_windows
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ set -euo pipefail
# expects python with installed dependencies
# required param: version arch

pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd.py src/starters/icloud.py --name icloudpd-$1-windows-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloud.py --name icloud-$1-windows-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd_ex.py --name icloudpd-ex-$1-windows-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd.py src/starters/icloud.py --name icloudpd-$1-windows-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloud.py --name icloud-$1-windows-$2
pyinstaller --collect-all keyrings.alt --hidden-import pkg_resources.extern --hidden-import pkgutil --collect-all tzdata --onefile src/starters/icloudpd_ex.py --name icloudpd-ex-$1-windows-$2
4 changes: 2 additions & 2 deletions scripts/type_check
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
set -euo pipefail
echo "Running mypy..."
python3 -m mypy src tests
# too strict now: --disallow-any-generics --disallow-untyped-defs --strict-equality --disallow-untyped-calls --warn-return-any
python3 -m mypy src tests --strict-equality --warn-return-any --disallow-any-generics
# too strict now: --disallow-untyped-defs --disallow-untyped-calls --check-untyped-defs
17 changes: 9 additions & 8 deletions src/icloudpd/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import logging
import sys
from typing import Callable, Optional
import click
import pyicloud_ipd

Expand All @@ -13,14 +14,14 @@ class TwoStepAuthRequiredError(Exception):
"""


def authenticator(logger: logging.Logger, domain: str):
def authenticator(logger: logging.Logger, domain: str) -> Callable[[str, Optional[str], Optional[str], bool, Optional[str]], pyicloud_ipd.PyiCloudService]:
"""Wraping authentication with domain context"""
def authenticate_(
username,
password,
cookie_directory=None,
raise_error_on_2sa=False,
client_id=None,
username:str,
password:Optional[str],
cookie_directory:Optional[str]=None,
raise_error_on_2sa:bool=False,
client_id:Optional[str]=None,
) -> pyicloud_ipd.PyiCloudService:
"""Authenticate with iCloud username and password"""
logger.debug("Authenticating...")
Expand Down Expand Up @@ -59,7 +60,7 @@ def authenticate_(
return authenticate_


def request_2sa(icloud: pyicloud_ipd.PyiCloudService, logger: logging.Logger):
def request_2sa(icloud: pyicloud_ipd.PyiCloudService, logger: logging.Logger) -> None:
"""Request two-step authentication. Prompts for SMS or device"""
devices = icloud.trusted_devices
devices_count = len(devices)
Expand Down Expand Up @@ -99,7 +100,7 @@ def request_2sa(icloud: pyicloud_ipd.PyiCloudService, logger: logging.Logger):
)


def request_2fa(icloud: pyicloud_ipd.PyiCloudService, logger: logging.Logger):
def request_2fa(icloud: pyicloud_ipd.PyiCloudService, logger: logging.Logger) -> None:
"""Request two-factor authentication."""
try:
devices = icloud.trusted_devices
Expand Down
7 changes: 4 additions & 3 deletions src/icloudpd/autodelete.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from tzlocal import get_localzone
from icloudpd.paths import local_download_path
import pyicloud_ipd
from pyicloud_ipd.services.photos import PhotoLibrary, PhotosService


def delete_file(logger: logging.Logger, path: str) -> bool:
Expand All @@ -24,9 +25,9 @@ def delete_file_dry_run(logger: logging.Logger, path: str) -> bool:
def autodelete_photos(
logger: logging.Logger,
dry_run: bool,
library_object,
library_object: PhotoLibrary,
folder_structure: str,
directory: str):
directory: str) -> None:
"""
Scans the "Recently Deleted" folder and deletes any matching files
from the download directory.
Expand All @@ -51,7 +52,7 @@ def autodelete_photos(
for size in ["small", "original", "medium", "thumb"]:
path = os.path.normpath(
local_download_path(
media, size, download_dir))
media.filename, size, download_dir))
if os.path.exists(path):
logger.debug("Deleting %s...", path)
delete_local = delete_file_dry_run if dry_run else delete_file
Expand Down
52 changes: 27 additions & 25 deletions src/icloudpd/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
from icloudpd.email_notifications import send_2sa_notification
from icloudpd import download
from icloudpd.authentication import authenticator, TwoStepAuthRequiredError
from pyicloud_ipd.services.photos import PhotoAsset
from pyicloud_ipd.services.photos import PhotoAsset, PhotoLibrary
from pyicloud_ipd.exceptions import PyiCloudAPIResponseException
from pyicloud_ipd import PyiCloudService
from tzlocal import get_localzone
from tqdm.contrib.logging import logging_redirect_tqdm
from tqdm import tqdm
import click
import urllib
from typing import Callable, Optional, TypeVar, cast
from typing import Callable, Iterable, NoReturn, Optional, Sequence, TypeVar, cast
import json
import subprocess
import itertools
Expand Down Expand Up @@ -247,7 +247,7 @@
# pylint: disable-msg=too-many-branches,too-many-locals
def main(
directory: Optional[str],
username: Optional[str],
username: str,
password: Optional[str],
auth_only: bool,
cookie_directory: str,
Expand All @@ -257,8 +257,8 @@ def main(
until_found: Optional[int],
album: str,
list_albums: bool,
library,
list_libraries,
library: str,
list_libraries: bool,
skip_videos: bool,
skip_live_photos: bool,
force_size: bool,
Expand All @@ -281,7 +281,7 @@ def main(
domain: str,
watch_with_interval: Optional[int],
dry_run: bool
):
) -> NoReturn:
"""Download all iCloud photos to a local directory"""

logging.basicConfig(
Expand Down Expand Up @@ -470,7 +470,7 @@ def download_photo_(counter: Counter, photo: PhotoAsset) -> bool:
download_size = "original"

download_path = local_download_path(
photo, download_size, download_dir)
photo.filename, download_size, download_dir)

original_download_path = None
file_exists = os.path.isfile(download_path)
Expand Down Expand Up @@ -604,7 +604,7 @@ def download_photo_(counter: Counter, photo: PhotoAsset) -> bool:
def delete_photo(
logger: logging.Logger,
icloud: PyiCloudService,
photo: PhotoAsset):
photo: PhotoAsset) -> None:
"""Delete a photo from the iCloud account."""
clean_filename_local = clean_filename(photo.filename)
logger.debug(
Expand Down Expand Up @@ -638,7 +638,7 @@ def delete_photo(
def delete_photo_dry_run(
logger: logging.Logger,
_icloud: PyiCloudService,
photo: PhotoAsset):
photo: PhotoAsset) -> None:
"""Dry run for deleting a photo from the iCloud"""
logger.info(
"[DRY RUN] Would delete %s in iCloud",
Expand All @@ -665,9 +665,9 @@ def retrier(
raise


def session_error_handle_builder(logger: Logger, icloud: PyiCloudService):
def session_error_handle_builder(logger: Logger, icloud: PyiCloudService) -> Callable[[Exception, int], None]:
"""Build handler for session error"""
def session_error_handler(ex, attempt):
def session_error_handler(ex: Exception, attempt: int) -> None:
"""Handles session errors in the PhotoAlbum photos iterator"""
if "Invalid global session" in str(ex):
if attempt > constants.MAX_RETRIES:
Expand All @@ -685,7 +685,7 @@ def session_error_handler(ex, attempt):
return session_error_handler


def internal_error_handle_builder(logger: logging.Logger):
def internal_error_handle_builder(logger: logging.Logger) -> Callable[[Exception, int], None]:
"""Build handler for internal error"""
def internal_error_handler(ex: Exception, attempt: int) -> None:
"""Handles session errors in the PhotoAlbum photos iterator"""
Expand All @@ -702,9 +702,9 @@ def internal_error_handler(ex: Exception, attempt: int) -> None:
return internal_error_handler


def compose_handlers(handlers):
def compose_handlers(handlers: Sequence[Callable[[Exception, int], None]]) -> Callable[[Exception, int], None]:
"""Compose multiple error handlers"""
def composed(ex, retries):
def composed(ex: Exception, retries: int) -> None:
for handler in handlers:
handler(ex, retries)
return composed
Expand All @@ -716,7 +716,7 @@ def composed(ex, retries):
def core(
downloader: Callable[[PyiCloudService], Callable[[Counter, PhotoAsset], bool]],
directory: Optional[str],
username: Optional[str],
username: str,
password: Optional[str],
auth_only: bool,
cookie_directory: str,
Expand All @@ -725,8 +725,8 @@ def core(
until_found: Optional[int],
album: str,
list_albums: bool,
library,
list_libraries,
library: str,
list_libraries: bool,
skip_videos: bool,
auto_delete: bool,
only_print_filenames: bool,
Expand All @@ -745,7 +745,7 @@ def core(
logger: logging.Logger,
watch_interval: Optional[int],
dry_run: bool
):
) -> int:
"""Download all iCloud photos to a local directory"""

raise_error_on_2sa = (
Expand All @@ -759,7 +759,7 @@ def core(
password,
cookie_directory,
raise_error_on_2sa,
client_id=os.environ.get("CLIENT_ID"),
os.environ.get("CLIENT_ID"),
)
except TwoStepAuthRequiredError:
if notification_script is not None:
Expand All @@ -784,7 +784,7 @@ def core(
download_photo = downloader(icloud)

# Access to the selected library. Defaults to the primary photos object.
library_object = icloud.photos
library_object: PhotoLibrary = icloud.photos

if list_libraries:
libraries_dict = icloud.photos.libraries
Expand Down Expand Up @@ -840,27 +840,29 @@ def core(

photos_count: Optional[int] = len(photos)

photos_enumerator: Iterable[PhotoAsset] = photos

# Optional: Only download the x most recent photos.
if recent is not None:
photos_count = recent
photos = itertools.islice(photos, recent)
photos_enumerator = itertools.islice(photos_enumerator, recent)

if until_found is not None:
photos_count = None
# ensure photos iterator doesn't have a known length
photos = (p for p in photos)
photos_enumerator = (p for p in photos_enumerator)

# Skip the one-line progress bar if we're only printing the filenames,
# or if the progress bar is explicitly disabled,
# or if this is not a terminal (e.g. cron or piping output to file)
skip_bar = not os.environ.get("FORCE_TQDM") and (
only_print_filenames or no_progress_bar or not sys.stdout.isatty())
if skip_bar:
photos_enumerator = photos
photos_enumerator = photos_enumerator
# logger.set_tqdm(None)
else:
photos_enumerator = tqdm(
iterable=photos,
iterable=photos_enumerator,
total=photos_count,
leave=False,
dynamic_ncols=True,
Expand Down Expand Up @@ -907,7 +909,7 @@ def should_break(counter: Counter) -> bool:
consecutive_files_found,
item) and delete_after_download:

def delete_cmd():
def delete_cmd() -> None:
delete_local = delete_photo_dry_run if dry_run else delete_photo
delete_local(logger, icloud, item)

Expand Down
8 changes: 4 additions & 4 deletions src/icloudpd/counter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@


class Counter(object):
def __init__(self, value=0):
def __init__(self, value:int=0):
self.initial_value = value
self.val = RawValue('i', value)
self.lock = Lock()

def increment(self):
def increment(self) -> None:
with self.lock:
self.val.value += 1

def reset(self):
def reset(self) -> None:
with self.lock:
self.val = RawValue('i', self.initial_value)

def value(self) -> int:
with self.lock:
return self.val.value
return int(self.val.value)
Loading

0 comments on commit 29039b5

Please sign in to comment.