From 653cc47c54d53d401d65d1debb3cdc1d7905b4a1 Mon Sep 17 00:00:00 2001 From: Raghav Katyal Date: Wed, 6 Jul 2022 16:14:12 -0700 Subject: [PATCH 01/40] Adding misc as PR type (#1058) --- .github/workflows/pr-lint.yml | 2 +- PULL_REQUEST_TEMPLATE.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml index dc7af3d3..2f5232b0 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint.yml @@ -10,6 +10,6 @@ jobs: steps: - uses: amannn/action-semantic-pull-request@v4 with: - types: chore docs fix feat test + types: chore docs fix feat test misc env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index f78ab391..f9448a3b 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -3,7 +3,7 @@ We appreciate the effort for this pull request but before that please make sure Please format the PR title appropriately based on the type of change: [!]: -Where is one of: docs, chore, feat, fix, test. +Where is one of: docs, chore, feat, fix, test, misc. Add a '!' after the type for breaking changes (e.g. feat!: new breaking feature). **All third-party contributors acknowledge that any contributions they provide will be made under the same open-source license that the open-source project is provided under.** From 4288aa618b04a3e99173f59ce43d7a4b379b93c4 Mon Sep 17 00:00:00 2001 From: Sam Harrison Date: Tue, 3 Jan 2023 09:09:32 -0600 Subject: [PATCH 02/40] docs: updated the year in the license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 5db04ff6..3154774a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (C) 2022, Twilio SendGrid, Inc. +Copyright (C) 2023, Twilio SendGrid, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 9e8a2599bd86d599465552de591d2ca5413fb101 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 13 Mar 2023 15:46:49 +0100 Subject: [PATCH 03/40] feat: Add Python 3.11 to the testing (#1059) --- .github/workflows/test-and-deploy.yml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index ac0bca0a..f14974e4 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -17,7 +17,7 @@ jobs: timeout-minutes: 20 strategy: matrix: - python-version: [ '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10' ] + python-version: [ '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11' ] env: DOCKER_LOGIN: ${{ secrets.DOCKER_USERNAME && secrets.DOCKER_AUTH_TOKEN }} steps: diff --git a/setup.py b/setup.py index 7e84802f..41f11e58 100644 --- a/setup.py +++ b/setup.py @@ -34,12 +34,12 @@ def getRequires(): classifiers=[ 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', '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', + 'Programming Language :: Python :: 3.11', ] ) From 93612afeb0114cd98b6efd7c82dbcf98c9a4df27 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 13 Mar 2023 15:57:25 +0100 Subject: [PATCH 04/40] misc: Upgrade GitHub Action pr-lint.yml (#1063) --- .github/workflows/pr-lint.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml index 2f5232b0..31520079 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint.yml @@ -8,8 +8,14 @@ jobs: name: Validate title runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@v4 + - uses: amannn/action-semantic-pull-request@v5 with: - types: chore docs fix feat test misc + types: | + chore + docs + fix + feat + misc + test env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 102842a9dbeb1cabe057ffc0e52603f2dbc44596 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 13 Mar 2023 16:05:34 +0100 Subject: [PATCH 05/40] misc: Upgrade GitHub Action test-and-deploy.yml (#1064) --- .github/workflows/test-and-deploy.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index f14974e4..4db532e3 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -22,11 +22,11 @@ jobs: DOCKER_LOGIN: ${{ secrets.DOCKER_USERNAME && secrets.DOCKER_AUTH_TOKEN }} steps: - name: Checkout sendgrid-python - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Login to Docker Hub if: env.DOCKER_LOGIN - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_AUTH_TOKEN }} @@ -41,10 +41,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sendgrid-python - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: '3.10' From c8076fa684d66ed5c0569feaf7b99dbedd976b77 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 13 Mar 2023 17:11:47 +0100 Subject: [PATCH 06/40] misc: Create GitHub Action to lint Python code (#1065) --- .github/workflows/lint-python.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/lint-python.yml diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml new file mode 100644 index 00000000..1c5f53f2 --- /dev/null +++ b/.github/workflows/lint-python.yml @@ -0,0 +1,16 @@ +# https://beta.ruff.rs +name: ruff +on: + push: + branches: + - main + pull_request: + branches: + - main +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: pip install --user ruff + - run: ruff --format=github --ignore="E722,F40,F841" --line-length=681 --target-version=py37 . From 4b5c605dda41dbf6dab9ed4ea72ff4014a735885 Mon Sep 17 00:00:00 2001 From: iLan Date: Tue, 21 Mar 2023 11:28:49 -0700 Subject: [PATCH 07/40] feat: Add reply_to_list functionality (#1062) --- sendgrid/helpers/mail/mail.py | 28 ++++++ test/unit/test_mail_helpers.py | 90 +++++++++++++++++++ ..._email_with_multiple_reply_to_addresses.md | 29 ++++++ 3 files changed, 147 insertions(+) create mode 100644 use_cases/send_a_single_email_with_multiple_reply_to_addresses.md diff --git a/sendgrid/helpers/mail/mail.py b/sendgrid/helpers/mail/mail.py index ba21f789..2472ad7d 100644 --- a/sendgrid/helpers/mail/mail.py +++ b/sendgrid/helpers/mail/mail.py @@ -60,6 +60,7 @@ def __init__( self._ip_pool_name = None self._mail_settings = None self._reply_to = None + self._reply_to_list = None self._send_at = None self._subject = None self._template_id = None @@ -695,6 +696,32 @@ def reply_to(self, value): value = ReplyTo(value[0], value[1]) self._reply_to = value + @property + def reply_to_list(self): + """A list of ReplyTo email addresses + + :rtype: list(ReplyTo), tuple + """ + return self._reply_to_list + + @reply_to_list.setter + def reply_to_list(self, value): + """A list of ReplyTo email addresses + + :param value: A list of ReplyTo email addresses + :type value: list(ReplyTo), tuple + """ + if isinstance(value, list): + for reply in value: + if isinstance(reply, ReplyTo): + if not isinstance(reply.email, str): + raise ValueError('You must provide an email for each entry in a reply_to_list') + else: + raise ValueError( + 'Please use a list of ReplyTos for a reply_to_list.' + ) + self._reply_to_list = value + @property def contents(self): """The contents of the email @@ -981,6 +1008,7 @@ def get(self): 'mail_settings': self._get_or_none(self.mail_settings), 'tracking_settings': self._get_or_none(self.tracking_settings), 'reply_to': self._get_or_none(self.reply_to), + 'reply_to_list': [r.get() for r in self.reply_to_list or []], } return {key: value for key, value in mail.items() diff --git a/test/unit/test_mail_helpers.py b/test/unit/test_mail_helpers.py index a7d08d89..c00307d4 100644 --- a/test/unit/test_mail_helpers.py +++ b/test/unit/test_mail_helpers.py @@ -235,6 +235,58 @@ def test_send_a_single_email_to_multiple_recipients(self): }''') ) + def test_send_a_single_email_with_multiple_reply_to_addresses(self): + from sendgrid.helpers.mail import (Mail, From, ReplyTo, To, Subject, + PlainTextContent, HtmlContent) + self.maxDiff = None + message = Mail( + from_email=From('test+from@example.com', 'Example From Name'), + to_emails=To('test+to0@example.com', 'Example To Name'), + subject=Subject('Sending with SendGrid is Fun'), + plain_text_content=PlainTextContent('and easy to do anywhere, even with Python'), + html_content=HtmlContent('and easy to do anywhere, even with Python')) + + message.reply_to_list = [ReplyTo(email = 'test+reply_to_1@example.com'), ReplyTo(email = 'test+reply_to_2@example.com')] + + self.assertEqual( + message.get(), + json.loads(r'''{ + "content": [ + { + "type": "text/plain", + "value": "and easy to do anywhere, even with Python" + }, + { + "type": "text/html", + "value": "and easy to do anywhere, even with Python" + } + ], + "from": { + "email": "test+from@example.com", + "name": "Example From Name" + }, + "personalizations": [ + { + "to": [ + { + "email": "test+to0@example.com", + "name": "Example To Name" + } + ] + } + ], + "reply_to_list": [ + { + "email": "test+reply_to_1@example.com" + }, + { + "email": "test+reply_to_2@example.com" + } + ], + "subject": "Sending with SendGrid is Fun" + }''') + ) + def test_multiple_emails_to_multiple_recipients(self): from sendgrid.helpers.mail import (Mail, From, To, Subject, PlainTextContent, HtmlContent, @@ -568,6 +620,44 @@ def test_value_error_is_raised_on_to_emails_set_to_list_of_lists(self): 'and easy to do anywhere, even with Python'), html_content=HtmlContent( 'and easy to do anywhere, even with Python')) + + def test_value_error_is_raised_on_to_emails_set_to_reply_to_list_of_strs(self): + from sendgrid.helpers.mail import (PlainTextContent, HtmlContent) + self.maxDiff = None + to_emails = [ + ('test+to0@example.com', 'Example To Name 0'), + ('test+to1@example.com', 'Example To Name 1') + ] + + mail = Mail( + from_email=From('test+from@example.com', 'Example From Name'), + to_emails=to_emails, + subject=Subject('Sending with SendGrid is Fun'), + plain_text_content=PlainTextContent( + 'and easy to do anywhere, even with Python'), + html_content=HtmlContent( + 'and easy to do anywhere, even with Python')) + with self.assertRaises(ValueError): + mail.reply_to_list = ['test+reply_to0@example.com', 'test+reply_to1@example.com'] + + def test_value_error_is_raised_on_to_emails_set_to_reply_to_list_of_tuples(self): + from sendgrid.helpers.mail import (PlainTextContent, HtmlContent) + self.maxDiff = None + to_emails = [ + ('test+to0@example.com', 'Example To Name 0'), + ('test+to1@example.com', 'Example To Name 1') + ] + + mail = Mail( + from_email=From('test+from@example.com', 'Example From Name'), + to_emails=to_emails, + subject=Subject('Sending with SendGrid is Fun'), + plain_text_content=PlainTextContent( + 'and easy to do anywhere, even with Python'), + html_content=HtmlContent( + 'and easy to do anywhere, even with Python')) + with self.assertRaises(ValueError): + mail.reply_to_list = [('test+reply_to@example.com', 'Test Name')] def test_error_is_not_raised_on_to_emails_set_to_list_of_tuples(self): from sendgrid.helpers.mail import (PlainTextContent, HtmlContent) diff --git a/use_cases/send_a_single_email_with_multiple_reply_to_addresses.md b/use_cases/send_a_single_email_with_multiple_reply_to_addresses.md new file mode 100644 index 00000000..55d6adf1 --- /dev/null +++ b/use_cases/send_a_single_email_with_multiple_reply_to_addresses.md @@ -0,0 +1,29 @@ +```python +import os +from sendgrid import SendGridAPIClient +from sendgrid.helpers.mail import Mail + +message = Mail( + from_email='from_email@example.com', + to_emails='to@example.com', + subject='Sending with Twilio SendGrid is Fun', + html_content='and easy to do anywhere, even with Python') +message.reply_to_list = [ + ReplyTo( + email='reply-to-1@example.com', + name="Reply To Name 1", + ), + ReplyTo( + email='reply-to-2@example.com', + name="Reply To Name 2", + ) +] +try: + sendgrid_client = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY')) + response = sendgrid_client.send(message) + print(response.status_code) + print(response.body) + print(response.headers) +except Exception as e: + print(e) +``` From 9fe3e235d06475573b27fb521d31a592799cf8a6 Mon Sep 17 00:00:00 2001 From: Twilio Date: Wed, 22 Mar 2023 11:45:20 -0700 Subject: [PATCH 08/40] [Librarian] Version Bump --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9d8f79d..802270cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,25 @@ # Change Log All notable changes to this project will be documented in this file. +[2023-03-22] Version 6.10.0 +--------------------------- +**Library - Feature** +- [PR #1062](https://github.com/sendgrid/sendgrid-python/pull/1062): Add reply_to_list functionality. Thanks to [@thepuzzlemaster](https://github.com/thepuzzlemaster)! +- [PR #1059](https://github.com/sendgrid/sendgrid-python/pull/1059): Add Python 3.11 to the testing. Thanks to [@cclauss](https://github.com/cclauss)! + +**Library - Miscellaneous** +- [PR #1065](https://github.com/sendgrid/sendgrid-python/pull/1065): Create GitHub Action to lint Python code. Thanks to [@cclauss](https://github.com/cclauss)! +- [PR #1064](https://github.com/sendgrid/sendgrid-python/pull/1064): Upgrade GitHub Action test-and-deploy.yml. Thanks to [@cclauss](https://github.com/cclauss)! +- [PR #1063](https://github.com/sendgrid/sendgrid-python/pull/1063): Upgrade GitHub Action pr-lint.yml. Thanks to [@cclauss](https://github.com/cclauss)! + +**Library - Test** +- [PR #1058](https://github.com/sendgrid/sendgrid-python/pull/1058): Adding misc as PR type. Thanks to [@rakatyal](https://github.com/rakatyal)! + +**Library - Docs** +- [PR #1055](https://github.com/sendgrid/sendgrid-python/pull/1055): Modify README.md in alignment with SendGrid Support. Thanks to [@garethpaul](https://github.com/garethpaul)! +- [PR #1052](https://github.com/sendgrid/sendgrid-python/pull/1052): Fix link that has drifted. Thanks to [@jonathanberger](https://github.com/jonathanberger)! + + [2022-03-09] Version 6.9.7 -------------------------- **Library - Chore** From 5e9687caf9ed643362220e239b71537ed539c535 Mon Sep 17 00:00:00 2001 From: Twilio Date: Wed, 22 Mar 2023 11:45:21 -0700 Subject: [PATCH 09/40] Release 6.10.0 --- sendgrid/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/version.py b/sendgrid/version.py index 59b5dedc..466501d0 100644 --- a/sendgrid/version.py +++ b/sendgrid/version.py @@ -1 +1 @@ -__version__ = '6.9.7' +__version__ = '6.10.0' From 2fe145956a1ee50355f5da8deab401e1e118c736 Mon Sep 17 00:00:00 2001 From: Seth Ammons Date: Mon, 17 Apr 2023 04:48:43 -0400 Subject: [PATCH 10/40] fix: removing codedev test dependency (#1066) Fix build by removing dead dependency. Per the announcment: https://about.codecov.io/blog/message-regarding-the-pypi-package/ coddev deprecated their pypi package on april 12, 2023 causing the build to break We no longer are registered at codedev: https://app.codecov.io/gh/sendgrid/sendgrid-python "This repo has been deactivated" --- README.rst | 4 +--- test/requirements.txt | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 93062ba6..e23e2300 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ -|Tests Badge| |codecov| |Python Versions| |PyPI Version| |Docker Badge| |MIT licensed| |Twitter Follow| |GitHub contributors| |Open Source Helpers| +|Tests Badge| |Python Versions| |PyPI Version| |Docker Badge| |MIT licensed| |Twitter Follow| |GitHub contributors| |Open Source Helpers| **This library allows you to quickly and easily use the Twilio SendGrid Web API v3 via Python.** @@ -288,8 +288,6 @@ License :target: https://pypi.org/project/sendgrid/ .. |PyPI Version| image:: https://img.shields.io/pypi/v/sendgrid.svg :target: https://pypi.org/project/sendgrid/ -.. |codecov| image:: https://img.shields.io/codecov/c/github/sendgrid/sendgrid-python/main.svg?style=flat-square&label=Codecov+Coverage - :target: https://codecov.io/gh/sendgrid/sendgrid-python .. |Docker Badge| image:: https://img.shields.io/docker/automated/sendgrid/sendgrid-python.svg :target: https://hub.docker.com/r/sendgrid/sendgrid-python/ .. |MIT licensed| image:: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/test/requirements.txt b/test/requirements.txt index 17302b22..859c2456 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -2,7 +2,6 @@ pyyaml Flask==1.1.4 six coverage -codecov mock itsdangerous==1.1.0 markupsafe==1.1.1 From bcb4f3379ec07f8d337d7aaa63d461259ec5ee41 Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Tue, 14 Nov 2023 15:44:25 +0530 Subject: [PATCH 11/40] feat: geolocation setter in sendgrid-python for GDPR compliance --- .github/workflows/lint-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml index 1c5f53f2..ba064864 100644 --- a/.github/workflows/lint-python.yml +++ b/.github/workflows/lint-python.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@v3 - run: pip install --user ruff - - run: ruff --format=github --ignore="E722,F40,F841" --line-length=681 --target-version=py37 . + - run: ruff --format=github --ignore="E722,F40,F841" --line-length=320 --target-version=py37 . From d3ddc8aa27707ec5e2b13a65dfa4c1e2bc837497 Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Tue, 14 Nov 2023 15:46:52 +0530 Subject: [PATCH 12/40] revert changes --- .github/workflows/lint-python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml index ba064864..1c5f53f2 100644 --- a/.github/workflows/lint-python.yml +++ b/.github/workflows/lint-python.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@v3 - run: pip install --user ruff - - run: ruff --format=github --ignore="E722,F40,F841" --line-length=320 --target-version=py37 . + - run: ruff --format=github --ignore="E722,F40,F841" --line-length=681 --target-version=py37 . From b5afcca6c2c4b29330f81f92b79bf2526645223a Mon Sep 17 00:00:00 2001 From: Manisha Singh Date: Fri, 24 Nov 2023 00:29:36 +0530 Subject: [PATCH 13/40] feat: geolocation setter in sendgrid-python for GDPR compliance (#1073) --- .github/workflows/lint-python.yml | 2 +- examples/dataresidency/set_region.py | 37 ++++++++++++++++++++++++++++ sendgrid/base_interface.py | 23 ++++++++++++++++- test/unit/test_sendgrid.py | 27 ++++++++++++++++++++ 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 examples/dataresidency/set_region.py create mode 100644 test/unit/test_sendgrid.py diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml index 1c5f53f2..ace47f77 100644 --- a/.github/workflows/lint-python.yml +++ b/.github/workflows/lint-python.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@v3 - run: pip install --user ruff - - run: ruff --format=github --ignore="E722,F40,F841" --line-length=681 --target-version=py37 . + - run: ruff --ignore="E722,F40,F841" --line-length=320 --target-version=py37 . diff --git a/examples/dataresidency/set_region.py b/examples/dataresidency/set_region.py new file mode 100644 index 00000000..9aae2611 --- /dev/null +++ b/examples/dataresidency/set_region.py @@ -0,0 +1,37 @@ +import sendgrid +import os + +from sendgrid import Email, To, Content, Mail + +# Example 1 +# setting region to be "global" + +sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY')) +from_email = Email("example@abc.com") +to_email = To("example@abc.com") +subject = "Sending with SendGrid is Fun" +content = Content("text/plain", "and easy to do anywhere, even with Python") +mail = Mail(from_email, to_email, subject, content) +sg.set_sendgrid_data_residency("global") +print(sg.client.host) +response = sg.client.mail.send.post(request_body=mail.get()) +print(response) +print(response.status_code) +print(response.body) +print(response.headers) + +# Example 2 +# setting region to "eu" +sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY_EU')) +sg.set_sendgrid_data_residency("eu") +from_email = Email("example@abc.com") +to_email = To("example@abc.com") +subject = "Sending with SendGrid is Fun" +content = Content("text/plain", "and easy to do anywhere, even with Python") +print(sg.client.host) +mail = Mail(from_email, to_email, subject, content) +response = sg.client.mail.send.post(request_body=mail.get()) +print(response) +print(response.status_code) +print(response.body) +print(response.headers) \ No newline at end of file diff --git a/sendgrid/base_interface.py b/sendgrid/base_interface.py index 92b38247..f94f0947 100644 --- a/sendgrid/base_interface.py +++ b/sendgrid/base_interface.py @@ -1,5 +1,6 @@ import python_http_client +region_host_dict = {'eu':'https://api.eu.sendgrid.com','global':'https://api.sendgrid.com'} class BaseInterface(object): def __init__(self, auth, host, impersonate_subuser): @@ -22,10 +23,10 @@ def __init__(self, auth, host, impersonate_subuser): """ from . import __version__ self.auth = auth - self.host = host self.impersonate_subuser = impersonate_subuser self.version = __version__ self.useragent = 'sendgrid/{};python'.format(self.version) + self.host = host self.client = python_http_client.Client( host=self.host, @@ -60,3 +61,23 @@ def send(self, message): message = message.get() return self.client.mail.send.post(request_body=message) + + def set_sendgrid_data_residency(self, region): + """ + Client libraries contain setters for specifying region/edge. + This supports global and eu regions only. This set will likely expand in the future. + Global is the default residency (or region) + Global region means the message will be sent through https://api.sendgrid.com + EU region means the message will be sent through https://api.eu.sendgrid.com + :param region: string + :return: + """ + if region in region_host_dict.keys(): + self.host = region_host_dict[region] + if self._default_headers is not None: + self.client = python_http_client.Client( + host=self.host, + request_headers=self._default_headers, + version=3) + else: + raise ValueError("region can only be \"eu\" or \"global\"") diff --git a/test/unit/test_sendgrid.py b/test/unit/test_sendgrid.py new file mode 100644 index 00000000..328d978a --- /dev/null +++ b/test/unit/test_sendgrid.py @@ -0,0 +1,27 @@ +import unittest +import sendgrid + +class UnitTests(unittest.TestCase): + def test_host_with_no_region(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + self.assertEqual("https://api.sendgrid.com",sg.client.host) + + def test_host_with_eu_region(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + sg.set_sendgrid_data_residency("eu") + self.assertEqual("https://api.eu.sendgrid.com",sg.client.host) + + def test_host_with_global_region(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + sg.set_sendgrid_data_residency("global") + self.assertEqual("https://api.sendgrid.com",sg.client.host) + + def test_with_region_is_none(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + with self.assertRaises(ValueError): + sg.set_sendgrid_data_residency(None) + + def test_with_region_is_invalid(self): + sg = sendgrid.SendGridAPIClient(api_key='MY_API_KEY') + with self.assertRaises(ValueError): + sg.set_sendgrid_data_residency("abc") \ No newline at end of file From 4804d32b17e9d396e319c1943792537dc1846008 Mon Sep 17 00:00:00 2001 From: Twilio Date: Fri, 1 Dec 2023 05:16:21 +0000 Subject: [PATCH 14/40] [Librarian] Version Bump --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 802270cd..4415a03f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Change Log All notable changes to this project will be documented in this file. +[2023-12-01] Version 6.11.0 +--------------------------- +**Library - Feature** +- [PR #1073](https://github.com/sendgrid/sendgrid-python/pull/1073): geolocation setter in sendgrid-python for GDPR compliance. Thanks to [@manisha1997](https://github.com/manisha1997)! + +**Library - Test** +- [PR #1066](https://github.com/sendgrid/sendgrid-python/pull/1066): removing codedev test dependency. Thanks to [@sethgrid](https://github.com/sethgrid)! + + [2023-03-22] Version 6.10.0 --------------------------- **Library - Feature** From 4e4ac47e61f52b9ead5020f807f4eb151f9a0bc6 Mon Sep 17 00:00:00 2001 From: Twilio Date: Fri, 1 Dec 2023 05:16:22 +0000 Subject: [PATCH 15/40] Release 6.11.0 --- sendgrid/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/version.py b/sendgrid/version.py index 466501d0..901082d4 100644 --- a/sendgrid/version.py +++ b/sendgrid/version.py @@ -1 +1 @@ -__version__ = '6.10.0' +__version__ = '6.11.0' From 78acb96d25a299ad862396c1fbc399c897998bfd Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Tue, 26 Dec 2023 13:53:33 +0530 Subject: [PATCH 16/40] chore: commit to main to check if the commits go directly into main. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 663d1e97..d41c0a1c 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ response = sg.client.mail.send.post(request_body=data) print(response.status_code) print(response.body) print(response.headers) + ``` ## General v3 Web API Usage (With [Fluent Interface](https://sendgrid.com/blog/using-python-to-implement-a-fluent-interface-to-any-rest-api/)) From 9515dce6c8229c9cf7272701d0c2664c05727f16 Mon Sep 17 00:00:00 2001 From: manisha1997 Date: Tue, 26 Dec 2023 13:54:42 +0530 Subject: [PATCH 17/40] chore: rolling back direct commits --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d41c0a1c..663d1e97 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,6 @@ response = sg.client.mail.send.post(request_body=data) print(response.status_code) print(response.body) print(response.headers) - ``` ## General v3 Web API Usage (With [Fluent Interface](https://sendgrid.com/blog/using-python-to-implement-a-fluent-interface-to-any-rest-api/)) From e2a2cfd7c0632ead3ecdeb725ec4798b711a7931 Mon Sep 17 00:00:00 2001 From: Shubham Date: Fri, 11 Apr 2025 15:00:51 +0530 Subject: [PATCH 18/40] chore: install docker-compose (#1099) * chore: install docker-compose * chore: update licence year * chore: fix lint-python --- .github/workflows/lint-python.yml | 2 +- .github/workflows/test-and-deploy.yml | 5 +++++ LICENSE | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml index ace47f77..5dcf0328 100644 --- a/.github/workflows/lint-python.yml +++ b/.github/workflows/lint-python.yml @@ -13,4 +13,4 @@ jobs: steps: - uses: actions/checkout@v3 - run: pip install --user ruff - - run: ruff --ignore="E722,F40,F841" --line-length=320 --target-version=py37 . + - run: ruff check --ignore="E722,F40,F841" --line-length=320 --target-version=py37 . diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index 4db532e3..25408ac0 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -31,6 +31,11 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_AUTH_TOKEN }} + - name: Install Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y docker-compose + - name: Build & Test run: make test-docker version=${{ matrix.python-version }} diff --git a/LICENSE b/LICENSE index 3154774a..126ceb1a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (C) 2023, Twilio SendGrid, Inc. +Copyright (C) 2025, Twilio SendGrid, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From ee54d81051d45b0a667642866b74dc7efbc7155b Mon Sep 17 00:00:00 2001 From: Meysam Date: Tue, 15 Apr 2025 22:05:45 +0700 Subject: [PATCH 19/40] feat: add support for python3.12 and 3.13 (#1087) --- .github/workflows/test-and-deploy.yml | 2 +- Makefile | 6 ++--- setup.py | 2 ++ test/requirements.txt | 1 + tox.ini | 32 ++++++++++++++++++++++++++- 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index 25408ac0..ba8b6990 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -17,7 +17,7 @@ jobs: timeout-minutes: 20 strategy: matrix: - python-version: [ '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11' ] + python-version: [ '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12' , '3.13'] env: DOCKER_LOGIN: ${{ secrets.DOCKER_USERNAME && secrets.DOCKER_AUTH_TOKEN }} steps: diff --git a/Makefile b/Makefile index fb61004c..96161106 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,11 @@ venv: clean virtualenv --python=python venv install: venv + . venv/bin/activate; pip install -r test/requirements.txt . venv/bin/activate; python setup.py install . venv/bin/activate; pip install -r requirements.txt -test-install: install - . venv/bin/activate; pip install -r test/requirements.txt - -test: test-install +test: install . venv/bin/activate; coverage run -m unittest discover -s test/unit test-integ: test diff --git a/setup.py b/setup.py index 41f11e58..7c797ae6 100644 --- a/setup.py +++ b/setup.py @@ -41,5 +41,7 @@ def getRequires(): 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', ] ) diff --git a/test/requirements.txt b/test/requirements.txt index 859c2456..40552deb 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -5,3 +5,4 @@ coverage mock itsdangerous==1.1.0 markupsafe==1.1.1 +setuptools diff --git a/tox.ini b/tox.ini index 926a3c9d..8f4f2db9 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py34, py35, py36, py37 +envlist = py27, py34, py35, py36, py37, py38, py39, py310, py311, py312, py313 [testenv] commands = coverage erase @@ -39,3 +39,33 @@ basepython = python3.6 commands = {[testenv]commands} deps = {[testenv]deps} basepython = python3.7 + +[testenv:py38] +commands = {[testenv]commands} +deps = {[testenv]deps} +basepython = python3.8 + +[testenv:py39] +commands = {[testenv]commands} +deps = {[testenv]deps} +basepython = python3.9 + +[testenv:py310] +commands = {[testenv]commands} +deps = {[testenv]deps} +basepython = python3.10 + +[testenv:py311] +commands = {[testenv]commands} +deps = {[testenv]deps} +basepython = python3.11 + +[testenv:py312] +commands = {[testenv]commands} +deps = {[testenv]deps} +basepython = python3.12 + +[testenv:py313] +commands = {[testenv]commands} +deps = {[testenv]deps} +basepython = python3.13 From 09bc8466ddf1ecd11a6dc7ebb7a8338fb5afca86 Mon Sep 17 00:00:00 2001 From: GPK Date: Fri, 25 Apr 2025 14:06:33 +0100 Subject: [PATCH 20/40] chore: Add werkzeug package to setup file (#1098) * Add werkzeug package to setup file * Add werkzeug package to setup file * Update versions * Remove EOL python versions --------- Co-authored-by: Shubham --- .github/workflows/test-and-deploy.yml | 2 +- setup.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index ba8b6990..959970d9 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -17,7 +17,7 @@ jobs: timeout-minutes: 20 strategy: matrix: - python-version: [ '2.7', '3.5', '3.6', '3.7', '3.8', '3.9', '3.10', '3.11', '3.12' , '3.13'] + python-version: [ '3.9', '3.10', '3.11', '3.12' , '3.13'] env: DOCKER_LOGIN: ${{ secrets.DOCKER_USERNAME && secrets.DOCKER_AUTH_TOKEN }} steps: diff --git a/setup.py b/setup.py index 7c797ae6..0753e399 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,11 @@ def getRequires(): deps = [ 'python_http_client>=3.2.1', - 'starkbank-ecdsa>=2.0.1' + 'starkbank-ecdsa>=2.0.1', + "werkzeug>=0.11.15,<1.0.0 ; python_version < '3.0'", + "werkzeug>=0.15.0,<2.0.0 ; python_version >= '3.0' and python_version < '3.6'", + "werkzeug>=2.0.0,<3.0.0 ; python_version >= '3.6' and python_version < '3.11'", + "werkzeug>=3.0.0 ; python_version >= '3.11'" ] return deps From ac693fe02295e5e0b77070bab6fa460c697f8997 Mon Sep 17 00:00:00 2001 From: Twilio Date: Mon, 5 May 2025 10:35:35 +0000 Subject: [PATCH 21/40] [Librarian] Version Bump --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4415a03f..a2215795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Change Log All notable changes to this project will be documented in this file. +[2025-05-05] Version 6.12.0 +--------------------------- +**Library - Chore** +- [PR #1098](https://github.com/sendgrid/sendgrid-python/pull/1098): Add werkzeug package to setup file. Thanks to [@gopidesupavan](https://github.com/gopidesupavan)! +- [PR #1099](https://github.com/sendgrid/sendgrid-python/pull/1099): install docker-compose. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! + +**Library - Feature** +- [PR #1087](https://github.com/sendgrid/sendgrid-python/pull/1087): add support for python3.12 and 3.13. Thanks to [@meysam81](https://github.com/meysam81)! + + [2023-12-01] Version 6.11.0 --------------------------- **Library - Feature** From 774663f5a0af011d23908d0dbd932cb64ee96d70 Mon Sep 17 00:00:00 2001 From: Twilio Date: Mon, 5 May 2025 10:35:35 +0000 Subject: [PATCH 22/40] Release 6.12.0 --- sendgrid/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/version.py b/sendgrid/version.py index 901082d4..142f2122 100644 --- a/sendgrid/version.py +++ b/sendgrid/version.py @@ -1 +1 @@ -__version__ = '6.11.0' +__version__ = '6.12.0' From 6eb4375f4d1ef1e0c1ad17d07858706291380ebc Mon Sep 17 00:00:00 2001 From: ranjanprasad1996 <141220652+ranjanprasad1996@users.noreply.github.com> Date: Fri, 9 May 2025 20:54:52 +0530 Subject: [PATCH 23/40] fix: Vulnerability fix for starkbank-ecdsa 2.2.0 dependency (#1085) * Updated webhook helper to use a different edcsa library * chore: dummy commit * Update dependencies --------- Co-authored-by: Shubham --- requirements.txt | 6 ++--- sendgrid/helpers/eventwebhook/__init__.py | 30 +++++++++++++---------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0c34aafd..c9520448 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ -Flask==1.1.2 +Flask==3.1.0 PyYAML>=4.2b1 python-http-client>=3.2.1 -six==1.11.0 -starkbank-ecdsa>=2.0.1 +six==1.17.0 +ecdsa>=0.19.1,<1 more-itertools==5.0.0 diff --git a/sendgrid/helpers/eventwebhook/__init__.py b/sendgrid/helpers/eventwebhook/__init__.py index c1ec7d1c..d97604df 100644 --- a/sendgrid/helpers/eventwebhook/__init__.py +++ b/sendgrid/helpers/eventwebhook/__init__.py @@ -1,8 +1,7 @@ -from ellipticcurve.ecdsa import Ecdsa -from ellipticcurve.publicKey import PublicKey -from ellipticcurve.signature import Signature - -from .eventwebhook_header import EventWebhookHeader +from ecdsa import VerifyingKey, BadSignatureError +from ecdsa.util import sigdecode_der +import base64 +import hashlib class EventWebhook: """ @@ -20,14 +19,15 @@ def __init__(self, public_key=None): def convert_public_key_to_ecdsa(self, public_key): """ - Convert the public key string to a ECPublicKey. + Convert the public key string to a VerifyingKey object. :param public_key: verification key under Mail Settings :type public_key string - :return: public key using the ECDSA algorithm - :rtype PublicKey + :return: VerifyingKey object using the ECDSA algorithm + :rtype VerifyingKey """ - return PublicKey.fromPem('\n-----BEGIN PUBLIC KEY-----\n'+public_key+'\n-----END PUBLIC KEY-----\n') + pem_key = "-----BEGIN PUBLIC KEY-----\n" + public_key + "\n-----END PUBLIC KEY-----" + return VerifyingKey.from_pem(pem_key) def verify_signature(self, payload, signature, timestamp, public_key=None): """ @@ -40,11 +40,15 @@ def verify_signature(self, payload, signature, timestamp, public_key=None): :param timestamp: value obtained from the 'X-Twilio-Email-Event-Webhook-Timestamp' header :type timestamp: string :param public_key: elliptic curve public key - :type public_key: PublicKey + :type public_key: VerifyingKey :return: true or false if signature is valid """ - timestamped_payload = timestamp + payload - decoded_signature = Signature.fromBase64(signature) + timestamped_payload = (timestamp + payload).encode('utf-8') + decoded_signature = base64.b64decode(signature) key = public_key or self.public_key - return Ecdsa.verify(timestamped_payload, decoded_signature, key) + try: + key.verify(decoded_signature, timestamped_payload, hashfunc=hashlib.sha256, sigdecode=sigdecode_der) + return True + except BadSignatureError: + return False From 096719d8daf5846664eb3f6b4649c826176a959c Mon Sep 17 00:00:00 2001 From: Twilio Date: Tue, 13 May 2025 09:44:12 +0000 Subject: [PATCH 24/40] [Librarian] Version Bump --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2215795..2936c9a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +[2025-05-13] Version 6.12.1 +--------------------------- +**Library - Fix** +- [PR #1085](https://github.com/sendgrid/sendgrid-python/pull/1085): Vulnerability fix for starkbank-ecdsa 2.2.0 dependency. Thanks to [@ranjanprasad1996](https://github.com/ranjanprasad1996)! + + [2025-05-05] Version 6.12.0 --------------------------- **Library - Chore** From 2acb010ae303cca78d696b82b739809b7f319187 Mon Sep 17 00:00:00 2001 From: Twilio Date: Tue, 13 May 2025 09:44:12 +0000 Subject: [PATCH 25/40] Release 6.12.1 --- sendgrid/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/version.py b/sendgrid/version.py index 142f2122..431e91f2 100644 --- a/sendgrid/version.py +++ b/sendgrid/version.py @@ -1 +1 @@ -__version__ = '6.12.0' +__version__ = '6.12.1' From e1ed323bb0a595b7488fbb0c8d8351682ff97f55 Mon Sep 17 00:00:00 2001 From: Shubham Date: Tue, 13 May 2025 16:19:25 +0530 Subject: [PATCH 26/40] chore: update ecdsa in setup.py (#1102) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0753e399..b3a8e5c8 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ def getRequires(): deps = [ 'python_http_client>=3.2.1', - 'starkbank-ecdsa>=2.0.1', + 'ecdsa>=0.19.1,<1', "werkzeug>=0.11.15,<1.0.0 ; python_version < '3.0'", "werkzeug>=0.15.0,<2.0.0 ; python_version >= '3.0' and python_version < '3.6'", "werkzeug>=2.0.0,<3.0.0 ; python_version >= '3.6' and python_version < '3.11'", From a1acbf3a53e5f5b122614718acbdc6a7196921a9 Mon Sep 17 00:00:00 2001 From: Twilio Date: Tue, 13 May 2025 10:51:11 +0000 Subject: [PATCH 27/40] [Librarian] Version Bump --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2936c9a1..607b465c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +[2025-05-13] Version 6.12.2 +--------------------------- +**Library - Chore** +- [PR #1102](https://github.com/sendgrid/sendgrid-python/pull/1102): update ecdsa in setup.py. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! + + [2025-05-13] Version 6.12.1 --------------------------- **Library - Fix** From 3f25a2a6ed33878e13cfe8d76d283b1d6c7b380a Mon Sep 17 00:00:00 2001 From: Twilio Date: Tue, 13 May 2025 10:51:12 +0000 Subject: [PATCH 28/40] Release 6.12.2 --- sendgrid/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/version.py b/sendgrid/version.py index 431e91f2..67234a38 100644 --- a/sendgrid/version.py +++ b/sendgrid/version.py @@ -1 +1 @@ -__version__ = '6.12.1' +__version__ = '6.12.2' From a78bd11b854ab38978f73c7e3fda98110bd9f840 Mon Sep 17 00:00:00 2001 From: Elad Kalif <45845474+eladkal@users.noreply.github.com> Date: Wed, 14 May 2025 11:02:30 +0300 Subject: [PATCH 29/40] chore: Relax werkzeug version (#1103) --- setup.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index b3a8e5c8..a1b1a974 100644 --- a/setup.py +++ b/setup.py @@ -13,8 +13,11 @@ def getRequires(): 'ecdsa>=0.19.1,<1', "werkzeug>=0.11.15,<1.0.0 ; python_version < '3.0'", "werkzeug>=0.15.0,<2.0.0 ; python_version >= '3.0' and python_version < '3.6'", - "werkzeug>=2.0.0,<3.0.0 ; python_version >= '3.6' and python_version < '3.11'", - "werkzeug>=3.0.0 ; python_version >= '3.11'" + "werkzeug>=0.15.0,<2.3.0 ; python_version >= '3.0' and python_version < '3.7'", + "werkzeug>=0.16.0,<3.1.0 ; python_version >= '3.0' and python_version < '3.8'", + "werkzeug>=1.0.0 ; python_version >= '3.9'", + "werkzeug>=2.2.0 ; python_version >= '3.11'", + "werkzeug>=2.3.5 ; python_version >= '3.12'" ] return deps From 4671192ce266f575eca28c062766ff4194d66ae9 Mon Sep 17 00:00:00 2001 From: Elad Kalif <45845474+eladkal@users.noreply.github.com> Date: Wed, 14 May 2025 17:35:46 +0300 Subject: [PATCH 30/40] chore: fix werkzeug lower versions (#1104) --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index a1b1a974..745edea8 100644 --- a/setup.py +++ b/setup.py @@ -12,9 +12,9 @@ def getRequires(): 'python_http_client>=3.2.1', 'ecdsa>=0.19.1,<1', "werkzeug>=0.11.15,<1.0.0 ; python_version < '3.0'", - "werkzeug>=0.15.0,<2.0.0 ; python_version >= '3.0' and python_version < '3.6'", - "werkzeug>=0.15.0,<2.3.0 ; python_version >= '3.0' and python_version < '3.7'", - "werkzeug>=0.16.0,<3.1.0 ; python_version >= '3.0' and python_version < '3.8'", + "werkzeug>=0.15.0,<2.0.0 ; python_version >= '3.0' and python_version < '3.7'", + "werkzeug>=0.15.0,<2.3.0 ; python_version >= '3.0' and python_version < '3.8'", # version 2.3.0 dropped support for Python 3.7 + "werkzeug>=0.16.0,<3.1.0 ; python_version >= '3.0' and python_version < '3.9'", # version 3.1.0 dropped support for Python 3.8 "werkzeug>=1.0.0 ; python_version >= '3.9'", "werkzeug>=2.2.0 ; python_version >= '3.11'", "werkzeug>=2.3.5 ; python_version >= '3.12'" From 3e9d4efac5d652de81fb82ae38d256aad78c00d5 Mon Sep 17 00:00:00 2001 From: Shubham Date: Fri, 23 May 2025 11:51:29 +0530 Subject: [PATCH 31/40] chore: export EventWebhookHeader (#1107) --- sendgrid/helpers/eventwebhook/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sendgrid/helpers/eventwebhook/__init__.py b/sendgrid/helpers/eventwebhook/__init__.py index d97604df..82a2cd6b 100644 --- a/sendgrid/helpers/eventwebhook/__init__.py +++ b/sendgrid/helpers/eventwebhook/__init__.py @@ -2,6 +2,7 @@ from ecdsa.util import sigdecode_der import base64 import hashlib +from .eventwebhook_header import EventWebhookHeader class EventWebhook: """ From a24ab9de13b2b182985860e5979b1368d2e9fa95 Mon Sep 17 00:00:00 2001 From: Twilio Date: Thu, 29 May 2025 12:11:32 +0000 Subject: [PATCH 32/40] [Librarian] Version Bump --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 607b465c..7a69e3f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Change Log All notable changes to this project will be documented in this file. +[2025-05-29] Version 6.12.3 +--------------------------- +**Library - Chore** +- [PR #1107](https://github.com/sendgrid/sendgrid-python/pull/1107): export EventWebhookHeader. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! +- [PR #1104](https://github.com/sendgrid/sendgrid-python/pull/1104): fix werkzeug lower versions. Thanks to [@eladkal](https://github.com/eladkal)! +- [PR #1103](https://github.com/sendgrid/sendgrid-python/pull/1103): Relax werkzeug version. Thanks to [@eladkal](https://github.com/eladkal)! + + [2025-05-13] Version 6.12.2 --------------------------- **Library - Chore** From 159f5dd2d4a6eb360ca61356b009b37fcc6aa5c8 Mon Sep 17 00:00:00 2001 From: Twilio Date: Thu, 29 May 2025 12:11:32 +0000 Subject: [PATCH 33/40] Release 6.12.3 --- sendgrid/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/version.py b/sendgrid/version.py index 67234a38..67ae80f9 100644 --- a/sendgrid/version.py +++ b/sendgrid/version.py @@ -1 +1 @@ -__version__ = '6.12.2' +__version__ = '6.12.3' From f5bf6153b5dabf6fe307a8acdb0e59ce17a0c07a Mon Sep 17 00:00:00 2001 From: Manisha Singh Date: Mon, 2 Jun 2025 16:40:55 +0530 Subject: [PATCH 34/40] chore: bug-fix (#1109) --- sendgrid/helpers/mail/mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/helpers/mail/mail.py b/sendgrid/helpers/mail/mail.py index 2472ad7d..e475fe76 100644 --- a/sendgrid/helpers/mail/mail.py +++ b/sendgrid/helpers/mail/mail.py @@ -567,7 +567,7 @@ def add_custom_arg(self, custom_arg): :param value: A CustomArg object or a dict of custom arg key/values :type value: CustomArg, dict """ - if custom_arg.personalization is not None: + if not isinstance(custom_arg, dict) and custom_arg.personalization is not None: try: personalization = \ self._personalizations[custom_arg.personalization] From 59994bfafc59fbe7fa2bb9338654e5e6a4e4f612 Mon Sep 17 00:00:00 2001 From: Twilio Date: Thu, 12 Jun 2025 10:27:17 +0000 Subject: [PATCH 35/40] [Librarian] Version Bump --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a69e3f9..87dde665 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Change Log All notable changes to this project will be documented in this file. +[2025-06-12] Version 6.12.4 +--------------------------- +**Library - Chore** +- [PR #1109](https://github.com/sendgrid/sendgrid-python/pull/1109): bug-fix. Thanks to [@manisha1997](https://github.com/manisha1997)! + + [2025-05-29] Version 6.12.3 --------------------------- **Library - Chore** From 7eafe1854f42ef2d6863a7288c1c2e73f9aa5c82 Mon Sep 17 00:00:00 2001 From: Twilio Date: Thu, 12 Jun 2025 10:27:18 +0000 Subject: [PATCH 36/40] Release 6.12.4 --- sendgrid/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/version.py b/sendgrid/version.py index 67ae80f9..127e2ed6 100644 --- a/sendgrid/version.py +++ b/sendgrid/version.py @@ -1 +1 @@ -__version__ = '6.12.3' +__version__ = '6.12.4' From af2c4e70b338cdf4ccca4ed12184fdda5926dcc6 Mon Sep 17 00:00:00 2001 From: Shubham Date: Thu, 11 Sep 2025 13:20:25 +0530 Subject: [PATCH 37/40] chore: use make-test instead of make test-docker (#1117) --- .github/workflows/test-and-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index 959970d9..98bbeefb 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -37,7 +37,7 @@ jobs: sudo apt-get install -y docker-compose - name: Build & Test - run: make test-docker version=${{ matrix.python-version }} + run: make test deploy: name: Deploy From 68288529c5e510e6fe535c42d7e52fea5922558a Mon Sep 17 00:00:00 2001 From: David Acevedo Date: Thu, 11 Sep 2025 02:57:07 -0500 Subject: [PATCH 38/40] fix: #1108 - Replace ecdsa with cryptography (#1114) Co-authored-by: Shubham --- CONTRIBUTING.md | 2 +- README.md | 2 +- README.rst | 4 ++-- requirements.txt | 2 +- sendgrid/helpers/eventwebhook/__init__.py | 21 +++++++++++---------- setup.py | 2 +- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 89064105..af950715 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,7 +26,7 @@ We welcome direct contributions to the sendgrid-python code base. Thank you! - Python version 2.7, 3.5, 3.6, 3.7, or 3.8 - [python_http_client](https://github.com/sendgrid/python-http-client) -- [ecdsa_python](https://github.com/starkbank/ecdsa-python) +- [cryptography](https://github.com/pyca/cryptography) - [pyenv](https://github.com/yyuu/pyenv) - [tox](https://pypi.python.org/pypi/tox) diff --git a/README.md b/README.md index 663d1e97..b1b36686 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ pip install sendgrid ## Dependencies - [Python-HTTP-Client](https://github.com/sendgrid/python-http-client) -- [ECDSA-Python](https://github.com/starkbank/ecdsa-python) +- [Cryptography](https://github.com/pyca/cryptography) diff --git a/README.rst b/README.rst index e23e2300..526c4ca4 100644 --- a/README.rst +++ b/README.rst @@ -90,7 +90,7 @@ Dependencies ------------ - `Python-HTTP-Client`_ -- `ECDSA-Python`_ +- `Cryptography`_ Quick Start =========== @@ -259,7 +259,7 @@ License .. _Twilio account: https://www.twilio.com/try-twilio?source=sendgrid-python .. _SENDGRID_API_KEY: https://app.sendgrid.com/settings/api_keys .. _Python-HTTP-Client: https://github.com/sendgrid/python-http-client -.. _ECDSA-Python: https://github.com/starkbank/ecdsa-python +.. _Cryptography: https://github.com/pyca/cryptography .. _/mail/send Helper: https://github.com/sendgrid/sendgrid-python/tree/HEAD/sendgrid/helpers/mail .. _personalization object: https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/personalizations.html .. _Fluent Interface: https://sendgrid.com/blog/using-python-to-implement-a-fluent-interface-to-any-rest-api/ diff --git a/requirements.txt b/requirements.txt index c9520448..ed2594a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,5 @@ Flask==3.1.0 PyYAML>=4.2b1 python-http-client>=3.2.1 six==1.17.0 -ecdsa>=0.19.1,<1 +cryptography>=45.0.6 more-itertools==5.0.0 diff --git a/sendgrid/helpers/eventwebhook/__init__.py b/sendgrid/helpers/eventwebhook/__init__.py index 82a2cd6b..9d618bf3 100644 --- a/sendgrid/helpers/eventwebhook/__init__.py +++ b/sendgrid/helpers/eventwebhook/__init__.py @@ -1,7 +1,8 @@ -from ecdsa import VerifyingKey, BadSignatureError -from ecdsa.util import sigdecode_der +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.serialization import load_pem_public_key import base64 -import hashlib from .eventwebhook_header import EventWebhookHeader class EventWebhook: @@ -20,15 +21,15 @@ def __init__(self, public_key=None): def convert_public_key_to_ecdsa(self, public_key): """ - Convert the public key string to a VerifyingKey object. + Convert the public key string to an EllipticCurvePublicKey object. :param public_key: verification key under Mail Settings :type public_key string - :return: VerifyingKey object using the ECDSA algorithm - :rtype VerifyingKey + :return: An EllipticCurvePublicKey object using the ECDSA algorithm + :rtype cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey """ pem_key = "-----BEGIN PUBLIC KEY-----\n" + public_key + "\n-----END PUBLIC KEY-----" - return VerifyingKey.from_pem(pem_key) + return load_pem_public_key(pem_key.encode("utf-8")) def verify_signature(self, payload, signature, timestamp, public_key=None): """ @@ -41,7 +42,7 @@ def verify_signature(self, payload, signature, timestamp, public_key=None): :param timestamp: value obtained from the 'X-Twilio-Email-Event-Webhook-Timestamp' header :type timestamp: string :param public_key: elliptic curve public key - :type public_key: VerifyingKey + :type public_key: cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey :return: true or false if signature is valid """ timestamped_payload = (timestamp + payload).encode('utf-8') @@ -49,7 +50,7 @@ def verify_signature(self, payload, signature, timestamp, public_key=None): key = public_key or self.public_key try: - key.verify(decoded_signature, timestamped_payload, hashfunc=hashlib.sha256, sigdecode=sigdecode_der) + key.verify(decoded_signature, timestamped_payload, ec.ECDSA(hashes.SHA256())) return True - except BadSignatureError: + except InvalidSignature: return False diff --git a/setup.py b/setup.py index 745edea8..904cd654 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ def getRequires(): deps = [ 'python_http_client>=3.2.1', - 'ecdsa>=0.19.1,<1', + 'cryptography>=45.0.6', "werkzeug>=0.11.15,<1.0.0 ; python_version < '3.0'", "werkzeug>=0.15.0,<2.0.0 ; python_version >= '3.0' and python_version < '3.7'", "werkzeug>=0.15.0,<2.3.0 ; python_version >= '3.0' and python_version < '3.8'", # version 2.3.0 dropped support for Python 3.7 From 5e26ead73a8d908550b4fab3166ab41697030c9c Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Fri, 19 Sep 2025 11:47:28 +0530 Subject: [PATCH 39/40] [Librarian] Version Bump --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87dde665..67d32764 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Change Log All notable changes to this project will be documented in this file. +[2025-09-19] Version 6.12.5 +--------------------------- +**Library - Fix** +- [PR #1114](https://github.com/sendgrid/sendgrid-python/pull/1114): #1108 - Replace ecdsa with cryptography. Thanks to [@dacevedo12](https://github.com/dacevedo12)! + +**Library - Chore** +- [PR #1117](https://github.com/sendgrid/sendgrid-python/pull/1117): use make-test instead of make test-docker. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! + + [2025-06-12] Version 6.12.4 --------------------------- **Library - Chore** From 76788e70a76b11ce2990821f190e52f887ab9ed6 Mon Sep 17 00:00:00 2001 From: Shubham Tiwari Date: Fri, 19 Sep 2025 11:48:40 +0530 Subject: [PATCH 40/40] Release 6.12.5 --- sendgrid/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sendgrid/version.py b/sendgrid/version.py index 127e2ed6..c1d623f9 100644 --- a/sendgrid/version.py +++ b/sendgrid/version.py @@ -1 +1 @@ -__version__ = '6.12.4' +__version__ = '6.12.5'