Skip to content

Commit fd0716c

Browse files
committed
Merge remote-tracking branch 'origin/master' into alias-storage-configuration-mapping
2 parents b12e27e + 252eb4e commit fd0716c

File tree

104 files changed

+4953
-2757
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+4953
-2757
lines changed

.github/workflows/_check_code.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: Checkout repository
1616
uses: actions/checkout@v6
1717
- name: Run actionlint
18-
uses: rhysd/actionlint@v1.7.10
18+
uses: rhysd/actionlint@v1.7.11
1919

2020
spell_check:
2121
name: Spell check

.github/workflows/_release_docs.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ jobs:
6868
run: uv run poe build-docs
6969
env:
7070
APIFY_SIGNING_TOKEN: ${{ secrets.APIFY_SIGNING_TOKEN }}
71+
SEGMENT_TOKEN: ${{ secrets.SEGMENT_TOKEN }}
7172

7273
- name: Set up GitHub Pages
7374
uses: actions/configure-pages@v5

.github/workflows/_tests.yaml

Lines changed: 120 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,123 @@ jobs:
2020
tests_concurrency: "1"
2121

2222
integration_tests:
23-
name: Integration tests
24-
uses: apify/workflows/.github/workflows/python_integration_tests.yaml@main
25-
secrets: inherit
26-
with:
27-
python_versions: '["3.10", "3.14"]'
28-
operating_systems: '["ubuntu-latest"]'
29-
python_version_for_codecov: "3.14"
30-
operating_system_for_codecov: ubuntu-latest
31-
tests_concurrency: "16"
23+
name: Integration tests (${{ matrix.python-version }}, ${{ matrix.os }})
24+
25+
if: >-
26+
${{
27+
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login == 'apify') ||
28+
(github.event_name == 'push' && github.ref == 'refs/heads/master')
29+
}}
30+
31+
strategy:
32+
matrix:
33+
os: ["ubuntu-latest"]
34+
python-version: ["3.10", "3.14"]
35+
36+
runs-on: ${{ matrix.os }}
37+
38+
env:
39+
TESTS_CONCURRENCY: "8"
40+
41+
steps:
42+
- name: Checkout repository
43+
uses: actions/checkout@v6
44+
45+
- name: Set up Python ${{ matrix.python-version }}
46+
uses: actions/setup-python@v6
47+
with:
48+
python-version: ${{ matrix.python-version }}
49+
50+
- name: Set up uv package manager
51+
uses: astral-sh/setup-uv@v7
52+
with:
53+
python-version: ${{ matrix.python-version }}
54+
55+
- name: Install Python dependencies
56+
run: uv run poe install-dev
57+
58+
- name: Run integration tests
59+
run: uv run poe integration-tests-cov
60+
env:
61+
APIFY_TEST_USER_API_TOKEN: ${{ secrets.APIFY_TEST_USER_PYTHON_SDK_API_TOKEN }}
62+
APIFY_TEST_USER_2_API_TOKEN: ${{ secrets.APIFY_TEST_USER_2_API_TOKEN }}
63+
64+
- name: Upload integration test coverage
65+
if: >-
66+
${{
67+
matrix.os == 'ubuntu-latest' &&
68+
matrix.python-version == '3.14' &&
69+
env.CODECOV_TOKEN != ''
70+
}}
71+
uses: codecov/codecov-action@v5
72+
env:
73+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
74+
with:
75+
token: ${{ env.CODECOV_TOKEN }}
76+
files: coverage-integration.xml
77+
flags: integration
78+
79+
e2e_tests:
80+
name: E2E tests (${{ matrix.python-version }}, ${{ matrix.os }})
81+
82+
if: >-
83+
${{
84+
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login == 'apify') ||
85+
(github.event_name == 'push' && github.ref == 'refs/heads/master')
86+
}}
87+
88+
# E2E tests build and run Actors on the platform. The combination of max-parallel 2 with 16 pytest
89+
# workers and a global concurrency group is a compromise between stability and performance - it keeps
90+
# the platform's resource usage in check while still allowing reasonable test throughput.
91+
concurrency:
92+
group: e2e-tests
93+
cancel-in-progress: false
94+
95+
strategy:
96+
max-parallel: 2
97+
matrix:
98+
os: ["ubuntu-latest"]
99+
python-version: ["3.10", "3.14"]
100+
101+
runs-on: ${{ matrix.os }}
102+
103+
env:
104+
TESTS_CONCURRENCY: "16"
105+
106+
steps:
107+
- name: Checkout repository
108+
uses: actions/checkout@v6
109+
110+
- name: Set up Python ${{ matrix.python-version }}
111+
uses: actions/setup-python@v6
112+
with:
113+
python-version: ${{ matrix.python-version }}
114+
115+
- name: Set up uv package manager
116+
uses: astral-sh/setup-uv@v7
117+
with:
118+
python-version: ${{ matrix.python-version }}
119+
120+
- name: Install Python dependencies
121+
run: uv run poe install-dev
122+
123+
- name: Run E2E tests
124+
run: uv run poe e2e-tests-cov
125+
env:
126+
APIFY_TEST_USER_API_TOKEN: ${{ secrets.APIFY_TEST_USER_PYTHON_SDK_API_TOKEN }}
127+
APIFY_TEST_USER_2_API_TOKEN: ${{ secrets.APIFY_TEST_USER_2_API_TOKEN }}
128+
129+
- name: Upload E2E test coverage
130+
if: >-
131+
${{
132+
matrix.os == 'ubuntu-latest' &&
133+
matrix.python-version == '3.14' &&
134+
env.CODECOV_TOKEN != ''
135+
}}
136+
uses: codecov/codecov-action@v5
137+
env:
138+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
139+
with:
140+
token: ${{ env.CODECOV_TOKEN }}
141+
files: coverage-e2e.xml
142+
flags: e2e

