diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a59eb527..96cfa777a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,25 +19,37 @@ on: jobs: lint: - name: Lint & Mypy + name: Static Checks runs-on: ubuntu-latest steps: + - uses: extractions/setup-just@v2 - uses: actions/checkout@v3 - name: Set up Python 3 uses: actions/setup-python@v4 with: python-version: "3.10" - - name: mypy - run: make mypy - - name: lint - run: make lint - - name: fmtcheck - run: make fmtcheck + - name: check examples w/ mypy (against python@3.10) + run: just typecheck-examples + # skip deps on all these since mypy installed everything + - name: check linting + run: just --no-deps lint + - name: check formatting + run: just --no-deps format-check + # pyright depends on node, which it handles and installs for itself as needed + # we _could_ run setup-node to make it available for it if we're having reliability problems + - name: check types (all Python versions) + run: | + set -eox + + for minor in {6..12}; do + just --no-deps typecheck $minor + done build: name: Build runs-on: ubuntu-latest steps: + - uses: extractions/setup-just@v2 - uses: actions/checkout@v3 - name: Set up Python 3 @@ -45,15 +57,9 @@ jobs: with: python-version: "3.10" - - name: Install tools - run: make venv - - name: Build and check package run: | - set -x - source venv/bin/activate - python setup.py clean --all sdist bdist_wheel --universal - python -m twine check dist/* + just build - name: "Upload Artifact" uses: actions/upload-artifact@v3 @@ -69,45 +75,30 @@ jobs: strategy: fail-fast: false matrix: - python: - - { version: "3.6" , env: "py36" } - - { version: "3.7" , env: "py37" } - - { version: "3.8" , env: "py38" } - - { version: "3.9" , env: "py39" } - - { version: "3.10" , env: "py310" } - - { version: "3.11" , env: "py311" } - - { version: "3.12" , env: "py312" } - - { version: "pypy-3.7" , env: "py3.7" } - - { version: "pypy-3.8" , env: "py3.8" } - - { version: "pypy-3.9" , env: "py3.9" } - - { version: "pypy-3.10" , env: "py3.10" } - name: Test (${{ matrix.python.version }}) + python_version: + - "3.6" + - "3.7" + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "pypy-3.7" + - "pypy-3.8" + - "pypy-3.9" + - "pypy-3.10" + name: Test (${{ matrix.python_version }}) steps: + - uses: extractions/setup-just@v2 - uses: actions/checkout@v3 - - - name: Set up Python ${{ matrix.python.version }} and 3.10 + - name: Set up Python ${{ matrix.python_version }} uses: actions/setup-python@v4 with: - python-version: | - ${{ matrix.python.version }} - 3.10 - + python-version: ${{ matrix.python_version }} - uses: stripe/openapi/actions/stripe-mock@master - - name: Typecheck with pyright - run: PYRIGHT_ARGS="-- --pythonversion ${{ matrix.python.version }}" make pyright - # Skip typecheking in pypy legs - if: ${{ !contains(matrix.python.version, 'pypy') }} - - - name: Test with pytest - run: TOX_ARGS="-e ${{ matrix.python.env }}" make ci-test - - - name: Calculate and publish coverage - run: make coveralls - if: env.COVERALLS_REPO_TOKEN && matrix.python.version == '3.10' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + - name: "run tests" + run: just test publish: name: Publish @@ -137,11 +128,12 @@ jobs: GPG_SIGNING_PRIVKEY: ${{ secrets.GPG_SIGNING_PRIVKEY }} GPG_SIGNING_PASSPHRASE: ${{ secrets.GPG_SIGNING_PASSPHRASE }} - name: Install tools - run: make venv - - name: Publish packages to PyPy + run: just install-build-deps + - name: Publish packages to PyPI + # could probably move this into a just recipe too? run: | set -ex - source venv/bin/activate + source .venv/bin/activate export VERSION=$(cat VERSION) gpg --detach-sign --local-user $GPG_SIGNING_KEYID --pinentry-mode loopback --passphrase $GPG_SIGNING_PASSPHRASE -a dist/stripe-$VERSION.tar.gz gpg --detach-sign --local-user $GPG_SIGNING_KEYID --pinentry-mode loopback --passphrase $GPG_SIGNING_PASSPHRASE -a dist/stripe-$VERSION-py2.py3-none-any.whl diff --git a/MANIFEST.in b/MANIFEST.in index ba34b83e6..ff12f246b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ -include .coveragerc .flake8 CHANGELOG.md LICENSE LONG_DESCRIPTION.rst README.md VERSION pytest.ini tox.ini +# this file specifies what's included in a source distribution (https://packaging.python.org/en/latest/glossary/#term-Source-Distribution-or-sdist) +# see also: https://setuptools.pypa.io/en/latest/userguide/miscellaneous.html +include .flake8 CHANGELOG.md LICENSE LONG_DESCRIPTION.rst README.md VERSION pytest.ini justfile recursive-include tests *.py recursive-include examples *.txt *.py diff --git a/Makefile b/Makefile deleted file mode 100644 index a85caa62a..000000000 --- a/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -VENV_NAME?=venv -PIP?=pip -PYTHON?=python3.10 -DEFAULT_TEST_ENV?=py310 - -venv: $(VENV_NAME)/bin/activate - -$(VENV_NAME)/bin/activate: setup.py requirements.txt - @test -d $(VENV_NAME) || $(PYTHON) -m venv --clear $(VENV_NAME) - ${VENV_NAME}/bin/python -m pip install -r requirements.txt - @touch $(VENV_NAME)/bin/activate - -test: venv pyright lint mypy - @${VENV_NAME}/bin/tox -p auto -e $(DEFAULT_TEST_ENV) $(TOX_ARGS) - -test-nomock: venv - @${VENV_NAME}/bin/tox -p auto -- --nomock $(TOX_ARGS) - -ci-test: venv - @${VENV_NAME}/bin/tox $(TOX_ARGS) - -coveralls: venv - @${VENV_NAME}/bin/tox -e coveralls - -pyright: venv - @${VENV_NAME}/bin/tox -e pyright $(PYRIGHT_ARGS) - -mypy: venv - @${VENV_NAME}/bin/tox -e mypy $(MYPY_ARGS) - -fmt: venv - @${VENV_NAME}/bin/tox -e fmt - -fmtcheck: venv - @${VENV_NAME}/bin/tox -e fmt -- --check --verbose - -lint: venv - @${VENV_NAME}/bin/tox -e lint - -clean: - @rm -rf $(VENV_NAME) .coverage .coverage.* build/ dist/ htmlcov/ - -update-version: - @echo "$(VERSION)" > VERSION - @perl -pi -e 's|VERSION = "[.\d\w]+"|VERSION = "$(VERSION)"|' stripe/_version.py - -codegen-format: fmt - -.PHONY: ci-test clean codegen-format coveralls fmt fmtcheck lint test test-nomock test-travis update-version venv pyright mypy diff --git a/README.md b/README.md index 62f399fd0..35cfa9912 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![pypi](https://img.shields.io/pypi/v/stripe.svg)](https://pypi.python.org/pypi/stripe) [![Build Status](https://github.com/stripe/stripe-python/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/stripe/stripe-python/actions?query=branch%3Amaster) -[![Coverage Status](https://coveralls.io/repos/github/stripe/stripe-python/badge.svg?branch=master)](https://coveralls.io/github/stripe/stripe-python?branch=master) The Stripe Python library provides convenient access to the Stripe API from applications written in the Python language. It includes a pre-defined set of @@ -341,46 +340,48 @@ go install github.com/stripe/stripe-mock@latest stripe-mock ``` -Run the following command to set up the development virtualenv: +We use [just](https://github.com/casey/just) for conveniently running development tasks. You can use them directly, or copy the commands out of the `justfile`. To our help docs, run `just`. By default, all commands will use an virtualenv created by your default python version (whatever comes out of `python --version`). We recommend using [mise](https://mise.jdx.dev/lang/python.html) or [pyenv](https://github.com/pyenv/pyenv) to control that version. -```sh -make -``` - -Run all tests on all supported Python versions: +Run the following command to set up the development virtualenv: ```sh -make test +just venv +# or: python -m venv venv && venv/bin/python -I -m pip install -e . ``` -Run all tests for a specific Python version (modify `-e` according to your Python target): +Run all tests: ```sh -TOX_ARGS="-e py37" make test +just test +# or: venv/bin/pytest ``` Run all tests in a single file: ```sh -TOX_ARGS="-e py37 -- tests/api_resources/abstract/test_updateable_api_resource.py" make test +just test tests/api_resources/abstract/test_updateable_api_resource.py +# or: venv/bin/pytest tests/api_resources/abstract/test_updateable_api_resource.py ``` Run a single test suite: ```sh -TOX_ARGS="-e py37 -- tests/api_resources/abstract/test_updateable_api_resource.py::TestUpdateableAPIResource" make test +just test tests/api_resources/abstract/test_updateable_api_resource.py::TestUpdateableAPIResource +# or: venv/bin/pytest tests/api_resources/abstract/test_updateable_api_resource.py::TestUpdateableAPIResource ``` Run a single test: ```sh -TOX_ARGS="-e py37 -- tests/api_resources/abstract/test_updateable_api_resource.py::TestUpdateableAPIResource::test_save" make test +just test tests/api_resources/abstract/test_updateable_api_resource.py::TestUpdateableAPIResource::test_save +# or: venv/bin/pytest tests/api_resources/abstract/test_updateable_api_resource.py::TestUpdateableAPIResource::test_save ``` Run the linter with: ```sh -make lint +just lint +# or: venv/bin/python -m flake8 --show-source stripe tests setup.py ``` The library uses [Ruff][ruff] for code formatting. Code must be formatted @@ -388,7 +389,8 @@ with Black before PRs are submitted, otherwise CI will fail. Run the formatter with: ```sh -make fmt +just format +# or: venv/bin/ruff format . --quiet ``` [api-keys]: https://dashboard.stripe.com/account/apikeys diff --git a/deps/build-requirements.txt b/deps/build-requirements.txt new file mode 100644 index 000000000..ae45d54c1 --- /dev/null +++ b/deps/build-requirements.txt @@ -0,0 +1,4 @@ +# packages needed to package & release + +twine +setuptools diff --git a/deps/dev-requirements.txt b/deps/dev-requirements.txt new file mode 100644 index 000000000..cedd76e2b --- /dev/null +++ b/deps/dev-requirements.txt @@ -0,0 +1,11 @@ +# packages needed to run static analysis (lints, types, etc) +# version requirements: any modern python version (currently 3.10) + +# typechecking for all versions +pyright == 1.1.336 +# general typechecking +mypy == 1.7.0 +# formatting +ruff == 0.4.4 +# linting +flake8 diff --git a/test-requirements.txt b/deps/test-requirements.txt similarity index 72% rename from test-requirements.txt rename to deps/test-requirements.txt index f2e9a43ba..a04785b92 100644 --- a/test-requirements.txt +++ b/deps/test-requirements.txt @@ -1,4 +1,5 @@ -# These requirements must be installable on all our supported versions +# packages needed to run unit tests (including extra supported http libraries) +# version requirements: all supported versions (currently 3.6-3.12) # This is the last version of httpx compatible with Python 3.6 httpx == 0.22.0; python_version == "3.6" @@ -8,10 +9,7 @@ aiohttp == 3.8.6; python_version <= "3.7" aiohttp == 3.9.4; python_version > "3.7" anyio[trio] == 3.6.2 -pytest-cov >= 2.8.1, < 2.11.0 pytest-mock >= 2.0.0 mock >= 4.0; python_version < "3.8" pytest-xdist >= 1.31.0 pytest >= 6.0.0 -coverage >= 4.5.3, < 5 -coveralls diff --git a/justfile b/justfile new file mode 100644 index 000000000..4e8c0fec8 --- /dev/null +++ b/justfile @@ -0,0 +1,81 @@ +set quiet + +import? '../sdk-codegen/justfile' + +VENV_NAME := ".venv" + +export PATH := `pwd` / VENV_NAME / "bin:" + env('PATH') + +_default: + just --list --unsorted + +# ⭐ run all unit tests +test *args: install-test-deps + # configured in pyproject.toml + pytest {{ args }} + +# ⭐ check for potential mistakes +lint: install-dev-deps + python -m flake8 --show-source stripe tests setup.py + +# verify types. optional argument to test as of a specific minor python version (e.g. `8` to test `python 3.8`); otherwise uses current version +typecheck minor_py_version="": install-test-deps install-dev-deps + # suppress version update warnings + PYRIGHT_PYTHON_IGNORE_WARNINGS=1 pyright {{ if minor_py_version == "" { "" } else { "--pythonversion 3." + minor_py_version } }} + +# ⭐ format all code +format: install-dev-deps + ruff format . --quiet + +# verify formatting, but don't modify files +format-check: install-dev-deps + ruff format . --check --quiet + +# remove venv +clean: + # clear old files too + rm -rf {{ VENV_NAME }} venv .tox + +# blow away and reinstall virtual env +reset: clean && venv + +# build the package for upload +build: install-build-deps + # --universal is deprecated, so we'll probably need to look at this eventually + # given that we don't care about universal 2 and 3 packages, we probably don't need it? + python -I setup.py clean --all sdist bdist_wheel --universal + python -m twine check dist/* + +# typecheck some examples w/ mypy +typecheck-examples: _install-all + # configured in pyproject.toml + mypy + +# install the tools for local development & static checks +install-dev-deps: (install "dev") + +# install everything for unit tests +install-test-deps: (install "test") + +# install dependencies to build the package +install-build-deps: (install "build") + +_install-all: install-dev-deps install-test-deps install-build-deps + +# installs files out of a {group}-requirements.txt into the local venv; mostly used by other recipes +install group: venv + python -I -m pip install -r deps/{{ group }}-requirements.txt --disable-pip-version-check {{ if is_dependency() == "true" {"--quiet"} else {""} }} + +# create a virtualenv if it doesn't exist; always installs the local package +[private] +venv: + [ -d {{ VENV_NAME }} ] || ( \ + python -m venv {{ VENV_NAME }} && \ + {{ VENV_NAME }}/bin/python -I -m pip install -e . --quiet --disable-pip-version-check \ + ) + +# called by tooling +[private] +update-version version: + @echo "{{ version }}" > VERSION + @perl -pi -e 's|VERSION = "[.\d\w]+"|VERSION = "{{ version }}"|' stripe/_version.py diff --git a/pyproject.toml b/pyproject.toml index 7ed6ba63c..dae143954 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,4 +33,15 @@ warn_unused_ignores = true no_implicit_reexport = true [tool.pytest.ini_options] -filterwarnings = "ignore::DeprecationWarning" +# use as many threads as available for tests +addopts = "-n auto" +# already the default, but will speed up searching a little, since this is the only place tests live +testpaths = "tests" +# these are warnings we show to users; we don't need them in our test logs +filterwarnings = [ + # single quotes so we don't have to re-backslack our backslashes + 'ignore:[\S\s]*stripe-python:DeprecationWarning', + 'ignore:[\S\s]*stripe\..* package:DeprecationWarning', + 'ignore:[\S\s]*RecipientTransfer:DeprecationWarning', + 'ignore:[\S\s]*`save` method is deprecated:DeprecationWarning', +] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index cfe397d29..000000000 --- a/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -# These requirements must be installable only on py3.10 + -twine -# 4.5.0 is the last version that works with virtualenv<20.22.0 -tox == 4.5.0 -#Virtualenv 20.22.0 dropped support for all Python versions smaller or equal to Python 3.6. -virtualenv<20.22.0 -pyright == 1.1.336 -ruff == 0.4.4 -flake8 -mypy == 1.7.0 - --r test-requirements.txt diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 23ee4816e..000000000 --- a/tox.ini +++ /dev/null @@ -1,56 +0,0 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, "pip install tox" -# and then run "tox" from this directory. - -[tox] -envlist = - fmt - lint - pyright - mypy - py{312,311,310,39,38,37,36,py3} -ignore_base_python_conflict = false - -[tool:pytest] -testpaths = tests -addopts = - --cov-report=term-missing - -[testenv] -description = run the unit tests under {basepython} -setenv = - COVERAGE_FILE = {toxworkdir}/.coverage.{envname} -deps = - -r test-requirements.txt - -# ignore stripe directory as all tests are inside ./tests -commands = pytest --cov {posargs:-n auto} --ignore stripe -# compilation flags can be useful when prebuilt wheels cannot be used, e.g. -# PyPy 2 needs to compile the `cryptography` module. On macOS this can be done -# by passing the following flags: -# LDFLAGS="-L$(brew --prefix openssl@1.1)/lib" -# CFLAGS="-I$(brew --prefix openssl@1.1)/include" -passenv = LDFLAGS,CFLAGS - -[testenv:{lint,fmt,pyright,mypy}] -basepython = python3.10 -skip_install = true -commands = - pyright: pyright {posargs} - lint: python -m flake8 --show-source stripe tests setup.py - fmt: ruff format . {posargs} - mypy: mypy {posargs} -deps = - -r requirements.txt - -[testenv:coveralls] -description = upload coverage to coveralls.io -skip_install = true -setenv = - COVERAGE_FILE = {toxworkdir}/.coverage -passenv = GITHUB_* -commands = - coverage combine - coveralls --service=github -depends = py{312,311,310,39,38,37,36,py3}