diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 90b657b..f5355bf 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -33,7 +33,6 @@ body: label: Python Version description: What Python version are you using? options: - - '3.9' - '3.10' - '3.11' - '3.12' diff --git a/.github/workflows/commit_checks.yaml b/.github/workflows/commit_checks.yaml index 10a1a83..a1e5609 100644 --- a/.github/workflows/commit_checks.yaml +++ b/.github/workflows/commit_checks.yaml @@ -7,15 +7,18 @@ on: - main pull_request: +permissions: + contents: read + jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.12' # Specify a Python version explicitly - - uses: pre-commit/action@v3.0.1 + - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 test: name: test py${{ matrix.python-version }} on ${{ matrix.os }} @@ -27,15 +30,15 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "macos-latest", "windows-latest"] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13"] env: MJ_APIKEY_PUBLIC: ${{ secrets.MJ_APIKEY_PUBLIC }} MJ_APIKEY_PRIVATE: ${{ secrets.MJ_APIKEY_PRIVATE }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 # Get full history with tags (required for setuptools-scm) - - uses: conda-incubator/setup-miniconda@v3 + - uses: conda-incubator/setup-miniconda@835234971496cad1653abb28a638a281cf32541f # v3.2.0 with: python-version: ${{ matrix.python-version }} channels: defaults diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml index e500793..d2f0460 100644 --- a/.github/workflows/issue-triage.yml +++ b/.github/workflows/issue-triage.yml @@ -4,6 +4,9 @@ on: issues: types: [opened, labeled, unlabeled, reopened] +permissions: + contents: read + jobs: triage: runs-on: ubuntu-latest @@ -11,7 +14,7 @@ jobs: issues: write steps: - name: Initial triage - uses: actions/github-script@v6 + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/pr_validation.yml b/.github/workflows/pr_validation.yml index a699c1f..543a2ea 100644 --- a/.github/workflows/pr_validation.yml +++ b/.github/workflows/pr_validation.yml @@ -4,22 +4,25 @@ on: pull_request: branches: [main] +permissions: + contents: read + jobs: validate: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.12' - name: Build package run: | - pip install --upgrade build setuptools wheel setuptools-scm + pip install --upgrade build setuptools setuptools-scm python -m build - name: Test installation diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 14b1b1c..903a1f4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,6 +7,9 @@ on: types: [published] # Triggers when a GitHub release is published workflow_dispatch: # Manual trigger +permissions: + contents: read + jobs: publish: runs-on: ubuntu-latest @@ -15,17 +18,17 @@ jobs: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.12' - name: Install build tools - run: pip install --upgrade build setuptools wheel setuptools-scm twine + run: pip install --upgrade build setuptools setuptools-scm twine - name: Extract version id: get_version diff --git a/.gitignore b/.gitignore index c2134aa..074ef42 100644 --- a/.gitignore +++ b/.gitignore @@ -234,3 +234,5 @@ dev/ # pytest cache .pytest_cache/ pytestdebug.log + +*/_version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fc71b5d..b436de4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,8 @@ # pre-commit run --all-files # Update this file: # pre-commit autoupdate +default_language_version: + python: python3 exclude: ^(.*/versioneer\.py|.*/_version\.py|.*/.*\.svg) ci: @@ -111,7 +113,7 @@ repos: rev: v3.20.0 hooks: - id: pyupgrade - args: [--py39-plus, --keep-runtime-typing] + args: [--py310-plus, --keep-runtime-typing] - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. @@ -123,6 +125,13 @@ repos: # Run the formatter. - id: ruff-format + - repo: https://github.com/pycqa/pydocstyle + rev: 6.3.0 + hooks: + - id: pydocstyle + args: [--select=D200,D213,D400,D415] + additional_dependencies: [tomli] + - repo: https://github.com/dosisod/refurb rev: v2.1.0 hooks: @@ -133,10 +142,9 @@ repos: rev: v1.16.1 hooks: - id: mypy - args: - [ - --config-file=./pyproject.toml, - ] + args: [--config-file=./pyproject.toml] + additional_dependencies: + - types-requests exclude: ^samples/ - repo: https://github.com/RobertCraigie/pyright-python diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f9468a..2581d13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,39 @@ We [keep a changelog.](http://keepachangelog.com/) ## [Unreleased] +## [1.5.0] - 2025-07-11 + +### Added + +- Add class `TestCsvImpor` with a test suite for testing CSV import functionality to `test.py` +- Add `types-requests` to `mypy`'s `additional_dependencies` in `pre-commit` hooks +- Add `pydocstyle` pre-commit's hook +- Add `*/_version.py` to `.gitignore` + +### Fixed + +- Fix a csvimport error 'List index (0) out of bounds': renamed `json_data` back to `data`. Corrected behavior broken since v1.4.0 + +### Changed + +- Update pre-commit hooks to the latest versions +- Breaking changes: drop support for Python 3.9 +- Import Callable from collections.abc +- Improve a conda recipe +- Update `README.md` + +### Security + +- Add the Security Policy file `SECURITY.md` +- Use `permissions: contents: read` in all CI workflow files explicitly +- Use commit hashes to ensure reproducible builds +- Update pinning for runtime dependency `requests >=2.32.4` + +### Pull Requests Merged + +- [PR_120](https://github.com/mailjet/mailjet-apiv3-python/pull/120) - Fix a csvimport error 'List index (0) out of bounds' +- [PR_123](https://github.com/mailjet/mailjet-apiv3-python/pull/123) - Release 1.5.0 + ## [1.4.0] - 2025-05-07 ### Added @@ -141,4 +174,5 @@ We [keep a changelog.](http://keepachangelog.com/) \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* [1.4.0]: https://github.com/mailjet/mailjet-apiv3-python/releases/tag/v1.4.0 -[unreleased]: https://github.com/mailjet/mailjet-apiv3-python/releases/tag/v1.4.0...HEAD +[1.5.0]: https://github.com/mailjet/mailjet-apiv3-python/releases/tag/v1.5.0 +[unreleased]: https://github.com/mailjet/mailjet-apiv3-python/releases/tag/v1.5.0...HEAD diff --git a/README.md b/README.md index 255213b..8fd1a30 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Check out all the resources and Python code examples in the official [Mailjet Do This library `mailjet_rest` officially supports the following Python versions: -- Python >=3.9,\<3.14 +- Python >=3.10,\<3.14 It's tested up to 3.13 (including). @@ -70,7 +70,7 @@ To build the `mailjet_rest` package from the sources you need `setuptools` (as a ### Runtime dependencies -At runtime the package requires only `requests >=2.32.3`. +At runtime the package requires only `requests >=2.32.4`. ### Test dependencies @@ -443,5 +443,5 @@ If you have suggestions on how to improve the guides, please submit an issue in - [@skupriienko](https://github.com/skupriienko) [api_credential]: https://app.mailjet.com/account/apikeys -[doc]: http://dev.mailjet.com/guides/?python# -[mailjet]: (http://www.mailjet.com/) +[doc]: https://dev.mailjet.com/email/guides/?python# +[mailjet]: (https://www.mailjet.com) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d15ee9f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,82 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 1.4.x | :white_check_mark: | +| < 1.4.0 | :x: | + +# Vulnerability Disclosure + +If you think you have found a potential security vulnerability in +mailjet-rest, please open a [draft Security Advisory](https://github.com/mailjet/mailjet-apiv3-python/security/advisories/new) +via GitHub. We will coordinate verification and next steps through +that secure medium. + +If English is not your first language, please try to describe the +problem and its impact to the best of your ability. For greater detail, +please use your native language and we will try our best to translate it +using online services. + +Please also include the code you used to find the problem and the +shortest amount of code necessary to reproduce it. + +Please do not disclose this to anyone else. We will retrieve a CVE +identifier if necessary and give you full credit under whatever name or +alias you provide. We will only request an identifier when we have a fix +and can publish it in a release. + +We will respect your privacy and will only publicize your involvement if +you grant us permission. + +## Process + +This following information discusses the process the project +follows in response to vulnerability disclosures. If you are disclosing +a vulnerability, this section of the documentation lets you know how we +will respond to your disclosure. + +### Timeline + +When you report an issue, one of the project members will respond to you +within five days *at the outside*. In most cases responses will be +faster, usually within 72 hours. This initial response will at the very +least confirm receipt of the report. + +If we were able to rapidly reproduce the issue, the initial response +will also contain confirmation of the issue. If we are not, we will +often ask for more information about the reproduction scenario. + +Our goal is to have a fix for any vulnerability released within two +weeks of the initial disclosure. This may potentially involve shipping +an interim release that simply disables function while a more mature fix +can be prepared, but will in the vast majority of cases mean shipping a +complete release as soon as possible. + +Throughout the fix process we will keep you up to speed with how the fix +is progressing. Once the fix is prepared, we will notify you that we +believe we have a fix. Often we will ask you to confirm the fix resolves +the problem in your environment, especially if we are not confident of +our reproduction scenario. + +At this point, we will prepare for the release. We will obtain a CVE +number if one is required, providing you with full credit for the +discovery. We will also decide on a planned release date, and let you +know when it is. This release date will *always* be on a weekday. + +At this point we will reach out to our major downstream packagers to +notify them of an impending security-related patch so they can make +arrangements. In addition, these packagers will be provided with the +intended patch ahead of time, to ensure that they are able to promptly +release their downstream packages. + +On release day, we will push the patch to our public repository, along +with an updated changelog that describes the issue and credits you. We +will then issue a PyPI release containing the patch. + +At this point, we will publicise the release. + +We will also explicitly mention which commits contain the fix to make it +easier for other distributors and users to easily patch their own +versions of mailjet-rest if upgrading is not an option. diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index a160382..677b1f4 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -16,7 +16,7 @@ source: build: number: 0 - skip: True # [py<39] + skip: True # [py<310] script: {{ PYTHON }} -m pip install . --no-deps --no-build-isolation -vv script_env: - SETUPTOOLS_SCM_PRETEND_VERSION={{ version }} @@ -41,6 +41,8 @@ test: - samples source_files: - tests/test_client.py + - test.py + - tests/doc_tests/files/data.csv requires: - pip - pytest @@ -48,15 +50,14 @@ test: - pip check # TODO: Add environment variables for tests - pytest tests/test_client.py -vv + - pytest test.py -vv about: home: {{ project['urls']['Homepage'] }} dev_url: {{ project['urls']['Repository'] }} doc_url: {{ project['urls']['Documentation'] }} summary: {{ project['description'] }} - # TODO: Add the description - # description: | - # - license: {{ project['license']['text'] }} - license_family: {{ project['license']['text'].split('-')[0] }} + description: {{ project['description'] }} + license: {{ project['license'] }} + license_family: {{ project['license'].split('-')[0] }} license_file: LICENSE diff --git a/environment-dev.yaml b/environment-dev.yaml index cb42993..6644524 100644 --- a/environment-dev.yaml +++ b/environment-dev.yaml @@ -3,14 +3,14 @@ name: mailjet-dev channels: - defaults dependencies: - - python >=3.9 + - python >=3.10 # build & host deps - pip - setuptools-scm - # PyPI publishing only - python-build # runtime deps - - requests >=2.32.3 + - requests >=2.32.4 # tests - conda-forge::pyfakefs - coverage >=4.5.4 @@ -52,8 +52,7 @@ dependencies: - pyment >=0.3.3 - pytype - pyupgrade - # refurb doesn't support py39 - #- refurb + - refurb - scalene >=1.3.16 - snakeviz - typos diff --git a/environment.yaml b/environment.yaml index 4303c29..051b9e9 100644 --- a/environment.yaml +++ b/environment.yaml @@ -3,11 +3,11 @@ name: mailjet channels: - defaults dependencies: - - python >=3.9 + - python >=3.10 # build & host deps - pip # runtime deps - - requests >=2.32.3 + - requests >=2.32.4 # tests - pytest >=7.0.0 # other diff --git a/mailjet_rest/_version.py b/mailjet_rest/_version.py deleted file mode 100644 index 3e8d9f9..0000000 --- a/mailjet_rest/_version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "1.4.0" diff --git a/mailjet_rest/client.py b/mailjet_rest/client.py index a41ae97..e0b7531 100644 --- a/mailjet_rest/client.py +++ b/mailjet_rest/client.py @@ -37,7 +37,6 @@ from re import Match from typing import TYPE_CHECKING from typing import Any -from typing import Callable import requests # type: ignore[import-untyped] from requests.compat import urljoin # type: ignore[import-untyped] @@ -46,12 +45,13 @@ if TYPE_CHECKING: + from collections.abc import Callable from collections.abc import Mapping from requests.models import Response # type: ignore[import-untyped] -requests.packages.urllib3.disable_warnings() +requests.packages.urllib3.disable_warnings() # type: ignore[attr-defined] def prepare_url(key: Match[str]) -> str: diff --git a/pyproject.toml b/pyproject.toml index 6917321..b7f1f01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=61.0", "wheel", "setuptools-scm"] +requires = ["setuptools>=77.0.0", "setuptools-scm"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] @@ -15,7 +15,7 @@ write_to_template = '__version__ = "{version}"' py-modules = ["mailjet_rest._version"] [tool.setuptools.packages.find] -include = ["mailjet_rest", "mailjet_rest.*", "samples", "tests", "test.py"] +include = ["mailjet_rest", "mailjet_rest.*", "samples", "tests", "tests.*", "test.py"] [tool.setuptools.package-data] mailjet_rest = ["py.typed", "*.pyi"] @@ -31,13 +31,12 @@ authors = [ maintainers = [ {name = "Serhii Kupriienko", email = "kupriienko.serhii@gmail.com"} ] -license = {text = "MIT"} -# TODO: Enable license-files when setuptools >=77.0.0 will be available -#license-files = ["LICENSE"] +license = "MIT" +license-files = ["LICENSE"] readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" -dependencies = ["requests>=2.32.3"] +dependencies = ["requests>=2.32.4"] keywords = [ "Mailjet API v3 / v3.1 Python Wrapper", @@ -54,7 +53,6 @@ classifiers = [ "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -66,10 +64,10 @@ classifiers = [ ] [project.urls] -"Issue Tracker" = "https://github.com/mailjet/mailjet-apiv3-python" -"Repository" = "https://github.com/mailjet/mailjet-apiv3-python" "Homepage" = "https://dev.mailjet.com" "Documentation" = "https://dev.mailjet.com" +"Repository" = "https://github.com/mailjet/mailjet-apiv3-python" +"Issue Tracker" = "https://github.com/mailjet/mailjet-apiv3-python/issues" [project.optional-dependencies] linting = [ @@ -136,7 +134,7 @@ other = ["toml"] [tool.black] line-length = 88 -target-version = ["py39", "py310", "py311", "py312", "py313"] +target-version = ["py310", "py311", "py312", "py313"] skip-string-normalization = false skip-magic-trailing-comma = false extend-exclude = ''' @@ -190,8 +188,8 @@ extend-exclude = ["tests", "test"] line-length = 88 #indent-width = 4 -# Assume Python 3.9. -target-version = "py39" +# Assume Python 3.10. +target-version = "py310" # Enumerate all fixed violations. show-fixes = true @@ -334,7 +332,7 @@ ignore_patterns = [ strict = true # Adapted from this StackOverflow post: # https://stackoverflow.com/questions/55944201/python-type-hinting-how-do-i-enforce-that-project-wide -python_version = "3.9" +python_version = "3.10" mypy_path = "type_stubs" namespace_packages = true # This flag enhances the user feedback for error messages diff --git a/test.py b/test.py index 28f51df..98779ec 100644 --- a/test.py +++ b/test.py @@ -215,7 +215,7 @@ def test_user_agent(self) -> None: None """ self.client = Client(auth=self.auth, version="v3.1") - self.assertEqual(self.client.config.user_agent, "mailjet-apiv3-python/v1.4.0") + self.assertEqual(self.client.config.user_agent, "mailjet-apiv3-python/v1.5.0") class TestCsvImport(unittest.TestCase):