diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml new file mode 100644 index 000000000..4a8bd835d --- /dev/null +++ b/.github/workflows/ci-build.yml @@ -0,0 +1,34 @@ +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions +name: CI Build + +on: + push: + branches: [ v2 ] + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + # TODO: 3.10 in GH Actions is not yet stable enough for us + # You may often encounter "Error: Process completed with exit code 250." + python-version: ['3.6', '3.9'] + env: + PYTHON_SLACK_SDK_MOCK_SERVER_MODE: 'threading' + #CI_UNSTABLE_TESTS_SKIP_ENABLED: '1' + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + pip install -U pip wheel + pip install -e ".[testing]" + pip install -e ".[optional]" + - name: Run validation + run: | + python setup.py validate diff --git a/.gitignore b/.gitignore index bd5e34fc8..a5ac565f9 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ tmp.txt logs/ .pytype/ +.venv* diff --git a/README.md b/README.md index 9de67ecf1..23e117015 100755 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ # Python slackclient +## Important Notice + +**This [`slackclient`](https://pypi.org/project/slackclient/) PyPI project is in maintenance mode now** and [`slack-sdk`](https://pypi.org/project/slack-sdk/) project is the successor. The v3 SDK provides more functionalities such as Socket Mode, OAuth flow module, SCIM API, Audit Logs API, better asyncio support, retry handlers, and many more. + +Refer to [the migration guide](https://slack.dev/python-slack-sdk/v3-migration/index.html#from-slackclient-2-x) to learn how to smoothly migrate your existing code. + +## Overview + The Python `slackclient` library is a developer kit for interfacing with the Slack Web API and Real Time Messaging (RTM) API on Python 3.6 and above. **Comprehensive documentation on using the Slack Python can be found at [https://slack.dev/python-slackclient/](https://slack.dev/python-slackclient/)** -[![pypi package][pypi-image]][pypi-url] -[![Build Status][travis-image]][travis-url] -[![Python Version][python-version]][pypi-url] -[![codecov][codecov-image]][codecov-url] -[![contact][contact-image]][contact-url] - Whether you're building a custom app for your team, or integrating a third party service into your Slack workflows, Slack Developer Kit for Python allows you to leverage the flexibility of Python to get your project up and running as quickly as possible. The **Python slackclient** allows interaction with: @@ -17,7 +19,7 @@ The **Python slackclient** allows interaction with: - The Slack web api methods available at our [Api Docs site][api-methods] - Interaction with our [RTM API][rtm-docs] -If you want to use our [Events API][events-docs], please check the [Slack Events API adapter for Python][python-slack-events-api]. +If you want to use our [Events API][events-docs] and Interactivity features, please check the [Bolt for Python][bolt-python] library. Details on the Tokens and Authentication can be found in our [Auth Guide](https://slack.dev/python-slack-sdk/installation/). Details on the Tokens and Authentication can be found in our [Auth Guide](https://slack.dev/python-slackclient/auth.html). @@ -189,7 +191,7 @@ When in async mode its important to remember to await or run/run_until_complete #### Slackclient as a script -```python +```python import asyncio import os from slack import WebClient @@ -326,10 +328,7 @@ If you're migrating from v1.x of slackclient to v2.x, Please follow our migratio --- -If you get stuck, we’re here to help. The following are the best ways to get assistance working through your issue: - -Use our [Github Issue Tracker][gh-issues] for reporting bugs or requesting features. -Visit the [Slack Community][slack-community] for getting help using Slack Developer Kit for Python or just generally bond with your fellow Slack developers. +We no longer provide support for v1 or v2 of this SDK and only maintain the latest v3 version. If you would like support from this project's maintainers please consider updating to the latest version of the SDK first. Otherwise, you may visit the [Slack Community][slack-community] for getting help using Slack Developer Kit for Python or just generally bond with your fellow Slack developers. diff --git a/setup.py b/setup.py index 175b83bcc..afc801199 100644 --- a/setup.py +++ b/setup.py @@ -16,10 +16,17 @@ with codecs.open(os.path.join(here, "README.md"), encoding="utf-8") as readme: long_description = readme.read() -tests_require = ["pytest>=5,<6", "pytest-cov>=2,<3", "codecov>=2,<3", "flake8>=3,<4", "black", "psutil"] +tests_require = [ + "pytest>=5,<6", + "pytest-cov>=2,<3", + "codecov>=2,<3", + "flake8>=3,<4", + "black", + "psutil", +] -needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv) -pytest_runner = ['pytest-runner'] if needs_pytest else [] +needs_pytest = {"pytest", "test", "ptr"}.intersection(sys.argv) +pytest_runner = ["pytest-runner"] if needs_pytest else [] class BaseCommand(Command): @@ -88,9 +95,9 @@ class ValidateCommand(BaseCommand): description = "Run Python static code analyzer (flake8), formatter (black) and unit tests (pytest)." user_options = [ - ('unit-test-target=', 'i', 'tests/{unit-test-target}'), - ('utt=', 'i', 'tests/{utt}'), - ('test-target=', 'i', 'tests/{test-target}') + ("unit-test-target=", "i", "tests/{unit-test-target}"), + ("utt=", "i", "tests/{utt}"), + ("test-target=", "i", "tests/{test-target}"), ] def initialize_options(self): @@ -102,27 +109,37 @@ def run(self): with open("./slack/web/client.py", "r") as original: source = original.read() import re - async_source = "# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" \ - "#\n" \ - "# *** DO NOT EDIT THIS FILE ***\n" \ - "#\n" \ - "# 1) Modify slack/web/client.py\n" \ - "# 2) Run `python setup.py validate`\n" \ - "#\n" \ - "# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" \ - "\n" \ - + source + + async_source = ( + "# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" + "#\n" + "# *** DO NOT EDIT THIS FILE ***\n" + "#\n" + "# 1) Modify slack/web/client.py\n" + "# 2) Run `python setup.py validate`\n" + "#\n" + "# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" + "\n" + 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("return self.api_call\(", "return await self.api_call(", async_source) - async_source = re.sub("Union\[Future, SlackResponse\]", "AsyncSlackResponse", async_source) + async_source = re.sub( + "return self.api_call\(", "return await self.api_call(", async_source + ) + async_source = re.sub( + "Union\[Future, SlackResponse\]", "AsyncSlackResponse", async_source + ) async_source = re.sub( "from slack.web.base_client import BaseClient, SlackResponse", - "from slack.web.async_base_client import AsyncBaseClient, AsyncSlackResponse", async_source) + "from slack.web.async_base_client import AsyncBaseClient, AsyncSlackResponse", + async_source, + ) async_source = re.sub( "class WebClient\(BaseClient\):", - "class AsyncWebClient(AsyncBaseClient):", async_source) - with open('./slack/web/async_client.py', 'w') as output: + "class AsyncWebClient(AsyncBaseClient):", + async_source, + ) + with open("./slack/web/async_client.py", "w") as output: output.write(async_source) self._run( @@ -132,7 +149,9 @@ def run(self): self._run("Running black…", [sys.executable, "-m", "black", f"{here}/slack"]) self._run("Running flake8…", [sys.executable, "-m", "flake8", f"{here}/slack"]) - target = (self.utt or self.unit_test_target or self.test_target).replace("tests/", "") + target = (self.utt or self.unit_test_target or self.test_target).replace( + "tests/", "" + ) self._run( "Running pytest…", [ @@ -152,9 +171,13 @@ class RunIntegrationTestsCommand(BaseCommand): description = "Run integration tests (pytest)." user_options = [ - ('integration-test-target=', 'i', 'integration_tests/{integration-test-target}'), - ('itt=', 'i', 'integration_tests/{itt}'), - ('test-target=', 'i', 'integration_tests/{test-target}') + ( + "integration-test-target=", + "i", + "integration_tests/{integration-test-target}", + ), + ("itt=", "i", "integration_tests/{itt}"), + ("test-target=", "i", "integration_tests/{test-target}"), ] def initialize_options(self): @@ -163,7 +186,9 @@ def initialize_options(self): self.test_target = "" def run(self): - target = (self.itt or self.integration_test_target or self.test_target).replace("integration_tests/", "") + target = (self.itt or self.integration_test_target or self.test_target).replace( + "integration_tests/", "" + ) self._run( "Running pytest…", [ @@ -183,11 +208,15 @@ class RunAllTestsCommand(ValidateCommand): description = ValidateCommand.description + "\nRun integration tests (pytest)." user_options = [ - ('unit-test-target=', 'i', 'tests/{unit-test-target}'), - ('utt=', 'i', 'tests/{utt}'), - ('integration-test-target=', 'i', 'integration_tests/{integration-test-target}'), - ('itt=', 'i', 'integration_tests/{itt}'), - ('test-target=', 'i', 'integration_tests/{test-target}') + ("unit-test-target=", "i", "tests/{unit-test-target}"), + ("utt=", "i", "tests/{utt}"), + ( + "integration-test-target=", + "i", + "integration_tests/{integration-test-target}", + ), + ("itt=", "i", "integration_tests/{itt}"), + ("test-target=", "i", "integration_tests/{test-target}"), ] def initialize_options(self): @@ -199,7 +228,9 @@ def initialize_options(self): def run(self): ValidateCommand.run(self) - target = (self.itt or self.integration_test_target or self.test_target).replace("integration_tests/", "") + target = (self.itt or self.integration_test_target or self.test_target).replace( + "integration_tests/", "" + ) self._run( "Running pytest…", [ @@ -216,11 +247,12 @@ def run(self): setup( name="slackclient", version=__version__, - description="Slack API clients for Web API and RTM API", + description="Slack API clients for Web API and RTM API (Legacy) - " + + "Please use https://pypi.org/project/slack-sdk/ instead.", long_description=long_description, long_description_content_type="text/markdown", - url="https://github.com/slackapi/python-slackclient", - author="Slack Technologies, Inc.", + url="https://github.com/slackapi/python-slack-sdk", + author="Slack Technologies, LLC", author_email="opensource@slack.com", python_requires=">=3.6.0", include_package_data=True, @@ -237,14 +269,21 @@ def run(self): "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", ], keywords="slack slack-web slack-rtm chat chatbots bots chatops", packages=find_packages( - exclude=["docs", "docs-src", "integration_tests", "tests", "tests.*", "tutorial"] + exclude=[ + "docs", + "docs-src", + "integration_tests", + "tests", + "tests.*", + "tutorial", + ] ), - install_requires=[ - "aiohttp>3.5.2,<4.0.0" # TODO: move to extras_require in v3 - ], + install_requires=["aiohttp>3.5.2,<4.0.0"], # TODO: move to extras_require in v3 extras_require={"optional": ["aiodns>1.0"]}, setup_requires=pytest_runner, test_suite="tests", diff --git a/slack/rtm/client.py b/slack/rtm/client.py index 7968e5a0e..a3465f4be 100644 --- a/slack/rtm/client.py +++ b/slack/rtm/client.py @@ -569,7 +569,7 @@ async def _wait_exponentially(self, exception, max_wait_time=300): """ wait_time = exception.response.get("headers", {}).get( "Retry-After", - min((2 ** self._connection_attempts) + random.random(), max_wait_time), + min((2**self._connection_attempts) + random.random(), max_wait_time), ) self._logger.debug("Waiting %s seconds before reconnecting.", wait_time) await asyncio.sleep(float(wait_time)) @@ -580,7 +580,8 @@ def _close_websocket(self) -> List[Future]: close_method = getattr(self._websocket, "close", None) if callable(close_method): future = asyncio.ensure_future( - close_method(), loop=self._event_loop, # skipcq: PYL-E1102 + close_method(), + loop=self._event_loop, # skipcq: PYL-E1102 ) futures.append(future) self._websocket = None diff --git a/slack/signature/verifier.py b/slack/signature/verifier.py index e42ca6011..9217502b0 100644 --- a/slack/signature/verifier.py +++ b/slack/signature/verifier.py @@ -23,7 +23,9 @@ def __init__(self, signing_secret: str, clock: Clock = Clock()): self.clock = clock def is_valid_request( - self, body: Union[str, bytes], headers: Dict[str, str], + self, + body: Union[str, bytes], + headers: Dict[str, str], ) -> bool: """Verifies if the given signature is valid""" if headers is None: @@ -36,7 +38,10 @@ def is_valid_request( ) def is_valid( - self, body: Union[str, bytes], timestamp: str, signature: str, + self, + body: Union[str, bytes], + timestamp: str, + signature: str, ) -> bool: """Verifies if the given signature is valid""" if timestamp is None or signature is None: diff --git a/slack/version.py b/slack/version.py index d5546fe61..402791620 100644 --- a/slack/version.py +++ b/slack/version.py @@ -1,2 +1,2 @@ # see: http://legacy.python.org/dev/peps/pep-0440/#public-version-identifiers -__version__ = "2.9.3" +__version__ = "2.9.4" diff --git a/slack/web/async_base_client.py b/slack/web/async_base_client.py index 053c4fa46..e8efa0853 100644 --- a/slack/web/async_base_client.py +++ b/slack/web/async_base_client.py @@ -111,7 +111,9 @@ async def api_call( # skipcq: PYL-R1710 show_2020_01_deprecation(api_method) return await self._send( - http_verb=http_verb, api_url=api_url, req_args=req_args, + http_verb=http_verb, + api_url=api_url, + req_args=req_args, ) async def _send( diff --git a/slack/web/async_slack_response.py b/slack/web/async_slack_response.py index 462b7d2dc..83a3e4244 100644 --- a/slack/web/async_slack_response.py +++ b/slack/web/async_slack_response.py @@ -133,7 +133,9 @@ async def __anext__(self): self.req_args.update({"params": params}) response = await self._client._request( # skipcq: PYL-W0212 - http_verb=self.http_verb, api_url=self.api_url, req_args=self.req_args, + http_verb=self.http_verb, + api_url=self.api_url, + req_args=self.req_args, ) self.data = response["data"] diff --git a/slack/web/classes/blocks.py b/slack/web/classes/blocks.py index b05f80261..8bf55d3c9 100644 --- a/slack/web/classes/blocks.py +++ b/slack/web/classes/blocks.py @@ -159,7 +159,10 @@ class DividerBlock(Block): type = "divider" def __init__( - self, *, block_id: Optional[str] = None, **others: dict, + self, + *, + block_id: Optional[str] = None, + **others: dict, ): """A content divider, like an