From 7b04d0159c79de3245c560513debc1498964c5dd Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 23 Dec 2024 22:55:13 +0000 Subject: [PATCH] chore: move away from setup.py (#1623) --- .github/ISSUE_TEMPLATE/01_question.md | 2 +- .github/ISSUE_TEMPLATE/04_bug.md | 2 +- .github/issue_template.md | 2 +- .github/workflows/ci-build.yml | 13 +- integration_tests/helpers.py | 2 +- integration_tests/web/test_issue_1143.py | 2 +- integration_tests/web/test_issue_770.py | 2 +- scripts/codegen.py | 102 ++++++++++ scripts/run_integration_tests.sh | 7 +- scripts/run_unit_tests.sh | 5 +- scripts/run_validation.sh | 13 +- setup.py | 240 ----------------------- slack/web/async_client.py | 3 +- slack_sdk/web/async_client.py | 3 +- slack_sdk/web/legacy_client.py | 3 +- 15 files changed, 137 insertions(+), 264 deletions(-) create mode 100644 scripts/codegen.py delete mode 100644 setup.py diff --git a/.github/ISSUE_TEMPLATE/01_question.md b/.github/ISSUE_TEMPLATE/01_question.md index 086e48d6f..66971561d 100644 --- a/.github/ISSUE_TEMPLATE/01_question.md +++ b/.github/ISSUE_TEMPLATE/01_question.md @@ -30,7 +30,7 @@ sw_vers && uname -v # or `ver` #### Steps to reproduce: -(Share the commands to run, source code, and project settings (e.g., setup.py)) +(Share the commands to run, source code, and project settings (e.g., pyproject.toml)) 1. 2. diff --git a/.github/ISSUE_TEMPLATE/04_bug.md b/.github/ISSUE_TEMPLATE/04_bug.md index f5297de97..fd1fc5718 100644 --- a/.github/ISSUE_TEMPLATE/04_bug.md +++ b/.github/ISSUE_TEMPLATE/04_bug.md @@ -30,7 +30,7 @@ sw_vers && uname -v # or `ver` #### Steps to reproduce: -(Share the commands to run, source code, and project settings (e.g., setup.py)) +(Share the commands to run, source code, and project settings (e.g., pyproject.toml)) 1. 2. diff --git a/.github/issue_template.md b/.github/issue_template.md index 4c61970db..33b393830 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -22,7 +22,7 @@ sw_vers && uname -v # or `ver` #### Steps to reproduce: -(Share the commands to run, source code, and project settings (e.g., setup.py)) +(Share the commands to run, source code, and project settings (e.g., pyproject.toml)) 1. 2. diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 6d206c238..5d483c1a3 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -39,22 +39,21 @@ jobs: pip install -U pip setuptools wheel pip install -r requirements/testing.txt pip install -r requirements/optional.txt - - name: Run codegen - run: | - python setup.py codegen - name: Run validation (black/flake8/pytest) run: | - python setup.py validate + black --check slack/ slack_sdk/ tests/ integration_tests/ + flake8 slack/ slack_sdk/ + PYTHONPATH=$PWD:$PYTHONPATH pytest --cov-report=xml --cov=slack_sdk/ tests/ - name: Run tests for SQLAlchemy v1.4 (backward-compatibility) run: | # Install v1.4 for testing pip install "SQLAlchemy>=1.4,<2" - python setup.py unit_tests --test-target tests/slack_sdk/oauth/installation_store/test_sqlalchemy.py && \ - python setup.py unit_tests --test-target tests/slack_sdk/oauth/state_store/test_sqlalchemy.py + PYTHONPATH=$PWD:$PYTHONPATH pytest tests/slack_sdk/oauth/installation_store/test_sqlalchemy.py + PYTHONPATH=$PWD:$PYTHONPATH pytest tests/slack_sdk/oauth/state_store/test_sqlalchemy.py - name: Run codecov (only with latest supported version) if: startsWith(matrix.python-version, '3.13') uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - # python setup.py validate generates the coverage file + # Run validation generates the coverage file files: ./coverage.xml diff --git a/integration_tests/helpers.py b/integration_tests/helpers.py index 8a096de96..983df01b5 100644 --- a/integration_tests/helpers.py +++ b/integration_tests/helpers.py @@ -21,6 +21,6 @@ def is_not_specified() -> bool: module = inspect.getmodule(frame[0]) filepath: str = module.__file__ - # python setup.py integration_tests --test-target=web/test_issue_560.py + # ./scripts/run_integration_tests.sh web/test_issue_560.py test_target: str = sys.argv[1] # e.g., web/test_issue_560.py return not test_target or not filepath.endswith(test_target) diff --git a/integration_tests/web/test_issue_1143.py b/integration_tests/web/test_issue_1143.py index 7cfd7a934..850651439 100644 --- a/integration_tests/web/test_issue_1143.py +++ b/integration_tests/web/test_issue_1143.py @@ -12,7 +12,7 @@ class TestWebClient(unittest.TestCase): """Runs integration tests with real Slack API export SLACK_SDK_TEST_BOT_TOKEN=xoxb-xxx - python setup.py run_integration_tests --test-target integration_tests/web/test_issue_1143.py + ./scripts/run_integration_tests.sh integration_tests/web/test_issue_1143.py https://github.com/slackapi/python-slack-sdk/issues/1143 """ diff --git a/integration_tests/web/test_issue_770.py b/integration_tests/web/test_issue_770.py index d9cb78aac..d92395549 100644 --- a/integration_tests/web/test_issue_770.py +++ b/integration_tests/web/test_issue_770.py @@ -16,7 +16,7 @@ class TestWebClient(unittest.TestCase): export SLACK_SDK_TEST_BOT_TOKEN=xoxb-xxx export SLACK_SDK_TEST_WEB_TEST_CHANNEL_ID=C111 - python setup.py run_integration_tests --test-target integration_tests/web/test_issue_770.py + ./scripts/run_integration_tests.sh integration_tests/web/test_issue_770.py https://github.com/slackapi/python-slack-sdk/issues/770 """ diff --git a/scripts/codegen.py b/scripts/codegen.py new file mode 100644 index 000000000..f58de8bd1 --- /dev/null +++ b/scripts/codegen.py @@ -0,0 +1,102 @@ +import sys +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("-p", "--path", help="Path to the project source code.", type=str) +if len(sys.argv) == 1: + parser.print_help(sys.stderr) + sys.exit(1) +args = parser.parse_args() + +header = ( + "# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" + "#\n" + "# *** DO NOT EDIT THIS FILE ***\n" + "#\n" + "# 1) Modify slack_sdk/web/client.py\n" + "# 2) Run `python scripts/codegen.py`\n" + "# 3) Run `black slack_sdk/`\n" + "#\n" + "# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" + "\n" +) + +with open(f"{args.path}/slack_sdk/web/client.py", "r") as original: + source = original.read() + import re + + async_source = header + source + async_source = re.sub(" def ", " async def ", async_source) + async_source = re.sub("from asyncio import Future\n", "", async_source) + async_source = re.sub(r"return self.api_call\(", "return await self.api_call(", async_source) + async_source = re.sub("-> SlackResponse", "-> AsyncSlackResponse", async_source) + async_source = re.sub( + "from .base_client import BaseClient, SlackResponse", + "from .async_base_client import AsyncBaseClient, AsyncSlackResponse", + async_source, + ) + # from slack_sdk import WebClient + async_source = re.sub( + r"class WebClient\(BaseClient\):", + "class AsyncWebClient(AsyncBaseClient):", + async_source, + ) + async_source = re.sub( + "from slack_sdk import WebClient", + "from slack_sdk.web.async_client import AsyncWebClient", + async_source, + ) + async_source = re.sub(r"= WebClient\(", "= AsyncWebClient(", async_source) + async_source = re.sub( + r" self.files_getUploadURLExternal\(", + " await self.files_getUploadURLExternal(", + async_source, + ) + async_source = re.sub( + r" self._upload_file\(", + " await self._upload_file(", + async_source, + ) + async_source = re.sub( + r" self.files_completeUploadExternal\(", + " await self.files_completeUploadExternal(", + async_source, + ) + async_source = re.sub( + r" self.files_info\(", + " await self.files_info(", + async_source, + ) + async_source = re.sub( + "_attach_full_file_metadata", + "_attach_full_file_metadata_async", + async_source, + ) + async_source = re.sub( + r" _attach_full_file_metadata_async\(", + " await _attach_full_file_metadata_async(", + async_source, + ) + with open(f"{args.path}/slack_sdk/web/async_client.py", "w") as output: + output.write(async_source) + + legacy_source = header + "from asyncio import Future\n" + source + legacy_source = re.sub("-> SlackResponse", "-> Union[Future, SlackResponse]", legacy_source) + legacy_source = re.sub( + "from .base_client import BaseClient, SlackResponse", + "from .legacy_base_client import LegacyBaseClient, SlackResponse", + legacy_source, + ) + legacy_source = re.sub( + r"class WebClient\(BaseClient\):", + "class LegacyWebClient(LegacyBaseClient):", + legacy_source, + ) + legacy_source = re.sub( + "from slack_sdk import WebClient", + "from slack_sdk.web.legacy_client import LegacyWebClient", + legacy_source, + ) + legacy_source = re.sub(r"= WebClient\(", "= LegacyWebClient(", legacy_source) + with open(f"{args.path}/slack_sdk/web/legacy_client.py", "w") as output: + output.write(legacy_source) diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh index df1db4cf3..8bf922c7b 100755 --- a/scripts/run_integration_tests.sh +++ b/scripts/run_integration_tests.sh @@ -12,7 +12,8 @@ pip install -U pip pip install -r requirements/testing.txt \ -r requirements/optional.txt -python setup.py codegen +echo "Generating code ..." && python scripts/codegen.py --path . +echo "Running black (code formatter) ..." && black slack_sdk/ -test_target="${1:-integration_tests/}" -python setup.py integration_tests --test-target $test_target +test_target="${1:-tests/integration_tests/}" +PYTHONPATH=$PWD:$PYTHONPATH pytest $test_target diff --git a/scripts/run_unit_tests.sh b/scripts/run_unit_tests.sh index 35c3bfdad..e827dc32a 100755 --- a/scripts/run_unit_tests.sh +++ b/scripts/run_unit_tests.sh @@ -12,7 +12,8 @@ pip install -U pip pip install -r requirements/testing.txt \ -r requirements/optional.txt -python setup.py codegen +echo "Generating code ..." && python scripts/codegen.py --path . +echo "Running black (code formatter) ..." && black slack_sdk/ test_target="${1:-tests/}" -python setup.py unit_tests --test-target $test_target +PYTHONPATH=$PWD:$PYTHONPATH pytest $test_target diff --git a/scripts/run_validation.sh b/scripts/run_validation.sh index 9248e7509..cbbec0355 100755 --- a/scripts/run_validation.sh +++ b/scripts/run_validation.sh @@ -1,5 +1,6 @@ #!/bin/bash -# ./scripts/run_validation.sh +# all: ./scripts/run_validation.sh +# single: ./scripts/run_validation.sh tests/slack_sdk_async/web/test_web_client_coverage.py set -e @@ -10,5 +11,11 @@ pip install -U pip setuptools wheel pip install -r requirements/testing.txt \ -r requirements/optional.txt -python setup.py codegen -python setup.py validate +echo "Generating code ..." && python scripts/codegen.py --path . +echo "Running black (code formatter) ..." && black slack_sdk/ + +black --check slack/ slack_sdk/ tests/ integration_tests/ +flake8 slack/ slack_sdk/ + +test_target="${1:-tests/}" +PYTHONPATH=$PWD:$PYTHONPATH pytest --cov-report=xml --cov=slack_sdk/ $test_target diff --git a/setup.py b/setup.py deleted file mode 100644 index 64217ac07..000000000 --- a/setup.py +++ /dev/null @@ -1,240 +0,0 @@ -# -*- coding: utf-8 -*- -import os -import subprocess -import sys - -from setuptools import setup, Command - -################################################################################### -# Legacy package configuration, prefer pyproject.toml over setup.cfg or setup.py # -################################################################################### - -here = os.path.abspath(os.path.dirname(__file__)) - - -class BaseCommand(Command): - user_options = [] - - @staticmethod - def status(s): - """Prints things in bold.""" - print("\033[1m{0}\033[0m".format(s)) - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def _run(self, s, command): - try: - self.status(s + "\n" + " ".join(command)) - subprocess.check_call(command) - except subprocess.CalledProcessError as error: - sys.exit(error.returncode) - - -class CodegenCommand(BaseCommand): - def run(self): - header = ( - "# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" - "#\n" - "# *** DO NOT EDIT THIS FILE ***\n" - "#\n" - "# 1) Modify slack_sdk/web/client.py\n" - "# 2) Run `python setup.py codegen`\n" - "#\n" - "# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" - "\n" - ) - with open(f"{here}/slack_sdk/web/client.py", "r") as original: - source = original.read() - import re - - async_source = header + source - async_source = re.sub(" def ", " async def ", async_source) - async_source = re.sub("from asyncio import Future\n", "", async_source) - async_source = re.sub(r"return self.api_call\(", "return await self.api_call(", async_source) - async_source = re.sub("-> SlackResponse", "-> AsyncSlackResponse", async_source) - async_source = re.sub( - "from .base_client import BaseClient, SlackResponse", - "from .async_base_client import AsyncBaseClient, AsyncSlackResponse", - async_source, - ) - # from slack_sdk import WebClient - async_source = re.sub( - r"class WebClient\(BaseClient\):", - "class AsyncWebClient(AsyncBaseClient):", - async_source, - ) - async_source = re.sub( - "from slack_sdk import WebClient", - "from slack_sdk.web.async_client import AsyncWebClient", - async_source, - ) - async_source = re.sub(r"= WebClient\(", "= AsyncWebClient(", async_source) - async_source = re.sub( - r" self.files_getUploadURLExternal\(", - " await self.files_getUploadURLExternal(", - async_source, - ) - async_source = re.sub( - r" self._upload_file\(", - " await self._upload_file(", - async_source, - ) - async_source = re.sub( - r" self.files_completeUploadExternal\(", - " await self.files_completeUploadExternal(", - async_source, - ) - async_source = re.sub( - r" self.files_info\(", - " await self.files_info(", - async_source, - ) - async_source = re.sub( - "_attach_full_file_metadata", - "_attach_full_file_metadata_async", - async_source, - ) - async_source = re.sub( - r" _attach_full_file_metadata_async\(", - " await _attach_full_file_metadata_async(", - async_source, - ) - with open(f"{here}/slack_sdk/web/async_client.py", "w") as output: - output.write(async_source) - - legacy_source = header + "from asyncio import Future\n" + source - legacy_source = re.sub("-> SlackResponse", "-> Union[Future, SlackResponse]", legacy_source) - legacy_source = re.sub( - "from .base_client import BaseClient, SlackResponse", - "from .legacy_base_client import LegacyBaseClient, SlackResponse", - legacy_source, - ) - legacy_source = re.sub( - r"class WebClient\(BaseClient\):", - "class LegacyWebClient(LegacyBaseClient):", - legacy_source, - ) - legacy_source = re.sub( - "from slack_sdk import WebClient", - "from slack_sdk.web.legacy_client import LegacyWebClient", - legacy_source, - ) - legacy_source = re.sub(r"= WebClient\(", "= LegacyWebClient(", legacy_source) - with open(f"{here}/slack_sdk/web/legacy_client.py", "w") as output: - output.write(legacy_source) - - self._run( - "Running black (code formatter) ... ", - [sys.executable, "-m", "black", f"{here}/slack_sdk"], - ) - - -class ValidateCommand(BaseCommand): - """Support setup.py validate.""" - - description = "Run Python static code analyzer (flake8), formatter (black) and unit tests (pytest)." - - user_options = [("test-target=", "i", "tests/{test-target}")] - - def initialize_options(self): - self.test_target = "" - - def run(self): - def run_black(target, target_name=None): - self._run( - f"Running black for {target_name or target} ...", - [sys.executable, "-m", "black", "--check", f"{here}/{target}"], - ) - - run_black("slack", "legacy packages") - run_black("slack_sdk", "slack_sdk package") - run_black("tests") - run_black("integration_tests") - - def run_flake8(target, target_name=None): - self._run( - f"Running flake8 for {target_name or target} ...", - [sys.executable, "-m", "flake8", f"{here}/{target}"], - ) - - run_flake8("slack", "legacy packages") - run_flake8("slack_sdk", "slack_sdk package") - # TODO: resolve linting errors for tests - # run_flake8("tests") - # run_flake8("integration_tests") - - target = self.test_target.replace("tests/", "", 1) - self._run( - "Running unit tests ...", - [ - sys.executable, - "-m", - "pytest", - "--cov-report=xml", - f"--cov={here}/slack_sdk", - f"tests/{target}", - ], - ) - - -class UnitTestsCommand(BaseCommand): - """Support setup.py unit_tests.""" - - description = "Run unit tests (pytest)." - user_options = [("test-target=", "i", "tests/{test-target}")] - - def initialize_options(self): - self.test_target = "" - - def run(self): - target = self.test_target.replace("tests/", "", 1) - self._run( - "Running unit tests ...", - [ - sys.executable, - "-m", - "pytest", - f"tests/{target}", - ], - ) - - -class IntegrationTestsCommand(BaseCommand): - """Support setup.py run_integration_tests""" - - description = "Run integration tests (pytest)." - - user_options = [ - ("test-target=", "i", "integration_tests/{test-target}"), - ] - - def initialize_options(self): - self.test_target = "" - self.legacy = "" - - def run(self): - target = self.test_target.replace("integration_tests/", "", 1) - path = f"integration_tests/{target}" - self._run( - "Running integration tests ...", - [ - sys.executable, - "-m", - "pytest", - path, - ], - ) - - -setup( - cmdclass={ - "codegen": CodegenCommand, - "validate": ValidateCommand, - "unit_tests": UnitTestsCommand, - "integration_tests": IntegrationTestsCommand, - }, -) diff --git a/slack/web/async_client.py b/slack/web/async_client.py index 06aeae240..7258c5eb2 100644 --- a/slack/web/async_client.py +++ b/slack/web/async_client.py @@ -3,7 +3,8 @@ # *** DO NOT EDIT THIS FILE *** # # 1) Modify slack/web/client.py -# 2) Run `python setup.py validate` +# 2) Run `python scripts/codegen.py` +# 3) Run `black slack_sdk/` # # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/slack_sdk/web/async_client.py b/slack_sdk/web/async_client.py index 162513ab6..200523bb4 100644 --- a/slack_sdk/web/async_client.py +++ b/slack_sdk/web/async_client.py @@ -3,7 +3,8 @@ # *** DO NOT EDIT THIS FILE *** # # 1) Modify slack_sdk/web/client.py -# 2) Run `python setup.py codegen` +# 2) Run `python scripts/codegen.py` +# 3) Run `black slack_sdk/` # # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/slack_sdk/web/legacy_client.py b/slack_sdk/web/legacy_client.py index 42ebf9124..5795298d9 100644 --- a/slack_sdk/web/legacy_client.py +++ b/slack_sdk/web/legacy_client.py @@ -3,7 +3,8 @@ # *** DO NOT EDIT THIS FILE *** # # 1) Modify slack_sdk/web/client.py -# 2) Run `python setup.py codegen` +# 2) Run `python scripts/codegen.py` +# 3) Run `black slack_sdk/` # # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!