Skip to content

Commit

Permalink
Added GitHub Actions build integration
Browse files Browse the repository at this point in the history
  • Loading branch information
jeking3 committed Sep 1, 2020
1 parent 754cee9 commit afad82c
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 1 deletion.
51 changes: 51 additions & 0 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#
# Copyright (C) 2019 - 2020 Tuono, Inc.
# All Rights Reserved
#
# On a pull request or push into master, this runs the unit tests
# and reports coverage.
#
---
name: coverage
on:
pull_request:
push:
branches:
- master

jobs:
coverage:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7.7]

steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "${GITHUB_CONTEXT}"

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- uses: actions/checkout@v2

- name: Install Build Dependencies
run: python3 -m pip install -rrequirements/ci.txt

- name: Enforce pre-commit
run: |
python3 -m pip install pre-commit
pre-commit install
pre-commit run -a
- name: Build, Test, Coverage
run: make coverage

- uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
57 changes: 57 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#
# Copyright (C) 2019 - 2020 Tuono, Inc.
# All Rights Reserved
#
---
name: release
on:
release:
types:
- published

jobs:
publish-gemfury:
# publishes a pypi package to GemFury
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7.7]

steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "${GITHUB_CONTEXT}"

- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install Build Dependencies
run: python3 -m pip install -rrequirements/ci.txt

- name: Publish
id: publish
run: |
rm -rf dist/
VERSION=$(python3 setup.py --version)
echo "::set-output name=version::$VERSION"
python3 setup.py sdist
PACKAGE=$(ls dist/*)
curl --fail -F package=@${PACKAGE} https://${{ secrets.GEMFURY_PUSH_TOKEN }}@push.fury.io/tuono/
- name: Show Version
run: echo "${{ steps.publish.outputs.version }}"

- name: Notify
uses: homoluctus/[email protected]
if: always()
with:
type: ${{ job.status }}
job_name: ${{ github.repository }} publish ${{ steps.publish.outputs.version }} to gemfury
username: tubot
icon_emoji: ":monorail:"
url: ${{ secrets.SLACK_WEBHOOK_URL }}
1 change: 1 addition & 0 deletions .yamllint
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ rules:
max: 120
truthy:
level: error
allowed-values: ['true', 'false', 'on']
90 changes: 89 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,91 @@
# interposer

FIXME: write content
[![Build Status](https://github.com/tuono/interposer/workflows/coverage/badge.svg)](https://github.com/tuono/interposer/actions?query=workflow%3Acoverage)
[![Release Status](https://github.com/tuono/interposer/workflows/release/badge.svg)](https://github.com/tuono/interposer/actions?query=workflow%3Arelease)
[![codecov](https://codecov.io/gh/tuono/interposer/branch/master/graph/badge.svg?token=HKUTULQQSA)](https://codecov.io/gh/tuono/interposer)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

This library lets you wrap any class or bare function and:

1. Record all the method calls (parameters, return values, exceptions).
2. Playback all the method calls.
3. Inspect the method calls to ensure they meet certain criteria.

## Record and Playback

The recorder is useful where you are dealing with a third party
library and you would like to:

- Occasionally ensure your code works live,
- Record detailed responses from third party libraries instead
of mocking them,
- Always ensure your code works.

Recording has advantages and disadvantages, so the right solution
for your situation depends on many things. Recording eliminates
the need to produce and maintain mocks. Mocks of third party
libraries that change or are not well understood are fragile and
lead to a false sense of safety. Recordings on the other hand
are always correct, but they need to be regenerated when your
logic changes around the third party calls.

## Call Inspection

You may want to limit the types of methods that can be called in
third party libraries as an extra measure of protection in certain
runtime modes. Interposer lets you intercept every method called
in a wrapped class.

## Usage

1. Instantiate an Interposer with a datafile path, and set
the mode to Recording.
2. To wrap something, call wrap() and pass in the definition.
3. Use the returned wrapper as if it were the actual definition
that was wrapped.
4. Every use of the wrapped definition will record:
- The call name
- The parameters (positional and keyword)
- The return value, if no exception was raised
- The exception raised, should one be raised

In most cases you want to leverage environment variables with
your own method that retrieves a class, for example if you want
to wrap the AWS boto3 library:

import boto3
import interposer
import os

if os.get("RECORDING_FILE"):
global client_args
wrapper = interposer.Interposer(os.get("RECORDING_FILE"), interposer.Mode.Recording)
client = boto3.client("ec2", **client_args)
wrapped_client = wrapper.wrap(client)
return wrapped_client

Now any method call on `client` will be recorded. To play back the same
stream with the same code, change the interposer mode to Playback and re-run
the code. Instead of calling boto3, the interposer will intercept and provide
the same return values or exceptions that boto3 provided during recording for
given combinations of method names and parameters.

## Restrictions

- Return values and Exceptions must be safe for pickling. Some
third party APIs use local definitions for exceptions, for example,
and local definitions cannot be pickled. If you get a pickling
error, you should subclass Interposer and provide your own
cleanup routine(s) as needed to substitute a class that can be
substituted for the local definition.

## Notes

- This is a resource, so you need to call open() and close() or
use the ScopedInterposer context manager.
- The class variables are ignored for purposes of hashing the
method calls into unique signatures (channel name + method name
+ parameters).

This documentation is not complete, for example pre and post cleanup
mechanisms are not documented, nor is the security check for call inspection.

0 comments on commit afad82c

Please sign in to comment.