diff --git a/.github/workflows/autoupdate-pr.yml b/.github/workflows/autoupdate-pr.yml new file mode 100644 index 000000000..569614720 --- /dev/null +++ b/.github/workflows/autoupdate-pr.yml @@ -0,0 +1,39 @@ +name: Autoupdate PR +on: + push: + branches: + - combined-sdk + +jobs: + update_pull_requests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.DISPATCH_ACCESS_TOKEN }} + + - name: Set up Git + run: | + git config --global user.name 'box-sdk-build' + git config --global user.email 'box-sdk-build@box.com' + + - name: Fetch all branches and tags + run: git fetch --prune --unshallow + + - name: Auto update pull requests + run: | + PR_LIST=$(curl -s -H "Authorization: Bearer ${{ secrets.DISPATCH_ACCESS_TOKEN }}" "https://api.github.com/repos/$GITHUB_REPOSITORY/pulls?state=open" | jq -r '.[] | .head.ref') + for pr_branch in $PR_LIST; do + git checkout "$pr_branch" + if git merge origin/combined-sdk; then + git push + else + # Conflict occurred, resolve by keeping our changes + git checkout --ours . + git add . + git commit -m "Auto resolve conflict by keeping our changes" + git push + fi + done \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1bad2ee08..5cd8b4692 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,11 +1,11 @@ -name: Build +name: Build boxsdk on: pull_request: branches: - - main + - combined-sdk push: branches: - - main + - combined-sdk jobs: build: diff --git a/.github/workflows/create_release.yml b/.github/workflows/create_release.yml deleted file mode 100644 index 6b8bbf08c..000000000 --- a/.github/workflows/create_release.yml +++ /dev/null @@ -1,30 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: Manually triggered release - -# Controls when the workflow will run -on: - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - # Runs a single command using the runners shell - - name: Run a one-line script - run: echo Hello, world! - - # Runs a set of commands using the runners shell - - name: Run a multi-line script - run: | - echo Add other actions to build, - echo test, and deploy your project. diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml new file mode 100644 index 000000000..4c4ad510c --- /dev/null +++ b/.github/workflows/format-check.yml @@ -0,0 +1,24 @@ +name: Format Check + +on: + pull_request: + branches: + - combined-sdk + +jobs: + black: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.8" + + - name: Install Tox + run: pip install tox + + - name: Run Format Check + run: | + tox -e format-check || (echo "Format check failed! Please run 'tox -e format' locally and fix formatting." && exit 1) diff --git a/.github/workflows/integration-tests-gen.yml b/.github/workflows/integration-tests-gen.yml new file mode 100644 index 000000000..8c941f1da --- /dev/null +++ b/.github/workflows/integration-tests-gen.yml @@ -0,0 +1,59 @@ +name: Integration tests box_sdk_gen +on: + pull_request: + branches: + - combined-sdk +jobs: + build: + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + matrix: + python-version: + - '3.8' + - '3.11' + - '3.13' + name: Build with Python ${{ matrix.python-version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e .[dev] + python -m pip install tox-gh-actions + - name: All Tests + if: startsWith(github.head_ref, 'codegen-release') + env: + JWT_CONFIG_BASE_64: ${{ secrets.JWT_CONFIG_BASE_64 }} + ADMIN_USER_ID: ${{ secrets.ADMIN_USER_ID }} + CLIENT_ID: ${{ secrets.CLIENT_ID }} + CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }} + USER_ID: ${{ secrets.USER_ID }} + ENTERPRISE_ID: ${{ secrets.ENTERPRISE_ID }} + BOX_FILE_REQUEST_ID: ${{ secrets.BOX_FILE_REQUEST_ID }} + BOX_EXTERNAL_USER_EMAIL: ${{ secrets.BOX_EXTERNAL_USER_EMAIL }} + BOX_EXTERNAL_USER_ID: ${{ secrets.BOX_EXTERNAL_USER_ID }} + APP_ITEM_ASSOCIATION_FILE_ID: ${{ secrets.APP_ITEM_ASSOCIATION_FILE_ID }} + APP_ITEM_ASSOCIATION_FOLDER_ID: ${{ secrets.APP_ITEM_ASSOCIATION_FOLDER_ID }} + WORKFLOW_FOLDER_ID: ${{ secrets.WORKFLOW_FOLDER_ID }} + APP_ITEM_SHARED_LINK: ${{ secrets.APP_ITEM_SHARED_LINK }} + SLACK_AUTOMATION_USER_ID: ${{ secrets.SLACK_AUTOMATION_USER_ID }} + SLACK_ORG_ID: ${{ secrets.SLACK_ORG_ID }} + SLACK_PARTNER_ITEM_ID: ${{ secrets.SLACK_PARTNER_ITEM_ID }} + run: | + tox -e integration-tests-gen + - name: Smoke Tests + if: "!startsWith(github.head_ref, 'codegen-release')" + env: + JWT_CONFIG_BASE_64: ${{ secrets.JWT_CONFIG_BASE_64 }} + CLIENT_ID: ${{ secrets.CLIENT_ID }} + CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }} + USER_ID: ${{ secrets.USER_ID }} + ENTERPRISE_ID: ${{ secrets.ENTERPRISE_ID }} + run: | + tox -e smoke-tests-gen \ No newline at end of file diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 00f0207c6..2d5c23ed7 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -1,11 +1,8 @@ -name: Integration tests +name: Integration tests boxsdk on: pull_request: branches: - - main - push: - branches: - - main + - combined-sdk jobs: tests: @@ -27,10 +24,19 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install -e ".[dev]" - - name: Run integration tests + - name: All Tests + if: startsWith(github.head_ref, 'codegen-release') + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JWT_CONFIG_BASE_64: ${{ secrets.JWT_CONFIG_BASE_64 }} + ADMIN_USER_ID: ${{ secrets.ADMIN_USER_ID }} run: | tox -e integration-tests + - name: Smoke Tests + if: "!startsWith(github.head_ref, 'codegen-release')" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} JWT_CONFIG_BASE_64: ${{ secrets.JWT_CONFIG_BASE_64 }} ADMIN_USER_ID: ${{ secrets.ADMIN_USER_ID }} + run: | + tox -e smoke-tests diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml deleted file mode 100644 index ef3e1b616..000000000 --- a/.github/workflows/releases.yml +++ /dev/null @@ -1,28 +0,0 @@ -# A GitHub action that notifies the developer -# changelog repository of any new releases. - -name: Notify changelog - -on: - # Only trigger for a full release, - # ignoring pre-releases and drafts - release: - types: - - released - -jobs: - notify: - # This job can run on the latest Ubuntu - # and it should not take more than 3 minutes - runs-on: ubuntu-latest - timeout-minutes: 3 - - steps: - # There's really only 1 step, and i - - name: Notify changelog of new release - uses: peter-evans/repository-dispatch@v1 - with: - token: ${{ secrets.DISPATCH_ACCESS_TOKEN }} - repository: box/box-developer-changelog - event-type: new-release-note - client-payload: '{"ref": "${{ github.ref }}", "repository": "${{github.repository}}", "labels": "sdks,python", "repo_display_name": "Box Python SDK"}' diff --git a/.github/workflows/semantic-pr.yml b/.github/workflows/semantic-pr.yml index cbff636c5..67d2bb018 100644 --- a/.github/workflows/semantic-pr.yml +++ b/.github/workflows/semantic-pr.yml @@ -6,6 +6,8 @@ on: - opened - edited - synchronize + branches: + - combined-sdk jobs: main: diff --git a/.github/workflows/spell-check-lint.yml b/.github/workflows/spell-check-lint.yml index d2cad31c3..cff82f58d 100644 --- a/.github/workflows/spell-check-lint.yml +++ b/.github/workflows/spell-check-lint.yml @@ -3,7 +3,7 @@ on: pull_request_target: types: [opened, synchronize, edited] branches: - - main + - combined-sdk jobs: spellcheck-request-title: runs-on: ubuntu-latest diff --git a/.pylintrc b/.pylintrc index 20209a128..89344a37c 100644 --- a/.pylintrc +++ b/.pylintrc @@ -38,9 +38,8 @@ # C0111 => Missing docstring # W0108 => unnecessary lambda -# W0142 => Used * or ** magic # R0401 => cyclic-import -disable=I, C0111, W0108, W0142, R0921, R0922, W0232, R0401, R0801 +disable=I, C0111, W0108, R0401, R0801 [REPORTS] @@ -50,11 +49,6 @@ disable=I, C0111, W0108, W0142, R0921, R0922, W0232, R0401, R0801 # mypackage.mymodule.MyReporterClass. output-format=text -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - # Tells whether to display a full report or only the messages reports=no @@ -72,9 +66,6 @@ msg-template={module}:{line}:{column}: [{msg_id}({symbol}), {obj}] {msg} [BASIC] -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input - # Regular expression which should only match correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ @@ -260,4 +251,4 @@ int-import-graph= # Exceptions that will emit a warning when being caught. Defaults to # "Exception" -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception diff --git a/boxsdk/auth/oauth2.py b/boxsdk/auth/oauth2.py index 8a72c2b85..d0ea84c44 100644 --- a/boxsdk/auth/oauth2.py +++ b/boxsdk/auth/oauth2.py @@ -331,20 +331,20 @@ def _oauth_exception( Create a BoxOAuthException instance to raise. If the error response is JSON, parse it and include the code and message in the exception. """ - exception_kwargs = dict( - status=network_response.status_code, - url=url, - method='POST', - network_response=network_response, - ) + exception_kwargs = { + 'status': network_response.status_code, + 'url': url, + 'method': 'POST', + 'network_response': network_response, + } if is_json_response(network_response): json_response = network_response.json() exception_kwargs.update( - dict( - code=json_response.get('code') or json_response.get('error'), - message=json_response.get('message') + { + 'code': json_response.get('code') or json_response.get('error'), + 'message': json_response.get('message') or json_response.get('error_description'), - ) + } ) else: exception_kwargs['message'] = network_response.content diff --git a/boxsdk/client/client.py b/boxsdk/client/client.py index 4f2c7b80c..5078d7a25 100644 --- a/boxsdk/client/client.py +++ b/boxsdk/client/client.py @@ -83,10 +83,6 @@ def __init__(self, oauth: 'OAuth2', session: Session = None): The session object to use. If None is provided then an instance of :class:`AuthorizedSession` will be used. """ super().__init__() - warn( - 'Package \'boxsdk\' is going to be deprecated soon. Please use \'box-sdk-gen\' instead.', - DeprecationWarning, - ) self._oauth = oauth if session is not None: self._session = session @@ -1336,7 +1332,6 @@ def get_url(self, endpoint: str, *args: Any) -> str: :param args: Additional parts of the endpoint URL. """ - # pylint:disable=no-self-use return self._session.get_url(endpoint, *args) def device_pinner(self, device_pin_id: str) -> 'DevicePinner': diff --git a/boxsdk/object/base_api_json_object.py b/boxsdk/object/base_api_json_object.py index 005ff72ad..fc2d755a3 100644 --- a/boxsdk/object/base_api_json_object.py +++ b/boxsdk/object/base_api_json_object.py @@ -51,14 +51,14 @@ def __init__(cls, name, bases, attrs): super().__init__(name, bases, attrs) item_type = attrs.get('_item_type', None) if item_type is not None: - Translator._default_translator.register( - item_type, cls - ) # pylint:disable=protected-access,no-member + # pylint:disable=protected-access,no-member + Translator._default_translator.register(item_type, cls) # Some types have - in them instead of _ in the API. if "-" in item_type: + # pylint:disable=protected-access,no-member Translator._default_translator.register( item_type.replace("-", "_"), cls - ) # pylint:disable=protected-access,no-member + ) class BaseAPIJSONObject(metaclass=BaseAPIJSONObjectMeta): @@ -144,9 +144,8 @@ def _untranslate(cls, value: Any) -> Iterable: A dictionary containing the untranslated object. """ if isinstance(value, BaseAPIJSONObject): - return cls._untranslate( - value._response_object - ) # pylint:disable=protected-access + # pylint:disable=protected-access + return cls._untranslate(value._response_object) if isinstance(value, dict): return {k: cls._untranslate(v) for (k, v) in value.items()} if isinstance(value, list): diff --git a/boxsdk/object/upload_session.py b/boxsdk/object/upload_session.py index fac5aef0a..00de7910a 100644 --- a/boxsdk/object/upload_session.py +++ b/boxsdk/object/upload_session.py @@ -106,8 +106,10 @@ def upload_part_bytes( part_content_sha1 = sha1.digest() range_end = min( - offset + self.part_size - 1, total_size - 1 - ) # pylint:disable=no-member + # pylint:disable=no-member + offset + self.part_size - 1, + total_size - 1, + ) headers = { 'Content-Type': 'application/octet-stream', 'Digest': f'SHA={base64.b64encode(part_content_sha1).decode("utf-8")}', diff --git a/boxsdk/session/session.py b/boxsdk/session/session.py index f16a22980..a73c27434 100644 --- a/boxsdk/session/session.py +++ b/boxsdk/session/session.py @@ -177,21 +177,20 @@ def get_url(self, endpoint: str, *args: Any) -> str: :param args: Additional parts of the endpoint URL. """ - # pylint:disable=no-self-use url = [f'{self._api_config.BASE_API_URL}/{endpoint}'] url.extend([f'/{x}' for x in args]) return ''.join(url) def get_constructor_kwargs(self) -> dict: - return dict( - network_layer=self._network_layer, - translator=self._translator, - default_network_request_kwargs=self._default_network_request_kwargs.copy(), - api_config=self._api_config, - client_config=self._client_config, - proxy_config=self._proxy_config, - default_headers=self._default_headers.copy(), - ) + return { + 'network_layer': self._network_layer, + 'translator': self._translator, + 'default_network_request_kwargs': self._default_network_request_kwargs.copy(), + 'api_config': self._api_config, + 'client_config': self._client_config, + 'proxy_config': self._proxy_config, + 'default_headers': self._default_headers.copy(), + } def as_user(self, user: 'User') -> 'Session': """ diff --git a/boxsdk/util/translator.py b/boxsdk/util/translator.py index 00e61e4d5..796a9b2df 100644 --- a/boxsdk/util/translator.py +++ b/boxsdk/util/translator.py @@ -180,6 +180,5 @@ def translate(self, session: 'Session', response_object: dict) -> Any: return translated_obj -Translator._default_translator = Translator( - extend_default_translator=False -) # pylint:disable=protected-access +# pylint:disable=protected-access +Translator._default_translator = Translator(extend_default_translator=False) diff --git a/pytest.ini b/pytest.ini index 0cd3c7748..160d0c434 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict --showlocals -r a --tb=long --ignore=test/integration_new/ +addopts = --strict --showlocals -r a --tb=long --ignore=test/boxsdk/integration_new/ --ignore=test/box_sdk_gen/ xfail_strict = True junit_suite_name = boxsdk testpaths = test/ diff --git a/setup.py b/setup.py index 1628e595f..c7c210b16 100644 --- a/setup.py +++ b/setup.py @@ -54,6 +54,7 @@ def main(): install_requires = [ 'attrs>=17.3.0', 'urllib3', + 'dataclasses', 'requests<3', 'requests-toolbelt<2', 'python-dateutil', @@ -71,6 +72,7 @@ def main(): 'pytest-timeout<3.0.0', 'pytest-cov<6', 'pytest-lazy-fixtures<2', + 'pytest-rerunfailures', 'pytz', 'urllib3<2', ] @@ -91,6 +93,7 @@ def main(): version=version, description='Official Box Python SDK', long_description_content_type="text/markdown", + # pylint:disable=consider-using-with long_description=open( join(base_dir, 'README.md'), encoding='utf-8' ).read(), # pylint:disable=consider-using-with diff --git a/test/boxsdk/functional/conftest.py b/test/boxsdk/functional/conftest.py index 7407cf30e..84203cb4a 100644 --- a/test/boxsdk/functional/conftest.py +++ b/test/boxsdk/functional/conftest.py @@ -46,7 +46,9 @@ def box_oauth(client_id, client_secret, user_login, unauthorized_session): session=unauthorized_session, ) url, _ = oauth2.get_authorization_url('http://localhost') - form = requests.get(url + '&box_login=' + user_login).content.decode('utf-8') + form = requests.get(url + '&box_login=' + user_login, timeout=10).content.decode( + 'utf-8' + ) form_action = re.search('action="([^"]*)"', form).group(1) auth_response = requests.post( form_action, @@ -56,6 +58,7 @@ def box_oauth(client_id, client_secret, user_login, unauthorized_session): 'client_id': client_id, 'client_secret': client_secret, }, + timeout=10, ) redirect_url = auth_response.headers['Location'] query_string = parse.urlparse(redirect_url).query diff --git a/test/boxsdk/integration/conftest.py b/test/boxsdk/integration/conftest.py index 642ba843a..53ba45d80 100644 --- a/test/boxsdk/integration/conftest.py +++ b/test/boxsdk/integration/conftest.py @@ -5,7 +5,7 @@ from boxsdk.auth.oauth2 import OAuth2 from boxsdk.session.session import Session, AuthorizedSession -from .mock_network import MockNetwork +from test.boxsdk.integration.mock_network import MockNetwork @pytest.fixture() diff --git a/test/boxsdk/integration_new/object/legal_hold_policy_itest.py b/test/boxsdk/integration_new/object/legal_hold_policy_itest.py index f3bbb4f20..6bdebef11 100644 --- a/test/boxsdk/integration_new/object/legal_hold_policy_itest.py +++ b/test/boxsdk/integration_new/object/legal_hold_policy_itest.py @@ -1,6 +1,5 @@ from test.boxsdk.integration_new import util from test.boxsdk.integration_new import CLIENT -from boxsdk.exception import BoxAPIException def test_create_legal_hold_policy(): diff --git a/test/boxsdk/unit/auth/test_ccg_auth.py b/test/boxsdk/unit/auth/test_ccg_auth.py index edbefbace..862677ace 100644 --- a/test/boxsdk/unit/auth/test_ccg_auth.py +++ b/test/boxsdk/unit/auth/test_ccg_auth.py @@ -2,7 +2,9 @@ from datetime import datetime, timedelta from unittest import mock from unittest.mock import Mock, MagicMock, call -from test.unit.object.conftest import mock_user # pylint:disable=unused-import + +# pylint:disable=unused-import +from test.boxsdk.unit.object.conftest import mock_user import pytz import pytest @@ -57,9 +59,8 @@ def ccg_auth(client_id, client_secret, ccg_user_id, ccg_enterprise_id) -> CCGAut user=ccg_user_id, enterprise_id=ccg_enterprise_id, ) - auth._session.get_retry_after_time = MagicMock( - return_value=0 - ) # pylint:disable=protected-access + # pylint:disable=protected-access + auth._session.get_retry_after_time = MagicMock(return_value=0) return auth @@ -97,8 +98,10 @@ def test_successful_create_ccg_auth_object_and_normalize_user_id( user=user, ) assert ( - auth._user_id == expected_normalized_user_id - ) # pylint:disable=protected-access + # pylint:disable=protected-access + auth._user_id + == expected_normalized_user_id + ) def test_throw_type_error_when_invalid_user_object_provided( @@ -154,8 +157,10 @@ def test_authenticate_enterprise(ccg_auth, enterprise_id, mock_enterprise_id): mock_enterprise_id, ENTERPRISE_SUBJECT_TYPE, None ) assert ( - ccg_auth._enterprise_id == mock_enterprise_id - ) # pylint:disable=protected-access + # pylint:disable=protected-access + ccg_auth._enterprise_id + == mock_enterprise_id + ) assert ccg_auth._user_id is None # pylint:disable=protected-access diff --git a/test/boxsdk/unit/auth/test_jwt_auth.py b/test/boxsdk/unit/auth/test_jwt_auth.py index 04401a39f..e22d8b8b5 100644 --- a/test/boxsdk/unit/auth/test_jwt_auth.py +++ b/test/boxsdk/unit/auth/test_jwt_auth.py @@ -82,13 +82,13 @@ def successful_token_response(successful_token_mock, successful_token_json_respo def test_jwt_auth_init_raises_type_error_unless_exactly_one_of_rsa_private_key_file_or_data_is_given( key_file, key_data, rsa_private_key_bytes ): - kwargs = dict( - rsa_private_key_data=rsa_private_key_bytes, - client_id=None, - client_secret=None, - jwt_key_id=None, - enterprise_id=None, - ) + kwargs = { + 'rsa_private_key_data': rsa_private_key_bytes, + 'client_id': None, + 'client_secret': None, + 'jwt_key_id': None, + 'enterprise_id': None, + } JWTAuth(**kwargs) kwargs.update(rsa_private_key_file_sys_path=key_file, rsa_private_key_data=key_data) with pytest.raises(TypeError): @@ -100,13 +100,13 @@ def test_jwt_auth_init_raises_type_error_unless_exactly_one_of_rsa_private_key_f def test_jwt_auth_init_raises_type_error_if_rsa_private_key_data_has_unexpected_type( key_data, rsa_private_key_bytes ): - kwargs = dict( - rsa_private_key_data=rsa_private_key_bytes, - client_id=None, - client_secret=None, - jwt_key_id=None, - enterprise_id=None, - ) + kwargs = { + 'rsa_private_key_data': rsa_private_key_bytes, + 'client_id': None, + 'client_secret': None, + 'jwt_key_id': None, + 'enterprise_id': None, + } JWTAuth(**kwargs) kwargs.update(rsa_private_key_data=key_data) with pytest.raises(TypeError): diff --git a/test/boxsdk/unit/auth/test_oauth2.py b/test/boxsdk/unit/auth/test_oauth2.py index 4c10ed88f..a7ae31d4c 100644 --- a/test/boxsdk/unit/auth/test_oauth2.py +++ b/test/boxsdk/unit/auth/test_oauth2.py @@ -466,7 +466,7 @@ def test_context_manager_fails_after_close(client_id, client_secret, mock_box_se @pytest.mark.parametrize( - ('close_args', 'close_kwargs'), [((), {}), ((True,), {}), ((), dict(revoke=True))] + ('close_args', 'close_kwargs'), [((), {}), ((True,), {}), ((), {'revoke': True})] ) def test_revoke_on_close( client_id, client_secret, access_token, mock_box_session, close_args, close_kwargs @@ -498,7 +498,7 @@ def test_auth_object_is_closed_even_if_revoke_fails( @pytest.mark.parametrize( - ('close_args', 'close_kwargs'), [((False,), {}), ((), dict(revoke=False))] + ('close_args', 'close_kwargs'), [((False,), {}), ((), {'revoke': False})] ) def test_revoke_on_close_can_be_skipped( client_id, client_secret, access_token, mock_box_session, close_args, close_kwargs @@ -522,7 +522,7 @@ def test_revoke_on_close_can_be_skipped( (MyError, BoxOAuthException(status=500), MyError), ], ) -@pytest.mark.parametrize('close_kwargs', [{}, dict(revoke=False), dict(revoke=True)]) +@pytest.mark.parametrize('close_kwargs', [{}, {'revoke': False}, {'revoke': True}]) def test_context_manager_reraises_first_exception_after_close( client_id, client_secret, @@ -543,7 +543,7 @@ def test_context_manager_reraises_first_exception_after_close( mock_close.assert_called_once_with(**close_kwargs) -@pytest.mark.parametrize('close_kwargs', [{}, dict(revoke=False), dict(revoke=True)]) +@pytest.mark.parametrize('close_kwargs', [{}, {'revoke': False}, {'revoke': True}]) def test_context_manager_skips_revoke_on_base_exception( client_id, client_secret, mock_box_session, close_kwargs ): diff --git a/test/boxsdk/unit/conftest.py b/test/boxsdk/unit/conftest.py index d7622bbd8..a1615fda1 100644 --- a/test/boxsdk/unit/conftest.py +++ b/test/boxsdk/unit/conftest.py @@ -36,8 +36,8 @@ def default_translator(original_default_translator): Translator._default_translator = translator # pylint:disable=protected-access yield translator finally: - Translator._default_translator = ( - original_default_translator # pylint:disable=protected-access + Translator._default_translator = ( # pylint:disable=protected-access + original_default_translator ) diff --git a/test/boxsdk/unit/network/test_network.py b/test/boxsdk/unit/network/test_network.py index 6fa4b55d5..85678b86e 100644 --- a/test/boxsdk/unit/network/test_network.py +++ b/test/boxsdk/unit/network/test_network.py @@ -198,7 +198,7 @@ def network_response_constructor(self): def test_network_logs_requests( make_network_request, http_verb, test_url, network, logger ): - kwargs = dict(custom_kwarg='foo') + kwargs = {'custom_kwarg': 'foo'} make_network_request(network, **kwargs) logger.info.assert_any_call( network.REQUEST_FORMAT, diff --git a/test/boxsdk/unit/object/test_user.py b/test/boxsdk/unit/object/test_user.py index 2dfdab281..5e10f179e 100644 --- a/test/boxsdk/unit/object/test_user.py +++ b/test/boxsdk/unit/object/test_user.py @@ -159,8 +159,10 @@ def test_add_email_alias_returns_the_correct_email_alias_object( mock_box_session.post.assert_called_once_with(expected_url, data=expected_body) assert isinstance(new_email_alias, EmailAlias) assert ( - new_email_alias._session == mock_box_session - ) # pylint: disable=protected-access + # pylint: disable=protected-access + new_email_alias._session + == mock_box_session + ) assert new_email_alias.object_id == '1234' diff --git a/test/boxsdk/unit/pagination/test_limit_offset_based_object_collection.py b/test/boxsdk/unit/pagination/test_limit_offset_based_object_collection.py index 8938fb8d5..d0285acdb 100644 --- a/test/boxsdk/unit/pagination/test_limit_offset_based_object_collection.py +++ b/test/boxsdk/unit/pagination/test_limit_offset_based_object_collection.py @@ -53,7 +53,6 @@ def mock_items_side_effect(_, params): @pytest.fixture() def mock_session_with_bogus_limit(self, mock_session, mock_items_response): """Baseclass override.""" - # pylint:disable=no-self-use def mock_items_side_effect(_, params): limit = 0 @@ -121,8 +120,10 @@ def test_object_collection_sets_limit_to_returned_value_if_originally_none( object_collection = self._object_collection_instance(mock_session) object_collection.next() assert ( - object_collection._limit == self.DEFAULT_LIMIT - ) # pylint:disable=protected-access + # pylint:disable=protected-access + object_collection._limit + == self.DEFAULT_LIMIT + ) def test_object_collection_sets_limit_to_returned_value_if_originally_too_high( self, mock_session @@ -132,8 +133,10 @@ def test_object_collection_sets_limit_to_returned_value_if_originally_too_high( ) object_collection.next() assert ( - object_collection._limit == self.DEFAULT_LIMIT - ) # pylint:disable=protected-access + # pylint:disable=protected-access + object_collection._limit + == self.DEFAULT_LIMIT + ) def test_box_returning_bogus_limit_raises_runtime_error( self, mock_session_with_bogus_limit, entries diff --git a/test/boxsdk/unit/util/test_api_call_decorator.py b/test/boxsdk/unit/util/test_api_call_decorator.py index 741461318..a47d1d91c 100644 --- a/test/boxsdk/unit/util/test_api_call_decorator.py +++ b/test/boxsdk/unit/util/test_api_call_decorator.py @@ -83,12 +83,14 @@ class CloneableSubclass2(Cloneable): pass with pytest.raises(TypeError): + # pylint: disable=C2801 api_call_method.__get__(mock_cloneable, CloneableSubclass2) def test_api_call_decorated_method_returns_itself_when_bound_to_none( api_call_method, cloneable_subclass_with_api_call_method ): + # pylint: disable=C2801 assert api_call_method.__get__(None, Cloneable) is api_call_method assert not hasattr(api_call_method.__get__(None, Cloneable), '__self__') assert cloneable_subclass_with_api_call_method.api_call_method is api_call_method @@ -98,6 +100,7 @@ def test_api_call_decorated_method_returns_itself_when_bound_to_none( def test_api_call_decorated_method_binds_to_instance(mock_cloneable, api_call_method): + # pylint: disable=C2801 assert api_call_method.__get__(mock_cloneable, Cloneable) is not api_call_method assert api_call_method.__get__(mock_cloneable, Cloneable).__self__ is mock_cloneable assert mock_cloneable.api_call_method is not api_call_method diff --git a/test/boxsdk/unit/util/test_enum.py b/test/boxsdk/unit/util/test_enum.py index 58e49c483..8136bf006 100644 --- a/test/boxsdk/unit/util/test_enum.py +++ b/test/boxsdk/unit/util/test_enum.py @@ -139,9 +139,8 @@ def test_getitem_raises_key_error_for_non_member_names( EnumBaseWithSubclassesDefined, enum_member_value ): with pytest.raises(KeyError): - EnumBaseWithSubclassesDefined[ - enum_member_value - ] # pylint:disable=pointless-statement + # pylint:disable=pointless-statement + EnumBaseWithSubclassesDefined[enum_member_value] def test_getattr(EnumBaseWithSubclassesDefined, enum_member_name, enum_instance): diff --git a/test/boxsdk/unit/util/test_text_enum.py b/test/boxsdk/unit/util/test_text_enum.py index ad1392a23..4c1d4a83b 100644 --- a/test/boxsdk/unit/util/test_text_enum.py +++ b/test/boxsdk/unit/util/test_text_enum.py @@ -6,9 +6,7 @@ class MockTextEnum(TextEnum): def test_text_enum_repr_is_value(): - assert ( - MockTextEnum.MEMBER.__repr__() == MockTextEnum.MEMBER.value - ) # pylint:disable=no-member + assert repr(MockTextEnum.MEMBER) == MockTextEnum.MEMBER.value def test_text_enum_str_is_value(): diff --git a/test/boxsdk/unit/util/test_translator.py b/test/boxsdk/unit/util/test_translator.py index 51e490109..31b499e0a 100644 --- a/test/boxsdk/unit/util/test_translator.py +++ b/test/boxsdk/unit/util/test_translator.py @@ -79,8 +79,10 @@ def test_translator_converts_response_to_correct_type(response_type): def test_default_translator(): assert isinstance( - Translator._default_translator, Translator - ) # pylint:disable=protected-access + # pylint:disable=protected-access + Translator._default_translator, + Translator, + ) @pytest.mark.parametrize( diff --git a/tox.ini b/tox.ini index 12ca9d759..e62526c34 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,6 @@ [tox] envlist = - pycodestyle, pylint, py38, py39, @@ -14,11 +13,12 @@ envlist = py312, py313, coverage, - integration-tests + integration-tests, + integration-tests-gen [gh-actions] python = - 3.8: py38, pycodestyle, pylint + 3.8: py38, pylint 3.9: py39 3.10: py310 3.11: py311 @@ -30,13 +30,6 @@ commands = pytest {posargs} --disable-pytest-warnings deps = -rrequirements-test.txt -[testenv:pycodestyle] -commands = - pycodestyle --ignore=E501,W292 boxsdk setup.py - pycodestyle --ignore=E501,W292 test -deps = - pycodestyle - [testenv:pylint] commands = pylint --rcfile=.pylintrc boxsdk setup.py @@ -46,14 +39,34 @@ deps = pylint -rrequirements-test.txt +[testenv:format] +commands = + black --skip-string-normalization . +deps = black + +[testenv:format-check] +commands = + black --skip-string-normalization --check . +deps = black + + [testenv:coverage] basepython = python3.13 commands = - py.test --cov boxsdk --cov-report term-missing test/unit test/integration + py.test --cov boxsdk --cov-report term-missing test/boxsdk/unit test/boxsdk/integration deps = coverage -rrequirements-test.txt +[testenv:coverage-gen] +basepython = python3.13 +commands = + py.test --cov box_sdk_gen --cov-report term-missing test/box_sdk_gen/test/ +deps = + coverage + -rrequirements-test.txt +passenv = JWT_CONFIG_BASE_64,ADMIN_USER_ID,CLIENT_ID,CLIENT_SECRET,USER_ID,ENTERPRISE_ID,BOX_FILE_REQUEST_ID,BOX_EXTERNAL_USER_EMAIL,BOX_EXTERNAL_USER_ID,WORKFLOW_FOLDER_ID,APP_ITEM_ASSOCIATION_FILE_ID,APP_ITEM_ASSOCIATION_FOLDER_ID,APP_ITEM_SHARED_LINK,SLACK_AUTOMATION_USER_ID,SLACK_ORG_ID,SLACK_PARTNER_ITEM_ID + [testenv:py311-build] description = Build the source and binary wheel packages for distribution. pypi_dist_dir = {toxinidir}/pypi-dist @@ -84,5 +97,33 @@ deps = passenv = JWT_CONFIG_BASE_64,ADMIN_USER_ID commands = - pytest {toxinidir}/test/integration_new {posargs} --disable-pytest-warnings + pytest {toxinidir}/test/boxsdk/integration_new {posargs} --disable-pytest-warnings --reruns 2 +deps = -rrequirements-test.txt + +[testenv:integration-tests-gen] +passenv = JWT_CONFIG_BASE_64,ADMIN_USER_ID,CLIENT_ID,CLIENT_SECRET,USER_ID,ENTERPRISE_ID,BOX_FILE_REQUEST_ID,BOX_EXTERNAL_USER_EMAIL,BOX_EXTERNAL_USER_ID,WORKFLOW_FOLDER_ID,APP_ITEM_ASSOCIATION_FILE_ID,APP_ITEM_ASSOCIATION_FOLDER_ID,APP_ITEM_SHARED_LINK,SLACK_AUTOMATION_USER_ID,SLACK_ORG_ID,SLACK_PARTNER_ITEM_ID +commands = + pytest {toxinidir}/test/box_sdk_gen/test {posargs} --disable-pytest-warnings --reruns 2 +deps = -rrequirements-test.txt +allowlist_externals = pytest + +[testenv:smoke-tests] +passenv = JWT_CONFIG_BASE_64,ADMIN_USER_ID +commands = + pytest \ + {toxinidir}/test/boxsdk/integration_new/object/file_itest.py \ + {toxinidir}/test/boxsdk/integration_new/object/folder_itest.py \ + --disable-pytest-warnings --reruns 2 +deps = -rrequirements-test.txt + +[testenv:smoke-tests-gen] +passenv = JWT_CONFIG_BASE_64,ADMIN_USER_ID,CLIENT_ID,CLIENT_SECRET,USER_ID,ENTERPRISE_ID,BOX_FILE_REQUEST_ID,BOX_EXTERNAL_USER_EMAIL,BOX_EXTERNAL_USER_ID,WORKFLOW_FOLDER_ID,APP_ITEM_ASSOCIATION_FILE_ID,APP_ITEM_ASSOCIATION_FOLDER_ID,APP_ITEM_SHARED_LINK,SLACK_AUTOMATION_USER_ID,SLACK_ORG_ID,SLACK_PARTNER_ITEM_ID +commands = + pytest \ + {toxinidir}/test/box_sdk_gen/test/auth.py \ + {toxinidir}/test/box_sdk_gen/test/files.py \ + {toxinidir}/test/box_sdk_gen/test/downloads.py \ + {toxinidir}/test/box_sdk_gen/test/uploads.py \ + --disable-pytest-warnings --reruns 2 deps = -rrequirements-test.txt +allowlist_externals = pytest