From 665951ac9fab4f552cfa8f6201595ce54a3eff26 Mon Sep 17 00:00:00 2001 From: Aleksandr Goncharov Date: Fri, 5 Jun 2026 15:13:23 +0300 Subject: [PATCH] fix: allow dots in bucket name --- b2/_internal/_utils/uri.py | 2 +- changelog.d/+b2-uri-bucket-period.fixed.md | 1 + test/integration/test_b2_command_line.py | 17 +++++++++++++++++ test/unit/_utils/test_uri.py | 2 ++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 changelog.d/+b2-uri-bucket-period.fixed.md diff --git a/b2/_internal/_utils/uri.py b/b2/_internal/_utils/uri.py index 3be7aa72..b12b8418 100644 --- a/b2/_internal/_utils/uri.py +++ b/b2/_internal/_utils/uri.py @@ -24,7 +24,7 @@ from b2sdk.v3.exception import B2Error _B2ID_PATTERN = re.compile(r'^b2id://(?P[a-zA-Z0-9:_-]+)$', re.IGNORECASE) -_B2_PATTERN = re.compile(r'^b2://(?P[a-z0-9-]*)(?P/.*)?$', re.IGNORECASE) +_B2_PATTERN = re.compile(r'^b2://(?P[a-z0-9.-]*)(?P/.*)?$', re.IGNORECASE) _SCHEME_PATTERN = re.compile(r'(?P[a-z0-9]*)://.*', re.IGNORECASE) _CONTROL_CHARACTERS_AND_SPACE = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ' diff --git a/changelog.d/+b2-uri-bucket-period.fixed.md b/changelog.d/+b2-uri-bucket-period.fixed.md new file mode 100644 index 00000000..d3259964 --- /dev/null +++ b/changelog.d/+b2-uri-bucket-period.fixed.md @@ -0,0 +1 @@ +Fix `b2://` URI parsing to accept bucket names containing periods. diff --git a/test/integration/test_b2_command_line.py b/test/integration/test_b2_command_line.py index 14f2d9f4..095b533f 100755 --- a/test/integration/test_b2_command_line.py +++ b/test/integration/test_b2_command_line.py @@ -305,6 +305,23 @@ def test_download(b2_tool, persistent_bucket, sample_filepath, uploaded_sample_f assert output_b.read_text() == sample_filepath.read_text() +def test_download__period_in_bucket_name(b2_tool, schedule_bucket_cleanup, sample_filepath, tmp_path): + bucket_name = b2_tool.generate_bucket_name() + bucket_name = bucket_name[:-2] + ".x" + schedule_bucket_cleanup(bucket_name) + b2_tool.should_succeed( + ['bucket', 'create', bucket_name, 'allPrivate', *b2_tool.get_bucket_info_args()] + ) + b2_tool.should_succeed_json( + ['file', 'upload', '--quiet', bucket_name, str(sample_filepath), 'kitten.jpg'] + ) + output = tmp_path / 'kitten.jpg' + b2_tool.should_succeed( + ['file', 'download', '--quiet', f'b2://{bucket_name}/kitten.jpg', str(output)] + ) + assert output.read_text() == sample_filepath.read_text() + + def test_basic(b2_tool, persistent_bucket, sample_file, tmp_path, b2_uri_args, apiver_int): bucket_name = persistent_bucket.bucket_name subfolder = f'{persistent_bucket.subfolder}/' diff --git a/test/unit/_utils/test_uri.py b/test/unit/_utils/test_uri.py index ef7f8818..110d1678 100644 --- a/test/unit/_utils/test_uri.py +++ b/test/unit/_utils/test_uri.py @@ -63,6 +63,8 @@ def test_b2fileuri_str(): ('./some/local/path', Path('some/local/path')), ('.', Path('')), ('b2://bucket', B2URI(bucket_name='bucket')), + ('b2://one.two/kitten.jpg', B2URI(bucket_name='one.two', path='kitten.jpg')), + ('b2://one.two', B2URI(bucket_name='one.two')), (' b2://bucket', B2URI(bucket_name='bucket')), ('b2://bucket/', B2URI(bucket_name='bucket')), ('b2://bucket/path/to/dir/', B2URI(bucket_name='bucket', path='path/to/dir/')),