CHANGELOG.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,27 @@
33
All notable changes to this project will be documented in this file.
44

55
<!-- git-cliff-unreleased-start -->
6-
## 3.1.1 - **not yet released**
6+
## 3.2.1 - **not yet released**
7+
8+
### 🐛 Bug Fixes
9+
10+
- Fix crawler deadlock when API fails during marking request as handled ([#768](https://github.com/apify/apify-sdk-python/pull/768)) ([66ed5ea](https://github.com/apify/apify-sdk-python/commit/66ed5eaf819f9c9f411c814606187d76d32a4e11)) by [@Pijukatel](https://github.com/Pijukatel), closes [#1694](https://github.com/apify/apify-sdk-python/issues/1694)
11+
12+
13+
<!-- git-cliff-unreleased-end -->
14+
## [3.2.0](https://github.com/apify/apify-sdk-python/releases/tag/v3.2.0) (2026-02-11)
715

816
### 🚀 Features
917

1018
- Add `Actor` method `use_state` ([#738](https://github.com/apify/apify-sdk-python/pull/738)) ([8a78b6f](https://github.com/apify/apify-sdk-python/commit/8a78b6fc9324e948b3481b07a3582215d3c966fe)) by [@Mantisus](https://github.com/Mantisus), closes [#735](https://github.com/apify/apify-sdk-python/issues/735)
1119

20+
### 🐛 Bug Fixes
21+
22+
- Stop silently swallowing exceptions in request queue ([#777](https://github.com/apify/apify-sdk-python/pull/777)) ([6358d66](https://github.com/apify/apify-sdk-python/commit/6358d66aeb83484845b17f7c8632b6c763cef368)) by [@vdusek](https://github.com/vdusek)
23+
- Handle TimeoutError in Actor __aexit__ to prevent resource leaks ([#776](https://github.com/apify/apify-sdk-python/pull/776)) ([fb13765](https://github.com/apify/apify-sdk-python/commit/fb13765448a2a6e2b776de819ece68f90abff1e3)) by [@vdusek](https://github.com/vdusek)
24+
- Pass name instead of id for name param in SmartApifyStorageClient ([#775](https://github.com/apify/apify-sdk-python/pull/775)) ([56cfc38](https://github.com/apify/apify-sdk-python/commit/56cfc38aa98a2a6689dd077e9d5c5d8729872413)) by [@vdusek](https://github.com/vdusek)
25+
1226

13-
<!-- git-cliff-unreleased-end -->
1427
## [3.1.0](https://github.com/apify/apify-sdk-python/releases/tag/v3.1.0) (2025-12-08)
1528

1629
### 🚀 Features

CONTRIBUTING.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,26 @@ To run the documentation locally (requires Node.js):
118118
uv run poe run-docs
119119
```
120120

121+
## Commits
122+
123+
We use [Conventional Commits](https://www.conventionalcommits.org/) format for commit messages. This convention is used to automatically determine version bumps during the release process.
124+
125+
### Available commit types
126+
127+
| Type | Description |
128+
| ---- | ----------- |
129+
| `feat` | A new feature |
130+
| `fix` | A bug fix |
131+
| `docs` | Documentation only changes |
132+
| `style` | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) |
133+
| `refactor` | A code change that neither fixes a bug nor adds a feature |
134+
| `perf` | A code change that improves performance |
135+
| `test` | Adding missing tests or correcting existing tests |
136+
| `build` | Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) |
137+
| `ci` | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) |
138+
| `chore` | Other changes that don't modify src or test files |
139+
| `revert` | Reverts a previous commit |
140+
121141
## Release process
122142

123143
Publishing new versions to [PyPI](https://pypi.org/project/apify) is automated through GitHub Actions.

pyproject.toml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "apify"
7-
version = "3.1.1"
7+
version = "3.2.1"
88
description = "Apify SDK for Python"
99
authors = [{ name = "Apify Technologies s.r.o.", email = "support@apify.com" }]
1010
license = { file = "LICENSE" }
@@ -64,6 +64,11 @@ scrapy = ["scrapy>=2.11.0"]
6464

6565
[dependency-groups]
6666
dev = [
67+
# TODO: Remove this constraint once pydoc-markdown updates its dependencies.
68+
# Package pydoc-markdown is unmaintained and pins old docspec-python with vulnerable black.
69+
# See https://github.com/apify/apify-client-python/pull/582/ for more details.
70+
# We explicitly constrain black>=24.3.0 to override the transitive dependency.
71+
"black>=24.3.0",
6772
"build<2.0.0",
6873
"crawlee[parsel]",
6974
"dycw-pytest-only<3.0.0",
@@ -140,15 +145,13 @@ indent-style = "space"
140145
"**/{tests}/*" = [
141146
"D", # Everything from the pydocstyle
142147
"INP001", # File {filename} is part of an implicit namespace package, add an __init__.py
148+
"PLC0415", # `import` should be at the top-level of a file
143149
"PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable
144150
"S101", # Use of assert detected
145151
"SLF001", # Private member accessed: `{name}`
146152
"T20", # flake8-print
147-
"TRY301", # Abstract `raise` to an inner function
148153
"TID252", # Prefer absolute imports over relative imports from parent modules
149-
]
150-
"**/{tests}/{integration}/*" = [
151-
"PLC0415", # `import` should be at the top-level of a file
154+
"TRY301", # Abstract `raise` to an inner function
152155
]
153156
"**/{docs,website}/**" = [
154157
"D", # Everything from the pydocstyle
@@ -229,6 +232,8 @@ unit-tests = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} tests/unit
229232
unit-tests-cov = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} --cov=src/apify --cov-report=xml:coverage-unit.xml tests/unit"
230233
integration-tests = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} tests/integration"
231234
integration-tests-cov = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} --cov=src/apify --cov-report=xml:coverage-integration.xml tests/integration"
235+
e2e-tests = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} tests/e2e"
236+
e2e-tests-cov = "uv run pytest --numprocesses=${TESTS_CONCURRENCY:-auto} --cov=src/apify --cov-report=xml:coverage-e2e.xml tests/e2e"
232237
check-code = ["lint", "type-check", "unit-tests"]
233238

234239
[tool.poe.tasks.install-dev]

src/apify/_actor.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,12 @@ async def finalize() -> None:
257257
# Persist Actor state
258258
await self._save_actor_state()
259259

260-
await asyncio.wait_for(finalize(), self._cleanup_timeout.total_seconds())
261-
self._is_initialized = False
260+
try:
261+
await asyncio.wait_for(finalize(), self._cleanup_timeout.total_seconds())
262+
except TimeoutError:
263+
self.log.exception('Actor cleanup timed out')
264+
finally:
265+
self._is_initialized = False
262266

263267
if self._exit_process:
264268
sys.exit(self.exit_code)

src/apify/_charging.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,11 @@ async def __aenter__(self) -> None:
158158
if self._configuration.test_pay_per_event:
159159
self._pricing_model = 'PAY_PER_EVENT'
160160
else:
161-
self._pricing_model = pricing_info.pricing_model if pricing_info else None
161+
self._pricing_model = pricing_info.pricing_model if pricing_info is not None else None
162162

163163
# Load per-event pricing information
164-
if pricing_info and pricing_info.pricing_model == 'PAY_PER_EVENT':
165-
for event_name, event_pricing in pricing_info.pricing_per_event.actor_charge_events.items(): # ty:ignore[possibly-missing-attribute]
164+
if pricing_info is not None and isinstance(pricing_info, PayPerEventActorPricingInfo):
165+
for event_name, event_pricing in pricing_info.pricing_per_event.actor_charge_events.items():
166166
self._pricing_info[event_name] = PricingInfoItem(
167167
price=event_pricing.event_price_usd,
168168
title=event_pricing.event_title,

src/apify/_utils.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ def get_system_info() -> dict:
2121
'os': sys.platform,
2222
}
2323

24-
if is_running_in_ipython():
25-
system_info['is_running_in_ipython'] = True
24+
system_info['is_running_in_ipython'] = is_running_in_ipython()
2625

2726
return system_info
2827

src/apify/storage_clients/_apify/_request_queue_shared_client.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ async def mark_request_as_handled(self, request: Request) -> ProcessedRequest |
235235
processed_request=processed_request,
236236
hydrated_request=request,
237237
)
238-
except Exception as exc:
239-
logger.debug(f'Error marking request {request.unique_key} as handled: {exc!s}')
238+
except Exception:
239+
logger.exception(f'Error marking request {request.unique_key} as handled.')
240240
return None
241241
else:
242242
return processed_request
@@ -279,8 +279,8 @@ async def reclaim_request(
279279
if forefront:
280280
self._should_check_for_forefront_requests = True
281281

282-
except Exception as exc:
283-
logger.debug(f'Error reclaiming request {request.unique_key}: {exc!s}')
282+
except Exception:
283+
logger.exception(f'Error reclaiming request {request.unique_key}')
284284
return None
285285
else:
286286
return processed_request

0 commit comments

Comments
 (0)