Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix missing exception #836 #839

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
43af942
add warn-return-any
AndreyNikiforov May 22, 2024
10a952f
add disable any generic
AndreyNikiforov May 22, 2024
3a7101b
add types to tests
AndreyNikiforov May 22, 2024
ddd6604
add types to tests
AndreyNikiforov May 22, 2024
af89cfc
add types to icloudpd src
AndreyNikiforov May 22, 2024
97ca7c9
replace union type with optional
AndreyNikiforov May 22, 2024
cda5ddb
bump mypy 1.7.0->1.10.0
AndreyNikiforov May 22, 2024
56a5c84
fix types for base mypy run
AndreyNikiforov May 22, 2024
0f87e2e
block builds on test runs
AndreyNikiforov May 22, 2024
f5a89a6
fix mypy errors from gh actions
AndreyNikiforov May 22, 2024
333f743
disable type check in unused services
AndreyNikiforov May 22, 2024
462173d
fix exceptions
AndreyNikiforov May 22, 2024
486d700
adding types to photos
AndreyNikiforov May 23, 2024
dcd0fdc
add types to utils
AndreyNikiforov May 23, 2024
96b9106
fix types to icloudpd base
AndreyNikiforov May 23, 2024
378974f
add types to py base
AndreyNikiforov May 23, 2024
7fe7e1c
more types to py base
AndreyNikiforov May 23, 2024
a30495c
ignore wierd old code
AndreyNikiforov May 23, 2024
5f5df69
fix undefined exception #836
AndreyNikiforov May 23, 2024
1487305
switch types from dict to Dict
AndreyNikiforov May 23, 2024
10b7a70
update changelog
AndreyNikiforov May 23, 2024
d3adb3c
add hidden imports
AndreyNikiforov May 23, 2024
ce7807a
bump pyinstaller 5.13.2 -> 6.7.0
AndreyNikiforov May 23, 2024
8f495dc
add typing extensions
AndreyNikiforov May 23, 2024
ea09f6e
rename typing_extensions
AndreyNikiforov May 23, 2024
883afdb
move typing_extensions to dev
AndreyNikiforov May 23, 2024
2663ade
move typing_extensions into main dependencies
AndreyNikiforov May 23, 2024
d2c550d
fix typo
AndreyNikiforov May 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading