diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 754d4cd1e..bdfc13547 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,7 +84,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "macos-latest", "windows-latest"] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9", "pypy-3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9", "pypy-3.9-nightly", "pypy-3.10"] exclude: - os: "macos-latest" python-version: "pypy-3.9" @@ -94,6 +94,13 @@ jobs: python-version: "pypy-3.10" - os: "windows-latest" python-version: "pypy-3.10" + # TODO: pypy-3.9 started breaking on windows, due to some prints leftover in pypy release + - os: "ubuntu-latest" + python-version: "pypy-3.9-nightly" + - os: "macos-latest" + python-version: "pypy-3.9-nightly" + - os: "windows-latest" + python-version: "pypy-3.9" steps: - uses: actions/checkout@v3 with: @@ -106,12 +113,12 @@ jobs: - name: Install dependencies run: python -m pip install --upgrade nox pip setuptools - name: Run unit tests - run: nox -vs unit + run: nox -vs unit -- -v env: SKIP_COVERAGE: ${{ startsWith(matrix.python-version, env.SKIP_COVERAGE_PYTHON_VERSION_PREFIX) }} - name: Run integration tests if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }} - run: nox -vs integration -- --dont-cleanup-old-buckets + run: nox -vs integration -- --dont-cleanup-old-buckets -v doc: needs: build runs-on: ubuntu-latest diff --git a/b2sdk/api.py b/b2sdk/api.py index bee11c0de..e76c177cd 100644 --- a/b2sdk/api.py +++ b/b2sdk/api.py @@ -209,13 +209,13 @@ def authorize_automatically(self): return self.session.authorize_automatically() @limit_trace_arguments(only=('self', 'realm')) - def authorize_account(self, realm, application_key_id, application_key): + def authorize_account(self, application_key_id, application_key, realm='production'): """ Perform account authorization. - :param str realm: a realm to authorize account in (usually just "production") :param str application_key_id: :term:`application key ID` :param str application_key: user's :term:`application key` + :param str realm: a realm to authorize account in (usually just "production") """ self.session.authorize_account(realm, application_key_id, application_key) self._populate_bucket_cache_from_key() diff --git a/b2sdk/v2/api.py b/b2sdk/v2/api.py index 40690c0f9..349153285 100644 --- a/b2sdk/v2/api.py +++ b/b2sdk/v2/api.py @@ -28,6 +28,7 @@ class Services(v3.Services): # override to use legacy B2Session with legacy B2Http # and to raise old style BucketIdNotFound exception # and to use old style Bucket +# and to use legacy authorize_account signature class B2Api(v3.B2Api): SESSION_CLASS = staticmethod(B2Session) BUCKET_CLASS = staticmethod(Bucket) @@ -46,3 +47,12 @@ def get_bucket_by_id(self, bucket_id: str) -> v3.Bucket: return super().get_bucket_by_id(bucket_id) except v3BucketIdNotFound as e: raise BucketIdNotFound(e.bucket_id) + + # one could contemplate putting "@limit_trace_arguments(only=('self', 'realm'))" here but logfury meta magic copies + # the appropriate attributes from base classes + def authorize_account(self, realm, application_key_id, application_key): + return super().authorize_account( + application_key_id=application_key_id, + application_key=application_key, + realm=realm, + ) \ No newline at end of file diff --git a/changelog.d/+authorize_account.changed.md b/changelog.d/+authorize_account.changed.md new file mode 100644 index 000000000..4ada0c76e --- /dev/null +++ b/changelog.d/+authorize_account.changed.md @@ -0,0 +1 @@ +Change v3.B2Api.authorize_account signature to make `realm` optional and `"production"` by default. \ No newline at end of file diff --git a/changelog.d/+make_tests_in_ci_verbose.infrastructure.md b/changelog.d/+make_tests_in_ci_verbose.infrastructure.md new file mode 100644 index 000000000..58575b342 --- /dev/null +++ b/changelog.d/+make_tests_in_ci_verbose.infrastructure.md @@ -0,0 +1 @@ +Add `-v` to pytest in CI. \ No newline at end of file diff --git a/changelog.d/+run_windows_pypy_tests_on_nightly.infrastructure.md b/changelog.d/+run_windows_pypy_tests_on_nightly.infrastructure.md new file mode 100644 index 000000000..032ad9988 --- /dev/null +++ b/changelog.d/+run_windows_pypy_tests_on_nightly.infrastructure.md @@ -0,0 +1 @@ +Run windows pypy3.9 tests on nightly builds. \ No newline at end of file diff --git a/test/unit/api/test_api.py b/test/unit/api/test_api.py index 08151f7bb..0cafb0456 100644 --- a/test/unit/api/test_api.py +++ b/test/unit/api/test_api.py @@ -55,6 +55,29 @@ def setUp(self): self.raw_api = self.api.session.raw_api (self.application_key_id, self.master_key) = self.raw_api.create_account() + @pytest.mark.apiver(from_ver=3) + def test_authorize_signature_v3_default_realm(self): + self.api.authorize_account( + self.application_key_id, + self.master_key, + ) + + @pytest.mark.apiver(from_ver=3) + def test_authorize_signature_v3_explicit_realm(self): + self.api.authorize_account( + self.application_key_id, + self.master_key, + 'production', + ) + + @pytest.mark.apiver(to_ver=2) + def test_authorize_signature_up_to_ver_2(self): + self.api.authorize_account( + 'production', + self.application_key_id, + self.master_key, + ) + def test_get_file_info(self): self._authorize_account() bucket = self.api.create_bucket('bucket1', 'allPrivate') @@ -312,7 +335,11 @@ def test_buckets_with_encryption(self): # now check it with no readBucketEncryption permission to see that it's unknown key = create_key(self.api, ['listBuckets'], 'key1') - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) buckets = { b.name: b for b in self.api.list_buckets() # scan again with new key @@ -352,7 +379,11 @@ def test_reauthorize_with_app_key(self): key = create_key(self.api, ['listBuckets'], 'key1') # authorize with the key - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) # expire the auth token we just got self.raw_api.expire_auth_token(self.account_info.get_account_auth_token()) @@ -365,14 +396,22 @@ def test_list_buckets_with_restriction(self): bucket1 = self.api.create_bucket('bucket1', 'allPrivate') self.api.create_bucket('bucket2', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) assert [b.name for b in self.api.list_buckets(bucket_name=bucket1.name)] == ['bucket1'] def test_get_bucket_by_name_with_bucket_restriction(self): self._authorize_account() bucket1 = self.api.create_bucket('bucket1', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) assert self.api.get_bucket_by_name('bucket1').id_ == bucket1.id_ def test_list_buckets_with_restriction_and_wrong_name(self): @@ -380,7 +419,11 @@ def test_list_buckets_with_restriction_and_wrong_name(self): bucket1 = self.api.create_bucket('bucket1', 'allPrivate') bucket2 = self.api.create_bucket('bucket2', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) with pytest.raises(RestrictedBucket) as excinfo: self.api.list_buckets(bucket_name=bucket2.name) assert str(excinfo.value) == 'Application key is restricted to bucket: bucket1' @@ -390,7 +433,11 @@ def test_list_buckets_with_restriction_and_no_name(self): bucket1 = self.api.create_bucket('bucket1', 'allPrivate') self.api.create_bucket('bucket2', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) with pytest.raises(RestrictedBucket) as excinfo: self.api.list_buckets() assert str(excinfo.value) == 'Application key is restricted to bucket: bucket1' @@ -400,13 +447,21 @@ def test_list_buckets_with_restriction_and_wrong_id(self): bucket1 = self.api.create_bucket('bucket1', 'allPrivate') self.api.create_bucket('bucket2', 'allPrivate') key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) with pytest.raises(RestrictedBucket) as excinfo: self.api.list_buckets(bucket_id='not the one bound to the key') assert str(excinfo.value) == f'Application key is restricted to bucket: {bucket1.id_}' def _authorize_account(self): - self.api.authorize_account('production', self.application_key_id, self.master_key) + self.api.authorize_account( + application_key_id=self.application_key_id, + application_key=self.master_key, + realm='production', + ) def test_update_file_retention(self): self._authorize_account() diff --git a/test/unit/bucket/test_bucket.py b/test/unit/bucket/test_bucket.py index f6bb3a04d..06014068f 100644 --- a/test/unit/bucket/test_bucket.py +++ b/test/unit/bucket/test_bucket.py @@ -250,7 +250,11 @@ def setUp(self): self.api = self.get_api() self.simulator = self.api.session.raw_api (self.account_id, self.master_key) = self.simulator.create_account() - self.api.authorize_account('production', self.account_id, self.master_key) + self.api.authorize_account( + application_key_id=self.account_id, + application_key=self.master_key, + realm='production', + ) self.api_url = self.account_info.get_api_url() self.account_auth_token = self.account_info.get_account_auth_token() self.bucket = self.api.create_bucket(self.bucket_name, 'allPublic') @@ -463,7 +467,11 @@ def test_version_by_name_file_lock(self): ] ) - low_perm_api.authorize_account('production', low_perm_key.id_, low_perm_key.application_key) + low_perm_api.authorize_account( + application_key_id=low_perm_key.id_, + application_key=low_perm_key.application_key, + realm='production', + ) low_perm_bucket = low_perm_api.get_bucket_by_name('my-bucket-with-file-lock') file_version = low_perm_bucket.get_file_info_by_name('a') @@ -2680,7 +2688,11 @@ def test_authorize_for_bucket_ensures_cache(self): bucket_id=self.bucket_id, ) - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) # Check whether the bucket fetching performs an API call. with mock.patch.object(self.api, 'list_buckets') as mock_list_buckets: @@ -2702,7 +2714,11 @@ def test_authorize_for_non_existing_bucket(self): ) with self.assertRaises(RestrictedBucketMissing): - self.api.authorize_account('production', key.id_, key.application_key) + self.api.authorize_account( + application_key_id=key.id_, + application_key=key.application_key, + realm='production', + ) class TestDownloadLocalDirectoryIssues(TestCaseWithBucket): diff --git a/test/unit/file_version/test_file_version.py b/test/unit/file_version/test_file_version.py index aad21dd9d..f7c4bfca7 100644 --- a/test/unit/file_version/test_file_version.py +++ b/test/unit/file_version/test_file_version.py @@ -47,7 +47,11 @@ def setUp(self): ) self.raw_api = self.api.session.raw_api (self.application_key_id, self.master_key) = self.raw_api.create_account() - self.api.authorize_account('production', self.application_key_id, self.master_key) + self.api.authorize_account( + application_key_id=self.application_key_id, + application_key=self.master_key, + realm='production', + ) self.bucket = self.api.create_bucket('testbucket', 'allPrivate', is_file_lock_enabled=True) self.file_version = self.bucket.upload_bytes( b'nothing', 'test_file', cache_control='private, max-age=3600' diff --git a/test/unit/replication/conftest.py b/test/unit/replication/conftest.py index 38a1718d2..c91542644 100644 --- a/test/unit/replication/conftest.py +++ b/test/unit/replication/conftest.py @@ -32,7 +32,11 @@ def api() -> B2Api: simulator = api.session.raw_api account_id, master_key = simulator.create_account() - api.authorize_account('production', account_id, master_key) + api.authorize_account( + application_key_id=account_id, + application_key=master_key, + realm='production', + ) # api_url = account_info.get_api_url() # account_auth_token = account_info.get_account_auth_token()1 return api diff --git a/test/unit/v_all/test_api.py b/test/unit/v_all/test_api.py index 782cd9b00..efb2552d3 100644 --- a/test/unit/v_all/test_api.py +++ b/test/unit/v_all/test_api.py @@ -116,7 +116,11 @@ def setUp(self): (self.application_key_id, self.master_key) = self.raw_api.create_account() def _authorize_account(self): - self.api.authorize_account('production', self.application_key_id, self.master_key) + self.api.authorize_account( + realm='production', + application_key_id=self.application_key_id, + application_key=self.master_key, + ) @pytest.mark.apiver(to_ver=1) def test_get_bucket_by_id_up_to_v1(self): diff --git a/test/unit/v_all/test_replication.py b/test/unit/v_all/test_replication.py index ad499f382..8d82721a9 100644 --- a/test/unit/v_all/test_replication.py +++ b/test/unit/v_all/test_replication.py @@ -39,7 +39,11 @@ def setUp(self): self.application_key_id, self.master_key = self.raw_api.create_account() def _authorize_account(self): - self.api.authorize_account('production', self.application_key_id, self.master_key) + self.api.authorize_account( + realm='production', + application_key_id=self.application_key_id, + application_key=self.master_key, + ) @pytest.mark.apiver(from_ver=2) def test_setup_both(self):