├── .envrc ├── .flake8 ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── publish.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── mailersend ├── __init__.py ├── activity │ └── __init__.py ├── analytics │ └── __init__.py ├── api_quota │ └── __init__.py ├── base │ ├── __init__.py │ └── base.py ├── domains │ └── __init__.py ├── email_verification │ └── __init__.py ├── emails │ └── __init__.py ├── inbound_routing │ └── __init__.py ├── messages │ └── __init__.py ├── recipients │ └── __init__.py ├── scheduled_messages │ └── __init__.py ├── sender_identities │ └── __init__.py ├── sms_activity │ └── __init__.py ├── sms_inbounds │ └── __init__.py ├── sms_messages │ └── __init__.py ├── sms_phone_numbers │ └── __init__.py ├── sms_recipients │ └── __init__.py ├── sms_sending │ └── __init__.py ├── sms_webhooks │ └── __init__.py ├── templates │ └── __init__.py ├── tokens │ └── __init__.py ├── utils │ └── __init__.py └── webhooks │ └── __init__.py ├── poetry.lock ├── pyproject.toml ├── renovate.json └── tests ├── __init__.py └── test_mailersend.py /.envrc: -------------------------------------------------------------------------------- 1 | layout_poetry -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 200 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @mailersend/python-skd-maintainers 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment (please complete the following information):** 27 | - OS: [e.g. macOS] 28 | - Python version [e.g. 3.9] 29 | - SDK version [e.g. 1.12] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v[0-9]+.[0-9]+.[0-9]+*" 7 | 8 | jobs: 9 | build-n-publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 13 | 14 | - name: Build and publish to pypi 15 | uses: JRubics/poetry-publish@v2.0 16 | with: 17 | pypi_token: ${{ secrets.PYPI_API_TOKEN }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | .installed.cfg 25 | *.egg-info/ 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | test.py 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytype static type analyzer 136 | .pytype/ 137 | 138 | # Cython debug symbols 139 | cython_debug/ 140 | .idea 141 | .pypirc 142 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_install_hook_types: 2 | - pre-commit 3 | - post-commit 4 | - post-checkout 5 | - commit-msg 6 | repos: 7 | - repo: https://github.com/pre-commit/pre-commit-hooks 8 | rev: v4.4.0 9 | hooks: 10 | - id: trailing-whitespace 11 | - id: mixed-line-ending 12 | - id: end-of-file-fixer 13 | - id: check-added-large-files 14 | - repo: https://github.com/psf/black 15 | rev: 23.3.0 16 | hooks: 17 | - id: black 18 | types: 19 | - python 20 | - repo: https://github.com/PyCQA/flake8 21 | rev: 6.0.0 22 | hooks: 23 | - id: flake8 24 | types: 25 | - python 26 | - repo: https://github.com/pycqa/bandit 27 | rev: 1.7.5 28 | hooks: 29 | - id: bandit 30 | args: ["-ll"] 31 | files: .py$ 32 | - repo: git@github.com:mailergroup/pre-commit-conventional-commits.git 33 | # renovate: datasource=github-releases depName=mailergroup/pre-commit-conventional-commits 34 | rev: v1.0.1 35 | hooks: 36 | - id: pre-commit-conventional-commits 37 | stages: [commit-msg] 38 | - repo: git@github.com:mailergroup/pre-commit-branch-name-validation.git 39 | # renovate: datasource=github-releases depName=mailergroup/pre-commit-branch-name-validation 40 | rev: v1.1.0 41 | hooks: 42 | - id: pre-commit-branch-validation 43 | stages: [post-commit, post-checkout] 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 MailerSend 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | MailerSend Python SDK 4 | 5 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) 6 | 7 | # Table of Contents 8 | - [Table of Contents](#table-of-contents) 9 | - [Installation](#installation) 10 | - [Requirements](#requirements) 11 | - [Authentication](#authentication) 12 | - [Usage](#usage) 13 | - [Email](#email) 14 | - [Send an email](#send-an-email) 15 | - [Add CC, BCC recipients](#add-cc-bcc-recipients) 16 | - [Send a template-based email](#send-a-template-based-email) 17 | - [Personalization](#personalization) 18 | - [Send email with attachment](#send-email-with-attachment) 19 | - [Email Verification](#email-verification) 20 | - [Get all email verification lists](#get-all-email-verification-lists) 21 | - [Get a single email verification list](#get-a-single-email-verification-list) 22 | - [Create a email verification list](#create-a-email-verification-list) 23 | - [Verify a list](#verify-a-list) 24 | - [Get list results](#get-list-results) 25 | - [Bulk Email](#bulk-email) 26 | - [Send bulk email](#send-bulk-email) 27 | - [Get bulk email status](#get-bulk-email-status) 28 | - [Activity](#activity) 29 | - [Get a list of activities (simple)](#get-a-list-of-activities-simple) 30 | - [Get a list of activities (full)](#get-a-list-of-activities-full) 31 | - [Analytics](#analytics) 32 | - [Activity data by date](#activity-data-by-date) 33 | - [Opens by country](#opens-by-country) 34 | - [Opens by user-agent name](#opens-by-user-agent-name) 35 | - [Opens by reading environment](#opens-by-reading-environment) 36 | - [Inbound Routes](#inbound-routes) 37 | - [Get a list of inbound routes](#get-a-list-of-inbound-routes) 38 | - [Get a single inbound route](#get-a-single-inbound-route) 39 | - [Add an inbound route](#add-an-inbound-route) 40 | - [Update an inbound route](#update-an-inbound-route) 41 | - [Delete an inbound route](#delete-an-inbound-route) 42 | - [Domains](#domains) 43 | - [Get a list of domains](#get-a-list-of-domains) 44 | - [Get a single domain](#get-a-single-domain) 45 | - [Get a single domain using helper function](#get-a-single-domain-using-helper-function) 46 | - [Add a domain](#add-a-domain) 47 | - [Delete a domain](#delete-a-domain) 48 | - [Get a list of recipients per domain](#get-a-list-of-recipients-per-domain) 49 | - [Update domain settings](#update-domain-settings) 50 | - [Get DNS Records](#get-dns-records) 51 | - [Verify a domain](#verify-a-domain) 52 | - [Messages](#messages) 53 | - [Get a list of messages](#get-a-list-of-messages) 54 | - [Get a single message](#get-a-single-message) 55 | - [Scheduled messages](#scheduled-messages) 56 | - [Get a list of scheduled messages](#get-a-list-of-scheduled-messages) 57 | - [Get a single scheduled message](#get-a-single-scheduled-message) 58 | - [Delete a scheduled message](#delete-a-scheduled-message) 59 | - [Recipients](#recipients) 60 | - [Get a list of recipients](#get-a-list-of-recipients) 61 | - [Get a single recipient](#get-a-single-recipient) 62 | - [Delete a recipient](#delete-a-recipient) 63 | - [Get recipients from a blocklist](#get-recipients-from-a-blocklist) 64 | - [Get recipients from hard bounces](#get-recipients-from-hard-bounces) 65 | - [Get recipients from spam complaints](#get-recipients-from-spam-complaints) 66 | - [Get recipients from unsubscribes](#get-recipients-from-unsubscribes) 67 | - [Add recipients to blocklist](#add-recipients-to-blocklist) 68 | - [Add hard bounced recipients](#add-hard-bounced-recipients) 69 | - [Add spam complaints](#add-spam-complaints) 70 | - [Add recipients to unsubscribe list](#add-recipients-to-unsubscribe-list) 71 | - [Delete recipients from blocklist](#delete-recipients-from-blocklist) 72 | - [Delete hard bounced recipients](#delete-hard-bounced-recipients) 73 | - [Delete spam complaints](#delete-spam-complaints) 74 | - [Delete recipients from unsubscribe list](#delete-recipients-from-unsubscribe-list) 75 | - [Tokens](#tokens) 76 | - [Create a token](#create-a-token) 77 | - [Pause / Unpause Token](#pause--unpause-token) 78 | - [Delete a Token](#delete-a-token) 79 | - [Templates](#templates) 80 | - [Get a list of templates](#get-a-list-of-templates) 81 | - [Get a single template](#get-a-single-template) 82 | - [Delete template](#delete-template) 83 | - [Webhooks](#webhooks) 84 | - [Get a list of webhooks](#get-a-list-of-webhooks) 85 | - [Get a single webhook](#get-a-single-webhook) 86 | - [Create a Webhook](#create-a-webhook) 87 | - [Create a disabled webhook](#create-a-disabled-webhook) 88 | - [Update a Webhook](#update-a-webhook) 89 | - [Disable/Enable a Webhook](#disableenable-a-webhook) 90 | - [Delete a Webhook](#delete-a-webhook) 91 | - [SMS](#sms) 92 | - [Sending SMS messages](#sending-sms-messages) 93 | - [SMS Activity](#sms-activity) 94 | - [Get a list of activities](#get-a-list-of-activities) 95 | - [Get activity of a single message](#get-activity-of-a-single-message) 96 | - [SMS Phone Numbers](#sms-phone-numbers) 97 | - [Get a list of SMS phone numbers](#get-a-list-of-sms-phone-numbers) 98 | - [Get an SMS phone number](#get-an-sms-phone-number) 99 | - [Update a single SMS phone number](#update-a-single-sms-phone-number) 100 | - [Delete an SMS phone number](#delete-an-sms-phone-number) 101 | - [SMS Recipients](#sms-recipients) 102 | - [Get a list of SMS recipients](#get-a-list-of-sms-recipients) 103 | - [Get an SMS recipient](#get-an-sms-recipient) 104 | - [Update a single SMS recipient](#update-a-single-sms-recipient) 105 | - [SMS Messages](#sms-messages) 106 | - [Get a list of SMS messages](#get-a-list-of-sms-messages) 107 | - [Get an SMS message](#get-an-sms-message) 108 | - [SMS Webhooks](#sms-webhooks) 109 | - [Get a list of SMS webhooks](#get-a-list-of-sms-webhooks) 110 | - [Get a single SMS webhook](#get-a-single-sms-webhook) 111 | - [Create an SMS webhook](#create-an-sms-webhook) 112 | - [Update a single SMS webhook](#update-a-single-sms-webhook) 113 | - [Delete an SMS webhook](#delete-an-sms-webhook) 114 | - [Get a list of SMS webhooks](#get-a-list-of-sms-webhooks-1) 115 | - [SMS Inbouds](#sms-inbouds) 116 | - [Get a list of SMS inbound routes](#get-a-list-of-sms-inbound-routes) 117 | - [Get a single SMS inbound route](#get-a-single-sms-inbound-route) 118 | - [Create an SMS inbound route](#create-an-sms-inbound-route) 119 | - [Update an SMS inbound route](#update-an-sms-inbound-route) 120 | - [Delete an SMS inbound route](#delete-an-sms-inbound-route) 121 | - [Sender Identities](#sender-identities) 122 | - [Get a list of sender identities](#get-a-list-of-sender-identities) 123 | - [Get a sender identity](#get-a-sender-identity) 124 | - [Create a sender identity](#create-a-sender-identity) 125 | - [Update a sender identity](#update-a-sender-identity) 126 | - [Delete a sender identity](#delete-a-sender-identity) 127 | - [API Quota](#api-quota) 128 | - [Get API Quota](#get-api-quota) 129 | - [Troubleshooting](#troubleshooting) 130 | - [Emails not being sent](#emails-not-being-sent) 131 | - [Testing](#testing) 132 | - [Available endpoints](#available-endpoints) 133 | - [Support and Feedback](#support-and-feedback) 134 | - [License](#license) 135 | 136 | 137 | 138 | # Installation 139 | 140 | ``` 141 | $ python -m pip install mailersend 142 | ``` 143 | 144 | ## Requirements 145 | 146 | - Python > 3.6.1 147 | - Python `pip` 148 | - An API Key from [mailersend.com](https://www.mailersend.com) 149 | 150 | ## Authentication 151 | 152 | We recommend you to define `MAILERSEND_API_KEY` environment variable in the `.env` file, and use it to store the API key. 153 | 154 | - Using environment variable 155 | ```python 156 | from mailersend import emails 157 | 158 | # assigning NewEmail() without params defaults to MAILERSEND_API_KEY env var 159 | mailer = emails.NewEmail() 160 | 161 | # define an empty dict to populate with mail values 162 | mail_body = {} 163 | 164 | mail_from = { 165 | "name": "Your Name", 166 | "email": "your@domain.com", 167 | } 168 | 169 | recipients = [ 170 | { 171 | "name": "Your Client", 172 | "email": "your@client.com", 173 | } 174 | ] 175 | 176 | reply_to = { 177 | "name": "Name", 178 | "email": "reply@domain.com", 179 | } 180 | 181 | mailer.set_mail_from(mail_from, mail_body) 182 | mailer.set_mail_to(recipients, mail_body) 183 | mailer.set_subject("Hello!", mail_body) 184 | mailer.set_html_content("This is the HTML content", mail_body) 185 | mailer.set_plaintext_content("This is the text content", mail_body) 186 | mailer.set_reply_to(reply_to, mail_body) 187 | 188 | # using print() will also return status code and data 189 | mailer.send(mail_body) 190 | ``` 191 | 192 | - Explicit declaration 193 | ```python 194 | from mailersend import emails 195 | 196 | mailer = emails.NewEmail("my-api-key") 197 | 198 | # define an empty dict to populate with mail values 199 | mail_body = {} 200 | 201 | mail_from = { 202 | "name": "Your Name", 203 | "email": "your@domain.com", 204 | } 205 | 206 | recipients = [ 207 | { 208 | "name": "Your Client", 209 | "email": "your@client.com", 210 | } 211 | ] 212 | 213 | reply_to = { 214 | "name": "Name", 215 | "email": "reply@domain.com", 216 | } 217 | 218 | mailer.set_mail_from(mail_from, mail_body) 219 | mailer.set_mail_to(recipients, mail_body) 220 | mailer.set_subject("Hello!", mail_body) 221 | mailer.set_html_content("This is the HTML content", mail_body) 222 | mailer.set_plaintext_content("This is the text content", mail_body) 223 | mailer.set_reply_to(reply_to, mail_body) 224 | 225 | # using print() will also return status code and data 226 | mailer.send(mail_body) 227 | ``` 228 | 229 | # Usage 230 | 231 | ## Email 232 | 233 | ### Send an email 234 | 235 | ```python 236 | from mailersend import emails 237 | from dotenv import load_dotenv 238 | 239 | load_dotenv() 240 | 241 | mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY')) 242 | 243 | # define an empty dict to populate with mail values 244 | mail_body = {} 245 | 246 | mail_from = { 247 | "name": "Your Name", 248 | "email": "your@domain.com", 249 | } 250 | 251 | recipients = [ 252 | { 253 | "name": "Your Client", 254 | "email": "your@client.com", 255 | } 256 | ] 257 | 258 | reply_to = { 259 | "name": "Name", 260 | "email": "reply@domain.com", 261 | } 262 | 263 | mailer.set_mail_from(mail_from, mail_body) 264 | mailer.set_mail_to(recipients, mail_body) 265 | mailer.set_subject("Hello!", mail_body) 266 | mailer.set_html_content("This is the HTML content", mail_body) 267 | mailer.set_plaintext_content("This is the text content", mail_body) 268 | mailer.set_reply_to(reply_to, mail_body) 269 | 270 | # using print() will also return status code and data 271 | mailer.send(mail_body) 272 | ``` 273 | 274 | ### Add CC, BCC recipients 275 | 276 | ```python 277 | from mailersend import emails 278 | from dotenv import load_dotenv 279 | 280 | load_dotenv() 281 | 282 | mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY')) 283 | 284 | # define an empty dict to populate with mail values 285 | mail_body = {} 286 | 287 | mail_from = { 288 | "name": "Your Name", 289 | "email": "your@domain.com", 290 | } 291 | 292 | recipients = [ 293 | { 294 | "name": "Your Client", 295 | "email": "your@client.com", 296 | } 297 | ] 298 | 299 | cc = [ 300 | { 301 | "name": "CC", 302 | "email": "cc@client.com" 303 | } 304 | ] 305 | 306 | bcc = [ 307 | { 308 | "name": "BCC", 309 | "email": "bcc@client.com" 310 | } 311 | ] 312 | 313 | mailer.set_mail_from(mail_from, mail_body) 314 | mailer.set_mail_to(recipients, mail_body) 315 | mailer.set_subject("Hello!", mail_body) 316 | mailer.set_html_content("This is the HTML content", mail_body) 317 | mailer.set_plaintext_content("This is the text content", mail_body) 318 | mailer.set_cc_recipients(cc, mail_body) 319 | mailer.set_bcc_recipients(bcc, mail_body) 320 | 321 | mailer.send(mail_body) 322 | ``` 323 | 324 | ### Send a template-based email 325 | 326 | ```python 327 | from mailersend import emails 328 | from dotenv import load_dotenv 329 | 330 | load_dotenv() 331 | 332 | mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY')) 333 | 334 | # define an empty dict to populate with mail values 335 | mail_body = {} 336 | 337 | mail_from = { 338 | "name": "Your Name", 339 | "email": "your@domain.com", 340 | } 341 | 342 | recipients = [ 343 | { 344 | "name": "Your Client", 345 | "email": "your@client.com", 346 | } 347 | ] 348 | 349 | 350 | variables = [ 351 | { 352 | "email": "your@client.com", 353 | "substitutions": [ 354 | { 355 | "var": "foo", 356 | "value": "bar" 357 | }, 358 | ] 359 | } 360 | ] 361 | 362 | 363 | mailer.set_mail_from(mail_from, mail_body) 364 | mailer.set_mail_to(recipients, mail_body) 365 | mailer.set_subject("Hello from {$company}", mail_body) 366 | mailer.set_template("templateID", mail_body) 367 | mailer.set_personalization(variables, mail_body) 368 | 369 | mailer.send(mail_body) 370 | ``` 371 | 372 | ### Personalization 373 | 374 | ```python 375 | from mailersend import emails 376 | from dotenv import load_dotenv 377 | 378 | load_dotenv() 379 | 380 | mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY')) 381 | 382 | # define an empty dict to populate with mail values 383 | mail_body = {} 384 | 385 | mail_from = { 386 | "name": "Your Name", 387 | "email": "your@domain.com", 388 | } 389 | 390 | recipients = [ 391 | { 392 | "name": "Your Client", 393 | "email": "your@client.com", 394 | } 395 | ] 396 | 397 | personalization = [ 398 | { 399 | "email": "test@mailersend.com", 400 | "data": { 401 | "var": "value", 402 | "boolean": True, 403 | "object": { 404 | "key" : "object-value" 405 | }, 406 | "number": 2, 407 | "array": [ 408 | 1, 409 | 2, 410 | 3 411 | ] 412 | } 413 | } 414 | ] 415 | 416 | 417 | mailer.set_mail_from(mail_from, mail_body) 418 | mailer.set_mail_to(recipients, mail_body) 419 | mailer.set_subject("Hello from {$company}", mail_body) 420 | mailer.set_html_content("This is the HTML content, {$name}", mail_body) 421 | mailer.set_plaintext_content("This is the text content, {$name}", mail_body) 422 | mailer.set_personalization(personalization, mail_body) 423 | 424 | mailer.send(mail_body) 425 | ``` 426 | 427 | ### Send email with attachment 428 | 429 | ```python 430 | from mailersend import emails 431 | import base64 432 | from dotenv import load_dotenv 433 | 434 | load_dotenv() 435 | 436 | mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY')) 437 | 438 | # define an empty dict to populate with mail values 439 | mail_body = {} 440 | 441 | mail_from = { 442 | "name": "Your Name", 443 | "email": "your@domain.com", 444 | } 445 | 446 | recipients = [ 447 | { 448 | "name": "Your Client", 449 | "email": "your@client.com", 450 | } 451 | ] 452 | 453 | variables = [ 454 | { 455 | "email": "your@client.com", 456 | "substitutions": [ 457 | { 458 | "var": "foo", 459 | "value": "bar" 460 | }, 461 | ] 462 | } 463 | ] 464 | 465 | attachment = open('path-to-file', 'rb') 466 | att_read = attachment.read() 467 | att_base64 = base64.b64encode(bytes(att_read)) 468 | attachments = [ 469 | { 470 | "id": "my-attached-file", 471 | "filename": "file.jpg", 472 | "content": f"{att_base64.decode('ascii')}", 473 | "disposition": "attachment" 474 | } 475 | ] 476 | 477 | mailer.set_mail_from(mail_from, mail_body) 478 | mailer.set_mail_to(recipients, mail_body) 479 | mailer.set_subject("Hello from {$foo}", mail_body) 480 | mailer.set_html_content("This is the HTML content, {$foo}", mail_body) 481 | mailer.set_plaintext_content("This is the text content, {$foo}", mail_body) 482 | mailer.set_personalization(variables, mail_body) 483 | mailer.set_attachments(attachments, mail_body) 484 | 485 | mailer.send(mail_body) 486 | ``` 487 | 488 | ## Email Verification 489 | 490 | ### Get all email verification lists 491 | 492 | ```python 493 | from mailersend import email_verification 494 | from dotenv import load_dotenv 495 | 496 | load_dotenv() 497 | 498 | mailer = email_verification.NewEmailVerification(os.getenv('MAILERSEND_API_KEY')) 499 | 500 | mailer.get_all_lists() 501 | ``` 502 | 503 | ### Get a single email verification list 504 | 505 | ```python 506 | from mailersend import email_verification 507 | from dotenv import load_dotenv 508 | 509 | load_dotenv() 510 | 511 | mailer = email_verification.NewEmailVerification(os.getenv('MAILERSEND_API_KEY')) 512 | 513 | email_verification_list_id = 123456 514 | 515 | mailer.get_list(email_verification_list_id) 516 | ``` 517 | 518 | ### Create a email verification list 519 | 520 | ```python 521 | from mailersend import email_verification 522 | from dotenv import load_dotenv 523 | 524 | load_dotenv() 525 | 526 | mailer = email_verification.NewEmailVerification(os.getenv('MAILERSEND_API_KEY')) 527 | 528 | name = "My List" 529 | emails = [ 530 | "some@email.com", 531 | "another@email.com" 532 | ] 533 | 534 | mailer.create_list(name, emails) 535 | ``` 536 | 537 | ### Verify a list 538 | 539 | ```python 540 | from mailersend import email_verification 541 | from dotenv import load_dotenv 542 | 543 | load_dotenv() 544 | 545 | mailer = email_verification.NewEmailVerification(os.getenv('MAILERSEND_API_KEY')) 546 | 547 | email_verification_list_id = 123456 548 | 549 | mailer.verify_list(email_verification_list_id) 550 | ``` 551 | 552 | ### Get list results 553 | 554 | ```python 555 | from mailersend import email_verification 556 | from dotenv import load_dotenv 557 | 558 | load_dotenv() 559 | 560 | mailer = email_verification.NewEmailVerification(os.getenv('MAILERSEND_API_KEY')) 561 | 562 | email_verification_list_id = 123456 563 | 564 | mailer.get_list_results(email_verification_list_id) 565 | ``` 566 | 567 | 568 | ## Bulk Email 569 | 570 | ### Send bulk email 571 | 572 | ```python 573 | from mailersend import emails 574 | from dotenv import load_dotenv 575 | 576 | load_dotenv() 577 | 578 | mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY')) 579 | 580 | mail_list = [ 581 | { 582 | "from": { 583 | "email": "your@domain.com", 584 | "name": "Your Name" 585 | }, 586 | "to": [ 587 | { 588 | "email": "your@client.com", 589 | "name": "Your Client" 590 | } 591 | ], 592 | "subject": "Subject", 593 | "text": "This is the text content", 594 | "html": "

This is the HTML content

", 595 | }, 596 | { 597 | "from": { 598 | "email": "your@domain.com", 599 | "name": "Your Name" 600 | }, 601 | "to": [ 602 | { 603 | "email": "your@client.com", 604 | "name": "Your Client" 605 | } 606 | ], 607 | "subject": "Subject", 608 | "text": "This is the text content", 609 | "html": "

This is the HTML content

", 610 | } 611 | ] 612 | 613 | print(mailer.send_bulk(mail_list)) 614 | ``` 615 | 616 | ### Get bulk email status 617 | 618 | ```python 619 | from mailersend import emails 620 | from dotenv import load_dotenv 621 | 622 | load_dotenv() 623 | 624 | mailer = emails.NewEmail(os.getenv('MAILERSEND_API_KEY')) 625 | 626 | print(mailer.get_bulk_status_by_id("bulk-email-id")) 627 | ``` 628 | 629 | 630 | 631 | ## Activity 632 | 633 | ### Get a list of activities (simple) 634 | 635 | ```python 636 | from mailersend import activity 637 | from dotenv import load_dotenv 638 | 639 | load_dotenv() 640 | 641 | mailer = activity.NewActivity(os.getenv('MAILERSEND_API_KEY')) 642 | 643 | mailer.get_domain_activity("domain-id") 644 | ``` 645 | 646 | ### Get a list of activities (full) 647 | 648 | ```python 649 | from mailersend import activity 650 | from dotenv import load_dotenv 651 | 652 | load_dotenv() 653 | 654 | mailer = activity.NewActivity(os.getenv('MAILERSEND_API_KEY')) 655 | 656 | page = 1 657 | limit = 20 658 | date_from = 1623073576 659 | date_to = 1623074976 660 | events = [ 661 | "queued", 662 | "sent", 663 | "delivered", 664 | "soft-bounced", 665 | "hard-bounced", 666 | "junk", 667 | "opened", 668 | "clicked", 669 | "unsubscribed", 670 | "spam_complaints", 671 | ] 672 | 673 | mailer.get_domain_activity("domain-id", page, limit, date_from, date_to, events) 674 | ``` 675 | 676 | ## Analytics 677 | 678 | ### Activity data by date 679 | 680 | ```python 681 | from mailersend import analytics 682 | from dotenv import load_dotenv 683 | 684 | load_dotenv() 685 | 686 | mailer = analytics.NewAnalytics(os.getenv('MAILERSEND_API_KEY')) 687 | 688 | date_from = 1623073576 689 | date_to = 1623074976 690 | events = [ 691 | "sent", 692 | ] 693 | 694 | # optional arguments 695 | domain_id = "domain-id" 696 | group_by = "days" 697 | 698 | mailer.get_activity_by_date(date_from, date_to, events, domain_id, group_by) 699 | ``` 700 | 701 | ### Opens by country 702 | 703 | ```python 704 | from mailersend import analytics 705 | from dotenv import load_dotenv 706 | 707 | load_dotenv() 708 | 709 | mailer = analytics.NewAnalytics(os.getenv('MAILERSEND_API_KEY')) 710 | 711 | date_from = 1623073576 712 | date_to = 1623074976 713 | 714 | # optional arguments 715 | domain_id = "domain-id" 716 | 717 | mailer.get_opens_by_country(date_from, date_to, domain_id) 718 | ``` 719 | 720 | ### Opens by user-agent name 721 | 722 | ```python 723 | from mailersend import analytics 724 | from dotenv import load_dotenv 725 | 726 | load_dotenv() 727 | 728 | mailer = analytics.NewAnalytics(os.getenv('MAILERSEND_API_KEY')) 729 | 730 | date_from = 1623073576 731 | date_to = 1623074976 732 | 733 | # optional arguments 734 | domain_id = "domain-id" 735 | 736 | mailer.get_opens_by_user_agent(date_from, date_to, domain_id) 737 | ``` 738 | 739 | ### Opens by reading environment 740 | 741 | ```python 742 | from mailersend import analytics 743 | from dotenv import load_dotenv 744 | 745 | load_dotenv() 746 | 747 | mailer = analytics.NewAnalytics(os.getenv('MAILERSEND_API_KEY')) 748 | 749 | date_from = 1623073576 750 | date_to = 1623074976 751 | 752 | # optional arguments 753 | domain_id = "domain-id" 754 | 755 | mailer.get_opens_by_reading_environment(date_from, date_to, domain_id) 756 | ``` 757 | 758 | ## Inbound Routes 759 | 760 | ### Get a list of inbound routes 761 | 762 | ```python 763 | from mailersend import inbound_routing 764 | from dotenv import load_dotenv 765 | 766 | load_dotenv() 767 | 768 | mailer = inbound_routing.NewInbound(os.getenv('MAILERSEND_API_KEY')) 769 | 770 | print(mailer.get_inbound_routes()) 771 | ``` 772 | 773 | ### Get a single inbound route 774 | 775 | ```python 776 | from mailersend import inbound_routing 777 | from dotenv import load_dotenv 778 | 779 | load_dotenv() 780 | 781 | mailer = inbound_routing.NewInbound(os.getenv('MAILERSEND_API_KEY')) 782 | 783 | print(mailer.get_inbound_by_id("inbound-id")) 784 | ``` 785 | 786 | ### Add an inbound route 787 | 788 | ```python 789 | from mailersend import inbound_routing 790 | from dotenv import load_dotenv 791 | 792 | load_dotenv() 793 | 794 | mailer = inbound_routing.NewInbound(os.getenv('MAILERSEND_API_KEY')) 795 | 796 | options = {} 797 | 798 | _catch_filter = { 799 | "type": "catch_recipient", 800 | "filters": [ 801 | { 802 | "comparer": "equal", 803 | "value": "test" 804 | } 805 | ] 806 | } 807 | 808 | _match_filter = { 809 | "type": "match_all" 810 | } 811 | 812 | _forwards = [ 813 | { 814 | "type": "webhook", 815 | "value": "https://www.mailersend.com/hook" 816 | } 817 | ] 818 | mailer.set_name("Example route", options) 819 | mailer.set_domain_enabled(True, options) 820 | mailer.set_inbound_domain("test.mailersend.com", options) 821 | mailer.set_catch_filter(_catch_filter, options) 822 | 823 | print(mailer.add_inbound_route()) 824 | ``` 825 | 826 | ### Update an inbound route 827 | 828 | ```python 829 | from mailersend import inbound_routing 830 | from dotenv import load_dotenv 831 | 832 | load_dotenv() 833 | 834 | route_id = "inbound-route-id" 835 | 836 | mailer = inbound_routing.NewInbound(os.getenv('MAILERSEND_API_KEY')) 837 | 838 | options = {} 839 | 840 | _catch_filter = { 841 | "type": "catch_recipient", 842 | "filters": [ 843 | { 844 | "comparer": "equal", 845 | "value": "test" 846 | } 847 | ] 848 | } 849 | 850 | _match_filter = { 851 | "type": "match_all" 852 | } 853 | 854 | _forwards = [ 855 | { 856 | "type": "webhook", 857 | "value": "https://www.mailersend.com/hook" 858 | } 859 | ] 860 | mailer.set_name("Example route", options) 861 | mailer.set_domain_enabled(True, options) 862 | mailer.set_inbound_domain("test.mailersend.com", options) 863 | mailer.set_catch_filter(_catch_filter, options) 864 | 865 | print(mailer.update_inbound_route(route_id)) 866 | ``` 867 | 868 | ### Delete an inbound route 869 | 870 | ```python 871 | from mailersend import inbound_routing 872 | from dotenv import load_dotenv 873 | 874 | load_dotenv() 875 | 876 | route_id = "inbound-route-id" 877 | 878 | mailer = inbound_routing.NewInbound(os.getenv('MAILERSEND_API_KEY')) 879 | 880 | print(mailer.delete_inbound_route(route_id)) 881 | ``` 882 | 883 | ## Domains 884 | 885 | ### Get a list of domains 886 | 887 | ```python 888 | from mailersend import domains 889 | from dotenv import load_dotenv 890 | 891 | load_dotenv() 892 | 893 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 894 | 895 | mailer.get_domains() 896 | ``` 897 | 898 | ### Get a single domain 899 | 900 | ```python 901 | from mailersend import domains 902 | from dotenv import load_dotenv 903 | 904 | load_dotenv() 905 | 906 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 907 | 908 | mailer.get_domain_by_id("domain-id") 909 | ``` 910 | 911 | ### Get a single domain using helper function 912 | 913 | ```python 914 | from mailersend import domains 915 | from mailersend import utils 916 | from dotenv import load_dotenv 917 | 918 | load_dotenv() 919 | 920 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 921 | helper = utils.NewHelper(os.getenv('MAILERSEND_API_KEY')) 922 | 923 | mailer.get_domain_by_id(helper.get_id_by_name("domains","domain-name")) 924 | ``` 925 | 926 | ### Add a domain 927 | 928 | You can find a full list of settings [here](https://developers.mailersend.com/api/v1/domains.html#request-parameters-3). 929 | 930 | ```python 931 | from mailersend import domains 932 | from dotenv import load_dotenv 933 | 934 | load_dotenv() 935 | 936 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 937 | 938 | domain_data = { 939 | "name": "mydomain.com", 940 | "return_path_subdomain": "rpsubdomain", 941 | "custom_tracking_subdomain": "ctsubdomain", 942 | "inbound_routing_subdomain": "irsubdomain" 943 | } 944 | mailer.add_domain("name", domain_data) 945 | ``` 946 | 947 | 948 | ### Delete a domain 949 | 950 | ```python 951 | from mailersend import domains 952 | from dotenv import load_dotenv 953 | 954 | load_dotenv() 955 | 956 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 957 | 958 | mailer.delete_domain("domain-id") 959 | ``` 960 | 961 | ### Get a list of recipients per domain 962 | 963 | ```python 964 | from mailersend import domains 965 | from dotenv import load_dotenv 966 | 967 | load_dotenv() 968 | 969 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 970 | 971 | mailer.get_recipients_for_domain("domain-id") 972 | ``` 973 | 974 | ### Update domain settings 975 | 976 | You can find a full list of settings [here](https://developers.mailersend.com/api/v1/domains.html#request-body). 977 | 978 | ```python 979 | from mailersend import domains 980 | from dotenv import load_dotenv 981 | 982 | load_dotenv() 983 | 984 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 985 | 986 | domain_data = { 987 | "send_paused": True, 988 | "track_clicks": True, 989 | "track_opens": True, 990 | "track_unsubscribe": True, 991 | "track_unsubscribe_html": "

Click to unsubscribe

", 992 | "track_unsubscribe_plain": "Click to unsubscribe: {$unsubscribe}", 993 | "track_content": True, 994 | "custom_tracking_enabled": True, 995 | "custom_tracking_subdomain": "email", 996 | "precedence_bulk": False 997 | } 998 | 999 | mailer.update_domain_setting("domain-id", domain_data) 1000 | ``` 1001 | 1002 | ### Get DNS Records 1003 | 1004 | ```python 1005 | from mailersend import domains 1006 | from dotenv import load_dotenv 1007 | 1008 | load_dotenv() 1009 | 1010 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 1011 | 1012 | mailer.get_dns_records("domain-id") 1013 | ``` 1014 | 1015 | ### Verify a domain 1016 | 1017 | ```python 1018 | from mailersend import domains 1019 | from dotenv import load_dotenv 1020 | 1021 | load_dotenv() 1022 | 1023 | mailer = domains.NewDomain(os.getenv('MAILERSEND_API_KEY')) 1024 | 1025 | mailer.verify_domain("domain-id") 1026 | ``` 1027 | 1028 | 1029 | ## Messages 1030 | 1031 | ### Get a list of messages 1032 | 1033 | ```python 1034 | from mailersend import messages 1035 | from dotenv import load_dotenv 1036 | 1037 | load_dotenv() 1038 | 1039 | mailer = messages.NewMessage(os.getenv('MAILERSEND_API_KEY')) 1040 | 1041 | mailer.get_messages() 1042 | ``` 1043 | 1044 | ### Get a single message 1045 | 1046 | ```python 1047 | from mailersend import messages 1048 | from dotenv import load_dotenv 1049 | 1050 | load_dotenv() 1051 | 1052 | mailer = messages.NewMessage(os.getenv('MAILERSEND_API_KEY')) 1053 | 1054 | mailer.get_message_by_id("message-id") 1055 | ``` 1056 | 1057 | ## Scheduled messages 1058 | 1059 | ### Get a list of scheduled messages 1060 | 1061 | ```python 1062 | from mailersend import scheduled_messages 1063 | from dotenv import load_dotenv 1064 | 1065 | load_dotenv() 1066 | 1067 | mailer = scheduled_messages.NewMessageSchedule(os.getenv('MAILERSEND_API_KEY')) 1068 | 1069 | print(mailer.get_scheduled_messages()) 1070 | ``` 1071 | 1072 | ### Get a single scheduled message 1073 | 1074 | ```python 1075 | from mailersend import scheduled_messages 1076 | from dotenv import load_dotenv 1077 | 1078 | load_dotenv() 1079 | 1080 | mailer = scheduled_messages.NewMessageSchedule(os.getenv('MAILERSEND_API_KEY')) 1081 | 1082 | print(mailer.get_scheduled_message_by_id("scheduled-message-id")) 1083 | ``` 1084 | 1085 | ### Delete a scheduled message 1086 | 1087 | ```python 1088 | from mailersend import scheduled_messages 1089 | from dotenv import load_dotenv 1090 | 1091 | load_dotenv() 1092 | 1093 | mailer = scheduled_messages.NewMessageSchedule(os.getenv('MAILERSEND_API_KEY')) 1094 | 1095 | print(mailer.delete_scheduled_message("scheduled-message-id")) 1096 | ``` 1097 | 1098 | ## Recipients 1099 | 1100 | ### Get a list of recipients 1101 | 1102 | ```python 1103 | from mailersend import recipients 1104 | from dotenv import load_dotenv 1105 | 1106 | load_dotenv() 1107 | 1108 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1109 | 1110 | mailer.get_recipients() 1111 | ``` 1112 | 1113 | ### Get a single recipient 1114 | 1115 | ```python 1116 | from mailersend import recipients 1117 | from dotenv import load_dotenv 1118 | 1119 | load_dotenv() 1120 | 1121 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1122 | 1123 | mailer.get_recipient_by_id("recipient-id") 1124 | ``` 1125 | 1126 | ### Delete a recipient 1127 | 1128 | ```python 1129 | from mailersend import recipients 1130 | from dotenv import load_dotenv 1131 | 1132 | load_dotenv() 1133 | 1134 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1135 | 1136 | mailer.delete_recipient("recipient-id") 1137 | ``` 1138 | 1139 | ### Get recipients from a blocklist 1140 | 1141 | ```python 1142 | from mailersend import recipients 1143 | from dotenv import load_dotenv 1144 | 1145 | load_dotenv() 1146 | 1147 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1148 | 1149 | mailer.get_recipients_from_blocklist("domain-id") 1150 | ``` 1151 | 1152 | ### Get recipients from hard bounces 1153 | 1154 | ```python 1155 | from mailersend import recipients 1156 | from dotenv import load_dotenv 1157 | 1158 | load_dotenv() 1159 | 1160 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1161 | 1162 | mailer.get_hard_bounces("domain-id") 1163 | ``` 1164 | 1165 | ### Get recipients from spam complaints 1166 | 1167 | ```python 1168 | from mailersend import recipients 1169 | from dotenv import load_dotenv 1170 | 1171 | load_dotenv() 1172 | 1173 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1174 | 1175 | mailer.get_spam_complaints("domain-id") 1176 | ``` 1177 | 1178 | ### Get recipients from unsubscribes 1179 | 1180 | ```python 1181 | from mailersend import recipients 1182 | from dotenv import load_dotenv 1183 | 1184 | load_dotenv() 1185 | 1186 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1187 | 1188 | mailer.get_unsubscribes("domain-id") 1189 | ``` 1190 | 1191 | ### Add recipients to blocklist 1192 | 1193 | Using recipients: 1194 | 1195 | ```python 1196 | from mailersend import recipients 1197 | from dotenv import load_dotenv 1198 | 1199 | load_dotenv() 1200 | 1201 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1202 | 1203 | recipient_list = [ 1204 | 'blocked@client.com' 1205 | ] 1206 | 1207 | mailer.add_to_blocklist("domain-id", recipients=recipient_list) 1208 | ``` 1209 | 1210 | Using patterns: 1211 | 1212 | ```python 1213 | from mailersend import recipients 1214 | from dotenv import load_dotenv 1215 | 1216 | load_dotenv() 1217 | 1218 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1219 | 1220 | recipient_patterns = [ 1221 | '*@client.com' 1222 | ] 1223 | 1224 | mailer.add_to_blocklist("domain-id", patterns=recipient_patterns) 1225 | ``` 1226 | 1227 | ### Add hard bounced recipients 1228 | 1229 | ```python 1230 | from mailersend import recipients 1231 | from dotenv import load_dotenv 1232 | 1233 | load_dotenv() 1234 | 1235 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1236 | 1237 | recipient_list = [ 1238 | "your@client.com" 1239 | ] 1240 | 1241 | mailer.add_hard_bounces("domain-id", recipient_list) 1242 | ``` 1243 | 1244 | ### Add spam complaints 1245 | 1246 | ```python 1247 | from mailersend import recipients 1248 | from dotenv import load_dotenv 1249 | 1250 | load_dotenv() 1251 | 1252 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1253 | 1254 | recipient_list = [ 1255 | "your@client.com" 1256 | ] 1257 | 1258 | mailer.add_spam_complaints("domain-id", recipient_list) 1259 | ``` 1260 | 1261 | ### Add recipients to unsubscribe list 1262 | 1263 | ```python 1264 | from mailersend import recipients 1265 | from dotenv import load_dotenv 1266 | 1267 | load_dotenv() 1268 | 1269 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1270 | 1271 | recipient_list = [ 1272 | "your@client.com" 1273 | ] 1274 | 1275 | mailer.add_unsubscribes("domain-id", recipient_list) 1276 | ``` 1277 | 1278 | ### Delete recipients from blocklist 1279 | 1280 | ```python 1281 | from mailersend import recipients 1282 | from dotenv import load_dotenv 1283 | 1284 | load_dotenv() 1285 | 1286 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1287 | 1288 | recipient_list = [ 1289 | "your@client.com" 1290 | ] 1291 | 1292 | mailer.delete_from_blocklist("domain-id", recipient_list) 1293 | ``` 1294 | 1295 | ### Delete hard bounced recipients 1296 | 1297 | ```python 1298 | from mailersend import recipients 1299 | from dotenv import load_dotenv 1300 | 1301 | load_dotenv() 1302 | 1303 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1304 | 1305 | recipient_list = [ 1306 | "your@client.com" 1307 | ] 1308 | 1309 | mailer.delete_hard_bounces("domain-id", recipient_list) 1310 | ``` 1311 | 1312 | ### Delete spam complaints 1313 | 1314 | ```python 1315 | from mailersend import recipients 1316 | from dotenv import load_dotenv 1317 | 1318 | load_dotenv() 1319 | 1320 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1321 | 1322 | recipient_list = [ 1323 | "your@client.com" 1324 | ] 1325 | 1326 | mailer.delete_spam_complaints("domain-id", recipient_list) 1327 | ``` 1328 | 1329 | ### Delete recipients from unsubscribe list 1330 | 1331 | ```python 1332 | from mailersend import recipients 1333 | from dotenv import load_dotenv 1334 | 1335 | load_dotenv() 1336 | 1337 | mailer = recipients.NewRecipient(os.getenv('MAILERSEND_API_KEY')) 1338 | 1339 | recipient_list = [ 1340 | "your@client.com" 1341 | ] 1342 | 1343 | mailer.delete_unsubscribes("domain-id", recipient_list) 1344 | ``` 1345 | 1346 | ## Tokens 1347 | 1348 | ### Create a token 1349 | 1350 | ```python 1351 | from mailersend import tokens 1352 | from dotenv import load_dotenv 1353 | 1354 | load_dotenv() 1355 | 1356 | mailer = tokens.NewToken(os.getenv('MAILERSEND_API_KEY')) 1357 | 1358 | scopes = ["email_full", "analytics_read"] 1359 | 1360 | mailer.create_token("my-token", scopes) 1361 | ``` 1362 | 1363 | Because of security reasons, we only allow access token appearance once during creation. In order to see the access token created you can do: 1364 | 1365 | ```python 1366 | from mailersend import tokens 1367 | from dotenv import load_dotenv 1368 | 1369 | load_dotenv() 1370 | 1371 | mailer = tokens.NewToken(os.getenv('MAILERSEND_API_KEY')) 1372 | 1373 | scopes = ["email_full", "analytics_read"] 1374 | 1375 | print(mailer.create_token("my-token", scopes)) 1376 | ``` 1377 | 1378 | ### Pause / Unpause Token 1379 | 1380 | ```python 1381 | from mailersend import tokens 1382 | from dotenv import load_dotenv 1383 | 1384 | load_dotenv() 1385 | 1386 | mailer = tokens.NewToken(os.getenv('MAILERSEND_API_KEY')) 1387 | 1388 | # pause 1389 | mailer.update_token("my-token") 1390 | 1391 | # unpause 1392 | mailer.update_token("my-token", pause=False) 1393 | ``` 1394 | 1395 | ### Delete a Token 1396 | 1397 | ```python 1398 | from mailersend import tokens 1399 | from dotenv import load_dotenv 1400 | 1401 | load_dotenv() 1402 | 1403 | mailer = tokens.NewToken(os.getenv('MAILERSEND_API_KEY')) 1404 | 1405 | mailer.delete_token("token-id") 1406 | ``` 1407 | 1408 | ## Templates 1409 | 1410 | ### Get a list of templates 1411 | 1412 | ```python 1413 | from mailersend import templates 1414 | from dotenv import load_dotenv 1415 | 1416 | load_dotenv() 1417 | 1418 | mailer = templates.NewTemplate(os.getenv('MAILERSEND_API_KEY')) 1419 | 1420 | mailer.get_templates() 1421 | ``` 1422 | 1423 | ### Get a single template 1424 | 1425 | ```python 1426 | from mailersend import templates 1427 | from dotenv import load_dotenv 1428 | 1429 | load_dotenv() 1430 | 1431 | mailer = templates.NewTemplate(os.getenv('MAILERSEND_API_KEY')) 1432 | template_id = 1234 1433 | 1434 | mailer.get_template_by_id() 1435 | ``` 1436 | 1437 | ### Delete template 1438 | 1439 | ```python 1440 | from mailersend import templates 1441 | from dotenv import load_dotenv 1442 | 1443 | load_dotenv() 1444 | 1445 | mailer = templates.NewTemplate(os.getenv('MAILERSEND_API_KEY')) 1446 | template_id = 1234 1447 | 1448 | mailer.delete_template() 1449 | ``` 1450 | 1451 | ## Webhooks 1452 | 1453 | ### Get a list of webhooks 1454 | 1455 | ```python 1456 | from mailersend import webhooks 1457 | from dotenv import load_dotenv 1458 | 1459 | load_dotenv() 1460 | 1461 | mailer = webhooks.NewWebhook(os.getenv('MAILERSEND_API_KEY')) 1462 | 1463 | mailer.get_webhooks("domain-id") 1464 | ``` 1465 | 1466 | ### Get a single webhook 1467 | 1468 | ```python 1469 | from mailersend import webhooks 1470 | from dotenv import load_dotenv 1471 | 1472 | load_dotenv() 1473 | 1474 | mailer = webhooks.NewWebhook(os.getenv('MAILERSEND_API_KEY')) 1475 | 1476 | mailer.get_webhook_by_id("webhook-id") 1477 | ``` 1478 | 1479 | ### Create a Webhook 1480 | 1481 | ```python 1482 | from mailersend import webhooks 1483 | from dotenv import load_dotenv 1484 | 1485 | load_dotenv() 1486 | 1487 | webhookEvents = ['activity.sent', 'activity.delivered'] 1488 | 1489 | webhook = webhooks.NewWebhook(os.getenv('MAILERSEND_API_KEY')) 1490 | webhook.set_webhook_url("https://webhooks.mysite.com") 1491 | webhook.set_webhook_name("my first webhook") 1492 | webhook.set_webhook_events(webhookEvents) 1493 | webhook.set_webhook_domain("domain-id") 1494 | 1495 | webhook.create_webhook() 1496 | ``` 1497 | 1498 | ### Create a disabled webhook 1499 | 1500 | ```python 1501 | from mailersend import webhooks 1502 | from dotenv import load_dotenv 1503 | 1504 | load_dotenv() 1505 | 1506 | webhookEvents = ['activity.sent', 'activity.delivered'] 1507 | 1508 | webhook = webhooks.NewWebhook(os.getenv('MAILERSEND_API_KEY')) 1509 | webhook.set_webhook_url("https://webhooks.mysite.com") 1510 | webhook.set_webhook_name("my first webhook") 1511 | webhook.set_webhook_events(webhookEvents) 1512 | webhook.set_webhook_domain("domain-id") 1513 | webhook.set_webhook_enabled(False) 1514 | 1515 | webhook.create_webhook() 1516 | ``` 1517 | 1518 | ### Update a Webhook 1519 | 1520 | ```python 1521 | from mailersend import webhooks 1522 | from dotenv import load_dotenv 1523 | 1524 | load_dotenv() 1525 | 1526 | webhook = webhooks.NewWebhook(os.getenv('MAILERSEND_API_KEY')) 1527 | 1528 | webhook.update_webhook("webhook-id", "name", "a new webhook name") 1529 | ``` 1530 | 1531 | ### Disable/Enable a Webhook 1532 | 1533 | ```python 1534 | from mailersend import webhooks 1535 | from dotenv import load_dotenv 1536 | 1537 | load_dotenv() 1538 | 1539 | webhook = webhooks.NewWebhook(os.getenv('MAILERSEND_API_KEY')) 1540 | 1541 | webhook.update_webhook("webhook-id", "enabled", False) 1542 | ``` 1543 | 1544 | ### Delete a Webhook 1545 | 1546 | ```python 1547 | from mailersend import webhooks 1548 | from dotenv import load_dotenv 1549 | 1550 | load_dotenv() 1551 | 1552 | webhook = webhooks.NewWebhook(os.getenv('MAILERSEND_API_KEY')) 1553 | 1554 | webhook.delete_webhook("webhook-id") 1555 | ``` 1556 | 1557 | ## SMS 1558 | 1559 | ### Sending SMS messages 1560 | 1561 | Without personalization: 1562 | ```python 1563 | from mailersend import sms_sending 1564 | from dotenv import load_dotenv 1565 | 1566 | load_dotenv() 1567 | 1568 | mailer = sms_sending.NewSmsSending(os.getenv('MAILERSEND_API_KEY')) 1569 | 1570 | # Number belonging to your account in E164 format 1571 | number_from = "+11234567890" 1572 | 1573 | # You can add up to 50 recipient numbers 1574 | numbers_to = [ 1575 | "+11234567891", 1576 | "+11234567892" 1577 | ] 1578 | text = "This is the text content" 1579 | 1580 | print(mailer.send_sms(number_from, numbers_to, text)) 1581 | ``` 1582 | 1583 | With personalization: 1584 | ```python 1585 | from mailersend import sms_sending 1586 | from dotenv import load_dotenv 1587 | 1588 | load_dotenv() 1589 | 1590 | mailer = sms_sending.NewSmsSending(os.getenv('MAILERSEND_API_KEY')) 1591 | 1592 | # Number belonging to your account in E164 format 1593 | number_from = "+11234567890" 1594 | 1595 | # You can add up to 50 recipient numbers 1596 | numbers_to = [ 1597 | "+11234567891", 1598 | "+11234567892" 1599 | ] 1600 | text = "Hi {{name}} how are you?" 1601 | personalization = [ 1602 | { 1603 | "phone_number": "+11234567891", 1604 | "data": { 1605 | "name": "Mike" 1606 | } 1607 | }, 1608 | { 1609 | "phone_number": "+11234567892", 1610 | "data": { 1611 | "name": "John" 1612 | } 1613 | } 1614 | ] 1615 | 1616 | print(mailer.send_sms(number_from, numbers_to, text, personalization)) 1617 | ``` 1618 | 1619 | ## SMS Activity 1620 | 1621 | ### Get a list of activities 1622 | ```python 1623 | from mailersend import sms_activity 1624 | from dotenv import load_dotenv 1625 | 1626 | load_dotenv() 1627 | 1628 | mailer = sms_activity.NewSmsActivity(os.getenv('MAILERSEND_API_KEY')) 1629 | 1630 | #Request parameters 1631 | sms_number_id = 1365743 1632 | date_from = 1655157601 1633 | date_to = 1655158601 1634 | status = ["queued", "failed"] 1635 | page = 1 1636 | limit = 200 1637 | 1638 | print(mailer.get_activities(sms_number_id=sms_number_id, date_from=date_from, date_to=date_to, status=status, page=page, limit=limit)) 1639 | ``` 1640 | 1641 | ### Get activity of a single message 1642 | ```python 1643 | from mailersend import sms_activity 1644 | from dotenv import load_dotenv 1645 | 1646 | load_dotenv() 1647 | 1648 | mailer = sms_activity.NewSmsActivity(os.getenv('MAILERSEND_API_KEY')) 1649 | 1650 | #Request parameters 1651 | sms_message_id = "62a9d12b07852eaf2207b417" 1652 | 1653 | print(mailer.get_activity(sms_message_id)) 1654 | ``` 1655 | 1656 | ## SMS Phone Numbers 1657 | 1658 | ### Get a list of SMS phone numbers 1659 | ```python 1660 | from mailersend import sms_phone_numbers 1661 | from dotenv import load_dotenv 1662 | 1663 | load_dotenv() 1664 | 1665 | mailer = sms_phone_numbers.NewSmsNumbers(os.getenv('MAILERSEND_API_KEY')) 1666 | 1667 | #Request parameters 1668 | paused = False 1669 | 1670 | print(mailer.get_phone_numbers(paused)) 1671 | ``` 1672 | 1673 | ### Get an SMS phone number 1674 | ```python 1675 | from mailersend import sms_phone_numbers 1676 | from dotenv import load_dotenv 1677 | 1678 | load_dotenv() 1679 | 1680 | mailer = sms_phone_numbers.NewSmsNumbers(os.getenv('MAILERSEND_API_KEY')) 1681 | 1682 | #Request parameters 1683 | sms_number_id = "9pq3enl6842vwrzx" 1684 | 1685 | print(mailer.get_phone_number(sms_number_id)) 1686 | ``` 1687 | 1688 | ### Update a single SMS phone number 1689 | ```python 1690 | from mailersend import sms_phone_numbers 1691 | from dotenv import load_dotenv 1692 | 1693 | load_dotenv() 1694 | 1695 | mailer = sms_phone_numbers.NewSmsNumbers(os.getenv('MAILERSEND_API_KEY')) 1696 | 1697 | #Request parameters 1698 | sms_number_id = "9pq3enl6842vwrzx" 1699 | paused = True 1700 | 1701 | print(mailer.update_phone_number(sms_number_id, paused)) 1702 | ``` 1703 | 1704 | ### Delete an SMS phone number 1705 | ```python 1706 | from mailersend import sms_phone_numbers 1707 | from dotenv import load_dotenv 1708 | 1709 | load_dotenv() 1710 | 1711 | mailer = sms_phone_numbers.NewSmsNumbers(os.getenv('MAILERSEND_API_KEY')) 1712 | 1713 | #Request parameters 1714 | sms_number_id = "9pq3enl6842vwrzx" 1715 | 1716 | print(mailer.delete_phone_number(sms_number_id)) 1717 | ``` 1718 | 1719 | ## SMS Recipients 1720 | 1721 | ### Get a list of SMS recipients 1722 | ```python 1723 | from mailersend import sms_recipients 1724 | from dotenv import load_dotenv 1725 | 1726 | load_dotenv() 1727 | 1728 | mailer = sms_recipients.NewSmsRecipients(os.getenv('MAILERSEND_API_KEY')) 1729 | 1730 | #Request parameters 1731 | sms_number_id = "9pq3enl6842vwrzx" 1732 | status = "active" 1733 | 1734 | print(mailer.get_recipients(status=status, sms_number_id=sms_number_id)) 1735 | ``` 1736 | 1737 | ### Get an SMS recipient 1738 | ```python 1739 | from mailersend import sms_recipients 1740 | from dotenv import load_dotenv 1741 | 1742 | load_dotenv() 1743 | 1744 | mailer = sms_recipients.NewSmsRecipients(os.getenv('MAILERSEND_API_KEY')) 1745 | 1746 | #Request parameters 1747 | sms_recipient_id = "627e756fd30078fb2208cc87" 1748 | 1749 | print(mailer.get_recipient(sms_recipient_id)) 1750 | ``` 1751 | 1752 | ### Update a single SMS recipient 1753 | ```python 1754 | from mailersend import sms_recipients 1755 | from dotenv import load_dotenv 1756 | 1757 | load_dotenv() 1758 | 1759 | mailer = sms_recipients.NewSmsRecipients(os.getenv('MAILERSEND_API_KEY')) 1760 | 1761 | #Request parameters 1762 | sms_recipient_id = "627e756fd30078fb2208cc87" 1763 | status = "opt_out" 1764 | 1765 | print(mailer.update_recipient(sms_recipient_id, status)) 1766 | ``` 1767 | 1768 | ## SMS Messages 1769 | 1770 | ### Get a list of SMS messages 1771 | ```python 1772 | from mailersend import sms_messages 1773 | from dotenv import load_dotenv 1774 | 1775 | load_dotenv() 1776 | 1777 | mailer = sms_messages.NewSmsMessages(os.getenv('MAILERSEND_API_KEY')) 1778 | 1779 | print(mailer.get_messages()) 1780 | ``` 1781 | 1782 | ### Get an SMS message 1783 | ```python 1784 | from mailersend import sms_messages 1785 | from dotenv import load_dotenv 1786 | 1787 | load_dotenv() 1788 | 1789 | #Request parameters 1790 | sms_message_id = "627e756fd30078fb2208cc87" 1791 | 1792 | mailer = sms_messages.NewSmsMessages(os.getenv('MAILERSEND_API_KEY')) 1793 | 1794 | print(mailer.get_message(sms_message_id)) 1795 | ``` 1796 | 1797 | ## SMS Webhooks 1798 | 1799 | ### Get a list of SMS webhooks 1800 | ```python 1801 | from mailersend import sms_webhooks 1802 | from dotenv import load_dotenv 1803 | 1804 | load_dotenv() 1805 | 1806 | mailer = sms_webhooks.NewSmsWebhooks(os.getenv('MAILERSEND_API_KEY')) 1807 | 1808 | #Request parameters 1809 | sms_number_id = "9pq3enl6842vwrzx" 1810 | 1811 | print(mailer.get_webhooks(sms_number_id)) 1812 | ``` 1813 | 1814 | ### Get a single SMS webhook 1815 | ```python 1816 | from mailersend import sms_webhooks 1817 | from dotenv import load_dotenv 1818 | 1819 | load_dotenv() 1820 | 1821 | mailer = sms_webhooks.NewSmsWebhooks(os.getenv('MAILERSEND_API_KEY')) 1822 | 1823 | #Request parameters 1824 | sms_webhook_id = "aaui13enl12f2vzx" 1825 | 1826 | print(mailer.get_webhook(sms_webhook_id)) 1827 | ``` 1828 | 1829 | ### Create an SMS webhook 1830 | ```python 1831 | from mailersend import sms_webhooks 1832 | from dotenv import load_dotenv 1833 | 1834 | load_dotenv() 1835 | 1836 | mailer = sms_webhooks.NewSmsWebhooks(os.getenv('MAILERSEND_API_KEY')) 1837 | 1838 | #Request parameters 1839 | url = "https://webhook-url.com" 1840 | name = "My Webhook Name" 1841 | events = ["sms.sent"] 1842 | sms_number_id = "9pq3enl6842vwrzx" 1843 | enabled = True 1844 | 1845 | print(mailer.create_webhook(url, name, events, sms_number_id, enabled)) 1846 | ``` 1847 | 1848 | ### Update a single SMS webhook 1849 | ```python 1850 | from mailersend import sms_webhooks 1851 | from dotenv import load_dotenv 1852 | 1853 | load_dotenv() 1854 | 1855 | mailer = sms_webhooks.NewSmsWebhooks(os.getenv('MAILERSEND_API_KEY')) 1856 | 1857 | #Request parameters 1858 | url = "https://different-url.com" 1859 | name = "New Webhook Name" 1860 | events = ["sms.sent", "sms.failed"], 1861 | sms_webhook_id = "aaui13enl12f2vzx" 1862 | enabled = False 1863 | 1864 | print(mailer.update_webhook(sms_webhook_id, url, name, events, enabled)) 1865 | ``` 1866 | 1867 | ### Delete an SMS webhook 1868 | ```python 1869 | from mailersend import sms_webhooks 1870 | from dotenv import load_dotenv 1871 | 1872 | load_dotenv() 1873 | 1874 | mailer = sms_webhooks.NewSmsWebhooks(os.getenv('MAILERSEND_API_KEY')) 1875 | 1876 | #Request parameters 1877 | sms_webhook_id = "aaui13enl12f2vzx" 1878 | 1879 | print(mailer.delete_webhook(sms_webhook_id)) 1880 | ``` 1881 | 1882 | ### Get a list of SMS webhooks 1883 | ```python 1884 | from mailersend import sms_webhooks 1885 | from dotenv import load_dotenv 1886 | 1887 | load_dotenv() 1888 | 1889 | mailer = sms_webhooks.NewSmsWebhooks(os.getenv('MAILERSEND_API_KEY')) 1890 | 1891 | #Request parameters 1892 | sms_number_id = "9pq3enl6842vwrzx" 1893 | 1894 | print(mailer.get_webhooks(sms_number_id)) 1895 | ``` 1896 | 1897 | ## SMS Inbouds 1898 | 1899 | ### Get a list of SMS inbound routes 1900 | ```python 1901 | from mailersend import sms_inbounds 1902 | from dotenv import load_dotenv 1903 | 1904 | load_dotenv() 1905 | 1906 | mailer = sms_inbounds.NewSmsInbounds(os.getenv('MAILERSEND_API_KEY')) 1907 | 1908 | #Request parameters 1909 | sms_number_id = "123456789" 1910 | enabled = True 1911 | page = 1 1912 | limit = 25 1913 | 1914 | print(mailer.get_inbound_routes(sms_number_id, enabled, page, limit)) 1915 | ``` 1916 | 1917 | ### Get a single SMS inbound route 1918 | ```python 1919 | from mailersend import sms_inbounds 1920 | from dotenv import load_dotenv 1921 | 1922 | load_dotenv() 1923 | 1924 | mailer = sms_inbounds.NewSmsInbounds(os.getenv('MAILERSEND_API_KEY')) 1925 | 1926 | #Request parameters 1927 | sms_inbound_id = "123456789" 1928 | 1929 | print(mailer.get_inbound_route(sms_inbound_id)) 1930 | ``` 1931 | 1932 | ### Create an SMS inbound route 1933 | ```python 1934 | from mailersend import sms_inbounds 1935 | from dotenv import load_dotenv 1936 | 1937 | load_dotenv() 1938 | 1939 | mailer = sms_inbounds.NewSmsInbounds(os.getenv('MAILERSEND_API_KEY')) 1940 | 1941 | #Request parameters 1942 | sms_number_id = "123456789" 1943 | name = "My route" 1944 | forward_url = "https://some.url" 1945 | filter = { 1946 | "comparer": "equal", 1947 | "value": "START" 1948 | } 1949 | enabled = True 1950 | 1951 | print(mailer.create_inbound_route(sms_number_id, name, forward_url, filter, enabled)) 1952 | ``` 1953 | 1954 | ### Update an SMS inbound route 1955 | ```python 1956 | from mailersend import sms_inbounds 1957 | from dotenv import load_dotenv 1958 | 1959 | load_dotenv() 1960 | 1961 | mailer = sms_inbounds.NewSmsInbounds(os.getenv('MAILERSEND_API_KEY')) 1962 | 1963 | #Request parameters 1964 | sms_number_id = "123456789" 1965 | name = "New name" 1966 | forward_url = "https://news.url" 1967 | filter = { 1968 | "comparer": "contains", 1969 | "value": "some-value" 1970 | } 1971 | enabled = True 1972 | 1973 | print(mailer.update_inbound_route(sms_number_id, name, forward_url, filter, enabled)) 1974 | ``` 1975 | 1976 | ### Delete an SMS inbound route 1977 | ```python 1978 | from mailersend import sms_inbounds 1979 | from dotenv import load_dotenv 1980 | 1981 | load_dotenv() 1982 | 1983 | mailer = sms_inbounds.NewSmsInbounds(os.getenv('MAILERSEND_API_KEY')) 1984 | 1985 | #Request parameters 1986 | sms_inbound_id = "123456789" 1987 | 1988 | print(mailer.delete_inbound_route()(sms_inbound_id)) 1989 | ``` 1990 | 1991 | ## Sender Identities 1992 | 1993 | ### Get a list of sender identities 1994 | ```python 1995 | from mailersend import sender_identities 1996 | from dotenv import load_dotenv 1997 | 1998 | load_dotenv() 1999 | 2000 | mailer = sender_identities.NewSenderIdentity(os.getenv('MAILERSEND_API_KEY')) 2001 | 2002 | print(mailer.get_identities()) 2003 | ``` 2004 | 2005 | ### Get a sender identity 2006 | ```python 2007 | from mailersend import sender_identities 2008 | from dotenv import load_dotenv 2009 | 2010 | load_dotenv() 2011 | 2012 | mailer = sender_identities.NewSenderIdentity(os.getenv('MAILERSEND_API_KEY')) 2013 | 2014 | print(mailer.get_identity(identity_id="12345")) 2015 | ``` 2016 | 2017 | ### Create a sender identity 2018 | ```python 2019 | from mailersend import sender_identities 2020 | from dotenv import load_dotenv 2021 | 2022 | load_dotenv() 2023 | 2024 | mailer = sender_identities.NewSenderIdentity(os.getenv('MAILERSEND_API_KEY')) 2025 | 2026 | print(mailer.create_identity( 2027 | domain_id="123456", 2028 | name="John Doe", 2029 | email="email@domain.com", 2030 | reply_to_email="reply@domain.com", 2031 | reply_to_name="Doe John", 2032 | add_note=True, 2033 | personal_note="My awesome note", 2034 | )) 2035 | ``` 2036 | 2037 | ### Update a sender identity 2038 | ```python 2039 | from mailersend import sender_identities 2040 | from dotenv import load_dotenv 2041 | 2042 | load_dotenv() 2043 | 2044 | mailer = sender_identities.NewSenderIdentity(os.getenv('MAILERSEND_API_KEY')) 2045 | 2046 | print(mailer.update_identity( 2047 | identity_id="123456", 2048 | domain_id="123456", 2049 | name="Abe Doe", 2050 | email="email@mydomain.com", 2051 | reply_to_email="reply@mydomain.com", 2052 | reply_to_name="Doe Abe", 2053 | add_note=False 2054 | )) 2055 | ``` 2056 | 2057 | ### Delete a sender identity 2058 | ```python 2059 | from mailersend import sender_identities 2060 | from dotenv import load_dotenv 2061 | 2062 | load_dotenv() 2063 | 2064 | mailer = sender_identities.NewSenderIdentity(os.getenv('MAILERSEND_API_KEY')) 2065 | 2066 | print(mailer.delete_identity(identity_id="123456")) 2067 | ``` 2068 | 2069 | ## API Quota 2070 | ### Get API Quota 2071 | ```python 2072 | from mailersend import api_quota 2073 | from dotenv import load_dotenv 2074 | 2075 | load_dotenv() 2076 | 2077 | mailer = api_quota.NewApiQuota(os.getenv('MAILERSEND_API_KEY')) 2078 | 2079 | print(mailer.get_quota()) 2080 | ``` 2081 | 2082 | # Troubleshooting 2083 | 2084 | ## Emails not being sent 2085 | 2086 | Print the output of `mailer.send()` to view status code and errors. 2087 | 2088 | ```python 2089 | from mailersend import emails 2090 | 2091 | mailer = emails.NewEmail() 2092 | 2093 | mail_body = {} 2094 | 2095 | print(mailer.send(mail_body)) 2096 | ``` 2097 | 2098 | # Testing 2099 | 2100 | TBD 2101 | 2102 | 2103 | # Available endpoints 2104 | 2105 | | Feature group | Endpoint | Available | 2106 | |-------------------|-----------------------------------------|-----------| 2107 | | Activity | `GET activity` | ✅ | 2108 | | Analytics | `GET analytics` | ✅ | 2109 | | Domains | `{GET, PUT, DELETE} domains` | ✅ | 2110 | | Emails | `POST send` | ✅ | 2111 | | Messages | `GET messages` | ✅ | 2112 | | Recipients | `{GET, DELETE} recipients` | ✅ | 2113 | | Templates | `{GET, DELETE} templates` | ✅ | 2114 | | Tokens | `{POST, PUT, DELETE} tokens` | ✅ | 2115 | | Webhooks | `{GET, POST, PUT, DELETE} webhooks` | ✅ | 2116 | | SMS Sending | `{POST} sms` | ✅ | 2117 | | SMS Activity | `{GET} sms-activity` | ✅ | 2118 | | SMS Phone numbers | `{GET, PUT, DELETE} sms-numbers` | ✅ | 2119 | | SMS Recipients | `{GET, PUT} sms-recipients` | ✅ | 2120 | | SMS Messages | `{GET} sms-messages` | ✅ | 2121 | | SMS Webhooks | `{GET, POST, PUT, DELETE} sms-webhooks` | ✅ | 2122 | | SMS Inbounds | `{GET, POST, PUT, DELETE} sms-inbounds` | ✅ | 2123 | | Sender Identities | `{GET, POST, PUT, DELETE} identities` | ✅ | 2124 | | API Quota | `{GET} api-quota` | ✅ | 2125 | 2126 | *If, at the moment, some endpoint is not available, please use other available tools to access it. [Refer to official API docs for more info](https://developers.mailersend.com/).* 2127 | 2128 | 2129 | 2130 | # Support and Feedback 2131 | 2132 | In case you find any bugs, submit an issue directly here in GitHub. 2133 | 2134 | You are welcome to create SDK for any other programming language. 2135 | 2136 | If you have any troubles using our API or SDK free to contact our support by email [info@mailersend.com](mailto:info@mailersend.com) 2137 | 2138 | The official documentation is at [https://developers.mailersend.com](https://developers.mailersend.com) 2139 | 2140 | 2141 | # License 2142 | 2143 | [The MIT License (MIT)](LICENSE) 2144 | -------------------------------------------------------------------------------- /mailersend/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | MailerSend Official Python DSK 3 | @maintainer: Igor Hrček (igor at mailerlite dot com) 4 | """ 5 | 6 | __version_info__ = ("0", "6", "0") 7 | __version__ = ".".join(__version_info__) 8 | -------------------------------------------------------------------------------- /mailersend/activity/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /activity endpoint 3 | Doc: https://developers.mailersend.com/api/v1/activity.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewActivity(base.NewAPIClient): 11 | """ 12 | Instantiates the /activity endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def get_domain_activity( 18 | self, domain_id, page=None, limit=None, date_from=None, date_to=None, event=None 19 | ): 20 | """ 21 | Returns a JSON response from the MailerSend API 22 | """ 23 | 24 | _data = { 25 | "page": page or None, 26 | "limit": limit or None, 27 | "date_from": date_from or None, 28 | "date_to": date_to or None, 29 | "event": event or None, 30 | } 31 | 32 | request = requests.get( 33 | f"{self.api_base}/activity/{domain_id}", 34 | headers=self.headers_default, 35 | json=_data, 36 | ) 37 | return request.text 38 | -------------------------------------------------------------------------------- /mailersend/analytics/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /analytics endpoint 3 | Doc: https://developers.mailersend.com/api/v1/analytics.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewAnalytics(base.NewAPIClient): 11 | """ 12 | Instantiates the /activity endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def get_activity_by_date( 18 | self, date_from, date_to, event, domain_id=None, group_by=None 19 | ): 20 | """ 21 | Returns a JSON response from the MailerSend API 22 | """ 23 | 24 | _data = { 25 | "date_from": date_from, 26 | "date_to": date_to, 27 | "event[]": event, 28 | } 29 | 30 | if domain_id is not None: 31 | _data["domain_id"] = domain_id 32 | 33 | if group_by is not None: 34 | _data["group_by"] = group_by 35 | 36 | request = requests.get( 37 | f"{self.api_base}/analytics/date", 38 | headers=self.headers_default, 39 | params=_data, 40 | ) 41 | 42 | return request.text 43 | 44 | def get_opens_by_country(self, date_from, date_to, domain_id=None, recipients=None): 45 | """ 46 | Returns a JSON response from the MailerSend API 47 | """ 48 | 49 | _data = { 50 | "date_from": date_from, 51 | "date_to": date_to, 52 | } 53 | 54 | if domain_id is not None: 55 | _data["domain_id"] = domain_id 56 | 57 | if recipients is not None: 58 | _data["recipient_id"] = recipients 59 | 60 | request = requests.get( 61 | f"{self.api_base}/analytics/country", 62 | headers=self.headers_default, 63 | json=_data, 64 | ) 65 | 66 | return request.text 67 | 68 | def get_opens_by_user_agent( 69 | self, date_from, date_to, domain_id=None, recipients=None 70 | ): 71 | """ 72 | Returns a JSON response from the MailerSend API 73 | """ 74 | 75 | _data = { 76 | "date_from": date_from, 77 | "date_to": date_to, 78 | } 79 | 80 | if domain_id is not None: 81 | _data["domain_id"] = domain_id 82 | 83 | if recipients is not None: 84 | _data["recipient_id"] = recipients 85 | 86 | request = requests.get( 87 | f"{self.api_base}/analytics/ua-name", 88 | headers=self.headers_default, 89 | json=_data, 90 | ) 91 | 92 | return request.text 93 | 94 | def get_opens_by_reading_environment( 95 | self, date_from, date_to, domain_id=None, recipients=None 96 | ): 97 | """ 98 | Returns a JSON response from the MailerSend API 99 | """ 100 | 101 | _data = { 102 | "date_from": date_from, 103 | "date_to": date_to, 104 | } 105 | 106 | if domain_id is not None: 107 | _data["domain_id"] = domain_id 108 | 109 | if recipients is not None: 110 | _data["recipient_id"] = recipients 111 | 112 | request = requests.get( 113 | f"{self.api_base}/analytics/ua-type", 114 | headers=self.headers_default, 115 | json=_data, 116 | ) 117 | 118 | return request.text 119 | -------------------------------------------------------------------------------- /mailersend/api_quota/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /api-quota endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewApiQuota(base.NewAPIClient): 10 | """ 11 | Instantiates the /api-quota endpoint object 12 | """ 13 | 14 | pass 15 | 16 | def get_quota(self, page=1, limit=25, verified=False): 17 | query_params = {"page": page, "limit": limit, "verified": verified} 18 | 19 | request = requests.get( 20 | f"{self.api_base}/api-quota", 21 | headers=self.headers_default, 22 | params=query_params, 23 | ) 24 | 25 | return request.text 26 | -------------------------------------------------------------------------------- /mailersend/base/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailersend/mailersend-python/61d8ff3d3993acd38726e00a860218e49378c7e4/mailersend/base/__init__.py -------------------------------------------------------------------------------- /mailersend/base/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Base object handles connection information to the MailerSend API 3 | """ 4 | 5 | import os 6 | 7 | API_BASE = "https://api.mailersend.com/v1" 8 | API_KEY = os.environ.get("MAILERSEND_API_KEY") 9 | 10 | 11 | class NewAPIClient: 12 | """ 13 | Instantiates the parent object all endpoints follow. 14 | Provides necessary connection information to perform API operations. 15 | """ 16 | 17 | def __init__( 18 | self, 19 | mailersend_api_key=API_KEY, 20 | api_base=None, 21 | headers_default=None, 22 | headers_auth=None, 23 | ): 24 | """ 25 | NewAPIClient constructor 26 | """ 27 | 28 | self.api_base = API_BASE 29 | self.mailersend_api_key = mailersend_api_key 30 | self.headers_auth = f"Bearer {self.mailersend_api_key}" 31 | self.headers_default = { 32 | "Content-Type": "application/json", 33 | "X-Requested-With": "XMLHttpRequest", 34 | "User-Agent": "MailerSend-Client-python-v1", 35 | "Authorization": f"{self.headers_auth}", 36 | } 37 | 38 | 39 | def generate_config_change_json_body(key, value): 40 | """ 41 | Returns a key:value pair 42 | """ 43 | data = {key: value} 44 | 45 | return data 46 | -------------------------------------------------------------------------------- /mailersend/domains/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /domains endpoint 3 | Doc: https://developers.mailersend.com/api/v1/domains.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewDomain(base.NewAPIClient): 11 | """ 12 | Instantiates the /domains endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def get_domains(self): 18 | """ 19 | Get a list of all domains 20 | 21 | Returns the JSON response of MailerSend API 22 | """ 23 | request = requests.get(f"{self.api_base}/domains", headers=self.headers_default) 24 | return request.text 25 | 26 | def get_domain_by_id(self, domain_id): 27 | """ 28 | Get info on a domain by its ID 29 | 30 | @params: 31 | domain_id (str): A domain ID 32 | 33 | Returns the JSON response of MailerSend API 34 | """ 35 | request = requests.get( 36 | f"{self.api_base}/domains/{domain_id}", headers=self.headers_default 37 | ) 38 | return request.text 39 | 40 | def add_domain(self, domain_data): 41 | """ 42 | Add a domain 43 | 44 | @params: 45 | domain_data (dic): Contains key:value data needed for creating a new domain 46 | 47 | """ 48 | 49 | request = requests.post( 50 | f"{self.api_base}/domains", 51 | headers=self.headers_default, 52 | json=domain_data, 53 | ) 54 | return request.text 55 | 56 | def delete_domain(self, domain_id): 57 | """ 58 | Delete a domain 59 | 60 | @params: 61 | domain_id (str): A domain ID 62 | 63 | Returns the JSON response of MailerSend API 64 | """ 65 | request = requests.delete( 66 | f"{self.api_base}/domains/{domain_id}", headers=self.headers_default 67 | ) 68 | return request.status_code 69 | 70 | def get_recipients_for_domain(self, domain_id): 71 | """ 72 | List all recipients for a domain 73 | 74 | @params: 75 | domain_id (str): A domain ID 76 | 77 | Returns the JSON response of MailerSend API 78 | """ 79 | request = requests.get( 80 | f"{self.api_base}/domains/{domain_id}/recipients", 81 | headers=self.headers_default, 82 | ) 83 | return request.text 84 | 85 | def update_domain_setting(self, domain_id, domain_data): 86 | """ 87 | Returns the JSON response of MailerSend API 88 | 89 | @params: 90 | domain_id (str): A domain ID 91 | domain_data (dict): A key:value list that contains parameters for updating domain name 92 | 93 | """ 94 | request = requests.put( 95 | f"{self.api_base}/domains/{domain_id}/settings", 96 | headers=self.headers_default, 97 | json=domain_data, 98 | ) 99 | return request.text 100 | 101 | def get_dns_records(self, domain_id): 102 | """ 103 | Returns the JSON response of dns records 104 | 105 | @params: 106 | domain_id (str): A domain ID 107 | 108 | """ 109 | 110 | request = requests.get( 111 | f"{self.api_base}/domains/{domain_id}/dns-records", 112 | headers=self.headers_default, 113 | ) 114 | return request.text 115 | 116 | def verify_domain(self, domain_id): 117 | """ 118 | Returns the JSON response of verified domain 119 | 120 | @params: 121 | domain_id (str): A domain ID 122 | 123 | """ 124 | 125 | request = requests.get( 126 | f"{self.api_base}/domains/{domain_id}/verify", headers=self.headers_default 127 | ) 128 | return request.text 129 | -------------------------------------------------------------------------------- /mailersend/email_verification/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /email-verification endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewEmailVerification(base.NewAPIClient): 10 | """ 11 | Instantiates the /email-verification endpoint object 12 | """ 13 | 14 | # you shall not 15 | pass 16 | 17 | def get_all_lists(self, page=1, limit=25): 18 | """ 19 | Returns all email verification lists 20 | :param page: int 21 | :param limit: int 22 | """ 23 | query_params = {"page": page, "limit": limit} 24 | 25 | request = requests.get( 26 | f"{self.api_base}/email-verification", 27 | headers=self.headers_default, 28 | params=query_params, 29 | ) 30 | 31 | return f"{request.status_code}\n{request.text}" 32 | 33 | def get_list(self, email_verification_id): 34 | """ 35 | Retrieve single email verification list 36 | :type email_verification_id: object 37 | """ 38 | 39 | query_params = {"email_verification_id": email_verification_id} 40 | 41 | request = requests.get( 42 | f"{self.api_base}/email-verification", 43 | headers=self.headers_default, 44 | params=query_params, 45 | ) 46 | 47 | return f"{request.status_code}\n{request.text}" 48 | 49 | def create_list(self, name, emails): 50 | """ 51 | Create email verification list 52 | :param name: str 53 | :param emails: list 54 | :return: 55 | """ 56 | 57 | data = {"name": name, "emails": emails} 58 | 59 | request = requests.post( 60 | f"{self.api_base}/email-verification", 61 | headers=self.headers_default, 62 | json=data, 63 | ) 64 | 65 | return f"{request.status_code}\n{request.text}" 66 | 67 | def verify_list(self, email_verification_id): 68 | """ 69 | Verify an email verification list 70 | :type email_verification_id: str 71 | """ 72 | 73 | request = requests.get( 74 | f"{self.api_base}/email-verification/{email_verification_id}/verify", 75 | headers=self.headers_default, 76 | ) 77 | 78 | return f"{request.status_code}\n{request.text}" 79 | 80 | def get_list_results(self, email_verification_id): 81 | """ 82 | Get the result for each individual email of an email verification list 83 | :type email_verification_id: str 84 | """ 85 | 86 | request = requests.get( 87 | f"{self.api_base}/email-verification/{email_verification_id}/results", 88 | headers=self.headers_default, 89 | ) 90 | 91 | return f"{request.status_code}\n{request.text}" 92 | -------------------------------------------------------------------------------- /mailersend/emails/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /email endpoint 3 | Doc: https://developers.mailersend.com/api/v1/email.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewEmail(base.NewAPIClient): 11 | """ 12 | Send an e-mail 13 | """ 14 | 15 | pass 16 | 17 | def set_mail_from(self, mail_from, message): 18 | """ 19 | Appends the 'from' part on an e-mail 20 | """ 21 | message["from"] = mail_from 22 | 23 | def set_mail_to(self, mail_to, message): 24 | """ 25 | Appends the 'to' part on an e-mail 26 | """ 27 | message["to"] = mail_to 28 | 29 | def set_subject(self, subject, message): 30 | """ 31 | Appends the 'subject' part on an e-mail 32 | """ 33 | message["subject"] = subject 34 | 35 | def set_html_content(self, content, message): 36 | """ 37 | Appends the HTML content of an e-mail 38 | """ 39 | message["html"] = content 40 | 41 | def set_plaintext_content(self, text, message): 42 | """ 43 | Appends the plaintext content of an e-mail 44 | """ 45 | message["text"] = text 46 | 47 | def set_template(self, template_id, message): 48 | """ 49 | Appends the 'template_id' part on an e-mail 50 | """ 51 | message["template_id"] = template_id 52 | 53 | def set_personalization(self, personalization, message): 54 | """ 55 | Handles advanced personalization 56 | """ 57 | message["personalization"] = personalization 58 | 59 | def set_cc_recipients(self, cc_recipient, message): 60 | """ 61 | Appends the 'cc' part on an e-mail 62 | """ 63 | message["cc"] = cc_recipient 64 | 65 | def set_bcc_recipients(self, bcc_recipient, message): 66 | """ 67 | Appends the 'bcc' part on an e-mail 68 | """ 69 | message["bcc"] = bcc_recipient 70 | 71 | def set_tags(self, tags, message): 72 | """ 73 | Handles e-mail tags 74 | """ 75 | message["tags"] = tags 76 | 77 | def set_attachments(self, attachments, message): 78 | """ 79 | Appends an attachment on an e-mail 80 | """ 81 | message["attachments"] = attachments 82 | 83 | def set_reply_to(self, reply_to, message): 84 | """ 85 | Appends 'reply to' on an e-mail 86 | """ 87 | message["reply_to"] = reply_to 88 | 89 | def set_in_reply_to(self, in_reply_to, message): 90 | """ 91 | Appends 'in reply to' on an e-mail 92 | """ 93 | message["in_reply_to"] = in_reply_to 94 | 95 | def set_send_at(self, send_at, message): 96 | """ 97 | Sets the 'send_at' parameter for scheduled messages 98 | """ 99 | message["send_at"] = send_at 100 | 101 | def set_list_unsubscribe(self, list_unsubscribe, message): 102 | """ 103 | Appends 'list_unsubscribe' on an e-mail 104 | """ 105 | message["list_unsubscribe"] = list_unsubscribe 106 | 107 | def send(self, message): 108 | """ 109 | Handles e-mail sending 110 | 111 | @params: 112 | message (dict): A dict containing required parameters for mail sending 113 | """ 114 | 115 | request = requests.post( 116 | f"{self.api_base}/email", headers=self.headers_default, json=message 117 | ) 118 | return f"{request.status_code}\n{request.text}" 119 | 120 | def get_bulk_status_by_id(self, bulk_email_id): 121 | """ 122 | Returns a JSON response from the MailerSend API 123 | """ 124 | 125 | request = requests.get( 126 | f"{self.api_base}/bulk-email/{bulk_email_id}", headers=self.headers_default 127 | ) 128 | return request.text 129 | 130 | def send_bulk(self, message_list): 131 | """ 132 | Handles bulk e-mail sending 133 | 134 | @params: 135 | message_list (list): A list containing e-mail dicts 136 | """ 137 | 138 | request = requests.post( 139 | f"{self.api_base}/bulk-email", 140 | headers=self.headers_default, 141 | json=message_list, 142 | ) 143 | return f"{request.status_code}\n{request.text}" 144 | -------------------------------------------------------------------------------- /mailersend/inbound_routing/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /inbound endpoint 3 | Doc: https://developers.mailersend.com/api/v1/inbound.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewInbound(base.NewAPIClient): 11 | """ 12 | Instantiates the /inbound endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def get_inbound_routes(self): 18 | """ 19 | Get a list of all inbound routes 20 | 21 | Returns the JSON response of MailerSend API 22 | """ 23 | request = requests.get(f"{self.api_base}/inbound", headers=self.headers_default) 24 | return f"{request.status_code}\n{request.text}" 25 | 26 | def get_inbound_by_id(self, inbound_id): 27 | """ 28 | Get info on an inbound route by its ID 29 | 30 | @params: 31 | inbound_id (str): An inbound route ID 32 | 33 | Returns the JSON response of MailerSend API 34 | """ 35 | request = requests.get( 36 | f"{self.api_base}/inbound/{inbound_id}", headers=self.headers_default 37 | ) 38 | return request.text 39 | 40 | def update_inbound_route(self, inbound_id, options): 41 | """ 42 | Update an inbound route 43 | 44 | @params: 45 | inbound_id (str): An inbound route ID 46 | key (str): The key param to change 47 | value (object): The value to update key with 48 | 49 | Returns the JSON response of MailerSend API 50 | """ 51 | 52 | request = requests.put( 53 | f"{self.api_base}/inbound/{inbound_id}", 54 | headers=self.headers_default, 55 | json=options, 56 | ) 57 | return f"{request.status_code}\n{request.text}" 58 | 59 | def delete_inbound_route(self, inbound_id): 60 | """ 61 | Returns the status code of delete inbound route operation 62 | 63 | @params: 64 | inbound_id (str): An inbound route ID 65 | """ 66 | 67 | request = requests.delete( 68 | f"{self.api_base}/inbound/{inbound_id}", 69 | headers=self.headers_default, 70 | ) 71 | return request.status_code 72 | 73 | def set_name(self, name, options): 74 | """ 75 | Appends the 'name' param of inbound route options 76 | """ 77 | options["name"] = name 78 | 79 | def set_domain_enabled(self, enabled, options): 80 | """ 81 | Appends the 'domain_enabled' param of inbound route options 82 | """ 83 | options["domain_enabled"] = enabled 84 | 85 | def set_inbound_domain(self, domain, options): 86 | """ 87 | Appends the 'inbound_domain' param of inbound route options 88 | """ 89 | options["inbound_domain"] = domain 90 | 91 | def set_catch_filter(self, content_json, options): 92 | """ 93 | Appends the 'catch_filter' param of inbound route options 94 | """ 95 | options["catch_filter"] = content_json 96 | 97 | def set_match_filter(self, content_json, options): 98 | """ 99 | Appends the 'match_filter' param of inbound route options 100 | """ 101 | options["match_filter"] = content_json 102 | 103 | def set_forwards(self, content_json, options): 104 | """ 105 | Appends the 'forwards' param of inbound route options 106 | """ 107 | options["forwards"] = content_json 108 | 109 | def add_inbound_route(self, domain_id, options): 110 | """ 111 | Add a new inbound route 112 | 113 | @params: 114 | domain_id (str): For which domain will inbound route be created 115 | options (str): Creation options as defined in https://developers.mailersend.com/api/v1/inbound.html#add-an-inbound-route 116 | 117 | """ 118 | 119 | options["domain_id"] = domain_id 120 | 121 | request = requests.post( 122 | f"{self.api_base}/inbound", 123 | headers=self.headers_default, 124 | json=options, 125 | ) 126 | return f"{request.text}\n{request.status_code}" 127 | -------------------------------------------------------------------------------- /mailersend/messages/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /messages endpoint 3 | Doc: https://developers.mailersend.com/api/v1/messages.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewMessage(base.NewAPIClient): 11 | """ 12 | Instantiates the /messages endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def get_message_by_id(self, message_id): 18 | """ 19 | Returns a JSON response from the MailerSend API 20 | """ 21 | 22 | request = requests.get( 23 | f"{self.api_base}/messages/{message_id}", headers=self.headers_default 24 | ) 25 | return request.text 26 | 27 | def get_messages(self): 28 | """ 29 | Returns a JSON response from the MailerSend API 30 | """ 31 | 32 | request = requests.get( 33 | f"{self.api_base}/messages", headers=self.headers_default 34 | ) 35 | return request.text 36 | -------------------------------------------------------------------------------- /mailersend/recipients/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /recipients endpoint 3 | Doc: https://developers.mailersend.com/api/v1/recipients.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewRecipient(base.NewAPIClient): 11 | """ 12 | Instantiates the /recipients endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def get_recipients(self): 18 | """ 19 | Returns a JSON response from the MailerSend API 20 | """ 21 | 22 | request = requests.get( 23 | f"{self.api_base}/recipients", headers=self.headers_default 24 | ) 25 | return request.text 26 | 27 | def get_recipient_by_id(self, recipient_id): 28 | """ 29 | Returns a JSON response from the MailerSend API 30 | """ 31 | 32 | request = requests.get( 33 | f"{self.api_base}/recipients/{recipient_id}", headers=self.headers_default 34 | ) 35 | return request.text 36 | 37 | def delete_recipient(self, recipient_id): 38 | """ 39 | Returns a HTTP status code from the MailerSend API 40 | """ 41 | 42 | request = requests.delete( 43 | f"{self.api_base}/recipients/{recipient_id}", headers=self.headers_default 44 | ) 45 | return request.status_code 46 | 47 | def get_recipients_from_blocklist(self, domain_id=None, limit=None, page=None): 48 | """ 49 | Returns a HTTP status code from the MailerSend API 50 | """ 51 | message = {} 52 | message["domain_id"] = domain_id 53 | message["limit"] = limit 54 | message["page"] = page 55 | 56 | request = requests.get( 57 | f"{self.api_base}/suppressions/blocklist", 58 | headers=self.headers_default, 59 | json=message, 60 | ) 61 | return request.text 62 | 63 | def get_hard_bounces(self, domain_id=None, limit=None, page=None): 64 | """ 65 | Returns a HTTP status code from the MailerSend API 66 | """ 67 | message = {} 68 | message["domain_id"] = domain_id 69 | message["limit"] = limit 70 | message["page"] = page 71 | 72 | request = requests.get( 73 | f"{self.api_base}/suppressions/hard_bounces", 74 | headers=self.headers_default, 75 | json=message, 76 | ) 77 | return request.text 78 | 79 | def get_spam_complaints(self, domain_id=None, limit=None, page=None): 80 | """ 81 | Returns a HTTP status code from the MailerSend API 82 | """ 83 | message = {} 84 | message["domain_id"] = domain_id 85 | message["limit"] = limit 86 | message["page"] = page 87 | 88 | request = requests.get( 89 | f"{self.api_base}/suppressions/spam-complaints", 90 | headers=self.headers_default, 91 | json=message, 92 | ) 93 | return request.text 94 | 95 | def get_unsubscribes(self, domain_id=None, limit=None, page=None): 96 | """ 97 | Returns a HTTP status code from the MailerSend API 98 | """ 99 | message = {} 100 | message["domain_id"] = domain_id 101 | message["limit"] = limit 102 | message["page"] = page 103 | 104 | request = requests.get( 105 | f"{self.api_base}/suppressions/unsubscribes", 106 | headers=self.headers_default, 107 | json=message, 108 | ) 109 | return request.text 110 | 111 | def add_to_blocklist(self, domain_id, recipients=None, patterns=None): 112 | """ 113 | Returns a HTTP status code from the MailerSend API 114 | """ 115 | message = {} 116 | message["domain_id"] = domain_id 117 | 118 | if recipients is not None: 119 | message["recipients"] = recipients 120 | if patterns is not None: 121 | message["patterns"] = patterns 122 | 123 | request = requests.post( 124 | f"{self.api_base}/suppressions/blocklist", 125 | headers=self.headers_default, 126 | json=message, 127 | ) 128 | return request.text 129 | 130 | def delete_from_blocklist(self, domain_id, ids=None, remove_all=False): 131 | """ 132 | Returns a HTTP status code from the MailerSend API 133 | """ 134 | message = {} 135 | message["domain_id"] = domain_id 136 | 137 | if ids is not None: 138 | message["ids"] = ids 139 | if remove_all is True: 140 | message["all"] = "true" 141 | 142 | request = requests.delete( 143 | f"{self.api_base}/suppressions/blocklist", 144 | headers=self.headers_default, 145 | json=message, 146 | ) 147 | return request.text 148 | 149 | def add_hard_bounces(self, domain_id=None, recipients=None): 150 | """ 151 | Returns a HTTP status code from the MailerSend API 152 | """ 153 | message = {} 154 | message["domain_id"] = domain_id 155 | 156 | if recipients is not None: 157 | message["recipients"] = recipients 158 | 159 | request = requests.post( 160 | f"{self.api_base}/suppressions/hard-bounces", 161 | headers=self.headers_default, 162 | json=message, 163 | ) 164 | return request.text 165 | 166 | def delete_hard_bounces(self, domain_id, ids=None, remove_all=False): 167 | """ 168 | Returns a HTTP status code from the MailerSend API 169 | """ 170 | message = {} 171 | message["domain_id"] = domain_id 172 | 173 | if ids is not None: 174 | message["ids"] = ids 175 | if remove_all is True: 176 | message["all"] = "true" 177 | 178 | request = requests.delete( 179 | f"{self.api_base}/suppressions/hard-bounces", 180 | headers=self.headers_default, 181 | json=message, 182 | ) 183 | return request.text 184 | 185 | def add_spam_complaints(self, domain_id=None, recipients=None): 186 | """ 187 | Returns a HTTP status code from the MailerSend API 188 | """ 189 | message = {} 190 | message["domain_id"] = domain_id 191 | 192 | if recipients is not None: 193 | message["recipients"] = recipients 194 | 195 | request = requests.post( 196 | f"{self.api_base}/suppressions/spam-complaints", 197 | headers=self.headers_default, 198 | json=message, 199 | ) 200 | return request.text 201 | 202 | def delete_spam_complaints(self, domain_id, ids=None, remove_all=False): 203 | """ 204 | Returns a HTTP status code from the MailerSend API 205 | """ 206 | message = {} 207 | message["domain_id"] = domain_id 208 | 209 | if ids is not None: 210 | message["ids"] = ids 211 | if remove_all is True: 212 | message["all"] = "true" 213 | 214 | request = requests.delete( 215 | f"{self.api_base}/suppressions/spam-complaints", 216 | headers=self.headers_default, 217 | json=message, 218 | ) 219 | return request.text 220 | 221 | def add_unsubscribes(self, domain_id=None, recipients=None): 222 | """ 223 | Returns a HTTP status code from the MailerSend API 224 | """ 225 | message = {} 226 | message["domain_id"] = domain_id 227 | 228 | if recipients is not None: 229 | message["recipients"] = recipients 230 | 231 | request = requests.post( 232 | f"{self.api_base}/suppressions/unsubscribes", 233 | headers=self.headers_default, 234 | json=message, 235 | ) 236 | return request.text 237 | 238 | def delete_unsubscribes(self, domain_id, ids=None, remove_all=False): 239 | """ 240 | Returns a HTTP status code from the MailerSend API 241 | """ 242 | message = {} 243 | message["domain_id"] = domain_id 244 | 245 | if ids is not None: 246 | message["ids"] = ids 247 | if remove_all is True: 248 | message["all"] = True 249 | 250 | request = requests.delete( 251 | f"{self.api_base}/suppressions/unsubscribes", 252 | headers=self.headers_default, 253 | json=message, 254 | ) 255 | return request.text 256 | -------------------------------------------------------------------------------- /mailersend/scheduled_messages/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /message-schedules endpoint 3 | Doc: https://developers.mailersend.com/api/v1/message-schedules.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewMessageSchedule(base.NewAPIClient): 11 | """ 12 | Instantiates the /message-schedules endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def get_scheduled_messages(self): 18 | """ 19 | Returns a JSON response from the MailerSend API 20 | """ 21 | 22 | request = requests.get( 23 | f"{self.api_base}/message-schedules", headers=self.headers_default 24 | ) 25 | return request.text 26 | 27 | def get_scheduled_message_by_id(self, message_id): 28 | """ 29 | Returns a JSON response from the MailerSend API 30 | """ 31 | 32 | request = requests.get( 33 | f"{self.api_base}/message-schedules/{message_id}", 34 | headers=self.headers_default, 35 | ) 36 | return request.text 37 | 38 | def delete_scheduled_message(self, message_id): 39 | """ 40 | Returns a JSON response from the MailerSend API 41 | """ 42 | 43 | request = requests.delete( 44 | f"{self.api_base}/message-schedules/{message_id}", 45 | headers=self.headers_default, 46 | ) 47 | return request.text 48 | -------------------------------------------------------------------------------- /mailersend/sender_identities/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /identities endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | class NewSenderIdentities(base.NewAPIClient): 9 | """ 10 | Instantiates the /identities endpoint object 11 | """ 12 | 13 | # you shall not 14 | pass 15 | 16 | def get_identities(self, domain_id=None, page=None, limit=25): 17 | """ 18 | Get all sender identities 19 | 20 | @params: 21 | domain_id (string) 22 | page (int) 23 | limit (int): Min: `10`, Max: `100`, default is 25 24 | """ 25 | 26 | passed_arguments = locals() 27 | query_params = {} 28 | 29 | for key, value in passed_arguments.items(): 30 | if key != "self" and value is not None: 31 | query_params[key] = value 32 | 33 | request = requests.get( 34 | f"{self.api_base}/identities", 35 | headers=self.headers_default, 36 | params=query_params, 37 | ) 38 | 39 | return f"{request.status_code}\n{request.text}" 40 | 41 | def get_identity(self, identity_id): 42 | """ 43 | Get a single sender identity 44 | 45 | @params: 46 | identity_id (string) 47 | """ 48 | 49 | request = requests.get( 50 | f"{self.api_base}/identities/{identity_id}", 51 | headers=self.headers_default, 52 | ) 53 | 54 | return f"{request.status_code}\n{request.text}" 55 | 56 | def add_identitity(self, domain_id, name, email, reply_to_email=None, reply_to_name=None, add_note=False, personal_note=None): 57 | """ 58 | Add a sender identity 59 | 60 | @params: 61 | domain_id (string) 62 | name (string) - Max 191 characters 63 | email (string) - Max 191 characters, unique 64 | reply_to_email (string) 65 | reply_to_name (string) 66 | add_note (boolean) 67 | personal_note (string) 68 | """ 69 | 70 | data = { 71 | "domain_id": domain_id, 72 | "name": name, 73 | "email": email 74 | } 75 | 76 | if reply_to_email != None: 77 | data["reply_to_email"] = reply_to_email 78 | 79 | if reply_to_name != None: 80 | data["reply_to_name"] = reply_to_name 81 | 82 | if add_note == True: 83 | data["add_note"] = add_note 84 | data["personal_note"] = personal_note 85 | 86 | request = requests.post( 87 | f"{self.api_base}/identities", 88 | headers=self.headers_default, 89 | json=data, 90 | ) 91 | 92 | return f"{request.status_code}\n{request.text}" 93 | 94 | def update_identitity(self, identity_id, domain_id=None, name=None, email=None, reply_to_email=None, reply_to_name=None, add_note=False, personal_note=None): 95 | """ 96 | Update a sender identity 97 | 98 | @params: 99 | identity_id (string) 100 | domain_id (string) 101 | name (string) - Max 191 characters 102 | email (string) - Max 191 characters, unique 103 | reply_to_email (string) 104 | reply_to_name (string) 105 | add_note (boolean) 106 | personal_note (string) 107 | """ 108 | 109 | passed_arguments = locals() 110 | data = {} 111 | 112 | for key, value in passed_arguments.items(): 113 | if key != "self" and key != "identity_id" and value is not None: 114 | data[key] = value 115 | 116 | request = requests.put( 117 | f"{self.api_base}/identities/{identity_id}", 118 | headers=self.headers_default, 119 | json=data, 120 | ) 121 | 122 | return f"{request.status_code}\n{request.text}" 123 | 124 | def delete_identity(self, identity_id): 125 | """ 126 | Delete a sender identity 127 | 128 | @params: 129 | identity_id (string) 130 | """ 131 | 132 | request = requests.delete( 133 | f"{self.api_base}/identities/{identity_id}", 134 | headers=self.headers_default, 135 | ) 136 | 137 | return request.text -------------------------------------------------------------------------------- /mailersend/sms_activity/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /sms-activity endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewSmsActivity(base.NewAPIClient): 10 | """ 11 | Instantiates the /sms-activity endpoint object 12 | """ 13 | 14 | pass 15 | 16 | def get_activities( 17 | self, 18 | sms_number_id=None, 19 | date_from=None, 20 | date_to=None, 21 | status=[], 22 | page=None, 23 | limit=25, 24 | ): 25 | """ 26 | Retrieve every single data point of the activity that happened for a specific phone number. 27 | 28 | @params: 29 | sms_number_id (str) 30 | date_from (int): Timestamp is assumed to be `UTC`. Must be lower than `date_to` 31 | date_to (int): Timestamp is assumed to be `UTC`. Must be higher than `date_from` 32 | status (dict): Possible types: `processed`,`queued`,`sent`,`delivered`, `failed` 33 | page (int) 34 | limit (int): Min: `10`, Max: `100`, default is 25 35 | """ 36 | 37 | passed_arguments = locals() 38 | query_params = {} 39 | 40 | for key, value in passed_arguments.items(): 41 | if key != "self": 42 | if key == "status": 43 | query_params[key + "[]"] = value 44 | else: 45 | query_params[key] = value 46 | 47 | request = requests.get( 48 | f"{self.api_base}/sms-activity", 49 | headers=self.headers_default, 50 | params=query_params, 51 | ) 52 | 53 | return f"{request.status_code}\n{request.text}" 54 | 55 | def get_activity(self, sms_message_id): 56 | """ 57 | Get every single activity data point that happened to a specific SMS message 58 | 59 | @params: 60 | sms_message_id (str) 61 | """ 62 | 63 | request = requests.get( 64 | f"{self.api_base}/sms-messages/{sms_message_id}", 65 | headers=self.headers_default, 66 | ) 67 | 68 | return f"{request.status_code}\n{request.text}" 69 | -------------------------------------------------------------------------------- /mailersend/sms_inbounds/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /sms-inbounds endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewSmsInbounds(base.NewAPIClient): 10 | """ 11 | Instantiates the /sms-inbounds endpoint object 12 | """ 13 | 14 | # you shall not 15 | pass 16 | 17 | def get_inbound_routes(self, sms_number_id=None, enabled=True, page=1, limit=25): 18 | """ 19 | Get a list of SMS inbound routes. 20 | 21 | @params: 22 | sms_number_id (string) 23 | enabled (bool) 24 | page (int) 25 | limit (int) 26 | """ 27 | 28 | passed_arguments = locals() 29 | query_params = {} 30 | 31 | for key, value in passed_arguments.items(): 32 | if key != "self": 33 | if key == "enabled": 34 | query_params[key] = int(value) 35 | else: 36 | query_params[key] = value 37 | 38 | request = requests.get( 39 | f"{self.api_base}/sms-inbounds", 40 | headers=self.headers_default, 41 | params=query_params, 42 | ) 43 | 44 | return f"{request.status_code}\n{request.text}" 45 | 46 | def get_inbound_route(self, sms_inbound_id): 47 | """ 48 | Get a single SMS inbound route. 49 | 50 | @params: 51 | sms_inbound_id (string) 52 | """ 53 | 54 | request = requests.get( 55 | f"{self.api_base}/sms-inbounds/{sms_inbound_id}", 56 | headers=self.headers_default, 57 | ) 58 | 59 | return f"{request.status_code}\n{request.text}" 60 | 61 | def create_inbound_route( 62 | self, sms_number_id, name, forward_url, filter={}, enabled=True 63 | ): 64 | """ 65 | Add an SMS inbound route. 66 | 67 | @params: 68 | sms_number_id (string) 69 | name (string) 70 | forward_url (string) 71 | filter (object) 72 | enabled (bool) 73 | """ 74 | 75 | data = { 76 | "sms_number_id": sms_number_id, 77 | "name": name, 78 | "forward_url": forward_url, 79 | "filter": filter, 80 | "enabled": int(enabled), 81 | } 82 | 83 | request = requests.post( 84 | f"{self.api_base}/sms-inbounds", headers=self.headers_default, json=data 85 | ) 86 | 87 | return f"{request.status_code}\n{request.text}" 88 | 89 | def update_inbound_route( 90 | self, sms_inbound_id, name=None, forward_url=None, filter=None, enabled=None 91 | ): 92 | """ 93 | Update an inbound route. 94 | 95 | @params: 96 | sms_inbound_id (string) 97 | name (string) 98 | forward_url (string) 99 | filter (object) 100 | enabled (bool) 101 | """ 102 | 103 | passed_arguments = locals() 104 | data = {} 105 | 106 | for key, value in passed_arguments.items(): 107 | if key != "self" and key != "sms_inbound_id" and value is not None: 108 | if key == "enabled": 109 | data[key] = int(value) 110 | else: 111 | data[key] = value 112 | 113 | request = requests.put( 114 | f"{self.api_base}/sms-inbounds/{sms_inbound_id}", 115 | headers=self.headers_default, 116 | json=data, 117 | ) 118 | 119 | return f"{request.status_code}\n{request.text}" 120 | 121 | def delete_inbound_route(self, sms_inbound_id): 122 | """ 123 | Delete an SMS inbound route. 124 | 125 | @params: 126 | sms_inbound_id (string) 127 | """ 128 | 129 | request = requests.delete( 130 | f"{self.api_base}/sms-inbounds/{sms_inbound_id}", 131 | headers=self.headers_default, 132 | ) 133 | 134 | return f"{request.status_code}\n{request.text}" 135 | -------------------------------------------------------------------------------- /mailersend/sms_messages/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /sms-messages endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewSmsMessages(base.NewAPIClient): 10 | """ 11 | Instantiates the /sms-messages endpoint object 12 | """ 13 | 14 | pass 15 | 16 | def get_messages(self, page=1, limit=25): 17 | """ 18 | Get a list of SMS messages. 19 | 20 | @params: 21 | page (int) 22 | limit (int): Min: `10`, Max: `100`, default is 25 23 | """ 24 | 25 | passed_arguments = locals() 26 | query_params = {} 27 | 28 | for key, value in passed_arguments.items(): 29 | query_params[key] = value 30 | 31 | request = requests.get( 32 | f"{self.api_base}/sms-messages", 33 | headers=self.headers_default, 34 | params=query_params, 35 | ) 36 | 37 | return f"{request.status_code}\n{request.text}" 38 | 39 | def get_message(self, sms_message_id): 40 | """ 41 | Get a single SMS message. 42 | 43 | @params: 44 | sms_message_id (string) 45 | """ 46 | 47 | request = requests.get( 48 | f"{self.api_base}/sms-messages/{sms_message_id}", 49 | headers=self.headers_default, 50 | ) 51 | 52 | return f"{request.status_code}\n{request.text}" 53 | -------------------------------------------------------------------------------- /mailersend/sms_phone_numbers/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /sms-numbers endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewSmsNumbers(base.NewAPIClient): 10 | """ 11 | Instantiates the /sms-numbers endpoint object 12 | """ 13 | 14 | pass 15 | 16 | def get_phone_numbers(self, paused=False, page=None, limit=25): 17 | """ 18 | Get a list of SMS phone numbers information. 19 | 20 | @params: 21 | paused (bool) 22 | page (int) 23 | limit (int): Min: `10`, Max: `100`, default is 25 24 | """ 25 | 26 | passed_arguments = locals() 27 | query_params = {} 28 | 29 | for key, value in passed_arguments.items(): 30 | if key != "self": 31 | if key == "paused": 32 | query_params[key] = int(value) 33 | else: 34 | query_params[key] = value 35 | 36 | request = requests.get( 37 | f"{self.api_base}/sms-numbers", 38 | headers=self.headers_default, 39 | params=query_params, 40 | ) 41 | 42 | return f"{request.status_code}\n{request.text}" 43 | 44 | def get_phone_number(self, sms_number_id): 45 | """ 46 | Get information about a specific SMS phone number 47 | 48 | @params: 49 | sms_number_id (string) 50 | """ 51 | 52 | request = requests.get( 53 | f"{self.api_base}/sms-numbers/{sms_number_id}", headers=self.headers_default 54 | ) 55 | 56 | return f"{request.status_code}\n{request.text}" 57 | 58 | def update_phone_number(self, sms_number_id, paused=True): 59 | """ 60 | Update a specific SMS phone number 61 | 62 | @params: 63 | sms_number_id (string) 64 | paused (bool) 65 | """ 66 | 67 | query_params = {"paused": int(paused)} 68 | data = {"sms_number_id": sms_number_id} 69 | 70 | request = requests.put( 71 | f"{self.api_base}/sms-numbers/{sms_number_id}", 72 | headers=self.headers_default, 73 | params=query_params, 74 | json=data, 75 | ) 76 | 77 | return f"{request.status_code}\n{request.text}" 78 | 79 | def delete_phone_number(self, sms_number_id): 80 | """ 81 | Delete a specific SMS phone number 82 | 83 | @params: 84 | sms_number_id (string) 85 | """ 86 | 87 | data = {"sms_number_id": sms_number_id} 88 | 89 | request = requests.delete( 90 | f"{self.api_base}/sms-numbers/{sms_number_id}", 91 | headers=self.headers_default, 92 | json=data, 93 | ) 94 | 95 | return f"{request.status_code}\n{request.text}" 96 | -------------------------------------------------------------------------------- /mailersend/sms_recipients/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /sms-recipients endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewSmsRecipients(base.NewAPIClient): 10 | """ 11 | Instantiates the /sms-recipients endpoint object 12 | """ 13 | 14 | pass 15 | 16 | def get_recipients(self, status="active", sms_number_id=None, page=None, limit=25): 17 | """ 18 | Get information about SMS recipients. 19 | 20 | @params: 21 | status (string) - Possible values are `active` and `opt_out` 22 | sms_number_id (string) 23 | page (int) 24 | limit (int): Min: `10`, Max: `100`, default is 25 25 | """ 26 | 27 | passed_arguments = locals() 28 | query_params = {} 29 | 30 | for key, value in passed_arguments.items(): 31 | if key != "self": 32 | query_params[key] = value 33 | 34 | request = requests.get( 35 | f"{self.api_base}/sms-recipients", 36 | headers=self.headers_default, 37 | params=query_params, 38 | ) 39 | 40 | return f"{request.status_code}\n{request.text}" 41 | 42 | def get_recipient(self, sms_recipient_id): 43 | """ 44 | Get information about a specific SMS recipient. 45 | 46 | @params: 47 | sms_recipient_id (string) - Possible values are `active` and `opt_out` 48 | """ 49 | 50 | request = requests.get( 51 | f"{self.api_base}/sms-recipients/{sms_recipient_id}", 52 | headers=self.headers_default, 53 | ) 54 | 55 | return f"{request.status_code}\n{request.text}" 56 | 57 | def update_recipient(self, sms_recipient_id, status): 58 | """ 59 | Update a specific SMS recipient 60 | 61 | @params: 62 | sms_recipient_id (string) 63 | status (string) 64 | """ 65 | 66 | query_params = {"status": status} 67 | data = {"sms_recipient_id": sms_recipient_id} 68 | 69 | request = requests.put( 70 | f"{self.api_base}/sms-recipients/{sms_recipient_id}", 71 | headers=self.headers_default, 72 | params=query_params, 73 | json=data, 74 | ) 75 | 76 | return f"{request.status_code}\n{request.text}" 77 | -------------------------------------------------------------------------------- /mailersend/sms_sending/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /sms endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewSmsSending(base.NewAPIClient): 10 | """ 11 | Instantiates the /sms endpoint object 12 | """ 13 | 14 | pass 15 | 16 | def send_sms(self, number_from, numbers_to, text, personalization=None): 17 | """ 18 | Send SMS message to one or more recipients 19 | 20 | Returns the JSON response of MailerSend API 21 | 22 | @params: 23 | number_from (str): Number belonging to your account in E164 format 24 | numbers_to (dict): Recipient phone numbers (up to 50) 25 | text (str): Message test 26 | personalization: Allows using personalization in {{ var }} syntax. Can be used in the text fields 27 | """ 28 | 29 | data = { 30 | "from": number_from, 31 | "to": numbers_to, 32 | "text": text, 33 | "personalization": personalization, 34 | } 35 | 36 | if personalization is None: 37 | data["personalization"] = [] 38 | 39 | request = requests.post( 40 | f"{self.api_base}/sms", headers=self.headers_default, json=data 41 | ) 42 | 43 | return f"{request.status_code}\n{request.headers['X-SMS-Message-Id']}" 44 | -------------------------------------------------------------------------------- /mailersend/sms_webhooks/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /sms-webhooks endpoint 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewSmsWebhooks(base.NewAPIClient): 10 | """ 11 | Instantiates the /sms-webhooks endpoint object 12 | """ 13 | 14 | # you shall not 15 | pass 16 | 17 | def get_webhooks(self, sms_number_id): 18 | """ 19 | Get a list of SMS webhooks. 20 | 21 | @params: 22 | sms_number_id (string) 23 | """ 24 | 25 | passed_arguments = locals() 26 | query_params = {"sms_number_id": sms_number_id} 27 | 28 | request = requests.get( 29 | f"{self.api_base}/sms-webhooks", 30 | headers=self.headers_default, 31 | params=query_params, 32 | ) 33 | 34 | return f"{request.status_code}\n{request.text}" 35 | 36 | def get_webhook(self, sms_webhook_id): 37 | """ 38 | Get a single SMS webhook. 39 | 40 | @params: 41 | sms_webhook_id (string) 42 | """ 43 | 44 | request = requests.get( 45 | f"{self.api_base}/sms-webhooks/{sms_webhook_id}", 46 | headers=self.headers_default, 47 | ) 48 | 49 | return f"{request.status_code}\n{request.text}" 50 | 51 | def create_webhook(self, url, name, events, sms_number_id, enabled=True): 52 | """ 53 | Create an SMS webhook. 54 | 55 | @params: 56 | url (string) 57 | name (string) 58 | events (dict) 59 | enabled (bool) 60 | sms_number_id (string) 61 | """ 62 | 63 | data = { 64 | "url": url, 65 | "name": name, 66 | "events": events, 67 | "sms_number_id": sms_number_id, 68 | "enabled": int(enabled), 69 | } 70 | 71 | request = requests.post( 72 | f"{self.api_base}/sms-webhooks", headers=self.headers_default, json=data 73 | ) 74 | 75 | return f"{request.status_code}\n{request.text}" 76 | 77 | def update_webhook( 78 | self, sms_webhook_id, url=None, name=None, events=None, enabled=None 79 | ): 80 | """ 81 | Update a single SMS Webhook. 82 | 83 | @params: 84 | sms_webhook_id (string) 85 | url (string) 86 | name (string) 87 | events (dict) 88 | enabled (bool) 89 | """ 90 | 91 | passed_arguments = locals() 92 | data = {} 93 | 94 | for key, value in passed_arguments.items(): 95 | if key != "self" and key != "sms_webhook_id" and value is not None: 96 | if key == "enabled": 97 | data[key] = int(value) 98 | else: 99 | data[key] = value 100 | 101 | request = requests.put( 102 | f"{self.api_base}/sms-webhooks/{sms_webhook_id}", 103 | headers=self.headers_default, 104 | json=data, 105 | ) 106 | 107 | return f"{request.status_code}\n{request.text}" 108 | 109 | def delete_webhook(self, sms_webhook_id): 110 | """ 111 | Delete an SMS webhook. 112 | 113 | @params: 114 | sms_webhook_id (string) 115 | """ 116 | 117 | request = requests.delete( 118 | f"{self.api_base}/sms-webhooks/{sms_webhook_id}", 119 | headers=self.headers_default, 120 | ) 121 | 122 | return f"{request.status_code}\n{request.text}" 123 | -------------------------------------------------------------------------------- /mailersend/templates/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /templates endpoint 3 | Doc: https://developers.mailersend.com/api/v1/templates.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewTemplate(base.NewAPIClient): 11 | """ 12 | Instantiates the /templates endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def get_templates(self): 18 | """ 19 | Returns a JSON response from the MailerSend API 20 | """ 21 | request = requests.get( 22 | f"{self.api_base}/templates", headers=self.headers_default 23 | ) 24 | return request.text 25 | 26 | def get_template_by_id(self, template_id): 27 | """ 28 | Returns a JSON response from the MailerSend API 29 | """ 30 | request = requests.get( 31 | f"{self.api_base}/templates/{template_id}", headers=self.headers_default 32 | ) 33 | return request.text 34 | 35 | def delete_template(self, template_id): 36 | """ 37 | Returns a JSON response from the MailerSend API 38 | """ 39 | request = requests.delete( 40 | f"{self.api_base}/templates/{template_id}", headers=self.headers_default 41 | ) 42 | return request.text 43 | -------------------------------------------------------------------------------- /mailersend/tokens/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /tokens endpoint 3 | Doc: https://developers.mailersend.com/api/v1/tokens.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | 10 | class NewToken(base.NewAPIClient): 11 | """ 12 | Instantiates the /tokens endpoint object 13 | """ 14 | 15 | pass 16 | 17 | def create_token(self, token_name, token_scopes): 18 | """ 19 | Returns a JSON response from the MailerSend API 20 | """ 21 | 22 | _data = {"name": token_name, "scopes": token_scopes} 23 | 24 | request = requests.post( 25 | f"{self.api_base}/token", headers=self.headers_default, json=_data 26 | ) 27 | return request.text 28 | 29 | def update_token(self, token_id, pause=True): 30 | """ 31 | Returns a JSON response from the MailerSend API 32 | """ 33 | 34 | if pause: 35 | _data = base.generate_config_change_json_body("status", "pause") 36 | else: 37 | _data = base.generate_config_change_json_body("status", "unpause") 38 | 39 | request = requests.put( 40 | f"{self.api_base}/token/{token_id}/settings", 41 | headers=self.headers_default, 42 | json=_data, 43 | ) 44 | return request.text 45 | 46 | def delete_token(self, token_id): 47 | """ 48 | Returns a HTTP status code from the MailerSend API 49 | """ 50 | 51 | request = requests.delete( 52 | f"{self.api_base}/token/{token_id}/", headers=self.headers_default 53 | ) 54 | return request.status_code 55 | -------------------------------------------------------------------------------- /mailersend/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provides helper functions to convenience devs 3 | """ 4 | 5 | import requests 6 | from mailersend.base import base 7 | 8 | 9 | class NewHelper(base.NewAPIClient): 10 | """ 11 | NewHelper extends base.NewAPIClient to inherit connection details 12 | """ 13 | 14 | def __init__(self): 15 | """ 16 | NewHelper constructor 17 | """ 18 | pass 19 | 20 | def get_id_by_name(self, category, name): 21 | """ 22 | Returns an ID given a category and item name from the MailerSend API 23 | 24 | @params: 25 | category (str): Can be one of "recipients", "domains" 26 | name (str): Object name 27 | """ 28 | 29 | request = requests.get( 30 | f"{self.api_base}/{category}", headers=self.headers_default 31 | ) 32 | 33 | _json_req = request.json() 34 | 35 | category_search_asset = {"recipients": "email", "domains": "name"} 36 | 37 | _data_block = _json_req["data"] 38 | 39 | for data in _data_block: 40 | if data[category_search_asset.get(category)] == name: 41 | return data["id"] 42 | 43 | return request.text 44 | -------------------------------------------------------------------------------- /mailersend/webhooks/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handles /webhooks endpoint 3 | Doc: https://developers.mailersend.com/api/v1/webhooks.html 4 | """ 5 | 6 | import requests 7 | from mailersend.base import base 8 | 9 | data = {} 10 | 11 | 12 | class NewWebhook(base.NewAPIClient): 13 | """ 14 | Instantiates the /webhooks endpoint object 15 | """ 16 | 17 | def __init__(self): 18 | """ 19 | NewWebhook constructor 20 | """ 21 | pass 22 | 23 | def get_webhooks(self, domain_id): 24 | """ 25 | Returns a JSON response from the MailerSend API 26 | 27 | @params: 28 | domain_id (str): A domain ID 29 | """ 30 | request = requests.get( 31 | f"{self.api_base}/webhooks", 32 | headers=self.headers_default, 33 | json={"domain_id": domain_id}, 34 | ) 35 | return request.text 36 | 37 | def get_webhook_by_id(self, webhook_id): 38 | """ 39 | Returns a JSON response from the MailerSend API 40 | 41 | @params: 42 | webhook_id (str): A webhook ID 43 | """ 44 | request = requests.get( 45 | f"{self.api_base}/webhooks/{webhook_id}", headers=self.headers_default 46 | ) 47 | return request.text 48 | 49 | def set_webhook_url(self, webhook_url): 50 | """ 51 | Sets the webhook 'url' field 52 | 53 | @params: 54 | webhook_url (str): A webhook URL 55 | """ 56 | data["url"] = webhook_url 57 | 58 | def set_webhook_name(self, webhook_name): 59 | """ 60 | Sets the webhook 'name' field 61 | 62 | @params: 63 | webhook_name (str): A webhook name 64 | """ 65 | 66 | data["name"] = webhook_name 67 | 68 | def set_webhook_events(self, events): 69 | """ 70 | Sets the webhook 'events' field 71 | 72 | @params: 73 | events (list): A list containing valid events 74 | """ 75 | data["events"] = events 76 | 77 | def set_webhook_enabled(self, enabled=True): 78 | """ 79 | Sets the webhook 'enabled' status field 80 | 81 | @params: 82 | enabled (bool): Controls webhook status 83 | """ 84 | 85 | data["enabled"] = enabled 86 | 87 | def set_webhook_domain(self, domain_id): 88 | """ 89 | Sets the webhook 'domain_id' status field 90 | 91 | @params: 92 | domain_id (str): A valid domain ID 93 | """ 94 | 95 | data["domain_id"] = domain_id 96 | 97 | def update_webhook(self, webhook_id, key, value): 98 | """ 99 | Updates a webhook setting 100 | 101 | @params: 102 | webhook_id (str): A valid webhook ID 103 | key (str): A setting key 104 | value (str): Corresponding keys value 105 | """ 106 | 107 | request = requests.put( 108 | f"{self.api_base}/webhooks/{webhook_id}", 109 | headers=self.headers_default, 110 | json={f"{key}": value}, 111 | ) 112 | return request.text 113 | 114 | def delete_webhook(self, webhook_id): 115 | """ 116 | Returns a JSON response from the MailerSend API 117 | 118 | @params: 119 | webhook_id (str): A valid webhook ID 120 | """ 121 | 122 | request = requests.delete( 123 | f"{self.api_base}/webhooks/{webhook_id}", headers=self.headers_default 124 | ) 125 | return request.text 126 | 127 | def create_webhook(self): 128 | """ 129 | Returns a JSON response from the MailerSend API 130 | """ 131 | 132 | request = requests.post( 133 | f"{self.api_base}/webhooks", headers=self.headers_default, json=data 134 | ) 135 | return request.text 136 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "backports-tarfile" 5 | version = "1.2.0" 6 | description = "Backport of CPython tarfile module" 7 | optional = false 8 | python-versions = ">=3.8" 9 | groups = ["dev"] 10 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\"" 11 | files = [ 12 | {file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"}, 13 | {file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"}, 14 | ] 15 | 16 | [package.extras] 17 | docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 18 | testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"] 19 | 20 | [[package]] 21 | name = "certifi" 22 | version = "2025.1.31" 23 | description = "Python package for providing Mozilla's CA Bundle." 24 | optional = false 25 | python-versions = ">=3.6" 26 | groups = ["main", "dev"] 27 | files = [ 28 | {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, 29 | {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, 30 | ] 31 | 32 | [[package]] 33 | name = "cffi" 34 | version = "1.17.1" 35 | description = "Foreign Function Interface for Python calling C code." 36 | optional = false 37 | python-versions = ">=3.8" 38 | groups = ["dev"] 39 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" 40 | files = [ 41 | {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, 42 | {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, 43 | {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, 44 | {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, 45 | {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, 46 | {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, 47 | {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, 48 | {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, 49 | {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, 50 | {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, 51 | {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, 52 | {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, 53 | {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, 54 | {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, 55 | {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, 56 | {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, 57 | {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, 58 | {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, 59 | {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, 60 | {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, 61 | {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, 62 | {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, 63 | {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, 64 | {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, 65 | {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, 66 | {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, 67 | {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, 68 | {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, 69 | {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, 70 | {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, 71 | {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, 72 | {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, 73 | {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, 74 | {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, 75 | {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, 76 | {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, 77 | {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, 78 | {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, 79 | {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, 80 | {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, 81 | {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, 82 | {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, 83 | {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, 84 | {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, 85 | {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, 86 | {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, 87 | {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, 88 | {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, 89 | {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, 90 | {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, 91 | {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, 92 | {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, 93 | {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, 94 | {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, 95 | {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, 96 | {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, 97 | {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, 98 | {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, 99 | {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, 100 | {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, 101 | {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, 102 | {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, 103 | {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, 104 | {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, 105 | {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, 106 | {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, 107 | {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, 108 | ] 109 | 110 | [package.dependencies] 111 | pycparser = "*" 112 | 113 | [[package]] 114 | name = "cfgv" 115 | version = "3.4.0" 116 | description = "Validate configuration and produce human readable error messages." 117 | optional = false 118 | python-versions = ">=3.8" 119 | groups = ["dev"] 120 | files = [ 121 | {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, 122 | {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, 123 | ] 124 | 125 | [[package]] 126 | name = "charset-normalizer" 127 | version = "3.4.1" 128 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 129 | optional = false 130 | python-versions = ">=3.7" 131 | groups = ["main", "dev"] 132 | files = [ 133 | {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, 134 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, 135 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, 136 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, 137 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, 138 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, 139 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, 140 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, 141 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, 142 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, 143 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, 144 | {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, 145 | {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, 146 | {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, 147 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, 148 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, 149 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, 150 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, 151 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, 152 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, 153 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, 154 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, 155 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, 156 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, 157 | {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, 158 | {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, 159 | {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, 160 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, 161 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, 162 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, 163 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, 164 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, 165 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, 166 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, 167 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, 168 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, 169 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, 170 | {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, 171 | {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, 172 | {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, 173 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, 174 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, 175 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, 176 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, 177 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, 178 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, 179 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, 180 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, 181 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, 182 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, 183 | {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, 184 | {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, 185 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, 186 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, 187 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, 188 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, 189 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, 190 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, 191 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, 192 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, 193 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, 194 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, 195 | {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, 196 | {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, 197 | {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, 198 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, 199 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, 200 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, 201 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, 202 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, 203 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, 204 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, 205 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, 206 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, 207 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, 208 | {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, 209 | {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, 210 | {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, 211 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, 212 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, 213 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, 214 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, 215 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, 216 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, 217 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, 218 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, 219 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, 220 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, 221 | {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, 222 | {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, 223 | {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, 224 | {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, 225 | ] 226 | 227 | [[package]] 228 | name = "colorama" 229 | version = "0.4.6" 230 | description = "Cross-platform colored terminal text." 231 | optional = false 232 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 233 | groups = ["dev"] 234 | markers = "sys_platform == \"win32\"" 235 | files = [ 236 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 237 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 238 | ] 239 | 240 | [[package]] 241 | name = "cryptography" 242 | version = "43.0.3" 243 | description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." 244 | optional = false 245 | python-versions = ">=3.7" 246 | groups = ["dev"] 247 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" 248 | files = [ 249 | {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, 250 | {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, 251 | {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, 252 | {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, 253 | {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, 254 | {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, 255 | {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, 256 | {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, 257 | {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, 258 | {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, 259 | {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, 260 | {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, 261 | {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, 262 | {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, 263 | {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, 264 | {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, 265 | {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, 266 | {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, 267 | {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, 268 | {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, 269 | {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, 270 | {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, 271 | {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, 272 | {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, 273 | {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, 274 | {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, 275 | {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, 276 | ] 277 | 278 | [package.dependencies] 279 | cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} 280 | 281 | [package.extras] 282 | docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] 283 | docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] 284 | nox = ["nox"] 285 | pep8test = ["check-sdist", "click", "mypy", "ruff"] 286 | sdist = ["build"] 287 | ssh = ["bcrypt (>=3.1.5)"] 288 | test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] 289 | test-randomorder = ["pytest-randomly"] 290 | 291 | [[package]] 292 | name = "distlib" 293 | version = "0.3.9" 294 | description = "Distribution utilities" 295 | optional = false 296 | python-versions = "*" 297 | groups = ["dev"] 298 | files = [ 299 | {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, 300 | {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, 301 | ] 302 | 303 | [[package]] 304 | name = "docutils" 305 | version = "0.21.2" 306 | description = "Docutils -- Python Documentation Utilities" 307 | optional = false 308 | python-versions = ">=3.9" 309 | groups = ["main", "dev"] 310 | files = [ 311 | {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, 312 | {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, 313 | ] 314 | 315 | [[package]] 316 | name = "exceptiongroup" 317 | version = "1.2.2" 318 | description = "Backport of PEP 654 (exception groups)" 319 | optional = false 320 | python-versions = ">=3.7" 321 | groups = ["dev"] 322 | markers = "python_version < \"3.11\"" 323 | files = [ 324 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 325 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 326 | ] 327 | 328 | [package.extras] 329 | test = ["pytest (>=6)"] 330 | 331 | [[package]] 332 | name = "filelock" 333 | version = "3.18.0" 334 | description = "A platform independent file lock." 335 | optional = false 336 | python-versions = ">=3.9" 337 | groups = ["dev"] 338 | files = [ 339 | {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, 340 | {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, 341 | ] 342 | 343 | [package.extras] 344 | docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] 345 | testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] 346 | typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] 347 | 348 | [[package]] 349 | name = "id" 350 | version = "1.5.0" 351 | description = "A tool for generating OIDC identities" 352 | optional = false 353 | python-versions = ">=3.8" 354 | groups = ["dev"] 355 | files = [ 356 | {file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"}, 357 | {file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"}, 358 | ] 359 | 360 | [package.dependencies] 361 | requests = "*" 362 | 363 | [package.extras] 364 | dev = ["build", "bump (>=1.3.2)", "id[lint,test]"] 365 | lint = ["bandit", "interrogate", "mypy", "ruff (<0.8.2)", "types-requests"] 366 | test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"] 367 | 368 | [[package]] 369 | name = "identify" 370 | version = "2.6.10" 371 | description = "File identification library for Python" 372 | optional = false 373 | python-versions = ">=3.9" 374 | groups = ["dev"] 375 | files = [ 376 | {file = "identify-2.6.10-py2.py3-none-any.whl", hash = "sha256:5f34248f54136beed1a7ba6a6b5c4b6cf21ff495aac7c359e1ef831ae3b8ab25"}, 377 | {file = "identify-2.6.10.tar.gz", hash = "sha256:45e92fd704f3da71cc3880036633f48b4b7265fd4de2b57627cb157216eb7eb8"}, 378 | ] 379 | 380 | [package.extras] 381 | license = ["ukkonen"] 382 | 383 | [[package]] 384 | name = "idna" 385 | version = "3.10" 386 | description = "Internationalized Domain Names in Applications (IDNA)" 387 | optional = false 388 | python-versions = ">=3.6" 389 | groups = ["main", "dev"] 390 | files = [ 391 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 392 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 393 | ] 394 | 395 | [package.extras] 396 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 397 | 398 | [[package]] 399 | name = "importlib-metadata" 400 | version = "8.6.1" 401 | description = "Read metadata from Python packages" 402 | optional = false 403 | python-versions = ">=3.9" 404 | groups = ["dev"] 405 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" 406 | files = [ 407 | {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, 408 | {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, 409 | ] 410 | 411 | [package.dependencies] 412 | zipp = ">=3.20" 413 | 414 | [package.extras] 415 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 416 | cover = ["pytest-cov"] 417 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 418 | enabler = ["pytest-enabler (>=2.2)"] 419 | perf = ["ipython"] 420 | test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] 421 | type = ["pytest-mypy"] 422 | 423 | [[package]] 424 | name = "iniconfig" 425 | version = "2.1.0" 426 | description = "brain-dead simple config-ini parsing" 427 | optional = false 428 | python-versions = ">=3.8" 429 | groups = ["dev"] 430 | files = [ 431 | {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, 432 | {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, 433 | ] 434 | 435 | [[package]] 436 | name = "jaraco-classes" 437 | version = "3.4.0" 438 | description = "Utility functions for Python class constructs" 439 | optional = false 440 | python-versions = ">=3.8" 441 | groups = ["dev"] 442 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" 443 | files = [ 444 | {file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"}, 445 | {file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"}, 446 | ] 447 | 448 | [package.dependencies] 449 | more-itertools = "*" 450 | 451 | [package.extras] 452 | docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 453 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)"] 454 | 455 | [[package]] 456 | name = "jaraco-context" 457 | version = "6.0.1" 458 | description = "Useful decorators and context managers" 459 | optional = false 460 | python-versions = ">=3.8" 461 | groups = ["dev"] 462 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" 463 | files = [ 464 | {file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"}, 465 | {file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"}, 466 | ] 467 | 468 | [package.dependencies] 469 | "backports.tarfile" = {version = "*", markers = "python_version < \"3.12\""} 470 | 471 | [package.extras] 472 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 473 | test = ["portend", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 474 | 475 | [[package]] 476 | name = "jaraco-functools" 477 | version = "4.1.0" 478 | description = "Functools like those found in stdlib" 479 | optional = false 480 | python-versions = ">=3.8" 481 | groups = ["dev"] 482 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" 483 | files = [ 484 | {file = "jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"}, 485 | {file = "jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"}, 486 | ] 487 | 488 | [package.dependencies] 489 | more-itertools = "*" 490 | 491 | [package.extras] 492 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 493 | cover = ["pytest-cov"] 494 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 495 | enabler = ["pytest-enabler (>=2.2)"] 496 | test = ["jaraco.classes", "pytest (>=6,!=8.1.*)"] 497 | type = ["pytest-mypy"] 498 | 499 | [[package]] 500 | name = "jeepney" 501 | version = "0.9.0" 502 | description = "Low-level, pure Python DBus protocol wrapper." 503 | optional = false 504 | python-versions = ">=3.7" 505 | groups = ["dev"] 506 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" 507 | files = [ 508 | {file = "jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683"}, 509 | {file = "jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732"}, 510 | ] 511 | 512 | [package.extras] 513 | test = ["async-timeout ; python_version < \"3.11\"", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] 514 | trio = ["trio"] 515 | 516 | [[package]] 517 | name = "keyring" 518 | version = "25.6.0" 519 | description = "Store and access your passwords safely." 520 | optional = false 521 | python-versions = ">=3.9" 522 | groups = ["dev"] 523 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" 524 | files = [ 525 | {file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"}, 526 | {file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"}, 527 | ] 528 | 529 | [package.dependencies] 530 | importlib_metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} 531 | "jaraco.classes" = "*" 532 | "jaraco.context" = "*" 533 | "jaraco.functools" = "*" 534 | jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} 535 | pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} 536 | SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} 537 | 538 | [package.extras] 539 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 540 | completion = ["shtab (>=1.1.0)"] 541 | cover = ["pytest-cov"] 542 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 543 | enabler = ["pytest-enabler (>=2.2)"] 544 | test = ["pyfakefs", "pytest (>=6,!=8.1.*)"] 545 | type = ["pygobject-stubs", "pytest-mypy", "shtab", "types-pywin32"] 546 | 547 | [[package]] 548 | name = "markdown-it-py" 549 | version = "3.0.0" 550 | description = "Python port of markdown-it. Markdown parsing, done right!" 551 | optional = false 552 | python-versions = ">=3.8" 553 | groups = ["dev"] 554 | files = [ 555 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 556 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 557 | ] 558 | 559 | [package.dependencies] 560 | mdurl = ">=0.1,<1.0" 561 | 562 | [package.extras] 563 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 564 | code-style = ["pre-commit (>=3.0,<4.0)"] 565 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 566 | linkify = ["linkify-it-py (>=1,<3)"] 567 | plugins = ["mdit-py-plugins"] 568 | profiling = ["gprof2dot"] 569 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 570 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 571 | 572 | [[package]] 573 | name = "mdurl" 574 | version = "0.1.2" 575 | description = "Markdown URL utilities" 576 | optional = false 577 | python-versions = ">=3.7" 578 | groups = ["dev"] 579 | files = [ 580 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 581 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 582 | ] 583 | 584 | [[package]] 585 | name = "more-itertools" 586 | version = "10.7.0" 587 | description = "More routines for operating on iterables, beyond itertools" 588 | optional = false 589 | python-versions = ">=3.9" 590 | groups = ["dev"] 591 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\"" 592 | files = [ 593 | {file = "more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e"}, 594 | {file = "more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3"}, 595 | ] 596 | 597 | [[package]] 598 | name = "nh3" 599 | version = "0.2.21" 600 | description = "Python binding to Ammonia HTML sanitizer Rust crate" 601 | optional = false 602 | python-versions = ">=3.8" 603 | groups = ["dev"] 604 | files = [ 605 | {file = "nh3-0.2.21-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:fcff321bd60c6c5c9cb4ddf2554e22772bb41ebd93ad88171bbbb6f271255286"}, 606 | {file = "nh3-0.2.21-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31eedcd7d08b0eae28ba47f43fd33a653b4cdb271d64f1aeda47001618348fde"}, 607 | {file = "nh3-0.2.21-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d426d7be1a2f3d896950fe263332ed1662f6c78525b4520c8e9861f8d7f0d243"}, 608 | {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9d67709bc0d7d1f5797b21db26e7a8b3d15d21c9c5f58ccfe48b5328483b685b"}, 609 | {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:55823c5ea1f6b267a4fad5de39bc0524d49a47783e1fe094bcf9c537a37df251"}, 610 | {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:818f2b6df3763e058efa9e69677b5a92f9bc0acff3295af5ed013da544250d5b"}, 611 | {file = "nh3-0.2.21-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b3b5c58161e08549904ac4abd450dacd94ff648916f7c376ae4b2c0652b98ff9"}, 612 | {file = "nh3-0.2.21-cp313-cp313t-win32.whl", hash = "sha256:637d4a10c834e1b7d9548592c7aad760611415fcd5bd346f77fd8a064309ae6d"}, 613 | {file = "nh3-0.2.21-cp313-cp313t-win_amd64.whl", hash = "sha256:713d16686596e556b65e7f8c58328c2df63f1a7abe1277d87625dcbbc012ef82"}, 614 | {file = "nh3-0.2.21-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a772dec5b7b7325780922dd904709f0f5f3a79fbf756de5291c01370f6df0967"}, 615 | {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d002b648592bf3033adfd875a48f09b8ecc000abd7f6a8769ed86b6ccc70c759"}, 616 | {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2a5174551f95f2836f2ad6a8074560f261cf9740a48437d6151fd2d4d7d617ab"}, 617 | {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b8d55ea1fc7ae3633d758a92aafa3505cd3cc5a6e40470c9164d54dff6f96d42"}, 618 | {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ae319f17cd8960d0612f0f0ddff5a90700fa71926ca800e9028e7851ce44a6f"}, 619 | {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ca02ac6f27fc80f9894409eb61de2cb20ef0a23740c7e29f9ec827139fa578"}, 620 | {file = "nh3-0.2.21-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f77e62aed5c4acad635239ac1290404c7e940c81abe561fd2af011ff59f585"}, 621 | {file = "nh3-0.2.21-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:087ffadfdcd497658c3adc797258ce0f06be8a537786a7217649fc1c0c60c293"}, 622 | {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ac7006c3abd097790e611fe4646ecb19a8d7f2184b882f6093293b8d9b887431"}, 623 | {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:6141caabe00bbddc869665b35fc56a478eb774a8c1dfd6fba9fe1dfdf29e6efa"}, 624 | {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:20979783526641c81d2f5bfa6ca5ccca3d1e4472474b162c6256745fbfe31cd1"}, 625 | {file = "nh3-0.2.21-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a7ea28cd49293749d67e4fcf326c554c83ec912cd09cd94aa7ec3ab1921c8283"}, 626 | {file = "nh3-0.2.21-cp38-abi3-win32.whl", hash = "sha256:6c9c30b8b0d291a7c5ab0967ab200598ba33208f754f2f4920e9343bdd88f79a"}, 627 | {file = "nh3-0.2.21-cp38-abi3-win_amd64.whl", hash = "sha256:bb0014948f04d7976aabae43fcd4cb7f551f9f8ce785a4c9ef66e6c2590f8629"}, 628 | {file = "nh3-0.2.21.tar.gz", hash = "sha256:4990e7ee6a55490dbf00d61a6f476c9a3258e31e711e13713b2ea7d6616f670e"}, 629 | ] 630 | 631 | [[package]] 632 | name = "nodeenv" 633 | version = "1.9.1" 634 | description = "Node.js virtual environment builder" 635 | optional = false 636 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 637 | groups = ["dev"] 638 | files = [ 639 | {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, 640 | {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, 641 | ] 642 | 643 | [[package]] 644 | name = "packaging" 645 | version = "25.0" 646 | description = "Core utilities for Python packages" 647 | optional = false 648 | python-versions = ">=3.8" 649 | groups = ["dev"] 650 | files = [ 651 | {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, 652 | {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, 653 | ] 654 | 655 | [[package]] 656 | name = "platformdirs" 657 | version = "4.3.7" 658 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 659 | optional = false 660 | python-versions = ">=3.9" 661 | groups = ["dev"] 662 | files = [ 663 | {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, 664 | {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, 665 | ] 666 | 667 | [package.extras] 668 | docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] 669 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] 670 | type = ["mypy (>=1.14.1)"] 671 | 672 | [[package]] 673 | name = "pluggy" 674 | version = "1.5.0" 675 | description = "plugin and hook calling mechanisms for python" 676 | optional = false 677 | python-versions = ">=3.8" 678 | groups = ["dev"] 679 | files = [ 680 | {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, 681 | {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, 682 | ] 683 | 684 | [package.extras] 685 | dev = ["pre-commit", "tox"] 686 | testing = ["pytest", "pytest-benchmark"] 687 | 688 | [[package]] 689 | name = "pre-commit" 690 | version = "2.21.0" 691 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 692 | optional = false 693 | python-versions = ">=3.7" 694 | groups = ["dev"] 695 | files = [ 696 | {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, 697 | {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, 698 | ] 699 | 700 | [package.dependencies] 701 | cfgv = ">=2.0.0" 702 | identify = ">=1.0.0" 703 | nodeenv = ">=0.11.1" 704 | pyyaml = ">=5.1" 705 | virtualenv = ">=20.10.0" 706 | 707 | [[package]] 708 | name = "pycparser" 709 | version = "2.22" 710 | description = "C parser in Python" 711 | optional = false 712 | python-versions = ">=3.8" 713 | groups = ["dev"] 714 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"" 715 | files = [ 716 | {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, 717 | {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, 718 | ] 719 | 720 | [[package]] 721 | name = "pygments" 722 | version = "2.19.1" 723 | description = "Pygments is a syntax highlighting package written in Python." 724 | optional = false 725 | python-versions = ">=3.8" 726 | groups = ["dev"] 727 | files = [ 728 | {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, 729 | {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, 730 | ] 731 | 732 | [package.extras] 733 | windows-terminal = ["colorama (>=0.4.6)"] 734 | 735 | [[package]] 736 | name = "pytest" 737 | version = "8.3.5" 738 | description = "pytest: simple powerful testing with Python" 739 | optional = false 740 | python-versions = ">=3.8" 741 | groups = ["dev"] 742 | files = [ 743 | {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, 744 | {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, 745 | ] 746 | 747 | [package.dependencies] 748 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 749 | exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} 750 | iniconfig = "*" 751 | packaging = "*" 752 | pluggy = ">=1.5,<2" 753 | tomli = {version = ">=1", markers = "python_version < \"3.11\""} 754 | 755 | [package.extras] 756 | dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 757 | 758 | [[package]] 759 | name = "pywin32-ctypes" 760 | version = "0.2.3" 761 | description = "A (partial) reimplementation of pywin32 using ctypes/cffi" 762 | optional = false 763 | python-versions = ">=3.6" 764 | groups = ["dev"] 765 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"win32\"" 766 | files = [ 767 | {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, 768 | {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, 769 | ] 770 | 771 | [[package]] 772 | name = "pyyaml" 773 | version = "6.0.2" 774 | description = "YAML parser and emitter for Python" 775 | optional = false 776 | python-versions = ">=3.8" 777 | groups = ["dev"] 778 | files = [ 779 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 780 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 781 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 782 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 783 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 784 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 785 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 786 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 787 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 788 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 789 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 790 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 791 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 792 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 793 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 794 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 795 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 796 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 797 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 798 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 799 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 800 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 801 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 802 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 803 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 804 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 805 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 806 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 807 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 808 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 809 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 810 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 811 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 812 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 813 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 814 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 815 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 816 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 817 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 818 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 819 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 820 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 821 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 822 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 823 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 824 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 825 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 826 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 827 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 828 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 829 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 830 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 831 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 832 | ] 833 | 834 | [[package]] 835 | name = "readme-renderer" 836 | version = "44.0" 837 | description = "readme_renderer is a library for rendering readme descriptions for Warehouse" 838 | optional = false 839 | python-versions = ">=3.9" 840 | groups = ["dev"] 841 | files = [ 842 | {file = "readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151"}, 843 | {file = "readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1"}, 844 | ] 845 | 846 | [package.dependencies] 847 | docutils = ">=0.21.2" 848 | nh3 = ">=0.2.14" 849 | Pygments = ">=2.5.1" 850 | 851 | [package.extras] 852 | md = ["cmarkgfm (>=0.8.0)"] 853 | 854 | [[package]] 855 | name = "requests" 856 | version = "2.32.3" 857 | description = "Python HTTP for Humans." 858 | optional = false 859 | python-versions = ">=3.8" 860 | groups = ["main", "dev"] 861 | files = [ 862 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, 863 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, 864 | ] 865 | 866 | [package.dependencies] 867 | certifi = ">=2017.4.17" 868 | charset-normalizer = ">=2,<4" 869 | idna = ">=2.5,<4" 870 | urllib3 = ">=1.21.1,<3" 871 | 872 | [package.extras] 873 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 874 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 875 | 876 | [[package]] 877 | name = "requests-toolbelt" 878 | version = "1.0.0" 879 | description = "A utility belt for advanced users of python-requests" 880 | optional = false 881 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 882 | groups = ["dev"] 883 | files = [ 884 | {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, 885 | {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, 886 | ] 887 | 888 | [package.dependencies] 889 | requests = ">=2.0.1,<3.0.0" 890 | 891 | [[package]] 892 | name = "rfc3986" 893 | version = "2.0.0" 894 | description = "Validating URI References per RFC 3986" 895 | optional = false 896 | python-versions = ">=3.7" 897 | groups = ["dev"] 898 | files = [ 899 | {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, 900 | {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, 901 | ] 902 | 903 | [package.extras] 904 | idna2008 = ["idna"] 905 | 906 | [[package]] 907 | name = "rich" 908 | version = "14.0.0" 909 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 910 | optional = false 911 | python-versions = ">=3.8.0" 912 | groups = ["dev"] 913 | files = [ 914 | {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, 915 | {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, 916 | ] 917 | 918 | [package.dependencies] 919 | markdown-it-py = ">=2.2.0" 920 | pygments = ">=2.13.0,<3.0.0" 921 | typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} 922 | 923 | [package.extras] 924 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 925 | 926 | [[package]] 927 | name = "secretstorage" 928 | version = "3.3.3" 929 | description = "Python bindings to FreeDesktop.org Secret Service API" 930 | optional = false 931 | python-versions = ">=3.6" 932 | groups = ["dev"] 933 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and sys_platform == \"linux\"" 934 | files = [ 935 | {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, 936 | {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, 937 | ] 938 | 939 | [package.dependencies] 940 | cryptography = ">=2.0" 941 | jeepney = ">=0.6" 942 | 943 | [[package]] 944 | name = "tomli" 945 | version = "2.2.1" 946 | description = "A lil' TOML parser" 947 | optional = false 948 | python-versions = ">=3.8" 949 | groups = ["dev"] 950 | markers = "python_version < \"3.11\"" 951 | files = [ 952 | {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, 953 | {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, 954 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, 955 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, 956 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, 957 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, 958 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, 959 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, 960 | {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, 961 | {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, 962 | {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, 963 | {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, 964 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, 965 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, 966 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, 967 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, 968 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, 969 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, 970 | {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, 971 | {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, 972 | {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, 973 | {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, 974 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, 975 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, 976 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, 977 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, 978 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, 979 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, 980 | {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, 981 | {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, 982 | {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, 983 | {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, 984 | ] 985 | 986 | [[package]] 987 | name = "twine" 988 | version = "6.1.0" 989 | description = "Collection of utilities for publishing packages on PyPI" 990 | optional = false 991 | python-versions = ">=3.8" 992 | groups = ["dev"] 993 | files = [ 994 | {file = "twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"}, 995 | {file = "twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"}, 996 | ] 997 | 998 | [package.dependencies] 999 | id = "*" 1000 | importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} 1001 | keyring = {version = ">=15.1", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} 1002 | packaging = ">=24.0" 1003 | readme-renderer = ">=35.0" 1004 | requests = ">=2.20" 1005 | requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" 1006 | rfc3986 = ">=1.4.0" 1007 | rich = ">=12.0.0" 1008 | urllib3 = ">=1.26.0" 1009 | 1010 | [package.extras] 1011 | keyring = ["keyring (>=15.1)"] 1012 | 1013 | [[package]] 1014 | name = "typing-extensions" 1015 | version = "4.13.2" 1016 | description = "Backported and Experimental Type Hints for Python 3.8+" 1017 | optional = false 1018 | python-versions = ">=3.8" 1019 | groups = ["dev"] 1020 | markers = "python_version < \"3.11\"" 1021 | files = [ 1022 | {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, 1023 | {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "urllib3" 1028 | version = "2.4.0" 1029 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1030 | optional = false 1031 | python-versions = ">=3.9" 1032 | groups = ["main", "dev"] 1033 | files = [ 1034 | {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, 1035 | {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, 1036 | ] 1037 | 1038 | [package.extras] 1039 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] 1040 | h2 = ["h2 (>=4,<5)"] 1041 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1042 | zstd = ["zstandard (>=0.18.0)"] 1043 | 1044 | [[package]] 1045 | name = "virtualenv" 1046 | version = "20.30.0" 1047 | description = "Virtual Python Environment builder" 1048 | optional = false 1049 | python-versions = ">=3.8" 1050 | groups = ["dev"] 1051 | files = [ 1052 | {file = "virtualenv-20.30.0-py3-none-any.whl", hash = "sha256:e34302959180fca3af42d1800df014b35019490b119eba981af27f2fa486e5d6"}, 1053 | {file = "virtualenv-20.30.0.tar.gz", hash = "sha256:800863162bcaa5450a6e4d721049730e7f2dae07720e0902b0e4040bd6f9ada8"}, 1054 | ] 1055 | 1056 | [package.dependencies] 1057 | distlib = ">=0.3.7,<1" 1058 | filelock = ">=3.12.2,<4" 1059 | platformdirs = ">=3.9.1,<5" 1060 | 1061 | [package.extras] 1062 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] 1063 | test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""] 1064 | 1065 | [[package]] 1066 | name = "zipp" 1067 | version = "3.21.0" 1068 | description = "Backport of pathlib-compatible object wrapper for zip files" 1069 | optional = false 1070 | python-versions = ">=3.9" 1071 | groups = ["dev"] 1072 | markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\" and python_version < \"3.12\" or python_version == \"3.9\"" 1073 | files = [ 1074 | {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, 1075 | {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, 1076 | ] 1077 | 1078 | [package.extras] 1079 | check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] 1080 | cover = ["pytest-cov"] 1081 | doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] 1082 | enabler = ["pytest-enabler (>=2.2)"] 1083 | test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] 1084 | type = ["pytest-mypy"] 1085 | 1086 | [metadata] 1087 | lock-version = "2.1" 1088 | python-versions = "^3.9" 1089 | content-hash = "b97637136dd0595cddbbb6cfaaa4a7a099a6cf0bb963f54cca7d08bd38d4dc58" 1090 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "mailersend" 3 | version = "0.6.0" 4 | description = "The official MailerSend Python SDK" 5 | authors = ["Igor Hrček "] 6 | readme = "README.md" 7 | keywords = ["mailersend", "mail"] 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.9" 11 | requests = "^2.25.0" 12 | docutils = "^0.21.2" 13 | 14 | [tool.poetry.group.dev.dependencies] 15 | pytest = "^8.0.0" 16 | twine = "^6.0.0" 17 | pre-commit = "^2.12.1" 18 | 19 | [build-system] 20 | requires = ["poetry>=0.12"] 21 | build-backend = "poetry.masonry.api" 22 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mailersend/mailersend-python/61d8ff3d3993acd38726e00a860218e49378c7e4/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_mailersend.py: -------------------------------------------------------------------------------- 1 | from mailersend import __version__ 2 | 3 | 4 | def test_version(): 5 | assert __version__ == "0.1.0" 6 | --------------------------------------------------------------------------------