apify 2.7.0__tar.gz → 2.7.0b2__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.
Potentially problematic release.
This version of apify might be problematic. Click here for more details.
- {apify-2.7.0 → apify-2.7.0b2}/PKG-INFO +1 -1
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/10_logging.mdx +30 -0
- apify-2.7.0b2/docs/03_concepts/code/10_redirect_log.py +15 -0
- apify-2.7.0b2/docs/03_concepts/code/10_redirect_log_existing_run.py +24 -0
- {apify-2.7.0 → apify-2.7.0b2}/pyproject.toml +4 -4
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_actor.py +40 -5
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_charging.py +13 -6
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_configuration.py +1 -1
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_crypto.py +0 -6
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_models.py +6 -6
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_platform_event_manager.py +15 -16
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_proxy_configuration.py +2 -5
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_utils.py +13 -1
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/_apify_storage_client.py +1 -1
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/log.py +0 -2
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/storages/_request_list.py +1 -1
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/README.md +23 -0
- apify-2.7.0b2/tests/integration/test_request_queue.py +1192 -0
- {apify-2.7.0 → apify-2.7.0b2}/uv.lock +50 -40
- apify-2.7.0b2/website/docusaurus.config.js +273 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/package-lock.json +355 -354
- {apify-2.7.0 → apify-2.7.0b2}/website/package.json +6 -6
- apify-2.7.0b2/website/static/img/guides/redirected_logs_example.webp +0 -0
- apify-2.7.0/tests/integration/test_request_queue.py +0 -113
- apify-2.7.0/website/docusaurus.config.js +0 -145
- {apify-2.7.0 → apify-2.7.0b2}/.editorconfig +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.github/CODEOWNERS +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.github/workflows/build_and_deploy_docs.yaml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.github/workflows/check_pr_title.yaml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.github/workflows/pre_release.yaml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.github/workflows/release.yaml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.github/workflows/run_code_checks.yaml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.github/workflows/update_new_issue.yaml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.gitignore +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.markdownlint.yaml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/.pre-commit-config.yaml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/CHANGELOG.md +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/CONTRIBUTING.md +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/LICENSE +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/Makefile +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/README.md +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/01_overview/01_introduction.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/01_overview/02_running_actors_locally.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/01_overview/03_actor_structure.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/01_overview/code/01_introduction.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/01_overview/code/actor_structure/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/01_overview/code/actor_structure/__main__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/01_overview/code/actor_structure/main.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/01_overview/code/actor_structure/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/01_beautifulsoup_httpx.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/02_crawlee.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/03_playwright.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/04_selenium.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/05_scrapy.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/01_beautifulsoup_httpx.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/02_crawlee_beautifulsoup.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/02_crawlee_playwright.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/03_playwright.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/04_selenium.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/__main__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/items.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/main.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/settings.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/spiders/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/spiders/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/02_guides/code/scrapy_project/src/spiders/title.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/01_actor_lifecycle.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/02_actor_input.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/03_storages.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/04_actor_events.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/05_proxy_management.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/06_interacting_with_other_actors.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/07_webhooks.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/08_access_apify_api.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/09_running_webserver.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/11_configuration.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/12_pay_per_event.mdx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/01_context_manager.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/01_init_exit.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/01_reboot.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/01_status_message.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/02_input.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/03_dataset_exports.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/03_dataset_read_write.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/03_deleting_storages.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/03_kvs_iterating.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/03_kvs_public_url.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/03_kvs_read_write.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/03_opening_storages.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/03_rq.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/04_actor_events.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/05_apify_proxy.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/05_apify_proxy_config.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/05_custom_proxy.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/05_custom_proxy_function.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/05_proxy_actor_input.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/05_proxy_httpx.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/05_proxy_rotation.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/06_interacting_call.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/06_interacting_call_task.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/06_interacting_metamorph.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/06_interacting_start.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/07_webhook.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/07_webhook_preventing.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/08_actor_client.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/08_actor_new_client.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/09_webserver.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/10_log_config.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/10_logger_usage.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/11_config.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/actor_charge.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/03_concepts/code/conditional_actor_charge.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/04_upgrading/upgrading_to_v2.md +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/docs/pyproject.toml +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/renovate.json +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/_consts.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/_dataset_client.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/_dataset_collection_client.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/_key_value_store_client.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/_key_value_store_collection_client.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/_request_queue_client.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/_request_queue_collection_client.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/apify_storage_client/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/_actor_runner.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/_async_thread.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/_logging_config.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/extensions/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/extensions/_httpcache.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/middlewares/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/middlewares/apify_proxy.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/middlewares/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/pipelines/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/pipelines/actor_dataset_push.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/pipelines/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/requests.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/scheduler.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/scrapy/utils.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/storages/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/src/apify/storages/py.typed +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/_utils.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/actor_source_base/Dockerfile +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/actor_source_base/requirements.txt +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/actor_source_base/src/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/actor_source_base/src/__main__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/actor_source_base/src/main.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/conftest.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_api_helpers.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_call_timeouts.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_charge.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_create_proxy_configuration.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_dataset.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_events.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_key_value_store.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_lifecycle.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_log.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_request_queue.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_actor_scrapy.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/integration/test_fixtures.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_create_proxy_configuration.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_dataset.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_env_helpers.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_helpers.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_key_value_store.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_lifecycle.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_log.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_non_default_instance.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_actor_request_queue.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_configuration.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/actor/test_request_list.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/conftest.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/extensions/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/extensions/test_httpcache.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/middlewares/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/middlewares/test_apify_proxy.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/pipelines/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/pipelines/test_actor_dataset_push.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/requests/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/requests/test_to_apify_request.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/requests/test_to_scrapy_request.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/utils/__init__.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/utils/test_apply_apify_settings.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/scrapy/utils/test_get_basic_auth_header.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/test_crypto.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/test_platform_event_manager.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/tests/unit/test_proxy_configuration.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/.eslintrc.json +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/babel.config.js +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/build_api_reference.sh +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/generate_module_shortcuts.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/sidebars.js +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/components/ApiLink.jsx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/components/Gradients.jsx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/components/Highlights.jsx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/components/Highlights.module.css +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/components/RunnableCodeBlock.jsx +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/components/RunnableCodeBlock.module.css +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/css/custom.css +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/pages/home_page_example.py +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/pages/index.js +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/src/pages/index.module.css +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/static/.nojekyll +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/static/img/docs-og.png +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/tools/docs-prettier.config.js +0 -0
- {apify-2.7.0 → apify-2.7.0b2}/website/tools/utils/externalLink.js +0 -0
|
@@ -7,6 +7,8 @@ import CodeBlock from '@theme/CodeBlock';
|
|
|
7
7
|
|
|
8
8
|
import LogConfigExample from '!!raw-loader!./code/10_log_config.py';
|
|
9
9
|
import LoggerUsageExample from '!!raw-loader!./code/10_logger_usage.py';
|
|
10
|
+
import RedirectLog from '!!raw-loader!./code/10_redirect_log.py';
|
|
11
|
+
import RedirectLogExistingRun from '!!raw-loader!./code/10_redirect_log_existing_run.py';
|
|
10
12
|
|
|
11
13
|
The Apify SDK is logging useful information through the [`logging`](https://docs.python.org/3/library/logging.html) module from Python's standard library, into the logger with the name `apify`.
|
|
12
14
|
|
|
@@ -82,3 +84,31 @@ Result:
|
|
|
82
84
|
</pre>
|
|
83
85
|
|
|
84
86
|
<!-- markdownlint-enable no-inline-html -->
|
|
87
|
+
|
|
88
|
+
## Redirect logs from other Actor runs
|
|
89
|
+
|
|
90
|
+
In some situations, one Actor is going to start one or more other Actors and wait for them to finish and produce some results. In such cases, you might want to redirect the logs and status messages of the started Actors runs back to the parent Actor run, so that you can see the progress of the started Actors' runs in the parent Actor's logs. This guide will show possibilities on how to do it.
|
|
91
|
+
|
|
92
|
+
### Redirecting logs from Actor.call
|
|
93
|
+
|
|
94
|
+
Typical use case for log redirection is to call another Actor using the [`Actor.call`](../../reference/class/Actor#call) method. This method has an optional `logger` argument, which is by default set to the `default` literal. This means that the logs of the called Actor will be automatically redirected to the parent Actor's logs with default formatting and filtering. If you set the `logger` argument to `None`, then no log redirection happens. The third option is to pass your own `Logger` instance with the possibility to define your own formatter, filter, and handler. Below you can see those three possible ways of log redirection when starting another Actor run through [`Actor.call`](../../reference/class/Actor#call).
|
|
95
|
+
|
|
96
|
+
<CodeBlock className="language-python">
|
|
97
|
+
{RedirectLog}
|
|
98
|
+
</CodeBlock>
|
|
99
|
+
|
|
100
|
+
Each default redirect logger log entry will have a specific format. After the timestamp, it will contain cyan colored text that will contain the redirect information - the other actor's name and the run ID. The rest of the log message will be printed in the same manner as the parent Actor's logger is configured.
|
|
101
|
+
|
|
102
|
+
The log redirection can be deep, meaning that if the other actor also starts another actor and is redirecting logs from it, then in the top-level Actor, you can see it as well. See the following example screenshot of the Apify log console when one actor recursively starts itself (there are 2 levels of recursion in the example).
|
|
103
|
+
|
|
104
|
+

|
|
105
|
+
|
|
106
|
+
### Redirecting logs from already running Actor run
|
|
107
|
+
|
|
108
|
+
In some cases, you might want to connect to an already running Actor run and redirect its logs to your current Actor run. This can be done using the [ApifyClient](../../reference/class/Actor#apify_client) and getting the streamed log from a specific Actor run. You can then use it as a context manager, and the log redirection will be active in the context, or you can control the log redirection manually by explicitly calling `start` and `stop` methods.
|
|
109
|
+
|
|
110
|
+
You can further decide whether you want to redirect just new logs of the ongoing Actor run, or if you also want to redirect historical logs from that Actor's run, so all logs it has produced since it was started. Both options are shown in the example code below.
|
|
111
|
+
|
|
112
|
+
<CodeBlock className="language-python">
|
|
113
|
+
{RedirectLogExistingRun}
|
|
114
|
+
</CodeBlock>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from apify import Actor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def main() -> None:
|
|
7
|
+
async with Actor:
|
|
8
|
+
# Default redirect logger
|
|
9
|
+
await Actor.call(actor_id='some_actor_id')
|
|
10
|
+
# No redirect logger
|
|
11
|
+
await Actor.call(actor_id='some_actor_id', logger=None)
|
|
12
|
+
# Custom redirect logger
|
|
13
|
+
await Actor.call(
|
|
14
|
+
actor_id='some_actor_id', logger=logging.getLogger('custom_logger')
|
|
15
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from apify import Actor
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def main() -> None:
|
|
7
|
+
async with Actor:
|
|
8
|
+
# Lifecycle of redirected logs is handled by the context manager.
|
|
9
|
+
async with await Actor.apify_client.run('some_actor_id').get_streamed_log(
|
|
10
|
+
# Redirect all logs from the start of that run, even the logs from past.
|
|
11
|
+
from_start=True
|
|
12
|
+
):
|
|
13
|
+
await asyncio.sleep(5)
|
|
14
|
+
# Logging will stop out of context
|
|
15
|
+
|
|
16
|
+
# Lifecycle of redirected logs can be handled manually.
|
|
17
|
+
streamed_log = await Actor.apify_client.run('some_id').get_streamed_log(
|
|
18
|
+
# Do not redirect historical logs from this actor run.
|
|
19
|
+
# Redirect only new logs from now on.
|
|
20
|
+
from_start=False
|
|
21
|
+
)
|
|
22
|
+
streamed_log.start()
|
|
23
|
+
await asyncio.sleep(5)
|
|
24
|
+
await streamed_log.stop()
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "apify"
|
|
7
|
-
version = "2.7.
|
|
7
|
+
version = "2.7.0b2"
|
|
8
8
|
description = "Apify SDK for Python"
|
|
9
9
|
authors = [{ name = "Apify Technologies s.r.o.", email = "support@apify.com" }]
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -64,11 +64,11 @@ scrapy = ["scrapy>=2.11.0"]
|
|
|
64
64
|
dev = [
|
|
65
65
|
"build~=1.2.0",
|
|
66
66
|
"dycw-pytest-only>=2.1.1",
|
|
67
|
-
"griffe~=1.
|
|
68
|
-
"mypy~=1.
|
|
67
|
+
"griffe~=1.9.0",
|
|
68
|
+
"mypy~=1.17.0",
|
|
69
69
|
"pre-commit~=4.2.0",
|
|
70
70
|
"pydoc-markdown~=4.8.0",
|
|
71
|
-
"pytest-asyncio~=1.
|
|
71
|
+
"pytest-asyncio~=1.1.0",
|
|
72
72
|
"pytest-cov~=6.2.0",
|
|
73
73
|
"pytest-timeout>=2.4.0",
|
|
74
74
|
"pytest-xdist~=3.8.0",
|
|
@@ -13,7 +13,7 @@ from pydantic import AliasChoices
|
|
|
13
13
|
|
|
14
14
|
from apify_client import ApifyClientAsync
|
|
15
15
|
from apify_shared.consts import ActorEnvVars, ActorExitCodes, ApifyEnvVars
|
|
16
|
-
from apify_shared.utils import
|
|
16
|
+
from apify_shared.utils import maybe_extract_enum_member_value
|
|
17
17
|
from crawlee import service_locator
|
|
18
18
|
from crawlee.events import (
|
|
19
19
|
Event,
|
|
@@ -54,9 +54,46 @@ MainReturnType = TypeVar('MainReturnType')
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
@docs_name('Actor')
|
|
57
|
-
@docs_group('
|
|
57
|
+
@docs_group('Actor')
|
|
58
58
|
class _ActorType:
|
|
59
|
-
"""The class
|
|
59
|
+
"""The core class for building Actors on the Apify platform.
|
|
60
|
+
|
|
61
|
+
Actors are serverless programs running in the cloud that can perform anything from simple actions
|
|
62
|
+
(such as filling out a web form or sending an email) to complex operations (such as crawling an
|
|
63
|
+
entire website or removing duplicates from a large dataset). They are packaged as Docker containers
|
|
64
|
+
which accept well-defined JSON input, perform an action, and optionally produce well-defined output.
|
|
65
|
+
|
|
66
|
+
### References
|
|
67
|
+
|
|
68
|
+
- Apify platform documentation: https://docs.apify.com/platform/actors
|
|
69
|
+
- Actor whitepaper: https://whitepaper.actor/
|
|
70
|
+
|
|
71
|
+
### Usage
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import asyncio
|
|
75
|
+
|
|
76
|
+
import httpx
|
|
77
|
+
from apify import Actor
|
|
78
|
+
from bs4 import BeautifulSoup
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def main() -> None:
|
|
82
|
+
async with Actor:
|
|
83
|
+
actor_input = await Actor.get_input()
|
|
84
|
+
async with httpx.AsyncClient() as client:
|
|
85
|
+
response = await client.get(actor_input['url'])
|
|
86
|
+
soup = BeautifulSoup(response.content, 'html.parser')
|
|
87
|
+
data = {
|
|
88
|
+
'url': actor_input['url'],
|
|
89
|
+
'title': soup.title.string if soup.title else None,
|
|
90
|
+
}
|
|
91
|
+
await Actor.push_data(data)
|
|
92
|
+
|
|
93
|
+
if __name__ == '__main__':
|
|
94
|
+
asyncio.run(main())
|
|
95
|
+
```
|
|
96
|
+
"""
|
|
60
97
|
|
|
61
98
|
_is_rebooting = False
|
|
62
99
|
_is_any_instance_initialized = False
|
|
@@ -108,7 +145,6 @@ class _ActorType:
|
|
|
108
145
|
|
|
109
146
|
self._is_initialized = False
|
|
110
147
|
|
|
111
|
-
@ignore_docs
|
|
112
148
|
async def __aenter__(self) -> Self:
|
|
113
149
|
"""Initialize the Actor.
|
|
114
150
|
|
|
@@ -120,7 +156,6 @@ class _ActorType:
|
|
|
120
156
|
await self.init()
|
|
121
157
|
return self
|
|
122
158
|
|
|
123
|
-
@ignore_docs
|
|
124
159
|
async def __aexit__(
|
|
125
160
|
self,
|
|
126
161
|
_exc_type: type[BaseException] | None,
|
|
@@ -8,7 +8,6 @@ from typing import TYPE_CHECKING, Protocol
|
|
|
8
8
|
|
|
9
9
|
from pydantic import TypeAdapter
|
|
10
10
|
|
|
11
|
-
from apify_shared.utils import ignore_docs
|
|
12
11
|
from crawlee._utils.context import ensure_context
|
|
13
12
|
|
|
14
13
|
from apify._models import ActorRun, PricingModel
|
|
@@ -26,9 +25,18 @@ if TYPE_CHECKING:
|
|
|
26
25
|
run_validator = TypeAdapter[ActorRun | None](ActorRun | None)
|
|
27
26
|
|
|
28
27
|
|
|
29
|
-
@docs_group('
|
|
28
|
+
@docs_group('Charging')
|
|
30
29
|
class ChargingManager(Protocol):
|
|
31
|
-
"""Provides fine-grained access to pay-per-event functionality.
|
|
30
|
+
"""Provides fine-grained access to pay-per-event functionality.
|
|
31
|
+
|
|
32
|
+
The ChargingManager allows you to charge for specific events in your Actor when using
|
|
33
|
+
the pay-per-event pricing model. This enables precise cost control and transparent
|
|
34
|
+
billing for different operations within your Actor.
|
|
35
|
+
|
|
36
|
+
### References
|
|
37
|
+
|
|
38
|
+
- Apify platform documentation: https://docs.apify.com/platform/actors/publishing/monetize
|
|
39
|
+
"""
|
|
32
40
|
|
|
33
41
|
async def charge(self, event_name: str, count: int = 1) -> ChargeResult:
|
|
34
42
|
"""Charge for a specified number of events - sub-operations of the Actor.
|
|
@@ -57,7 +65,7 @@ class ChargingManager(Protocol):
|
|
|
57
65
|
"""
|
|
58
66
|
|
|
59
67
|
|
|
60
|
-
@docs_group('
|
|
68
|
+
@docs_group('Charging')
|
|
61
69
|
@dataclass(frozen=True)
|
|
62
70
|
class ChargeResult:
|
|
63
71
|
"""Result of the `ChargingManager.charge` method."""
|
|
@@ -72,7 +80,7 @@ class ChargeResult:
|
|
|
72
80
|
"""How many events of each known type can still be charged within the limit."""
|
|
73
81
|
|
|
74
82
|
|
|
75
|
-
@docs_group('
|
|
83
|
+
@docs_group('Charging')
|
|
76
84
|
@dataclass
|
|
77
85
|
class ActorPricingInfo:
|
|
78
86
|
"""Result of the `ChargingManager.get_pricing_info` method."""
|
|
@@ -90,7 +98,6 @@ class ActorPricingInfo:
|
|
|
90
98
|
"""Price of every known event type."""
|
|
91
99
|
|
|
92
100
|
|
|
93
|
-
@ignore_docs
|
|
94
101
|
class ChargingManagerImplementation(ChargingManager):
|
|
95
102
|
"""Implementation of the `ChargingManager` Protocol - this is only meant to be instantiated internally."""
|
|
96
103
|
|
|
@@ -25,7 +25,7 @@ def _transform_to_list(value: Any) -> list[str] | None:
|
|
|
25
25
|
return value if isinstance(value, list) else str(value).split(',')
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
@docs_group('
|
|
28
|
+
@docs_group('Configuration')
|
|
29
29
|
class Configuration(CrawleeConfiguration):
|
|
30
30
|
"""A class for specifying the configuration of an Actor.
|
|
31
31
|
|
|
@@ -12,7 +12,6 @@ from cryptography.hazmat.primitives import hashes, serialization
|
|
|
12
12
|
from cryptography.hazmat.primitives.asymmetric import padding, rsa
|
|
13
13
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
14
14
|
|
|
15
|
-
from apify_shared.utils import ignore_docs
|
|
16
15
|
from crawlee._utils.crypto import crypto_random_object_id
|
|
17
16
|
|
|
18
17
|
from apify._consts import ENCRYPTED_INPUT_VALUE_REGEXP, ENCRYPTED_JSON_VALUE_PREFIX, ENCRYPTED_STRING_VALUE_PREFIX
|
|
@@ -22,7 +21,6 @@ ENCRYPTION_IV_LENGTH = 16
|
|
|
22
21
|
ENCRYPTION_AUTH_TAG_LENGTH = 16
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
@ignore_docs
|
|
26
24
|
def public_encrypt(value: str, *, public_key: rsa.RSAPublicKey) -> dict:
|
|
27
25
|
"""Encrypts the given value using AES cipher and the password for encryption using the public key.
|
|
28
26
|
|
|
@@ -66,7 +64,6 @@ def public_encrypt(value: str, *, public_key: rsa.RSAPublicKey) -> dict:
|
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
|
|
69
|
-
@ignore_docs
|
|
70
67
|
def private_decrypt(
|
|
71
68
|
encrypted_password: str,
|
|
72
69
|
encrypted_value: str,
|
|
@@ -118,7 +115,6 @@ def private_decrypt(
|
|
|
118
115
|
return decipher_bytes.decode('utf-8')
|
|
119
116
|
|
|
120
117
|
|
|
121
|
-
@ignore_docs
|
|
122
118
|
def load_private_key(private_key_file_base64: str, private_key_password: str) -> rsa.RSAPrivateKey:
|
|
123
119
|
private_key = serialization.load_pem_private_key(
|
|
124
120
|
base64.b64decode(private_key_file_base64.encode('utf-8')),
|
|
@@ -138,7 +134,6 @@ def _load_public_key(public_key_file_base64: str) -> rsa.RSAPublicKey:
|
|
|
138
134
|
return public_key
|
|
139
135
|
|
|
140
136
|
|
|
141
|
-
@ignore_docs
|
|
142
137
|
def decrypt_input_secrets(private_key: rsa.RSAPrivateKey, input_data: Any) -> Any:
|
|
143
138
|
"""Decrypt input secrets."""
|
|
144
139
|
if not isinstance(input_data, dict):
|
|
@@ -180,7 +175,6 @@ def encode_base62(num: int) -> str:
|
|
|
180
175
|
return res
|
|
181
176
|
|
|
182
177
|
|
|
183
|
-
@ignore_docs
|
|
184
178
|
def create_hmac_signature(secret_key: str, message: str) -> str:
|
|
185
179
|
"""Generate an HMAC signature and encodes it using Base62. Base62 encoding reduces the signature length.
|
|
186
180
|
|
|
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|
|
16
16
|
from typing import TypeAlias
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
@docs_group('
|
|
19
|
+
@docs_group('Actor')
|
|
20
20
|
class Webhook(BaseModel):
|
|
21
21
|
__model_config__ = ConfigDict(populate_by_name=True)
|
|
22
22
|
|
|
@@ -35,14 +35,14 @@ class Webhook(BaseModel):
|
|
|
35
35
|
] = None
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
@docs_group('
|
|
38
|
+
@docs_group('Actor')
|
|
39
39
|
class ActorRunMeta(BaseModel):
|
|
40
40
|
__model_config__ = ConfigDict(populate_by_name=True)
|
|
41
41
|
|
|
42
42
|
origin: Annotated[MetaOrigin, Field()]
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
@docs_group('
|
|
45
|
+
@docs_group('Actor')
|
|
46
46
|
class ActorRunStats(BaseModel):
|
|
47
47
|
__model_config__ = ConfigDict(populate_by_name=True)
|
|
48
48
|
|
|
@@ -63,7 +63,7 @@ class ActorRunStats(BaseModel):
|
|
|
63
63
|
compute_units: Annotated[float, Field(alias='computeUnits')]
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
@docs_group('
|
|
66
|
+
@docs_group('Actor')
|
|
67
67
|
class ActorRunOptions(BaseModel):
|
|
68
68
|
__model_config__ = ConfigDict(populate_by_name=True)
|
|
69
69
|
|
|
@@ -74,7 +74,7 @@ class ActorRunOptions(BaseModel):
|
|
|
74
74
|
max_total_charge_usd: Annotated[Decimal | None, Field(alias='maxTotalChargeUsd')] = None
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
@docs_group('
|
|
77
|
+
@docs_group('Actor')
|
|
78
78
|
class ActorRunUsage(BaseModel):
|
|
79
79
|
__model_config__ = ConfigDict(populate_by_name=True)
|
|
80
80
|
|
|
@@ -92,7 +92,7 @@ class ActorRunUsage(BaseModel):
|
|
|
92
92
|
proxy_serps: Annotated[float | None, Field(alias='PROXY_SERPS')] = None
|
|
93
93
|
|
|
94
94
|
|
|
95
|
-
@docs_group('
|
|
95
|
+
@docs_group('Actor')
|
|
96
96
|
class ActorRun(BaseModel):
|
|
97
97
|
__model_config__ = ConfigDict(populate_by_name=True)
|
|
98
98
|
|
|
@@ -27,17 +27,10 @@ if TYPE_CHECKING:
|
|
|
27
27
|
|
|
28
28
|
from apify._configuration import Configuration
|
|
29
29
|
|
|
30
|
-
|
|
31
30
|
__all__ = ['EventManager', 'LocalEventManager', 'PlatformEventManager']
|
|
32
31
|
|
|
33
32
|
|
|
34
|
-
@docs_group('
|
|
35
|
-
class PersistStateEvent(BaseModel):
|
|
36
|
-
name: Literal[Event.PERSIST_STATE]
|
|
37
|
-
data: Annotated[EventPersistStateData, Field(default_factory=lambda: EventPersistStateData(is_migrating=False))]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
@docs_group('Data structures')
|
|
33
|
+
@docs_group('Event data')
|
|
41
34
|
class SystemInfoEventData(BaseModel):
|
|
42
35
|
mem_avg_bytes: Annotated[float, Field(alias='memAvgBytes')]
|
|
43
36
|
mem_current_bytes: Annotated[float, Field(alias='memCurrentBytes')]
|
|
@@ -64,31 +57,37 @@ class SystemInfoEventData(BaseModel):
|
|
|
64
57
|
)
|
|
65
58
|
|
|
66
59
|
|
|
67
|
-
@docs_group('
|
|
60
|
+
@docs_group('Events')
|
|
61
|
+
class PersistStateEvent(BaseModel):
|
|
62
|
+
name: Literal[Event.PERSIST_STATE]
|
|
63
|
+
data: Annotated[EventPersistStateData, Field(default_factory=lambda: EventPersistStateData(is_migrating=False))]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@docs_group('Events')
|
|
68
67
|
class SystemInfoEvent(BaseModel):
|
|
69
68
|
name: Literal[Event.SYSTEM_INFO]
|
|
70
69
|
data: SystemInfoEventData
|
|
71
70
|
|
|
72
71
|
|
|
73
|
-
@docs_group('
|
|
72
|
+
@docs_group('Events')
|
|
74
73
|
class MigratingEvent(BaseModel):
|
|
75
74
|
name: Literal[Event.MIGRATING]
|
|
76
75
|
data: Annotated[EventMigratingData, Field(default_factory=EventMigratingData)]
|
|
77
76
|
|
|
78
77
|
|
|
79
|
-
@docs_group('
|
|
78
|
+
@docs_group('Events')
|
|
80
79
|
class AbortingEvent(BaseModel):
|
|
81
80
|
name: Literal[Event.ABORTING]
|
|
82
81
|
data: Annotated[EventAbortingData, Field(default_factory=EventAbortingData)]
|
|
83
82
|
|
|
84
83
|
|
|
85
|
-
@docs_group('
|
|
84
|
+
@docs_group('Events')
|
|
86
85
|
class ExitEvent(BaseModel):
|
|
87
86
|
name: Literal[Event.EXIT]
|
|
88
87
|
data: Annotated[EventExitData, Field(default_factory=EventExitData)]
|
|
89
88
|
|
|
90
89
|
|
|
91
|
-
@docs_group('
|
|
90
|
+
@docs_group('Events')
|
|
92
91
|
class EventWithoutData(BaseModel):
|
|
93
92
|
name: Literal[
|
|
94
93
|
Event.SESSION_RETIRED,
|
|
@@ -101,13 +100,13 @@ class EventWithoutData(BaseModel):
|
|
|
101
100
|
data: Any = None
|
|
102
101
|
|
|
103
102
|
|
|
104
|
-
@docs_group('
|
|
103
|
+
@docs_group('Events')
|
|
105
104
|
class DeprecatedEvent(BaseModel):
|
|
106
105
|
name: Literal['cpuInfo']
|
|
107
106
|
data: Annotated[dict[str, Any], Field(default_factory=dict)]
|
|
108
107
|
|
|
109
108
|
|
|
110
|
-
@docs_group('
|
|
109
|
+
@docs_group('Events')
|
|
111
110
|
class UnknownEvent(BaseModel):
|
|
112
111
|
name: str
|
|
113
112
|
data: Annotated[dict[str, Any], Field(default_factory=dict)]
|
|
@@ -120,7 +119,7 @@ event_data_adapter = TypeAdapter[EventMessage | DeprecatedEvent | UnknownEvent](
|
|
|
120
119
|
)
|
|
121
120
|
|
|
122
121
|
|
|
123
|
-
@docs_group('
|
|
122
|
+
@docs_group('Event managers')
|
|
124
123
|
class PlatformEventManager(EventManager):
|
|
125
124
|
"""A class for managing Actor events.
|
|
126
125
|
|
|
@@ -10,7 +10,6 @@ from urllib.parse import urljoin, urlparse
|
|
|
10
10
|
import httpx
|
|
11
11
|
|
|
12
12
|
from apify_shared.consts import ApifyEnvVars
|
|
13
|
-
from apify_shared.utils import ignore_docs
|
|
14
13
|
from crawlee.proxy_configuration import ProxyConfiguration as CrawleeProxyConfiguration
|
|
15
14
|
from crawlee.proxy_configuration import ProxyInfo as CrawleeProxyInfo
|
|
16
15
|
from crawlee.proxy_configuration import _NewUrlFunction
|
|
@@ -28,7 +27,6 @@ COUNTRY_CODE_REGEX = re.compile(r'^[A-Z]{2}$')
|
|
|
28
27
|
SESSION_ID_MAX_LENGTH = 50
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
@ignore_docs
|
|
32
30
|
def is_url(url: str) -> bool:
|
|
33
31
|
"""Check if the given string is a valid URL."""
|
|
34
32
|
try:
|
|
@@ -69,7 +67,7 @@ def _check(
|
|
|
69
67
|
raise ValueError(f'{error_str} does not match pattern {pattern.pattern!r}')
|
|
70
68
|
|
|
71
69
|
|
|
72
|
-
@docs_group('
|
|
70
|
+
@docs_group('Configuration')
|
|
73
71
|
@dataclass
|
|
74
72
|
class ProxyInfo(CrawleeProxyInfo):
|
|
75
73
|
"""Provides information about a proxy connection that is used for requests."""
|
|
@@ -89,7 +87,7 @@ class ProxyInfo(CrawleeProxyInfo):
|
|
|
89
87
|
"""
|
|
90
88
|
|
|
91
89
|
|
|
92
|
-
@docs_group('
|
|
90
|
+
@docs_group('Configuration')
|
|
93
91
|
class ProxyConfiguration(CrawleeProxyConfiguration):
|
|
94
92
|
"""Configures a connection to a proxy server with the provided options.
|
|
95
93
|
|
|
@@ -104,7 +102,6 @@ class ProxyConfiguration(CrawleeProxyConfiguration):
|
|
|
104
102
|
|
|
105
103
|
_configuration: Configuration
|
|
106
104
|
|
|
107
|
-
@ignore_docs
|
|
108
105
|
def __init__(
|
|
109
106
|
self,
|
|
110
107
|
*,
|
|
@@ -30,7 +30,19 @@ def is_running_in_ipython() -> bool:
|
|
|
30
30
|
return getattr(builtins, '__IPYTHON__', False)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
# The order of the rendered API groups is defined in the website/docusaurus.config.js file.
|
|
34
|
+
GroupName = Literal[
|
|
35
|
+
'Actor',
|
|
36
|
+
'Charging',
|
|
37
|
+
'Configuration',
|
|
38
|
+
'Event data',
|
|
39
|
+
'Event managers',
|
|
40
|
+
'Events',
|
|
41
|
+
'Request loaders',
|
|
42
|
+
'Storage clients',
|
|
43
|
+
'Storage data',
|
|
44
|
+
'Storages',
|
|
45
|
+
]
|
|
34
46
|
|
|
35
47
|
|
|
36
48
|
def docs_group(group_name: GroupName) -> Callable: # noqa: ARG001
|
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
-
from apify_shared.utils import ignore_docs
|
|
6
5
|
from crawlee._log_config import CrawleeLogFormatter, configure_logger, get_configured_log_level
|
|
7
6
|
|
|
8
7
|
# Name of the logger used throughout the library (resolves to 'apify')
|
|
@@ -12,7 +11,6 @@ logger_name = __name__.split('.')[0]
|
|
|
12
11
|
logger = logging.getLogger(logger_name)
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
@ignore_docs
|
|
16
14
|
class ActorLogFormatter(CrawleeLogFormatter): # noqa: D101 (Inherited from parent class)
|
|
17
15
|
pass
|
|
18
16
|
|
|
@@ -38,7 +38,7 @@ class _SimpleUrlInput(_RequestDetails):
|
|
|
38
38
|
url_input_adapter = TypeAdapter(list[_RequestsFromUrlInput | _SimpleUrlInput])
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
@docs_group('
|
|
41
|
+
@docs_group('Request loaders')
|
|
42
42
|
class RequestList(CrawleeRequestList):
|
|
43
43
|
"""Extends crawlee RequestList.
|
|
44
44
|
|
|
@@ -126,3 +126,26 @@ async def test_something(
|
|
|
126
126
|
|
|
127
127
|
assert actor_run.status == 'SUCCEEDED'
|
|
128
128
|
```
|
|
129
|
+
|
|
130
|
+
### Asserts
|
|
131
|
+
|
|
132
|
+
Since test Actors are not executed as standard pytest tests, we don't get introspection of assertion expressions. In case of failure, only a bare `AssertionError` is shown, without the left and right values. This means, we must include explicit assertion messages to aid potential debugging.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
async def test_add_and_fetch_requests(
|
|
136
|
+
make_actor: MakeActorFunction,
|
|
137
|
+
run_actor: RunActorFunction,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""Test basic functionality of adding and fetching requests."""
|
|
140
|
+
|
|
141
|
+
async def main() -> None:
|
|
142
|
+
async with Actor:
|
|
143
|
+
rq = await Actor.open_request_queue()
|
|
144
|
+
await rq.add_request(f'https://apify.com/')
|
|
145
|
+
assert is_finished is False, f'is_finished={is_finished}'
|
|
146
|
+
|
|
147
|
+
actor = await make_actor(label='rq-test', main_func=main)
|
|
148
|
+
run_result = await run_actor(actor)
|
|
149
|
+
|
|
150
|
+
assert run_result.status == 'SUCCEEDED'
|
|
151
|
+
```
|