Skip to content

"Exception ignored" message emitted to console at end of I/O operation #524

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

Closed
rcook opened this issue Nov 12, 2024 · 5 comments
Closed
Labels
bug Something isn't working

Comments

@rcook
Copy link

rcook commented Nov 12, 2024

I think this is benign since the file is uploaded in its entirety, but it does messed up the output of my script:

Exception ignored in: <b2sdk._internal.stream.progress.ReadingStreamWithProgress object at 0x00000256ED631870>
Traceback (most recent call last):
  File "C:\python\Lib\site-packages\b2sdk\_internal\stream\wrapper.py", line 55, in flush
    self.stream.flush()
  File "C:\python\Lib\site-packages\b2sdk\_internal\stream\wrapper.py", line 55, in flush
    self.stream.flush()
ValueError: I/O operation on closed file.

This is the offending line of code: https://github.com/Backblaze/b2-sdk-python/blob/master/b2sdk/_internal/stream/wrapper.py#L55

Perhaps it would be as simple as wrapping the call to self.stream.flush() and catching ValueError.

@ppolewicz ppolewicz added the more-information-needed More information is needed label Nov 13, 2024
@ppolewicz
Copy link
Collaborator

Does this happen every time? I've never seen it so far.

Does your script do something weird with descriptors?

@rcook
Copy link
Author

rcook commented Nov 13, 2024

@ppolewicz : It does not happen every single time, but it happens often enough that it's distracting. Here's the class from my code that calls into the b2sdk:

from b2sdk.v2 import \
    AbstractAction, \
    AbstractFileSyncPolicy, \
    B2Api, \
    DownAndDeletePolicy, \
    LocalDeleteAction, \
    SqliteAccountInfo, \
    SyncPolicyManager
from b2sdk.v2 import ScanPoliciesManager
from b2sdk.v2 import parse_folder
from b2sdk.v2 import Synchronizer, SyncReport
from b2sdk.v2 import KeepOrDeleteMode, CompareVersionMode, NewerFileSyncMode
from pathlib import Path
from time import time
from typing import Generator, Optional
import sys


MAX_WORKERS: int = 10


class IgnoringSyncPolicyManager(SyncPolicyManager):
    def __init__(self, policies_manager: ScanPoliciesManager):
        self._policies_manager = policies_manager

    def get_policy_class(self, sync_type: str, delete: bool, keep_days: bool) -> AbstractFileSyncPolicy:
        def make_policy_class():
            policies_manager = self._policies_manager

            class IgnoringDownAndDeletePolicy(DownAndDeletePolicy):
                def _get_hide_delete_actions(self) -> Generator[AbstractAction, None, None]:
                    def predicate(action):
                        if isinstance(action, LocalDeleteAction):
                            return not policies_manager._exclude_file_set.matches(action.relative_name)
                        return True
                    yield from filter(predicate, super()._get_hide_delete_actions())

            return IgnoringDownAndDeletePolicy

        cls = super().get_policy_class(
            sync_type=sync_type,
            delete=delete,
            keep_days=keep_days)
        return cls if cls is not DownAndDeletePolicy else make_policy_class()


def run_b2_sync(source_path: Path | str, target_path: Path | str, compare_version_mode: CompareVersionMode, ignore_regex: Optional[str], single_threaded: bool, delete: bool, dry_run: bool) -> None:
    def now_millis() -> int:
        return int(round(time() * 1000))

    info = SqliteAccountInfo()
    b2_api = B2Api(info)

    source = parse_folder(str(source_path), b2_api)
    target = parse_folder(str(target_path), b2_api)

    policies_manager = ScanPoliciesManager(
        exclude_all_symlinks=True,
        exclude_file_regexes=[] if ignore_regex is None else [ignore_regex])
    sync_policy_manager = IgnoringSyncPolicyManager(
        policies_manager=policies_manager)

    keep_or_delete_mode = KeepOrDeleteMode.DELETE if delete else KeepOrDeleteMode.NO_DELETE
    synchronizer = Synchronizer(
        policies_manager=policies_manager,
        sync_policy_manager=sync_policy_manager,
        max_workers=1 if single_threaded else MAX_WORKERS,
        dry_run=dry_run,
        allow_empty_source=True,
        compare_version_mode=compare_version_mode,
        compare_threshold=0,
        newer_file_mode=NewerFileSyncMode.SKIP,
        keep_days_or_delete=keep_or_delete_mode)

    with SyncReport(sys.stdout, no_progress=False) as reporter:
        # Warnings about ignore exception thrown in call to sync_folders
        synchronizer.sync_folders(
            source_folder=source,
            dest_folder=target,
            now_millis=now_millis(),
            reporter=reporter)

I don't think this code is doing anything particularly strange.

@titus8
Copy link

titus8 commented Nov 14, 2024

Is this message a feature of Python 3.13? From https://docs.python.org/3/whatsnew/3.13.html:

io
The IOBase finalizer now logs any errors raised by the close() method with sys.unraisablehook. Previously, errors were ignored silently by default, and only logged in Python Development Mode or when using a Python debug build. (Contributed by Victor Stinner in gh-62948.)

I observed the message on b2sdk-2.5.1, but not in b2sdk-2.6.0, so I assumed that b2sdk now avoids the issue. BTW I'm using MacOS Sequoia 15.0.1.

@rcook
Copy link
Author

rcook commented Nov 14, 2024

@titus8 : Yes, I'm using Python 3.13. I'll update the latest b2sdk. Thanks!

@ppolewicz ppolewicz added bug Something isn't working and removed more-information-needed More information is needed labels Nov 14, 2024
@rcook
Copy link
Author

rcook commented Nov 14, 2024

This is the commit where this was fixed: cad8b2b

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants