Skip to content

Commit

Permalink
style: fix pre-commit lints
Browse files Browse the repository at this point in the history
This commit fixes pre-commit linting and formatting issues. To avoid
refactoring all of the existing Python code (which will is going to be
deprecated), the pre-commit configuration has been updated to ignore
these Python files.

All other changes in this commit are purely stylistic.

Signed-off-by: JP-Ellis <[email protected]>
  • Loading branch information
JP-Ellis committed Oct 11, 2023
1 parent fbd1fbe commit dab6dba
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 280 deletions.
2 changes: 0 additions & 2 deletions .github/semantic.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
titleAndCommits: true
allowMergeCommits: true


2 changes: 1 addition & 1 deletion .github/workflows/trigger_pact_docs_update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
branches:
- master
paths:
- '**.md'
- "**.md"

jobs:
run:
Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,19 @@ repos:
rev: v0.0.289
hooks:
- id: ruff
# Exclude python files in pact/** and tests/**, except for the
# files in pact/v3/** and tests/v3/**.
exclude: ^(pact|tests)/(?!v3/).*\.py$
args: [--fix, --exit-non-zero-on-fix]
stages: [pre-push]

- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black
# Exclude python files in pact/** and tests/**, except for the
# files in pact/v3/** and tests/v3/**.
exclude: ^(pact|tests)/(?!v3/).*\.py$
stages: [pre-push]

- repo: https://github.com/commitizen-tools/commitizen
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.ubuntu
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ ENV DEBIAN_FRONTEND=noninteractive
ARG PYTHON_VERSION 3.9

#Set of all dependencies needed for pyenv to work on Ubuntu
RUN apt-get update \
RUN apt-get update \
&& apt-get install -y --no-install-recommends make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget ca-certificates curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev mecab-ipadic-utf8 git

# Set-up necessary Env vars for PyEnv
Expand Down
245 changes: 100 additions & 145 deletions README.md

Large diffs are not rendered by default.

70 changes: 32 additions & 38 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,53 @@

## Preparing the release

The easiest way is to just run the following command from the root folder with
the HEAD commit on trunk and the appropriate version. We follow
`<MAJOR>.<MINOR>.<PATCH>` versioning.
The easiest way is to just run the following command from the root folder with the HEAD commit on trunk and the appropriate version. We follow `<MAJOR>.<MINOR>.<PATCH>` versioning.

```shell
$ script/release_prep.sh X.Y.Z
```
```shell
$ script/release_prep.sh X.Y.Z
```

This script effectively runs the following:

1. Increment the version according to semantic versioning rules in `pact/__version__.py`
1. Increment the version according to semantic versioning rules in `pact/__version__.py`

2. Update the `CHANGELOG.md` using:
```shell
$ git log --pretty=format:' * %h - %s (%an, %ad)' vX.Y.Z..HEAD
```
2. Update the `CHANGELOG.md` using:

3. Add files to git
```shell
$ git add CHANGELOG.md pact/__version__.py
```
```shell
$ git log --pretty=format:' * %h - %s (%an, %ad)' vX.Y.Z..HEAD
```

4. Commit
```shell
$ git commit -m "Releasing version X.Y.Z"
```
3. Add files to git

5. Tag
```shell
$ git tag -a vX.Y.Z -m "Releasing version X.Y.Z"
$ git push origin master --tags
```
```shell
$ git add CHANGELOG.md pact/__version__.py
```

4. Commit

```shell
$ git commit -m "Releasing version X.Y.Z"
```

5. Tag

```shell
$ git tag -a vX.Y.Z -m "Releasing version X.Y.Z"
$ git push origin master --tags
```

## Updating Pact Ruby

