Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:

env:
PYTHON_DEFAULT_VERSION: "3.12"
UV_VERSION: "0.8.24"

jobs:
deploy:
Expand All @@ -26,13 +27,17 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
- uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
- name: Display Python version
run: python -c "import sys; print(sys.version)"
- name: Install dependencies
run: python -m pip install --upgrade nox pdm==2.26.2
run: uv sync --only-group nox --locked
- name: Build the distribution
id: build
run: nox -vs build
run: uv run nox -vs build
- name: Read the Changelog
id: read-changelog
uses: mindsers/changelog-reader-action@v2
Expand Down
54 changes: 36 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:

env:
PYTHON_DEFAULT_VERSION: "3.12"
UV_VERSION: "0.8.24"

jobs:
lint:
Expand All @@ -24,17 +25,20 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
cache: "pip"
- uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
- name: Install dependencies
run: python -m pip install --upgrade nox pdm==2.26.2
run: uv sync --only-group nox --locked
- name: Run linters
run: nox -vs lint
run: uv run nox -vs lint
- name: Validate new changelog entries
if: (contains(github.event.pull_request.labels.*.name, '-changelog') == false) && (github.event.pull_request.base.ref != '')
run: if [ -z "$(git diff --diff-filter=A --name-only origin/${{ github.event.pull_request.base.ref }} changelog.d)" ];
then echo no changelog item added; exit 1; fi
- name: Changelog validation
run: nox -vs towncrier_check
run: uv run nox -vs towncrier_check
build:
timeout-minutes: 30
needs: lint
Expand All @@ -47,11 +51,14 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
cache: "pip"
- uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
- name: Install dependencies
run: python -m pip install --upgrade nox pdm==2.26.2
run: uv sync --only-group nox --locked
- name: Build the distribution
run: nox -vs build
run: uv run nox -vs build
cleanup_buckets:
timeout-minutes: 30
needs: lint
Expand All @@ -69,13 +76,17 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
cache: "pip"
- uses: astral-sh/setup-uv@v7
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
- name: Install dependencies
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
run: python -m pip install --upgrade nox pdm==2.26.2
run: uv sync --only-group nox --locked
- name: Find and remove old buckets
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} # TODO: skip this whole job instead
run: nox -vs cleanup_old_buckets
run: uv run nox -vs cleanup_old_buckets
test:
timeout-minutes: 90
needs: cleanup_buckets
Expand Down Expand Up @@ -107,14 +118,17 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
- uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
- name: Install dependencies
run: python -m pip install --upgrade nox pdm==2.26.2
run: uv sync --only-group nox --locked
- name: Run unit tests
run: nox -vs unit -- -v
run: uv run nox -vs unit -- -v
- name: Run integration tests
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }}
run: nox -vs integration -- --dont-cleanup-old-buckets -v
run: uv run nox -vs integration -- --dont-cleanup-old-buckets -v
doc:
timeout-minutes: 30
needs: build
Expand All @@ -127,13 +141,17 @@ jobs:
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_DEFAULT_VERSION }}
cache: "pip"
- name: Install dependencies
- uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
- name: Install system dependencies
env:
DEBIAN_FRONTEND: noninteractive
run: |
sudo apt-get update -y
sudo apt-get install -y graphviz plantuml
python -m pip install --upgrade nox pdm==2.26.2
- name: Install dependencies
run: uv sync --only-group nox --locked
- name: Build the docs
run: nox --non-interactive -vs doc
run: uv run nox --non-interactive -vs doc
4 changes: 2 additions & 2 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ build:
- graphviz
jobs:
post_create_environment:
- pip install pdm
- pdm export --format requirements --group doc --output requirements-doc.txt
- python -m pip install uv==0.8.4
- uv export --format requirements-txt --group doc --output-file requirements-doc.txt

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ upcoming release can be found in [changelog.d](changelog.d).

<!-- towncrier release notes start -->

## [2.10.3](https://github.com/Backblaze/b2-sdk-python/releases/tag/v2.10.3) - 2026-02-23


### Fixed

- Address backwards compatibility issue for sqlite account info caused by the migration of schema to a new multi-bucket format.
- Avoid http-level retries during upload requests.
- Use `stat.S_ISDIR` check for local folder children scanning instead of `Path.is_dir` to account for an api change in Python 3.14.

### Infrastructure

- Bump pip version.
- Migrate from pdm to uv.
- Move pytest_plugins to top-level conftest (fix for newer pytest).
- Set temporary directory as account info config dir in tests.
- Use a separate nox dependency group for reprocible installs of nox in ci / cd.


## [2.10.2](https://github.com/Backblaze/b2-sdk-python/releases/tag/v2.10.2) - 2025-12-16


Expand Down
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ for a given github issue).

## Developer Info

You'll need to have [nox](https://github.com/theacodes/nox) and [pdm](https://pdm-project.org/) installed:
You'll need to have [nox](https://github.com/theacodes/nox) and [uv](https://docs.astral.sh/uv/) installed:

* `pip install nox pdm`
* `pip install nox uv`

With `nox`, you can run different sessions (default are `lint` and `test`):

Expand Down Expand Up @@ -99,11 +99,11 @@ Given Python interpreters should be installed in the operating system or via [py

## Managing dependencies

We use [pdm](https://pdm-project.org/) for managing dependencies and developing locally.
We use [uv](https://docs.astral.sh/uv/) for managing dependencies and developing locally.
If you want to change any of the project requirements (or requirement bounds) in `pyproject.toml`,
make sure that `pdm.lock` file reflects those changes by using `pdm add`, `pdm update` or other
commands - see [documentation](https://pdm-project.org/latest/). You can verify that lock file
is up to date by running the linter.
make sure that `uv.lock` file reflects those changes by using `uv add`, `uv lock` or other
commands - see [documentation](https://docs.astral.sh/uv/). You can verify that the lock file
is up to date by running `uv lock --check`.

## Linting

Expand Down
24 changes: 11 additions & 13 deletions b2sdk/_internal/b2http.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
B2ConnectionError,
B2Error,
B2RequestTimeout,
B2RequestTimeoutDuringUpload,
BadDateFormat,
BrokenPipe,
ClockSkew,
Expand Down Expand Up @@ -254,7 +253,7 @@ def request(
:param url: a URL to call
:param headers: headers to send.
:param data: raw bytes or a file-like object to send
:param try_count: a number of retries
:param try_count: a number of attempts
:param params: a dict that will be converted to query string for GET requests or additional metadata for POST requests
:param stream: if True, the response will be streamed
:param _timeout: a timeout for the request in seconds if not default
Expand Down Expand Up @@ -355,14 +354,9 @@ def post_content_return_json(
:param data: a file-like object to send
:return: a dict that is the decoded JSON
"""
try:
return self.request_content_return_json(
'POST', url, headers, data, try_count, post_params, _timeout=_timeout
)
except B2RequestTimeout:
# this forces a token refresh, which is necessary if request is still alive
# on the server but has terminated for some reason on the client. See #79
raise B2RequestTimeoutDuringUpload()
return self.request_content_return_json(
'POST', url, headers, data, try_count, post_params, _timeout=_timeout
)

def post_json_return_json(self, url, headers, params, try_count: int = TRY_COUNT_OTHER):
"""
Expand Down Expand Up @@ -423,7 +417,7 @@ def get_content(self, url, headers, try_count: int = TRY_COUNT_DOWNLOAD):

:param str url: a URL to call
:param dict headers: headers to send
:param int try_count: a number or retries
:param int try_count: a number of attempts
:return: Context manager that returns an object that supports iter_content()
"""
response = self.request(
Expand Down Expand Up @@ -456,7 +450,7 @@ def head_content(

:param str url: a URL to call
:param dict headers: headers to send
:param int try_count: a number or retries
:param int try_count: a number of attempts
:return: HTTP response
"""
return self.request('HEAD', url, headers=headers, try_count=try_count)
Expand Down Expand Up @@ -586,9 +580,13 @@ def _translate_and_retry(
the exception is a retryable B2Error.

:param fcn: request function to call
:param try_count: a number of retries
:param try_count: a number of attempts
:param post_params: request parameters
"""

if try_count < 1:
raise ValueError('try_count must be >= 1')

# For all but the last try, catch the exception.
wait_time = 1.0
max_wait_time = 64
Expand Down
16 changes: 13 additions & 3 deletions b2sdk/_internal/raw_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ def _get_json(self, base_url: str, endpoint: str, auth: str, **params) -> JSON:

def authorize_account(self, realm_url, application_key_id, application_key):
auth = (
f"Basic {base64.b64encode(f'{application_key_id}:{application_key}'.encode()).decode()}"
f'Basic {base64.b64encode(f"{application_key_id}:{application_key}".encode()).decode()}'
)
return self._post_json(realm_url, 'b2_authorize_account', auth)

Expand Down Expand Up @@ -1071,7 +1071,12 @@ def upload_file(
legal_hold=legal_hold,
custom_upload_timestamp=custom_upload_timestamp,
)
return self.b2_http.post_content_return_json(upload_url, headers, data_stream)
return self.b2_http.post_content_return_json(
upload_url,
headers,
data_stream,
try_count=1,
)

def upload_part(
self,
Expand All @@ -1097,7 +1102,12 @@ def upload_part(
)
server_side_encryption.add_to_upload_headers(headers)

return self.b2_http.post_content_return_json(upload_url, headers, data_stream)
return self.b2_http.post_content_return_json(
upload_url,
headers,
data_stream,
try_count=1,
)

def copy_file(
self,
Expand Down
1 change: 0 additions & 1 deletion changelog.d/+account_info_change_dir.infrastructure.md

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion changelog.d/+py314-test.fixed.md

This file was deleted.

This file was deleted.

Loading
Loading