def assert_breaking(self, breaking: bool, from_version: int, to_version: int) -> None: self.assertEqual(dbver.semver_is_breaking(from_version, to_version), breaking) if breaking: with self.assertRaises(dbver.VersionError): dbver.semver_check_breaking(from_version, to_version) else: dbver.semver_check_breaking(from_version, to_version)
def _apply(self, jobs: Iterable[Job]) -> None: with dbver.begin_pool(self._pool, dbver.IMMEDIATE) as conn: dbver.semver_check_breaking(LATEST, upgrade(conn)) for job in jobs: try: job(conn) except apsw.Error: _LOG.exception( "dropped resume data update %r", job, )
def iter_resume_data_from_db(conn: apsw.Connection) -> Iterator[lt.add_torrent_params]: version = get_version(conn) if version == 0: return dbver.semver_check_breaking(LATEST, version) cur = conn.cursor().execute( "SELECT info_sha1, info_sha256, resume_data, info FROM torrent" ) for row in cur: info_sha1, info_sha256, resume_data, info = cast( tuple[Optional[bytes], Optional[bytes], bytes, Optional[bytes]], row ) # NB: certain fields (creation date, creator, comment) live in the torrent_info # object at runtime, but are serialized with the resume data. If the b"info" # field is empty, the torrent_info won't be created, and these fields will be # dropped. We want to deserialize the resume data all at once, rather than # deserialize the torrent_info separately. info_dict: Optional[Any] = None if info is not None: try: with ltpy.translate_exceptions(): info_dict = lt.bdecode(info) except ltpy.Error: _LOG.exception( "%s parsing info dict", _log_ih_bytes(info_sha1, info_sha256) ) try: with ltpy.translate_exceptions(): bdecoded = lt.bdecode(resume_data) if not isinstance(bdecoded, dict): _LOG.error( "%s resume data not a dict", _log_ih_bytes(info_sha1, info_sha256), ) continue if bdecoded.get(b"info") is None and info_dict is not None: bdecoded[b"info"] = info_dict yield lt.read_resume_data(lt.bencode(bdecoded)) except ltpy.Error: _LOG.exception( "%s parsing resume data", _log_ih_bytes(info_sha1, info_sha256) )
def _user_write(pool: dbver.Pool) -> Iterator[Tuple[sqlite3.Connection, int]]: with dbver.begin_pool(pool, dbver.LockMode.IMMEDIATE) as conn: version = user_db.upgrade(conn) dbver.semver_check_breaking(version, _USER_VERSION_SUPPORTED) yield (conn, version)
def _meta_read(pool: dbver.Pool) -> Iterator[Tuple[sqlite3.Connection, int]]: with dbver.begin_pool(pool, dbver.LockMode.DEFERRED) as conn: version = metadata_db.get_version(conn) dbver.semver_check_breaking(version, _META_VERSION_SUPPORTED) yield (conn, version)
def write_metadata_db() -> Iterator[Tuple[sqlite3.Connection, int]]: # TODO: should we set WAL? where? with dbver.begin_pool(metadata_db_pool, dbver.LockMode.IMMEDIATE) as conn: version = metadata_db.upgrade(conn) dbver.semver_check_breaking(version, METADATA_DB_VERSION_SUPPORTED) yield (conn, version)