To upgrade the versions of `pact-mock_service` and `pact-provider-verifier`, change the
`PACT_STANDALONE_VERSION` in `setup.py` to match the latest version available from the
[pact-ruby-standalone](https://github.com/pact-foundation/pact-ruby-standalone/releases)
repository. Do this before preparing the release.
To upgrade the versions of `pact-mock_service` and `pact-provider-verifier`, change the `PACT_STANDALONE_VERSION` in `setup.py` to match the latest version available from the [pact-ruby-standalone](https://github.com/pact-foundation/pact-ruby-standalone/releases) repository. Do this before preparing the release.

## Publishing to pypi

1. Wait until GitHub Actions have run and the new tag is available at
https://github.com/pact-foundation/pact-python/releases/tag/vX.Y.Z
1. Wait until GitHub Actions have run and the new tag is available at https://github.com/pact-foundation/pact-python/releases/tag/vX.Y.Z

2. Set the title to `pact-python-X.Y.Z`
2. Set the title to `pact-python-X.Y.Z`

3. Save
3. Save

4. Go to GitHub Actions for Pact Python and you should see an 'Upload Python
Package' action blocked for your version.
4. Go to GitHub Actions for Pact Python and you should see an 'Upload Python Package' action blocked for your version.

5. Click this and then 'Review deployments'. Select 'Upload Python Package'
and Approve deploy. If you can't do this you may need an administrator to
give you permissions or do it for you. You should see in Slack #pact-python
that the release has happened. Verify in [pypi](https://pypi.org/project/pact-python/)
5. Click this and then 'Review deployments'. Select 'Upload Python Package' and Approve deploy. If you can't do this you may need an administrator to give you permissions or do it for you. You should see in Slack #pact-python that the release has happened. Verify in [pypi](https://pypi.org/project/pact-python/)
24 changes: 7 additions & 17 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
# Introduction

This is for contributors who want to make changes and test for all different
versions of python currently supported. If you don't want to set up and install
all the different python versions locally (and there are some difficulties with
that) you can just run them in docker using containers.
This is for contributors who want to make changes and test for all different versions of python currently supported. If you don't want to set up and install all the different python versions locally (and there are some difficulties with that) you can just run them in docker using containers.

# Setup

To build a container say for Python 3.11, change to the root directory of the
project and run:
To build a container say for Python 3.11, change to the root directory of the project and run:

```bash
(export PY=3.11 && docker build --build-arg PY="$PY" --build-arg TOXPY="$(sed 's/\.//' <<< "$PY")" -t pactfoundation:python${PY} -f docker/Dockerfile .)
```

This uses an Alpine based image (currently 3.17), which is available as of
2023-04 for Python versions 3.7 - 3.11.
This uses an Alpine based image (currently 3.17), which is available as of 2023-04 for Python versions 3.7 - 3.11.

Note: To run tox, the Python version without the '.' is required, i.e. '311'
instead of '3.11', so some manipulation with `sed` is used to remove the '.'
Note: To run tox, the Python version without the '.' is required, i.e. '311' instead of '3.11', so some manipulation with `sed` is used to remove the '.'

To build for Python versions which require a different Alpine image, such as if
trying to build against Python 3.6, an extra `ALPINE` arg can be provided:
To build for Python versions which require a different Alpine image, such as if trying to build against Python 3.6, an extra `ALPINE` arg can be provided:

```bash
(export PY=3.6 && docker build --build-arg PY="$PY" --build-arg TOXPY="$(sed 's/\.//' <<< "$PY")" --build-arg ALPINE=3.15 -t pactfoundation:python${PY} -f docker/Dockerfile .)
Expand All @@ -39,16 +32,13 @@ If you need to debug you can change the command to:
docker run -it --rm -v "$(pwd)":/home pactfoundation:python3.11 sh
```

This will open a container with a prompt. From the `/home` location in the
container you can run the same tests manually:
This will open a container with a prompt. From the `/home` location in the container you can run the same tests manually:

```bash
tox -e py311-{test,install}
```

In all the above if you need to run a different version change
`py311`/`python3.11` where appropriate. Or you can run the convenience script
to build:
In all the above if you need to run a different version change `py311`/`python3.11` where appropriate. Or you can run the convenience script to build:

```bash
docker/build.sh 3.11
Expand Down
77 changes: 14 additions & 63 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
# Examples

This directory contains an end-to-end example of using Pact in Python. While
this document and the documentation within the examples themselves are intended
to be mostly self-contained, it is highly recommended that you read the [Pact
Documentation](https://docs.pact.io/) as well.
This directory contains an end-to-end example of using Pact in Python. While this document and the documentation within the examples themselves are intended to be mostly self-contained, it is highly recommended that you read the [Pact Documentation](https://docs.pact.io/) as well.

Assuming you have [hatch](https://hatch.pypa.io/latest/) installed, the example
suite can be executed with:
Assuming you have [hatch](https://hatch.pypa.io/latest/) installed, the example suite can be executed with:

```sh
hatch run example
```

The code within the examples is intended to be well documented and you are
encouraged to look through the code as well (or submit a PR if anything is
unclear!).
The code within the examples is intended to be well documented and you are encouraged to look through the code as well (or submit a PR if anything is unclear!).

## Overview

Pact is a contract testing tool. Contract testing is a way to ensure that
services (such as an API provider and a client) can communicate with each other.
This example focuses on HTTP interactions, but Pact can be used to test more
general interactions as well such as through message queues.
Pact is a contract testing tool. Contract testing is a way to ensure that services (such as an API provider and a client) can communicate with each other. This example focuses on HTTP interactions, but Pact can be used to test more general interactions as well such as through message queues.

An interaction between a HTTP client (the _consumer_) and a server (the
_provider_) would typically look like this:
An interaction between a HTTP client (the _consumer_) and a server (the _provider_) would typically look like this:

<div align="center">

Expand All @@ -40,11 +30,7 @@ sequenceDiagram

</div>

To test this interaction naively would require both the consumer and provider to
be running at the same time. While this is straightforward in the above example,
this quickly becomes impractical as the number of interactions grows between
many microservices. Pact solves this by allowing the consumer and provider to be
tested independently.
To test this interaction naively would require both the consumer and provider to be running at the same time. While this is straightforward in the above example, this quickly becomes impractical as the number of interactions grows between many microservices. Pact solves this by allowing the consumer and provider to be tested independently.

Pact achieves this be mocking the other side of the interaction:

Expand Down Expand Up @@ -75,34 +61,20 @@ sequenceDiagram

</div>

In the first stage, the consumer defines a number of interactions in the form
below. Pact sets up a mock server that will respond to the requests as defined
by the consumer. All these interactions, containing both the request and
expected response, are all sent to the Pact Broker.
In the first stage, the consumer defines a number of interactions in the form below. Pact sets up a mock server that will respond to the requests as defined by the consumer. All these interactions, containing both the request and expected response, are all sent to the Pact Broker.

> Given {provider state} \
> Upon receiving {description} \
> With {request} \
> Will respond with {response}
In the second stage, the provider retrieves the interactions from the Pact
Broker. It then sets up a mock client that will make the requests as defined by
the consumer. Pact then verifies that the responses from the provider match the
expected responses defined by the consumer.
In the second stage, the provider retrieves the interactions from the Pact Broker. It then sets up a mock client that will make the requests as defined by the consumer. Pact then verifies that the responses from the provider match the expected responses defined by the consumer.

In this way, Pact is consumer driven and can ensure that the provider is
compatible with the consumer. While this example showcases both sides in Python,
this is absolutely not required. The provider could be written in any language,
and satisfy contracts from a number of consumers all written in different
languages.
In this way, Pact is consumer driven and can ensure that the provider is compatible with the consumer. While this example showcases both sides in Python, this is absolutely not required. The provider could be written in any language, and satisfy contracts from a number of consumers all written in different languages.

### Consumer

The consumer in this example is a simple Python script that makes a HTTP GET
request to a server. It is defined in [`src/consumer.py`](src/consumer.py). The
tests for the consumer are defined in
[`tests/test_00_consumer.py`](tests/test_00_consumer.py). Each interaction is
defined using the format mentioned above. Programmatically, this looks like:
The consumer in this example is a simple Python script that makes a HTTP GET request to a server. It is defined in [`src/consumer.py`](src/consumer.py). The tests for the consumer are defined in [`tests/test_00_consumer.py`](tests/test_00_consumer.py). Each interaction is defined using the format mentioned above. Programmatically, this looks like:

```py
expected: dict[str, Any] = {
Expand All @@ -121,17 +93,9 @@ expected: dict[str, Any] = {

### Provider

This example showcases to different providers, one written in Flask and one
written in FastAPI. Both are simple Python web servers that respond to a HTTP
GET request. The Flask provider is defined in [`src/flask.py`](src/flask.py) and
the FastAPI provider is defined in [`src/fastapi.py`](src/fastapi.py). The
tests for the providers are defined in
[`tests/test_01_provider_flask.py`](tests/test_01_provider_flask.py) and
[`tests/test_01_provider_fastapi.py`](tests/test_01_provider_fastapi.py).
This example showcases to different providers, one written in Flask and one written in FastAPI. Both are simple Python web servers that respond to a HTTP GET request. The Flask provider is defined in [`src/flask.py`](src/flask.py) and the FastAPI provider is defined in [`src/fastapi.py`](src/fastapi.py). The tests for the providers are defined in [`tests/test_01_provider_flask.py`](tests/test_01_provider_flask.py) and [`tests/test_01_provider_fastapi.py`](tests/test_01_provider_fastapi.py).

Unlike the consumer side, the provider side is responsible to responding to the
interactions defined by the consumers. In this regard, the provider testing
is rather simple:
Unlike the consumer side, the provider side is responsible to responding to the interactions defined by the consumers. In this regard, the provider testing is rather simple:

```py
code, _ = verifier.verify_with_broker(
Expand All @@ -142,21 +106,8 @@ code, _ = verifier.verify_with_broker(
assert code == 0
```

The complication comes from the fact that the provider needs to know what state
to be in before responding to the request. In order to achieve this, a testing
endpoint is defined that sets the state of the provider as defined in the
`provider_states_setup_url` above. For example, the consumer requests has _Given
user 123 exists_ as the provider state, and the provider will need to ensure
that this state is satisfied. This would typically entail setting up a database
with the correct data, but it is advisable to achieve the equivalent state by
mocking the appropriate calls. This has been showcased in both provider
examples.
The complication comes from the fact that the provider needs to know what state to be in before responding to the request. In order to achieve this, a testing endpoint is defined that sets the state of the provider as defined in the `provider_states_setup_url` above. For example, the consumer requests has _Given user 123 exists_ as the provider state, and the provider will need to ensure that this state is satisfied. This would typically entail setting up a database with the correct data, but it is advisable to achieve the equivalent state by mocking the appropriate calls. This has been showcased in both provider examples.

### Broker

The broker acts as the intermediary between these test suites. It stores the
interactions defined by the consumer and makes them available to the provider.
Once the provider has verified that it satisfies all interactions, the broker
also stores the verification results. The example here runs the open source
broker within a Docker container. An alternative is to use the hosted [Pactflow
service](https://pactflow.io).
The broker acts as the intermediary between these test suites. It stores the interactions defined by the consumer and makes them available to the provider. Once the provider has verified that it satisfies all interactions, the broker also stores the verification results. The example here runs the open source broker within a Docker container. An alternative is to use the hosted [Pactflow service](https://pactflow.io).
2 changes: 1 addition & 1 deletion run-docker.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

for arch in arm64 amd64; do
# for version in 3.6; do
# for version in 3.6; do
for version in 3.7 3.8 3.9 3.10 3.11; do
docker build -t python-$arch-$version --build-arg PYTHON_VERSION=$version --platform=linux/$arch .
docker run -it --rm python-$arch-$version
Expand Down
21 changes: 11 additions & 10 deletions script/commit_message.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# ruff: noqa
import re
import sys
import subprocess
import sys

examples = """+ 61c8ca9 fix: navbar not responsive on mobile
+ 479c48b test: prepared test cases for user authentication
Expand All @@ -13,21 +14,21 @@


def main():

cmd_tag = "git describe --abbrev=0"
tag = subprocess.check_output(cmd_tag,
shell=True).decode("utf-8").split('\n')[0]
tag = subprocess.check_output(cmd_tag, shell=True).decode("utf-8").split("\n")[0]

cmd = "git log --pretty=format:'%s' {}..HEAD".format(tag)
cmd = f"git log --pretty=format:'%s' {tag}..HEAD"
commits = subprocess.check_output(cmd, shell=True)
commits = commits.decode("utf-8").split('\n')
commits = commits.decode("utf-8").split("\n")
for commit in commits:

pattern = r'((build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert)(\([\w\-]+\))?:\s.*)|((Merge|Fixed)(\([\w\-]+\))?\s.*)' # noqa
pattern = r"((build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert)(\([\w\-]+\))?:\s.*)|((Merge|Fixed)(\([\w\-]+\))?\s.*)"
m = re.match(pattern, commit)
if m is None:
print("\nError with git message '{}' style".format(commit))
print("\nPlease change commit message to the conventional format and try to commit again. Examples:") # noqa
print(f"\nError with git message '{commit}' style")
print(
"\nPlease change commit message to the conventional format and try to"
" commit again. Examples:",
)

print("\n" + examples)
sys.exit(1)
Expand Down
2 changes: 0 additions & 2 deletions script/release_prep.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,3 @@ git add CHANGELOG.md pact/__version__.py
git commit -m "chore: Releasing version $VERSION"

git tag -a "$TAG_NAME" -m "Releasing version $VERSION" && git push origin master --tags


0 comments on commit dab6dba

Please sign in to comment.