comicbox 5.2.0__tar.gz → 5.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {comicbox-5.2.0 → comicbox-5.2.1}/NEWS.md +6 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/PKG-INFO +1 -1
- {comicbox-5.2.0 → comicbox-5.2.1}/bun.lock +9 -9
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicvine_api/online_source.py +70 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/online_session.py +20 -8
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/run.py +14 -5
- {comicbox-5.2.0 → comicbox-5.2.1}/docs/NEWS.md +6 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/package.json +1 -1
- {comicbox-5.2.0 → comicbox-5.2.1}/pyproject.toml +1 -1
- {comicbox-5.2.0 → comicbox-5.2.1}/pyproject.toml.orig +1 -1
- comicbox-5.2.1/tests/unit/test_comicvine_session_lifecycle.py +273 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/online_client.py +14 -15
- {comicbox-5.2.0 → comicbox-5.2.1}/uv.lock +1 -1
- {comicbox-5.2.0 → comicbox-5.2.1}/.dockerignore +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/.gitignore +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/.prettierignore +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/.readthedocs.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/.remarkignore +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/.shellcheckrc +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/Makefile +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/README.md +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/ci-download-dist-if-identical.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/clean-pycache.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/cleanup-pypi-alpha.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/dev-module.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/docker-compose-exit.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/docker-tag-latest.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/fix-docker.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/fix-python.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/fix-sh.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/fix.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/kill-eslint_d.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/lint-ci.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/lint-complexity.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/lint-darwin.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/lint-docker.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/lint-python.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/lint-sh.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/lint.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/prettier-nginx.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/publish-pypi.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/regenerate-pdf-test-pages.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/release-tag.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/remark-for-claude.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/roman.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/sort-ignore.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/test-docker.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/test-python.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/type-python.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/uml.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/update-deps-node.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/update-deps-python.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/version-node.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/bin/version-python.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/ci.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/common.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/docker.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/docs.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/eslint.config.base.js +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/gha_std.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/help.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/node.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/node_root.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/cfg/python.mk +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/check-jsonschema.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/_pdf.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/_rar.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/archive.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/archiveinfo.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/filenames.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/init.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/mtime.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/pages.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/read.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/sniff.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/archive/write.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/date.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/issue.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/notes.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/pages.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/stamp.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/stories_title.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/url_identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/computed/urls.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/dump.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/dump_files.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/init.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/load.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/merge.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/metadata.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/normalize.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/online_covers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/online_lookup.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/pages/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/pages/covers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/pages/extract.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/print.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/sources.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/types.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/validate/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/box/validate/guess_format.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/cli/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/cli/__main__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/cli/epilog.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/cli/parser.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/computed.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/formats.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/online/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/online/build.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/online/settings.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/online/template.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/paths.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/read.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config/settings.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/config_default.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/constants.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/empty.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/comet.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/comicbookinfo.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/comicbox.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/comicinfo.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/generic/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/generic/age_rating.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/generic/role.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/maps/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/maps/age_rating.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/maps/formats.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/maps/identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/maps/reading_direction.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/maps/roles.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/enums/metroninfo.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/events.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/exceptions.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/_base.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/collection_fields.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/comicbox.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/comicinfo.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/enum_fields.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/fields.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/metroninfo.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/number_fields.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/pdf.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/pycountry.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/time_fields.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/union.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/fields/xml_fields.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/auto_engage.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/cli_overrides.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/cover_hash.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/credentials.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/matcher.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/outcome_stats.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/profile.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/prompt.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/rate_gate.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/rate_limits.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/retry.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/sanitize.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/selector.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/series_cache.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/series_filter.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/session_state.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/signals.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/sources/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/sources/base.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/transform_helpers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/vacuum.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/online/warn_once.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/schemas/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/schemas/base.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/schemas/cache.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/schemas/decorators.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/schemas/error_store.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/schemas/json_schemas.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/schemas/xml_schemas.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/schemas/yaml.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/transforms/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/transforms/base.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/transforms/identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/transforms/price.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/transforms/publishing_tags.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/transforms/spec.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/transforms/xml_credits.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/base/transforms/xml_reprints.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comet/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comet/schema.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comet/transform/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comet/transform/reprints.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_book_info/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_book_info/schema.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_book_info/transform/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_book_info/transform/credits.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_book_info/transform/rating.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_info/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_info/schema.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_info/transform/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_info/transform/identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_info/transform/manga.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_info/transform/pages.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comic_info/transform/storyarcs.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/schema/README.md +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/schema/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/schema/cli.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/schema/identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/schema/json_schema.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/schema/pages.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/schema/publishing.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/schema/yaml.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/transform/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/transform/cli.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/transform/credits.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/transform/json.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/transform/name_objs.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicbox/transform/yaml.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicvine_api/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicvine_api/schema.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/comicvine_api/transform.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/filename/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/filename/schema.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/filename/transform.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_api/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_api/online_source.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_api/paced_session.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_api/schema.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_api/transform.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/schema/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/schema/credits.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/schema/identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/schema/price.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/schema/publishing.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/schema/rating.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/schema/resource.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/const.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/credits.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/identified_name.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/identifier_attribute.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/publishing_tags.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/rating.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/reprints.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/metron_info/transform/resources.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/pdf/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/pdf/schema.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/pdf/transform/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/pdf/transform/credits.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/formats/sources.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/identifiers/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/identifiers/identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/identifiers/other.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/identifiers/urns.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/logger.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/merge/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/merge/mergedeep.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/online_estimate.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/predict.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/print.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/process.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/py.typed +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/CoMet-v1.1.xsd +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/ComicInfo-v2.1-Draft.xsd +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/MetronInfo-v1.1.xsd +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/comic-book-info-v1.0.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v1.0/comicbox-v1.0.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v1.0/page.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v1.0/reprint.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v1.0/series.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v1.0/volume.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/comicbox-v2.0.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/comicbox.example.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/credit.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/date.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/identified-object.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/identified-objects.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/identifiers.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/named-identified-object.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/page.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/reprint.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/series.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v2.0/volume.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/alternative-name.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/comicbox-v3.0.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/comicbox.example.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/credit.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/date.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/identified-object.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/identified-objects.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/identifiers.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/named-identified-object.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/page.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/reprint.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/role.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/series.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/schemas/v3.0/volume.schema.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/validate/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/validate/base.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/validate/json_validator.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/validate/spec.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/validate/xml_validator.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/validate/yaml_validator.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/version.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/comicbox/write.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/compose.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/docs/README.md +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/docs/requirements.txt +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/docs/style.css +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/docs/style.mkdocs.css +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/eslint.config.js +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/generate-schema/gen-schema.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/generate-schema/mjs.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/mkdocs.yml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/.gitignore +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/README.md +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/_harness_helpers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/bootstrap.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/compare.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/debug_search.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/fixtures.example.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/label_metron.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/run.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/sample.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/summarize.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_bootstrap.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_compare.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_harness.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_harness_checkpoint.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_harness_diagnostics.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_harness_filter.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_harness_resume.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_label_metron.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_sample.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/calibration/test_summarize.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/cli/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/cli/test_cli_exit_code.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/cli/test_cli_export.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/cli/test_cli_extract.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/cli/test_cli_online.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/cli/test_cli_print.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/cli/test_cli_rename.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/cli/test_cli_write.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/conftest.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/const.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001 (1950) The Beginning - multi.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001 (1950) The Beginning - nothing.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001-cbi.cbr +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001-cix-cbi.cbr +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001-cix.cbt +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001-cix.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001-comet.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001-metron.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001.cb7 +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science #001.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/CaptainScience#1_01.jpg +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/CaptainScience#1_02.jpg +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/CaptainScience#1_03.jpg +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/CaptainScience#1_04.jpg +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/CaptainScience#1_05.jpg +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/CoMet.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/comic-book-info.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/comicinfo-original.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/comicinfo.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/metroninfo-full.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/Captain Science 001/metroninfo.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/comicbox-cli.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/comicbox.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/create-archive.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/create_cs_archives.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/create_macos_resource_fork.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/empty.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/examples/comic-book-info-example.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/examples/metroninfo-v1.1-valid.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/comet.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/comic-book-info.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/comicbox-cli.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/comicbox.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/comicbox.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/comicinfo.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/create-export-archive.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/metroninfo.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/mupdf.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export/pdf-metadata.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/export.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/list_recurse_output.txt +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/macos_resource_fork.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comet-cover-path-list.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comet-write.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comet.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comic-book-info-example.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comic-book-info.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicbox-cli.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicbox-filename.txt +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicbox-write.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicbox-write.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicbox.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicbox.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicbox.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicinfo-bad-multiple.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicinfo-cover-path-list.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicinfo-ids-from-tags.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicinfo-metron-origin.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicinfo-notes-date.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicinfo-validation.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicinfo-write.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/comicinfo.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/create-archives.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/metroninfo-no-primary-id.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/metroninfo-v1.1-valid.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/metroninfo-write.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/metroninfo.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/mupdf-nested.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/mupdf.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/pdf-cix.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/pdf-nested.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/metadata/pdf.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/multi/comet.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/multi/comic-book-info.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/multi/comicbox-filename.txt +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/multi/comicbox.json +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/multi/comicbox.yaml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/multi/comicinfo.xml +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/multi/create-multi-archive.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/patchme.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/pdf/0.pdf +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/pdf/1.pdf +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/pdf/2.pdf +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/pdf/3.pdf +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/test_cix.pdf +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/test_pdf.pdf +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/test_pdf_legacy_keywords.pdf +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/xml.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/files/yaml.cbz +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_cbi.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_cix.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_comet.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_filename.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_json.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_json_schema_drift.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_metron.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_multi.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_pdf_json.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_pdf_xml.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_validation.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/schemas/test_yaml.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/.gitignore +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/README.md +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/jobs_accuracy.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/prompt_ux.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/run.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/test_jobs_accuracy.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/test_prompt_ux.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/stress/test_run.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_codex_api.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_compute.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_convert_pdf.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_extract.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_import_export.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_mtime.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_pages.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_stories_title.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_validate_testfiles.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/test_writing.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_archive_errors.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_archive_read.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_archiveinfo_mtime.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_auto_engage.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_box_cache_invalidation.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_cbi_rating_normalize.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_cli_choices_enum_drift.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_cli_dest_folds.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_cli_dest_template_drift.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_comicinfo_alternates.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_comicinfo_series_groups.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_comicvine.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_comicvine_client.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_computed_pipeline.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_computed_reprints.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_config_container_inputs.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_config_defaults_drift.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_config_env_tree.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_config_file_option.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_config_layering.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_config_no_path_guard.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_config_online_leaf.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_config_online_sources.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_cover_hash.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_credit_primary.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_defer_prompts.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_destination_occupied_error.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_error_store_visibility.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_exceptions.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_lazy_startup.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_library_logging.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_list_field_dedupe.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_list_field_sort_key_types.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_manga_and_age_rating.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_manga_volume.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_merge_mode_config.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_merge_order.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_merge_precedence.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_mergedeep.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_metron_community_rating.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_metron_id_attribute_fallback.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_metron_prefetch.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_metron_session_lifecycle.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_metron_source.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_metron_transform.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_abort_propagation.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_degradation.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_estimate.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_events.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_lookup.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_matcher.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_matcher_resolution.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_matcher_scoring.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_prompt.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_prompt_loops.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_result_contract.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_retry.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_selector.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_session.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_online_session_state.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_outcome_stats.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_paced_session.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_parse_decimal.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_parse_issue.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_pdf_pages_help.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_process.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_prompt_dedup.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_quota_reserved_skip.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_rar_time_overflow.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_rate_gate.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_rate_gate_concurrency.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_rate_limits.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_read_events.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_refresh_cache.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_rename_and_convert_safety.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_reprints_and_alternative_names.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_rich_transforms.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_role_vocabulary.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_run_parallel.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_run_recurse.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_series_cache.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_series_filter.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_series_single_flight.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_session_series_batching.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_single_valued_tags.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_sniff.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_sources.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_to_metron_age_rating.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_transform_spec_dest_collisions.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_transform_spec_missing_keys.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_transform_spec_scope_phases.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_urls_and_identifiers.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_vacuum.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_validate.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_write_api.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_write_destination_prediction.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_write_destination_safety.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_write_order_and_claims.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_xml_cdata_fields.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/unit/test_yes_no_field.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/__init__.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/compare.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/diff.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/metadata.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/metron_transport.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/online_matcher.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/online_prompt.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/parser.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/util/tmp.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/tests/validate_cli.py +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/validate-schema.sh +0 -0
- {comicbox-5.2.0 → comicbox-5.2.1}/vulture_ignorelist.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: comicbox
|
|
3
|
-
Version: 5.2.
|
|
3
|
+
Version: 5.2.1
|
|
4
4
|
Summary: Comic book archive multi format metadata read/write/transform tool and image extractor.
|
|
5
5
|
Keywords: cb7,cbr,cbt,cbz,comet,comic,comicbookinfo,comicinfo,metroninfo,pdf
|
|
6
6
|
Author: AJ Slater
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"eslint-config-prettier": "^10.1.8",
|
|
15
15
|
"eslint-plugin-array-func": "^5.1.1",
|
|
16
16
|
"eslint-plugin-compat": "^7.0.2",
|
|
17
|
-
"eslint-plugin-de-morgan": "^2.
|
|
17
|
+
"eslint-plugin-de-morgan": "^2.2.0",
|
|
18
18
|
"eslint-plugin-depend": "^1.5.0",
|
|
19
19
|
"eslint-plugin-html": "^8.2.0",
|
|
20
20
|
"eslint-plugin-import-x": "^4.17.1",
|
|
@@ -174,19 +174,19 @@
|
|
|
174
174
|
|
|
175
175
|
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
|
176
176
|
|
|
177
|
-
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.70.
|
|
177
|
+
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.70.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.70.1", "@typescript-eslint/types": "^8.70.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-62xOgboPfwc3/IgPSX/W6oQR3ZbF04194FPGUGH8HL8iLFHbt/456/8Ph1wLNUgVF+s94FlHoipBsz+v7+LMnA=="],
|
|
178
178
|
|
|
179
|
-
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.70.
|
|
179
|
+
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.70.1", "", { "dependencies": { "@typescript-eslint/types": "8.70.1", "@typescript-eslint/visitor-keys": "8.70.1" } }, "sha512-Pa0EeSeAusQc1WbjQMac+YfenewYTBu0KjgYvkUKwhXaHUKbFog23Dm/rp0DX/6tyYOQ3Xl1a+3EcFNZynGHCw=="],
|
|
180
180
|
|
|
181
|
-
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.70.
|
|
181
|
+
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.70.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-jumze1fPI+sDOaM2TWGQdn39PDxTr7TZGeuyLkAbNyx2vtMT3uRnVKChN0hfht5V2TugphJzF6bYXvBcE09qqg=="],
|
|
182
182
|
|
|
183
|
-
"@typescript-eslint/types": ["@typescript-eslint/types@8.70.
|
|
183
|
+
"@typescript-eslint/types": ["@typescript-eslint/types@8.70.1", "", {}, "sha512-Dm1ypdhhrGCTyyehxElhgJ6kgk8MVCv5qXdoOVqPr1uqk42jX8KjrZqhROvdShczA8qrDoYiOWn1ykWlx2k81Q=="],
|
|
184
184
|
|
|
185
|
-
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.70.
|
|
185
|
+
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.70.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.70.1", "@typescript-eslint/tsconfig-utils": "8.70.1", "@typescript-eslint/types": "8.70.1", "@typescript-eslint/visitor-keys": "8.70.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-TU8PwyGN0PQJUcE96mw8eCQ44SmxGdQlJmlWakHaHQ15eIuuvye5yNtmh/i6oS88jzXVQB71xdNkbkB/fMwL0g=="],
|
|
186
186
|
|
|
187
|
-
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.70.
|
|
187
|
+
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.70.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.70.1", "@typescript-eslint/types": "8.70.1", "@typescript-eslint/typescript-estree": "8.70.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Esgul8MsnKnRLdYU2Eb2cRV9bS5HJYtKj1ByJnOzzG2M58DGdSUQ1jUuILxipqcpB2h9WLrbD5GijIWUjX/Tqw=="],
|
|
188
188
|
|
|
189
|
-
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.70.
|
|
189
|
+
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.70.1", "", { "dependencies": { "@typescript-eslint/types": "8.70.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-Vwj9lUIW5Xq3wQ9w6gv3R86g1hMK8f2zNOdGTAgeXUMMXFK78G9ruCjjqutHMNJc0+CH7LYRnHeUB9IT8wFmcw=="],
|
|
190
190
|
|
|
191
191
|
"@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.12.2", "", { "os": "android", "cpu": "arm" }, "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w=="],
|
|
192
192
|
|
|
@@ -370,7 +370,7 @@
|
|
|
370
370
|
|
|
371
371
|
"eslint-plugin-compat": ["eslint-plugin-compat@7.0.2", "", { "dependencies": { "@mdn/browser-compat-data": "^6.1.1", "ast-metadata-inferer": "^0.8.1", "browserslist": "^4.25.2", "find-up": "^5.0.0", "globals": "^15.7.0", "lodash.memoize": "^4.1.2", "semver": "^7.6.2" }, "peerDependencies": { "eslint": "^9.0.0 || ^10.0.0" } }, "sha512-gN8hF+4NzMsHUbr4m/TYZK0FtW3DcV4g8rXpTsY2EV5xiRD8jsilUlB9lNSkGGX0veDCxMhKSWbSd+faJByQDA=="],
|
|
372
372
|
|
|
373
|
-
"eslint-plugin-de-morgan": ["eslint-plugin-de-morgan@2.
|
|
373
|
+
"eslint-plugin-de-morgan": ["eslint-plugin-de-morgan@2.2.0", "", { "peerDependencies": { "eslint": "^8.45.0 || ^9.0.0 || ^10.0.0" } }, "sha512-bHg8e68rQTJ01av4sxhgNWQeoSzPWDjb6MqeKgmgRGaCzq83Ou6/g5S1ZssvFdHHS4BsblkJkagoioUmSEHPMg=="],
|
|
374
374
|
|
|
375
375
|
"eslint-plugin-depend": ["eslint-plugin-depend@1.5.0", "", { "dependencies": { "empathic": "^2.0.0", "module-replacements": "^2.10.1", "semver": "^7.6.3" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "sha512-i3UeLYmclf1Icp35+6W7CR4Bp2PIpDgBuf/mpmXK5UeLkZlvYJ21VuQKKHHAIBKRTPivPGX/gZl5JGno1o9Y0A=="],
|
|
376
376
|
|
|
@@ -126,6 +126,76 @@ def reset_shared_sessions() -> None:
|
|
|
126
126
|
_session_cache.clear()
|
|
127
127
|
|
|
128
128
|
|
|
129
|
+
def close_shared_sessions() -> None:
|
|
130
|
+
"""
|
|
131
|
+
Release the connections and sqlite handles every shared client holds.
|
|
132
|
+
|
|
133
|
+
Optional, and not a cancel: a closed client stays usable, at the cost
|
|
134
|
+
of reopening what it needs on its next request. The response cache
|
|
135
|
+
reopens lazily and the hourly budget lives in the rate-limit bucket
|
|
136
|
+
*file* rather than in the bucket object, so nothing a run has spent
|
|
137
|
+
is forgotten by closing. Mirrors `metron_api`'s `close_shared_sessions`.
|
|
138
|
+
"""
|
|
139
|
+
with _session_cache_lock:
|
|
140
|
+
entries = list(_session_cache.values())
|
|
141
|
+
for client, _signature in entries:
|
|
142
|
+
close_client(client)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def close_client(client: Any) -> None:
|
|
146
|
+
"""
|
|
147
|
+
Close one simyan client: the session first, then what that leaves open.
|
|
148
|
+
|
|
149
|
+
A `Comicvine` exposes no `close` of its own, so this goes through
|
|
150
|
+
`_session` -- private simyan surface, as in `_maintain_cache`.
|
|
151
|
+
Closing it releases the pooled sockets and the response cache's
|
|
152
|
+
sqlite connection and stops pyrate-limiter's leaker thread, but
|
|
153
|
+
leaves the rate-limit buckets holding theirs (`_close_limiter_buckets`).
|
|
154
|
+
"""
|
|
155
|
+
# Tests seed fakes that are neither `Comicvine`s nor real sessions.
|
|
156
|
+
session = getattr(client, "_session", client)
|
|
157
|
+
close = getattr(session, "close", None)
|
|
158
|
+
if close is not None:
|
|
159
|
+
close()
|
|
160
|
+
_close_limiter_buckets(session)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _close_limiter_buckets(session: Any) -> None:
|
|
164
|
+
"""
|
|
165
|
+
Close the rate-limit buckets the session's own close leaves open.
|
|
166
|
+
|
|
167
|
+
requests-ratelimiter's `close` does ask pyrate-limiter to close them,
|
|
168
|
+
but `BucketFactory.close()` drops its leaker before iterating
|
|
169
|
+
`get_buckets()`, which reads that leaker -- so the loop sees nothing
|
|
170
|
+
and every `SQLiteBucket` keeps its sqlite connection (pyrate-limiter
|
|
171
|
+
4.5.0). There is one bucket per endpoint pool, so closing only the
|
|
172
|
+
session trades a silent leak for a `ResourceWarning` apiece once the
|
|
173
|
+
buckets are finally collected: worse than not closing at all.
|
|
174
|
+
|
|
175
|
+
The factory would hand a closed bucket straight back out, and a
|
|
176
|
+
request holding one dies on its `None` connection, so the registry is
|
|
177
|
+
emptied first -- a racing lookup then builds a fresh bucket over the
|
|
178
|
+
same file instead of finding the one being closed. Each bucket's own
|
|
179
|
+
lock covers the rest: `close` waits behind an in-flight acquire.
|
|
180
|
+
"""
|
|
181
|
+
factory = getattr(getattr(session, "limiter", None), "bucket_factory", None)
|
|
182
|
+
registry = getattr(factory, "buckets", None)
|
|
183
|
+
if not isinstance(registry, dict):
|
|
184
|
+
# The limiter stack is upstream's to rearrange; a run that cannot
|
|
185
|
+
# find the buckets has still closed the session above.
|
|
186
|
+
logger.debug("online comicvine: no rate-limit buckets to close")
|
|
187
|
+
return
|
|
188
|
+
buckets = list(registry.values())
|
|
189
|
+
registry.clear()
|
|
190
|
+
for bucket in buckets:
|
|
191
|
+
try:
|
|
192
|
+
bucket.close()
|
|
193
|
+
# Broad on purpose: one bucket backend that will not close must
|
|
194
|
+
# not cost the others their close, nor fail the caller's teardown.
|
|
195
|
+
except Exception as exc:
|
|
196
|
+
logger.debug(f"online comicvine: rate-limit bucket close skipped: {exc}")
|
|
197
|
+
|
|
198
|
+
|
|
129
199
|
def _bucket_window(conn: sqlite3.Connection, table: str, now_ms: int) -> dict[str, Any]:
|
|
130
200
|
"""Summarize one bucket table as a `limit`/`remaining`/`reset_epoch` window."""
|
|
131
201
|
window_start = now_ms - _RATE_LIMIT_WINDOW_MS
|
|
@@ -422,18 +422,30 @@ class OnlineSession:
|
|
|
422
422
|
|
|
423
423
|
def close(self) -> None:
|
|
424
424
|
"""
|
|
425
|
-
Release the pooled
|
|
425
|
+
Release the pooled connections and cache handles both sources hold.
|
|
426
426
|
|
|
427
427
|
Optional, and not a cancel: the session stays usable afterwards,
|
|
428
|
-
at the cost of
|
|
429
|
-
|
|
430
|
-
another `OnlineSession` in the same process is mid-run
|
|
431
|
-
run
|
|
432
|
-
the sockets to be finalized -- is
|
|
428
|
+
at the cost of reopening what it needs on its next request. The
|
|
429
|
+
upstream clients are process-wide per credential set, so closing
|
|
430
|
+
while another `OnlineSession` in the same process is mid-run
|
|
431
|
+
costs that run the same reopen. Acceptable, and the alternative
|
|
432
|
+
-- leaving the sockets and sqlite handles to be finalized -- is
|
|
433
|
+
what emits `ResourceWarning`.
|
|
434
|
+
|
|
435
|
+
Comic Vine is the one sharp edge: its rate-limit buckets are
|
|
436
|
+
closed with the session, so a lookup already holding one can fail
|
|
437
|
+
outright rather than merely reconnect. Close between files, not
|
|
438
|
+
under one.
|
|
433
439
|
"""
|
|
434
|
-
from comicbox.formats.
|
|
440
|
+
from comicbox.formats.comicvine_api.online_source import (
|
|
441
|
+
close_shared_sessions as close_comicvine,
|
|
442
|
+
)
|
|
443
|
+
from comicbox.formats.metron_api.online_source import (
|
|
444
|
+
close_shared_sessions as close_metron,
|
|
445
|
+
)
|
|
435
446
|
|
|
436
|
-
|
|
447
|
+
close_metron()
|
|
448
|
+
close_comicvine()
|
|
437
449
|
|
|
438
450
|
def __enter__(self) -> Self:
|
|
439
451
|
"""Enter the context manager, returning this session."""
|
|
@@ -37,6 +37,13 @@ if TYPE_CHECKING:
|
|
|
37
37
|
#: log one line. Anything else is a bug and earns the stack.
|
|
38
38
|
_EXPECTED_FILE_ERRORS = (UnsupportedArchiveTypeError,)
|
|
39
39
|
|
|
40
|
+
#: The online sources that hold process-wide clients to release at the
|
|
41
|
+
#: end of a run. Named, not imported: see `_close_shared_online_sessions`.
|
|
42
|
+
_ONLINE_SOURCE_MODULES = (
|
|
43
|
+
"comicbox.formats.metron_api.online_source",
|
|
44
|
+
"comicbox.formats.comicvine_api.online_source",
|
|
45
|
+
)
|
|
46
|
+
|
|
40
47
|
|
|
41
48
|
def _leaders_first(clustered: list[Path]) -> list[Path]:
|
|
42
49
|
"""
|
|
@@ -55,15 +62,17 @@ def _leaders_first(clustered: list[Path]) -> list[Path]:
|
|
|
55
62
|
|
|
56
63
|
def _close_shared_online_sessions() -> None:
|
|
57
64
|
"""
|
|
58
|
-
Release the
|
|
65
|
+
Release the connections and handles a run opened, if it opened any.
|
|
59
66
|
|
|
60
67
|
Read out of ``sys.modules`` rather than imported: an offline run
|
|
61
68
|
never loads the online packages, and importing one here just to call
|
|
62
|
-
a no-op would put mokkari's import cost back on every
|
|
69
|
+
a no-op would put mokkari's and simyan's import cost back on every
|
|
70
|
+
run.
|
|
63
71
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
for name in _ONLINE_SOURCE_MODULES:
|
|
73
|
+
module = sys.modules.get(name)
|
|
74
|
+
if module is not None:
|
|
75
|
+
module.close_shared_sessions()
|
|
67
76
|
|
|
68
77
|
|
|
69
78
|
class Runner:
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"eslint-config-prettier": "^10.1.8",
|
|
78
78
|
"eslint-plugin-array-func": "^5.1.1",
|
|
79
79
|
"eslint-plugin-compat": "^7.0.2",
|
|
80
|
-
"eslint-plugin-de-morgan": "^2.
|
|
80
|
+
"eslint-plugin-de-morgan": "^2.2.0",
|
|
81
81
|
"eslint-plugin-depend": "^1.5.0",
|
|
82
82
|
"eslint-plugin-html": "^8.2.0",
|
|
83
83
|
"eslint-plugin-import-x": "^4.17.1",
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lifecycle of the shared simyan client.
|
|
3
|
+
|
|
4
|
+
A `Comicvine` holds more than sockets: the response cache's sqlite
|
|
5
|
+
connection, one rate-limit bucket connection per endpoint pool, and
|
|
6
|
+
pyrate-limiter's leaker thread. Closing the session releases the first
|
|
7
|
+
and stops the last but leaves every bucket open -- and, by dropping the
|
|
8
|
+
references that had kept them alive, turns a silent leak into a
|
|
9
|
+
`ResourceWarning` apiece. These tests pin the whole close.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import gc
|
|
15
|
+
import warnings
|
|
16
|
+
from argparse import Namespace
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
import pytest
|
|
20
|
+
from typing_extensions import override
|
|
21
|
+
|
|
22
|
+
from comicbox.config.online.settings import OnlineCacheSettings, OnlineSettings
|
|
23
|
+
from comicbox.formats.base.online.rate_limits import COMICVINE_DEFAULT_PER_HOUR
|
|
24
|
+
from comicbox.formats.comicvine_api import online_source as comicvine
|
|
25
|
+
from tests.util.online_client import build_comicvine_client, spend
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class _FakeBucket:
|
|
32
|
+
"""A bucket double that records the registry's state when it was closed."""
|
|
33
|
+
|
|
34
|
+
def __init__(self) -> None:
|
|
35
|
+
self.closed = 0
|
|
36
|
+
self.registry: dict[str, Any] = {}
|
|
37
|
+
self.registry_was_empty: bool | None = None
|
|
38
|
+
|
|
39
|
+
def close(self) -> None:
|
|
40
|
+
self.closed += 1
|
|
41
|
+
self.registry_was_empty = not self.registry
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class _StuckBucket(_FakeBucket):
|
|
45
|
+
"""A bucket double whose close fails, as a rearranged upstream might."""
|
|
46
|
+
|
|
47
|
+
@override
|
|
48
|
+
def close(self) -> None:
|
|
49
|
+
super().close()
|
|
50
|
+
msg = "no connection"
|
|
51
|
+
raise RuntimeError(msg)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class _FakeSession:
|
|
55
|
+
"""A `CachedLimiterSession` double, down to `limiter.bucket_factory`."""
|
|
56
|
+
|
|
57
|
+
def __init__(self, *buckets: _FakeBucket) -> None:
|
|
58
|
+
self.closed = 0
|
|
59
|
+
registry = {f"pool{i}": bucket for i, bucket in enumerate(buckets)}
|
|
60
|
+
for bucket in buckets:
|
|
61
|
+
bucket.registry = registry
|
|
62
|
+
self.limiter = Namespace(bucket_factory=Namespace(buckets=registry))
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def registry(self) -> dict[str, Any]:
|
|
66
|
+
"""The bucket registry the factory serves lookups from."""
|
|
67
|
+
return self.limiter.bucket_factory.buckets
|
|
68
|
+
|
|
69
|
+
def close(self) -> None:
|
|
70
|
+
self.closed += 1
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class _FakeClient:
|
|
74
|
+
"""A `Comicvine` double: the session hangs off the same private name."""
|
|
75
|
+
|
|
76
|
+
def __init__(self, session: _FakeSession) -> None:
|
|
77
|
+
self._session = session
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _Unclosable:
|
|
81
|
+
"""A client double with neither a session nor a close, as fakes have."""
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@pytest.fixture(autouse=True)
|
|
85
|
+
def _clear_cache(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
86
|
+
"""Every test gets its own empty shared-client cache."""
|
|
87
|
+
monkeypatch.setattr(comicvine, "_session_cache", {})
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _seed(client: Any, key: tuple[str, str] = ("key", "")) -> None:
|
|
91
|
+
comicvine._session_cache[key] = (client, ())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _buckets(client: Any) -> list[Any]:
|
|
95
|
+
return list(client._session.limiter.bucket_factory.buckets.values())
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_close_closes_every_shared_client() -> None:
|
|
99
|
+
"""One run, one close per credential set."""
|
|
100
|
+
first = _FakeSession()
|
|
101
|
+
second = _FakeSession()
|
|
102
|
+
_seed(_FakeClient(first), ("key", ""))
|
|
103
|
+
_seed(_FakeClient(second), ("other", "https://cv.example/api"))
|
|
104
|
+
|
|
105
|
+
comicvine.close_shared_sessions()
|
|
106
|
+
|
|
107
|
+
assert first.closed == 1
|
|
108
|
+
assert second.closed == 1
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_close_keeps_the_cache_entries() -> None:
|
|
112
|
+
"""
|
|
113
|
+
A closed simyan client is still the right one to reuse.
|
|
114
|
+
|
|
115
|
+
It reopens its response cache on demand and rebuilds a rate-limit
|
|
116
|
+
bucket over the same file, so the credential set keeps its client.
|
|
117
|
+
"""
|
|
118
|
+
_seed(_FakeClient(_FakeSession()))
|
|
119
|
+
|
|
120
|
+
comicvine.close_shared_sessions()
|
|
121
|
+
|
|
122
|
+
assert comicvine._session_cache
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_close_tolerates_a_client_without_a_session() -> None:
|
|
126
|
+
"""Tests seed doubles; a missing `_session` must not break teardown."""
|
|
127
|
+
_seed(_Unclosable())
|
|
128
|
+
|
|
129
|
+
comicvine.close_shared_sessions() # must not raise
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_close_empties_the_registry_before_closing_the_buckets() -> None:
|
|
133
|
+
"""
|
|
134
|
+
A closed bucket must never be handed back out.
|
|
135
|
+
|
|
136
|
+
The factory serves its registry without checking, and a request that
|
|
137
|
+
gets a closed bucket dies on its `None` connection, so the registry
|
|
138
|
+
is emptied first: a racing lookup then builds a fresh bucket instead
|
|
139
|
+
of finding this one mid-close.
|
|
140
|
+
"""
|
|
141
|
+
bucket = _FakeBucket()
|
|
142
|
+
session = _FakeSession(bucket)
|
|
143
|
+
_seed(_FakeClient(session))
|
|
144
|
+
|
|
145
|
+
comicvine.close_shared_sessions()
|
|
146
|
+
|
|
147
|
+
assert bucket.closed == 1
|
|
148
|
+
assert bucket.registry_was_empty is True
|
|
149
|
+
assert not session.registry
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def test_close_survives_a_bucket_that_will_not_close() -> None:
|
|
153
|
+
"""One bad bucket must not stop the rest, nor fail the caller."""
|
|
154
|
+
stuck = _StuckBucket()
|
|
155
|
+
fine = _FakeBucket()
|
|
156
|
+
session = _FakeSession(stuck, fine)
|
|
157
|
+
_seed(_FakeClient(session))
|
|
158
|
+
|
|
159
|
+
comicvine.close_shared_sessions()
|
|
160
|
+
|
|
161
|
+
assert stuck.closed == 1
|
|
162
|
+
assert fine.closed == 1
|
|
163
|
+
assert session.closed == 1
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def test_a_closed_client_releases_every_sqlite_handle(tmp_path: Path) -> None:
|
|
167
|
+
"""
|
|
168
|
+
The response cache's connection, and one per rate-limit pool.
|
|
169
|
+
|
|
170
|
+
`session.close()` alone gets the first; the buckets are
|
|
171
|
+
pyrate-limiter's and have to be closed by name (see
|
|
172
|
+
`_close_limiter_buckets`).
|
|
173
|
+
"""
|
|
174
|
+
client = build_comicvine_client(tmp_path)
|
|
175
|
+
spend(client, "issues", 2)
|
|
176
|
+
spend(client, "search", 1)
|
|
177
|
+
buckets = _buckets(client)
|
|
178
|
+
assert len(buckets) == 2
|
|
179
|
+
assert all(bucket.conn is not None for bucket in buckets)
|
|
180
|
+
|
|
181
|
+
comicvine.close_client(client)
|
|
182
|
+
|
|
183
|
+
assert all(bucket.conn is None for bucket in buckets)
|
|
184
|
+
assert client._session.cache.responses._connection is None
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def test_a_closed_client_emits_no_resource_warning(tmp_path: Path) -> None:
|
|
188
|
+
"""The symptom that asked for this: unclosed sqlite handles."""
|
|
189
|
+
with warnings.catch_warnings(record=True) as caught:
|
|
190
|
+
warnings.simplefilter("always")
|
|
191
|
+
client = build_comicvine_client(tmp_path)
|
|
192
|
+
spend(client, "issues", 1)
|
|
193
|
+
spend(client, "search", 1)
|
|
194
|
+
|
|
195
|
+
comicvine.close_client(client)
|
|
196
|
+
|
|
197
|
+
del client
|
|
198
|
+
gc.collect()
|
|
199
|
+
|
|
200
|
+
leaked = [
|
|
201
|
+
str(warning.message)
|
|
202
|
+
for warning in caught
|
|
203
|
+
if issubclass(warning.category, ResourceWarning)
|
|
204
|
+
]
|
|
205
|
+
assert not leaked
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_a_closed_client_is_still_usable(tmp_path: Path) -> None:
|
|
209
|
+
"""
|
|
210
|
+
Not a cancel: the client works after a close, and remembers.
|
|
211
|
+
|
|
212
|
+
The hourly budget lives in the bucket file, so the rebuilt bucket
|
|
213
|
+
picks up where the closed one left off.
|
|
214
|
+
"""
|
|
215
|
+
client = build_comicvine_client(tmp_path)
|
|
216
|
+
spend(client, "issues", 3)
|
|
217
|
+
|
|
218
|
+
comicvine.close_client(client)
|
|
219
|
+
spend(client, "issues", 1)
|
|
220
|
+
|
|
221
|
+
status = comicvine.shared_client_rate_limit_status(
|
|
222
|
+
OnlineSettings(cache=OnlineCacheSettings(dir=tmp_path))
|
|
223
|
+
)
|
|
224
|
+
assert status["issues"]["remaining"] == COMICVINE_DEFAULT_PER_HOUR - 4
|
|
225
|
+
comicvine.close_client(client)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_close_is_safe_to_repeat(tmp_path: Path) -> None:
|
|
229
|
+
"""A second close finds nothing to do rather than raising."""
|
|
230
|
+
client = build_comicvine_client(tmp_path)
|
|
231
|
+
spend(client, "issues", 1)
|
|
232
|
+
|
|
233
|
+
comicvine.close_client(client)
|
|
234
|
+
comicvine.close_client(client) # must not raise
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def test_the_runner_closes_shared_clients(
|
|
238
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
239
|
+
) -> None:
|
|
240
|
+
"""A finished run releases what its Comic Vine lookups opened."""
|
|
241
|
+
from comicbox.run import Runner
|
|
242
|
+
|
|
243
|
+
calls: list[int] = []
|
|
244
|
+
monkeypatch.setattr(comicvine, "close_shared_sessions", lambda: calls.append(1))
|
|
245
|
+
comic = tmp_path / "file_0.cbz"
|
|
246
|
+
comic.write_bytes(b"") # zero-byte cbz; Comicbox will fail to open it
|
|
247
|
+
runner = Runner(
|
|
248
|
+
Namespace(comicbox=Namespace(paths=[str(comic)], print=Namespace(phases="p")))
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
runner.run()
|
|
252
|
+
|
|
253
|
+
assert calls == [1]
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def test_online_session_close_releases_both_sources(
|
|
257
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
258
|
+
) -> None:
|
|
259
|
+
"""`OnlineSession.close()` is not a Metron-only release."""
|
|
260
|
+
from comicbox.formats.metron_api import online_source as metron
|
|
261
|
+
from comicbox.online_session import OnlineCredentials, OnlineSession
|
|
262
|
+
|
|
263
|
+
calls: list[str] = []
|
|
264
|
+
monkeypatch.setattr(
|
|
265
|
+
comicvine, "close_shared_sessions", lambda: calls.append("comicvine")
|
|
266
|
+
)
|
|
267
|
+
monkeypatch.setattr(metron, "close_shared_sessions", lambda: calls.append("metron"))
|
|
268
|
+
credentials = OnlineCredentials(comicvine_key="k")
|
|
269
|
+
|
|
270
|
+
with OnlineSession(sources={"comicvine"}, credentials=credentials):
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
assert calls == ["metron", "comicvine"]
|
|
@@ -14,24 +14,17 @@ def close_comicvine_client(client: Any) -> None:
|
|
|
14
14
|
"""
|
|
15
15
|
Release every sqlite connection a simyan `Comicvine` opened.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
own `buckets` dict, which pyrate-limiter's `BucketFactory.close()`
|
|
20
|
-
does not iterate, so it closes the leaker but not the bucket
|
|
21
|
-
connections. Closing them here keeps tests from leaking sqlite
|
|
22
|
-
handles and emitting ResourceWarnings.
|
|
17
|
+
Delegates to the production close rather than keeping a second copy
|
|
18
|
+
of the bucket dance it has to do.
|
|
23
19
|
"""
|
|
24
|
-
|
|
25
|
-
factory = session.limiter.bucket_factory
|
|
26
|
-
session.close()
|
|
27
|
-
for bucket in list(getattr(factory, "buckets", {}).values()):
|
|
28
|
-
bucket.close()
|
|
20
|
+
from comicbox.formats.comicvine_api.online_source import close_client
|
|
29
21
|
|
|
22
|
+
close_client(client)
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
def
|
|
24
|
+
|
|
25
|
+
def build_comicvine_client(cache_dir: Path, api_key: str = "k") -> Any:
|
|
33
26
|
"""
|
|
34
|
-
|
|
27
|
+
Build a real `Comicvine` pointed at `cache_dir`.
|
|
35
28
|
|
|
36
29
|
Its file names match what `ComicVineOnlineSource` would choose, so a
|
|
37
30
|
status read against the same directory finds them.
|
|
@@ -39,11 +32,17 @@ def comicvine_client(cache_dir: Path, api_key: str = "k") -> Generator[Any]:
|
|
|
39
32
|
from simyan.comicvine import Comicvine
|
|
40
33
|
|
|
41
34
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
42
|
-
|
|
35
|
+
return Comicvine(
|
|
43
36
|
api_key=api_key,
|
|
44
37
|
cache_path=cache_dir / "comicvine_cache.sqlite",
|
|
45
38
|
ratelimit_path=cache_dir / "comicvine_rate_limit.sqlite",
|
|
46
39
|
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@contextmanager
|
|
43
|
+
def comicvine_client(cache_dir: Path, api_key: str = "k") -> Generator[Any]:
|
|
44
|
+
"""Yield a real `Comicvine` pointed at `cache_dir`, fully closed on exit."""
|
|
45
|
+
client = build_comicvine_client(cache_dir, api_key)
|
|
47
46
|
try:
|
|
48
47
|
yield client
|
|
49
48
|
finally:
|
|
@@ -876,7 +876,7 @@ wheels = [
|
|
|
876
876
|
|
|
877
877
|
[[package]]
|
|
878
878
|
name = "comicbox"
|
|
879
|
-
version = "5.2.
|
|
879
|
+
version = "5.2.1"
|
|
880
880
|
source = { editable = "." }
|
|
881
881
|
dependencies = [
|
|
882
882
|
{ name = "bidict", version = "0.23.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|