From 5b2e3208c90f34d9fb1c3df3c0c48dd1b850dad4 Mon Sep 17 00:00:00 2001 From: Patrick Decat Date: Wed, 29 Sep 2021 15:47:38 +0200 Subject: [PATCH 01/49] Add build and release jobs for python 3.10-dev on Windows Signed-off-by: Patrick Decat --- .github/workflows/build.yml | 5 +++++ .github/workflows/release.yml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2549fde3..76a7960b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,6 +61,7 @@ "fail-fast": false, "matrix": { "name": [ + "win-py-3.10-dev", "win-py-3.9", "win-py-3.8", "win-py-3.7", @@ -71,6 +72,10 @@ "x86" ], "include": [ + { + "name": "win-py-3.10-dev", + "pyenv": "3.10-dev", + }, { "name": "win-py-3.9", "pyenv": "3.9", diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6224c26a..c3544faf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,6 +80,7 @@ "strategy": { "matrix": { "name": [ + "win-wheel-3.10-dev", "win-wheel-3.9", "win-wheel-3.8", "win-wheel-3.7", @@ -90,6 +91,10 @@ "x86" ], "include": [ + { + "name": "win-wheel-3.10-dev", + "pyenv": "3.10-dev", + }, { "name": "win-wheel-3.9", "pyenv": "3.9", From 2bde020fecfc48160c6546dcb37fea6d6d2db532 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Wed, 6 Oct 2021 06:43:54 +1000 Subject: [PATCH 02/49] Use final release for 3.10 Signed-off-by: Jordan Borean --- .github/workflows/build.yml | 6 +++--- .github/workflows/release.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 76a7960b..a5085af1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,7 +61,7 @@ "fail-fast": false, "matrix": { "name": [ - "win-py-3.10-dev", + "win-py-3.10", "win-py-3.9", "win-py-3.8", "win-py-3.7", @@ -73,8 +73,8 @@ ], "include": [ { - "name": "win-py-3.10-dev", - "pyenv": "3.10-dev", + "name": "win-py-3.10", + "pyenv": "3.10", }, { "name": "win-py-3.9", diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c3544faf..c299e02f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,7 +80,7 @@ "strategy": { "matrix": { "name": [ - "win-wheel-3.10-dev", + "win-wheel-3.10", "win-wheel-3.9", "win-wheel-3.8", "win-wheel-3.7", @@ -92,8 +92,8 @@ ], "include": [ { - "name": "win-wheel-3.10-dev", - "pyenv": "3.10-dev", + "name": "win-wheel-3.10", + "pyenv": "3.10", }, { "name": "win-wheel-3.9", From 1f313cb432740136a637f6b69f3be1bcb8e7c59a Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Wed, 6 Oct 2021 07:30:49 +1000 Subject: [PATCH 03/49] Use distutils provided by setuptools The distutils module provided by the stdlib has been deprecated in Python 3.10. This PR sets an env var that is read by setuptools which will shim its vendored copy of distutils if present avoiding a failure once distutils is removed from the stdlib. Signed-off-by: Jordan Borean --- setup.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index a0109666..ee0b2e51 100755 --- a/setup.py +++ b/setup.py @@ -1,10 +1,6 @@ #!/usr/bin/env python from __future__ import print_function -from setuptools import setup -from setuptools import Distribution -from setuptools.command.sdist import sdist -from setuptools.extension import Extension import subprocess import platform import re @@ -13,6 +9,17 @@ import shutil import shlex +# Enables the vendored distutils in setuptools over the stdlib one to avoid +# the deprecation warning. Must be done before importing setuptools, +# setuptools also must be imported before distutils. +# https://github.com/pypa/setuptools/blob/main/docs/deprecated/distutils-legacy.rst +os.environ['SETUPTOOLS_USE_DISTUTILS'] = 'local' + +from setuptools import setup # noqa: E402 +from setuptools import Distribution # noqa: E402 +from setuptools.command.sdist import sdist # noqa: E402 +from setuptools.extension import Extension # noqa: E402 + SKIP_CYTHON_FILE = '__dont_use_cython__.txt' From 744335852ffbe980b49adf492457fb1e2bb83226 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Wed, 6 Oct 2021 10:20:10 +1000 Subject: [PATCH 04/49] Clarify context step example Signed-off-by: Jordan Borean --- gssapi/sec_contexts.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gssapi/sec_contexts.py b/gssapi/sec_contexts.py index 356b939a..80787ac4 100644 --- a/gssapi/sec_contexts.py +++ b/gssapi/sec_contexts.py @@ -481,6 +481,8 @@ def step(self, token=None): try: while not ctx.complete: output_token = ctx.step(input_token) + if not output_token: + break input_token = send_and_receive(output_token) except GSSError as e: handle_the_issue() From 7c0c20a8f78b29834dcd4edeb7df381e5a8a92a3 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Wed, 6 Oct 2021 13:44:20 +1000 Subject: [PATCH 05/49] Convert GitHub actions to YAML, adjust release Converts the existing GitHub action files to YAML to reduce some of the quotes, lines, and support comments. Changes the release process to build the sdist first and then the wheels are built from that sdist. Also adds macOS wheels to the publishing process. Signed-off-by: Jordan Borean --- .github/stale.yml | 14 +- .github/workflows/build.yml | 222 +++++++++------------ .github/workflows/deploy-docs.yml | 54 +++-- .github/workflows/release.yml | 317 ++++++++++++++++-------------- ci/release-win.sh | 43 ---- 5 files changed, 295 insertions(+), 355 deletions(-) delete mode 100755 ci/release-win.sh diff --git a/.github/stale.yml b/.github/stale.yml index ec0dcc5c..a11e868f 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,7 +1,7 @@ -{ - "daysUntilStale": 28, - "daysUntilClose": 14, - "staleLabel": "stale", - "markComment": false, - "only": "pulls", -} +# TODO: This is marked as deprecated - migrate to https://github.com/actions/stale +# https://github.com/marketplace/actions/stale-for-actions +daysUntilStale: 28 +daysUntilClose: 14 +staleLabel: stale +markComment: 'false' +only: pulls diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5085af1..61500dfe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,135 +1,93 @@ -{ - "name": "Build", - "on": { - "push": { - "branches-ignore": "gh-pages", - "tags-ignore": "*", - }, - "pull_request": null, - }, +name: Build +on: + push: + branches-ignore: gh-pages + tags-ignore: '*' + pull_request: null - "jobs": { - "linux": { - "runs-on": "ubuntu-latest", - "strategy": { - "fail-fast": false, - "matrix": { - "name": ["debian-stable", "debian-heimdal", "centos-8", - "fedora-latest"], - "include": [ - { - "name": "debian-stable", - "distro": "debian:stable", - }, - { - "name": "debian-heimdal", - "distro": "debian:stable", - "krb5_ver": "heimdal", - }, - { - "name": "centos-8", - "distro": "centos:8", - }, - { - "name": "fedora-latest", - "distro": "fedora:latest", - "flake": "yes", - }, - ], - }, - }, - "steps": [ - { - "name": "Check out code", - "uses": "actions/checkout@v2", - }, - { - "name": "Build and test gssapi", - "run": "./ci/run-on-linux.sh ./ci/build.sh", - "env": { - "DISTRO": "${{ matrix.distro }}", - "KRB5_VER": "${{ matrix.krb5_ver }}", - "FLAKE": "${{ matrix.flake }}", - }, - }, - ], - }, +jobs: + linux: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + name: + - debian-stable + - debian-heimdal + - centos-8 + - fedora-latest + include: + - name: debian-stable + distro: debian:stable + - name: debian-heimdal + distro: debian:stable + krb5_ver: heimdal + - name: centos-8 + distro: centos:8 + - name: fedora-latest + distro: fedora:latest + flake: 'yes' - "windows": { - "runs-on": "windows-latest", - "strategy": { - "fail-fast": false, - "matrix": { - "name": [ - "win-py-3.10", - "win-py-3.9", - "win-py-3.8", - "win-py-3.7", - "win-py-3.6", - ], - "arch": [ - "x64", - "x86" - ], - "include": [ - { - "name": "win-py-3.10", - "pyenv": "3.10", - }, - { - "name": "win-py-3.9", - "pyenv": "3.9", - }, - { - "name": "win-py-3.8", - "pyenv": "3.8", - }, - { - "name": "win-py-3.7", - "pyenv": "3.7", - }, - { - "name": "win-py-3.6", - "pyenv": "3.6", - }, - ], - }, - }, - "steps": [ - { - "name": "Check out code", - "uses": "actions/checkout@v2", - }, - { - "name": "Install the right python", - "uses": "actions/setup-python@v2", - "with": { - "python-version": "${{ matrix.pyenv }}", - "architecture": "${{ matrix.arch }}" - }, - }, - { - "name": "Build and test gssapi", - "shell": "bash", - "run": "./ci/build.sh", - "env": { "OS_NAME": "windows" }, - }, - ], - }, + steps: + - name: Check out code + uses: actions/checkout@v2 - "macos-heimdal": { - "runs-on": "macos-latest", - "steps": [ - { - "name": "Check out code", - "uses": "actions/checkout@v2", - }, - { - "name": "Build and test gssapi", - "run": "./ci/build.sh", - "env": { "KRB5_VER": "heimdal" }, - }, - ], - }, - }, -} + - name: Build and test gssapi + run: ./ci/run-on-linux.sh ./ci/build.sh + env: + DISTRO: ${{ matrix.distro }} + KRB5_VER: ${{ matrix.krb5_ver }} + FLAKE: ${{ matrix.flake }} + + windows: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + name: + - win-py-3.10 + - win-py-3.9 + - win-py-3.8 + - win-py-3.7 + - win-py-3.6 + arch: + - x64 + - x86 + include: + - name: win-py-3.10 + pyenv: '3.10' + - name: win-py-3.9 + pyenv: '3.9' + - name: win-py-3.8 + pyenv: '3.8' + - name: win-py-3.7 + pyenv: '3.7' + - name: win-py-3.6 + pyenv: '3.6' + + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Install the right python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.pyenv }} + architecture: ${{ matrix.arch }} + + - name: Build and test gssapi + shell: bash + run: ./ci/build.sh + env: + OS_NAME: windows + + macos-heimdal: + runs-on: macos-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Build and test gssapi + run: ./ci/build.sh + env: + KRB5_VER: heimdal diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index df17abac..32021ce5 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -1,30 +1,24 @@ -{ - "name": "Deploy docs", - "on": { "push": { "branches": "main" }}, - "jobs": { - "update-pages": { - "runs-on": "ubuntu-latest", - "steps": [ - { - "name": "Check out code", - "uses": "actions/checkout@v2", - }, - { - "name": "Build docs", - "env": { "DISTRO": "fedora:latest" }, - "run": "./ci/run-on-linux.sh ./ci/before-docs-deploy.sh", - }, - { - "name": "Deploy latest docs", - "uses": "JamesIves/github-pages-deploy-action@3.7.1", - "with": { - "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}", - "BRANCH": "gh-pages", - "FOLDER": "ci_docs_build/html", - "TARGET_FOLDER": "latest", - }, - }, - ], - }, - }, -} +name: Deploy docs +on: + push: + branches: main + +jobs: + update-pages: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Build docs + run: ./ci/run-on-linux.sh ./ci/before-docs-deploy.sh + env: + DISTRO: fedora:latest + + - name: Deploy latest docs + uses: JamesIves/github-pages-deploy-action@3.7.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages + FOLDER: ci_docs_build/html + TARGET_FOLDER: latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c299e02f..226253a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,143 +1,174 @@ -{ - "name": "Release", - "on": { "push": { "tags": "v*" }}, - "jobs": { - "release-linux": { - "runs-on": "ubuntu-latest", - "steps": [ - { - "name": "Check out code", - "uses": "actions/checkout@v2", - }, - { - "name": "Set things up", - "env": { "DISTRO": "fedora:latest" }, - "run": "./ci/run-on-linux.sh ./ci/before-deploy.sh", - }, - { - "name": "Deploy to PyPI", - "uses": "pypa/gh-action-pypi-publish@v1.1.0", - "with": { - "user": "__token__", - "password": "${{ secrets.pypi_password }}", - }, - }, - { - "name": "Deploy stable docs", - "uses": "JamesIves/github-pages-deploy-action@3.7.1", - "with": { - "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}", - "BRANCH": "gh-pages", - "FOLDER": "ci_docs_build/html", - "TARGET_FOLDER": "stable", - }, - }, - { - "name": "Create release", - "uses": "actions/create-release@v1", - "id": "cr", - "env": { "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}" }, - "with": { - "tag_name": "${{ github.ref }}", - "release_name": "${{ github.ref }}", - }, - }, - { - "id": "tarball", - "run": "echo \"::set-output name=tarball::`ls tag_build/*.tar.gz | awk -F/ '{print $2}'`\"" - }, - { - "id": "checksum", - "run": "echo \"::set-output name=checksum::`ls tag_build/*.sha512sum | awk -F/ '{print $2}'`\"" - }, - { - "name": "Upload release tarball", - "uses": "actions/upload-release-asset@v1", - "env": { "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}" }, - "with": { - "upload_url": "${{ steps.cr.outputs.upload_url }}", - "asset_path": "tag_build/${{ steps.tarball.outputs.tarball }}", - "asset_name": "${{ steps.tarball.outputs.tarball }}", - "asset_content_type": "application/octet-stream", - }, - }, - { - "name": "Upload release checksum", - "uses": "actions/upload-release-asset@v1", - "env": { "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}" }, - "with": { - "upload_url": "${{ steps.cr.outputs.upload_url }}", - "asset_path": "tag_build/${{ steps.checksum.outputs.checksum }}", - "asset_name": "${{ steps.checksum.outputs.checksum }}", - "asset_content_type": "text/plain", - }, - }, - ], - }, - - "release-windows": { - "runs-on": "windows-latest", - "strategy": { - "matrix": { - "name": [ - "win-wheel-3.10", - "win-wheel-3.9", - "win-wheel-3.8", - "win-wheel-3.7", - "win-wheel-3.6", - ], - "arch": [ - "x64", - "x86" - ], - "include": [ - { - "name": "win-wheel-3.10", - "pyenv": "3.10", - }, - { - "name": "win-wheel-3.9", - "pyenv": "3.9", - }, - { - "name": "win-wheel-3.8", - "pyenv": "3.8", - }, - { - "name": "win-wheel-3.7", - "pyenv": "3.7", - }, - { - "name": "win-wheel-3.6", - "pyenv": "3.6", - }, - ], - }, - }, - "steps": [ - { - "name": "Check out code", - "uses": "actions/checkout@v2", - }, - { - "name": "Install the right python", - "uses": "actions/setup-python@v2", - "with": { - "python-version": "${{ matrix.pyenv }}", - "architecture": "${{ matrix.arch }}" - }, - }, - { - "name": "Create and upload Windows wheel", - "shell": "bash", - "run": "./ci/release-win.sh", - "env": { - "OS_NAME": "windows", - "TWINE_USER": "__token__", - "TWINE_PASSWORD": "${{ secrets.pypi_password }}", - }, - }, - ], - }, - }, -} +name: Release +on: + push: + tags: v* + +jobs: + sdist-release: + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Set things up + run: ./ci/run-on-linux.sh ./ci/before-deploy.sh + env: + DISTRO: fedora:latest + + - name: Upload sdist + uses: actions/upload-artifact@v2 + with: + path: ./dist/*.tar.gz + name: artifact + + - name: Deploy stable docs + uses: JamesIves/github-pages-deploy-action@3.7.1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages + FOLDER: ci_docs_build/html + TARGET_FOLDER: stable + + - name: Create release + uses: actions/create-release@v1 + id: cr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + + - id: tarball + run: echo "::set-output name=tarball::`ls tag_build/*.tar.gz | awk -F/ '{print $2}'`" + + - id: checksum + run: echo "::set-output name=checksum::`ls tag_build/*.sha512sum | awk -F/ '{print $2}'`" + + - name: Upload release tarball + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.cr.outputs.upload_url }} + asset_path: tag_build/${{ steps.tarball.outputs.tarball }} + asset_name: ${{ steps.tarball.outputs.tarball }} + asset_content_type: application/octet-stream + + - name: Upload release checksum + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.cr.outputs.upload_url }} + asset_path: tag_build/${{ steps.checksum.outputs.checksum }} + asset_name: ${{ steps.checksum.outputs.checksum }} + asset_content_type: text/plain + + wheel: + needs: + - sdist-release + + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: macOS-10.15 + version: cp310-macosx_x86_64 + - os: macOS-10.15 + version: cp310-macosx_arm64 + - os: macOS-10.15 + version: cp39-macosx_x86_64 + - os: macOS-10.15 + version: cp39-macosx_arm64 + - os: macOS-10.15 + version: cp38-macosx_x86_64 + - os: macOS-10.15 + version: cp38-macosx_arm64 + - os: macOS-10.15 + version: cp37-macosx_x86_64 + - os: macOS-10.15 + version: cp36-macosx_x86_64 + + - os: windows-2019 + version: cp310-win_amd64 + - os: windows-2019 + version: cp310-win32 + - os: windows-2019 + version: cp39-win_amd64 + - os: windows-2019 + version: cp39-win32 + - os: windows-2019 + version: cp38-win_amd64 + - os: windows-2019 + version: cp38-win32 + - os: windows-2019 + version: cp37-win_amd64 + - os: windows-2019 + version: cp37-win32 + - os: windows-2019 + version: cp36-win_amd64 + - os: windows-2019 + version: cp36-win32 + + steps: + - name: Set up environment + if: startsWith(matrix.os, 'windows-') + shell: bash + run: | + choco.exe install \ + --no-progress \ + --yes \ + --ignore-detected-reboot \ + --allow-downgrade \ + --install-arguments "'ADDLOCAL=ALL'" \ + ${{ endsWith(matrix.version, '-win32') && '--x86' || '' }} mitkerberos || true + + echo "C:\Program Files${{ endsWith(matrix.version, '-win32') && ' (x86)' || '' }}\MIT\Kerberos\bin;$PATH" >> $GITHUB_PATH + + - name: Download sdist + uses: actions/download-artifact@v2 + with: + name: artifact + path: ./ + + - name: Extract sdist + shell: bash + run: | + tar xf gssapi-*.tar.gz + mv gssapi-*/* . + rm -r gssapi-*/ + rm gssapi-*.tar.gz + + - name: Build wheel + uses: pypa/cibuildwheel@v2.1.2 + env: + CIBW_ARCHS: all + CIBW_TEST_SKIP: '*_arm64' + CIBW_BUILD: ${{ matrix.version }} + CIBW_BUILD_VERBOSITY: 1 + + - name: Upload wheel + uses: actions/upload-artifact@v2 + with: + path: ./wheelhouse/*.whl + name: artifact + + pypi: + needs: + - wheel + + runs-on: ubuntu-latest + + steps: + - name: Download sdist and wheels + uses: actions/download-artifact@v2 + with: + name: artifact + path: ./dist + + - name: Deploy to PyPI + uses: pypa/gh-action-pypi-publish@v1.1.0 + with: + user: __token__ + password: ${{ secrets.pypi_password }} diff --git a/ci/release-win.sh b/ci/release-win.sh deleted file mode 100755 index 925dc3c3..00000000 --- a/ci/release-win.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -e - -source ./ci/build.sh - -# build the wheel -python -m pip install wheel -python setup.py bdist_wheel - -cd dist - -tag=$(git describe --tags) - -# Rename and checksum the wheel -if [ x"${tag#v[0-9]}" = "x${tag}" ]; then - PYTHON_GSSAPI_VERSION=${tag} -else - PYTHON_GSSAPI_VERSION=${tag#v} -fi - -PKG_NAME_VER=$(ls *.whl | sed "s/gssapi-[^-]*-\(.*\)\.whl/python-gssapi-${PYTHON_GSSAPI_VERSION}-\1/") - -cp *.whl "${PKG_NAME_VER}.whl" - -sha512sum --binary ./${PKG_NAME_VER}.whl > ./${PKG_NAME_VER}.sha512sum - -cd .. - -# Hack around https://github.com/pypa/gh-action-pypi-publish/issues/32 - -echo 'Running: python -m pip install twine ...' -python -m pip install twine - -echo 'Running: set +x; python -m twine upload...' -# Please note this cannot be set -x or passwords will leak! -set +x - -python -m twine upload -u $TWINE_USER -p $TWINE_PASSWORD dist/gssapi* > out.log 2>&1 || true - -# and restore... -set -x -egrep -i 'fail|error' out.log && cat out.log && exit 1 - -exit 0 From 8656374d0c5adbe4f93a9ac2d94d66c77b7d7eae Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Thu, 7 Oct 2021 05:33:54 +1000 Subject: [PATCH 06/49] Use stale action over deprecated workflow Signed-off-by: Jordan Borean --- .github/stale.yml | 7 ------- .github/workflows/stale.yml | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) delete mode 100644 .github/stale.yml create mode 100644 .github/workflows/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index a11e868f..00000000 --- a/.github/stale.yml +++ /dev/null @@ -1,7 +0,0 @@ -# TODO: This is marked as deprecated - migrate to https://github.com/actions/stale -# https://github.com/marketplace/actions/stale-for-actions -daysUntilStale: 28 -daysUntilClose: 14 -staleLabel: stale -markComment: 'false' -only: pulls diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 00000000..47bbdb5a --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,23 @@ +workflow-dispatchname: Stale pull request handler +on: + schedule: + - cron: 0 0 * * * + +permissions: + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@main + id: stale + with: + days-before-stale: -1 + days-before-pr-stable: 28 + days-before-pr-close: 14 + stale-pr-label: stale + stale-pr-message: >- + This pull request is stale because it has been open for 4 weeks with no activity. + Remove stale label or comment or this will be closed in 2 weeks. From e9073f8d5f793eafb8a0c7c31e56fb9095223223 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Thu, 7 Oct 2021 05:43:39 +1000 Subject: [PATCH 07/49] Prepare for 1.7.1rc1 release Signed-off-by: Jordan Borean --- docs/source/conf.py | 4 ++-- setup.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3cf1dcf6..ad73b4fd 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,9 +58,9 @@ # built documents. # # The short X.Y version. -version = '1.7.0' +version = '1.7.1rc1' # The full version, including alpha/beta/rc tags. -release = '1.7.0' +release = '1.7.1rc1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index ee0b2e51..ebca33b5 100755 --- a/setup.py +++ b/setup.py @@ -344,7 +344,7 @@ def gssapi_modules(lst): setup( name='gssapi', - version='1.7.0', + version='1.7.1rc1', author='The Python GSSAPI Team', author_email='jborean93@gmail.com', packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions', @@ -362,6 +362,7 @@ def gssapi_modules(lst): 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Intended Audience :: Developers', 'License :: OSI Approved :: ISC License (ISCL)', 'Programming Language :: Python :: Implementation :: CPython', From 3983aff54c6a2b76e7517696dee61c75bc4e3a1e Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 11 Oct 2021 04:55:53 +1000 Subject: [PATCH 08/49] Fix typos in stale workflow Signed-off-by: Jordan Borean --- .github/workflows/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 47bbdb5a..a7929e94 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,4 +1,4 @@ -workflow-dispatchname: Stale pull request handler +name: Stale pull request handler on: schedule: - cron: 0 0 * * * @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@main + - uses: actions/stale@4.0.0 id: stale with: days-before-stale: -1 From e2711d4dd62888a2523da084ea2e6c04258fd9f4 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 11 Oct 2021 05:50:13 +1000 Subject: [PATCH 09/49] Fix README.rst symlink in GitHub release asset Fixes the symlink in the GitHub tar.gz release asset created during a release. This symlink links to the README.txt file in the same directory and should be preserved to the same target when extracting. Signed-off-by: Jordan Borean --- ci/before-deploy.sh | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/ci/before-deploy.sh b/ci/before-deploy.sh index 1f86b7d1..12a3a1c5 100755 --- a/ci/before-deploy.sh +++ b/ci/before-deploy.sh @@ -48,5 +48,22 @@ fi PKG_NAME_VER="python-gssapi-${PYTHON_GSSAPI_VERSION}" -tar -czvf ./tag_build/${PKG_NAME_VER}.tar.gz --exclude='dist' --exclude='tag_build' --exclude='.git' --exclude='travis_docs_build' --exclude='.git' --transform "s,^\.,${PKG_NAME_VER}," . +tar -cvf ./tag_build/${PKG_NAME_VER}.tar \ + --exclude='dist' \ + --exclude='tag_build' \ + --exclude='.git' \ + --exclude='travis_docs_build' \ + --exclude='README.rst' \ + --transform="s,^\.,${PKG_NAME_VER}," . + +# --transform clobbers symlink so add it last using Python +python - << EOF +import tarfile + +with tarfile.open("tag_build/${PKG_NAME_VER}.tar", mode="a:") as tf: + tf.add("README.rst", arcname="${PKG_NAME_VER}/README.rst") +EOF + +gzip ./tag_build/${PKG_NAME_VER}.tar + sha512sum --binary ./tag_build/${PKG_NAME_VER}.tar.gz > ./tag_build/${PKG_NAME_VER}.sha512sum From 9889db2ee019b66ed48f7f2205017f4713c79bff Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 11 Oct 2021 06:05:54 +1000 Subject: [PATCH 10/49] Fix up release pipeline by selecting Python Signed-off-by: Jordan Borean --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 226253a6..3f65b3a2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,9 @@ jobs: runs-on: ubuntu-latest steps: + - name: Setup Python + uses: actions/setup-python@v2 + - name: Check out code uses: actions/checkout@v2 From c4397dad30c3d06071bc85be3df88f5b4a502131 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 11 Oct 2021 06:52:55 +1000 Subject: [PATCH 11/49] Final fix for building release GitHub asset Signed-off-by: Jordan Borean --- .github/workflows/release.yml | 3 --- ci/before-deploy.sh | 20 +++++++------------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f65b3a2..226253a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,9 +8,6 @@ jobs: runs-on: ubuntu-latest steps: - - name: Setup Python - uses: actions/setup-python@v2 - - name: Check out code uses: actions/checkout@v2 diff --git a/ci/before-deploy.sh b/ci/before-deploy.sh index 12a3a1c5..1465a3b9 100755 --- a/ci/before-deploy.sh +++ b/ci/before-deploy.sh @@ -12,26 +12,18 @@ yum -y install tar git # build the docs deploy::build-docs -# NB(directxman12): this is a *terrible* hack, but basically, -# `twine` gets called like this: -# - python setup.py $PYPI_DISTRIBUTIONS -# - twine upload -r pypi dist/* -# - [some other stuff] -# -# so if we set $PYPI_DISTRIBUTIONS to something harmless, like `check`, -# and then build the dist ourselves (and save it from the cleanup), -# dpl will upload that - -# build the sdist +# build the sdist and save the dirs before the clean python setup.py sdist mv dist dist_saved +mv .venv .venv_saved # for the tarball upload # clean up -git clean -Xdf +git clean -Xdf --exclude .venv -# restore the saved "dist" directory +# restore the saved "dist"/".venv" directory mv dist_saved dist +mv .venv_saved .venv # make the dir rm -rf ./tag_build || true @@ -53,6 +45,7 @@ tar -cvf ./tag_build/${PKG_NAME_VER}.tar \ --exclude='tag_build' \ --exclude='.git' \ --exclude='travis_docs_build' \ + --exclude='.venv' \ --exclude='README.rst' \ --transform="s,^\.,${PKG_NAME_VER}," . @@ -67,3 +60,4 @@ EOF gzip ./tag_build/${PKG_NAME_VER}.tar sha512sum --binary ./tag_build/${PKG_NAME_VER}.tar.gz > ./tag_build/${PKG_NAME_VER}.sha512sum + From 74471141ab2f7f4530c39dd44427b4a697e02bff Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 11 Oct 2021 07:08:23 +1000 Subject: [PATCH 12/49] Prepare for 1.7.2 release Signed-off-by: Jordan Borean --- ci/before-deploy.sh | 2 +- docs/source/conf.py | 4 ++-- setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/before-deploy.sh b/ci/before-deploy.sh index 1465a3b9..dc446374 100755 --- a/ci/before-deploy.sh +++ b/ci/before-deploy.sh @@ -19,7 +19,7 @@ mv .venv .venv_saved # for the tarball upload # clean up -git clean -Xdf --exclude .venv +git clean -Xdf # restore the saved "dist"/".venv" directory mv dist_saved dist diff --git a/docs/source/conf.py b/docs/source/conf.py index ad73b4fd..485aa7a0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,9 +58,9 @@ # built documents. # # The short X.Y version. -version = '1.7.1rc1' +version = '1.7.2' # The full version, including alpha/beta/rc tags. -release = '1.7.1rc1' +release = '1.7.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index ebca33b5..1daa7438 100755 --- a/setup.py +++ b/setup.py @@ -344,7 +344,7 @@ def gssapi_modules(lst): setup( name='gssapi', - version='1.7.1rc1', + version='1.7.2', author='The Python GSSAPI Team', author_email='jborean93@gmail.com', packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions', From 9d498fd7e7751129630ade502f2289554afce11e Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 11 Oct 2021 11:55:02 +1000 Subject: [PATCH 13/49] Fix typo in stale workflow Signed-off-by: Jordan Borean --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index a7929e94..d6f8903a 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -15,7 +15,7 @@ jobs: id: stale with: days-before-stale: -1 - days-before-pr-stable: 28 + days-before-pr-stale: 28 days-before-pr-close: 14 stale-pr-label: stale stale-pr-message: >- From 6ec1927a64cb791b9eca45fa45502e5b8e7d548b Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Fri, 15 Oct 2021 05:17:19 +1000 Subject: [PATCH 14/49] Update Stale Issues version --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index d6f8903a..aef40d7f 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@4.0.0 + - uses: actions/stale@v4.0.0 id: stale with: days-before-stale: -1 From 5b4d4055d4bbf76c5dec90ecc4c6dfd640044cd6 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Wed, 2 Feb 2022 14:45:10 -0500 Subject: [PATCH 15/49] Fix sha512sum file generation Change sha512sum command line so that it does not include subdirectory paths with the filename in the generated file, as that makes it harder to check the sha512sum later via the --check option. Signed-off-by: Simo Sorce --- ci/before-deploy.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ci/before-deploy.sh b/ci/before-deploy.sh index dc446374..3b78ff53 100755 --- a/ci/before-deploy.sh +++ b/ci/before-deploy.sh @@ -57,7 +57,8 @@ with tarfile.open("tag_build/${PKG_NAME_VER}.tar", mode="a:") as tf: tf.add("README.rst", arcname="${PKG_NAME_VER}/README.rst") EOF -gzip ./tag_build/${PKG_NAME_VER}.tar - -sha512sum --binary ./tag_build/${PKG_NAME_VER}.tar.gz > ./tag_build/${PKG_NAME_VER}.sha512sum +pushd ./tag_build +gzip ${PKG_NAME_VER}.tar +sha512sum --binary ${PKG_NAME_VER}.tar.gz > ${PKG_NAME_VER}.sha512sum +popd From 5552513a47a636a4d956f76b498c6fa2e368cc98 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Thu, 3 Feb 2022 13:11:49 +1000 Subject: [PATCH 16/49] Fix up CI containers and stop using nose Signed-off-by: Jordan Borean --- .github/workflows/build.yml | 6 +++--- README.txt | 2 -- ci/build.sh | 2 +- test-requirements.txt | 1 - tox.ini | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61500dfe..045b7bcc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: name: - debian-stable - debian-heimdal - - centos-8 + - centos-stream-8 - fedora-latest include: - name: debian-stable @@ -22,8 +22,8 @@ jobs: - name: debian-heimdal distro: debian:stable krb5_ver: heimdal - - name: centos-8 - distro: centos:8 + - name: centos-stream-8 + distro: quay.io/centos/centos:stream8 - name: fedora-latest distro: fedora:latest flake: 'yes' diff --git a/README.txt b/README.txt index 14219cc6..da63ece1 100644 --- a/README.txt +++ b/README.txt @@ -48,8 +48,6 @@ To compile from scratch, you will need Cython >= 0.21.1. For Running the Tests --------------------- -* the `nose` package - * the `k5test` package To install test dependencies using pip: diff --git a/ci/build.sh b/ci/build.sh index cd96ee27..52846487 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -40,5 +40,5 @@ if [ "$OS_NAME" = "windows" ]; then # Windows can't run the tests yet, so just make sure it imports and exit python -c "import gssapi" || exit $? else - python setup.py nosetests --verbosity=3 || exit $? + python -m unittest -v || exit $? fi diff --git a/test-requirements.txt b/test-requirements.txt index 7d800c70..c0a63b3a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,5 +1,4 @@ flake8 -nose parameterized Cython k5test diff --git a/tox.ini b/tox.ini index 30da1a0a..7eeefc40 100644 --- a/tox.ini +++ b/tox.ini @@ -10,6 +10,6 @@ envlist = py36,py37,py38 whitelist_externals=bash commands = bash -c "source ./.travis/lib-verify.sh && verify::flake8" - python setup.py nosetests [] + python -m unittest deps = -r{toxinidir}/test-requirements.txt From 08fe53100e3c065b868512ba11336f3f3f128cb1 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Thu, 3 Feb 2022 14:01:03 +1000 Subject: [PATCH 17/49] Fix up doc type ambiguity Signed-off-by: Jordan Borean --- gssapi/raw/named_tuples.py | 54 ++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/gssapi/raw/named_tuples.py b/gssapi/raw/named_tuples.py index 03501316..e41bec95 100644 --- a/gssapi/raw/named_tuples.py +++ b/gssapi/raw/named_tuples.py @@ -1,31 +1,27 @@ from typing import Iterable, List, NamedTuple, Optional, Set, TYPE_CHECKING if TYPE_CHECKING: - from gssapi.raw.creds import Creds - from gssapi.raw.names import Name - from gssapi.raw.oids import OID - from gssapi.raw.sec_contexts import SecurityContext - from gssapi.raw.types import RequirementFlag + import gssapi.raw as g class AcquireCredResult(NamedTuple): """Credential result when acquiring a GSSAPI credential.""" - creds: "Creds" #: GSSAPI credentials that were acquired - mechs: Set["OID"] #: Set of mechs the cred is for + creds: "g.Creds" #: GSSAPI credentials that were acquired + mechs: Set["g.OID"] #: Set of mechs the cred is for lifetime: int #: Number of seconds for which the cred will remain valid class InquireCredResult(NamedTuple): """Information about the credential.""" - name: Optional["Name"] #: The principal associated with the credential + name: Optional["g.Name"] #: The principal associated with the credential lifetime: Optional[int] #: Number of seconds which the cred is valid for usage: Optional[str] #: How the credential can be used - mechs: Optional[Set["OID"]] #: Set of mechs the cred is for + mechs: Optional[Set["g.OID"]] #: Set of mechs the cred is for class InquireCredByMechResult(NamedTuple): """Information about the credential for a specific mechanism.""" - name: Optional["Name"] #: The principal associated with the credential + name: Optional["g.Name"] #: The principal associated with the credential init_lifetime: Optional[int] #: Time valid for initiation accept_lifetime: Optional[int] #: Time valid for accepting usage: Optional[str] #: How the credential can be used @@ -33,8 +29,8 @@ class InquireCredByMechResult(NamedTuple): class AddCredResult(NamedTuple): """Result of adding to a GSSAPI credential.""" - creds: Optional["Creds"] #: The credential that was generated - mechs: Set["OID"] #: Set of mechs the cred is for + creds: Optional["g.Creds"] #: The credential that was generated + mechs: Set["g.OID"] #: Set of mechs the cred is for init_lifetime: int #: Time valid for initiation accept_lifetime: int #: Time valid for accepting @@ -42,7 +38,7 @@ class AddCredResult(NamedTuple): class DisplayNameResult(NamedTuple): """Textual representation of a GSSAPI name.""" name: bytes #: The representation of the GSSAPI name - name_type: Optional["OID"] #: The type of GSSAPI name + name_type: Optional["g.OID"] #: The type of GSSAPI name class WrapResult(NamedTuple): @@ -60,21 +56,21 @@ class UnwrapResult(NamedTuple): class AcceptSecContextResult(NamedTuple): """Result when accepting a security context by an initiator.""" - context: "SecurityContext" #: The acceptor security context - initiator_name: "Name" #: The authenticated name of the initiator - mech: "OID" #: Mechanism with which the context was established + context: "g.SecurityContext" #: The acceptor security context + initiator_name: "g.Name" #: The authenticated name of the initiator + mech: "g.OID" #: Mechanism with which the context was established token: Optional[bytes] #: Token to be returned to the initiator - flags: "RequirementFlag" #: Services requested by the initiator + flags: "g.equirementFlag" #: Services requested by the initiator lifetime: int #: Seconds for which the context is valid for - delegated_creds: Optional["Creds"] #: Delegated credentials + delegated_creds: Optional["g.Creds"] #: Delegated credentials more_steps: bool #: More input is required to complete the exchange class InitSecContextResult(NamedTuple): """Result when initiating a security context""" - context: "SecurityContext" #: The initiator security context - mech: "OID" #: Mechanism used in the security context - flags: "RequirementFlag" #: Services available for the context + context: "g.SecurityContext" #: The initiator security context + mech: "g.OID" #: Mechanism used in the security context + flags: "g.RequirementFlag" #: Services available for the context token: Optional[bytes] #: Token to be sent to the acceptor lifetime: int #: Seconds for which the context is valid for more_steps: bool #: More input is required to complete the exchange @@ -82,18 +78,18 @@ class InitSecContextResult(NamedTuple): class InquireContextResult(NamedTuple): """Information about the security context.""" - initiator_name: Optional["Name"] #: Name of the initiator - target_name: Optional["Name"] #: Name of the acceptor + initiator_name: Optional["g.Name"] #: Name of the initiator + target_name: Optional["g.Name"] #: Name of the acceptor lifetime: Optional[int] #: Time valid for the security context - mech: Optional["OID"] #: Mech used to create the security context - flags: Optional["RequirementFlag"] #: Services available for the context + mech: Optional["g.OID"] #: Mech used to create the security context + flags: Optional["g.RequirementFlag"] #: Services available for the context locally_init: Optional[bool] #: Context was initiated locally complete: Optional[bool] #: Context has been established and ready to use class StoreCredResult(NamedTuple): """Result of the credential storing operation.""" - mechs: List["OID"] #: Mechs that were stored in the credential store + mechs: List["g.OID"] #: Mechs that were stored in the credential store usage: str #: How the credential can be used @@ -107,7 +103,7 @@ class InquireNameResult(NamedTuple): """Information about a GSSAPI Name.""" attrs: List[bytes] #: Set of attribute names is_mech_name: bool #: Name is a mechanism name - mech: "OID" #: The mechanism if is_name_mech is True + mech: "g.OID" #: The mechanism if is_name_mech is True class GetNameAttributeResult(NamedTuple): @@ -120,8 +116,8 @@ class GetNameAttributeResult(NamedTuple): class InquireAttrsResult(NamedTuple): """Set of attributes supported and known by a mechanism.""" - mech_attrs: Set["OID"] #: The mechanisms attributes - known_mech_attrs: Set["OID"] #: Known attributes of the mechanism + mech_attrs: Set["g.OID"] #: The mechanisms attributes + known_mech_attrs: Set["g.OID"] #: Known attributes of the mechanism class DisplayAttrResult(NamedTuple): From 935f95aaaf403f6f5ae665b24d056a15c518c37b Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 7 Feb 2022 17:02:29 +1000 Subject: [PATCH 18/49] Add Python type annotation Signed-off-by: Jordan Borean --- ci/build.sh | 6 + docs-requirements.txt | 1 + docs/source/conf.py | 67 +++++- docs/source/gssapi.raw.rst | 62 ++--- gssapi/_utils.py | 79 ++++--- gssapi/_win_config.py | 25 +- gssapi/creds.py | 143 ++++++++---- gssapi/exceptions.py | 15 +- gssapi/mechs.py | 65 ++++-- gssapi/names.py | 151 ++++++++---- gssapi/py.typed | 0 gssapi/raw/_enum_extensions/__init__.py | 17 +- gssapi/raw/chan_bindings.pyi | 30 +++ gssapi/raw/chan_bindings.pyx | 16 -- gssapi/raw/creds.pyi | 186 +++++++++++++++ gssapi/raw/creds.pyx | 137 ----------- gssapi/raw/exceptions.pyi | 67 ++++++ gssapi/raw/ext_cred_imp_exp.pyi | 44 ++++ gssapi/raw/ext_cred_imp_exp.pyx | 35 --- gssapi/raw/ext_cred_store.pyi | 136 +++++++++++ gssapi/raw/ext_cred_store.pyx | 105 --------- gssapi/raw/ext_dce.pyi | 187 +++++++++++++++ gssapi/raw/ext_dce.pyx | 99 -------- gssapi/raw/ext_dce_aead.pyi | 61 +++++ gssapi/raw/ext_dce_aead.pyx | 46 ---- gssapi/raw/ext_ggf.pyi | 97 ++++++++ gssapi/raw/ext_ggf.pyx | 81 ------- gssapi/raw/ext_iov_mic.pyi | 90 ++++++++ gssapi/raw/ext_iov_mic.pyx | 69 ------ gssapi/raw/ext_krb5.pyi | 280 ++++++++++++++++++++++ gssapi/raw/ext_krb5.pyx | 218 ------------------ gssapi/raw/ext_password.pyi | 41 ++++ gssapi/raw/ext_password.pyx | 29 --- gssapi/raw/ext_password_add.pyi | 48 ++++ gssapi/raw/ext_password_add.pyx | 35 --- gssapi/raw/ext_rfc4178.pyi | 27 +++ gssapi/raw/ext_rfc4178.pyx | 19 -- gssapi/raw/ext_rfc5587.pyi | 63 +++++ gssapi/raw/ext_rfc5587.pyx | 48 ---- gssapi/raw/ext_rfc5588.pyi | 42 ++++ gssapi/raw/ext_rfc5588.pyx | 29 --- gssapi/raw/ext_rfc5801.pyi | 39 ++++ gssapi/raw/ext_rfc5801.pyx | 28 --- gssapi/raw/ext_rfc6680.pyi | 169 ++++++++++++++ gssapi/raw/ext_rfc6680.pyx | 138 ----------- gssapi/raw/ext_rfc6680_comp_oid.pyi | 0 gssapi/raw/ext_s4u.pyi | 86 +++++++ gssapi/raw/ext_s4u.pyx | 66 ------ gssapi/raw/ext_set_cred_opt.pyi | 50 ++++ gssapi/raw/ext_set_cred_opt.pyx | 41 ---- gssapi/raw/mech_krb5.pyi | 0 gssapi/raw/message.pyi | 156 +++++++++++++ gssapi/raw/misc.pyi | 166 +++++++++++++ gssapi/raw/misc.pyx | 118 ---------- gssapi/raw/named_tuples.py | 37 +-- gssapi/raw/names.pyi | 170 ++++++++++++++ gssapi/raw/names.pyx | 137 ----------- gssapi/raw/oids.pyi | 50 ++++ gssapi/raw/oids.pyx | 36 --- gssapi/raw/sec_contexts.pyi | 294 ++++++++++++++++++++++++ gssapi/raw/sec_contexts.pyx | 236 ------------------- gssapi/raw/types.pyi | 174 ++++++++++++++ gssapi/raw/types.pyx | 54 ----- gssapi/sec_contexts.py | 140 ++++++++--- gssapi/tests/test_high_level.py | 2 +- mypy.ini | 31 +++ setup.py | 4 + test-requirements.txt | 2 + 68 files changed, 3356 insertions(+), 2064 deletions(-) create mode 100644 gssapi/py.typed create mode 100644 gssapi/raw/chan_bindings.pyi create mode 100644 gssapi/raw/creds.pyi create mode 100644 gssapi/raw/exceptions.pyi create mode 100644 gssapi/raw/ext_cred_imp_exp.pyi create mode 100644 gssapi/raw/ext_cred_store.pyi create mode 100644 gssapi/raw/ext_dce.pyi create mode 100644 gssapi/raw/ext_dce_aead.pyi create mode 100644 gssapi/raw/ext_ggf.pyi create mode 100644 gssapi/raw/ext_iov_mic.pyi create mode 100644 gssapi/raw/ext_krb5.pyi create mode 100644 gssapi/raw/ext_password.pyi create mode 100644 gssapi/raw/ext_password_add.pyi create mode 100644 gssapi/raw/ext_rfc4178.pyi create mode 100644 gssapi/raw/ext_rfc5587.pyi create mode 100644 gssapi/raw/ext_rfc5588.pyi create mode 100644 gssapi/raw/ext_rfc5801.pyi create mode 100644 gssapi/raw/ext_rfc6680.pyi create mode 100644 gssapi/raw/ext_rfc6680_comp_oid.pyi create mode 100644 gssapi/raw/ext_s4u.pyi create mode 100644 gssapi/raw/ext_set_cred_opt.pyi create mode 100644 gssapi/raw/mech_krb5.pyi create mode 100644 gssapi/raw/message.pyi create mode 100644 gssapi/raw/misc.pyi create mode 100644 gssapi/raw/names.pyi create mode 100644 gssapi/raw/oids.pyi create mode 100644 gssapi/raw/sec_contexts.pyi create mode 100644 gssapi/raw/types.pyi create mode 100644 mypy.ini diff --git a/ci/build.sh b/ci/build.sh index 52846487..8b455d7d 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -26,6 +26,12 @@ if [ x"$FLAKE" = "xyes" ]; then fi fi +python -m mypy . +MYPY_RES=$? +if [ $MYPY_RES -ne 0 ]; then + exit $MYPY_RES +fi + # always build in-place so that Sphinx can find the modules python setup.py build_ext --inplace $EXTRA_BUILDEXT BUILD_RES=$? diff --git a/docs-requirements.txt b/docs-requirements.txt index 0e60f68f..a2223745 100644 --- a/docs-requirements.txt +++ b/docs-requirements.txt @@ -1,3 +1,4 @@ Sphinx >= 1.3.1 +sphinx-autoapi sphinx-rtd-theme >= 0.2.5b1 recommonmark >= 0.4.0 diff --git a/docs/source/conf.py b/docs/source/conf.py index 485aa7a0..4688d998 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -11,7 +11,7 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import sys, os, re # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -30,7 +30,20 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', "sphinx.ext.napoleon", 'gssapi_find_missing', 'requires_rfc'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', + 'autoapi.extension', + 'gssapi_find_missing', + 'requires_rfc', +] + +autoapi_generate_api_docs = False +autoapi_type = 'python' +autoapi_dirs = ['../../gssapi'] +autoapi_file_patterns = ['*.pyi'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -56,11 +69,55 @@ # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -# +# https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions +setup_py_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'setup.py')) +version_pattern = re.compile(r""" +^\s*version=['|\"](?P + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
+)['|\"],?\s*$
+""", re.VERBOSE | re.IGNORECASE)
+
 # The short X.Y version.
-version = '1.7.2'
+version = ''
 # The full version, including alpha/beta/rc tags.
-release = '1.7.2'
+release = ''
+
+with open(setup_py_path, mode='r') as fd:
+    for line in fd:
+        version_match = version_pattern.match(line)
+        if version_match:
+            version = version_match.group('release')
+            release = version_match.group('full_version')
+            break
+
+if not version or not release:
+    raise Exception("Failed to find version in setup.py")
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/docs/source/gssapi.raw.rst b/docs/source/gssapi.raw.rst
index 970955a9..912c5364 100644
--- a/docs/source/gssapi.raw.rst
+++ b/docs/source/gssapi.raw.rst
@@ -25,44 +25,44 @@ Names
     Instead, they are a special form of name specific to
     a given mechanism.
 
-.. automodule:: gssapi.raw.names
+.. autoapimodule:: gssapi.raw.names
     :members:
     :undoc-members:
 
 Credentials
 ~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.creds
+.. autoapimodule:: gssapi.raw.creds
     :members:
     :undoc-members:
 
 Security Contexts
 ~~~~~~~~~~~~~~~~~
 
-.. automodule::  gssapi.raw.sec_contexts
+.. autoapimodule::  gssapi.raw.sec_contexts
     :members:
     :undoc-members:
 
-.. automodule:: gssapi.raw.message
+.. autoapimodule:: gssapi.raw.message
     :members:
     :undoc-members:
 
 Misc
 ~~~~
 
-.. automodule:: gssapi.raw.oids
+.. autoapimodule:: gssapi.raw.oids
     :members:
     :undoc-members:
 
-.. automodule:: gssapi.raw.misc
+.. autoapimodule:: gssapi.raw.misc
     :members:
     :undoc-members:
 
-.. automodule:: gssapi.raw.types
+.. autoapimodule:: gssapi.raw.types
     :members:
     :undoc-members:
 
-.. automodule:: gssapi.raw.chan_bindings
+.. autoapimodule:: gssapi.raw.chan_bindings
     :members:
     :undoc-members:
 
@@ -80,116 +80,116 @@ The following is a list of GSSAPI extensions supported by the low-level API.
 :rfc:`4178` (GSS-API Negotiation Mechanism)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_rfc4178
+.. autoapimodule:: gssapi.raw.ext_rfc4178
     :members:
     :undoc-members:
 
 :rfc:`5587` (GSS-API Extension for Mech Attributes)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_rfc5587
+.. autoapimodule:: gssapi.raw.ext_rfc5587
     :members:
     :undoc-members:
 
 :rfc:`5588` (GSS-API Extension for Storing Delegated Credentials)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_rfc5588
+.. autoapimodule:: gssapi.raw.ext_rfc5588
     :members:
     :undoc-members:
 
 :rfc:`5801` (GSS-API SASL Extensions)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_rfc5801
+.. autoapimodule:: gssapi.raw.ext_rfc5801
     :members:
     :undoc-members:
 
 Credential Store Extensions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_cred_store
+.. autoapimodule:: gssapi.raw.ext_cred_store
     :members:
     :undoc-members:
 
 :rfc:`6680` (GSS-API Naming Extensions)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_rfc6680
-    :members:
-    :undoc-members:
-
-.. automodule:: gssapi.raw.ext_rfc6680_comp_oid
+.. autoapimodule:: gssapi.raw.ext_rfc6680
     :members:
     :undoc-members:
 
 Credentials Import-Export Extensions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_cred_imp_exp
+.. autoapimodule:: gssapi.raw.ext_cred_imp_exp
     :members:
     :undoc-members:
 
 DCE (IOV/AEAD) Extensions
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_dce
+.. autoapimodule:: gssapi.raw.ext_dce
     :members:
     :undoc-members:
 
-.. automodule:: gssapi.raw.ext_dce_aead
-    :members:
-    :undoc-members:
+..
+    gssapi.raw.ext_dce_aead is imported with ext_dce so no need to double up.
+
 
 IOV MIC Extensions
 ~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_iov_mic
+.. autoapimodule:: gssapi.raw.ext_iov_mic
     :members:
     :undoc-members:
 
 Global Grid Forum (GGF) Extensions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_ggf
+.. autoapimodule:: gssapi.raw.ext_ggf
     :members:
     :undoc-members:
 
 Services4User Extensions
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_s4u
+.. autoapimodule:: gssapi.raw.ext_s4u
     :members:
     :undoc-members:
 
 Acquiring Credentials With a Password Extensions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_password
+.. autoapimodule:: gssapi.raw.ext_password
     :members:
     :undoc-members:
 
-.. automodule:: gssapi.raw.ext_password_add
+.. autoapimodule:: gssapi.raw.ext_password_add
     :members:
     :undoc-members:
 
 Kerberos Specific Extensions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_krb5
+.. autoapimodule:: gssapi.raw.ext_krb5
     :members:
     :undoc-members:
 
 Other Extensions
 ~~~~~~~~~~~~~~~~
 
-.. automodule:: gssapi.raw.ext_set_cred_opt
+.. autoapimodule:: gssapi.raw.ext_set_cred_opt
     :members:
     :undoc-members:
 
 Results
 -------
 
+..
+    Use autoapimodule once
+    https://github.com/readthedocs/sphinx-autoapi/issues/323 is resolved.
+
 .. automodule:: gssapi.raw.named_tuples
     :members:
     :undoc-members:
@@ -197,7 +197,7 @@ Results
 Exceptions
 ----------
 
-.. automodule:: gssapi.raw.exceptions
+.. autoapimodule:: gssapi.raw.exceptions
     :members:
     :undoc-members:
     :show-inheritance:
diff --git a/gssapi/_utils.py b/gssapi/_utils.py
index d2da9e81..0a2d8132 100644
--- a/gssapi/_utils.py
+++ b/gssapi/_utils.py
@@ -1,14 +1,18 @@
 import sys
 import types
+import typing as t
 
 import decorator as deco
 
-from typing import Optional
-
 from gssapi.raw.misc import GSSError
 
+if t.TYPE_CHECKING:
+    from gssapi.sec_contexts import SecurityContext
+
 
-def import_gssapi_extension(name):
+def import_gssapi_extension(
+    name: str,
+) -> t.Optional[types.ModuleType]:
     """Import a GSSAPI extension module
 
     This method imports a GSSAPI extension module based
@@ -31,20 +35,10 @@ def import_gssapi_extension(name):
         return None
 
 
-def flag_property(flag):
-    def setter(self, val):
-        if val:
-            self.flags.add(flag)
-        else:
-            self.flags.discard(flag)
-
-    def getter(self):
-        return flag in self.flags
-
-    return property(getter, setter)
-
-
-def inquire_property(name: str, doc: Optional[str] = None):
+def inquire_property(
+    name: str,
+    doc: t.Optional[str] = None
+) -> property:
     """Creates a property based on an inquire result
 
     This method creates a property that calls the
@@ -58,7 +52,7 @@ def inquire_property(name: str, doc: Optional[str] = None):
         property: the created property
     """
 
-    def inquire_property(self):
+    def inquire_property(self: "SecurityContext") -> t.Any:
         if not self._started:
             msg = (f"Cannot read {name} from a security context whose "
                    "establishment has not yet been started.")
@@ -73,7 +67,7 @@ def inquire_property(self):
 _ENCODING = 'UTF-8'
 
 
-def _get_encoding():
+def _get_encoding() -> str:
     """Gets the current encoding used for strings.
 
     This value is used to encode and decode string
@@ -85,7 +79,9 @@ def _get_encoding():
     return _ENCODING
 
 
-def set_encoding(enc):
+def set_encoding(
+    enc: str,
+) -> None:
     """Sets the current encoding used for strings
 
     This value is used to encode and decode string
@@ -99,9 +95,11 @@ def set_encoding(enc):
     _ENCODING = enc
 
 
-def _encode_dict(d):
+def _encode_dict(
+    d: t.Dict[t.Union[bytes, str], t.Union[bytes, str]],
+) -> t.Dict[bytes, bytes]:
     """Encodes any relevant strings in a dict"""
-    def enc(x):
+    def enc(x: t.Union[bytes, str]) -> bytes:
         if isinstance(x, str):
             return x.encode(_ENCODING)
         else:
@@ -112,7 +110,12 @@ def enc(x):
 
 # in case of Python 3, just use exception chaining
 @deco.decorator
-def catch_and_return_token(func, self, *args, **kwargs):
+def catch_and_return_token(
+    func: t.Callable,
+    self: "SecurityContext",
+    *args: t.Any,
+    **kwargs: t.Any,
+) -> t.Optional[bytes]:
     """Optionally defer exceptions and return a token instead
 
     When `__DEFER_STEP_ERRORS__` is set on the implementing class
@@ -127,10 +130,12 @@ def catch_and_return_token(func, self, *args, **kwargs):
     try:
         return func(self, *args, **kwargs)
     except GSSError as e:
-        if e.token is not None and self.__DEFER_STEP_ERRORS__:
+        defer_step_errors = getattr(self, '__DEFER_STEP_ERRORS__', False)
+        if e.token is not None and defer_step_errors:
             self._last_err = e
             # skip the "return func" line above in the traceback
-            self._last_err.__traceback__ = e.__traceback__.tb_next
+            tb = e.__traceback__.tb_next  # type: ignore[union-attr]
+            self._last_err.__traceback__ = tb
 
             return e.token
         else:
@@ -138,7 +143,12 @@ def catch_and_return_token(func, self, *args, **kwargs):
 
 
 @deco.decorator
-def check_last_err(func, self, *args, **kwargs):
+def check_last_err(
+    func: t.Callable,
+    self: "SecurityContext",
+    *args: t.Any,
+    **kwargs: t.Any,
+) -> t.Any:
     """Check and raise deferred errors before running the function
 
     This method checks :python:`_last_err` before running the wrapped
@@ -154,16 +164,6 @@ def check_last_err(func, self, *args, **kwargs):
     else:
         return func(self, *args, **kwargs)
 
-    @deco.decorator
-    def check_last_err(func, self, *args, **kwargs):
-        if self._last_err is not None:
-            try:
-                raise self._last_err
-            finally:
-                self._last_err = None
-        else:
-            return func(self, *args, **kwargs)
-
 
 class CheckLastError(type):
     """Check for a deferred error on all methods
@@ -174,7 +174,12 @@ class CheckLastError(type):
     Additionally, it enabled `__DEFER_STEP_ERRORS__` by default.
     """
 
-    def __new__(cls, name, parents, attrs):
+    def __new__(
+        cls,
+        name: str,
+        parents: t.Tuple[t.Type],
+        attrs: t.Dict[str, t.Any],
+    ) -> "CheckLastError":
         attrs['__DEFER_STEP_ERRORS__'] = True
 
         for attr_name in attrs:
diff --git a/gssapi/_win_config.py b/gssapi/_win_config.py
index 9b765b20..1ef0a581 100644
--- a/gssapi/_win_config.py
+++ b/gssapi/_win_config.py
@@ -19,21 +19,32 @@
 #: Download location for KfW
 KFW_DL = "https://web.mit.edu/KERBEROS/dist"
 
+# Mypy needs to run on both Win and non-Win so the missing attribute will fire
+# on non-Win and Win will fire with unused ignore. Instead just cache the attr
+# by name and use it as needed.
+ADD_DLL_DIR = getattr(os, "add_dll_directory", None)
+CTYPES_WIN_DLL = getattr(ctypes, "WinDLL", ctypes.CDLL)
 
-def kfw_available():
+
+def _add_dll_directory(path: str) -> None:
+    if ADD_DLL_DIR:
+        ADD_DLL_DIR(path)
+
+
+def kfw_available() -> bool:
     """Return if the main GSSAPI DLL for KfW can be loaded"""
     try:  # to load the main GSSAPI DLL
         if sys.maxsize > 2**32:
-            ctypes.WinDLL('gssapi64.dll')
+            CTYPES_WIN_DLL('gssapi64.dll')
         else:
-            ctypes.WinDLL('gssapi32.dll')
+            CTYPES_WIN_DLL('gssapi32.dll')
     except OSError:  # DLL is not in PATH
         return False
     else:  # DLL is in PATH, everything should work
         return True
 
 
-def error_not_found():
+def error_not_found() -> None:
     """Raise an OSError detailing that KfW is missing and how to get it"""
     raise OSError(
         "Could not find KfW installation. Please download and install "
@@ -43,7 +54,7 @@ def error_not_found():
     )
 
 
-def configure_windows():
+def configure_windows() -> None:
     """
     Validate that KfW appears to be installed correctly and add it to the
     DLL directories/PATH if necessary. In the case that it can't be located,
@@ -54,7 +65,7 @@ def configure_windows():
 
     if os.path.exists(KFW_BIN):  # In standard location
         try:  # to use Python 3.8's DLL handling
-            os.add_dll_directory(KFW_BIN)
+            _add_dll_directory(KFW_BIN)
         except AttributeError:  # <3.8, use PATH
             os.environ['PATH'] += os.pathsep + KFW_BIN
         if kfw_available():
@@ -64,7 +75,7 @@ def configure_windows():
     kinit_path = shutil.which('kinit')  # KfW provided binary
     if kinit_path:  # Non-standard install location
         try:  # Most likely >=3.8, otherwise it would have been found already
-            os.add_dll_directory(os.path.dirname(kinit_path))
+            _add_dll_directory(os.path.dirname(kinit_path))
         except AttributeError:  # <3.8, corrupted installation?
             pass
         else:
diff --git a/gssapi/creds.py b/gssapi/creds.py
index 888fea50..0141c648 100644
--- a/gssapi/creds.py
+++ b/gssapi/creds.py
@@ -1,5 +1,9 @@
+import typing as t
+
 from gssapi.raw import creds as rcreds
 from gssapi.raw import named_tuples as tuples
+from gssapi.raw import names as rnames
+from gssapi.raw import oids as roids
 from gssapi._utils import import_gssapi_extension, _encode_dict
 
 from gssapi import names
@@ -26,7 +30,7 @@ class Credentials(rcreds.Creds):
     credentials.
 
     If the `base` argument is used, an existing
-    :class:`~gssapi.raw.creds.Cred` object from the low-level API is
+    :class:`~gssapi.raw.creds.Creds` object from the low-level API is
     converted into a high-level object.
 
     If the `token` argument is used, the credentials
@@ -46,8 +50,18 @@ class Credentials(rcreds.Creds):
 
     __slots__ = ()
 
-    def __new__(cls, base=None, token=None, name=None, lifetime=None,
-                mechs=None, usage='both', store=None):
+    def __new__(
+        cls,
+        base: t.Optional[rcreds.Creds] = None,
+        token: t.Optional[bytes] = None,
+        name: t.Optional[rnames.Name] = None,
+        lifetime: t.Optional[int] = None,
+        mechs: t.Optional[t.Iterable[roids.OID]] = None,
+        usage: str = 'both',
+        store: t.Optional[
+            t.Dict[t.Union[bytes, str], t.Union[bytes, str]]
+        ] = None,
+    ) -> "Credentials":
         # TODO(directxman12): this is missing support for password
         #                     (non-RFC method)
         if base is not None:
@@ -64,35 +78,48 @@ def __new__(cls, base=None, token=None, name=None, lifetime=None,
                               store=store)
             base_creds = res.creds
 
-        return super(Credentials, cls).__new__(cls, base_creds)
+        return t.cast("Credentials",
+                      super(Credentials, cls).__new__(cls, base_creds))
 
     @property
-    def name(self):
+    def name(self) -> rnames.Name:
         """Get the name associated with these credentials"""
-        return self.inquire(name=True, lifetime=False,
-                            usage=False, mechs=False).name
+        return t.cast(rnames.Name,
+                      self.inquire(name=True, lifetime=False, usage=False,
+                                   mechs=False).name)
 
     @property
-    def lifetime(self):
+    def lifetime(self) -> int:
         """Get the remaining lifetime of these credentials"""
-        return self.inquire(name=False, lifetime=True,
-                            usage=False, mechs=False).lifetime
+        return t.cast(int,
+                      self.inquire(name=False, lifetime=True,
+                                   usage=False, mechs=False).lifetime)
 
     @property
-    def mechs(self):
+    def mechs(self) -> t.Set[roids.OID]:
         """Get the mechanisms for these credentials"""
-        return self.inquire(name=False, lifetime=False,
-                            usage=False, mechs=True).mechs
+        return t.cast(t.Set[roids.OID],
+                      self.inquire(name=False, lifetime=False,
+                                   usage=False, mechs=True).mechs)
 
     @property
-    def usage(self):
+    def usage(self) -> str:
         """Get the usage (initiate, accept, or both) of these credentials"""
-        return self.inquire(name=False, lifetime=False,
-                            usage=True, mechs=False).usage
+        return t.cast(str,
+                      self.inquire(name=False, lifetime=False,
+                                   usage=True, mechs=False).usage)
 
     @classmethod
-    def acquire(cls, name=None, lifetime=None, mechs=None, usage='both',
-                store=None):
+    def acquire(
+        cls,
+        name: t.Optional[rnames.Name] = None,
+        lifetime: t.Optional[int] = None,
+        mechs: t.Optional[t.Iterable[roids.OID]] = None,
+        usage: str = 'both',
+        store: t.Optional[
+            t.Dict[t.Union[bytes, str], t.Union[bytes, str]]
+        ] = None,
+    ) -> tuples.AcquireCredResult:
         """Acquire GSSAPI credentials
 
         This method acquires credentials.  If the `store` argument is
@@ -122,7 +149,7 @@ def acquire(cls, name=None, lifetime=None, mechs=None, usage='both',
 
         Returns:
             AcquireCredResult: the acquired credentials and information about
-                them
+            them
 
         Raises:
             ~gssapi.exceptions.BadMechanismError
@@ -141,17 +168,25 @@ def acquire(cls, name=None, lifetime=None, mechs=None, usage='both',
                                           "not have support for manipulating "
                                           "credential stores")
 
-            store = _encode_dict(store)
+            b_store = _encode_dict(store)
 
-            res = rcred_cred_store.acquire_cred_from(store, name,
+            res = rcred_cred_store.acquire_cred_from(b_store, name,
                                                      lifetime, mechs,
                                                      usage)
 
         return tuples.AcquireCredResult(cls(base=res.creds), res.mechs,
                                         res.lifetime)
 
-    def store(self, store=None, usage='both', mech=None,
-              overwrite=False, set_default=False):
+    def store(
+        self,
+        store: t.Optional[
+            t.Dict[t.Union[bytes, str], t.Union[bytes, str]]
+        ] = None,
+        usage: str = 'both',
+        mech: t.Optional[roids.OID] = None,
+        overwrite: bool = False,
+        set_default: bool = False,
+    ) -> tuples.StoreCredResult:
         """Store these credentials into the given store
 
         This method stores the current credentials into the specified
@@ -197,13 +232,18 @@ def store(self, store=None, usage='both', mech=None,
                                           "not have support for manipulating "
                                           "credential stores directly")
 
-            store = _encode_dict(store)
+            b_store = _encode_dict(store)
 
-            return rcred_cred_store.store_cred_into(store, self, usage, mech,
+            return rcred_cred_store.store_cred_into(b_store, self, usage, mech,
                                                     overwrite, set_default)
 
-    def impersonate(self, name=None, lifetime=None, mechs=None,
-                    usage='initiate'):
+    def impersonate(
+        self,
+        name: t.Optional[rnames.Name] = None,
+        lifetime: t.Optional[int] = None,
+        mechs: t.Optional[t.Iterable[roids.OID]] = None,
+        usage: str = 'initiate',
+    ) -> "Credentials":
         """Impersonate a name using the current credentials
 
         This method acquires credentials by impersonating another
@@ -235,7 +275,13 @@ def impersonate(self, name=None, lifetime=None, mechs=None,
 
         return type(self)(base=res.creds)
 
-    def inquire(self, name=True, lifetime=True, usage=True, mechs=True):
+    def inquire(
+        self,
+        name: bool = True,
+        lifetime: bool = True,
+        usage: bool = True,
+        mechs: bool = True,
+    ) -> tuples.InquireCredResult:
         """Inspect these credentials for information
 
         This method inspects these credentials for information about them.
@@ -248,7 +294,7 @@ def inquire(self, name=True, lifetime=True, usage=True, mechs=True):
 
         Returns:
             InquireCredResult: the information about the credentials,
-                with None used when the corresponding argument was False
+            with None used when the corresponding argument was False
 
         Raises:
             ~gssapi.exceptions.MissingCredentialsError
@@ -266,8 +312,14 @@ def inquire(self, name=True, lifetime=True, usage=True, mechs=True):
         return tuples.InquireCredResult(res_name, res.lifetime,
                                         res.usage, res.mechs)
 
-    def inquire_by_mech(self, mech, name=True, init_lifetime=True,
-                        accept_lifetime=True, usage=True):
+    def inquire_by_mech(
+        self,
+        mech: roids.OID,
+        name: bool = True,
+        init_lifetime: bool = True,
+        accept_lifetime: bool = True,
+        usage: bool = True,
+    ) -> tuples.InquireCredByMechResult:
         """Inspect these credentials for per-mechanism information
 
         This method inspects these credentials for per-mechanism information
@@ -285,7 +337,7 @@ def inquire_by_mech(self, mech, name=True, init_lifetime=True,
 
         Returns:
             InquireCredByMechResult: the information about the credentials,
-                with None used when the corresponding argument was False
+            with None used when the corresponding argument was False
         """
 
         res = rcreds.inquire_cred_by_mech(self, mech, name, init_lifetime,
@@ -301,9 +353,18 @@ def inquire_by_mech(self, mech, name=True, init_lifetime=True,
                                               res.accept_lifetime,
                                               res.usage)
 
-    def add(self, name, mech, usage='both',
-            init_lifetime=None, accept_lifetime=None, impersonator=None,
-            store=None):
+    def add(
+        self,
+        name: rnames.Name,
+        mech: roids.OID,
+        usage: str = 'both',
+        init_lifetime: t.Optional[int] = None,
+        accept_lifetime: t.Optional[int] = None,
+        impersonator: t.Optional[rcreds.Creds] = None,
+        store: t.Optional[
+            t.Dict[t.Union[bytes, str], t.Union[bytes, str]]
+        ] = None,
+    ) -> "Credentials":
         """Acquire more credentials to add to the current set
 
         This method works like :meth:`acquire`, except that it adds the
@@ -347,7 +408,7 @@ def add(self, name, mech, usage='both',
 
         Returns:
             Credentials: the credentials set containing the current credentials
-                and the newly acquired ones.
+            and the newly acquired ones.
 
         Raises:
             ~gssapi.exceptions.BadMechanismError
@@ -367,9 +428,9 @@ def add(self, name, mech, usage='both',
                 raise NotImplementedError("Your GSSAPI implementation does "
                                           "not have support for manipulating "
                                           "credential stores")
-            store = _encode_dict(store)
+            b_store = _encode_dict(store)
 
-            res = rcred_cred_store.add_cred_from(store, self, name, mech,
+            res = rcred_cred_store.add_cred_from(b_store, self, name, mech,
                                                  usage, init_lifetime,
                                                  accept_lifetime)
         elif impersonator is not None:
@@ -386,7 +447,7 @@ def add(self, name, mech, usage='both',
 
         return Credentials(res.creds)
 
-    def export(self):
+    def export(self) -> bytes:
         """Export these credentials into a token
 
         This method exports the current credentials to a token that can
@@ -408,6 +469,8 @@ def export(self):
         return rcred_imp_exp.export_cred(self)
 
     # pickle protocol support
-    def __reduce__(self):
+    def __reduce__(
+        self,
+    ) -> t.Tuple[t.Type["Credentials"], t.Tuple[None, bytes]]:
         # the unpickle arguments to new are (base=None, token=self.export())
         return (type(self), (None, self.export()))
diff --git a/gssapi/exceptions.py b/gssapi/exceptions.py
index 332a2bf3..4775bfe1 100644
--- a/gssapi/exceptions.py
+++ b/gssapi/exceptions.py
@@ -1,3 +1,5 @@
+import typing as t
+
 from gssapi.raw.exceptions import *  # noqa
 from gssapi.raw.misc import GSSError  # noqa
 
@@ -15,7 +17,11 @@ class GeneralError(Exception):
     MAJOR_MESSAGE = "General error"
     FMT_STR = "{maj}: {min}."
 
-    def __init__(self, minor_message, **kwargs):
+    def __init__(
+        self,
+        minor_message: str,
+        **kwargs: str,
+    ) -> None:
         maj_str = self.MAJOR_MESSAGE.format(**kwargs)
         err_str = self.FMT_STR.format(maj=maj_str, min=minor_message)
         super(GeneralError, self).__init__(err_str)
@@ -30,7 +36,12 @@ class EncryptionNotUsed(GeneralError):
     """An Error indicating that encryption was requested, but not used"""
     MAJOR_MESSAGE = "Confidentiality was requested, but not used"
 
-    def __init__(self, minor_message, unwrapped_message=None, **kwargs):
+    def __init__(
+        self,
+        minor_message: str,
+        unwrapped_message: t.Optional[bytes] = None,
+        **kwargs: str,
+    ) -> None:
         super(EncryptionNotUsed, self).__init__(minor_message, **kwargs)
 
         self.unwrapped_message = unwrapped_message
diff --git a/gssapi/mechs.py b/gssapi/mechs.py
index f22819da..a6d7d18c 100644
--- a/gssapi/mechs.py
+++ b/gssapi/mechs.py
@@ -1,6 +1,10 @@
-from gssapi.raw import oids as roids
+import typing as t
+
 from gssapi._utils import import_gssapi_extension
+from gssapi.raw import oids as roids
 from gssapi.raw import misc as rmisc
+from gssapi.raw import named_tuples as tuples
+from gssapi.raw import names as rnames
 from gssapi import _utils
 
 rfc5587 = import_gssapi_extension('rfc5587')
@@ -17,39 +21,44 @@ class Mechanism(roids.OID):
     It inherits from the low-level GSSAPI :class:`~gssapi.raw.oids.OID` class,
     and thus can be used with both low-level and high-level API calls.
     """
-    def __new__(cls, cpy=None, elements=None):
-        return super(Mechanism, cls).__new__(cls, cpy, elements)
+    def __new__(
+        cls,
+        cpy: t.Optional[roids.OID] = None,
+        elements: t.Optional[bytes] = None,
+    ) -> "Mechanism":
+        return t.cast("Mechanism",
+                      super(Mechanism, cls).__new__(cls, cpy, elements))
 
     @property
-    def name_types(self):
+    def name_types(self) -> t.Set[roids.OID]:
         """
         Get the set of name types supported by this mechanism.
         """
         return rmisc.inquire_names_for_mech(self)
 
     @property
-    def _saslname(self):
+    def _saslname(self) -> tuples.InquireSASLNameResult:
         if rfc5801 is None:
             raise NotImplementedError("Your GSSAPI implementation does not "
                                       "have support for RFC 5801")
         return rfc5801.inquire_saslname_for_mech(self)
 
     @property
-    def _attrs(self):
+    def _attrs(self) -> tuples.InquireAttrsResult:
         if rfc5587 is None:
             raise NotImplementedError("Your GSSAPI implementation does not "
                                       "have support for RFC 5587")
 
         return rfc5587.inquire_attrs_for_mech(self)
 
-    def __str__(self):
+    def __str__(self) -> str:
         return self._bytes_desc().decode(_utils._get_encoding())
 
-    def __unicode__(self):
+    def __unicode__(self) -> str:
         return self._bytes_desc().decode(_utils._get_encoding())
 
-    def _bytes_desc(self):
-        base = self.dotted_form
+    def _bytes_desc(self) -> bytes:
+        base: t.Union[bytes, str] = self.dotted_form
         if rfc5801 is not None and self._saslname and self._saslname.mech_name:
             base = self._saslname.mech_name
 
@@ -58,7 +67,7 @@ def _bytes_desc(self):
 
         return base
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         """
         Get a name representing the mechanism; always safe to call
         """
@@ -72,7 +81,7 @@ def __repr__(self):
         return base
 
     @property
-    def sasl_name(self):
+    def sasl_name(self) -> str:
         """
         Get the SASL name for the mechanism
 
@@ -81,7 +90,7 @@ def sasl_name(self):
         return self._saslname.sasl_mech_name.decode('UTF-8')
 
     @property
-    def description(self):
+    def description(self) -> str:
         """
         Get the description of the mechanism
 
@@ -90,7 +99,7 @@ def description(self):
         return self._saslname.mech_description.decode('UTF-8')
 
     @property
-    def known_attrs(self):
+    def known_attrs(self) -> t.Set[roids.OID]:
         """
         Get the known attributes of the mechanism; returns a set of OIDs
         ([OID])
@@ -100,7 +109,7 @@ def known_attrs(self):
         return self._attrs.known_mech_attrs
 
     @property
-    def attrs(self):
+    def attrs(self) -> t.Set[roids.OID]:
         """
         Get the attributes of the mechanism; returns a set of OIDs ([OID])
 
@@ -109,14 +118,17 @@ def attrs(self):
         return self._attrs.mech_attrs
 
     @classmethod
-    def all_mechs(cls):
+    def all_mechs(cls) -> t.Iterator["Mechanism"]:
         """
         Get a generator of all mechanisms supported by GSSAPI
         """
         return (cls(mech) for mech in rmisc.indicate_mechs())
 
     @classmethod
-    def from_name(cls, name=None):
+    def from_name(
+        cls,
+        name: rnames.Name,
+    ) -> t.Iterator["Mechanism"]:
         """
         Get a generator of mechanisms that may be able to process the name
 
@@ -132,7 +144,10 @@ def from_name(cls, name=None):
         return (cls(mech) for mech in rmisc.inquire_mechs_for_name(name))
 
     @classmethod
-    def from_sasl_name(cls, name=None):
+    def from_sasl_name(
+        cls,
+        name: t.Optional[t.Union[bytes, str]] = None,
+    ) -> "Mechanism":
         """
         Create a Mechanism from its SASL name
 
@@ -158,8 +173,18 @@ def from_sasl_name(cls, name=None):
         return cls(m)
 
     @classmethod
-    def from_attrs(cls, desired_attrs=None, except_attrs=None,
-                   critical_attrs=None):
+    def from_attrs(
+        cls,
+        desired_attrs: t.Optional[
+            t.Union[roids.OID, t.Iterable[roids.OID]]
+        ] = None,
+        except_attrs: t.Optional[
+            t.Union[roids.OID, t.Iterable[roids.OID]]
+        ] = None,
+        critical_attrs: t.Optional[
+            t.Union[roids.OID, t.Iterable[roids.OID]]
+        ] = None,
+    ) -> t.Iterator["Mechanism"]:
         """
         Get a generator of mechanisms supporting the specified attributes. See
         RFC 5587's :func:`indicate_mechs_by_attrs` for more information.
diff --git a/gssapi/names.py b/gssapi/names.py
index 3cb2ff4b..0f6201c6 100644
--- a/gssapi/names.py
+++ b/gssapi/names.py
@@ -1,11 +1,14 @@
+
+import typing as t
+
 from gssapi.raw import names as rname
 from gssapi.raw import NameType
 from gssapi.raw import named_tuples as tuples
+from gssapi.raw import oids as roids
 from gssapi import _utils
 
 from collections.abc import MutableMapping, Iterable
 
-
 rname_rfc6680 = _utils.import_gssapi_extension('rfc6680')
 rname_rfc6680_comp_oid = _utils.import_gssapi_extension('rfc6680_comp_oid')
 
@@ -35,8 +38,13 @@ class Name(rname.Name):
 
     __slots__ = ('_attr_obj')
 
-    def __new__(cls, base=None, name_type=None, token=None,
-                composite=False):
+    def __new__(
+        cls,
+        base: t.Optional[t.Union[rname.Name, bytes, str]] = None,
+        name_type: t.Optional[roids.OID] = None,
+        token: t.Optional[bytes] = None,
+        composite: bool = False,
+    ) -> "Name":
         if token is not None:
             if composite:
                 if rname_rfc6680 is None:
@@ -67,11 +75,19 @@ def __new__(cls, base=None, name_type=None, token=None,
             if isinstance(base, str):
                 base = base.encode(_utils._get_encoding())
 
-            base_name = rname.import_name(base, name_type)
+            base_name = rname.import_name(
+                base,  # type: ignore[arg-type]
+                name_type)
 
-        return super(Name, cls).__new__(cls, base_name)
+        return t.cast("Name", super(Name, cls).__new__(cls, base_name))
 
-    def __init__(self, base=None, name_type=None, token=None, composite=False):
+    def __init__(
+        self,
+        base: t.Optional[t.Union[rname.Name, bytes, str]] = None,
+        name_type: t.Optional[roids.OID] = None,
+        token: t.Optional[bytes] = None,
+        composite: bool = False,
+    ) -> None:
         """
         The constructor can be used to "import" a name from a human readable
         representation, or from a token, and can also be used to convert a
@@ -96,28 +112,34 @@ def __init__(self, base=None, name_type=None, token=None, composite=False):
             ~gssapi.exceptions.BadMechanismError
         """
 
+        self._attr_obj: t.Optional[_NameAttributeMapping]
+
         if rname_rfc6680 is not None:
             self._attr_obj = _NameAttributeMapping(self)
         else:
             self._attr_obj = None
 
-    def __str__(self):
+    def __str__(self) -> str:
         return bytes(self).decode(_utils._get_encoding())
 
-    def __unicode__(self):
+    def __unicode__(self) -> str:
         # Python 2 -- someone asked for unicode
         return self.__bytes__().decode(_utils._get_encoding())
 
-    def __bytes__(self):
+    def __bytes__(self) -> bytes:
         # Python 3 -- someone asked for bytes
         return rname.display_name(self, name_type=False).name
 
-    def display_as(self, name_type):
+    def display_as(
+        self,
+        name_type: roids.OID,
+    ) -> str:
         """
         Display this name as the given name type.
 
         This method attempts to display the current :class:`Name`
-        using the syntax of the given :class:`NameType`, if possible.
+        using the syntax of the given :class:`~gssapi.raw.types.NameType`, if
+        possible.
 
         Warning:
 
@@ -137,8 +159,8 @@ def display_as(self, name_type):
         :requires-ext:`rfc6680`
 
         Args:
-            name_type (~gssapi.OID): the :class:`NameType` to use to display
-                the given name
+            name_type (~gssapi.OID): the :class:`~gssapi.raw.types.NameType` to
+                use to display the given name
 
         Returns:
             str: the displayed name
@@ -155,11 +177,14 @@ def display_as(self, name_type):
             _utils._get_encoding())
 
     @property
-    def name_type(self):
-        """The :class:`NameType` of this name"""
+    def name_type(self) -> t.Optional[roids.OID]:
+        """The :class:`~gssapi.raw.types.NameType` of this name"""
         return rname.display_name(self, name_type=True).name_type
 
-    def __eq__(self, other):
+    def __eq__(
+        self,
+        other: object,
+    ) -> bool:
         if not isinstance(other, rname.Name):
             # maybe something else can compare this
             # to other classes, but we certainly can't
@@ -167,15 +192,21 @@ def __eq__(self, other):
         else:
             return rname.compare_name(self, other)
 
-    def __ne__(self, other):
+    def __ne__(
+        self,
+        other: object,
+    ) -> bool:
         return not self.__eq__(other)
 
-    def __repr__(self):
+    def __repr__(self) -> str:
         disp_res = rname.display_name(self, name_type=True)
-        return "Name({name}, {name_type})".format(name=disp_res.name,
-                                                  name_type=disp_res.name_type)
+        return "Name({name!r}, {name_type})".format(
+            name=disp_res.name, name_type=disp_res.name_type)
 
-    def export(self, composite=False):
+    def export(
+        self,
+        composite: bool = False,
+    ) -> bytes:
         """Export this name as a token.
 
         This method exports the name into a byte string which can then be
@@ -204,7 +235,10 @@ def export(self, composite=False):
         else:
             return rname.export_name(self)
 
-    def canonicalize(self, mech):
+    def canonicalize(
+        self,
+        mech: roids.OID
+    ) -> "Name":
         """Canonicalize a name with respect to a mechanism.
 
         This method returns a new :class:`Name` that is canonicalized according
@@ -224,13 +258,19 @@ def canonicalize(self, mech):
 
         return type(self)(rname.canonicalize_name(self, mech))
 
-    def __copy__(self):
+    def __copy__(self) -> "Name":
         return type(self)(rname.duplicate_name(self))
 
-    def __deepcopy__(self, memo):
+    def __deepcopy__(
+        self,
+        memo: t.Dict,
+    ) -> "Name":
         return type(self)(rname.duplicate_name(self))
 
-    def _inquire(self, **kwargs):
+    def _inquire(
+        self,
+        **kwargs: t.Any,
+    ) -> tuples.InquireNameResult:
         """Inspect this name for information.
 
         This method inspects the name for information.
@@ -269,20 +309,20 @@ def _inquire(self, **kwargs):
                                           attrs=attrs)
 
     @property
-    def is_mech_name(self):
+    def is_mech_name(self) -> bool:
         """Whether or not this name is a mechanism name
         (:requires-ext:`rfc6680`)
         """
         return self._inquire(mech_name=True).is_mech_name
 
     @property
-    def mech(self):
+    def mech(self) -> roids.OID:
         """The mechanism associated with this name (:requires-ext:`rfc6680`)
         """
         return self._inquire(mech_name=True).mech
 
     @property
-    def attributes(self):
+    def attributes(self) -> t.Optional["_NameAttributeMapping"]:
         """The attributes of this name (:requires-ext:`rfc6680`)
 
         The attributes are presenting in the form of a
@@ -310,31 +350,48 @@ def attributes(self):
 class _NameAttributeMapping(MutableMapping):
 
     """Provides dict-like access to RFC 6680 Name attributes."""
-    def __init__(self, name):
+    def __init__(
+        self,
+        name: Name,
+    ) -> None:
         self._name = name
 
-    def __getitem__(self, key):
+    def __getitem__(
+        self,
+        key: t.Union[bytes, str],
+    ) -> tuples.GetNameAttributeResult:
         if isinstance(key, str):
             key = key.encode(_utils._get_encoding())
 
-        res = rname_rfc6680.get_name_attribute(self._name, key)
-        return tuples.GetNameAttributeResult(frozenset(res.values),
-                                             frozenset(res.display_values),
+        res = rname_rfc6680.get_name_attribute(  # type: ignore[union-attr]
+            self._name, key)
+        res = t.cast(tuples.GetNameAttributeResult, res)
+
+        return tuples.GetNameAttributeResult(list(res.values),
+                                             list(res.display_values),
                                              res.authenticated,
                                              res.complete)
 
-    def __setitem__(self, key, value):
+    def __setitem__(
+        self,
+        key: t.Union[bytes, str],
+        value: t.Union[
+            tuples.GetNameAttributeResult, t.Tuple[bytes, bool], bytes
+        ],
+    ) -> None:
         if isinstance(key, str):
             key = key.encode(_utils._get_encoding())
 
-        rname_rfc6680.delete_name_attribute(self._name, key)
+        rname_rfc6680.delete_name_attribute(  # type: ignore[union-attr]
+            self._name, key)
 
+        attr_value: t.List[bytes]
         if isinstance(value, tuples.GetNameAttributeResult):
             complete = value.complete
-            value = value.values
+            attr_value = value.values
         elif isinstance(value, tuple) and len(value) == 2:
-            complete = value[1]
-            value = value[0]
+            complete = t.cast(bool, value[1])
+            attr_value = [t.cast(bytes, value[0])]
         else:
             complete = False
 
@@ -342,19 +399,23 @@ def __setitem__(self, key, value):
                 not isinstance(value, Iterable)):
             # NB(directxman12): this allows us to easily assign a single
             # value, since that's a common case
-            value = [value]
+            attr_value = [value]
 
-            rname_rfc6680.set_name_attribute(self._name, key, value,
-                                             complete=complete)
+        rname_rfc6680.set_name_attribute(  # type: ignore[union-attr]
+            self._name, key, attr_value, complete=complete)
 
-    def __delitem__(self, key):
+    def __delitem__(
+        self,
+        key: t.Union[bytes, str],
+    ) -> None:
         if isinstance(key, str):
             key = key.encode(_utils._get_encoding())
 
-        rname_rfc6680.delete_name_attribute(self._name, key)
+        rname_rfc6680.delete_name_attribute(  # type: ignore[union-attr]
+            self._name, key)
 
-    def __iter__(self):
+    def __iter__(self) -> t.Iterator[bytes]:
         return iter(self._name._inquire(attrs=True).attrs)
 
-    def __len__(self):
+    def __len__(self) -> int:
         return len(self._name._inquire(attrs=True).attrs)
diff --git a/gssapi/py.typed b/gssapi/py.typed
new file mode 100644
index 00000000..e69de29b
diff --git a/gssapi/raw/_enum_extensions/__init__.py b/gssapi/raw/_enum_extensions/__init__.py
index 8051c50c..ee2c43ee 100644
--- a/gssapi/raw/_enum_extensions/__init__.py
+++ b/gssapi/raw/_enum_extensions/__init__.py
@@ -1,16 +1,27 @@
+import typing as t
+
 from enum import EnumMeta
 
 
-_extra_values = {}
+_extra_values: t.Dict[str, t.Dict[str, t.Any]] = {}
 
 
-def register_value(cl_str, name, value):
+def register_value(
+    cl_str: str,
+    name: str,
+    value: t.Any,
+) -> None:
     _extra_values[cl_str] = _extra_values.get(cl_str, {})
     _extra_values[cl_str][name] = value
 
 
 class ExtendableEnum(EnumMeta):
-    def __new__(metacl, name, bases, classdict):
+    def __new__(
+        metacl,
+        name: str,
+        bases: t.Tuple[t.Type],
+        classdict: t.Dict[str, t.Any],
+    ) -> "ExtendableEnum":
         extra_vals = _extra_values.get(name)
 
         if extra_vals is not None:
diff --git a/gssapi/raw/chan_bindings.pyi b/gssapi/raw/chan_bindings.pyi
new file mode 100644
index 00000000..bd1a2e1b
--- /dev/null
+++ b/gssapi/raw/chan_bindings.pyi
@@ -0,0 +1,30 @@
+import typing as t
+
+
+class ChannelBindings:
+    """GSSAPI Channel Bindings
+
+    This class represents a set of GSSAPI channel bindings.
+
+    Args:
+        initiator_address_type: the initiator address type
+        initiator_address: the initiator address
+        acceptor_address_type:  the acceptor address type
+        acceptor_address: the acceptor address
+        application_data: additional application-specific data
+    """
+
+    initiator_address_type: t.Optional[int]
+    initiator_address: t.Optional[bytes]
+    acceptor_address_type: t.Optional[int]
+    acceptor_address: t.Optional[bytes]
+    application_data: t.Optional[bytes]
+
+    def __init__(
+        self,
+        initiator_address_type: t.Optional[int] = None,
+        initiator_address: t.Optional[bytes] = None,
+        acceptor_address_type: t.Optional[int] = None,
+        acceptor_address: t.Optional[bytes] = None,
+        application_data: t.Optional[bytes] = None,
+    ) -> None: ...
diff --git a/gssapi/raw/chan_bindings.pyx b/gssapi/raw/chan_bindings.pyx
index d66f587a..cb9c6e38 100644
--- a/gssapi/raw/chan_bindings.pyx
+++ b/gssapi/raw/chan_bindings.pyx
@@ -3,11 +3,6 @@ from libc.stdlib cimport calloc, free
 from gssapi.raw.cython_types cimport *
 
 cdef class ChannelBindings:
-    """GSSAPI Channel Bindings
-
-    This class represents a set of GSSAPI channel bindings.
-    """
-
     # defined in pxd file
     # cdef public object initiator_address_type
     # cdef public bytes initiator_address
@@ -20,17 +15,6 @@ cdef class ChannelBindings:
     def __init__(ChannelBindings self, initiator_address_type=None,
                  initiator_address=None, acceptor_address_type=None,
                  acceptor_address=None, application_data=None):
-        """
-        Args:
-            initiator_address_type (~gssapi.AddressType): the initiator address
-                type
-            initiator_address (bytes): the initiator address
-            acceptor_address_type (~gssapi.AddressType):  the acceptor address
-                type
-            acceptor_address (bytes): the acceptor address
-            application_data (bytes): additional application-specific data
-        """
-
         self.initiator_address_type = initiator_address_type
         self.initiator_address = initiator_address
 
diff --git a/gssapi/raw/creds.pyi b/gssapi/raw/creds.pyi
new file mode 100644
index 00000000..19f5e162
--- /dev/null
+++ b/gssapi/raw/creds.pyi
@@ -0,0 +1,186 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.names import Name
+    from gssapi.raw.oids import OID
+    from gssapi.raw.named_tuples import (
+        AcquireCredResult,
+        AddCredResult,
+        InquireCredResult,
+        InquireCredByMechResult,
+    )
+
+
+class Creds:
+    """
+    GSSAPI Credentials
+    """
+
+    def __new__(
+        cls,
+        cpy: t.Optional["Creds"] = None,
+    ) -> "Creds": ...
+
+
+def acquire_cred(
+    name: t.Optional["Name"],
+    lifetime: t.Optional[int] = None,
+    mechs: t.Optional[t.Iterable["OID"]] = None,
+    usage: str = 'both',
+) -> "AcquireCredResult":
+    """Get GSSAPI credentials for the given name and mechanisms.
+
+    This method gets GSSAPI credentials corresponding to the given name
+    and mechanims.  The desired TTL and usage for the the credential may also
+    be specified.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name for which to acquire the
+            credentials (or None for the "no name" functionality)
+        lifetime (int): the lifetime for the credentials (or None for
+            indefinite)
+        mechs (~gssapi.raw.types.MechType): the desired mechanisms for which
+            the credentials should work, or None for the default set
+        usage (str): the usage type for the credentials: may be
+            'initiate', 'accept', or 'both'
+
+    Returns:
+        AcquireCredResult: the resulting credentials, the actual mechanisms
+        with which they may be used, and their actual lifetime (or None for
+        indefinite or not supported)
+
+    Raises:
+        ~gssapi.exceptions.BadMechanismError
+        ~gssapi.exceptions.BadNameTypeError
+        ~gssapi.exceptions.BadNameError
+        ~gssapi.exceptions.ExpiredCredentialsError
+        ~gssapi.exceptions.MissingCredentialsError
+    """
+
+
+def release_cred(
+    creds: Creds,
+) -> None:
+    """
+    release_cred(creds)
+    Release GSSAPI Credentials.
+
+    This method releases GSSAPI credentials.
+
+    Warning:
+        This method is deprecated.  Credentials are
+        automatically freed by Python.
+
+    Args:
+        creds (Creds): the credentials in question
+
+    Raises:
+        ~gssapi.exceptions.MissingCredentialsError
+    """
+
+
+def add_cred(
+    input_cred: Creds,
+    name: "Name",
+    mech: "OID",
+    usage: str = 'initiate',
+    init_lifetime: t.Optional[int] = None,
+    accept_lifetime: t.Optional[int] = None,
+    mutate_input: bool = False,
+) -> "AddCredResult":
+    """Add a credential element to a credential.
+
+    This method can be used to either compose two credentials (i.e., original
+    and new credential), or to add a new element to an existing credential.
+
+    Args:
+        input_cred (Cred): the set of credentials to which to add the new
+            credentials
+        name (~gssapi.raw.names.Name): name of principal to acquire a
+            credential for
+        mech (~gssapi.raw.types.MechType): the desired security mechanism
+            (required).
+        usage (str): usage type for credentials.  Possible values:
+            'initiate' (default), 'accept', 'both' (failsafe).
+        init_lifetime (int): lifetime of credentials for use in initiating
+            security contexts (None for indefinite)
+        accept_lifetime (int): lifetime of credentials for use in accepting
+            security contexts (None for indefinite)
+        mutate_input (bool): whether to mutate the input credentials (True)
+            or produce a new set of credentials (False).  Defaults to False
+
+    Returns:
+        AddCredResult: the actual mechanisms with which the credentials may be
+        used, the actual initiator TTL, and the actual acceptor TTL (None for
+        either indefinite or not supported).  Note that the credentials may
+        be set to None if mutate_input is set to True.
+
+    Raises:
+        ~gssapi.exceptions.BadMechanismError
+        ~gssapi.exceptions.BadNameTypeError
+        ~gssapi.exceptions.BadNameError
+        ~gssapi.exceptions.DuplicateCredentialsElementError
+        ~gssapi.exceptions.ExpiredCredentialsError
+        ~gssapi.exceptions.MissingCredentialsError
+    """
+
+
+def inquire_cred(
+    creds: Creds,
+    name: bool = True,
+    lifetime: bool = True,
+    usage: bool = True,
+    mechs: bool = True,
+) -> "InquireCredResult":
+    """Inspect credentials for information.
+
+    This method inspects a :class:`Creds` object for information.
+
+    Args:
+        creds (Creds): the credentials to inspect
+        name (bool): get the Name associated with the credentials
+        lifetime (bool): get the TTL for the credentials
+        usage (bool): get the usage type of the credentials
+        mechs (bool): the mechanims used with the credentials
+
+    Returns:
+        InquireCredResult: the information about the credentials,
+        with unused fields set to None
+
+    Raises:
+        ~gssapi.exceptions.MissingCredentialsError
+        ~gssapi.exceptions.InvalidCredentialsError
+        ~gssapi.exceptions.ExpiredCredentialsError
+    """
+
+
+def inquire_cred_by_mech(
+    creds: Creds,
+    mech: "OID",
+    name: bool = True,
+    init_lifetime: bool = True,
+    accept_lifetime: bool = True,
+    usage: bool = True,
+) -> "InquireCredByMechResult":
+    """Inspect credentials for mechanism-specific information.
+
+    This method inspects a :class:`Creds` object for information
+    specific to a particular mechanism.  It functions similarly
+    to :func:`inquire_cred`.
+
+    Args:
+        creds (Creds): the credentials to inspect
+        mech (~gssapi.OID): the desired mechanism
+        name (bool): get the Name associated with the credentials
+        init_lifetime (bool): get the initiator TTL for the credentials
+        accept_lifetime (bool): get the acceptor TTL for the credentials
+        usage (bool): get the usage type of the credentials
+
+    Returns:
+        InquireCredByMechResult: the information about the credentials,
+        with unused fields set to None
+
+    Raises:
+        ~gssapi.exceptions.MissingCredentialsError
+        ~gssapi.exceptions.InvalidCredentialsError
+    """
diff --git a/gssapi/raw/creds.pyx b/gssapi/raw/creds.pyx
index caac3ea6..6a01cd02 100644
--- a/gssapi/raw/creds.pyx
+++ b/gssapi/raw/creds.pyx
@@ -56,9 +56,6 @@ cdef extern from "python_gssapi.h":
 
 
 cdef class Creds:
-    """
-    GSSAPI Credentials
-    """
     # defined in pxd
     # cdef gss_cred_id_t raw_creds
 
@@ -81,37 +78,6 @@ cdef class Creds:
 
 
 def acquire_cred(Name name=None, lifetime=None, mechs=None, usage='both'):
-    """
-    acquire_cred(name=None, lifetime=None, mechs=None, usage='both')
-    Get GSSAPI credentials for the given name and mechanisms.
-
-    This method gets GSSAPI credentials corresponding to the given name
-    and mechanims.  The desired TTL and usage for the the credential may also
-    be specified.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name for which to acquire the
-            credentials (or None for the "no name" functionality)
-        lifetime (int): the lifetime for the credentials (or None for
-            indefinite)
-        mechs (~gssapi.MechType): the desired mechanisms for which the
-            credentials should work, or None for the default set
-        usage (str): the usage type for the credentials: may be
-            'initiate', 'accept', or 'both'
-
-    Returns:
-        AcquireCredResult: the resulting credentials, the actual mechanisms
-        with which they may be used, and their actual lifetime (or None for
-        indefinite or not supported)
-
-    Raises:
-        ~gssapi.exceptions.BadMechanismError
-        ~gssapi.exceptions.BadNameTypeError
-        ~gssapi.exceptions.BadNameError
-        ~gssapi.exceptions.ExpiredCredentialsError
-        ~gssapi.exceptions.MissingCredentialsError
-    """
-
     cdef gss_OID_set desired_mechs
     if mechs is not None:
         desired_mechs = c_get_mech_oid_set(mechs)
@@ -162,23 +128,6 @@ def acquire_cred(Name name=None, lifetime=None, mechs=None, usage='both'):
 
 
 def release_cred(Creds creds not None):
-    """
-    release_cred(creds)
-    Release GSSAPI Credentials.
-
-    This method releases GSSAPI credentials.
-
-    Warning:
-        This method is deprecated.  Credentials are
-        automatically freed by Python.
-
-    Args:
-        creds (Creds): the credentials in question
-
-    Raises:
-        ~gssapi.exceptions.MissingCredentialsError
-    """
-
     cdef OM_uint32 maj_stat, min_stat
     maj_stat = gss_release_cred(&min_stat, &creds.raw_creds)
     if maj_stat != GSS_S_COMPLETE:
@@ -189,43 +138,6 @@ def release_cred(Creds creds not None):
 def add_cred(Creds input_cred, Name name not None, OID mech not None,
              usage='initiate', init_lifetime=None,
              accept_lifetime=None, mutate_input=False):
-    """
-    add_cred(input_cred, name, mech, usage='initiate', init_lifetime=None, \
-accept_lifetime=None, mutate_input=False)
-    Add a credential element to a credential.
-
-    This method can be used to either compose two credentials (i.e., original
-    and new credential), or to add a new element to an existing credential.
-
-    Args:
-        input_cred (Cred): the set of credentials to which to add the new
-            credentials
-        name (~gssapi.raw.names.Name): name of principal to acquire a
-            credential for
-        mech (~gssapi.MechType): the desired security mechanism (required).
-        usage (str): usage type for credentials.  Possible values:
-            'initiate' (default), 'accept', 'both' (failsafe).
-        init_lifetime (int): lifetime of credentials for use in initiating
-            security contexts (None for indefinite)
-        accept_lifetime (int): lifetime of credentials for use in accepting
-            security contexts (None for indefinite)
-        mutate_input (bool): whether to mutate the input credentials (True)
-            or produce a new set of credentials (False).  Defaults to False
-
-    Returns:
-        AddCredResult: the actual mechanisms with which the credentials may be
-        used, the actual initiator TTL, and the actual acceptor TTL (None for
-        either indefinite or not supported).  Note that the credentials may
-        be set to None if mutate_input is set to True.
-
-    Raises:
-        ~gssapi.exceptions.BadMechanismError
-        ~gssapi.exceptions.BadNameTypeError
-        ~gssapi.exceptions.BadNameError
-        ~gssapi.exceptions.DuplicateCredentialsElementError
-        ~gssapi.exceptions.ExpiredCredentialsError
-        ~gssapi.exceptions.MissingCredentialsError
-    """
     cdef gss_cred_usage_t c_usage
     if usage == 'initiate':
         c_usage = GSS_C_INITIATE
@@ -280,29 +192,6 @@ accept_lifetime=None, mutate_input=False)
 
 def inquire_cred(Creds creds not None, name=True, lifetime=True, usage=True,
                  mechs=True):
-    """
-    inquire_cred(creds, name=True, lifetime=True, usage=True, mechs=True)
-    Inspect credentials for information.
-
-    This method inspects a :class:`Creds` object for information.
-
-    Args:
-        creds (Creds): the credentials to inspect
-        name (bool): get the Name associated with the credentials
-        lifetime (bool): get the TTL for the credentials
-        usage (bool): get the usage type of the credentials
-        mechs (bool): the mechanims used with the credentials
-
-    Returns:
-        InquireCredResult: the information about the credentials,
-            with unused fields set to None
-
-    Raises:
-        ~gssapi.exceptions.MissingCredentialsError
-        ~gssapi.exceptions.InvalidCredentialsError
-        ~gssapi.exceptions.ExpiredCredentialsError
-    """
-
     # TODO(directxman12): add docs
     cdef gss_name_t res_name
     cdef gss_name_t *res_name_ptr = NULL
@@ -361,32 +250,6 @@ def inquire_cred(Creds creds not None, name=True, lifetime=True, usage=True,
 def inquire_cred_by_mech(Creds creds not None, OID mech not None,
                          name=True, init_lifetime=True,
                          accept_lifetime=True, usage=True):
-    """
-    inquire_cred_by_mech(creds, mech, name=True, init_lifetime=True, \
-accept_lifetime=True, usage=True)
-    Inspect credentials for mechanism-specific information.
-
-    This method inspects a :class:`Creds` object for information
-    specific to a particular mechanism.  It functions similarly
-    to :func:`inquire_cred`.
-
-    Args:
-        creds (Creds): the credentials to inspect
-        mech (~gssapi.OID): the desired mechanism
-        name (bool): get the Name associated with the credentials
-        init_lifetime (bool): get the initiator TTL for the credentials
-        accept_lifetime (bool): get the acceptor TTL for the credentials
-        usage (bool): get the usage type of the credentials
-
-    Returns:
-        InquireCredByMechResult: the information about the credentials,
-            with unused fields set to None
-
-    Raises:
-        ~gssapi.exceptions.MissingCredentialsError
-        ~gssapi.exceptions.InvalidCredentialsError
-    """
-
     # TODO(directxman12): add docs
     cdef gss_name_t res_name
     cdef gss_name_t *res_name_ptr = NULL
diff --git a/gssapi/raw/exceptions.pyi b/gssapi/raw/exceptions.pyi
new file mode 100644
index 00000000..4e2328f2
--- /dev/null
+++ b/gssapi/raw/exceptions.pyi
@@ -0,0 +1,67 @@
+from gssapi.raw.misc import GSSError
+
+class ParameterReadError(GSSError): ...
+
+class ParameterWriteError(GSSError): ...
+
+class MalformedParameterError(GSSError): ...
+
+class BadMechanismError(GSSError): ...
+
+class BadNameError(GSSError): ...
+
+class BadNameTypeError(GSSError): ...
+
+class BadChannelBindingsError(GSSError): ...
+
+class BadStatusError(GSSError): ...
+
+class BadMICError(GSSError): ...
+
+class MissingCredentialsError(GSSError): ...
+
+class MissingContextError(GSSError): ...
+
+class InvalidTokenError(GSSError): ...
+
+class InvalidCredentialsError(GSSError): ...
+
+class ExpiredCredentialsError(GSSError): ...
+
+class ExpiredContextError(GSSError): ...
+
+class BadQoPError(GSSError): ...
+
+class UnauthorizedError(GSSError): ...
+
+class OperationUnavailableError(GSSError): ...
+
+class DuplicateCredentialsElementError(GSSError): ...
+
+class MechanismNameRequiredError(GSSError): ...
+
+class NameReadError(ParameterReadError, BadNameError): ...
+
+class NameTypeReadError(ParameterReadError, BadNameTypeError): ...
+
+class TokenReadError(ParameterReadError, InvalidTokenError): ...
+
+class ContextReadError(ParameterReadError, MissingContextError): ...
+
+class CredentialsReadError(ParameterReadError, MissingCredentialsError): ...
+
+class ContextWriteError(ParameterWriteError, MissingContextError): ...
+
+class CredentialsWriteError(ParameterWriteError, MissingCredentialsError): ...
+
+class SupplementaryError(GSSError): ...
+
+class DuplicateTokenError(SupplementaryError): ...
+
+class ExpiredTokenError(SupplementaryError): ...
+
+class TokenOutOfSequenceError(SupplementaryError): ...
+
+class TokenTooLateError(TokenOutOfSequenceError): ...
+
+class TokenTooEarlyError(TokenOutOfSequenceError): ...
diff --git a/gssapi/raw/ext_cred_imp_exp.pyi b/gssapi/raw/ext_cred_imp_exp.pyi
new file mode 100644
index 00000000..2677e45c
--- /dev/null
+++ b/gssapi/raw/ext_cred_imp_exp.pyi
@@ -0,0 +1,44 @@
+"""Credentials Import/Export Extension"""
+
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+
+
+def export_cred(
+    creds: "Creds",
+) -> bytes:
+    """Export GSSAPI credentials.
+
+    This method exports GSSSAPI credentials into a token
+    which may be transmitted between different processes.
+
+    Args:
+        creds (Creds): the credentials object to be exported
+
+    Returns:
+        bytes: the exported token representing the given credentials object
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def import_cred(
+    token: bytes,
+) -> "Creds":
+    """Import GSSAPI credentials from a token.
+
+    This method imports a credentials object from a token
+    previously exported by :func:`export_cred`.
+
+    Args:
+        token (bytes): the token to import
+
+    Returns:
+        Creds: the imported credentials object
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_cred_imp_exp.pyx b/gssapi/raw/ext_cred_imp_exp.pyx
index aa43cf86..d0561448 100644
--- a/gssapi/raw/ext_cred_imp_exp.pyx
+++ b/gssapi/raw/ext_cred_imp_exp.pyx
@@ -1,4 +1,3 @@
-"""Credentials Import/Export Extension"""
 GSSAPI="BASE"  # This ensures that a full module is generated by Cython
 
 from gssapi.raw.cython_types cimport *
@@ -22,23 +21,6 @@ cdef extern from "python_gssapi_ext.h":
 
 
 def export_cred(Creds creds not None):
-    """
-    export_cred(creds)
-    Export GSSAPI credentials.
-
-    This method exports GSSSAPI credentials into a token
-    which may be transmitted between different processes.
-
-    Args:
-        creds (Creds): the credentials object to be exported
-
-    Returns:
-        bytes: the exported token representing the given credentials object
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     # GSS_C_EMPTY_BUFFER
     cdef gss_buffer_desc exported_creds = gss_buffer_desc(0, NULL)
 
@@ -56,23 +38,6 @@ def export_cred(Creds creds not None):
 
 
 def import_cred(token not None):
-    """
-    import_cred(token)
-    Import GSSAPI credentials from a token.
-
-    This method imports a credentials object from a token
-    previously exported by :func:`export_cred`.
-
-    Args:
-        token (bytes): the token to import
-
-    Returns:
-        Creds: the imported credentials object
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_desc token_buffer = gss_buffer_desc(len(token), token)
 
     cdef gss_cred_id_t creds
diff --git a/gssapi/raw/ext_cred_store.pyi b/gssapi/raw/ext_cred_store.pyi
new file mode 100644
index 00000000..b025f81e
--- /dev/null
+++ b/gssapi/raw/ext_cred_store.pyi
@@ -0,0 +1,136 @@
+"""Credential Store Extension"""
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.named_tuples import AcquireCredResult, StoreCredResult
+    from gssapi.raw.names import Name
+    from gssapi.raw.oids import OID
+
+
+def acquire_cred_from(
+    dict_store: t.Optional[
+        t.Dict[t.Union[bytes, str], t.Union[bytes, str]]
+    ] = None,
+    name: t.Optional["Name"] = None,
+    lifetime: t.Optional[int] = None,
+    mechs: t.Optional[t.Iterable["OID"]] = None,
+    usage: str = 'both',
+) -> "AcquireCredResult":
+    """Acquire credentials from the given store.
+
+    This method acquires credentials from the store specified by the
+    given credential store information.
+
+    The credential store information is a dictionary containing
+    mechanisms-specific keys and values pointing to a credential store
+    or stores.
+
+    Args:
+        store (dict): the credential store information pointing to the
+            credential store from which to acquire the credentials.
+            See :doc:`credstore` for valid values
+        name (~gssapi.raw.names.Name): the name associated with the
+            credentials, or None for the default name
+        lifetime (int): the desired lifetime of the credentials, or None
+            for indefinite
+        mechs (list): the desired mechanisms to be used with these
+            credentials, or None for the default set
+        usage (str): the usage for these credentials -- either 'both',
+            'initiate', or 'accept'
+
+    Returns:
+        AcquireCredResult: the acquired credentials and information about
+        them
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def add_cred_from(
+    dict_store: t.Optional[
+        t.Dict[t.Union[bytes, str], t.Union[bytes, str]]
+    ],
+    input_creds: "Creds",
+    name: "Name",
+    mech: "OID",
+    usage: str = 'both',
+    init_lifetime: t.Optional[int] = None,
+    accept_lifetime: t.Optional[int] = None,
+) -> "AcquireCredResult":
+    """Acquire credentials to add to the current set from the given store.
+
+    This method works like :func:`acquire_cred_from`, except that it
+    adds the acquired credentials for a single mechanism to a copy of
+    the current set, instead of creating a new set for multiple mechanisms.
+    Unlike :func:`~gssapi.raw.creds.acquire_cred`, you cannot pass None for the
+    desired name or mechanism.
+
+    The credential store information is a dictionary containing
+    mechanisms-specific keys and values pointing to a credential store
+    or stores.
+
+    Args:
+        store (dict): the store into which to store the credentials,
+            or None for the default store.
+            See :doc:`credstore` for valid values
+        name (~gssapi.raw.names.Name): the name associated with the credentials
+        mech (~gssapi.OID): the desired mechanism to be used with these
+            credentials
+        usage (str): the usage for these credentials -- either 'both',
+            'initiate', or 'accept'
+        init_lifetime (int): the desired initiate lifetime of the
+            credentials, or None for indefinite
+        accept_lifetime (int): the desired accept lifetime of the
+            credentials, or None for indefinite
+
+    Returns:
+        AcquireCredResult: the new credentials set and information about
+        it
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def store_cred_into(
+    dict_store: t.Optional[
+        t.Dict[t.Union[bytes, str], t.Union[bytes, str]]
+    ],
+    creds: "Creds",
+    usage: str = 'both',
+    mech: t.Optional["OID"] = None,
+    overwrite: bool = False,
+    set_default: bool = False,
+) -> "StoreCredResult":
+    """Store credentials into the given store.
+
+    This method stores the given credentials into the store specified
+    by the given store information.  They may then be retrieved later using
+    :func:`acquire_cred_from` or :func:`add_cred_from`.
+
+    The credential store information is a dictionary containing
+    mechanisms-specific keys and values pointing to a credential store
+    or stores.
+
+    Args:
+        store (dict): the store into which to store the credentials,
+            or None for the default store.
+            See :doc:`credstore` for valid values
+        creds (Creds): the credentials to store
+        usage (str): the usage to store the credentials with -- either
+            'both', 'initiate', or 'accept'
+        mech (~gssapi.OID): the mechansim to associate with the stored
+            credentials
+        overwrite (bool): whether or not to overwrite existing credentials
+            stored with the same name, etc
+        set_default (bool): whether or not to set these credentials as
+            the default credentials for the given store.
+
+    Returns:
+        StoreCredResult: the results of the credential storing operation
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_cred_store.pyx b/gssapi/raw/ext_cred_store.pyx
index 5edd5e65..0b216204 100644
--- a/gssapi/raw/ext_cred_store.pyx
+++ b/gssapi/raw/ext_cred_store.pyx
@@ -1,4 +1,3 @@
-"""Credential Store Extension"""
 GSSAPI="BASE"  # This ensures that a full module is generated by Cython
 
 from libc.string cimport memcmp, memcpy, memset
@@ -104,39 +103,6 @@ cdef void c_free_key_value_set(gss_key_value_set_desc *kvset):
 
 def acquire_cred_from(dict store=None, Name name=None, lifetime=None,
                       mechs=None, usage='both'):
-    """
-    acquire_cred_from(store=None, name=None, lifetime=None, mechs=None, \
-usage='both')
-    Acquire credentials from the given store.
-
-    This method acquires credentials from the store specified by the
-    given credential store information.
-
-    The credential store information is a dictionary containing
-    mechanisms-specific keys and values pointing to a credential store
-    or stores.
-
-    Args:
-        store (dict): the credential store information pointing to the
-            credential store from which to acquire the credentials.
-            See :doc:`credstore` for valid values
-        name (~gssapi.raw.names.Name): the name associated with the
-            credentials, or None for the default name
-        lifetime (int): the desired lifetime of the credentials, or None
-            for indefinite
-        mechs (list): the desired mechanisms to be used with these
-            credentials, or None for the default set
-        usage (str): the usage for these credentials -- either 'both',
-            'initiate', or 'accept'
-
-    Returns:
-        AcquireCredResult: the acquired credentials and information about
-        them
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_OID_set desired_mechs
     if mechs is not None:
         desired_mechs = c_get_mech_oid_set(mechs)
@@ -199,43 +165,6 @@ def add_cred_from(dict store, Creds input_creds,
                   Name name not None, OID mech not None,
                   usage='both', init_lifetime=None,
                   accept_lifetime=None):
-    """
-    add_cred_from(store, input_creds, name, mech, usage='both', \
-init_lifetime=None, accept_lifetime=None)
-    Acquire credentials to add to the current set from the given store.
-
-    This method works like :func:`acquire_cred_from`, except that it
-    adds the acquired credentials for a single mechanism to a copy of
-    the current set, instead of creating a new set for multiple mechanisms.
-    Unlike :func:`acquire_cred`, you cannot pass None for the desired name or
-    mechanism.
-
-    The credential store information is a dictionary containing
-    mechanisms-specific keys and values pointing to a credential store
-    or stores.
-
-    Args:
-        store (dict): the store into which to store the credentials,
-            or None for the default store.
-            See :doc:`credstore` for valid values
-        name (~gssapi.raw.names.Name): the name associated with the credentials
-        mech (~gssapi.OID): the desired mechanism to be used with these
-            credentials
-        usage (str): the usage for these credentials -- either 'both',
-            'initiate', or 'accept'
-        init_lifetime (int): the desired initiate lifetime of the
-            credentials, or None for indefinite
-        accept_lifetime (int): the desired accept lifetime of the
-            credentials, or None for indefinite
-
-    Returns:
-        AcquireCredResult: the new credentials set and information about
-        it
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef OM_uint32 input_initiator_ttl = c_py_ttl_to_c(init_lifetime)
     cdef OM_uint32 input_acceptor_ttl = c_py_ttl_to_c(accept_lifetime)
 
@@ -296,40 +225,6 @@ init_lifetime=None, accept_lifetime=None)
 def store_cred_into(dict store, Creds creds not None,
                     usage='both', OID mech=None, bint overwrite=False,
                     bint set_default=False):
-    """
-    store_cred_into(store, creds, usage='both', mech=None, overwrite=False, \
-set_default=False)
-    Store credentials into the given store.
-
-    This method stores the given credentials into the store specified
-    by the given store information.  They may then be retrieved later using
-    :func:`acquire_cred_from` or :func:`add_cred_from`.
-
-    The credential store information is a dictionary containing
-    mechanisms-specific keys and values pointing to a credential store
-    or stores.
-
-    Args:
-        store (dict): the store into which to store the credentials,
-            or None for the default store.
-            See :doc:`credstore` for valid values
-        creds (Creds): the credentials to store
-        usage (str): the usage to store the credentials with -- either
-            'both', 'initiate', or 'accept'
-        mech (~gssapi.OID): the mechansim to associate with the stored
-            credentials
-        overwrite (bool): whether or not to overwrite existing credentials
-            stored with the same name, etc
-        set_default (bool): whether or not to set these credentials as
-            the default credentials for the given store.
-
-    Returns:
-        StoreCredResult: the results of the credential storing operation
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_OID desired_mech
     if mech is not None:
         desired_mech = &mech.raw_oid
diff --git a/gssapi/raw/ext_dce.pyi b/gssapi/raw/ext_dce.pyi
new file mode 100644
index 00000000..a535df9c
--- /dev/null
+++ b/gssapi/raw/ext_dce.pyi
@@ -0,0 +1,187 @@
+import typing as t
+
+from enum import IntEnum
+
+from gssapi.raw.ext_dce_aead import wrap_aead, unwrap_aead
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.named_tuples import IOVUnwrapResult, WrapResult
+    from gssapi.raw.sec_contexts import SecurityContext
+
+
+class IOVBufferType(IntEnum):
+    """
+    IOV Buffer Types
+
+    This IntEnum represent GSSAPI IOV buffer
+    types to be used with the IOV methods.
+
+    The numbers behind the values correspond directly
+    to their C counterparts.
+    """
+
+    empty = 0 #: GSS_IOV_BUFFER_TYPE_EMPTY
+    data = 1 #: GSS_IOV_BUFFER_TYPE_DATA
+    header = 2 #: GSS_IOV_BUFFER_TYPE_HEADER
+    mech_params = 3 #: GSS_IOV_BUFFER_TYPE_MECH_PARAMS
+    trailer = 7 #: GSS_IOV_BUFFER_TYPE_TRAILER
+    padding = 9 #: GSS_IOV_BUFFER_TYPE_PADDING
+    stream = 10 #: GSS_IOV_BUFFER_TYPE_STREAM
+    sign_only = 11 #: GSS_IOV_BUFFER_TYPE_SIGN_ONLY
+    mic_token = 12 #: GSS_IOV_BUFFER_TYPE_MIC_TOKEN
+
+
+class IOVBuffer(t.NamedTuple):
+    type: IOVBufferType
+    allocate: t.Optional[bool]
+    value: t.Optional[bytes]
+
+
+class IOV:
+    """A GSSAPI IOV"""
+
+    def __init__(
+        self,
+        *args: t.Union[
+            IOVBuffer,
+            t.Tuple[
+                t.Union[IOVBufferType, int],
+                t.Optional[bool],
+                t.Optional[bytes]],
+            t.Tuple[
+                t.Union[IOVBufferType, int],
+                t.Optional[t.Union[bool, bytes]],
+            ],
+            bytes,
+            t.Union[IOVBufferType, int],
+        ],
+        std_layout: bool = True,
+        auto_alloc: bool = True,
+    ) -> None: ...
+
+    def __getitem__(
+        self,
+        ind: int,
+    ) -> IOVBuffer: ...
+
+    def __len__(self) -> int: ...
+
+    def __iter__(self) -> t.Iterator[IOVBuffer]: ...
+
+    def __contains__(
+        self,
+        item: IOVBuffer,
+    ) -> bool: ...
+
+    def __reversed__(self) -> t.Iterator[IOVBuffer]: ...
+
+    def index(
+        self,
+        value: t.Any,
+    ) -> int: ...
+
+    def count(
+        self,
+        value: t.Any,
+    ) -> int: ...
+
+
+def wrap_iov(
+    context: "SecurityContext",
+    message: IOV,
+    confidential: bool = True,
+    qop: t.Optional[int] = None,
+) -> bool:
+    """Wrap/Encrypt an IOV message.
+
+    This method wraps or encrypts an IOV message.  The allocate
+    parameter of the :class:`IOVBuffer` objects in the :class:`IOV`
+    indicates whether or not that particular buffer should be
+    automatically allocated (for use with padding, header, and
+    trailer buffers).
+
+    Warning:
+        This modifies the input :class:`IOV`.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (IOV): an :class:`IOV` containing the message
+        confidential (bool): whether or not to encrypt the miovessage (True),
+            or just wrap it with a MIC (False)
+        qop (int): the desired Quality of Protection
+            (or None for the default QoP)
+
+    Returns:
+        bool: whether or not confidentiality was actually used
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def unwrap_iov(
+    context: "SecurityContext",
+    message: IOV,
+) -> "IOVUnwrapResult":
+    """Unwrap/Decrypt an IOV message.
+
+    This method uwraps or decrypts an IOV message.  The allocate
+    parameter of the :class:`IOVBuffer` objects in the :class:`IOV`
+    indicates whether or not that particular buffer should be
+    automatically allocated (for use with padding, header, and
+    trailer buffers).
+
+    As a special case, you may pass an entire IOV message
+    as a single 'stream'.  In this case, pass a buffer type
+    of :attr:`IOVBufferType.stream` followed by a buffer type of
+    :attr:`IOVBufferType.data`.  The former should contain the
+    entire IOV message, while the latter should be empty.
+
+    Warning:
+        This modifies the input :class:`IOV`.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (IOV): an :class:`IOV` containing the message
+
+    Returns:
+        IOVUnwrapResult: whether or not confidentiality was used,
+        and the QoP used.
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def wrap_iov_length(
+    context: "SecurityContext",
+    message: IOV,
+    confidential: bool = True,
+    qop: t.Optional[int] = None,
+) -> "WrapResult":
+    """Appropriately size padding, trailer, and header IOV buffers.
+
+    This method sets the length values on the IOV buffers.  You
+    should already have data provided for the data (and sign-only)
+    buffer(s) so that padding lengths can be appropriately computed.
+
+    In Python terms, this will result in an appropriately sized
+    `bytes` object consisting of all zeros.
+
+    Warning:
+        This modifies the input :class:`IOV`.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (IOV): an :class:`IOV` containing the message
+
+    Returns:
+        WrapResult: a list of :class:IOVBuffer` objects, and whether or not
+        encryption was actually used
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_dce.pyx b/gssapi/raw/ext_dce.pyx
index 7cc44b25..19476bb9 100644
--- a/gssapi/raw/ext_dce.pyx
+++ b/gssapi/raw/ext_dce.pyx
@@ -71,16 +71,6 @@ cdef extern from "python_gssapi_ext.h":
 
 
 class IOVBufferType(IntEnum, metaclass=ExtendableEnum):
-    """
-    IOV Buffer Types
-
-    This IntEnum represent GSSAPI IOV buffer
-    types to be used with the IOV methods.
-
-    The numbers behind the values correspond directly
-    to their C counterparts.
-    """
-
     empty = GSS_IOV_BUFFER_TYPE_EMPTY
     data = GSS_IOV_BUFFER_TYPE_DATA
     header = GSS_IOV_BUFFER_TYPE_HEADER
@@ -95,7 +85,6 @@ IOVBuffer = namedtuple('IOVBuffer', ['type', 'allocate', 'value'])
 
 
 cdef class IOV:
-    """A GSSAPI IOV"""
     # defined in ext_dce.pxd
 
     # cdef int iov_len
@@ -308,35 +297,6 @@ cdef class IOV:
 
 def wrap_iov(SecurityContext context not None, IOV message not None,
              confidential=True, qop=None):
-    """
-    wrap_iov(context, message, confidential=True, qop=None)
-    Wrap/Encrypt an IOV message.
-
-    This method wraps or encrypts an IOV message.  The allocate
-    parameter of the :class:`IOVBuffer` objects in the :class:`IOV`
-    indicates whether or not that particular buffer should be
-    automatically allocated (for use with padding, header, and
-    trailer buffers).
-
-    Warning:
-        This modifies the input :class:`IOV`.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        message (IOV): an :class:`IOV` containing the message
-        confidential (bool): whether or not to encrypt the message (True),
-            or just wrap it with a MIC (False)
-        qop (int): the desired Quality of Protection
-            (or None for the default QoP)
-
-    Returns:
-        bool: whether or not confidentiality was actually used
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef int conf_req = confidential
     cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT
     cdef int conf_used
@@ -357,38 +317,6 @@ def wrap_iov(SecurityContext context not None, IOV message not None,
 
 
 def unwrap_iov(SecurityContext context not None, IOV message not None):
-    """
-    unwrap_iov(context, message)
-    Unwrap/Decrypt an IOV message.
-
-    This method uwraps or decrypts an IOV message.  The allocate
-    parameter of the :class:`IOVBuffer` objects in the :class:`IOV`
-    indicates whether or not that particular buffer should be
-    automatically allocated (for use with padding, header, and
-    trailer buffers).
-
-    As a special case, you may pass an entire IOV message
-    as a single 'stream'.  In this case, pass a buffer type
-    of :attr:`IOVBufferType.stream` followed by a buffer type of
-    :attr:`IOVBufferType.data`.  The former should contain the
-    entire IOV message, while the latter should be empty.
-
-    Warning:
-        This modifies the input :class:`IOV`.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        message (IOV): an :class:`IOV` containing the message
-
-    Returns:
-        IOVUnwrapResult: whether or not confidentiality was used,
-        and the QoP used.
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef int conf_used
     cdef gss_qop_t qop_used
     cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()
@@ -408,33 +336,6 @@ def unwrap_iov(SecurityContext context not None, IOV message not None):
 
 def wrap_iov_length(SecurityContext context not None, IOV message not None,
                     confidential=True, qop=None):
-    """
-    wrap_iov_length(context, message, confidential=True, qop=None)
-    Appropriately size padding, trailer, and header IOV buffers.
-
-    This method sets the length values on the IOV buffers.  You
-    should already have data provided for the data (and sign-only)
-    buffer(s) so that padding lengths can be appropriately computed.
-
-    In Python terms, this will result in an appropriately sized
-    `bytes` object consisting of all zeros.
-
-    Warning:
-        This modifies the input :class:`IOV`.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        message (IOV): an :class:`IOV` containing the message
-
-    Returns:
-        WrapResult: a list of :class:IOVBuffer` objects, and whether or not
-        encryption was actually used
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef int conf_req = confidential
     cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT
     cdef int conf_used
diff --git a/gssapi/raw/ext_dce_aead.pyi b/gssapi/raw/ext_dce_aead.pyi
new file mode 100644
index 00000000..55310f1e
--- /dev/null
+++ b/gssapi/raw/ext_dce_aead.pyi
@@ -0,0 +1,61 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.named_tuples import WrapResult, UnwrapResult
+    from gssapi.raw.sec_contexts import SecurityContext
+
+
+def wrap_aead(
+    context: "SecurityContext",
+    message: bytes,
+    associated: t.Optional[bytes] = None,
+    confidential: bool = True,
+    qop: t.Optional[int] = None,
+) -> "WrapResult":
+    """Wrap/Encrypt an AEAD message.
+
+    This method takes an input message and associated data,
+    and outputs and AEAD message.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (bytes): the message to wrap or encrypt
+        associated (bytes): associated data to go with the message
+        confidential (bool): whether or not to encrypt the message (True),
+            or just wrap it with a MIC (False)
+        qop (int): the desired Quality of Protection
+            (or None for the default QoP)
+
+    Returns:
+        WrapResult: the wrapped/encrypted total message, and whether or not
+        encryption was actually used
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def unwrap_aead(
+    context: "SecurityContext",
+    message: bytes,
+    associated: t.Optional[bytes] = None,
+) -> "UnwrapResult":
+    """Unwrap/Decrypt an AEAD message.
+
+    This method takes an encrpyted/wrapped AEAD message and some associated
+    data, and returns an unwrapped/decrypted message.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (bytes): the AEAD message to unwrap or decrypt
+        associated (bytes): associated data that goes with the message
+
+    Returns:
+        UnwrapResult: the unwrapped/decrypted message, whether or on
+        encryption was used, and the QoP used
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_dce_aead.pyx b/gssapi/raw/ext_dce_aead.pyx
index 32af479b..2b31dbf4 100644
--- a/gssapi/raw/ext_dce_aead.pyx
+++ b/gssapi/raw/ext_dce_aead.pyx
@@ -23,31 +23,6 @@ cdef extern from "python_gssapi_ext.h":
 
 def wrap_aead(SecurityContext context not None, bytes message not None,
               bytes associated=None, confidential=True, qop=None):
-    """
-    wrap_aead(context, message, associated=None, confidential=True, qop=None)
-    Wrap/Encrypt an AEAD message.
-
-    This method takes an input message and associated data,
-    and outputs and AEAD message.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        message (bytes): the message to wrap or encrypt
-        associated (bytes): associated data to go with the message
-        confidential (bool): whether or not to encrypt the message (True),
-            or just wrap it with a MIC (False)
-        qop (int): the desired Quality of Protection
-            (or None for the default QoP)
-
-    Returns:
-        WrapResult: the wrapped/encrypted total message, and whether or not
-        encryption was actually used
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef int conf_req = confidential
     cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT
     cdef gss_buffer_desc message_buffer = gss_buffer_desc(len(message),
@@ -80,27 +55,6 @@ def wrap_aead(SecurityContext context not None, bytes message not None,
 
 def unwrap_aead(SecurityContext context not None, bytes message not None,
                 bytes associated=None):
-    """
-    unwrap_aead(context, message, associated=None)
-    Unwrap/Decrypt an AEAD message.
-
-    This method takes an encrpyted/wrapped AEAD message and some associated
-    data, and returns an unwrapped/decrypted message.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        message (bytes): the AEAD message to unwrap or decrypt
-        associated (bytes): associated data that goes with the message
-
-    Returns:
-        UnwrapResult: the unwrapped/decrypted message, whether or on
-        encryption was used, and the QoP used
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_desc input_buffer = gss_buffer_desc(len(message), message)
 
     cdef gss_buffer_t assoc_buffer_ptr = GSS_C_NO_BUFFER
diff --git a/gssapi/raw/ext_ggf.pyi b/gssapi/raw/ext_ggf.pyi
new file mode 100644
index 00000000..30436667
--- /dev/null
+++ b/gssapi/raw/ext_ggf.pyi
@@ -0,0 +1,97 @@
+"""
+GGF Extensions
+
+GGF provides extended credential and security context inquiry that allows
+application to retrieve more information about the client's credentials and
+security context. One common use case is to use
+:meth:`inquire_sec_context_by_oid` to retrieve the "session" key that is
+required by the SMB protocol for signing and encrypting a message.
+
+Draft IETF document for these extensions can be found at
+https://tools.ietf.org/html/draft-engert-ggf-gss-extensions-00
+"""
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.oids import OID
+    from gssapi.raw.sec_contexts import SecurityContext
+
+
+def inquire_cred_by_oid(
+    cred_handle: "Creds",
+    desired_aspect: "OID",
+) -> t.List[bytes]:
+    """
+    This method inspects a :class:`~gssapi.raw.creds.Creds` object for
+    information specific to a particular desired aspect as an OID.
+
+    Args:
+        cred_handle (Creds): the Credentials to query
+        desired_aspect (~gssapi.raw.oids.OID): the desired aspect of the
+            Credentials to inquire about.
+
+    Returns:
+        list: A list of zero or more pieces of data (as bytes objects)
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def inquire_sec_context_by_oid(
+    context: "SecurityContext",
+    desired_aspect: "OID",
+) -> t.List[bytes]:
+    """
+    This method inspects a :class:`~gssapi.raw.sec_contexts.SecurityContext`
+    object for information specific to a particular desired aspect as an OID.
+
+    This method can be used with the GSS_KRB5_INQ_SSPI_SESSION_KEY_OID OID to
+    retrieve the required key that is used to derive the SMB/SAMBA signing and
+    encryption keys.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the Security
+            Context to query
+        desired_aspect (~gssapi.raw.oids.OID): the desired aspect of the
+            Security Context to inquire about.
+
+    Returns:
+        list: A list of zero or more pieces of data (as bytes objects)
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def set_sec_context_option(
+    desired_aspect: "OID",
+    context: "SecurityContext",
+    value: t.Optional[bytes] = None,
+) -> None:
+    """
+    This method is used to set a value for a specific OID of a
+    :class:`~gssapi.raw.sec_contexts.SecurityContext` object. The OID and value
+    to pass in depends on the mech the SecurityContext backs.
+
+    An example of how this can be used would be to reset the NTLM crypto engine
+    used in gss-ntlmssp. The OID that controls this value is
+    '1.3.6.1.4.1.7165.655.1.3' and it takes it a byte value that represents
+    an int32 where 1 resets the verifier handle and any other int resets the
+    sender handle.
+
+    Args:
+        desired_aspect (~gssapi.raw.oids.OID): the desired aspect of the
+            Security Context to set the value for.
+        context (~gssapi.raw.sec_contexts.SecurityContext): the Security
+            Context to set, or None to create a new context.
+        value (bytes): the value to set on the desired aspect of the Security
+            Context or None to send GSS_C_EMPTY_BUFFER.
+
+    Returns:
+        ~gssapi.raw.sec_contexts.SecurityContext: The output security context.
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_ggf.pyx b/gssapi/raw/ext_ggf.pyx
index 249631ac..ba689cf8 100644
--- a/gssapi/raw/ext_ggf.pyx
+++ b/gssapi/raw/ext_ggf.pyx
@@ -1,15 +1,3 @@
-"""
-GGF Extensions
-
-GGF provides extended credential and security context inquiry that allows
-application to retrieve more information about the client's credentials and
-security context. One common use case is to use
-:meth:`inquire_sec_context_by_oid` to retrieve the "session" key that is
-required by the SMB protocol for signing and encrypting a message.
-
-Draft IETF document for these extensions can be found at
-https://tools.ietf.org/html/draft-engert-ggf-gss-extensions-00
-"""
 GSSAPI="BASE"  # This ensures that a full module is generated by Cython
 
 from gssapi.raw.cython_types cimport *
@@ -39,24 +27,6 @@ cdef extern from "python_gssapi_ext.h":
 
 def inquire_cred_by_oid(Creds cred_handle not None,
                         OID desired_aspect not None):
-    """
-    inquire_cred_by_oid(cred_handle, desired_aspect)
-
-    This method inspects a :class:`Creds` object for information
-    specific to a particular desired aspect as an OID.
-
-    Args:
-        cred_handle (Creds): the Credentials to query
-        desired_aspect (~gssapi.OID): the desired aspect of the Credentials
-            to inquire about.
-
-    Returns:
-        list: A list of zero or more pieces of data (as bytes objects)
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_set_t *data_set_ptr = NULL
     cdef gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET
     cdef OM_uint32 maj_stat, min_stat
@@ -85,29 +55,6 @@ def inquire_cred_by_oid(Creds cred_handle not None,
 
 def inquire_sec_context_by_oid(SecurityContext context not None,
                                OID desired_aspect not None):
-    """
-    inquire_sec_context_by_oid(context, desired_aspect)
-
-    This method inspects a :class:`SecurityContext` object for information
-    specific to a particular desired aspect as an OID.
-
-    This method can be used with the GSS_KRB5_INQ_SSPI_SESSION_KEY_OID OID to
-    retrieve the required key that is used to derive the SMB/SAMBA signing and
-    encryption keys.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the Security
-            Context to query
-        desired_aspect (~gssapi.OID): the desired aspect of the Security
-            Context to inquire about.
-
-    Returns:
-        list: A list of zero or more pieces of data (as bytes objects)
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_set_t *data_set_ptr = NULL
     cdef gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET
     cdef OM_uint32 maj_stat, min_stat
@@ -137,34 +84,6 @@ def inquire_sec_context_by_oid(SecurityContext context not None,
 def set_sec_context_option(OID desired_aspect not None,
                            SecurityContext context=None,
                            value=None):
-    """
-    set_sec_context_option(desired_aspect, context=None, value=None)
-
-    This method is used to set a value for a specific OID of a
-    :class:`SecurityContext` object. The OID and value to pass in depends on
-    the mech the SecurityContext backs.
-
-    An example of how this can be used would be to reset the NTLM crypto engine
-    used in gss-ntlmssp. The OID that controls this value is
-    '1.3.6.1.4.1.7165.655.1.3' and it takes it a byte value that represents
-    an int32 where 1 resets the verifier handle and any other int resets the
-    sender handle.
-
-    Args:
-        desired_aspect (~gssapi.OID): the desired aspect of the Security
-            Context to set the value for.
-        context (~gssapi.raw.sec_contexts.SecurityContext): the Security
-            Context to set, or None to create a new context.
-        value (bytes): the value to set on the desired aspect of the Security
-            Context or None to send GSS_C_EMPTY_BUFFER.
-
-    Returns:
-        ~gssapi.raw.sec_contexts.SecurityContext: The output security context.
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_desc value_buffer
     if value is not None:
         value_buffer = gss_buffer_desc(len(value), value)
diff --git a/gssapi/raw/ext_iov_mic.pyi b/gssapi/raw/ext_iov_mic.pyi
new file mode 100644
index 00000000..377db63b
--- /dev/null
+++ b/gssapi/raw/ext_iov_mic.pyi
@@ -0,0 +1,90 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.ext_dce import IOV
+    from gssapi.raw.sec_contexts import SecurityContext
+
+
+def get_mic_iov(
+    context: "SecurityContext",
+    message: "IOV",
+    qop: t.Optional[int] = None,
+) -> None:
+    """Generate MIC tokens for the given IOV message.
+
+    This method generates a MIC token for the given IOV message, and places it
+    in the :attr:`~gssapi.raw.ext_dce.IOVBufferType.mic_token` buffer in the
+    IOV. This method operates entirely in-place, and returns nothing.
+
+    Warning:
+        This modifies the input :class:`~gssapi.raw.ext_dce.IOV`.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (IOV): the :class:`~gssapi.raw.ext_dce.IOV` containing the
+            message
+        qop (int): the desired Quality of Protection
+            (or None for the default QoP)
+
+    Returns:
+        None
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def get_mic_iov_length(
+    context: "SecurityContext",
+    message: "IOV",
+    qop: t.Optional[int] = None,
+) -> None:
+    """Allocate space for the MIC buffer in the given IOV message.
+
+    This method allocates space for the MIC token buffer
+    (:attr:`~gssapi.raw.ext_dce.IOVBufferType.mic_token`) in the given IOV
+    message.
+
+    Warning:
+        This modifies the input :class:`~gssapi.raw.ext_dce.IOV`.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (IOV): the :class:`~gssapi.raw.ext_dce.IOV` containing the
+            message
+        qop (int): the desired Quality of Protection
+            (or None for the default QoP)
+
+    Returns:
+        None
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def verify_mic_iov(
+    context: "SecurityContext",
+    message: "IOV",
+    qop: t.Optional[int] = None,
+) -> int:
+    """Verify that the MIC matches the data in the given IOV message.
+
+    This method verifies that the MIC token in the MIC buffer
+    (:attr:`~gssapi.raw.ext_dce.IOVBufferType.mic_token`) match the data
+    buffer(s) in the given IOV method.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (IOV): the :class:`~gssapi.raw.ext_dce.IOV` containing the
+            message
+
+    Returns:
+        int: the QoP used to generate the MIC token
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_iov_mic.pyx b/gssapi/raw/ext_iov_mic.pyx
index 8d31ff5c..f6c5dda1 100644
--- a/gssapi/raw/ext_iov_mic.pyx
+++ b/gssapi/raw/ext_iov_mic.pyx
@@ -33,31 +33,6 @@ IOV.AUTO_ALLOC_BUFFERS.add(IOVBufferType.mic_token)
 
 def get_mic_iov(SecurityContext context not None, IOV message not None,
                 qop=None):
-    """
-    get_mic_iov(context, message, qop=None)
-    Generate MIC tokens for the given IOV message.
-
-    This method generates a MIC token for the given IOV message, and places it
-    in the :attr:`IOVBufferType.mic_token` buffer in the IOV.  This method
-    operates entirely in-place, and returns nothing.
-
-    Warning:
-        This modifies the input :class:`IOV`.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        message (IOV): the :class:`IOV` containing the message
-        qop (int): the desired Quality of Protection
-            (or None for the default QoP)
-
-    Returns:
-        None
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT
 
     cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()
@@ -77,30 +52,6 @@ def get_mic_iov(SecurityContext context not None, IOV message not None,
 
 def get_mic_iov_length(SecurityContext context not None, IOV message not None,
                        qop=None):
-    """
-    get_mic_iov_length(context, message, qop=None)
-    Allocate space for the MIC buffer in the given IOV message.
-
-    This method allocates space for the MIC token buffer
-    (:attr:`IOVBufferType.mic_token`) in the given IOV message.
-
-    Warning:
-        This modifies the input :class:`IOV`.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        message (IOV): the :class:`IOV` containing the message
-        qop (int): the desired Quality of Protection
-            (or None for the default QoP)
-
-    Returns:
-        None
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT
 
     cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()
@@ -120,26 +71,6 @@ def get_mic_iov_length(SecurityContext context not None, IOV message not None,
 
 def verify_mic_iov(SecurityContext context not None, IOV message not None,
                    qop=None):
-    """
-    verify_mic_iov(context, message, qop=None)
-    Verify that the MIC matches the data in the given IOV message.
-
-    This method verifies that the MIC token in the MIC buffer
-    (:attr:`IOVBufferType.mic_token`) match the data buffer(s)
-    in the given IOV method.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        message (IOV): the :class:`IOV` containing the message
-
-    Returns:
-        int: the QoP used to generate the MIC token
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()
 
     cdef gss_qop_t qop_state
diff --git a/gssapi/raw/ext_krb5.pyi b/gssapi/raw/ext_krb5.pyi
new file mode 100644
index 00000000..f109c8b0
--- /dev/null
+++ b/gssapi/raw/ext_krb5.pyi
@@ -0,0 +1,280 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.named_tuples import CfxKeyData, Rfc1964KeyData
+    from gssapi.raw.sec_contexts import SecurityContext
+
+
+class Krb5LucidContext:
+    """
+    The base container returned by :meth:`krb5_export_lucid_sec_context` when
+    an unknown version was requested.
+    """
+
+
+class Krb5LucidContextV1(Krb5LucidContext):
+    """
+    Kerberos context data returned by :meth:`krb5_export_lucid_sec_context`
+    when version 1 was requested.
+    """
+
+    @property
+    def version(self) -> t.Optional[int]:
+        """The structure version number
+
+        Returns:
+            Optional[int]: the structure version number
+        """
+
+    @property
+    def is_initiator(self) -> t.Optional[bool]:
+        """Whether the context was the initiator
+
+        Returns:
+            Optional[bool]: ``True`` when the exported context was the
+            initiator
+        """
+
+    @property
+    def endtime(self) -> t.Optional[int]:
+        """Expiration time of the context
+
+        Returns:
+            Optional[int]: the expiration time of the context
+        """
+
+    @property
+    def send_seq(self) -> t.Optional[int]:
+        """Sender sequence number
+
+        Returns:
+            Optional[int]: the sender sequence number
+        """
+
+    @property
+    def recv_seq(self) -> t.Optional[int]:
+        """Receiver sequence number
+
+        Returns:
+            Optional[int]: the receiver sequence number
+        """
+
+    @property
+    def protocol(self) -> t.Optional[int]:
+        """The protocol number
+
+        If the protocol number is 0 then :attr:`rfc1964_kd` is set and
+        :attr:`cfx_kd` is `None`. If the protocol number is 1 then the opposite
+        is true.
+
+        Protocol 0 refers to RFC1964 and 1 refers to RFC4121.
+
+        Returns:
+            Optional[int]: the protocol number
+        """
+
+    @property
+    def rfc1964_kd(self) -> t.Optional["Rfc1964KeyData"]:
+        """Keydata for protocol 0 (RFC1964)
+
+        This will be set when :attr:`protocol` is ``0``.
+
+        Returns:
+            Optional[Rfc1964KeyData]: the RFC1964 key data
+        """
+
+    @property
+    def cfx_kd(self) -> t.Optional["CfxKeyData"]:
+        """Key data for protocol 1 (RFC4121)
+
+        This will be set when :attr:`protocol` is ``1``.
+
+        Returns:
+            Optional[CfxKeyData]: the RFC4121 key data
+        """
+
+
+def krb5_ccache_name(
+    name: t.Optional[bytes],
+) -> bytes:
+    """Set the default Kerberos Protocol credentials cache name.
+
+    This method sets the default credentials cache name for use by he Kerberos
+    mechanism. The default credentials cache is used by
+    :meth:`~gssapi.raw.creds.acquire_cred` to create a GSS-API credential. It
+    is also used by :meth:`~gssapi.raw.sec_contexts.init_sec_context` when
+    `GSS_C_NO_CREDENTIAL` is specified.
+
+    Note:
+        Heimdal does not return the old name when called. It also does not
+        reset the ccache lookup behaviour when setting to ``None``.
+
+    Note:
+        The return value may not be thread safe.
+
+    Args:
+        name (Optional[bytes]): the name to set as the new thread specific
+            ccache name. Set to ``None`` to revert back to getting the ccache
+            from the config/environment settings.
+
+    Returns:
+        bytes: the old name that was previously set
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def krb5_export_lucid_sec_context(
+    context: "SecurityContext",
+    version: int,
+) -> Krb5LucidContext:
+    """Retuns a non-opaque version of the internal context info.
+
+    Gets information about the Kerberos security context passed in. Currently
+    only version 1 is known and supported by this library.
+
+    Note:
+        The context handle must not be used again by the caller after this
+        call.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        version (int): the output structure version to export.  Currently
+            only 1 is supported.
+
+    Returns:
+        Krb5LucidContext: the non-opaque version context info
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def krb5_extract_authtime_from_sec_context(
+    context: "SecurityContext",
+) -> int:
+    """Get the auth time for the security context.
+
+    Gets the auth time for the established security context.
+
+    Note:
+        Heimdal can only get the authtime on the acceptor security context.
+        MIT is able to get the authtime on both initiators and acceptors.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+
+    Returns:
+        int: the authtime
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def krb5_extract_authz_data_from_sec_context(
+    context: "SecurityContext",
+    ad_type: int,
+) -> bytes:
+    """Extracts Kerberos authorization data.
+
+    Extracts authorization data that may be stored within the context.
+
+    Note:
+        Only operates on acceptor contexts.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        ad_type (int): the type of data to extract
+
+    Returns:
+        bytes: the raw authz data from the sec context
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def krb5_import_cred(
+    cred_handle: "Creds",
+    cache: t.Optional[int] = None,
+    keytab_principal: t.Optional[int] = None,
+    keytab: t.Optional[int] = None,
+) -> None:
+    """Import Krb5 credentials into GSSAPI credential.
+
+    Imports the krb5 credentials (either or both of the keytab and cache) into
+    the GSSAPI credential so it can be used within GSSAPI. The ccache is
+    copied by reference and thus shared, so if the credential is destroyed,
+    all users of cred_handle will fail.
+
+    Args:
+        cred_handle (Creds): the credential handle to import into
+        cache (int): the krb5_ccache address pointer, as an int, to import
+            from
+        keytab_principal (int): the krb5_principal address pointer, as an int,
+            of the credential to import
+        keytab (int): the krb5_keytab address pointer, as an int, of the
+            keytab to import
+
+    Returns:
+        None
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def krb5_get_tkt_flags(
+    context: "SecurityContext",
+) -> int:
+    """Return ticket flags for the kerberos ticket.
+
+    Return the ticket flags for the kerberos ticket received when
+    authenticating the initiator.
+
+    Note:
+        Heimdal can only get the tkt flags on the acceptor security context.
+        MIT is able to get the tkt flags on initators and acceptors.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the security
+            context
+
+    Returns:
+        int: the ticket flags for the received kerberos ticket
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def krb5_set_allowable_enctypes(
+    cred_handle: "Creds",
+    ktypes: t.Iterable[int],
+) -> None:
+    """Limits the keys that can be exported.
+
+    Called by a context initiator after acquiring the creds but before calling
+    :meth:`~gssapi.raw.sec_contexts.init_sec_context` to restrict the set of
+    enctypes which will be negotiated during context establisment to those in
+    the provided list.
+
+    Warning:
+        The cred_handle should not be ``GSS_C_NO_CREDENTIAL``.
+
+    Args:
+        cred_hande (Creds): the credential handle
+        ktypes (List[int]): list of enctypes allowed
+
+    Returns:
+        None
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_krb5.pyx b/gssapi/raw/ext_krb5.pyx
index 3ac36b64..d6134a30 100644
--- a/gssapi/raw/ext_krb5.pyx
+++ b/gssapi/raw/ext_krb5.pyx
@@ -105,10 +105,6 @@ cdef extern from "python_gssapi_krb5.h":
 
 
 cdef class Krb5LucidContext:
-    """
-    The base container returned by :meth:`krb5_export_lucid_sec_context` when
-    an unknown version was requested.
-    """
     # defined in pxd
     # cdef void *raw_ctx
 
@@ -124,18 +120,9 @@ cdef class Krb5LucidContext:
 
 
 cdef class Krb5LucidContextV1(Krb5LucidContext):
-    """
-    Kerberos context data returned by :meth:`krb5_export_lucid_sec_context`
-    when version 1 was requested.
-    """
 
     @property
     def version(self) -> typing.Optional[int]:
-        """The structure version number
-
-        Returns:
-            Optional[int]: the structure version number
-        """
         cdef gss_krb5_lucid_context_v1_t *ctx = NULL
 
         if self.raw_ctx:
@@ -144,12 +131,6 @@ cdef class Krb5LucidContextV1(Krb5LucidContext):
 
     @property
     def is_initiator(self) -> typing.Optional[bool]:
-        """Whether the context was the initiator
-
-        Returns:
-            Optional[bool]: ``True`` when the exported context was the
-            initiator
-        """
         cdef gss_krb5_lucid_context_v1_t *ctx = NULL
 
         if self.raw_ctx:
@@ -158,11 +139,6 @@ cdef class Krb5LucidContextV1(Krb5LucidContext):
 
     @property
     def endtime(self) -> typing.Optional[int]:
-        """Expiration time of the context
-
-        Returns:
-            Optional[int]: the expiration time of the context
-        """
         cdef gss_krb5_lucid_context_v1_t *ctx = NULL
 
         if self.raw_ctx:
@@ -171,11 +147,6 @@ cdef class Krb5LucidContextV1(Krb5LucidContext):
 
     @property
     def send_seq(self) -> typing.Optional[int]:
-        """Sender sequence number
-
-        Returns:
-            Optional[int]: the sender sequence number
-        """
         cdef gss_krb5_lucid_context_v1_t *ctx = NULL
 
         if self.raw_ctx:
@@ -184,11 +155,6 @@ cdef class Krb5LucidContextV1(Krb5LucidContext):
 
     @property
     def recv_seq(self) -> typing.Optional[int]:
-        """Receiver sequence number
-
-        Returns:
-            Optional[int]: the receiver sequence number
-        """
         cdef gss_krb5_lucid_context_v1_t *ctx = NULL
 
         if self.raw_ctx:
@@ -197,17 +163,6 @@ cdef class Krb5LucidContextV1(Krb5LucidContext):
 
     @property
     def protocol(self) -> typing.Optional[int]:
-        """The protocol number
-
-        If the protocol number is 0 then :attr:`rfc1964_kd` is set and
-        :attr:`cfx_kd` is `None`. If the protocol number is 1 then the opposite
-        is true.
-
-        Protocol 0 refers to RFC1964 and 1 refers to RFC4121.
-
-        Returns:
-            Optional[int]: the protocol number
-        """
         cdef gss_krb5_lucid_context_v1_t *ctx = NULL
 
         if self.raw_ctx:
@@ -216,13 +171,6 @@ cdef class Krb5LucidContextV1(Krb5LucidContext):
 
     @property
     def rfc1964_kd(self) -> typing.Optional[Rfc1964KeyData]:
-        """Keydata for protocol 0 (RFC1964)
-
-        This will be set when :attr:`protocol` is ``0``.
-
-        Returns:
-            Optional[Rfc1964KeyData]: the RFC1964 key data
-        """
         cdef gss_krb5_lucid_context_v1_t *ctx = NULL
 
         if self.raw_ctx != NULL and self.protocol == 0:
@@ -235,13 +183,6 @@ cdef class Krb5LucidContextV1(Krb5LucidContext):
 
     @property
     def cfx_kd(self) -> typing.Optional[CfxKeyData]:
-        """Key data for protocol 1 (RFC4121)
-
-        This will be set when :attr:`protocol` is ``1``.
-
-        Returns:
-            Optional[CfxKeyData]: the RFC4121 key data
-        """
         cdef gss_krb5_lucid_context_v1_t *ctx = NULL
 
         if self.raw_ctx != NULL and self.protocol == 1:
@@ -267,34 +208,6 @@ gsstypes.NameType.krb5_nt_principal_name = c_make_oid(
 
 
 def krb5_ccache_name(const unsigned char[:] name):
-    """
-    krb5_ccache_name(name)
-    Set the default Kerberos Protocol credentials cache name.
-
-    This method sets the default credentials cache name for use by he Kerberos
-    mechanism. The default credentials cache is used by
-    :meth:`~gssapi.raw.creds.acquire_cred` to create a GSS-API credential. It
-    is also used by :meth:`~gssapi.raw.sec_contexts.init_sec_context` when
-    `GSS_C_NO_CREDENTIAL` is specified.
-
-    Note:
-        Heimdal does not return the old name when called. It also does not
-        reset the ccache lookup behaviour when setting to ``None``.
-
-    Note:
-        The return value may not be thread safe.
-
-    Args:
-        name (Optional[bytes]): the name to set as the new thread specific
-            ccache name. Set to ``None`` to revert back to getting the ccache
-            from the config/environment settings.
-
-    Returns:
-        bytes: the old name that was previously set
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     cdef const char *name_ptr = NULL
     if name is not None and len(name):
         name_ptr = &name[0]
@@ -317,29 +230,6 @@ def krb5_ccache_name(const unsigned char[:] name):
 
 def krb5_export_lucid_sec_context(SecurityContext context not None,
                                   OM_uint32 version):
-    """
-    krb5_export_lucid_sec_context(context, version)
-    Retuns a non-opaque version of the internal context info.
-
-    Gets information about the Kerberos security context passed in. Currently
-    only version 1 is known and supported by this library.
-
-    Note:
-        The context handle must not be used again by the caller after this
-        call.
-
-    Args:
-        context ((~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        version (int): the output structure version to export.  Currently
-            only 1 is supported.
-
-    Returns:
-        Krb5LucidContext: the non-opaque version context info
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     info = {
         1: Krb5LucidContextV1,
     }.get(version, Krb5LucidContext)()
@@ -358,26 +248,6 @@ def krb5_export_lucid_sec_context(SecurityContext context not None,
 
 
 def krb5_extract_authtime_from_sec_context(SecurityContext context not None):
-    """
-    krb5_extract_authtime_from_sec_context(context)
-    Get the auth time for the security context.
-
-    Gets the auth time for the established security context.
-
-    Note:
-        Heimdal can only get the authtime on the acceptor security context.
-        MIT is able to get the authtime on both initiators and acceptors.
-
-    Args:
-        context ((~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-
-    Returns:
-        int: the authtime
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     # In Heimdal, authtime is time_t which is either a 4 or 8 byte int.  By
     # passing in a uint64_t reference, there should be enough space for GSSAPI
     # to store the data in either situation. Coming back to Python it will be
@@ -398,26 +268,6 @@ def krb5_extract_authtime_from_sec_context(SecurityContext context not None):
 
 def krb5_extract_authz_data_from_sec_context(SecurityContext context not None,
                                              ad_type):
-    """
-    krb5_extract_authz_data_from_sec_context(context, ad_type)
-    Extracts Kerberos authorization data.
-
-    Extracts authorization data that may be stored within the context.
-
-    Note:
-        Only operates on acceptor contexts.
-
-    Args:
-        context ((~gssapi.raw.sec_contexts.SecurityContext): the current
-            security context
-        ad_type (int): the type of data to extract
-
-    Returns:
-        bytes: the raw authz data from the sec context
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     # GSS_C_EMPTY_BUFFER
     cdef gss_buffer_desc ad_data = gss_buffer_desc(0, NULL)
     cdef int ad_type_val = ad_type
@@ -441,31 +291,6 @@ def krb5_extract_authz_data_from_sec_context(SecurityContext context not None,
 
 def krb5_import_cred(Creds cred_handle not None, cache=None,
                      keytab_principal=None, keytab=None):
-    """
-    krb5_import_cred(cred_handle, cache=None, keytab_principal=None, \
-    keytab=None)
-    Import Krb5 credentials into GSSAPI credential.
-
-    Imports the krb5 credentials (either or both of the keytab and cache) into
-    the GSSAPI credential so it can be used within GSSAPI. The ccache is
-    copied by reference and thus shared, so if the credential is destroyed,
-    all users of cred_handle will fail.
-
-    Args:
-        cred_handle (Creds): the credential handle to import into
-        cache (int): the krb5_ccache address pointer, as an int, to import
-            from
-        keytab_principal (int): the krb5_principal address pointer, as an int,
-            of the credential to import
-        keytab (int): the krb5_keytab address pointer, as an int, of the
-            keytab to import
-
-    Returns:
-        None
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     cdef void *cache_ptr = NULL
     if cache is not None and cache:
         cache_ptr = (cache)
@@ -491,27 +316,6 @@ def krb5_import_cred(Creds cred_handle not None, cache=None,
 
 
 def krb5_get_tkt_flags(SecurityContext context not None):
-    """
-    krb5_get_tkt_flags(context)
-    Return ticket flags for the kerberos ticket.
-
-    Return the ticket flags for the kerberos ticket received when
-    authenticating the initiator.
-
-    Note:
-        Heimdal can only get the tkt flags on the acceptor security context.
-        MIT is able to get the tkt flags on initators and acceptors.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the security
-            context
-
-    Returns:
-        int: the ticket flags for the received kerberos ticket
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     cdef OM_uint32 maj_stat, min_stat
     cdef uint32_t ticket_flags = 0
 
@@ -527,28 +331,6 @@ def krb5_get_tkt_flags(SecurityContext context not None):
 
 def krb5_set_allowable_enctypes(Creds cred_handle not None,
                                 ktypes):
-    """
-    krb5_set_allowable_enctypes(cred_handle, ktypes)
-    Limits the keys that can be exported.
-
-    Called by a context initiator after acquiring the creds but before calling
-    :meth:`~gssapi.raw.sec_contexts.init_sec_context` to restrict the set of
-    enctypes which will be negotiated during context establisment to those in
-    the provided list.
-
-    Warning:
-        The cred_handle should not be ``GSS_C_NO_CREDENTIAL``.
-
-    Args:
-        cred_hande (Creds): the credential handle
-        ktypes (List[int]): list of enctypes allowed
-
-    Returns:
-        None
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     cdef OM_uint32 maj_stat, min_stat
 
     # This shouldn't ever happen but it's here to satisfy compiler warnings
diff --git a/gssapi/raw/ext_password.pyi b/gssapi/raw/ext_password.pyi
new file mode 100644
index 00000000..c5e101c2
--- /dev/null
+++ b/gssapi/raw/ext_password.pyi
@@ -0,0 +1,41 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.named_tuples import AcquireCredResult
+    from gssapi.raw.names import Name
+    from gssapi.raw.oids import OID
+
+
+def acquire_cred_with_password(
+    name: "Name",
+    password: bytes,
+    lifetime: t.Optional[int] = None,
+    mechs: t.Optional[t.Iterable["OID"]] = None,
+    usage: str = 'initiate',
+) -> "AcquireCredResult":
+    """Acquire credentials through provided password.
+
+    This function is originally from Solaris and is not documented by either
+    MIT or Heimdal.
+
+    In general, it functions similarly to
+    :func:`~gssapi.raw.creds.acquire_cred`.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name to acquire credentials for
+        password (bytes): the password used to acquire credentialss with
+        lifetime (int): the lifetime for the credentials (or None for
+            indefinite)
+        mechs (~gssapi.raw.types.MechType): the desired mechanisms for which
+            the credentials should work (or None for the default set)
+        usage (str): usage type for credentials.  Possible values:
+            'initiate' (default), 'accept', 'both' (failsafe).
+
+    Returns:
+        AcquireCredResult: the resulting credentials, the actual mechanisms
+        with which they may be used, and their actual lifetime (or None for
+        indefinite or not supported)
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_password.pyx b/gssapi/raw/ext_password.pyx
index 4f1ed81e..33b2bae1 100644
--- a/gssapi/raw/ext_password.pyx
+++ b/gssapi/raw/ext_password.pyx
@@ -29,35 +29,6 @@ cdef extern from "python_gssapi_ext.h":
 
 def acquire_cred_with_password(Name name not None, password not None,
                                lifetime=None, mechs=None, usage="initiate"):
-    """
-    acquire_cred_with_password(name, password, lifetime=None, mechs=None, \
-usage="initiate")
-    Acquire credentials through provided password.
-
-    This function is originally from Solaris and is not documented by either
-    MIT or Heimdal.
-
-    In general, it functions similarly to :func:`acquire_cred`.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name to acquire credentials for
-        password (bytes): the password used to acquire credentialss with
-        lifetime (int): the lifetime for the credentials (or None for
-            indefinite)
-        mechs (~gssapi.MechType): the desired mechanisms for which the
-            credentials should work (or None for the default set)
-        usage (str): usage type for credentials.  Possible values:
-            'initiate' (default), 'accept', 'both' (failsafe).
-
-    Returns:
-        AcquireCredResult: the resulting credentials, the actual mechanisms
-        with which they may be used, and their actual lifetime (or None for
-        indefinite or not supported)
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_desc password_buffer = gss_buffer_desc(len(password),
                                                            password)
 
diff --git a/gssapi/raw/ext_password_add.pyi b/gssapi/raw/ext_password_add.pyi
new file mode 100644
index 00000000..32b270b3
--- /dev/null
+++ b/gssapi/raw/ext_password_add.pyi
@@ -0,0 +1,48 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.named_tuples import AddCredResult
+    from gssapi.raw.names import Name
+    from gssapi.raw.oids import OID
+
+
+def add_cred_with_password(
+    input_cred: "Creds",
+    name: "Name",
+    mech: "OID",
+    password: bytes,
+    usage: str = 'initiate',
+    init_lifetime: t.Optional[int] = None,
+    accept_lifetime: t.Optional[int] = None,
+) -> "AddCredResult":
+    """Add a credential-element to a credential using provided password.
+
+    This function is originally from Solaris and is not documented by either
+    MIT or Heimdal.
+
+    In general, it functions similarly to :func:`~gssapi.raw.creds.add_cred`.
+
+    Args:
+        input_cred (~gssapi.raw.creds.Creds): the credentials to add to
+        name (~gssapi.raw.names.Name): the name to acquire credentials for
+        mech (~gssapi.raw.types.MechType): the desired mechanism.  Note that
+            this is both singular and required
+        password (bytes): the password used to acquire credentialss with
+        usage (str): the usage type for the credentials: may be
+            'initiate', 'accept', or 'both'
+        init_lifetime (int): the lifetime for the credentials to remain valid
+            when using them to initiate security contexts (or None for
+            indefinite)
+        accept_lifetime (int): the lifetime for the credentials to remain
+            valid when using them to accept security contexts (or None for
+            indefinite)
+
+    Returns:
+        AddCredResult: the actual mechanisms with which the credentials may be
+        used, the actual initiator TTL, and the actual acceptor TTL (the TTLs
+        may be None for indefinite or not supported)
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_password_add.pyx b/gssapi/raw/ext_password_add.pyx
index 5a142e76..9db07889 100644
--- a/gssapi/raw/ext_password_add.pyx
+++ b/gssapi/raw/ext_password_add.pyx
@@ -35,41 +35,6 @@ def add_cred_with_password(Creds input_cred not None, Name name not None,
                            OID mech not None, password not None,
                            usage="initiate", init_lifetime=None,
                            accept_lifetime=None):
-
-    """
-    add_cred_with_password(input_cred, name, mech, password, \
-usage='initiate', init_lifetime=None, accept_lifetime=None)
-    Add a credential-element to a credential using provided password.
-
-    This function is originally from Solaris and is not documented by either
-    MIT or Heimdal.
-
-    In general, it functions similarly to :func:`add_cred`.
-
-    Args:
-        input_cred (Creds): the credentials to add to
-        name (~gssapi.raw.names.Name): the name to acquire credentials for
-        mech (~gssapi.MechType): the desired mechanism.  Note that this is both
-            singular and required
-        password (bytes): the password used to acquire credentialss with
-        usage (str): the usage type for the credentials: may be
-            'initiate', 'accept', or 'both'
-        init_lifetime (int): the lifetime for the credentials to remain valid
-            when using them to initiate security contexts (or None for
-            indefinite)
-        accept_lifetime (int): the lifetime for the credentials to remain
-            valid when using them to accept security contexts (or None for
-            indefinite)
-
-    Returns:
-        AddCredResult: the actual mechanisms with which the credentials may be
-        used, the actual initiator TTL, and the actual acceptor TTL (the TTLs
-        may be None for indefinite or not supported)
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_desc password_buffer = gss_buffer_desc(len(password),
                                                            password)
 
diff --git a/gssapi/raw/ext_rfc4178.pyi b/gssapi/raw/ext_rfc4178.pyi
new file mode 100644
index 00000000..6826bc1e
--- /dev/null
+++ b/gssapi/raw/ext_rfc4178.pyi
@@ -0,0 +1,27 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.oids import OID
+
+
+def set_neg_mechs(
+    cred_handle: "Creds",
+    mech_set: t.Iterable["OID"],
+) -> None:
+    """
+    Specify the set of security mechanisms that may be negotiated with
+    the credential identified by cred_handle.
+    If more than one mechanism is specified in mech_set, the order in
+    which those mechanisms are specified implies a relative preference.
+
+    Args:
+        cred_handle (Creds): credentials to set negotiable mechanisms for
+        mech_set (~gssapi.raw.types.MechType): negotiable mechanisms to be set
+
+    Returns:
+        None
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_rfc4178.pyx b/gssapi/raw/ext_rfc4178.pyx
index 407d1f3d..44db2f8f 100644
--- a/gssapi/raw/ext_rfc4178.pyx
+++ b/gssapi/raw/ext_rfc4178.pyx
@@ -14,25 +14,6 @@ cdef extern from "python_gssapi_ext.h":
 
 
 def set_neg_mechs(Creds cred_handle not None, mech_set not None):
-    """
-    set_neg_mechs(cred_handle not None, mech_set not None)
-
-    Specify the set of security mechanisms that may be negotiated with
-    the credential identified by cred_handle.
-    If more than one mechanism is specified in mech_set, the order in
-    which those mechanisms are specified implies a relative preference.
-
-    Args:
-        cred_handle (Creds): credentials to set negotiable mechanisms for
-        mech_set (~gssapi.MechType): negotiable mechanisms to be set
-
-    Returns:
-        None
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_OID_set negotiable_mechs = c_get_mech_oid_set(mech_set)
 
     cdef OM_uint32 maj_stat, min_stat
diff --git a/gssapi/raw/ext_rfc5587.pyi b/gssapi/raw/ext_rfc5587.pyi
new file mode 100644
index 00000000..b1a6ee31
--- /dev/null
+++ b/gssapi/raw/ext_rfc5587.pyi
@@ -0,0 +1,63 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.named_tuples import InquireAttrsResult, DisplayAttrResult
+    from gssapi.raw.oids import OID
+
+
+def indicate_mechs_by_attrs(
+    desired_mech_attrs: t.Optional[t.Iterable["OID"]] = None,
+    except_mech_attrs: t.Optional[t.Iterable["OID"]] = None,
+    critical_mech_attrs: t.Optional[t.Iterable["OID"]] = None,
+) -> t.Set["OID"]:
+    """Get a set of mechanisms that have the specified attributes.
+
+    Args:
+        desired_mech_attrs (~gssapi.OID): Attributes that the output mechs MUST
+            offer
+        except_mech_attrs (~gssapi.OID): Attributes that the output mechs MUST
+            NOT offer
+        critical_mech_attrs (~gssapi.OID): Attributes that the output mechs
+            MUST understand and offer
+
+    Returns:
+        ~gssapi.MechType: a set of mechs which satisfy the given criteria
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def inquire_attrs_for_mech(
+    mech: "OID",
+) -> "InquireAttrsResult":
+    """Gets the set of attrs supported and known by a mechanism.
+
+    Args:
+        mech (~gssapi.raw.types.MechType): Mechanism to inquire about
+
+    Returns:
+        InquireAttrsResult: the results of inquiry; a mech's attributes and
+        known attributes
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def display_mech_attr(
+    attr: "OID",
+) -> "DisplayAttrResult":
+    """Returns information about attributes in human readable form.
+
+    Args:
+        attr (~gssapi.OID): Mechanism attribute to retrive names and
+            descriptions of
+
+    Returns:
+        DisplayAttrResult: the results of displaying the attribute; mech name,
+        short description, and long description.
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_rfc5587.pyx b/gssapi/raw/ext_rfc5587.pyx
index ca669023..fbedf6ed 100644
--- a/gssapi/raw/ext_rfc5587.pyx
+++ b/gssapi/raw/ext_rfc5587.pyx
@@ -32,25 +32,6 @@ cdef extern from "python_gssapi_ext.h":
 
 def indicate_mechs_by_attrs(desired_mech_attrs=None, except_mech_attrs=None,
                             critical_mech_attrs=None):
-    """
-    indicate_mechs_by_attrs(desired_mech_attrs=None, except_mech_attrs=None,\
-    critical_mech_attrs=None)
-    Get a set of mechanisms that have the specified attributes.
-
-    Args:
-        desired_mech_attrs (~gssapi.OID): Attributes that the output mechs MUST
-            offer
-        except_mech_attrs (~gssapi.OID): Attributes that the output mechs MUST
-            NOT offer
-        critical_mech_attrs (~gssapi.OID): Attributes that the output mechs
-            MUST understand and offer
-
-    Returns:
-        ~gssapi.MechType: a set of mechs which satisfy the given criteria
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     cdef OM_uint32 maj_stat, min_stat
     cdef gss_OID_set desired_attrs = GSS_C_NO_OID_SET
     cdef gss_OID_set except_attrs = GSS_C_NO_OID_SET
@@ -78,20 +59,6 @@ def indicate_mechs_by_attrs(desired_mech_attrs=None, except_mech_attrs=None,
 
 
 def inquire_attrs_for_mech(OID mech):
-    """
-    inquire_attrs_for_mech(mech)
-    Gets the set of attrs supported and known by a mechanism.
-
-    Args:
-        mech (~gssapi.MechType): Mechanism to inquire about
-
-    Returns:
-        InquireAttrsResult: the results of inquiry; a mech's attributes and
-        known attributes
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     cdef OM_uint32 maj_stat, min_stat
     cdef gss_OID m = GSS_C_NO_OID
     cdef gss_OID_set mech_attrs = GSS_C_NO_OID_SET
@@ -112,21 +79,6 @@ def inquire_attrs_for_mech(OID mech):
 
 
 def display_mech_attr(OID attr):
-    """
-    display_mech_attrs(attr)
-    Returns information about attributes in human readable form.
-
-    Args:
-        attr (~gssapi.OID): Mechanism attribute to retrive names and
-            descriptions of
-
-    Returns:
-        DisplayAttrResult: the results of displaying the attribute; mech name,
-        short description, and long description.
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
     cdef OM_uint32 maj_stat, min_stat
     cdef gss_OID a = GSS_C_NO_OID
     cdef gss_buffer_desc name
diff --git a/gssapi/raw/ext_rfc5588.pyi b/gssapi/raw/ext_rfc5588.pyi
new file mode 100644
index 00000000..320b74aa
--- /dev/null
+++ b/gssapi/raw/ext_rfc5588.pyi
@@ -0,0 +1,42 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.named_tuples import StoreCredResult
+    from gssapi.raw.oids import OID
+
+
+def store_cred(
+    creds: "Creds",
+    usage: str = 'both',
+    mech: t.Optional["OID"] = None,
+    overwrite: bool = False,
+    set_default: bool = False,
+) -> "StoreCredResult":
+    """Store credentials into the default store.
+
+    This method stores the given credentials into the default store.
+    They may then be retrieved later using
+    :func:`~gssapi.raw.creds.acquire_cred`.
+
+    Args:
+        creds (Creds): the credentials to store
+        usage (str): the usage to store the credentials with -- either
+            'both', 'initiate', or 'accept'
+        mech (~gssapi.OID): the mechansim to associate with the stored
+            credentials
+        overwrite (bool): whether or not to overwrite existing credentials
+            stored with the same name, etc
+        set_default (bool): whether or not to set these credentials as
+            the default credentials for the given store.
+
+    Returns:
+        StoreCredResult: the results of the credential storing operation
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+        ~gssapi.exceptions.ExpiredCredentialsError
+        ~gssapi.exceptions.MissingCredentialsError
+        ~gssapi.exceptions.OperationUnavailableError
+        ~gssapi.exceptions.DuplicateCredentialsElementError
+    """
diff --git a/gssapi/raw/ext_rfc5588.pyx b/gssapi/raw/ext_rfc5588.pyx
index dbf1fcd0..95b924f5 100644
--- a/gssapi/raw/ext_rfc5588.pyx
+++ b/gssapi/raw/ext_rfc5588.pyx
@@ -24,35 +24,6 @@ cdef extern from "python_gssapi.h":
 
 def store_cred(Creds creds not None, usage='both', OID mech=None,
                bint overwrite=False, bint set_default=False):
-    """
-    store_cred(creds, usage='both', mech=None, overwrite=False, \
-set_default=False)
-    Store credentials into the default store.
-
-    This method stores the given credentials into the default store.
-    They may then be retrieved later using :func:`acquire_cred`.
-
-    Args:
-        creds (Creds): the credentials to store
-        usage (str): the usage to store the credentials with -- either
-            'both', 'initiate', or 'accept'
-        mech (~gssapi.OID): the mechansim to associate with the stored
-            credentials
-        overwrite (bool): whether or not to overwrite existing credentials
-            stored with the same name, etc
-        set_default (bool): whether or not to set these credentials as
-            the default credentials for the given store.
-
-    Returns:
-        StoreCredResult: the results of the credential storing operation
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-        ~gssapi.exceptions.ExpiredCredentialsError
-        ~gssapi.exceptions.MissingCredentialsError
-        ~gssapi.exceptions.OperationUnavailableError
-        ~gssapi.exceptions.DuplicateCredentialsElementError
-    """
     cdef gss_OID desired_mech
     if mech is not None:
         desired_mech = &mech.raw_oid
diff --git a/gssapi/raw/ext_rfc5801.pyi b/gssapi/raw/ext_rfc5801.pyi
new file mode 100644
index 00000000..55a7d06d
--- /dev/null
+++ b/gssapi/raw/ext_rfc5801.pyi
@@ -0,0 +1,39 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.named_tuples import InquireSASLNameResult
+    from gssapi.raw.oids import OID
+
+
+def inquire_saslname_for_mech(
+    mech: "OID",
+) -> "InquireSASLNameResult":
+    """Gets information about a specified mech, including the SASL name,
+    the mech name, and the mech description.
+
+    Args:
+        mech (~gssapi.OID): Mechanism to inquire about
+
+    Returns:
+        InquireSASLNameResult: the results of inquiry; a mech's SASL name,
+        name, and description.
+
+    Raises:
+        ~gssapi.exceptions.GSSError: an unknown failure occurred
+    """
+
+
+def inquire_mech_for_saslname(
+    sasl_name: bytes,
+) -> "OID":
+    """Gets the OID for the mech specified by SASL name.
+
+    Args:
+        sasl_name (bytes): SASL name of the mechanism
+
+    Returns:
+        ~gssapi.OID: the mechanism with corresponding SASL name.
+
+    Raises:
+        ~gssapi.exceptions.GSSError: An unknown failure occurred
+    """
diff --git a/gssapi/raw/ext_rfc5801.pyx b/gssapi/raw/ext_rfc5801.pyx
index 89da2ae1..4d9f5837 100644
--- a/gssapi/raw/ext_rfc5801.pyx
+++ b/gssapi/raw/ext_rfc5801.pyx
@@ -22,21 +22,6 @@ cdef extern from "python_gssapi_ext.h":
 
 
 def inquire_saslname_for_mech(OID mech not None):
-    """
-    inquire_saslname_for_mech(mech)
-    Gets information about a specified mech, including the SASL name,
-    the mech name, and the mech description.
-
-    Args:
-        mech (~gssapi.OID): Mechanism to inquire about
-
-    Returns:
-        InquireSASLNameResult: the results of inquiry; a mech's SASL name,
-        name, and description.
-
-    Raises:
-        ~gssapi.exceptions.GSSError: an unknown failure occurred
-    """
     cdef OM_uint32 maj_stat, min_stat
     cdef gss_buffer_desc sasl_mech_name
     cdef gss_buffer_desc mech_name
@@ -64,19 +49,6 @@ def inquire_saslname_for_mech(OID mech not None):
 
 
 def inquire_mech_for_saslname(bytes sasl_name not None):
-    """
-    inquire_mech_for_saslname(sasl_name)
-    Gets the OID for the mech specified by SASL name.
-
-    Args:
-        sasl_name (bytes): SASL name of the mechanism
-
-    Returns:
-        ~gssapi.OID: the mechanism with corresponding SASL name.
-
-    Raises:
-        ~gssapi.exceptions.GSSError: An unknown failure occurred
-    """
     cdef OM_uint32 maj_stat, min_stat
     cdef gss_buffer_desc sn
     cdef gss_OID m
diff --git a/gssapi/raw/ext_rfc6680.pyi b/gssapi/raw/ext_rfc6680.pyi
new file mode 100644
index 00000000..862e6163
--- /dev/null
+++ b/gssapi/raw/ext_rfc6680.pyi
@@ -0,0 +1,169 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.named_tuples import (
+        GetNameAttributeResult,
+        InquireNameResult,
+    )
+    from gssapi.raw.names import Name
+    from gssapi.raw.oids import OID
+
+
+def display_name_ext(
+    name: "Name",
+    name_type: "OID",
+) -> bytes:
+    """Display the given Name using the given name type.
+
+    This method attempts to display the given Name using the syntax of
+    the given name type.  If this is not possible, an appropriate error
+    will be raised.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name to display
+        name_type (~gssapi.OID): the name type (see NameType) to use to
+            display the given name
+
+    Returns:
+        bytes: the displayed name
+
+    Raises:
+        ~gssapi.exceptions.OperationUnavailableError: the given name could not
+            be displayed using the given name type
+    """
+
+
+def inquire_name(
+    name: "Name",
+    mech_name: bool = True,
+    attrs: bool = True,
+) -> "InquireNameResult":
+    """Get information about a Name.
+
+    This method retrives information about the given name, including
+    the set of attribute names for the given name, as well as whether or
+    not the name is a mechanism name.  Additionally, if the given name is
+    a mechanism name, the associated mechansim is returned as well.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name about which to inquire
+        mech_name (bool): whether or not to retrieve if this name
+            is a mech_name (and the associate mechanism)
+        attrs (bool): whether or not to retrieve the attribute name list
+
+    Returns:
+        InquireNameResult: the set of attribute names for the given name,
+        whether or not the name is a Mechanism Name, and potentially
+        the associated mechanism if it is a Mechanism Name
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def set_name_attribute(
+    name: "Name",
+    attr: bytes,
+    value: t.Iterable[bytes],
+    complete: bool = False,
+) -> None:
+    """Set the value(s) of a name attribute.
+
+    This method sets the value(s) of the given attribute on the given name.
+
+    Note that this functionality more closely matches the pseudo-API
+    presented in RFC 6680, not the C API (which uses multiple calls to
+    add multiple values).  However, multiple calls to this method will
+    continue adding values, so :func:`delete_name_attribute` must be
+    used in between calls to "clear" the values.
+
+    Args:
+        name (~gssapi.raw.names.Name): the Name on which to set the attribute
+        attr (bytes): the name of the attribute
+        value (list): a list of bytes objects to use as the value(s)
+        complete (bool): whether or not to mark this attribute's value
+            set as being "complete"
+
+    Raises:
+        ~gssapi.exceptions.OperationUnavailableError: the given attribute name
+            is unknown or could not be set
+    """
+
+
+def get_name_attribute(
+    name: "Name",
+    attr: bytes,
+    more: t.Optional[int] = None,
+) -> "GetNameAttributeResult":
+    """Get the value(s) of a name attribute.
+
+    This method retrieves the value(s) of the given attribute
+    for the given Name.
+
+    Note that this functionality matches pseudo-API presented
+    in RFC 6680, not the C API (which uses a state variable and
+    multiple calls to retrieve multiple values).
+
+    Args:
+        name (~gssapi.raw.names.Name): the Name from which to get the attribute
+        attr (bytes): the name of the attribute
+
+    Returns:
+        GetNameAttributeResult: the raw version of the value(s),
+        the human-readable version of the value(s), whether
+        or not the attribute was authenticated, and whether or
+        not the attribute's value set was marked as complete
+
+    Raises:
+        ~gssapi.exceptions.OperationUnavailableError: the given attribute is
+            unknown or unset
+    """
+
+
+def delete_name_attribute(
+    name: "Name",
+    attr: bytes,
+) -> None:
+    """Remove an attribute from a name.
+
+    This method removes an attribute from a Name.  This method may be
+    used before :func:`set_name_attribute` clear the values of an attribute
+    before setting a new value (making the latter method work like a 'set'
+    operation instead of an 'add' operation).
+
+    Note that the removal of certain attributes may not be allowed.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name to remove the attribute from
+        attr (bytes): the name of the attribute
+
+    Raises:
+        ~gssapi.exceptions.OperationUnavailableError
+        ~gssapi.exceptions.UnauthorizedError
+    """
+
+
+def export_name_composite(
+    name: "Name",
+) -> bytes:
+    """Export a name, preserving attribute information.
+
+    This method functions similarly to :func:`~gssapi.raw.names.export_name`,
+    except that it preserves attribute information.  The resulting bytes may be
+    imported using :func:`~gssapi.raw.names.import_name` with the
+    :attr:`~gssapi.raw.types.NameType.composite_export` name type.
+
+    Note:
+        Some versions of MIT Kerberos require you to either canonicalize a name
+        once it has been imported with composite-export name type, or to import
+        using the normal export name type.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name to export
+
+    Returns:
+        bytes: the exported composite name
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_rfc6680.pyx b/gssapi/raw/ext_rfc6680.pyx
index 94f52708..70adcce3 100644
--- a/gssapi/raw/ext_rfc6680.pyx
+++ b/gssapi/raw/ext_rfc6680.pyx
@@ -37,27 +37,6 @@ cdef extern from "python_gssapi_ext.h":
 
 
 def display_name_ext(Name name not None, OID name_type not None):
-    """
-    display_name_ext(name, name_type)
-    Display the given Name using the given name type.
-
-    This method attempts to display the given Name using the syntax of
-    the given name type.  If this is not possible, an appropriate error
-    will be raised.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name to display
-        name_type (~gssapi.OID): the name type (see NameType) to use to
-            display the given name
-
-    Returns:
-        bytes: the displayed name
-
-    Raises:
-        ~gssapi.exceptions.OperationUnavailableError: the given name could not
-            be displayed using the given name type
-    """
-
     # GSS_C_EMPTY_BUFFER
     cdef gss_buffer_desc output_name = gss_buffer_desc(0, NULL)
 
@@ -75,30 +54,6 @@ def display_name_ext(Name name not None, OID name_type not None):
 
 
 def inquire_name(Name name not None, mech_name=True, attrs=True):
-    """
-    inquire_name(name, mech_name=True, attrs=True)
-    Get information about a Name.
-
-    This method retrives information about the given name, including
-    the set of attribute names for the given name, as well as whether or
-    not the name is a mechanism name.  Additionally, if the given name is
-    a mechanism name, the associated mechansim is returned as well.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name about which to inquire
-        mech_name (bool): whether or not to retrieve if this name
-            is a mech_name (and the associate mechanism)
-        attrs (bool): whether or not to retrieve the attribute name list
-
-    Returns:
-        InquireNameResult: the set of attribute names for the given name,
-            whether or not the name is a Mechanism Name, and potentially
-            the associated mechanism if it is a Mechanism Name
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef int *name_is_mn_ptr = NULL
     cdef gss_OID *mn_mech_ptr = NULL
     cdef gss_buffer_set_t *attr_names_ptr = NULL
@@ -143,30 +98,6 @@ def inquire_name(Name name not None, mech_name=True, attrs=True):
 
 def set_name_attribute(Name name not None, attr not None, value not None,
                        bint complete=False):
-    """
-    set_name_attribute(name, attr, value, complete=False)
-    Set the value(s) of a name attribute.
-
-    This method sets the value(s) of the given attribute on the given name.
-
-    Note that this functionality more closely matches the pseudo-API
-    presented in RFC 6680, not the C API (which uses multiple calls to
-    add multiple values).  However, multiple calls to this method will
-    continue adding values, so :func:`delete_name_attribute` must be
-    used in between calls to "clear" the values.
-
-    Args:
-        name (~gssapi.raw.names.Name): the Name on which to set the attribute
-        attr (bytes): the name of the attribute
-        value (list): a list of bytes objects to use as the value(s)
-        complete (bool): whether or not to mark this attribute's value
-            set as being "complete"
-
-    Raises:
-        ~gssapi.exceptions.OperationUnavailableError: the given attribute name
-            is unknown or could not be set
-    """
-
     cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr)
     cdef gss_buffer_desc val_buff
 
@@ -189,31 +120,6 @@ def set_name_attribute(Name name not None, attr not None, value not None,
 
 
 def get_name_attribute(Name name not None, attr not None, more=None):
-    """
-    get_name_attribute(name, attr, more=None)
-    Get the value(s) of a name attribute.
-
-    This method retrieves the value(s) of the given attribute
-    for the given Name.
-
-    Note that this functionality matches pseudo-API presented
-    in RFC 6680, not the C API (which uses a state variable and
-    multiple calls to retrieve multiple values).
-
-    Args:
-        name (~gssapi.raw.names.Name): the Name from which to get the attribute
-        attr (bytes): the name of the attribute
-
-    Returns:
-        GetNameAttributeResult: the raw version of the value(s),
-        the human-readable version of the value(s), whether
-        or not the attribute was authenticated, and whether or
-        not the attribute's value set was marked as complete
-
-    Raises:
-        ~gssapi.exceptions.OperationUnavailableError: the given attribute is
-            unknown or unset
-    """
     cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr)
 
     cdef gss_buffer_desc val_buff = gss_buffer_desc(0, NULL)
@@ -249,26 +155,6 @@ def get_name_attribute(Name name not None, attr not None, more=None):
 
 
 def delete_name_attribute(Name name not None, attr not None):
-    """
-    delete_name_attribute(name, attr)
-    Remove an attribute from a name.
-
-    This method removes an attribute from a Name.  This method may be
-    used before :func:`set_name_attribute` clear the values of an attribute
-    before setting a new value (making the latter method work like a 'set'
-    operation instead of an 'add' operation).
-
-    Note that the removal of certain attributes may not be allowed.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name to remove the attribute from
-        attr (bytes): the name of the attribute
-
-    Raises:
-        ~gssapi.exceptions.OperationUnavailableError
-        ~gssapi.exceptions.UnauthorizedError
-    """
-
     cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr)
 
     cdef OM_uint32 maj_stat, min_stat
@@ -281,30 +167,6 @@ def delete_name_attribute(Name name not None, attr not None):
 
 
 def export_name_composite(Name name not None):
-    """
-    export_name_composite(name)
-    Export a name, preserving attribute information.
-
-    This method functions similarly to :func:`export_name`, except that
-    it preserves attribute information.  The resulting bytes may be imported
-    using :func:`import_name` with the :attr:`NameType.composite_export`
-    name type.
-
-    Note:
-        Some versions of MIT Kerberos require you to either canonicalize a name
-        once it has been imported with composite-export name type, or to import
-        using the normal export name type.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name to export
-
-    Returns:
-        bytes: the exported composite name
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_desc res = gss_buffer_desc(0, NULL)
 
     cdef OM_uint32 maj_stat, min_stat
diff --git a/gssapi/raw/ext_rfc6680_comp_oid.pyi b/gssapi/raw/ext_rfc6680_comp_oid.pyi
new file mode 100644
index 00000000..e69de29b
diff --git a/gssapi/raw/ext_s4u.pyi b/gssapi/raw/ext_s4u.pyi
new file mode 100644
index 00000000..85756b9d
--- /dev/null
+++ b/gssapi/raw/ext_s4u.pyi
@@ -0,0 +1,86 @@
+"""Service4User Extension"""
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.named_tuples import AcquireCredResult, AddCredResult
+    from gssapi.raw.names import Name
+    from gssapi.raw.oids import OID
+
+
+def acquire_cred_impersonate_name(
+    impersonator_cred: "Creds",
+    name: "Name",
+    lifetime: t.Optional[int] = None,
+    mechs: t.Optional[t.Iterable["OID"]] = None,
+    usage: str = 'initiate',
+) -> "AcquireCredResult":
+    """Acquire credentials by impersonating another name.
+
+    This method is one of the ways to use S4U2Self.  It acquires credentials
+    by impersonating another name using a set of proxy credentials.  The
+    impersonator credentials must have a usage of 'both' or 'initiate'.
+
+    Args:
+        impersonator_cred (~gssapi.raw.creds.Creds): the credentials with
+            permissions to impersonate the target name
+        name (~gssapi.raw.names.Name): the name to impersonate
+        lifetime (int): the lifetime for the credentials (or None for
+            indefinite)
+        mechs (~gssapi.raw.types.MechType): the desired mechanisms for which
+            the credentials should work (or None for the default set)
+        usage (str): the usage type for the credentials: may be
+            'initiate', 'accept', or 'both'
+
+    Returns:
+        AcquireCredResult: the resulting credentials, the actual mechanisms
+        with which they may be used, and their actual lifetime (or None for
+        indefinite or not support)
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def add_cred_impersonate_name(
+    input_cred: "Creds",
+    impersonator_cred: "Creds",
+    name: "Name",
+    mech: "OID",
+    usage: str = 'initiate',
+    init_lifetime: t.Optional[int] = None,
+    accept_lifetime: t.Optional[int] = None,
+) -> "AddCredResult":
+    """Add a credentials element to a credential by impersonating another name.
+
+    This method is one of the ways to use S4U2Self.  It adds credentials
+    to the input credentials by impersonating another name using a set of
+    proxy credentials.  The impersonator credentials must have a usage of
+    'both' or 'initiate'.
+
+    Args:
+        input_cred (~gssapi.raw.creds.Creds): the set of credentials to which
+            to add the new credentials
+        impersonator_cred (~gssapi.raw.creds.Creds): the credentials with
+            permissions to impersonate the target name
+        name (~gssapi.raw.names.Name): the name to impersonate
+        mech (~gssapi.raw.types.MechType): the desired mechanism. Note that
+            this is both
+            singular and required, unlike acquireCredImpersonateName
+        usage (str): the usage type for the credentials: may be
+            'initiate', 'accept', or 'both'
+        init_lifetime (int): the lifetime for the credentials to remain
+            valid when using them to initiate security contexts (or None for
+            indefinite)
+        accept_lifetime (int): the lifetime for the credentials to remain
+            valid when using them to accept security contexts (or None for
+            indefinite)
+
+    Returns:
+        AddCredResult: the actual mechanisms with which the credentials may be
+        used, the actual initiator TTL, and the actual acceptor TTL (the TTLs
+        may be None for indefinite or not supported)
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_s4u.pyx b/gssapi/raw/ext_s4u.pyx
index 177b02e3..b123e165 100644
--- a/gssapi/raw/ext_s4u.pyx
+++ b/gssapi/raw/ext_s4u.pyx
@@ -1,4 +1,3 @@
-"""Service4User Extension"""
 GSSAPI="BASE"  # This ensures that a full module is generated by Cython
 
 from gssapi.raw.cython_types cimport *
@@ -41,35 +40,6 @@ cdef extern from "python_gssapi_ext.h":
 def acquire_cred_impersonate_name(Creds impersonator_cred not None,
                                   Name name not None, lifetime=None,
                                   mechs=None, usage='initiate'):
-    """
-    acquire_cred_impersonate_name(impersonator_cred, name, lifetime=None, \
-mechs=None, usage='initiate')
-    Acquire credentials by impersonating another name.
-
-    This method is one of the ways to use S4U2Self.  It acquires credentials
-    by impersonating another name using a set of proxy credentials.  The
-    impersonator credentials must have a usage of 'both' or 'initiate'.
-
-    Args:
-        impersonator_cred (Cred): the credentials with permissions to
-            impersonate the target name
-        name (~gssapi.raw.names.Name): the name to impersonate
-        lifetime (int): the lifetime for the credentials (or None for
-            indefinite)
-        mechs (~gssapi.MechType): the desired mechanisms for which the
-            credentials should work (or None for the default set)
-        usage (str): the usage type for the credentials: may be
-            'initiate', 'accept', or 'both'
-
-    Returns:
-        AcquireCredResult: the resulting credentials, the actual mechanisms
-        with which they may be used, and their actual lifetime (or None for
-        indefinite or not support)
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_OID_set desired_mechs
     if mechs is not None:
         desired_mechs = c_get_mech_oid_set(mechs)
@@ -120,42 +90,6 @@ def add_cred_impersonate_name(Creds input_cred,
                               Name name not None, OID mech not None,
                               usage='initiate', init_lifetime=None,
                               accept_lifetime=None):
-    """
-    add_cred_impersonate_name(input_cred, impersonator_cred, name, mech, \
-usage='initiate', init_lifetime=None, accept_lifetime=None)
-    Add a credentials element to a credential by impersonating another name.
-
-    This method is one of the ways to use S4U2Self.  It adds credentials
-    to the input credentials by impersonating another name using a set of
-    proxy credentials.  The impersonator credentials must have a usage of
-    'both' or 'initiate'.
-
-    Args:
-        input_cred (Cred): the set of credentials to which to add the new
-            credentials
-        impersonator_cred (Cred): the credentials with permissions to
-            impersonate the target name
-        name (~gssapi.raw.names.Name): the name to impersonate
-        mech (~gssapi.MechType): the desired mechanism.  Note that this is both
-            singular and required, unlike acquireCredImpersonateName
-        usage (str): the usage type for the credentials: may be
-            'initiate', 'accept', or 'both'
-        init_lifetime (int): the lifetime for the credentials to remain
-            valid when using them to initiate security contexts (or None for
-            indefinite)
-        accept_lifetime (int): the lifetime for the credentials to remain
-            valid when using them to accept security contexts (or None for
-            indefinite)
-
-    Returns:
-        AddCredResult: the actual mechanisms with which the credentials may be
-        used, the actual initiator TTL, and the actual acceptor TTL (the TTLs
-        may be None for indefinite or not supported)
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef OM_uint32 input_initiator_ttl = c_py_ttl_to_c(init_lifetime)
     cdef OM_uint32 input_acceptor_ttl = c_py_ttl_to_c(accept_lifetime)
     cdef gss_name_t c_name = name.raw_name
diff --git a/gssapi/raw/ext_set_cred_opt.pyi b/gssapi/raw/ext_set_cred_opt.pyi
new file mode 100644
index 00000000..eeb0e123
--- /dev/null
+++ b/gssapi/raw/ext_set_cred_opt.pyi
@@ -0,0 +1,50 @@
+"""
+gss_set_cred_option
+
+Provides a way to set options on a credential based on the OID specified. A
+common use case is to set the GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos
+credential. This is used for interoperability with Microsoft's SSPI.
+
+Note this function is commonly lumped with the GGF extensions but they are not
+part of the GGF IETF draft so it's separated into it's own file.
+
+Closest draft IETF document for the gss_set_cred_option can be found at
+https://tools.ietf.org/html/draft-williams-kitten-channel-bound-flag-01
+"""
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.oids import OID
+
+
+def set_cred_option(
+    desired_aspect: "OID",
+    creds: t.Optional["Creds"] = None,
+    value: t.Optional[bytes] = None,
+) -> "Creds":
+    """
+    This method is used to set options of a :class:`~gssapi.raw.creds.Creds`
+    object based on an OID key. The options that can be set depends on the mech
+    the credentials were created with.
+
+    An example of how this can be used would be to set the
+    GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos credential. The OID string for
+    this flag is '1.2.752.43.13.29' and it requires no value to be set. This
+    must be set before the SecurityContext was initialised with the
+    credentials.
+
+    Args:
+        desired_aspect (~gssapi.raw.oids.OID): the desired aspect of the
+            Credential to set.
+        cred_handle (~gssapi.raw.creds.Creds): the Credentials to set, or None
+            to create a new credential.
+        value (bytes): the value to set on the desired aspect of the Credential
+            or None to send GSS_C_EMPTY_BUFFER.
+
+    Returns:
+        Creds: The output credential.
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
diff --git a/gssapi/raw/ext_set_cred_opt.pyx b/gssapi/raw/ext_set_cred_opt.pyx
index 506dd12c..b59d18d1 100644
--- a/gssapi/raw/ext_set_cred_opt.pyx
+++ b/gssapi/raw/ext_set_cred_opt.pyx
@@ -1,16 +1,3 @@
-"""
-gss_set_cred_option
-
-Provides a way to set options on a credential based on the OID specified. A
-common use case is to set the GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos
-credential. This is used for interoperability with Microsoft's SSPI.
-
-Note this function is commonly lumped with the GGF extensions but they are not
-part of the GGF IETF draft so it's separated into it's own file.
-
-Closest draft IETF document for the gss_set_cred_option can be found at
-https://tools.ietf.org/html/draft-williams-kitten-channel-bound-flag-01
-"""
 GSSAPI="BASE"  # This ensures that a full module is generated by Cython
 
 from gssapi.raw.cython_types cimport *
@@ -28,34 +15,6 @@ cdef extern from "python_gssapi_ext.h":
 
 
 def set_cred_option(OID desired_aspect not None, Creds creds=None, value=None):
-    """
-    set_cred_option(desired_aspect, creds=None, value=None)
-
-    This method is used to set options of a :class:`Creds` object based on
-    an OID key. The options that can be set depends on the mech the credentials
-    were created with.
-
-    An example of how this can be used would be to set the
-    GSS_KRB5_CRED_NO_CI_FLAGS_X on a Kerberos credential. The OID string for
-    this flag is '1.2.752.43.13.29' and it requires no value to be set. This
-    must be set before the SecurityContext was initialised with the
-    credentials.
-
-    Args:
-        desired_aspect (~gssapi.OID): the desired aspect of the Credential to
-            set.
-        cred_handle (Creds): the Credentials to set, or None to create a new
-            credential.
-        value (bytes): the value to set on the desired aspect of the Credential
-            or None to send GSS_C_EMPTY_BUFFER.
-
-    Returns:
-        Creds: The output credential.
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_buffer_desc value_buffer
     if value is not None:
         value_buffer = gss_buffer_desc(len(value), value)
diff --git a/gssapi/raw/mech_krb5.pyi b/gssapi/raw/mech_krb5.pyi
new file mode 100644
index 00000000..e69de29b
diff --git a/gssapi/raw/message.pyi b/gssapi/raw/message.pyi
new file mode 100644
index 00000000..04c6df5e
--- /dev/null
+++ b/gssapi/raw/message.pyi
@@ -0,0 +1,156 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.named_tuples import WrapResult, UnwrapResult
+    from gssapi.sec_contexts import SecurityContext
+
+
+def get_mic(
+    context: "SecurityContext",
+    message: bytes,
+    qop: t.Optional[int] = None,
+) -> bytes:
+    """Generate a MIC for a message.
+
+    This method generates a Message Integrity Check token for the
+    given message.  This can be separately trasmitted to the other
+    entity, unlike wrap, which bundles the MIC and the message
+    together.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (bytes): the message for which to generate the MIC
+        qop (int): the requested Quality of Protection
+            (or None to use the default)
+
+    Returns:
+        bytes: the generated MIC token
+
+    Raises:
+        ~gssapi.exceptions.ExpiredContextError
+        ~gssapi.exceptions.MissingContextError
+        ~gssapi.exceptions.BadQoPError
+    """
+
+
+def verify_mic(
+    context: "SecurityContext",
+    message: bytes,
+    token: bytes,
+) -> int:
+    """Verify that a MIC matches a message.
+
+    This method verifies that the given MIC matches the given message.
+    If the MIC does not match the given message, an exception will
+    be raised.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (bytes): the message in question
+        token (bytes): the MIC token in question
+
+    Returns:
+        int: the QoP used.
+
+    Raises:
+        ~gssapi.exceptions.InvalidTokenError
+        ~gssapi.exceptions.BadMICError
+        ~gssapi.exceptions.DuplicateTokenError
+        ~gssapi.exceptions.ExpiredTokenError
+        ~gssapi.exceptions.TokenTooLateError
+        ~gssapi.exceptions.TokenTooEarlyError
+        ~gssapi.exceptions.ExpiredContextError
+        ~gssapi.exceptions.MissingContextError
+    """
+
+
+def wrap_size_limit(
+    context: "SecurityContext",
+    output_size: int,
+    confidential: bool = True,
+    qop: t.Optional[int] = None,
+) -> int:
+    """Calculate the max message size.
+
+    This method calculates the unwrapped/unencrypted message size for
+    the given maximum wrapped/encrypted message size.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        output_size (int): the maximum desired wrapped/encrypted message size
+        confidential (bool): whether or not confidentiality is being used
+        qop (int): the QoP that will be when you actually call wrap
+            (or None for the default QoP)
+
+    Returns:
+        int: the maximum unencrypted/unwrapped message size
+
+    Raises:
+        ~gssapi.exceptions.MissingContextError
+        ~gssapi.exceptions.ExpiredContextError
+        ~gssapi.exceptions.BadQoPError
+    """
+
+
+def wrap(
+    context: "SecurityContext",
+    message: bytes,
+    confidential: bool = True,
+    qop: t.Optional[int] = None,
+) -> "WrapResult":
+    """Wrap/Encrypt a message.
+
+    This method wraps or encrypts a message (depending on the value
+    of confidential) with the given Quality of Protection.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (bytes): the message to wrap or encrypt
+        confidential (bool): whether or not to encrypt the message (True),
+            or just wrap it with a MIC (False)
+        qop (int): the desired Quality of Protection
+            (or None for the default QoP)
+
+    Returns:
+        WrapResult: the wrapped/encrypted message, and whether or not
+            encryption was actually used
+
+    Raises:
+        ~gssapi.exceptions.ExpiredContextError
+        ~gssapi.exceptions.MissingContextError
+        ~gssapi.exceptions.BadQoPError
+    """
+
+
+def unwrap(
+    context: "SecurityContext",
+    message: bytes,
+) -> "UnwrapResult":
+    """Unwrap/Decrypt a message.
+
+    This method unwraps or decrypts a message, depending
+    on whether the sender used confidentiality.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the current
+            security context
+        message (bytes): the message to unwrap/decrypt
+
+    Returns:
+        UnwrapResult: the unwrapped/decrypted message, whether or on
+            encryption was used, and the QoP used
+
+    Raises:
+        ~gssapi.exceptions.InvalidTokenError
+        ~gssapi.exceptions.BadMICError
+        ~gssapi.exceptions.DuplicateTokenError
+        ~gssapi.exceptions.ExpiredTokenError
+        ~gssapi.exceptions.TokenTooLateError
+        ~gssapi.exceptions.TokenTooEarlyError
+        ~gssapi.exceptions.ExpiredContextError
+        ~gssapi.exceptions.MissingContextError
+    """
diff --git a/gssapi/raw/misc.pyi b/gssapi/raw/misc.pyi
new file mode 100644
index 00000000..00c17a93
--- /dev/null
+++ b/gssapi/raw/misc.pyi
@@ -0,0 +1,166 @@
+import typing as t
+
+from gssapi.raw.names import Name
+from gssapi.raw.oids import OID
+from gssapi.raw.types import MechType
+
+
+def indicate_mechs() -> t.Set[OID]:
+    """Get the currently supported mechanisms.
+
+    This method retrieves the currently supported GSSAPI mechanisms.
+    Note that if unknown mechanims are found, those will be skipped.
+    """
+
+
+def inquire_names_for_mech(
+    mech: OID,
+) -> t.Set[OID]:
+    """
+    inquire_names_for_mech(mech)
+    Get the name types supported by a mechanism.
+
+    This method retrives the different name types supported by
+    the given mechanism.
+
+    Args:
+        mech (~gssapi.OID): the mechanism in question
+
+    Returns:
+        list: the name type OIDs supported by the given mechanism
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def inquire_mechs_for_name(
+    name: Name,
+) -> t.Set[OID]:
+    """
+    inquire_mechs_for_name(name)
+    List the mechanisms which can process a name.
+
+    This method lists the mechanisms which may be able to
+    process the given name.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name in question
+
+    Returns:
+        The mechanism OIDs able to process the given name
+
+    Raises:
+        ~gssapi.exceptions.GSSError
+    """
+
+
+def _display_status(
+    error_code: int,
+    is_major_code: bool,
+    mech: t.Optional[MechType] = None,
+    message_context: int = 0,
+) -> t.Tuple[bytes, int, bool]:
+    """
+    Display a string message for a GSSAPI error code.
+
+    This method displays a message for a corresponding GSSAPI error code.
+    Since some error codes might have multiple messages, a context parameter
+    may be passed to indicate where in the series of messages we currently are
+    (this is the second item in the return value tuple).  Additionally, the
+    third item in the return value tuple indicates whether or not more
+    messages are available.
+
+    Args:
+        error_code (int): The error code in question
+        is_major_code (bool): is this a major code (True) or a
+            minor code (False)
+        mech (~gssapi.raw.types.MechType): The mechanism type that returned
+            this error code (defaults to None, for the default mechanism)
+        message_context (int): The context for this call -- this is used when
+            multiple messages are available (defaults to 0)
+
+    Returns:
+        (bytes, int, bool): the message, the new message context, and
+            whether or not to call again for further messages
+
+    Raises:
+       ValueError
+    """
+
+
+class GSSError(Exception):
+    """
+    A GSSAPI Error
+
+    This Exception represents an error returned from the GSSAPI
+    C bindings.  It contains the major and minor status codes
+    returned by the method which caused the error, and can
+    generate human-readable string messages from the error
+    codes
+    """
+
+    maj_code: int
+    min_code: int
+    token: t.Optional[bytes]
+    calling_code: int
+    routine_code: int
+    supplementary_code: int
+
+    @classmethod
+    def _parse_major_code(
+        cls,
+        maj_code: int
+    ) -> t.Tuple[int, int, int]: ...
+
+    def __init__(
+        self,
+        maj_code: int,
+        min_code: int,
+        token: t.Optional[bytes] = None,
+    ) -> None:
+        """
+        Create a new GSSError.
+
+        This method creates a new GSSError,
+        retrieves the releated human-readable
+        string messages, and uses the results to construct an
+        exception message
+
+        Args:
+            maj_code: the major code associated with this error
+            min_code: the minor code associated with this error
+            token: an error token associated with the error
+        """
+
+    def get_all_statuses(
+        self,
+        code: int,
+        is_maj: bool,
+    ) -> t.List[str]:
+        """
+        Retrieve all messages for a status code.
+
+        This method retrieves all human-readable messages
+        available for the given status code.
+
+        Args:
+            code: the status code in question
+            is_maj: whether this is a major status code (True)
+                or minor status code (False)
+
+        Returns:
+            [str]: A list of string messages associated with the
+                given code
+        """
+
+    def gen_message(self) -> str:
+        """
+        Retrieves all messages for this error's status codes
+
+        This method retrieves all messages for this error's status codes,
+        and forms them into a string for use as an exception message
+
+        Returns:
+            str: a string for use as this error's message
+        """
diff --git a/gssapi/raw/misc.pyx b/gssapi/raw/misc.pyx
index c16a0431..4179f642 100644
--- a/gssapi/raw/misc.pyx
+++ b/gssapi/raw/misc.pyx
@@ -31,14 +31,6 @@ cdef extern from "python_gssapi.h":
 
 
 def indicate_mechs():
-    """
-    indicate_mechs()
-    Get the currently supported mechanisms.
-
-    This method retrieves the currently supported GSSAPI mechanisms.
-    Note that if unknown mechanims are found, those will be skipped.
-    """
-
     cdef gss_OID_set mech_set
 
     cdef OM_uint32 maj_stat, min_stat
@@ -52,23 +44,6 @@ def indicate_mechs():
 
 
 def inquire_names_for_mech(OID mech not None):
-    """
-    inquire_names_for_mech(mech)
-    Get the name types supported by a mechanism.
-
-    This method retrives the different name types supported by
-    the given mechanism.
-
-    Args:
-        mech (~gssapi.OID): the mechanism in question
-
-    Returns:
-        list: the name type OIDs supported by the given mechanism
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_OID_set name_types
 
     cdef OM_uint32 maj_stat, min_stat
@@ -83,23 +58,6 @@ def inquire_names_for_mech(OID mech not None):
 
 
 def inquire_mechs_for_name(Name name not None):
-    """
-    inquire_mechs_for_name(name)
-    List the mechanisms which can process a name.
-
-    This method lists the mechanisms which may be able to
-    process the given name.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name in question
-
-    Returns:
-        list: the mechanism OIDs able to process the given name
-
-    Raises:
-        ~gssapi.exceptions.GSSError
-    """
-
     cdef gss_OID_set mech_types
 
     cdef OM_uint32 maj_stat, min_stat
@@ -115,33 +73,6 @@ def inquire_mechs_for_name(Name name not None):
 
 def _display_status(unsigned int error_code, bint is_major_code,
                     OID mech=None, unsigned int message_context=0):
-    """
-    Display a string message for a GSSAPI error code.
-
-    This method displays a message for a corresponding GSSAPI error code.
-    Since some error codes might have multiple messages, a context parameter
-    may be passed to indicate where in the series of messages we currently are
-    (this is the second item in the return value tuple).  Additionally, the
-    third item in the return value tuple indicates whether or not more
-    messages are available.
-
-    Args:
-        error_code (int): The error code in question
-        is_major_code (bool): is this a major code (True) or a
-            minor code (False)
-        mech (~gssapi.MechType): The mechanism type that returned this error
-            code (defaults to None, for the default mechanism)
-        message_context (int): The context for this call -- this is used when
-            multiple messages are available (defaults to 0)
-
-    Returns:
-        (bytes, int, bool): the message, the new message context, and
-            whether or not to call again for further messages
-
-    Raises:
-       ValueError
-    """
-
     cdef int status_type
     cdef gss_OID c_mech_type
 
@@ -223,15 +154,6 @@ class GSSErrorRegistry(type):
 # NB(directxman12): this needs to be here (and not in another file)
 #                   so that display_status can use it
 class GSSError(Exception, metaclass=GSSErrorRegistry):
-    """
-    A GSSAPI Error
-
-    This Exception represents an error returned from the GSSAPI
-    C bindings.  It contains the major and minor status codes
-    returned by the method which caused the error, and can
-    generate human-readable string messages from the error
-    codes
-    """
 
     MESSAGE = u"Major ({maj_stat}): {maj_str}, Minor ({min_stat}): {min_str}"
 
@@ -248,20 +170,6 @@ class GSSError(Exception, metaclass=GSSErrorRegistry):
         return (calling_code, routine_code, supplementary_code)
 
     def __init__(self, maj_code, min_code, token=None):
-        """
-        Create a new GSSError.
-
-        This method creates a new GSSError,
-        retrieves the releated human-readable
-        string messages, and uses the results to construct an
-        exception message
-
-        Args:
-            maj_code (int): the major code associated with this error
-            min_code (int): the minor code associated with this error
-            token (bytes): an error token associated with the error
-        """
-
         self.maj_code = maj_code
         self.min_code = min_code
 
@@ -275,22 +183,6 @@ class GSSError(Exception, metaclass=GSSErrorRegistry):
         super(GSSError, self).__init__(self.gen_message())
 
     def get_all_statuses(self, code, is_maj):
-        """
-        Retrieve all messages for a status code.
-
-        This method retrieves all human-readable messages
-        available for the given status code.
-
-        Args:
-            code (int): the status code in question
-            is_maj (bool): whether this is a major status code (True)
-                or minor status code (False)
-
-        Returns:
-            [bytes]: A list of string messages associated with the
-                given code
-        """
-
         try:
             msg_encoding = locale.getlocale(locale.LC_MESSAGES)[1] or 'UTF-8'
         except AttributeError:  # Windows doesn't have LC_MESSAGES
@@ -316,16 +208,6 @@ class GSSError(Exception, metaclass=GSSErrorRegistry):
         return res
 
     def gen_message(self):
-        """
-        Retrieves all messages for this error's status codes
-
-        This method retrieves all messages for this error's status codes,
-        and forms them into a string for use as an exception message
-
-        Returns:
-            bytes: a string for use as this error's message
-        """
-
         maj_statuses = self.get_all_statuses(self.maj_code, True)
         min_statuses = self.get_all_statuses(self.min_code, False)
 
diff --git a/gssapi/raw/named_tuples.py b/gssapi/raw/named_tuples.py
index e41bec95..f6e2b95b 100644
--- a/gssapi/raw/named_tuples.py
+++ b/gssapi/raw/named_tuples.py
@@ -1,4 +1,7 @@
-from typing import Iterable, List, NamedTuple, Optional, Set, TYPE_CHECKING
+from typing import List, NamedTuple, Optional, Set, TYPE_CHECKING
+
+from gssapi.raw.oids import OID
+from gssapi.raw.types import RequirementFlag
 
 if TYPE_CHECKING:
     import gssapi.raw as g
@@ -7,7 +10,7 @@
 class AcquireCredResult(NamedTuple):
     """Credential result when acquiring a GSSAPI credential."""
     creds: "g.Creds"  #: GSSAPI credentials that were acquired
-    mechs: Set["g.OID"]  #: Set of mechs the cred is for
+    mechs: Set[OID]  #: Set of mechs the cred is for
     lifetime: int  #: Number of seconds for which the cred will remain valid
 
 
@@ -16,7 +19,7 @@ class InquireCredResult(NamedTuple):
     name: Optional["g.Name"]  #: The principal associated with the credential
     lifetime: Optional[int]  #: Number of seconds which the cred is valid for
     usage: Optional[str]  #: How the credential can be used
-    mechs: Optional[Set["g.OID"]]  #: Set of mechs the cred is for
+    mechs: Optional[Set[OID]]  #: Set of mechs the cred is for
 
 
 class InquireCredByMechResult(NamedTuple):
@@ -30,7 +33,7 @@ class InquireCredByMechResult(NamedTuple):
 class AddCredResult(NamedTuple):
     """Result of adding to a GSSAPI credential."""
     creds: Optional["g.Creds"]  #: The credential that was generated
-    mechs: Set["g.OID"]  #: Set of mechs the cred is for
+    mechs: Set[OID]  #: Set of mechs the cred is for
     init_lifetime: int  #: Time valid for initiation
     accept_lifetime: int  #: Time valid for accepting
 
@@ -38,7 +41,7 @@ class AddCredResult(NamedTuple):
 class DisplayNameResult(NamedTuple):
     """Textual representation of a GSSAPI name."""
     name: bytes  #: The representation of the GSSAPI name
-    name_type: Optional["g.OID"]  #: The type of GSSAPI name
+    name_type: Optional[OID]  #: The type of GSSAPI name
 
 
 class WrapResult(NamedTuple):
@@ -58,9 +61,9 @@ class AcceptSecContextResult(NamedTuple):
     """Result when accepting a security context by an initiator."""
     context: "g.SecurityContext"  #: The acceptor security context
     initiator_name: "g.Name"  #: The authenticated name of the initiator
-    mech: "g.OID"  #: Mechanism with which the context was established
+    mech: OID  #: Mechanism with which the context was established
     token: Optional[bytes]  #: Token to be returned to the initiator
-    flags: "g.equirementFlag"  #: Services requested by the initiator
+    flags: RequirementFlag  #: Services requested by the initiator
     lifetime: int  #: Seconds for which the context is valid for
     delegated_creds: Optional["g.Creds"]  #: Delegated credentials
     more_steps: bool  #: More input is required to complete the exchange
@@ -69,8 +72,8 @@ class AcceptSecContextResult(NamedTuple):
 class InitSecContextResult(NamedTuple):
     """Result when initiating a security context"""
     context: "g.SecurityContext"  #: The initiator security context
-    mech: "g.OID"  #: Mechanism used in the security context
-    flags: "g.RequirementFlag"  #: Services available for the context
+    mech: OID  #: Mechanism used in the security context
+    flags: RequirementFlag  #: Services available for the context
     token: Optional[bytes]  #: Token to be sent to the acceptor
     lifetime: int  #: Seconds for which the context is valid for
     more_steps: bool  #: More input is required to complete the exchange
@@ -81,15 +84,15 @@ class InquireContextResult(NamedTuple):
     initiator_name: Optional["g.Name"]  #: Name of the initiator
     target_name: Optional["g.Name"]  #: Name of the acceptor
     lifetime: Optional[int]  #: Time valid for the security context
-    mech: Optional["g.OID"]  #: Mech used to create the security context
-    flags: Optional["g.RequirementFlag"]  #: Services available for the context
+    mech: Optional[OID]  #: Mech used to create the security context
+    flags: Optional[RequirementFlag]  #: Services available for the context
     locally_init: Optional[bool]  #: Context was initiated locally
     complete: Optional[bool]  #: Context has been established and ready to use
 
 
 class StoreCredResult(NamedTuple):
     """Result of the credential storing operation."""
-    mechs: List["g.OID"]  #: Mechs that were stored in the credential store
+    mechs: List[OID]  #: Mechs that were stored in the credential store
     usage: str  #: How the credential can be used
 
 
@@ -103,21 +106,21 @@ class InquireNameResult(NamedTuple):
     """Information about a GSSAPI Name."""
     attrs: List[bytes]  #: Set of attribute names
     is_mech_name: bool  #: Name is a mechanism name
-    mech: "g.OID"  #: The mechanism if is_name_mech is True
+    mech: OID  #: The mechanism if is_name_mech is True
 
 
 class GetNameAttributeResult(NamedTuple):
     """GSSAPI Name attribute values."""
-    values: Iterable[bytes]  #: Raw values
-    display_values: Iterable[bytes]  #: Human-readable values
+    values: List[bytes]  #: Raw values
+    display_values: List[bytes]  #: Human-readable values
     authenticated: bool  #: Attribute has been authenticated
     complete: bool  #: Attribute value is marked as complete
 
 
 class InquireAttrsResult(NamedTuple):
     """Set of attributes supported and known by a mechanism."""
-    mech_attrs: Set["g.OID"]  #: The mechanisms attributes
-    known_mech_attrs: Set["g.OID"]  #: Known attributes of the mechanism
+    mech_attrs: Set[OID]  #: The mechanisms attributes
+    known_mech_attrs: Set[OID]  #: Known attributes of the mechanism
 
 
 class DisplayAttrResult(NamedTuple):
diff --git a/gssapi/raw/names.pyi b/gssapi/raw/names.pyi
new file mode 100644
index 00000000..85d91611
--- /dev/null
+++ b/gssapi/raw/names.pyi
@@ -0,0 +1,170 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.named_tuples import DisplayNameResult
+    from gssapi.raw.oids import OID
+
+class Name:
+    """
+    A GSSAPI Name
+    """
+
+    def __new__(
+        cls,
+        cpy: t.Optional["Name"] = None,
+    ) -> "Name": ...
+
+
+def import_name(
+    name: bytes,
+    name_type: t.Optional["OID"] = None,
+) -> Name:
+    """Convert a string and a name type into a GSSAPI name.
+
+    This method takes a string name and a name type and converts
+    them into a GSSAPI :class:`Name`.
+
+    Args:
+        name (~gssapi.raw.names.Name): the string version of the name
+        name_type (~gssapi.raw.types.MechType): the type of this name
+
+    Returns:
+        Name: the GSSAPI version of the name
+
+    Raises:
+        ~gssapi.exceptions.BadNameTypeError
+        ~gssapi.exceptions.BadNameError
+        ~gssapi.exceptions.BadMechanismError
+    """
+
+
+def display_name(
+    name: Name,
+    name_type: bool = True,
+) -> "DisplayNameResult":
+    """Convert a GSSAPI name into its components.
+
+    This method converts a GSSAPI :class:`Name` back into its
+    text form.  If ``name_type`` is True, it also attempts to
+    retrieve the :class:`~gssapi.raw.types.NameType` of the name (otherwise the
+    returned name type will be ``None``).
+
+    Args:
+        name (~gssapi.raw.names.Name): the name in question
+        name_type (~gssapi.raw.types.MechType): whether or not to retrieve the
+            name type
+
+    Returns:
+        DisplayNameResult: the text part of the name and its type
+
+    Raises:
+        ~gssapi.exceptions.BadNameError
+    """
+
+
+def compare_name(
+    name1: Name,
+    name2: Name,
+) -> bool:
+    """Check two GSSAPI names to see if they are the same.
+
+    This method compares two GSSAPI names, checking to
+    see if they are equivalent.
+
+    Args:
+        name1 (~gssapi.raw.names.Name): the first name to compare
+        name2 (~gssapi.raw.names.Name): the second name to compare
+
+    Returns:
+        bool: whether or not the names are equal
+
+    Raises:
+        ~gssapi.exceptions.BadNameTypeError
+        ~gssapi.exceptions.BadNameError
+    """
+
+
+def export_name(
+    name: Name,
+) -> bytes:
+    """Export a GSSAPI name.
+
+    This method "produces a canonical contigous string representation
+    of a mechanism name, suitable for direct comparison for use in
+    authorization functions".
+
+    The input name must be a valid GSSAPI mechanism name, as generated by
+    :func:`canonicalize_name` or
+    :func:`~gssapi.raw.sec_contexts.accept_sec_context`.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name to export
+
+    Returns:
+        bytes: the exported name
+
+    Raises:
+        ~gssapi.exceptions.MechanismNameRequiredError
+        ~gssapi.exceptions.BadNameTypeError
+        ~gssapi.exceptions.BadNameError
+    """
+
+
+def canonicalize_name(
+    name: Name,
+    mech: "OID",
+) -> Name:
+    """Canonicalize an arbitrary GSSAPI Name into a Mechanism Name
+
+    This method turns any GSSAPI name into a "mechanism name" --
+    a full form name specific to a mechanism.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name to canonicalize
+        mech (~gssapi.raw.types.MechType): the mechanism type to use to
+            canonicalize the name
+
+    Returns:
+        Name: a canonicalized version of the input name
+
+    Raises:
+        ~gssapi.exceptions.BadMechanismError
+        ~gssapi.exceptions.BadNameTypeError
+        ~gssapi.exceptions.BadNameError
+    """
+
+
+def duplicate_name(
+    name: Name,
+) -> Name:
+    """Duplicate a GSSAPI name.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name to duplicate
+
+    Returns:
+        Name: a duplicate of the input name
+
+    Raises:
+        ~gssapi.exceptions.BadNameError
+    """
+
+
+def release_name(
+    name: Name,
+) -> None:
+    """Release a GSSAPI name.
+
+    This method frees a GSSAPI :class:`Name`.
+    You probably won't have to do this.
+
+    Warning:
+        This method is deprecated.  Names are
+        automatically freed by Python.
+
+    Args:
+        name (~gssapi.raw.names.Name): the name in question
+
+    Raises:
+        ~gssapi.exceptions.BadNameError
+    """
diff --git a/gssapi/raw/names.pyx b/gssapi/raw/names.pyx
index a6521dce..f4d22bef 100644
--- a/gssapi/raw/names.pyx
+++ b/gssapi/raw/names.pyx
@@ -41,9 +41,6 @@ cdef extern from "python_gssapi.h":
 
 
 cdef class Name:
-    """
-    A GSSAPI Name
-    """
     # defined in pxd
     # cdef gss_name_t raw_name
 
@@ -66,26 +63,6 @@ cdef class Name:
 
 
 def import_name(name not None, OID name_type=None):
-    """
-    import_name(name, name_type=None)
-    Convert a string and a name type into a GSSAPI name.
-
-    This method takes a string name and a name type and converts
-    them into a GSSAPI :class:`Name`.
-
-    Args:
-        name (bytes): the string version of the name
-        name_type (~gssapi.NameType): the type of this name
-
-    Returns:
-        Name: the GSSAPI version of the name
-
-    Raises:
-        ~gssapi.exceptions.BadNameTypeError
-        ~gssapi.exceptions.BadNameError
-        ~gssapi.exceptions.BadMechanismError
-    """
-
     cdef gss_OID nt
     if name_type is None:
         nt = GSS_C_NO_OID
@@ -114,26 +91,6 @@ def import_name(name not None, OID name_type=None):
 
 
 def display_name(Name name not None, name_type=True):
-    """
-    display_name(name, name_type=True)
-    Convert a GSSAPI name into its components.
-
-    This method converts a GSSAPI :class:`Name` back into its
-    text form.  If ``name_type`` is True, it also attempts to
-    retrieve the :class:`NameType` of the name (otherwise the
-    returned name type will be ``None``).
-
-    Args:
-        name (~gssapi.raw.names.Name): the name in question
-        name_type (bool): whether or not to retrieve the name type
-
-    Returns:
-        DisplayNameResult: the text part of the name and its type
-
-    Raises:
-        ~gssapi.exceptions.BadNameError
-    """
-
     # GSS_C_EMPTY_BUFFER
     cdef gss_buffer_desc output_buffer = gss_buffer_desc(0, NULL)
 
@@ -169,25 +126,6 @@ def display_name(Name name not None, name_type=True):
 
 
 def compare_name(Name name1=None, Name name2=None):
-    """
-    compare_name(name1, name2)
-    Check two GSSAPI names to see if they are the same.
-
-    This method compares two GSSAPI names, checking to
-    see if they are equivalent.
-
-    Args:
-        name1 (~gssapi.raw.names.Name): the first name to compare
-        name2 (~gssapi.raw.names.Name): the second name to compare
-
-    Returns:
-        bool: whether or not the names are equal
-
-    Raises:
-        ~gssapi.exceptions.BadNameTypeError
-        ~gssapi.exceptions.BadNameError
-    """
-
     # check for either value being None
     if name1 is None and name2 is None:
         return True
@@ -208,28 +146,6 @@ def compare_name(Name name1=None, Name name2=None):
 
 
 def export_name(Name name not None):
-    """
-    Export a GSSAPI name.
-
-    This method "produces a canonical contigous string representation
-    of a mechanism name, suitable for direct comparison for use in
-    authorization functions".
-
-    The input name must be a valid GSSAPI mechanism name, as generated
-    by :func:`canonicalize_name` or :func:`accept_sec_context`.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name to export
-
-    Returns:
-        bytes: the exported name
-
-    Raises:
-        ~gssapi.exceptions.MechanismNameRequiredError
-        ~gssapi.exceptions.BadNameTypeError
-        ~gssapi.exceptions.BadNameError
-    """
-
     # GSS_C_EMPTY_BUFFER
     cdef gss_buffer_desc exported_name = gss_buffer_desc(0, NULL)
 
@@ -248,27 +164,6 @@ def export_name(Name name not None):
 
 
 def canonicalize_name(Name name not None, OID mech not None):
-    """
-    canonicalize_name(name, mech)
-    Canonicalize an arbitrary GSSAPI Name into a Mechanism Name
-
-    This method turns any GSSAPI name into a "mechanism name" --
-    a full form name specific to a mechanism.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name to canonicalize
-        mech (~gssapi.MechType): the mechanism type to use to
-            canonicalize the name
-
-    Returns:
-        Name: a canonicalized version of the input name
-
-    Raises:
-        ~gssapi.exceptions.BadMechanismError
-        ~gssapi.exceptions.BadNameTypeError
-        ~gssapi.exceptions.BadNameError
-    """
-
     cdef gss_name_t canonicalized_name
 
     cdef OM_uint32 maj_stat, min_stat
@@ -287,20 +182,6 @@ def canonicalize_name(Name name not None, OID mech not None):
 
 
 def duplicate_name(Name name not None):
-    """
-    duplicate_name(name)
-    Duplicate a GSSAPI name.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name to duplicate
-
-    Returns:
-        Name: a duplicate of the input name
-
-    Raises:
-        ~gssapi.exceptions.BadNameError
-    """
-
     cdef gss_name_t new_name
 
     cdef OM_uint32 maj_stat, min_stat
@@ -316,24 +197,6 @@ def duplicate_name(Name name not None):
 
 
 def release_name(Name name not None):
-    """
-    release_name(name)
-    Release a GSSAPI name.
-
-    This method frees a GSSAPI :class:`Name`.
-    You probably won't have to do this.
-
-    Warning:
-        This method is deprecated.  Names are
-        automatically freed by Python.
-
-    Args:
-        name (~gssapi.raw.names.Name): the name in question
-
-    Raises:
-        ~gssapi.exceptions.BadNameError
-    """
-
     cdef OM_uint32 maj_stat, min_stat
     maj_stat = gss_release_name(&min_stat, &name.raw_name)
     if maj_stat != GSS_S_COMPLETE:
diff --git a/gssapi/raw/oids.pyi b/gssapi/raw/oids.pyi
new file mode 100644
index 00000000..a613d82a
--- /dev/null
+++ b/gssapi/raw/oids.pyi
@@ -0,0 +1,50 @@
+import typing as t
+
+class OID:
+    """
+    A GSSAPI OID
+
+    A new OID may be created by passing the `elements` argument
+    to the constructor.  The `elements` argument should be a
+    `bytes` consisting of the BER-encoded values in the OID.
+
+    To retrive the underlying bytes, use the :func:`bytes`
+    function in Python 3.
+
+    This object is hashable, and may be compared using equality
+    operators.
+    """
+
+    def __new__(
+        cls,
+        cpy: t.Optional["OID"] = None,
+        elements: t.Optional[bytes] = None,
+    ) -> "OID": ...
+
+    @classmethod
+    def from_int_seq(
+        cls,
+        integer_sequence: t.Union[str, t.Iterable[int]],
+    ) -> "OID":
+        """Create a OID from a sequence of integers.
+
+        This method creates an OID from a sequence of integers.
+        The sequence can either be in dotted form as a string,
+        or in list form.
+
+        This method is not for BER-encoded byte strings, which
+        can be passed directly to the OID constructor.
+
+        Args:
+            integer_sequence: either a list of integers or
+                a string in dotted form
+
+        Returns:
+            OID: the OID represented by the given integer sequence
+
+        Raises:
+            ValueError: the sequence is less than two elements long
+        """
+
+    @property
+    def dotted_form(self) -> str: ...
diff --git a/gssapi/raw/oids.pyx b/gssapi/raw/oids.pyx
index b0bd5546..aa0e17af 100644
--- a/gssapi/raw/oids.pyx
+++ b/gssapi/raw/oids.pyx
@@ -11,20 +11,6 @@ cdef inline bint c_compare_oids(gss_OID a, gss_OID b):
 
 
 cdef class OID:
-    """
-    A GSSAPI OID
-
-    A new OID may be created by passing the `elements` argument
-    to the constructor.  The `elements` argument should be a
-    `bytes` consisting of the BER-encoded values in the OID.
-
-    To retrive the underlying bytes, use the :func:`bytes`
-    function in Python 3 or the :meth:`__bytes__` method directly
-    in Python 2.
-
-    This object is hashable, and may be compared using equality
-    operators.
-    """
     # defined in pxd
     # cdef gss_OID_desc raw_oid = NULL
     # cdef bint _free_on_dealloc = NULL
@@ -73,28 +59,6 @@ cdef class OID:
 
     @classmethod
     def from_int_seq(cls, integer_sequence):
-        """
-        from_int_seq(integer_sequence)
-        Create a OID from a sequence of integers.
-
-        This method creates an OID from a sequence of integers.
-        The sequence can either be in dotted form as a string,
-        or in list form.
-
-        This method is not for BER-encoded byte strings, which
-        can be passed directly to the OID constructor.
-
-        Args:
-            integer_sequence: either a list of integers or
-                a string in dotted form
-
-        Returns:
-            OID: the OID represented by the given integer sequence
-
-        Raises:
-            ValueError: the sequence is less than two elements long
-        """
-
         if isinstance(integer_sequence, str):
             integer_sequence = integer_sequence.split('.')
 
diff --git a/gssapi/raw/sec_contexts.pyi b/gssapi/raw/sec_contexts.pyi
new file mode 100644
index 00000000..0c8068cf
--- /dev/null
+++ b/gssapi/raw/sec_contexts.pyi
@@ -0,0 +1,294 @@
+import typing as t
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.chan_bindings import ChannelBindings
+    from gssapi.raw.creds import Creds
+    from gssapi.raw.named_tuples import (
+        AcceptSecContextResult,
+        InitSecContextResult,
+        InquireContextResult,
+    )
+    from gssapi.raw.names import Name
+    from gssapi.raw.oids import OID
+    from gssapi.raw.types import RequirementFlag
+
+class SecurityContext:
+    """
+    A GSSAPI Security Context
+    """
+
+    def __new__(
+        cls,
+        cpy: t.Optional["SecurityContext"] = None,
+    ) -> "SecurityContext": ...
+
+    @property
+    def _started(self) -> bool: ...
+
+
+def init_sec_context(
+    name: "Name",
+    creds: t.Optional["Creds"] = None,
+    context: t.Optional[SecurityContext] = None,
+    mech: t.Optional["OID"] = None,
+    flags: t.Optional[t.Union[
+        int, "RequirementFlag",
+        t.Iterable[int], t.Iterable["RequirementFlag"]
+    ]] = None,
+    lifetime: t.Optional[int] = None,
+    channel_bindings: t.Optional["ChannelBindings"] = None,
+    input_token: t.Optional[bytes] = None,
+) -> "InitSecContextResult":
+    """Initiate a GSSAPI security context.
+
+    This method initiates a GSSAPI security context, targeting the given
+    target name.  To create a basic context, just provide the target name.
+    Further calls used to update the context should pass in the output context
+    of the last call, as well as the input token received from the acceptor.
+
+    Warning:
+        This changes the input context!
+
+    Args:
+        target_name (~gssapi.raw.names.Name): the target for the security
+            context
+        creds (Creds): the credentials to use to initiate the context,
+            or None to use the default credentials
+        context (~gssapi.raw.sec_contexts.SecurityContext): the security
+            context to update, or None to create a new context
+        mech (~gssapi.raw.types.MechType): the mechanism type for this security
+            context, or None for the default mechanism type
+        flags (list): the flags to request for the security context, or
+            None to use the default set: mutual_authentication and
+            out_of_sequence_detection.  This may also be an
+            :class:`IntEnumFlagSet`
+        lifetime (int): the request lifetime of the security context (a value
+            of 0 or None means indefinite)
+        channel_bindings (ChannelBindings): The channel bindings (or None for
+            no channel bindings)
+        input_token (bytes): the token to use to update the security context,
+            or None if you are creating a new context
+
+    Returns:
+        InitSecContextResult: the output security context, the actual mech
+        type, the actual flags used, the output token to send to the acceptor,
+        the actual lifetime of the context (or None if not supported or
+        indefinite), and whether or not more calls are needed to finish the
+        initiation.
+
+    Raises:
+        ~gssapi.exceptions.InvalidTokenError
+        ~gssapi.exceptions.InvalidCredentialsError
+        ~gssapi.exceptions.MissingCredentialsError
+        ~gssapi.exceptions.ExpiredCredentialsError
+        ~gssapi.exceptions.BadChannelBindingsError
+        ~gssapi.exceptions.BadMICError
+        ~gssapi.exceptions.ExpiredTokenError
+        ~gssapi.exceptions.DuplicateTokenError
+        ~gssapi.exceptions.MissingContextError
+        ~gssapi.exceptions.BadNameTypeError
+        ~gssapi.exceptions.BadNameError
+        ~gssapi.exceptions.BadMechanismError
+    """
+
+
+def accept_sec_context(
+    input_token: bytes,
+    acceptor_creds: t.Optional["Creds"] = None,
+    context: t.Optional[SecurityContext] = None,
+    channel_bindings: t.Optional["ChannelBindings"] = None,
+) -> "AcceptSecContextResult":
+    """Accept a GSSAPI security context.
+
+    This method accepts a GSSAPI security context using a token sent by the
+    initiator, using the given credentials.  It can either be used to accept a
+    security context and create a new security context object, or to update an
+    existing security context object.
+
+    Warning:
+        This changes the input context!
+
+    Args:
+        input_token (bytes): the token sent by the context initiator
+        acceptor_creds (Creds): the credentials to be used to accept the
+            context (or None to use the default credentials)
+        context (~gssapi.raw.sec_contexts.SecurityContext): the security
+            context to update (or None to create a new security context object)
+        channel_bindings (ChannelBindings): The channel bindings (or None for
+            no channel bindings)
+
+    Returns:
+        AcceptSecContextResult: the resulting security context, the initiator
+        name, the mechanism being used, the output token, the flags in use,
+        the lifetime of the context (or None for indefinite or not
+        supported), the delegated credentials (valid only if the
+        delegate_to_peer flag is set), and whether or not further token
+        exchanges are needed to finalize the security context.
+
+    Raises:
+        ~gssapi.exceptions.InvalidTokenError
+        ~gssapi.exceptions.InvalidCredentialsError
+        ~gssapi.exceptions.MissingCredentialsError
+        ~gssapi.exceptions.ExpiredCredentialsError
+        ~gssapi.exceptions.BadChannelBindingsError
+        ~gssapi.exceptions.MissingContextError
+        ~gssapi.exceptions.BadMICError
+        ~gssapi.exceptions.ExpiredTokenError
+        ~gssapi.exceptions.DuplicateTokenError
+        ~gssapi.exceptions.BadMechanismError
+    """
+
+
+def inquire_context(
+    context: SecurityContext,
+    initiator_name: bool = True,
+    target_name: bool = True,
+    lifetime: bool = True,
+    mech: bool = True,
+    flags: bool = True,
+    locally_init: bool = True,
+    complete: bool = True,
+) -> "InquireContextResult":
+    """Get information about a security context.
+
+    This method obtains information about a security context, including
+    the initiator and target names, as well as the TTL, mech,
+    flags, and its current state (open vs closed).
+
+    Note:
+        the target name may be ``None`` if it would have been ``GSS_C_NO_NAME``
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the context in
+            question
+
+    Returns:
+        InquireContextResult: the initiator name, the target name, the TTL
+        (can be None for indefinite or not supported), the mech type, the
+        flags, whether or not the context was locally initiated,
+        and whether or not the context is currently fully established
+
+    Raises:
+        ~gssapi.exceptions.MissingContextError
+    """
+
+
+def context_time(
+    context: SecurityContext,
+) -> int:
+    """Get the amount of time for which the given context will remain valid.
+
+    This method determines the amount of time for which the given
+    security context will remain valid.  An expired context will
+    give a result of 0.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the security
+            context in question
+
+    Returns:
+        int: the number of seconds for which the context will be valid
+
+    Raises:
+        ~gssapi.exceptions.ExpiredContextError
+        ~gssapi.exceptions.MissingContextError
+    """
+
+
+def process_context_token(
+    context: SecurityContext,
+    token: bytes,
+) -> None:
+    """Process a token asynchronously.
+
+    This method provides a way to process a token, even if the
+    given security context is not expecting one.  For example,
+    if the initiator has the initSecContext return that the context
+    is complete, but the acceptor is unable to accept the context,
+    and wishes to send a token to the initiator, letting the
+    initiator know of the error.
+
+    Warning:
+        This method has been essentially deprecated by :rfc:`2744`.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the security
+            context against which to process the token
+        token (bytes): the token to process
+
+    Raises:
+        ~gssapi.exceptions.InvalidTokenError
+        ~gssapi.exceptions.MissingContextError
+    """
+
+
+def import_sec_context(
+    token: bytes,
+) -> SecurityContext:
+    """Import a context from another process.
+
+    This method imports a security context established in another process
+    by reading the specified token which was output by
+    :func:`export_sec_context`.
+
+    Raises:
+        ~gssapi.exceptions.MissingContextError
+        ~gssapi.exceptions.InvalidTokenError
+        ~gssapi.exceptions.OperationUnavailableError
+        ~gssapi.exceptions.UnauthorizedError
+    """
+
+
+def export_sec_context(
+    context: SecurityContext,
+) -> bytes:
+    """Export a context for use in another process.
+
+    This method exports a security context, deactivating in the current process
+    and creating a token which can then be imported into another process
+    with :func:`import_sec_context`.
+
+    Warning: this modifies the input context
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the context to send
+            to another process
+
+    Returns:
+        bytes: the output token to be imported
+
+    Raises:
+        ~gssapi.exceptions.ExpiredContextError
+        ~gssapi.exceptions.MissingContextError
+        ~gssapi.exceptions.OperationUnavailableError
+    """
+
+
+def delete_sec_context(
+    context: SecurityContext,
+    local_only: bool = True,
+) -> bytes:
+    """Delete a GSSAPI security context.
+
+    This method deletes a GSSAPI security context,
+    returning an output token to send to the other
+    holder of the security context to notify them
+    of the deletion.
+
+    Note:
+        This method generally should not be used.  :class:`SecurityContext`
+        objects will automatically be freed by Python.
+
+    Args:
+        context (~gssapi.raw.sec_contexts.SecurityContext): the security
+            context in question
+        local_only (bool): should we request local deletion (True), or also
+            remote deletion (False), in which case a token is also returned
+
+    Returns:
+        bytes: the output token (if remote deletion is requested).  Generally
+            this is None, but bytes for compatibility.
+
+    Raises:
+        ~gssapi.exceptions.MissingContextError
+    """
diff --git a/gssapi/raw/sec_contexts.pyx b/gssapi/raw/sec_contexts.pyx
index 5ed9b3c2..d2c92051 100644
--- a/gssapi/raw/sec_contexts.pyx
+++ b/gssapi/raw/sec_contexts.pyx
@@ -75,9 +75,6 @@ cdef extern from "python_gssapi.h":
 
 
 cdef class SecurityContext:
-    """
-    A GSSAPI Security Context
-    """
     # defined in pxd
     # cdef gss_ctx_id_t raw_ctx
 
@@ -115,61 +112,6 @@ def init_sec_context(Name target_name not None, Creds creds=None,
                      flags=None, lifetime=None,
                      ChannelBindings channel_bindings=None,
                      input_token=None):
-    """
-    init_sec_context(target_name, creds=None, context=None, mech=None, \
-flags=None, lifetime=None, channel_bindings=None, input_token=None)
-    Initiate a GSSAPI security context.
-
-    This method initiates a GSSAPI security context, targeting the given
-    target name.  To create a basic context, just provide the target name.
-    Further calls used to update the context should pass in the output context
-    of the last call, as well as the input token received from the acceptor.
-
-    Warning:
-        This changes the input context!
-
-    Args:
-        target_name (~gssapi.raw.names.Name): the target for the security
-            context
-        creds (Creds): the credentials to use to initiate the context,
-            or None to use the default credentials
-        context (~gssapi.raw.sec_contexts.SecurityContext): the security
-            context to update, or None to create a new context
-        mech (~gssapi.MechType): the mechanism type for this security context,
-            or None for the default mechanism type
-        flags (list): the flags to request for the security context, or
-            None to use the default set: mutual_authentication and
-            out_of_sequence_detection.  This may also be an
-            :class:`IntEnumFlagSet`
-        lifetime (int): the request lifetime of the security context (a value
-            of 0 or None means indefinite)
-        channel_bindings (ChannelBindings): The channel bindings (or None for
-            no channel bindings)
-        input_token (bytes): the token to use to update the security context,
-            or None if you are creating a new context
-
-    Returns:
-        InitSecContextResult: the output security context, the actual mech
-        type, the actual flags used, the output token to send to the acceptor,
-        the actual lifetime of the context (or None if not supported or
-        indefinite), and whether or not more calls are needed to finish the
-        initiation.
-
-    Raises:
-        ~gssapi.exceptions.InvalidTokenError
-        ~gssapi.exceptions.InvalidCredentialsError
-        ~gssapi.exceptions.MissingCredentialsError
-        ~gssapi.exceptions.ExpiredCredentialsError
-        ~gssapi.exceptions.BadChannelBindingsError
-        ~gssapi.exceptions.BadMICError
-        ~gssapi.exceptions.ExpiredTokenError
-        ~gssapi.exceptions.DuplicateTokenError
-        ~gssapi.exceptions.MissingContextError
-        ~gssapi.exceptions.BadNameTypeError
-        ~gssapi.exceptions.BadNameError
-        ~gssapi.exceptions.BadMechanismError
-    """
-
     cdef gss_OID mech_oid
     if mech is not None:
         mech_oid = &mech.raw_oid
@@ -249,49 +191,6 @@ flags=None, lifetime=None, channel_bindings=None, input_token=None)
 def accept_sec_context(input_token not None, Creds acceptor_creds=None,
                        SecurityContext context=None,
                        ChannelBindings channel_bindings=None):
-    """
-    accept_sec_context(input_token, acceptor_creds=None, context=None, \
-channel_bindings=None)
-    Accept a GSSAPI security context.
-
-    This method accepts a GSSAPI security context using a token sent by the
-    initiator, using the given credentials.  It can either be used to accept a
-    security context and create a new security context object, or to update an
-    existing security context object.
-
-    Warning:
-        This changes the input context!
-
-    Args:
-        input_token (bytes): the token sent by the context initiator
-        acceptor_creds (Creds): the credentials to be used to accept the
-            context (or None to use the default credentials)
-        context (~gssapi.raw.sec_contexts.SecurityContext): the security
-            context to update (or None to create a new security context object)
-        channel_bindings (ChannelBindings): The channel bindings (or None for
-            no channel bindings)
-
-    Returns:
-        AcceptSecContextResult: the resulting security context, the initiator
-            name, the mechanism being used, the output token, the flags in use,
-            the lifetime of the context (or None for indefinite or not
-            supported), the delegated credentials (valid only if the
-            delegate_to_peer flag is set), and whether or not further token
-            exchanges are needed to finalize the security context.
-
-    Raises:
-        ~gssapi.exceptions.InvalidTokenError
-        ~gssapi.exceptions.InvalidCredentialsError
-        ~gssapi.exceptions.MissingCredentialsError
-        ~gssapi.exceptions.ExpiredCredentialsError
-        ~gssapi.exceptions.BadChannelBindingsError
-        ~gssapi.exceptions.MissingContextError
-        ~gssapi.exceptions.BadMICError
-        ~gssapi.exceptions.ExpiredTokenError
-        ~gssapi.exceptions.DuplicateTokenError
-        ~gssapi.exceptions.BadMechanismError
-    """
-
     cdef gss_channel_bindings_t bdng
     if channel_bindings is not None:
         bdng = channel_bindings.__cvalue__()
@@ -374,32 +273,6 @@ channel_bindings=None)
 def inquire_context(SecurityContext context not None, initiator_name=True,
                     target_name=True, lifetime=True, mech=True,
                     flags=True, locally_init=True, complete=True):
-    """
-    inquire_context(context, initiator_name=True, target_name=True, \
-lifetime=True, mech=True, flags=True, locally_init=True, complete=True)
-    Get information about a security context.
-
-    This method obtains information about a security context, including
-    the initiator and target names, as well as the TTL, mech,
-    flags, and its current state (open vs closed).
-
-    Note:
-        the target name may be ``None`` if it would have been ``GSS_C_NO_NAME``
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the context in
-            question
-
-    Returns:
-        InquireContextResult: the initiator name, the target name, the TTL
-            (can be None for indefinite or not supported), the mech type, the
-            flags, whether or not the context was locally initiated,
-            and whether or not the context is currently fully established
-
-    Raises:
-        ~gssapi.exceptions.MissingContextError
-    """
-
     cdef gss_name_t output_init_name
     cdef gss_name_t *init_name_ptr = NULL
     if initiator_name:
@@ -491,26 +364,6 @@ lifetime=True, mech=True, flags=True, locally_init=True, complete=True)
 
 
 def context_time(SecurityContext context not None):
-    """
-    context_time(context)
-    Get the amount of time for which the given context will remain valid.
-
-    This method determines the amount of time for which the given
-    security context will remain valid.  An expired context will
-    give a result of 0.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the security
-            context in question
-
-    Returns:
-        int: the number of seconds for which the context will be valid
-
-    Raises:
-        ~gssapi.exceptions.ExpiredContextError
-        ~gssapi.exceptions.MissingContextError
-    """
-
     cdef OM_uint32 ttl
 
     cdef OM_uint32 maj_stat, min_stat
@@ -524,30 +377,6 @@ def context_time(SecurityContext context not None):
 
 
 def process_context_token(SecurityContext context not None, token):
-    """
-    process_context_token(context, token)
-    Process a token asynchronously.
-
-    This method provides a way to process a token, even if the
-    given security context is not expecting one.  For example,
-    if the initiator has the initSecContext return that the context
-    is complete, but the acceptor is unable to accept the context,
-    and wishes to send a token to the initiator, letting the
-    initiator know of the error.
-
-    Warning:
-        This method has been essentially deprecated by :rfc:`2744`.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the security
-            context against which to process the token
-        token (bytes): the token to process
-
-    Raises:
-        ~gssapi.exceptions.InvalidTokenError
-        ~gssapi.exceptions.MissingContextError
-    """
-
     cdef gss_buffer_desc token_buffer = gss_buffer_desc(len(token), token)
 
     cdef OM_uint32 maj_stat, min_stat
@@ -561,21 +390,6 @@ def process_context_token(SecurityContext context not None, token):
 
 
 def import_sec_context(token not None):
-    """
-    import_sec_context(token)
-    Import a context from another process.
-
-    This method imports a security context established in another process
-    by reading the specified token which was output by
-    :func:`export_sec_context`.
-
-    Raises:
-        ~gssapi.exceptions.MissingContextError
-        ~gssapi.exceptions.InvalidTokenError
-        ~gssapi.exceptions.OperationUnavailableError
-        ~gssapi.exceptions.UnauthorizedError
-    """
-
     cdef gss_buffer_desc token_buffer = gss_buffer_desc(len(token), token)
 
     cdef gss_ctx_id_t ctx
@@ -594,29 +408,6 @@ def import_sec_context(token not None):
 
 
 def export_sec_context(SecurityContext context not None):
-    """
-    export_sec_context(context)
-    Export a context for use in another process.
-
-    This method exports a security context, deactivating in the current process
-    and creating a token which can then be imported into another process
-    with :func:`import_sec_context`.
-
-    Warning: this modifies the input context
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the context to send
-            to another process
-
-    Returns:
-        bytes: the output token to be imported
-
-    Raises:
-        ~gssapi.exceptions.ExpiredContextError
-        ~gssapi.exceptions.MissingContextError
-        ~gssapi.exceptions.OperationUnavailableError
-    """
-
     cdef gss_buffer_desc output_token = gss_buffer_desc(0, NULL)
 
     cdef OM_uint32 maj_stat, min_stat
@@ -634,33 +425,6 @@ def export_sec_context(SecurityContext context not None):
 
 
 def delete_sec_context(SecurityContext context not None, local_only=True):
-    """
-    delete_sec_context(context, local_only=True)
-    Delete a GSSAPI security context.
-
-    This method deletes a GSSAPI security context,
-    returning an output token to send to the other
-    holder of the security context to notify them
-    of the deletion.
-
-    Note:
-        This method generally should not be used.  :class:`SecurityContext`
-        objects will automatically be freed by Python.
-
-    Args:
-        context (~gssapi.raw.sec_contexts.SecurityContext): the security
-            context in question
-        local_only (bool): should we request local deletion (True), or also
-            remote deletion (False), in which case a token is also returned
-
-    Returns:
-        bytes: the output token (if remote deletion is requested).  Generally
-            this is None, but bytes for compatibility.
-
-    Raises:
-        ~gssapi.exceptions.MissingContextError
-    """
-
     cdef OM_uint32 maj_stat, min_stat
     # GSS_C_EMPTY_BUFFER
     cdef gss_buffer_desc output_token = gss_buffer_desc(0, NULL)
diff --git a/gssapi/raw/types.pyi b/gssapi/raw/types.pyi
new file mode 100644
index 00000000..8d928664
--- /dev/null
+++ b/gssapi/raw/types.pyi
@@ -0,0 +1,174 @@
+import numbers
+import typing as t
+
+from collections.abc import MutableSet
+from enum import IntEnum
+
+if t.TYPE_CHECKING:
+    from gssapi.raw.oids import OID
+
+
+class NameType:
+    """
+    GSSAPI Name Types
+
+    This enum-like object represents GSSAPI name
+    types (to be used with :func:`~gssapi.raw.names.import_name`, etc)
+    """
+    #: GSS_C_NT_ANONYMOUS 1.3.6.1.5.6.3
+    anonymous: "OID" = ...
+    #: GSS_C_NT_EXPORT_NAME 1.3.6.1.5.6.4
+    export: "OID" = ...
+    #: GSS_C_NT_HOSTBASED_SERVICE 1.2.840.113554.1.2.1.4
+    hostbased_service: "OID" = ...
+    #: GSS_C_NT_MACHINE_UID_NAME 1.2.840.113554.1.2.1.2
+    machine_uid: "OID" = ...
+    #: GSS_C_NT_STRING_UID_NAME 1.2.840.113554.1.2.1.3
+    string_uid: "OID" = ...
+    #: GSS_C_NT_USER_NAME 1.2.840.113554.1.2.1.1
+    user: "OID" = ...
+
+    # Provided through optional extensions
+    #: GSS_C_NT_COMPOSITE_EXPORT 1.3.6.1.5.6.6
+    composite_export: "OID" = ...
+    #: GSS_KRB5_NT_PRINCIPAL_NAME 1.2.840.113554.1.2.2.1
+    kerberos_principal: "OID" = ...
+    #: GSS_KRB5_NT_PRINCIPAL_NAME 1.2.840.113554.1.2.2.1
+    krb5_nt_principal_name: "OID" = ...
+
+
+class RequirementFlag(IntEnum):
+    """
+    GSSAPI Requirement Flags
+
+    This :class:`~enum.IntEnum` represents flags used with the
+    :class:`~gssapi.raw.sec_contexts.SecurityContext`-related methods (e.g.
+    :func:`~gssapi.raw.sec_contexts.init_sec_context`)
+
+    The numbers behind the values correspond directly
+    to their C counterparts.
+    """
+    # Note the values are only set here for documentation and type hints
+    delegate_to_peer = 1 #: GSS_C_DELEG_FLAG
+    mutual_authentication = 2 #: GSS_C_MUTUAL_FLAG
+    replay_detection = 4 #: GSS_C_REPLAY_FLAG
+    out_of_sequence_detection = 8 #: GSS_C_SEQUENCE_FLAG
+    confidentiality = 16 #: GSS_C_CONF_FLAG
+    integrity = 32 #: GSS_C_INTEG_FLAG
+    anonymity = 64 #: GSS_C_ANON_FLAG
+    protection_ready = 128 #: GSS_C_PROT_READY_FLAG
+    transferable = 256 #: GSS_C_TRANS_FLAG
+    channel_bound = 2048 #: GSS_C_CHANNEL_BOUND_FLAG
+    dce_style = 4096 #: GSS_C_DCE_STYLE
+    identify = 8192 #: GSS_C_IDENTIFY_FLAG
+    extended_error = 16384 #: GSS_C_EXTENDED_ERROR_FLAG
+    ok_as_delegate = 32768 #: GSS_C_DELEG_POLICY_FLAG
+
+
+class AddressType(IntEnum):
+    """
+    GSSAPI Channel Bindings Address Types
+
+    This :class:`~enum.IntEnum` represents the various address
+    types used with the :class:`~gssapi.raw.chan_bindings.ChannelBindings`
+    structure.
+
+    The numbers behind the values correspond directly
+    to their C counterparts.  There is no value for
+    ``GSS_C_AF_UNSPEC``, since this is represented
+    by ``None``.
+    """
+    # Note the values are only set here for documentation and type hints
+    local = 1 #: GSS_C_AF_LOCAL
+    ip = 2 #: GSS_C_AF_INET
+    arpanet = 3 #: GSS_C_AF_IMPLINK
+    pup = 4 #: GSS_C_AF_PUP
+    chaos = 5 #: GSS_C_AF_CHAOS
+    xerox_ns = 6 #: GSS_C_AF_NS
+    nbs = 7 #: GSS_C_AF_NBS
+    ecma = 8 #: GSS_C_AF_ECMA
+    datakit = 9 #: GSS_C_AF_DATAKIT
+    ccitt = 10 #: GSS_C_AF_CCITT
+    ibm_sna = 11 #: GSS_C_AF_SNA
+    decnet = 12 #: GSS_C_AF_DECnet
+    dli = 13 #: GSS_C_AF_DLI
+    lat = 14 #: GSS_C_AF_LAT
+    hyperchannel = 15 #: GSS_C_AF_HYLINK
+    appletalk = 16 #: GSS_C_AF_APPLETALK
+    bisync = 17 #: GSS_C_AF_BSC
+    dss = 18 #: GSS_C_AF_DSS
+    osi_tp4 = 19 #: GSS_C_AF_OSI
+    x25 = 21 #: GSS_C_AF_X25
+    null = 255 #: GSS_C_AF_NULLADDR
+
+
+class MechType:
+    """
+    GSSAPI Mechanism Types
+
+    This enum-like object contains any mechanism :class:`~gssapi.raw.oids.OID`
+    values registered by imported mechanisms.
+    """
+    kerberos: "OID" #: gss_mech_krb5 1.2.840.113554.1.2.2
+
+
+class GenericFlagSet(MutableSet):
+    """A set backed by a 32-bit integer
+
+    This is a set backed by a 32 bit integer.
+    the members are integers where only one
+    bit is set.
+
+    The class supports normal set operations,
+    as well as traditional "flag set" operations,
+    such as bitwise AND, OR, and XOR.
+    """
+
+    MAX_VAL: int
+
+    def __init__(
+        self,
+        flags: t.Optional[
+            t.Union[GenericFlagSet, numbers.Integral, int]
+        ] = None,
+    ) -> None: ...
+
+    def __contains__(
+        self,
+        flag: object,
+    ) -> bool: ...
+
+    def __iter__(self) -> t.Iterator[int]: ...
+
+    def __len__(self) -> int: ...
+
+    def add(
+        self,
+        flag: int,
+    ) -> None: ...
+
+    def discard(
+        self,
+        flag: int,
+    ) -> None: ...
+
+
+class IntEnumFlagSet(GenericFlagSet):
+    """A set backed by a 32-bit integer with enum members
+
+    This class is a :class:`GenericFlagSet` where the returned
+    members are values in an :class:`~enum.IntEnum`.
+
+    It functions exactly like a `GenericFlagSet`, except that
+    it also supports bitwise operations with the enum values.
+    """
+
+    def __init__(
+        self,
+        enum: t.Type[IntEnum],
+        flags: t.Optional[
+            t.Union[GenericFlagSet, numbers.Integral, int]
+        ] = None,
+    ) -> None: ...
+
+    def __iter__(self) -> t.Iterator[IntEnum]: ...
diff --git a/gssapi/raw/types.pyx b/gssapi/raw/types.pyx
index 322d6b16..37697a13 100644
--- a/gssapi/raw/types.pyx
+++ b/gssapi/raw/types.pyx
@@ -16,13 +16,6 @@ from collections.abc import MutableSet
 
 
 class NameType(object):
-    """
-    GSSAPI Name Types
-
-    This enum-like object represents GSSAPI name
-    types (to be used with :func:`import_name`, etc)
-    """
-
     # mech-agnostic name types
     hostbased_service = c_make_oid(GSS_C_NT_HOSTBASED_SERVICE)
     # NB(directxman12): skip GSS_C_NT_HOSTBASED_SERVICE_X since it's deprecated
@@ -36,17 +29,6 @@ class NameType(object):
 
 
 class RequirementFlag(IntEnum, metaclass=ExtendableEnum):
-    """
-    GSSAPI Requirement Flags
-
-    This :class:`~enum.IntEnum` represents flags used with the
-    :class:`SecurityContext`-related methods (e.g.
-    :func:`init_sec_context`)
-
-    The numbers behind the values correspond directly
-    to their C counterparts.
-    """
-
     delegate_to_peer = GSS_C_DELEG_FLAG
     mutual_authentication = GSS_C_MUTUAL_FLAG
     replay_detection = GSS_C_REPLAY_FLAG
@@ -68,18 +50,6 @@ class RequirementFlag(IntEnum, metaclass=ExtendableEnum):
 
 
 class AddressType(IntEnum, metaclass=ExtendableEnum):
-    """
-    GSSAPI Channel Bindings Address Types
-
-    This :class:`~enum.IntEnum` represents the various address
-    types used with the :class:`ChannelBindings` structure.
-
-    The numbers behind the values correspond directly
-    to their C counterparts.  There is no value for
-    ``GSS_C_AF_UNSPEC``, since this is represented
-    by ``None``.
-    """
-
     # unspecified = GSS_C_AF_UNSPEC  # None --> GSS_C_AF_UNSPEC
     local = GSS_C_AF_LOCAL
     ip = GSS_C_AF_INET
@@ -105,28 +75,12 @@ class AddressType(IntEnum, metaclass=ExtendableEnum):
 
 
 class MechType(object):
-    """
-    GSSAPI Mechanism Types
-
-    This enum-like object contains any mechanism :class:`OID`
-    values registered by imported mechanisms.
-    """
     pass
 
     # these are added in by the individual mechanism files on import
 
 
 class GenericFlagSet(MutableSet):
-    """A set backed by a 32-bit integer
-
-    This is a set backed by a 32 bit integer.
-    the members are integers where only one
-    bit is set.
-
-    The class supports normal set operations,
-    as well as traditional "flag set" operations,
-    such as bitwise AND, OR, and XOR.
-    """
 
     __slots__ = '_val'
     MAX_VAL = 1 << 31
@@ -221,14 +175,6 @@ class GenericFlagSet(MutableSet):
 
 
 class IntEnumFlagSet(GenericFlagSet):
-    """A set backed by a 32-bit integer with enum members
-
-    This class is a :class:`GenericFlagSet` where the returned
-    members are values in an :class:`~enum.IntEnum`.
-
-    It functions exactly like a `GenericFlagSet`, except that
-    it also supports bitwise operations with the enum values.
-    """
 
     __slots__ = ('_val', '_enum')
 
diff --git a/gssapi/sec_contexts.py b/gssapi/sec_contexts.py
index 80787ac4..97d54d03 100644
--- a/gssapi/sec_contexts.py
+++ b/gssapi/sec_contexts.py
@@ -1,6 +1,11 @@
+import typing as t
+
+from gssapi.raw import chan_bindings as rchan_bindings
 from gssapi.raw import sec_contexts as rsec_contexts
 from gssapi.raw import message as rmessage
 from gssapi.raw import named_tuples as tuples
+from gssapi.raw import names as rnames
+from gssapi.raw import oids as roids
 from gssapi.raw.types import RequirementFlag, IntEnumFlagSet
 
 import gssapi.exceptions as excs
@@ -24,18 +29,37 @@ class SecurityContext(rsec_contexts.SecurityContext,
     credentials object will not be preserved, however).
     """
 
-    def __new__(cls, base=None, token=None,
-                name=None, creds=None, lifetime=None, flags=None,
-                mech=None, channel_bindings=None, usage=None):
+    def __new__(
+        cls,
+        base: t.Optional[rsec_contexts.SecurityContext] = None,
+        token: t.Optional[bytes] = None,
+        name: t.Optional[rnames.Name] = None,
+        creds: t.Optional[Credentials] = None,
+        lifetime: t.Optional[int] = None,
+        flags: t.Optional[int] = None,
+        mech: t.Optional[roids.OID] = None,
+        channel_bindings: t.Optional[rchan_bindings.ChannelBindings] = None,
+        usage: t.Optional[str] = None,
+    ) -> "SecurityContext":
 
         if token is not None:
             base = rsec_contexts.import_sec_context(token)
 
-        return super(SecurityContext, cls).__new__(cls, base)
-
-    def __init__(self, base=None, token=None,
-                 name=None, creds=None, lifetime=None, flags=None,
-                 mech=None, channel_bindings=None, usage=None):
+        return t.cast("SecurityContext",
+                      super(SecurityContext, cls).__new__(cls, base))
+
+    def __init__(
+        self,
+        base: t.Optional[rsec_contexts.SecurityContext] = None,
+        token: t.Optional[bytes] = None,
+        name: t.Optional[rnames.Name] = None,
+        creds: t.Optional[Credentials] = None,
+        lifetime: t.Optional[int] = None,
+        flags: t.Optional[int] = None,
+        mech: t.Optional[roids.OID] = None,
+        channel_bindings: t.Optional[rchan_bindings.ChannelBindings] = None,
+        usage: t.Optional[str] = None,
+    ) -> None:
         """
         The constructor creates a new security context, but does not begin
         the initiate or accept process.
@@ -123,14 +147,17 @@ def __init__(self, base=None, token=None,
                 raise excs.UnknownUsageError(msg, obj="security context")
 
         # This is to work around an MIT krb5 bug (see the `complete` property)
-        self._complete = None
+        self._complete: t.Optional[bool] = None
 
     # NB(directxman12): DO NOT ADD AN __del__ TO THIS CLASS -- it screws up
     #                   the garbage collector if _last_tb is still defined
 
     # TODO(directxman12): implement flag properties
 
-    def get_signature(self, message):
+    def get_signature(
+        self,
+        message: bytes,
+    ) -> bytes:
         """Calculate the signature for a message.
 
         This method calculates the signature (called a MIC) for
@@ -154,7 +181,11 @@ def get_signature(self, message):
         # TODO(directxman12): check flags?
         return rmessage.get_mic(self, message)
 
-    def verify_signature(self, message, mic):
+    def verify_signature(
+        self,
+        message: bytes,
+        mic: bytes,
+    ) -> int:
         """Verify the signature for a message.
 
         This method verifies that a signature (generated by
@@ -167,6 +198,9 @@ def verify_signature(self, message, mic):
             message (bytes): the message
             mic (bytes): the signature to verify
 
+        Returns:
+            int: the QoP used.
+
         Raises:
             ~gssapi.exceptions.BadMICError: the signature was not valid
             ~gssapi.exceptions.InvalidTokenError
@@ -180,7 +214,11 @@ def verify_signature(self, message, mic):
 
         return rmessage.verify_mic(self, message, mic)
 
-    def wrap(self, message, encrypt):
+    def wrap(
+        self,
+        message: bytes,
+        encrypt: bool,
+    ) -> tuples.WrapResult:
         """Wrap a message, optionally with encryption
 
         This wraps a message, signing it and optionally
@@ -192,7 +230,7 @@ def wrap(self, message, encrypt):
 
         Returns:
             WrapResult: the wrapped message and details about it
-                (e.g. whether encryption was used succesfully)
+            (e.g. whether encryption was used succesfully)
 
         Raises:
             ~gssapi.exceptions.ExpiredContextError
@@ -202,7 +240,10 @@ def wrap(self, message, encrypt):
 
         return rmessage.wrap(self, message, encrypt)
 
-    def unwrap(self, message):
+    def unwrap(
+        self,
+        message: bytes,
+    ) -> tuples.UnwrapResult:
         """Unwrap a wrapped message.
 
         This method unwraps/unencrypts a wrapped message,
@@ -213,7 +254,7 @@ def unwrap(self, message):
 
         Returns:
             UnwrapResult: the unwrapped message and details about it
-                (e.g. wheter encryption was used)
+            (e.g. wheter encryption was used)
 
         Raises:
             ~gssapi.exceptions.InvalidTokenError
@@ -228,7 +269,10 @@ def unwrap(self, message):
 
         return rmessage.unwrap(self, message)
 
-    def encrypt(self, message):
+    def encrypt(
+        self,
+        message: bytes,
+    ) -> bytes:
         """Encrypt a message.
 
         This method wraps and encrypts a message, similarly to
@@ -258,7 +302,10 @@ def encrypt(self, message):
 
         return res.message
 
-    def decrypt(self, message):
+    def decrypt(
+        self,
+        message: bytes,
+    ) -> bytes:
         """Decrypt a message.
 
         This method decrypts and unwraps a message, verifying the signature
@@ -297,8 +344,11 @@ def decrypt(self, message):
 
         return res.message
 
-    def get_wrap_size_limit(self, desired_output_size,
-                            encrypted=True):
+    def get_wrap_size_limit(
+        self,
+        desired_output_size: int,
+        encrypted: bool = True,
+    ) -> int:
         """Calculate the maximum message size for a given wrapped message size.
 
         This method calculates the maximum input message size for a given
@@ -321,7 +371,10 @@ def get_wrap_size_limit(self, desired_output_size,
         return rmessage.wrap_size_limit(self, desired_output_size,
                                         encrypted)
 
-    def process_token(self, token):
+    def process_token(
+        self,
+        token: bytes,
+    ) -> None:
         """Process an output token asynchronously.
 
         This method processes an output token even when the security context
@@ -340,7 +393,7 @@ def process_token(self, token):
 
         rsec_contexts.process_context_token(self, token)
 
-    def export(self):
+    def export(self) -> bytes:
         """Export a security context.
 
         This method exports a security context, allowing it to be passed
@@ -361,7 +414,10 @@ def export(self):
                      'mech', 'flags', 'locally_init', 'complete')
 
     @_utils.check_last_err
-    def _inquire(self, **kwargs):
+    def _inquire(
+        self,
+        **kwargs: bool,
+    ) -> tuples.InquireContextResult:
         """Inspect the security context for information
 
         This method inspects the security context for information.
@@ -382,7 +438,7 @@ def _inquire(self, **kwargs):
 
         Returns:
             InquireContextResult: the results of the inquiry, with unused
-                fields set to None
+            fields set to None
 
         Raises:
             ~gssapi.exceptions.MissingContextError
@@ -415,12 +471,12 @@ def _inquire(self, **kwargs):
                                            res.complete)
 
     @property
-    def lifetime(self):
+    def lifetime(self) -> int:
         """The amount of time for which this context remains valid"""
         return rsec_contexts.context_time(self)
 
     @property
-    def delegated_creds(self):
+    def delegated_creds(self) -> t.Optional[Credentials]:
         """The credentials delegated from the initiator to the acceptor
 
         .. warning::
@@ -442,28 +498,32 @@ def delegated_creds(self):
     locally_initiated = _utils.inquire_property(
         'locally_init', 'Whether this context was locally intiated')
 
-    @property
+    @property  # type: ignore # https://github.com/python/mypy/issues/1362
     @_utils.check_last_err
-    def complete(self):
+    def complete(self) -> bool:
         """Whether negotiation for this context has been completed"""
         # NB(directxman12): MIT krb5 has a bug where it refuses to
         #                   inquire about partially completed contexts,
         #                   so we can't just use `self._inquire` generally
         if self._started:
-            if self._complete is None:
+            complete = self._complete
+            if complete is None:
                 try:
-                    res = self._inquire(complete=True).complete
+                    complete = self._inquire(complete=True).complete
                 except excs.MissingContextError:
                     return False
                 else:
-                    self._complete = res
+                    self._complete = complete
 
-            return self._complete
+            return complete
         else:
             return False
 
     @_utils.catch_and_return_token
-    def step(self, token=None):
+    def step(
+        self,
+        token: t.Optional[bytes] = None,
+    ) -> t.Optional[bytes]:
         """Perform a negotation step.
 
         This method performs a negotiation step based on the usage type
@@ -518,11 +578,14 @@ def step(self, token=None):
         """
 
         if self.usage == 'accept':
-            return self._acceptor_step(token=token)
+            return self._acceptor_step(token=token or b"")
         else:
             return self._initiator_step(token=token)
 
-    def _acceptor_step(self, token):
+    def _acceptor_step(
+        self,
+        token: bytes,
+    ) -> t.Optional[bytes]:
         res = rsec_contexts.accept_sec_context(token, self._creds,
                                                self, self._channel_bindings)
 
@@ -535,7 +598,10 @@ def _acceptor_step(self, token):
 
         return res.token
 
-    def _initiator_step(self, token=None):
+    def _initiator_step(
+        self,
+        token: t.Optional[bytes] = None,
+    ) -> t.Optional[bytes]:
         res = rsec_contexts.init_sec_context(self._target_name, self._creds,
                                              self, self._mech,
                                              self._desired_flags,
@@ -548,6 +614,8 @@ def _initiator_step(self, token=None):
         return res.token
 
     # pickle protocol support
-    def __reduce__(self):
+    def __reduce__(
+        self,
+    ) -> t.Tuple[t.Type["SecurityContext"], t.Tuple[None, bytes]]:
         # the unpickle arguments to new are (base=None, token=self.export())
         return (type(self), (None, self.export()))
diff --git a/gssapi/tests/test_high_level.py b/gssapi/tests/test_high_level.py
index badd597e..d7c43aaf 100644
--- a/gssapi/tests/test_high_level.py
+++ b/gssapi/tests/test_high_level.py
@@ -24,7 +24,7 @@
 SERVICE_PRINCIPAL = TARGET_SERVICE_NAME + b'/' + FQDN
 
 # disable error deferring to catch errors immediately
-gssctx.SecurityContext.__DEFER_STEP_ERRORS__ = False
+gssctx.SecurityContext.__DEFER_STEP_ERRORS__ = False  # type: ignore
 
 
 class _GSSAPIKerberosTestCase(kt.KerberosTestCase):
diff --git a/mypy.ini b/mypy.ini
new file mode 100644
index 00000000..71944250
--- /dev/null
+++ b/mypy.ini
@@ -0,0 +1,31 @@
+[mypy]
+exclude = (?x)(
+    setup.py
+    | docs/     # doc files
+    | build/    # temp build folder
+  )
+show_error_codes = True
+show_column_numbers = True
+disallow_any_unimported = true
+disallow_untyped_calls = true
+disallow_untyped_defs = true
+disallow_incomplete_defs = true
+check_untyped_defs = true
+disallow_untyped_decorators = true
+warn_redundant_casts = true
+warn_unused_ignores = true
+
+[mypy-gssapi.tests.*]
+disallow_any_unimported = false
+disallow_untyped_calls = false
+disallow_untyped_defs = false
+check_untyped_defs = false
+
+[mypy-k5test]
+ignore_missing_imports = True
+
+[mypy-k5test.unit]
+ignore_missing_imports = True
+
+[mypy-parameterized]
+ignore_missing_imports = True
diff --git a/setup.py b/setup.py
index 1daa7438..eba32494 100755
--- a/setup.py
+++ b/setup.py
@@ -349,6 +349,10 @@ def gssapi_modules(lst):
     author_email='jborean93@gmail.com',
     packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',
               'gssapi.tests'],
+    package_data={
+        "gssapi": ["py.typed"],
+        "gssapi.raw": ["*.pyi"],
+    },
     description='Python GSSAPI Wrapper',
     long_description=long_desc,
     license='LICENSE.txt',
diff --git a/test-requirements.txt b/test-requirements.txt
index c0a63b3a..5085d282 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,3 +3,5 @@ parameterized
 Cython
 k5test
 decorator
+mypy
+types-decorator
\ No newline at end of file

From 92bb1c2e3029a294d5b84d0632ff41d0b50f1a38 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Wed, 16 Feb 2022 06:54:13 +1000
Subject: [PATCH 19/49] Various doc fixes and update maintainer list

Signed-off-by: Jordan Borean 
---
 README.txt                      |  3 ++-
 gssapi/creds.py                 | 20 ++++++++++----------
 gssapi/raw/creds.pyi            | 18 ++++++++++--------
 gssapi/raw/ext_cred_store.pyi   | 12 ++++++------
 gssapi/raw/ext_krb5.pyi         |  4 ++--
 gssapi/raw/ext_password.pyi     |  8 ++++----
 gssapi/raw/ext_password_add.pyi | 16 ++++++++--------
 gssapi/raw/ext_rfc5587.pyi      |  2 +-
 gssapi/raw/ext_rfc6680.pyi      |  2 +-
 gssapi/raw/ext_s4u.pyi          | 22 +++++++++++-----------
 gssapi/raw/misc.pyi             |  4 ++--
 gssapi/raw/named_tuples.py      | 10 +++++-----
 gssapi/raw/oids.pyi             |  2 +-
 gssapi/raw/sec_contexts.pyi     | 10 +++++-----
 gssapi/sec_contexts.py          |  3 ++-
 15 files changed, 70 insertions(+), 66 deletions(-)

diff --git a/README.txt b/README.txt
index da63ece1..9154f7b0 100644
--- a/README.txt
+++ b/README.txt
@@ -170,8 +170,9 @@ The Team
 
 (GitHub usernames in parentheses)
 
-* Robbie Harwood (@frozencemetery) - current maintainer and developer
+* Jordan Borean (@jborean93) - current maintainer and developer
 * Simo Sorce (@simo5) - developer
+* Robbie Harwood (@frozencemetery) - author emeritus
 * Solly Ross (@directxman12) - author emeritus
 * Hugh Cole-Baker (@sigmaris) - author emeritus
 
diff --git a/gssapi/creds.py b/gssapi/creds.py
index 0141c648..4ea53bfd 100644
--- a/gssapi/creds.py
+++ b/gssapi/creds.py
@@ -90,7 +90,7 @@ def name(self) -> rnames.Name:
 
     @property
     def lifetime(self) -> int:
-        """Get the remaining lifetime of these credentials"""
+        """Get the remaining lifetime of these credentials, in seconds"""
         return t.cast(int,
                       self.inquire(name=False, lifetime=True,
                                    usage=False, mechs=False).lifetime)
@@ -137,8 +137,8 @@ def acquire(
         Args:
             name (~gssapi.names.Name): the name associated with the
                 credentials, or None for the default name
-            lifetime (int): the desired lifetime of the credentials, or None
-                for indefinite
+            lifetime (int): the desired lifetime of the credentials in seconds,
+                or None for indefinite
             mechs (list): the desired :class:`MechType` OIDs to be used
                 with the credentials, or None for the default set
             usage (str): the usage for the credentials -- either 'both',
@@ -253,8 +253,8 @@ def impersonate(
 
         Args:
             name (~gssapi.names.Name): the name to impersonate
-            lifetime (int): the desired lifetime of the new credentials,
-                or None for indefinite
+            lifetime (int): the desired lifetime of the new credentials in
+                seconds, or None for indefinite
             mechs (list): the desired :class:`MechType` OIDs for the new
                 credentials
             usage (str): the desired usage for the new credentials -- either
@@ -326,13 +326,13 @@ def inquire_by_mech(
         about them.
 
         Args:
-            mech (~gssapi.OID): the mechanism for which to retrive the
+            mech (~gssapi.OID): the mechanism for which to retrieve the
                 information
             name (bool): get the name associated with the credentials
             init_lifetime (bool): get the remaining initiate lifetime for
-                the credentials
+                the credentials in seconds
             accept_lifetime (bool): get the remaining accept lifetime for
-                the credentials
+                the credentials in seconds
             usage (bool): get the usage for the credentials
 
         Returns:
@@ -396,9 +396,9 @@ def add(
             usage (str): the usage for the credentials -- either 'both',
                 'initiate', or 'accept'
             init_lifetime (int): the desired initiate lifetime of the
-                credentials, or None for indefinite
+                credentials in seconds, or None for indefinite
             accept_lifetime (int): the desired accept lifetime of the
-                credentials, or None for indefinite
+                credentials in seconds, or None for indefinite
             impersonator (Credentials): the credentials to use to impersonate
                 the given name, or None to not acquire normally
                 (:requires-ext:`s4u`)
diff --git a/gssapi/raw/creds.pyi b/gssapi/raw/creds.pyi
index 19f5e162..16e5d94e 100644
--- a/gssapi/raw/creds.pyi
+++ b/gssapi/raw/creds.pyi
@@ -37,8 +37,8 @@ def acquire_cred(
     Args:
         name (~gssapi.raw.names.Name): the name for which to acquire the
             credentials (or None for the "no name" functionality)
-        lifetime (int): the lifetime for the credentials (or None for
-            indefinite)
+        lifetime (int): the lifetime in seconds for the credentials (or None
+            for indefinite)
         mechs (~gssapi.raw.types.MechType): the desired mechanisms for which
             the credentials should work, or None for the default set
         usage (str): the usage type for the credentials: may be
@@ -46,8 +46,8 @@ def acquire_cred(
 
     Returns:
         AcquireCredResult: the resulting credentials, the actual mechanisms
-        with which they may be used, and their actual lifetime (or None for
-        indefinite or not supported)
+        with which they may be used, and their actual lifetime in seconds (or
+        None for indefinite or not supported)
 
     Raises:
         ~gssapi.exceptions.BadMechanismError
@@ -103,9 +103,9 @@ def add_cred(
         usage (str): usage type for credentials.  Possible values:
             'initiate' (default), 'accept', 'both' (failsafe).
         init_lifetime (int): lifetime of credentials for use in initiating
-            security contexts (None for indefinite)
+            security contexts in seconds (None for indefinite)
         accept_lifetime (int): lifetime of credentials for use in accepting
-            security contexts (None for indefinite)
+            security contexts in seconds (None for indefinite)
         mutate_input (bool): whether to mutate the input credentials (True)
             or produce a new set of credentials (False).  Defaults to False
 
@@ -172,8 +172,10 @@ def inquire_cred_by_mech(
         creds (Creds): the credentials to inspect
         mech (~gssapi.OID): the desired mechanism
         name (bool): get the Name associated with the credentials
-        init_lifetime (bool): get the initiator TTL for the credentials
-        accept_lifetime (bool): get the acceptor TTL for the credentials
+        init_lifetime (bool): get the initiator TTL for the credentials (in
+            seconds)
+        accept_lifetime (bool): get the acceptor TTL for the credentials (in
+            seconds)
         usage (bool): get the usage type of the credentials
 
     Returns:
diff --git a/gssapi/raw/ext_cred_store.pyi b/gssapi/raw/ext_cred_store.pyi
index b025f81e..e3ae9b47 100644
--- a/gssapi/raw/ext_cred_store.pyi
+++ b/gssapi/raw/ext_cred_store.pyi
@@ -32,8 +32,8 @@ def acquire_cred_from(
             See :doc:`credstore` for valid values
         name (~gssapi.raw.names.Name): the name associated with the
             credentials, or None for the default name
-        lifetime (int): the desired lifetime of the credentials, or None
-            for indefinite
+        lifetime (int): the desired lifetime of the credentials in seconds, or
+            None for indefinite
         mechs (list): the desired mechanisms to be used with these
             credentials, or None for the default set
         usage (str): the usage for these credentials -- either 'both',
@@ -80,10 +80,10 @@ def add_cred_from(
             credentials
         usage (str): the usage for these credentials -- either 'both',
             'initiate', or 'accept'
-        init_lifetime (int): the desired initiate lifetime of the
-            credentials, or None for indefinite
-        accept_lifetime (int): the desired accept lifetime of the
-            credentials, or None for indefinite
+        init_lifetime (int): the desired initiate lifetime of the credentials
+            in seconds, or None for indefinite
+        accept_lifetime (int): the desired accept lifetime of the credentials
+            in seconds, or None for indefinite
 
     Returns:
         AcquireCredResult: the new credentials set and information about
diff --git a/gssapi/raw/ext_krb5.pyi b/gssapi/raw/ext_krb5.pyi
index f109c8b0..6d74b029 100644
--- a/gssapi/raw/ext_krb5.pyi
+++ b/gssapi/raw/ext_krb5.pyi
@@ -130,7 +130,7 @@ def krb5_export_lucid_sec_context(
     context: "SecurityContext",
     version: int,
 ) -> Krb5LucidContext:
-    """Retuns a non-opaque version of the internal context info.
+    """Returns a non-opaque version of the internal context info.
 
     Gets information about the Kerberos security context passed in. Currently
     only version 1 is known and supported by this library.
@@ -240,7 +240,7 @@ def krb5_get_tkt_flags(
 
     Note:
         Heimdal can only get the tkt flags on the acceptor security context.
-        MIT is able to get the tkt flags on initators and acceptors.
+        MIT is able to get the tkt flags on initiators and acceptors.
 
     Args:
         context (~gssapi.raw.sec_contexts.SecurityContext): the security
diff --git a/gssapi/raw/ext_password.pyi b/gssapi/raw/ext_password.pyi
index c5e101c2..f926701f 100644
--- a/gssapi/raw/ext_password.pyi
+++ b/gssapi/raw/ext_password.pyi
@@ -24,8 +24,8 @@ def acquire_cred_with_password(
     Args:
         name (~gssapi.raw.names.Name): the name to acquire credentials for
         password (bytes): the password used to acquire credentialss with
-        lifetime (int): the lifetime for the credentials (or None for
-            indefinite)
+        lifetime (int): the lifetime for the credentials in seconds (or None
+            for indefinite)
         mechs (~gssapi.raw.types.MechType): the desired mechanisms for which
             the credentials should work (or None for the default set)
         usage (str): usage type for credentials.  Possible values:
@@ -33,8 +33,8 @@ def acquire_cred_with_password(
 
     Returns:
         AcquireCredResult: the resulting credentials, the actual mechanisms
-        with which they may be used, and their actual lifetime (or None for
-        indefinite or not supported)
+        with which they may be used, and their actual lifetime in seconds (or
+        None for indefinite or not supported)
 
     Raises:
         ~gssapi.exceptions.GSSError
diff --git a/gssapi/raw/ext_password_add.pyi b/gssapi/raw/ext_password_add.pyi
index 32b270b3..a09bb3d2 100644
--- a/gssapi/raw/ext_password_add.pyi
+++ b/gssapi/raw/ext_password_add.pyi
@@ -31,17 +31,17 @@ def add_cred_with_password(
         password (bytes): the password used to acquire credentialss with
         usage (str): the usage type for the credentials: may be
             'initiate', 'accept', or 'both'
-        init_lifetime (int): the lifetime for the credentials to remain valid
-            when using them to initiate security contexts (or None for
-            indefinite)
-        accept_lifetime (int): the lifetime for the credentials to remain
-            valid when using them to accept security contexts (or None for
-            indefinite)
+        init_lifetime (int): the lifetime, in seconds, for the credentials to
+            remain valid when using them to initiate security contexts (or None
+            for indefinite)
+        accept_lifetime (int): the lifetime, in seconds, for the credentials to
+            remain valid when using them to accept security contexts (or None
+            for indefinite)
 
     Returns:
         AddCredResult: the actual mechanisms with which the credentials may be
-        used, the actual initiator TTL, and the actual acceptor TTL (the TTLs
-        may be None for indefinite or not supported)
+        used, the actual initiator TTL in seconds, and the actual acceptor TTL
+        in seconds (the TTLs may be None for indefinite or not supported)
 
     Raises:
         ~gssapi.exceptions.GSSError
diff --git a/gssapi/raw/ext_rfc5587.pyi b/gssapi/raw/ext_rfc5587.pyi
index b1a6ee31..332a4063 100644
--- a/gssapi/raw/ext_rfc5587.pyi
+++ b/gssapi/raw/ext_rfc5587.pyi
@@ -51,7 +51,7 @@ def display_mech_attr(
     """Returns information about attributes in human readable form.
 
     Args:
-        attr (~gssapi.OID): Mechanism attribute to retrive names and
+        attr (~gssapi.OID): Mechanism attribute to retrieve names and
             descriptions of
 
     Returns:
diff --git a/gssapi/raw/ext_rfc6680.pyi b/gssapi/raw/ext_rfc6680.pyi
index 862e6163..e84efb07 100644
--- a/gssapi/raw/ext_rfc6680.pyi
+++ b/gssapi/raw/ext_rfc6680.pyi
@@ -40,7 +40,7 @@ def inquire_name(
 ) -> "InquireNameResult":
     """Get information about a Name.
 
-    This method retrives information about the given name, including
+    This method retrieves information about the given name, including
     the set of attribute names for the given name, as well as whether or
     not the name is a mechanism name.  Additionally, if the given name is
     a mechanism name, the associated mechansim is returned as well.
diff --git a/gssapi/raw/ext_s4u.pyi b/gssapi/raw/ext_s4u.pyi
index 85756b9d..87987fcf 100644
--- a/gssapi/raw/ext_s4u.pyi
+++ b/gssapi/raw/ext_s4u.pyi
@@ -26,7 +26,7 @@ def acquire_cred_impersonate_name(
             permissions to impersonate the target name
         name (~gssapi.raw.names.Name): the name to impersonate
         lifetime (int): the lifetime for the credentials (or None for
-            indefinite)
+            indefinite) in seconds
         mechs (~gssapi.raw.types.MechType): the desired mechanisms for which
             the credentials should work (or None for the default set)
         usage (str): the usage type for the credentials: may be
@@ -34,8 +34,8 @@ def acquire_cred_impersonate_name(
 
     Returns:
         AcquireCredResult: the resulting credentials, the actual mechanisms
-        with which they may be used, and their actual lifetime (or None for
-        indefinite or not support)
+        with which they may be used, and their actual lifetime in seconds (or
+        None for indefinite or not support)
 
     Raises:
         ~gssapi.exceptions.GSSError
@@ -69,17 +69,17 @@ def add_cred_impersonate_name(
             singular and required, unlike acquireCredImpersonateName
         usage (str): the usage type for the credentials: may be
             'initiate', 'accept', or 'both'
-        init_lifetime (int): the lifetime for the credentials to remain
-            valid when using them to initiate security contexts (or None for
-            indefinite)
-        accept_lifetime (int): the lifetime for the credentials to remain
-            valid when using them to accept security contexts (or None for
-            indefinite)
+        init_lifetime (int): the lifetime, in seconds, for the credentials to
+            remain valid when using them to initiate security contexts (or None
+            for indefinite)
+        accept_lifetime (int): the lifetime, in seconds, for the credentials to
+            remain valid when using them to accept security contexts (or None
+            for indefinite)
 
     Returns:
         AddCredResult: the actual mechanisms with which the credentials may be
-        used, the actual initiator TTL, and the actual acceptor TTL (the TTLs
-        may be None for indefinite or not supported)
+        used, the actual initiator TTL in seconds, and the actual acceptor TTL
+        in seconds (the TTLs may be None for indefinite or not supported)
 
     Raises:
         ~gssapi.exceptions.GSSError
diff --git a/gssapi/raw/misc.pyi b/gssapi/raw/misc.pyi
index 00c17a93..574b178f 100644
--- a/gssapi/raw/misc.pyi
+++ b/gssapi/raw/misc.pyi
@@ -20,7 +20,7 @@ def inquire_names_for_mech(
     inquire_names_for_mech(mech)
     Get the name types supported by a mechanism.
 
-    This method retrives the different name types supported by
+    This method retrieves the different name types supported by
     the given mechanism.
 
     Args:
@@ -123,7 +123,7 @@ class GSSError(Exception):
         Create a new GSSError.
 
         This method creates a new GSSError,
-        retrieves the releated human-readable
+        retrieves the related human-readable
         string messages, and uses the results to construct an
         exception message
 
diff --git a/gssapi/raw/named_tuples.py b/gssapi/raw/named_tuples.py
index f6e2b95b..19ad157f 100644
--- a/gssapi/raw/named_tuples.py
+++ b/gssapi/raw/named_tuples.py
@@ -25,8 +25,8 @@ class InquireCredResult(NamedTuple):
 class InquireCredByMechResult(NamedTuple):
     """Information about the credential for a specific mechanism."""
     name: Optional["g.Name"]  #: The principal associated with the credential
-    init_lifetime: Optional[int]  #: Time valid for initiation
-    accept_lifetime: Optional[int]  #: Time valid for accepting
+    init_lifetime: Optional[int]  #: Time valid for initiation, in seconds
+    accept_lifetime: Optional[int]  #: Time valid for accepting, in seconds
     usage: Optional[str]  #: How the credential can be used
 
 
@@ -34,8 +34,8 @@ class AddCredResult(NamedTuple):
     """Result of adding to a GSSAPI credential."""
     creds: Optional["g.Creds"]  #: The credential that was generated
     mechs: Set[OID]  #: Set of mechs the cred is for
-    init_lifetime: int  #: Time valid for initiation
-    accept_lifetime: int  #: Time valid for accepting
+    init_lifetime: int  #: Time valid for initiation, in seconds
+    accept_lifetime: int  #: Time valid for accepting, in seconds
 
 
 class DisplayNameResult(NamedTuple):
@@ -83,7 +83,7 @@ class InquireContextResult(NamedTuple):
     """Information about the security context."""
     initiator_name: Optional["g.Name"]  #: Name of the initiator
     target_name: Optional["g.Name"]  #: Name of the acceptor
-    lifetime: Optional[int]  #: Time valid for the security context
+    lifetime: Optional[int]  #: Time valid for the security context, in seconds
     mech: Optional[OID]  #: Mech used to create the security context
     flags: Optional[RequirementFlag]  #: Services available for the context
     locally_init: Optional[bool]  #: Context was initiated locally
diff --git a/gssapi/raw/oids.pyi b/gssapi/raw/oids.pyi
index a613d82a..7e36f648 100644
--- a/gssapi/raw/oids.pyi
+++ b/gssapi/raw/oids.pyi
@@ -8,7 +8,7 @@ class OID:
     to the constructor.  The `elements` argument should be a
     `bytes` consisting of the BER-encoded values in the OID.
 
-    To retrive the underlying bytes, use the :func:`bytes`
+    To retrieve the underlying bytes, use the :func:`bytes`
     function in Python 3.
 
     This object is hashable, and may be compared using equality
diff --git a/gssapi/raw/sec_contexts.pyi b/gssapi/raw/sec_contexts.pyi
index 0c8068cf..40022053 100644
--- a/gssapi/raw/sec_contexts.pyi
+++ b/gssapi/raw/sec_contexts.pyi
@@ -62,8 +62,8 @@ def init_sec_context(
             None to use the default set: mutual_authentication and
             out_of_sequence_detection.  This may also be an
             :class:`IntEnumFlagSet`
-        lifetime (int): the request lifetime of the security context (a value
-            of 0 or None means indefinite)
+        lifetime (int): the request lifetime of the security context in seconds
+            (a value of 0 or None means indefinite)
         channel_bindings (ChannelBindings): The channel bindings (or None for
             no channel bindings)
         input_token (bytes): the token to use to update the security context,
@@ -72,8 +72,8 @@ def init_sec_context(
     Returns:
         InitSecContextResult: the output security context, the actual mech
         type, the actual flags used, the output token to send to the acceptor,
-        the actual lifetime of the context (or None if not supported or
-        indefinite), and whether or not more calls are needed to finish the
+        the actual lifetime of the context in seconds (or None if not supported
+        or indefinite), and whether or not more calls are needed to finish the
         initiation.
 
     Raises:
@@ -120,7 +120,7 @@ def accept_sec_context(
     Returns:
         AcceptSecContextResult: the resulting security context, the initiator
         name, the mechanism being used, the output token, the flags in use,
-        the lifetime of the context (or None for indefinite or not
+        the lifetime of the context in seconds (or None for indefinite or not
         supported), the delegated credentials (valid only if the
         delegate_to_peer flag is set), and whether or not further token
         exchanges are needed to finalize the security context.
diff --git a/gssapi/sec_contexts.py b/gssapi/sec_contexts.py
index 97d54d03..812a6a3f 100644
--- a/gssapi/sec_contexts.py
+++ b/gssapi/sec_contexts.py
@@ -429,7 +429,8 @@ def _inquire(
         Args:
             initiator_name (bool): get the initiator name for this context
             target_name (bool): get the target name for this context
-            lifetime (bool): get the remaining lifetime for this context
+            lifetime (bool): get the remaining lifetime, in seconds, for this
+                context
             mech (bool): get the :class:`MechType` used by this context
             flags (bool): get the flags set on this context
             locally_init (bool): get whether this context was locally initiated

From a5f8b7855697fc66387820b1db66b3006272ce89 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Wed, 16 Feb 2022 14:52:02 +1000
Subject: [PATCH 20/49] Prepare for 1.7.3 release

Signed-off-by: Jordan Borean 
---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index eba32494..7d3eec60 100755
--- a/setup.py
+++ b/setup.py
@@ -344,7 +344,7 @@ def gssapi_modules(lst):
 
 setup(
     name='gssapi',
-    version='1.7.2',
+    version='1.7.3',
     author='The Python GSSAPI Team',
     author_email='jborean93@gmail.com',
     packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',

From 08e2b09fc68082fbec7b94aadb8c2c36efaea1b0 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Wed, 16 Feb 2022 15:24:00 +1000
Subject: [PATCH 21/49] More fixes for the release process

Signed-off-by: Jordan Borean 
---
 ci/before-deploy.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/ci/before-deploy.sh b/ci/before-deploy.sh
index 3b78ff53..2f135a95 100755
--- a/ci/before-deploy.sh
+++ b/ci/before-deploy.sh
@@ -15,7 +15,7 @@ deploy::build-docs
 # build the sdist and save the dirs before the clean
 python setup.py sdist
 mv dist dist_saved
-mv .venv .venv_saved
+mv .venv /tmp/.venv
 
 # for the tarball upload
 # clean up
@@ -23,7 +23,7 @@ git clean -Xdf
 
 # restore the saved "dist"/".venv" directory
 mv dist_saved dist
-mv .venv_saved .venv
+mv /tmp/.venv .venv
 
 # make the dir
 rm -rf ./tag_build || true

From 6b32538f5ae7bf53c82eccd63e6004f222db08c9 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 9 Aug 2022 10:05:24 +1000
Subject: [PATCH 22/49] Fix up doc references

Signed-off-by: Jordan Borean 
---
 gssapi/names.py                         |   8 +-
 gssapi/raw/_enum_extensions/__init__.py |   8 +-
 gssapi/raw/creds.pyi                    |   2 +-
 gssapi/raw/named_tuples.py              | 212 ++++++++++++++++--------
 gssapi/raw/oids.pyi                     |   4 +-
 setup.cfg                               |   1 +
 test-requirements.txt                   |   2 +-
 7 files changed, 156 insertions(+), 81 deletions(-)

diff --git a/gssapi/names.py b/gssapi/names.py
index 0f6201c6..c6fd972a 100644
--- a/gssapi/names.py
+++ b/gssapi/names.py
@@ -24,7 +24,7 @@ class Name(rname.Name):
 
     This class may be pickled and unpickled, as well as copied.
 
-    The :func:`str` and :func:`bytes` methods may be used to retrieve the
+    The :class:`str` and :class:`bytes` methods may be used to retrieve the
     text of the name.
 
     Note:
@@ -322,13 +322,13 @@ def mech(self) -> roids.OID:
         return self._inquire(mech_name=True).mech
 
     @property
-    def attributes(self) -> t.Optional["_NameAttributeMapping"]:
+    def attributes(self) -> t.Optional[MutableMapping]:
         """The attributes of this name (:requires-ext:`rfc6680`)
 
         The attributes are presenting in the form of a
-        :class:`~collections.MutableMapping` (a dict-like object).
+        :class:`~collections.abc.MutableMapping` (a dict-like object).
 
-        Retrieved values will always be in the form of :class:`frozensets`.
+        Retrieved values will always be in the form of :class:`frozenset`.
 
         When assigning values, if iterables are used, they be considered to be
         the set of values for the given attribute.  If a non-iterable is used,
diff --git a/gssapi/raw/_enum_extensions/__init__.py b/gssapi/raw/_enum_extensions/__init__.py
index ee2c43ee..96622e1f 100644
--- a/gssapi/raw/_enum_extensions/__init__.py
+++ b/gssapi/raw/_enum_extensions/__init__.py
@@ -33,5 +33,9 @@ def __new__(
                 else:
                     classdict[extra_name] = extra_val
 
-        return super(ExtendableEnum, metacl).__new__(metacl, name,
-                                                     bases, classdict)
+        return super(ExtendableEnum, metacl).__new__(
+            metacl,
+            name,
+            bases,
+            classdict,  # type: ignore[arg-type] # Uses private explicit type
+        )
diff --git a/gssapi/raw/creds.pyi b/gssapi/raw/creds.pyi
index 16e5d94e..723476cd 100644
--- a/gssapi/raw/creds.pyi
+++ b/gssapi/raw/creds.pyi
@@ -94,7 +94,7 @@ def add_cred(
     and new credential), or to add a new element to an existing credential.
 
     Args:
-        input_cred (Cred): the set of credentials to which to add the new
+        input_cred (Creds): the set of credentials to which to add the new
             credentials
         name (~gssapi.raw.names.Name): name of principal to acquire a
             credential for
diff --git a/gssapi/raw/named_tuples.py b/gssapi/raw/named_tuples.py
index 19ad157f..4eda145c 100644
--- a/gssapi/raw/named_tuples.py
+++ b/gssapi/raw/named_tuples.py
@@ -4,150 +4,220 @@
 from gssapi.raw.types import RequirementFlag
 
 if TYPE_CHECKING:
-    import gssapi.raw as g
+    import gssapi
 
 
 class AcquireCredResult(NamedTuple):
     """Credential result when acquiring a GSSAPI credential."""
-    creds: "g.Creds"  #: GSSAPI credentials that were acquired
-    mechs: Set[OID]  #: Set of mechs the cred is for
-    lifetime: int  #: Number of seconds for which the cred will remain valid
+    #: GSSAPI credentials that were acquired
+    creds: "gssapi.raw.creds.Creds"
+    #: Set of mechs the cred is for
+    mechs: Set[OID]
+    #: Number of seconds for which the cred will remain valid
+    lifetime: int
 
 
 class InquireCredResult(NamedTuple):
     """Information about the credential."""
-    name: Optional["g.Name"]  #: The principal associated with the credential
-    lifetime: Optional[int]  #: Number of seconds which the cred is valid for
-    usage: Optional[str]  #: How the credential can be used
-    mechs: Optional[Set[OID]]  #: Set of mechs the cred is for
+    #: The principal associated with the credential
+    name: Optional["gssapi.raw.names.Name"]
+    #: Number of seconds which the cred is valid for
+    lifetime: Optional[int]
+    #: How the credential can be used
+    usage: Optional[str]
+    #: Set of mechs the cred is for
+    mechs: Optional[Set[OID]]
 
 
 class InquireCredByMechResult(NamedTuple):
     """Information about the credential for a specific mechanism."""
-    name: Optional["g.Name"]  #: The principal associated with the credential
-    init_lifetime: Optional[int]  #: Time valid for initiation, in seconds
-    accept_lifetime: Optional[int]  #: Time valid for accepting, in seconds
-    usage: Optional[str]  #: How the credential can be used
+    #: The principal associated with the credential
+    name: Optional["gssapi.raw.names.Name"]
+    #: Time valid for initiation, in seconds
+    init_lifetime: Optional[int]
+    #: Time valid for accepting, in seconds
+    accept_lifetime: Optional[int]
+    #: How the credential can be used
+    usage: Optional[str]
 
 
 class AddCredResult(NamedTuple):
     """Result of adding to a GSSAPI credential."""
-    creds: Optional["g.Creds"]  #: The credential that was generated
-    mechs: Set[OID]  #: Set of mechs the cred is for
-    init_lifetime: int  #: Time valid for initiation, in seconds
-    accept_lifetime: int  #: Time valid for accepting, in seconds
+    #: The credential that was generated
+    creds: Optional["gssapi.raw.creds.Creds"]
+    #: Set of mechs the cred is for
+    mechs: Set[OID]
+    #: Time valid for initiation, in seconds
+    init_lifetime: int
+    #: Time valid for accepting, in seconds
+    accept_lifetime: int
 
 
 class DisplayNameResult(NamedTuple):
     """Textual representation of a GSSAPI name."""
-    name: bytes  #: The representation of the GSSAPI name
-    name_type: Optional[OID]  #: The type of GSSAPI name
+    #: The representation of the GSSAPI name
+    name: bytes
+    #: The type of GSSAPI name
+    name_type: Optional[OID]
 
 
 class WrapResult(NamedTuple):
     """Wrapped message result."""
-    message: bytes  #: The wrapped message
-    encrypted: bool  #: Whether the message is encrypted and not just signed
+    #: The wrapped message
+    message: bytes
+    #: Whether the message is encrypted and not just signed
+    encrypted: bool
 
 
 class UnwrapResult(NamedTuple):
     """Unwrapped message result."""
-    message: bytes  #: The unwrapped message
-    encrypted: bool  #: Whether the message was encrypted and not just signed
-    qop: int  #: The quality of protection applied to the message
+    #: The unwrapped message
+    message: bytes
+    #: Whether the message was encrypted and not just signed
+    encrypted: bool
+    #: The quality of protection applied to the message
+    qop: int
 
 
 class AcceptSecContextResult(NamedTuple):
     """Result when accepting a security context by an initiator."""
-    context: "g.SecurityContext"  #: The acceptor security context
-    initiator_name: "g.Name"  #: The authenticated name of the initiator
-    mech: OID  #: Mechanism with which the context was established
-    token: Optional[bytes]  #: Token to be returned to the initiator
-    flags: RequirementFlag  #: Services requested by the initiator
-    lifetime: int  #: Seconds for which the context is valid for
-    delegated_creds: Optional["g.Creds"]  #: Delegated credentials
-    more_steps: bool  #: More input is required to complete the exchange
+    #: The acceptor security context
+    context: "gssapi.raw.sec_contexts.SecurityContext"
+    #: The authenticated name of the initiator
+    initiator_name: "gssapi.raw.names.Name"
+    #: Mechanism with which the context was established
+    mech: OID
+    #: Token to be returned to the initiator
+    token: Optional[bytes]
+    #: Services requested by the initiator
+    flags: RequirementFlag
+    #: Seconds for which the context is valid for
+    lifetime: int
+    #: Delegated credentials
+    delegated_creds: Optional["gssapi.raw.creds.Creds"]
+    #: More input is required to complete the exchange
+    more_steps: bool
 
 
 class InitSecContextResult(NamedTuple):
     """Result when initiating a security context"""
-    context: "g.SecurityContext"  #: The initiator security context
-    mech: OID  #: Mechanism used in the security context
-    flags: RequirementFlag  #: Services available for the context
-    token: Optional[bytes]  #: Token to be sent to the acceptor
-    lifetime: int  #: Seconds for which the context is valid for
-    more_steps: bool  #: More input is required to complete the exchange
+    #: The initiator security context
+    context: "gssapi.raw.sec_contexts.SecurityContext"
+    #: Mechanism used in the security context
+    mech: OID
+    #: Services available for the context
+    flags: RequirementFlag
+    #: Token to be sent to the acceptor
+    token: Optional[bytes]
+    #: Seconds for which the context is valid for
+    lifetime: int
+    #: More input is required to complete the exchange
+    more_steps: bool
 
 
 class InquireContextResult(NamedTuple):
     """Information about the security context."""
-    initiator_name: Optional["g.Name"]  #: Name of the initiator
-    target_name: Optional["g.Name"]  #: Name of the acceptor
-    lifetime: Optional[int]  #: Time valid for the security context, in seconds
-    mech: Optional[OID]  #: Mech used to create the security context
-    flags: Optional[RequirementFlag]  #: Services available for the context
-    locally_init: Optional[bool]  #: Context was initiated locally
-    complete: Optional[bool]  #: Context has been established and ready to use
+    #: Name of the initiator
+    initiator_name: Optional["gssapi.raw.names.Name"]
+    #: Name of the acceptor
+    target_name: Optional["gssapi.raw.names.Name"]
+    #: Time valid for the security context, in seconds
+    lifetime: Optional[int]
+    #: Mech used to create the security context
+    mech: Optional[OID]
+    #: Services available for the context
+    flags: Optional[RequirementFlag]
+    #: Context was initiated locally
+    locally_init: Optional[bool]
+    #: Context has been established and ready to use
+    complete: Optional[bool]
 
 
 class StoreCredResult(NamedTuple):
     """Result of the credential storing operation."""
-    mechs: List[OID]  #: Mechs that were stored in the credential store
-    usage: str  #: How the credential can be used
+    #: Mechs that were stored in the credential store
+    mechs: List[OID]
+    #: How the credential can be used
+    usage: str
 
 
 class IOVUnwrapResult(NamedTuple):
     """Unwrapped IOV message result."""
-    encrypted: bool  #: Whether the message was encrypted and not just signed
-    qop: int  #: The quality of protection applied to the message
+    #: Whether the message was encrypted and not just signed
+    encrypted: bool
+    #: The quality of protection applied to the message
+    qop: int
 
 
 class InquireNameResult(NamedTuple):
     """Information about a GSSAPI Name."""
-    attrs: List[bytes]  #: Set of attribute names
-    is_mech_name: bool  #: Name is a mechanism name
-    mech: OID  #: The mechanism if is_name_mech is True
+    #: Set of attribute names
+    attrs: List[bytes]
+    #: Name is a mechanism name
+    is_mech_name: bool
+    #: The mechanism if is_name_mech is True
+    mech: OID
 
 
 class GetNameAttributeResult(NamedTuple):
     """GSSAPI Name attribute values."""
-    values: List[bytes]  #: Raw values
-    display_values: List[bytes]  #: Human-readable values
-    authenticated: bool  #: Attribute has been authenticated
-    complete: bool  #: Attribute value is marked as complete
+    #: Raw values
+    values: List[bytes]
+    #: Human-readable values
+    display_values: List[bytes]
+    #: Attribute has been authenticated
+    authenticated: bool
+    #: Attribute value is marked as complete
+    complete: bool
 
 
 class InquireAttrsResult(NamedTuple):
     """Set of attributes supported and known by a mechanism."""
-    mech_attrs: Set[OID]  #: The mechanisms attributes
-    known_mech_attrs: Set[OID]  #: Known attributes of the mechanism
+    #: The mechanisms attributes
+    mech_attrs: Set[OID]
+    #: Known attributes of the mechanism
+    known_mech_attrs: Set[OID]
 
 
 class DisplayAttrResult(NamedTuple):
     """Information about an attribute."""
-    name: bytes  #: The mechanism name
-    short_desc: bytes  #: Short description of the mechanism
-    long_desc: bytes  #: Long description of the mechanism
+    #: The mechanism name
+    name: bytes
+    #: Short description of the mechanism
+    short_desc: bytes
+    #: Long description of the mechanism
+    long_desc: bytes
 
 
 class InquireSASLNameResult(NamedTuple):
     """SASL informmation about a GSSAPI Name."""
-    sasl_mech_name: bytes  #: The SASL name
-    mech_name: bytes  #: The mechanism name
-    mech_description: bytes  #: The mechanism description
+    #: The SASL name
+    sasl_mech_name: bytes
+    #: The mechanism name
+    mech_name: bytes
+    #: The mechanism description
+    mech_description: bytes
 
 
 class Rfc1964KeyData(NamedTuple):
     """Security context key data based on RFC1964."""
-    sign_alg: int  #: Signing algorithm identifier
-    seal_alg: int  #: Sealing algorithm identifier
-    key_type: int  #: Key encryption type identifier
-    key: bytes  #: Encryption key data
+    #: Signing algorithm identifier
+    sign_alg: int
+    #: Sealing algorithm identifier
+    seal_alg: int
+    #: Key encryption type identifier
+    key_type: int
+    #: Encryption key data
+    key: bytes
 
 
 class CfxKeyData(NamedTuple):
     """Securty context key data."""
-    ctx_key_type: int  #: Context key encryption type identifier
-    ctx_key: bytes  #: Context key data - session or sub-session key
-    acceptor_subkey_type: Optional[int]  #: Acceptor key enc type identifier
-    acceptor_subkey: Optional[bytes]  #: Acceptor key data
+    #: Context key encryption type identifier
+    ctx_key_type: int
+    #: Context key data - session or sub-session key
+    ctx_key: bytes
+    #: Acceptor key enc type identifier
+    acceptor_subkey_type: Optional[int]
+    #: Acceptor key data
+    acceptor_subkey: Optional[bytes]
diff --git a/gssapi/raw/oids.pyi b/gssapi/raw/oids.pyi
index 7e36f648..7815601b 100644
--- a/gssapi/raw/oids.pyi
+++ b/gssapi/raw/oids.pyi
@@ -6,9 +6,9 @@ class OID:
 
     A new OID may be created by passing the `elements` argument
     to the constructor.  The `elements` argument should be a
-    `bytes` consisting of the BER-encoded values in the OID.
+    :class:`bytes` consisting of the BER-encoded values in the OID.
 
-    To retrieve the underlying bytes, use the :func:`bytes`
+    To retrieve the underlying bytes, use the :class:`bytes`
     function in Python 3.
 
     This object is hashable, and may be compared using equality
diff --git a/setup.cfg b/setup.cfg
index 9d312837..ce995309 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,6 +3,7 @@ source-dir=docs/source
 build-dir=docs/build
 all_files=1
 warning-is-error=1
+nitpicky=1
 
 [upload_sphinx]
 upload-dir = docs/build/html
diff --git a/test-requirements.txt b/test-requirements.txt
index 5085d282..1480519f 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -3,5 +3,5 @@ parameterized
 Cython
 k5test
 decorator
-mypy
+mypy==0.971
 types-decorator
\ No newline at end of file

From 66592c0730ea4bbf9ff8e0422e131d26a0d2e8c5 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Thu, 4 Aug 2022 07:06:12 +1000
Subject: [PATCH 23/49] Change sdist and build to use PEP517

Changes the sdist to no longer include the cythonised .c files and rely
on the PEP517 features to define Cython as a build requirement. This
ensures that when building this package from the sdist, the process will
include any bugfixes for Cython that have been fixed after the
python-gssapi version has been released.

The removal of the .c files also simplifies the setup.py by removing
more functionality that was tied specifically to setuptools or even
distutils making it more flexible for the future.

The CI process has been updated to test against this new artifact rather
than testing against an build_ext --inplace instance to better replicate
how end users will be using the library.

Signed-off-by: Jordan Borean 
---
 .github/workflows/build.yml                   |  93 -----
 .github/workflows/ci.yml                      | 327 ++++++++++++++++++
 .github/workflows/deploy-docs.yml             |  15 +
 .github/workflows/release.yml                 | 174 ----------
 MANIFEST.in                                   |   3 +-
 README.txt                                    |   3 +-
 ci/before-docs-deploy.sh                      |  11 +-
 ...before-deploy.sh => create-release-tar.sh} |  26 +-
 ci/lib-deploy.sh                              |  13 -
 ci/{lib-setup.sh => lib.sh}                   |  86 +++--
 ci/run-on-linux.sh                            |  16 +-
 ci/{build.sh => test.sh}                      |  34 +-
 docs/source/conf.py                           |   1 -
 mypy.ini                                      |  31 --
 pyproject.toml                                |  58 ++++
 setup.cfg                                     |   9 -
 setup.py                                      |  80 +----
 test-requirements.txt                         |   2 +-
 tox.ini                                       |  15 -
 19 files changed, 510 insertions(+), 487 deletions(-)
 delete mode 100644 .github/workflows/build.yml
 create mode 100644 .github/workflows/ci.yml
 delete mode 100644 .github/workflows/release.yml
 rename ci/{before-deploy.sh => create-release-tar.sh} (72%)
 delete mode 100755 ci/lib-deploy.sh
 rename ci/{lib-setup.sh => lib.sh} (57%)
 rename ci/{build.sh => test.sh} (67%)
 delete mode 100644 mypy.ini
 create mode 100644 pyproject.toml
 delete mode 100644 setup.cfg
 delete mode 100644 tox.ini

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index 045b7bcc..00000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,93 +0,0 @@
-name: Build
-on:
-  push:
-    branches-ignore: gh-pages
-    tags-ignore: '*'
-  pull_request: null
-
-jobs:
-  linux:
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        name:
-        - debian-stable
-        - debian-heimdal
-        - centos-stream-8
-        - fedora-latest
-        include:
-        - name: debian-stable
-          distro: debian:stable
-        - name: debian-heimdal
-          distro: debian:stable
-          krb5_ver: heimdal
-        - name: centos-stream-8
-          distro: quay.io/centos/centos:stream8
-        - name: fedora-latest
-          distro: fedora:latest
-          flake: 'yes'
-
-    steps:
-    - name: Check out code
-      uses: actions/checkout@v2
-
-    - name: Build and test gssapi
-      run: ./ci/run-on-linux.sh ./ci/build.sh
-      env:
-        DISTRO: ${{ matrix.distro }}
-        KRB5_VER: ${{ matrix.krb5_ver }}
-        FLAKE: ${{ matrix.flake }}
-
-  windows:
-    runs-on: windows-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        name:
-        - win-py-3.10
-        - win-py-3.9
-        - win-py-3.8
-        - win-py-3.7
-        - win-py-3.6
-        arch:
-        - x64
-        - x86
-        include:
-        - name: win-py-3.10
-          pyenv: '3.10'
-        - name: win-py-3.9
-          pyenv: '3.9'
-        - name: win-py-3.8
-          pyenv: '3.8'
-        - name: win-py-3.7
-          pyenv: '3.7'
-        - name: win-py-3.6
-          pyenv: '3.6'
-
-    steps:
-    - name: Check out code
-      uses: actions/checkout@v2
-
-    - name: Install the right python
-      uses: actions/setup-python@v2
-      with:
-        python-version: ${{ matrix.pyenv }}
-        architecture: ${{ matrix.arch }}
-
-    - name: Build and test gssapi
-      shell: bash
-      run: ./ci/build.sh
-      env:
-        OS_NAME: windows
-
-  macos-heimdal:
-    runs-on: macos-latest
-    steps:
-    - name: Check out code
-      uses: actions/checkout@v2
-
-    - name: Build and test gssapi
-      run: ./ci/build.sh
-      env:
-        KRB5_VER: heimdal
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..90b47986
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,327 @@
+name: Build
+on:
+  push:
+    branches-ignore: gh-pages
+    tags: v*
+  pull_request: null
+
+jobs:
+  build_sdist:
+    name: Build sdist
+    runs-on: ubuntu-latest
+    steps:
+    - name: Check out code
+      uses: actions/checkout@v2
+    
+    - name: Select python
+      uses: actions/setup-python@v2
+      with:
+        python-version: 3.9
+
+    - name: Build sdist
+      run: |
+        python -m pip install build
+        python -m build --sdist
+      env:
+        GSSAPI_SUPPORT_DETECT: false
+        GSSAPI_LINKER_ARGS: ''
+        GSSAPI_COMPILER_ARGS: ''
+
+    - name: Upload sdist
+      uses: actions/upload-artifact@v2
+      with:
+        name: artifact
+        path: ./dist/*.tar.gz
+
+  build_wheels:
+    name: Build wheels
+    needs:
+    - build_sdist
+
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+        - os: macOS-12
+          version: cp310-macosx_x86_64
+        - os: macOS-12
+          version: cp310-macosx_arm64
+        - os: macOS-12
+          version: cp39-macosx_x86_64
+        - os: macOS-12
+          version: cp39-macosx_arm64
+        - os: macOS-12
+          version: cp38-macosx_x86_64
+        - os: macOS-12
+          version: cp38-macosx_arm64
+        - os: macOS-12
+          version: cp37-macosx_x86_64
+        - os: macOS-12
+          version: cp36-macosx_x86_64
+
+        - os: windows-2022
+          version: cp310-win_amd64
+        - os: windows-2022
+          version: cp310-win32
+        - os: windows-2022
+          version: cp39-win_amd64
+        - os: windows-2022
+          version: cp39-win32
+        - os: windows-2022
+          version: cp38-win_amd64
+        - os: windows-2022
+          version: cp38-win32
+        - os: windows-2022
+          version: cp37-win_amd64
+        - os: windows-2022
+          version: cp37-win32
+        - os: windows-2022
+          version: cp36-win_amd64
+        - os: windows-2022
+          version: cp36-win32
+
+    steps:
+    - name: Set up environment
+      if: startsWith(matrix.os, 'windows-')
+      shell: bash
+      run: |
+        choco.exe install \
+          --no-progress \
+          --yes \
+          --ignore-detected-reboot \
+          --allow-downgrade \
+          --install-arguments "'ADDLOCAL=ALL'" \
+          ${{ endsWith(matrix.version, '-win32') && '--x86' || '' }} mitkerberos || true
+
+        echo "C:\Program Files${{ endsWith(matrix.version, '-win32') && ' (x86)' || '' }}\MIT\Kerberos\bin;$PATH" >> $GITHUB_PATH
+
+    - name: Download gssapi sdist
+      uses: actions/download-artifact@v2
+      with:
+        name: artifact
+        path: ./
+
+    - name: Extract sdist
+      shell: bash
+      run: |
+        tar xf gssapi-*.tar.gz
+        mv gssapi-*/* .
+        rm -r gssapi-*/
+        rm gssapi-*.tar.gz
+
+    - name: Build wheel
+      uses: pypa/cibuildwheel@v2.8.1
+      env:
+        CIBW_ARCHS: all
+        CIBW_TEST_SKIP: '*_arm64'
+        CIBW_BUILD: ${{ matrix.version }}
+        CIBW_BUILD_VERBOSITY: 1
+
+    - name: Upload wheel
+      uses: actions/upload-artifact@v2
+      with:
+        path: ./wheelhouse/*.whl
+        name: artifact
+
+  linux:
+    needs:
+    - build_sdist
+    - build_wheels
+
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        name:
+        - debian-stable
+        - debian-heimdal
+        - centos-stream-8
+        - fedora-latest
+        include:
+        - name: debian-stable
+          distro: debian:stable
+        - name: debian-heimdal
+          distro: debian:stable
+          krb5_ver: heimdal
+        - name: centos-stream-8
+          distro: quay.io/centos/centos:stream8
+        - name: fedora-latest
+          distro: fedora:latest
+          flake: 'yes'
+
+    steps:
+    - name: Check out code
+      uses: actions/checkout@v2
+
+    - name: Download built project
+      uses: actions/download-artifact@v2
+      with:
+        name: artifact
+        path: ./dist
+
+    - name: Test gssapi
+      run: ./ci/run-on-linux.sh ./ci/test.sh
+      env:
+        DISTRO: ${{ matrix.distro }}
+        KRB5_VER: ${{ matrix.krb5_ver }}
+        FLAKE: ${{ matrix.flake }}
+
+  windows:
+    needs:
+    - build_sdist
+    - build_wheels
+
+    runs-on: windows-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        name:
+        - win-py-3.10
+        - win-py-3.9
+        - win-py-3.8
+        - win-py-3.7
+        - win-py-3.6
+        arch:
+        - x64
+        - x86
+        include:
+        - name: win-py-3.10
+          pyenv: '3.10'
+        - name: win-py-3.9
+          pyenv: '3.9'
+        - name: win-py-3.8
+          pyenv: '3.8'
+        - name: win-py-3.7
+          pyenv: '3.7'
+        - name: win-py-3.6
+          pyenv: '3.6'
+
+    steps:
+    - name: Check out code
+      uses: actions/checkout@v2
+
+    - name: Download built project
+      uses: actions/download-artifact@v2
+      with:
+        name: artifact
+        path: ./dist
+
+    - name: Install the right python
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.pyenv }}
+        architecture: ${{ matrix.arch }}
+
+    - name: Test gssapi
+      shell: bash
+      run: ./ci/test.sh
+      env:
+        OS_NAME: windows
+
+  macos:
+    needs:
+    - build_sdist
+    - build_wheels
+
+    runs-on: macos-latest
+    steps:
+    - name: Check out code
+      uses: actions/checkout@v2
+
+    - name: Download built project
+      uses: actions/download-artifact@v2
+      with:
+        name: artifact
+        path: ./dist
+
+    - name: Test gssapi
+      run: ./ci/test.sh
+      env:
+        KRB5_VER: heimdal
+
+  publish:
+    name: publish
+
+    needs:
+    - linux
+    - macos
+    - windows
+
+    runs-on: ubuntu-latest
+    steps:
+    - name: Check out code
+      uses: actions/checkout@v2
+
+    - name: Download built project
+      uses: actions/download-artifact@v2
+      with:
+        name: artifact
+        path: ./dist
+
+    - name: Create GitHub release artifact
+      run: ./ci/run-on-linux.sh ./ci/create-release-tar.sh
+      env:
+        DISTRO: fedora:latest
+
+    - name: Get tarball path
+      id: tarball
+      run: echo "::set-output name=tarball::`ls tag_build/*.tar.gz | awk -F/ '{print $2}'`"
+
+    - name: Get release checksum path
+      id: checksum
+      run: echo "::set-output name=checksum::`ls tag_build/*.sha512sum | awk -F/ '{print $2}'`"
+
+    - name: Upload tagged build artifact
+      uses: actions/upload-artifact@v2
+      with:
+        path: tag_build/${{ steps.tarball.outputs.tarball }}
+        name: artifact
+
+    - name: Deploy stable docs
+      if: startsWith(github.ref, 'refs/tags/v')
+      uses: JamesIves/github-pages-deploy-action@3.7.1
+      with:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        BRANCH: gh-pages
+        FOLDER: ci_docs_build/html
+        TARGET_FOLDER: stable
+
+    - name: Create release
+      if: startsWith(github.ref, 'refs/tags/v')
+      uses: actions/create-release@v1
+      id: cr
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      with:
+        tag_name: ${{ github.ref }}
+        release_name: ${{ github.ref }}
+
+    - name: Upload release tarball
+      if: startsWith(github.ref, 'refs/tags/v')
+      uses: actions/upload-release-asset@v1
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      with:
+        upload_url: ${{ steps.cr.outputs.upload_url }}
+        asset_path: tag_build/${{ steps.tarball.outputs.tarball }}
+        asset_name: ${{ steps.tarball.outputs.tarball }}
+        asset_content_type: application/octet-stream
+
+    - name: Upload release checksum
+      if: startsWith(github.ref, 'refs/tags/v')
+      uses: actions/upload-release-asset@v1
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      with:
+        upload_url: ${{ steps.cr.outputs.upload_url }}
+        asset_path: tag_build/${{ steps.checksum.outputs.checksum }}
+        asset_name: ${{ steps.checksum.outputs.checksum }}
+        asset_content_type: text/plain
+
+    - name: Deploy to PyPI
+      if: startsWith(github.ref, 'refs/tags/v')
+      uses: pypa/gh-action-pypi-publish@v1.1.0
+      with:
+        user: __token__
+        password: ${{ secrets.pypi_password }}
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 32021ce5..e17737cb 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -10,12 +10,27 @@ jobs:
     - name: Check out code
       uses: actions/checkout@v2
 
+    - name: Select python
+      uses: actions/setup-python@v2
+      with:
+        python-version: 3.9
+
+    - name: Build sdist
+      run: |
+        python -m pip install build
+        python -m build --sdist
+      env:
+        GSSAPI_SUPPORT_DETECT: false
+        GSSAPI_LINKER_ARGS: ''
+        GSSAPI_COMPILER_ARGS: ''
+
     - name: Build docs
       run: ./ci/run-on-linux.sh ./ci/before-docs-deploy.sh
       env:
         DISTRO: fedora:latest
 
     - name: Deploy latest docs
+      if: false
       uses: JamesIves/github-pages-deploy-action@3.7.1
       with:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 226253a6..00000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,174 +0,0 @@
-name: Release
-on:
-  push:
-    tags: v*
-
-jobs:
-  sdist-release:
-    runs-on: ubuntu-latest
-
-    steps:
-    - name: Check out code
-      uses: actions/checkout@v2
-
-    - name: Set things up
-      run: ./ci/run-on-linux.sh ./ci/before-deploy.sh
-      env:
-        DISTRO: fedora:latest
-
-    - name: Upload sdist
-      uses: actions/upload-artifact@v2
-      with:
-        path: ./dist/*.tar.gz
-        name: artifact
-
-    - name: Deploy stable docs
-      uses: JamesIves/github-pages-deploy-action@3.7.1
-      with:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        BRANCH: gh-pages
-        FOLDER: ci_docs_build/html
-        TARGET_FOLDER: stable
-
-    - name: Create release
-      uses: actions/create-release@v1
-      id: cr
-      env:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      with:
-        tag_name: ${{ github.ref }}
-        release_name: ${{ github.ref }}
-
-    - id: tarball
-      run: echo "::set-output name=tarball::`ls tag_build/*.tar.gz | awk -F/ '{print $2}'`"
-
-    - id: checksum
-      run: echo "::set-output name=checksum::`ls tag_build/*.sha512sum | awk -F/ '{print $2}'`"
-
-    - name: Upload release tarball
-      uses: actions/upload-release-asset@v1
-      env:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      with:
-        upload_url: ${{ steps.cr.outputs.upload_url }}
-        asset_path: tag_build/${{ steps.tarball.outputs.tarball }}
-        asset_name: ${{ steps.tarball.outputs.tarball }}
-        asset_content_type: application/octet-stream
-
-    - name: Upload release checksum
-      uses: actions/upload-release-asset@v1
-      env:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      with:
-        upload_url: ${{ steps.cr.outputs.upload_url }}
-        asset_path: tag_build/${{ steps.checksum.outputs.checksum }}
-        asset_name: ${{ steps.checksum.outputs.checksum }}
-        asset_content_type: text/plain
-
-  wheel:
-    needs:
-    - sdist-release
-
-    runs-on: ${{ matrix.os }}
-    strategy:
-      matrix:
-        include:
-        - os: macOS-10.15
-          version: cp310-macosx_x86_64
-        - os: macOS-10.15
-          version: cp310-macosx_arm64
-        - os: macOS-10.15
-          version: cp39-macosx_x86_64
-        - os: macOS-10.15
-          version: cp39-macosx_arm64
-        - os: macOS-10.15
-          version: cp38-macosx_x86_64
-        - os: macOS-10.15
-          version: cp38-macosx_arm64
-        - os: macOS-10.15
-          version: cp37-macosx_x86_64
-        - os: macOS-10.15
-          version: cp36-macosx_x86_64
-
-        - os: windows-2019
-          version: cp310-win_amd64
-        - os: windows-2019
-          version: cp310-win32
-        - os: windows-2019
-          version: cp39-win_amd64
-        - os: windows-2019
-          version: cp39-win32
-        - os: windows-2019
-          version: cp38-win_amd64
-        - os: windows-2019
-          version: cp38-win32
-        - os: windows-2019
-          version: cp37-win_amd64
-        - os: windows-2019
-          version: cp37-win32
-        - os: windows-2019
-          version: cp36-win_amd64
-        - os: windows-2019
-          version: cp36-win32
-
-    steps:
-    - name: Set up environment
-      if: startsWith(matrix.os, 'windows-')
-      shell: bash
-      run: |
-        choco.exe install \
-          --no-progress \
-          --yes \
-          --ignore-detected-reboot \
-          --allow-downgrade \
-          --install-arguments "'ADDLOCAL=ALL'" \
-          ${{ endsWith(matrix.version, '-win32') && '--x86' || '' }} mitkerberos || true
-
-        echo "C:\Program Files${{ endsWith(matrix.version, '-win32') && ' (x86)' || '' }}\MIT\Kerberos\bin;$PATH" >> $GITHUB_PATH
-
-    - name: Download sdist
-      uses: actions/download-artifact@v2
-      with:
-        name: artifact
-        path: ./
-
-    - name: Extract sdist
-      shell: bash
-      run: |
-        tar xf gssapi-*.tar.gz
-        mv gssapi-*/* .
-        rm -r gssapi-*/
-        rm gssapi-*.tar.gz
-
-    - name: Build wheel
-      uses: pypa/cibuildwheel@v2.1.2
-      env:
-        CIBW_ARCHS: all
-        CIBW_TEST_SKIP: '*_arm64'
-        CIBW_BUILD: ${{ matrix.version }}
-        CIBW_BUILD_VERBOSITY: 1
-
-    - name: Upload wheel
-      uses: actions/upload-artifact@v2
-      with:
-        path: ./wheelhouse/*.whl
-        name: artifact
-
-  pypi:
-    needs:
-    - wheel
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - name: Download sdist and wheels
-      uses: actions/download-artifact@v2
-      with:
-        name: artifact
-        path: ./dist
-
-    - name: Deploy to PyPI
-      uses: pypa/gh-action-pypi-publish@v1.1.0
-      with:
-        user: __token__
-        password: ${{ secrets.pypi_password }}
diff --git a/MANIFEST.in b/MANIFEST.in
index 67cc2bbc..c9ba29f4 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,6 @@
 include *.txt
 recursive-include docs *.txt
 recursive-include gssapi *.pxd
-recursive-include gssapi *.c
+recursive-include gssapi *.pyx
 recursive-include gssapi *.h
+recursive-exclude gssapi *.c
diff --git a/README.txt b/README.txt
index 9154f7b0..69a34f90 100644
--- a/README.txt
+++ b/README.txt
@@ -74,8 +74,7 @@ After being sure to install all the requirements,
 .. code-block:: bash
 
     $ git clone https://github.com/pythongssapi/python-gssapi.git
-    $ python setup.py build
-    $ python setup.py install
+    $ pip install .
 
 Tests
 =====
diff --git a/ci/before-docs-deploy.sh b/ci/before-docs-deploy.sh
index a6bb1eb1..d7c719ed 100755
--- a/ci/before-docs-deploy.sh
+++ b/ci/before-docs-deploy.sh
@@ -1,12 +1,7 @@
 #!/bin/bash -ex
 
-source ./ci/lib-setup.sh
-source ./ci/lib-deploy.sh
+source ./ci/lib.sh
 
-# GitHub Actions doesn't have a good concept of connected pipelines here, so
-# just rebuild rather than trying to figure it out.
-./ci/build.sh
+lib::setup::install
 
-setup::activate
-
-deploy::build-docs
+lib::deploy::build_docs
diff --git a/ci/before-deploy.sh b/ci/create-release-tar.sh
similarity index 72%
rename from ci/before-deploy.sh
rename to ci/create-release-tar.sh
index 2f135a95..52410f04 100755
--- a/ci/before-deploy.sh
+++ b/ci/create-release-tar.sh
@@ -1,19 +1,21 @@
 #!/bin/bash -ex
 
-source ./ci/lib-setup.sh
-source ./ci/lib-deploy.sh
+source ./ci/lib.sh
 
-./ci/build.sh
-
-setup::activate
+lib::setup::install
 
 yum -y install tar git
 
+# Git complains if this isn't owned by the user which is the case when running
+# through the run-on-linux.sh
+if [ -f /.dockerenv ]; then
+    git config --global --add safe.directory "${PWD}"
+fi
+
 # build the docs
-deploy::build-docs
+lib::deploy::build_docs
 
-# build the sdist and save the dirs before the clean
-python setup.py sdist
+# Save the sdist and venv dirs before the clean
 mv dist dist_saved
 mv .venv /tmp/.venv
 
@@ -31,7 +33,13 @@ mkdir ./tag_build
 
 # create and checksum the tarball
 
+set +e
 tag=$(git describe --tags)
+if [ "${?}" -ne 0 ]; then
+    tag=$(git rev-parse --short HEAD)
+fi
+set -e
+
 if [ x"${tag#v[0-9]}" = "x${tag}" ]; then
     PYTHON_GSSAPI_VERSION=${tag}
 else
@@ -44,7 +52,7 @@ tar -cvf ./tag_build/${PKG_NAME_VER}.tar \
     --exclude='dist' \
     --exclude='tag_build' \
     --exclude='.git' \
-    --exclude='travis_docs_build' \
+    --exclude='ci_docs_build' \
     --exclude='.venv' \
     --exclude='README.rst' \
     --transform="s,^\.,${PKG_NAME_VER}," .
diff --git a/ci/lib-deploy.sh b/ci/lib-deploy.sh
deleted file mode 100755
index 25c11309..00000000
--- a/ci/lib-deploy.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-deploy::build-docs() {
-    # the first run is for the docs build, so don't clean up
-    pip install -r docs-requirements.txt
-
-    # install dependencies so that sphinx doesn't have issues
-    # (this actually just installs the whole package in dev mode)
-    pip install -e .
-
-    # place in a non-standard location so that they don't get cleaned up
-    python setup.py build_sphinx --build-dir ci_docs_build
-
-    echo "docs_build"
-}
diff --git a/ci/lib-setup.sh b/ci/lib.sh
similarity index 57%
rename from ci/lib-setup.sh
rename to ci/lib.sh
index cf12856c..07408c8a 100755
--- a/ci/lib-setup.sh
+++ b/ci/lib.sh
@@ -1,9 +1,6 @@
 #!/bin/bash
 
-# We test Debian's cython.  el7's cython is too old, and Rawhide's virtualenv
-# doesn't work right (usrmerge bugs) so we can only test Debian's cython.
-
-setup::debian::install() {
+lib::setup::debian_install() {
     export DEBIAN_FRONTEND=noninteractive
     apt-get update
 
@@ -19,52 +16,47 @@ setup::debian::install() {
         export GSSAPI_KRB5_MAIN_LIB="/usr/lib/x86_64-linux-gnu/libkrb5.so"
     fi
 
-    apt-get -y install gcc virtualenv python3-{virtualenv,dev} cython3
+    apt-get -y install gcc virtualenv python3-{venv,dev}
 
-    virtualenv --system-site-packages -p $(which python3) .venv
+    virtualenv -p $(which python3) .venv
     source ./.venv/bin/activate
 }
 
-setup::rh::yuminst() {
+lib::setup::rh_yuminst() {
     # yum has no update-only verb.  Also: modularity just makes this slower.
     yum -y --nogpgcheck --disablerepo=\*modul\* install $@
 }
 
-setup::centos::install() {
-    # Cython on el7 is too old - downstream patches
-    setup::rh::yuminst python3-{virtualenv,devel}
+lib::setup::centos_install() {
+    lib::setup::rh_yuminst python3-{virtualenv,devel}
     virtualenv -p $(which python3) .venv
     source ./.venv/bin/activate
-    pip install --upgrade pip # el7 pip doesn't quite work right
-    pip install --install-option='--no-cython-compile' cython
 }
 
-setup::fedora::install() {
+lib::setup::fedora_install() {
     # path to binary here in case Rawhide changes it
-    setup::rh::yuminst redhat-rpm-config \
+    lib::setup::rh_yuminst redhat-rpm-config \
         /usr/bin/virtualenv python3-{virtualenv,devel}
     virtualenv -p $(which python3) .venv
     source ./.venv/bin/activate
-    pip install --install-option='--no-cython-compile' cython
 }
 
-setup::rh::install() {
-    setup::rh::yuminst krb5-{devel,libs,server,workstation} \
+lib::setup::rh_install() {
+    lib::setup::rh_yuminst krb5-{devel,libs,server,workstation} \
                        which gcc findutils gssntlmssp
     export GSSAPI_KRB5_MAIN_LIB="/usr/lib64/libkrb5.so"
 
     if [ -f /etc/fedora-release ]; then
-        setup::fedora::install
+        lib::setup::fedora_install
     else
-        setup::centos::install
+        lib::setup::centos_install
     fi
 }
 
-setup::macos::install() {
+lib::setup::macos_install() {
     sudo pip3 install virtualenv
     python3 -m virtualenv -p $(which python3) .venv
     source .venv/bin/activate
-    pip install --install-option='--no-cython-compile' cython
 
     export GSSAPI_KRB5_MAIN_LIB="/System/Library/PrivateFrameworks/Heimdal.framework/Heimdal"
 
@@ -74,7 +66,7 @@ setup::macos::install() {
     export KRB5_KTNAME=initial
 }
 
-setup::windows::install() {
+lib::setup::windows_install() {
     CHINST="choco install --no-progress --yes --ignore-detected-reboot --allow-downgrade"
 
     # Install the 32bit version if Python is 32bit
@@ -90,32 +82,52 @@ setup::windows::install() {
 
     # Update path to include it
     export PATH="/c/$PF/MIT/Kerberos/bin:$PATH"
-
-    python -m pip install --upgrade pip
 }
 
-setup::install() {
+lib::setup::install() {
     if [ -f /etc/debian_version ]; then
-        setup::debian::install
+        lib::setup::debian_install
     elif [ -f /etc/redhat-release ]; then
-        setup::rh::install
+        lib::setup::rh_install
     elif [ "$(uname)" == "Darwin" ]; then
-        setup::macos::install
+        lib::setup::macos_install
     elif [ "$OS_NAME" == "windows" ]; then
-        setup::windows::install
+        lib::setup::windows_install
     else
         echo "Distro not found!"
         false
     fi
 
-    pip install -r test-requirements.txt
+    # Get the explicit version to force pip to install from our local dir in
+    # case this is a pre-release and/or PyPi has a later version
+    echo "Installing gssapi"
+    GSSAPI_VER="$( grep 'version=' setup.py | cut -d "'" -f2 )"
+
+    if [ "$(expr substr $(uname -s) 1 5)" == "MINGW" ]; then
+        DIST_LINK_PATH="$( echo "${PWD}/dist" | sed -e 's/^\///' -e 's/\//\\/g' -e 's/^./\0:/' )"
+    else
+        DIST_LINK_PATH="${PWD}/dist"
+    fi
+
+    python -m pip install gssapi=="${GSSAPI_VER}" \
+        --find-links "file://${DIST_LINK_PATH}" \
+        --verbose
+
+    echo "Installing dev dependencies"
+    python -m pip install -r test-requirements.txt
 }
 
-setup::activate() {
-    # remove (and restore) set -x to avoid log-spam the source
-    # script, which we don't care about
-    wastrace=${-//[^x]/}
-    set +x
-    source .venv/bin/activate
-    if [[ -n "$wastrace" ]]; then set -x; fi
+lib::deploy::build_docs() {
+    # the first run is for the docs build, so don't clean up
+    pip install -r docs-requirements.txt
+
+    # Don't run in root to make sure the local copies aren't imported
+    pushd docs
+
+    # place in a non-standard location so that they don't get cleaned up
+    sphinx-build source ../ci_docs_build -a -W -n
+
+    popd
+
+    echo "docs_build"
 }
diff --git a/ci/run-on-linux.sh b/ci/run-on-linux.sh
index 96814721..272a9039 100755
--- a/ci/run-on-linux.sh
+++ b/ci/run-on-linux.sh
@@ -1,10 +1,10 @@
 #!/bin/bash -ex
 
-# If we try to use a normal Github Actions container with
-# github-pages-deploy-action, it will fail due to inability to find git.
-
-docker run -h test.krbtest.com \
-       -v `pwd`:/tmp/build -w /tmp/build \
-       -e KRB5_VER=${KRB5_VER:-mit} \
-       -e FLAKE=${FLAKE:no} \
-       $DISTRO /bin/bash -ex $@
+docker run \
+    --rm \
+    --hostname test.krbtest.com \
+    --volume "$( pwd )":/tmp/build:z \
+    --workdir /tmp/build \
+    --env KRB5_VER=${KRB5_VER:-mit} \
+    --env FLAKE=${FLAKE:no} \
+    ${DISTRO} /bin/bash -ex $@
diff --git a/ci/build.sh b/ci/test.sh
similarity index 67%
rename from ci/build.sh
rename to ci/test.sh
index 8b455d7d..c953f207 100755
--- a/ci/build.sh
+++ b/ci/test.sh
@@ -1,8 +1,18 @@
 #!/bin/bash -ex
 
 # set up dependencies, etc
-source ./ci/lib-setup.sh
-setup::install
+source ./ci/lib.sh
+
+if [ x"${GITHUB_ACTIONS}" = "xtrue" ]; then
+    echo "::group::Installing Requirements"
+fi
+
+lib::setup::install
+
+if [ x"${GITHUB_ACTIONS}" = "xtrue" ]; then
+    echo "::endgroup::"
+    echo "::group::Running Sanity Checks"
+fi
 
 if [ x"$FLAKE" = "xyes" ]; then
     flake8 setup.py
@@ -32,15 +42,15 @@ if [ $MYPY_RES -ne 0 ]; then
     exit $MYPY_RES
 fi
 
-# always build in-place so that Sphinx can find the modules
-python setup.py build_ext --inplace $EXTRA_BUILDEXT
-BUILD_RES=$?
-
-if [ $BUILD_RES -ne 0 ]; then
-    # if the build failed, don't run the tests
-    exit $BUILD_RES
+if [ x"${GITHUB_ACTIONS}" = "xtrue" ]; then
+    echo "::endgroup::"
+    echo "::group::Running Tests"
 fi
 
+# Ensure we don't run in the normal dir so that unittest imports our installed
+# package and not the source code
+pushd gssapi/tests
+
 # Only call exit on failures so we can source this script
 if [ "$OS_NAME" = "windows" ]; then
     # Windows can't run the tests yet, so just make sure it imports and exit
@@ -48,3 +58,9 @@ if [ "$OS_NAME" = "windows" ]; then
 else
     python -m unittest -v || exit $?
 fi
+
+popd
+
+if [ x"${GITHUB_ACTIONS}" = "xtrue" ]; then
+    echo "::endgroup::"
+fi
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 4688d998..1644b8cd 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -18,7 +18,6 @@
 # documentation root, use os.path.abspath to make it absolute, like shown here.
 #sys.path.insert(0, os.path.abspath('.'))
 
-sys.path.insert(0, os.path.abspath('../..'))
 sys.path.insert(0, os.path.abspath('../custom_extensions'))
 
 from custom_recommonmark import AllCodeCommonMarkParser
diff --git a/mypy.ini b/mypy.ini
deleted file mode 100644
index 71944250..00000000
--- a/mypy.ini
+++ /dev/null
@@ -1,31 +0,0 @@
-[mypy]
-exclude = (?x)(
-    setup.py
-    | docs/     # doc files
-    | build/    # temp build folder
-  )
-show_error_codes = True
-show_column_numbers = True
-disallow_any_unimported = true
-disallow_untyped_calls = true
-disallow_untyped_defs = true
-disallow_incomplete_defs = true
-check_untyped_defs = true
-disallow_untyped_decorators = true
-warn_redundant_casts = true
-warn_unused_ignores = true
-
-[mypy-gssapi.tests.*]
-disallow_any_unimported = false
-disallow_untyped_calls = false
-disallow_untyped_defs = false
-check_untyped_defs = false
-
-[mypy-k5test]
-ignore_missing_imports = True
-
-[mypy-k5test.unit]
-ignore_missing_imports = True
-
-[mypy-parameterized]
-ignore_missing_imports = True
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..2f5c208c
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,58 @@
+[build-system]
+requires = [
+    "Cython >= 0.29.29, < 3.0.0",  # 0.29.29 includes fixes for Python 3.11
+    "setuptools >= 40.6.0",  # Start of PEP 517 support for setuptools
+]
+build-backend = "setuptools.build_meta"
+
+[tool.mypy]
+exclude = """
+(?x)(
+  setup.py
+  | docs/
+  | build/
+)
+"""
+show_error_codes = true
+show_column_numbers = true
+disallow_any_unimported = true
+disallow_untyped_calls = true
+disallow_untyped_defs = true
+disallow_incomplete_defs = true
+check_untyped_defs = true
+disallow_untyped_decorators = true
+warn_redundant_casts = true
+warn_unused_ignores = true
+
+[[tool.mypy.overrides]]
+module = "gssapi.tests.*"
+disallow_any_unimported = false
+disallow_untyped_calls = false
+disallow_untyped_defs = false
+check_untyped_defs = false
+
+[[tool.mypy.overrides]]
+module = "k5test"
+ignore_missing_imports = true
+
+[[tool.mypy.overrides]]
+module = "k5test.unit"
+ignore_missing_imports = true
+
+[[tool.mypy.overrides]]
+module = "parameterized"
+ignore_missing_imports = true
+
+[tool.tox]
+legacy_tox_ini = """
+[tox]
+envlist = py36,py37,py38
+
+[testenv]
+whitelist_externals=bash
+commands =
+    bash -c "source ./.travis/lib-verify.sh && verify::flake8"
+    python -m unittest
+
+deps = -r{toxinidir}/test-requirements.txt
+"""
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index ce995309..00000000
--- a/setup.cfg
+++ /dev/null
@@ -1,9 +0,0 @@
-[build_sphinx]
-source-dir=docs/source
-build-dir=docs/build
-all_files=1
-warning-is-error=1
-nitpicky=1
-
-[upload_sphinx]
-upload-dir = docs/build/html
diff --git a/setup.py b/setup.py
index 7d3eec60..75e71fc9 100755
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,4 @@
 #!/usr/bin/env python
-from __future__ import print_function
-
 import subprocess
 import platform
 import re
@@ -16,25 +14,8 @@
 os.environ['SETUPTOOLS_USE_DISTUTILS'] = 'local'
 
 from setuptools import setup  # noqa: E402
-from setuptools import Distribution  # noqa: E402
-from setuptools.command.sdist import sdist  # noqa: E402
 from setuptools.extension import Extension  # noqa: E402
-
-
-SKIP_CYTHON_FILE = '__dont_use_cython__.txt'
-
-if os.path.exists(SKIP_CYTHON_FILE):
-    print("In distributed package, building from C files...", file=sys.stderr)
-    SOURCE_EXT = 'c'
-else:
-    try:
-        from Cython.Build import cythonize
-        print("Building from Cython files...", file=sys.stderr)
-        SOURCE_EXT = 'pyx'
-    except ImportError:
-        print("Cython not found, building from C files...",
-              file=sys.stderr)
-        SOURCE_EXT = 'c'
+from Cython.Build import cythonize  # noqa: E402
 
 
 def get_output(*args, **kwargs):
@@ -225,60 +206,9 @@ def get_output(*args, **kwargs):
     GSSAPI_LIB = ctypes.CDLL(os.path.join(main_path, main_lib))
 
 
-# add in the flag that causes us not to compile from Cython when
-# installing from an sdist
-class sdist_gssapi(sdist):
-    def run(self):
-        if not self.dry_run:
-            with open(SKIP_CYTHON_FILE, 'w') as flag_file:
-                flag_file.write('COMPILE_FROM_C_ONLY')
-
-            sdist.run(self)
-
-            os.remove(SKIP_CYTHON_FILE)
-
-
-DONT_CYTHONIZE_FOR = ('clean',)
-
-
-class GSSAPIDistribution(Distribution, object):
-    def run_command(self, command):
-        self._last_run_command = command
-        Distribution.run_command(self, command)
-
-    @property
-    def ext_modules(self):
-        if SOURCE_EXT != 'pyx':
-            return getattr(self, '_ext_modules', None)
-
-        if getattr(self, '_ext_modules', None) is None:
-            return None
-
-        if getattr(self, '_last_run_command', None) in DONT_CYTHONIZE_FOR:
-            return self._ext_modules
-
-        if getattr(self, '_cythonized_ext_modules', None) is None:
-            self._cythonized_ext_modules = cythonize(
-                self._ext_modules,
-                language_level=2,
-            )
-
-        return self._cythonized_ext_modules
-
-    @ext_modules.setter
-    def ext_modules(self, mods):
-        self._cythonized_ext_modules = None
-        self._ext_modules = mods
-
-    @ext_modules.deleter
-    def ext_modules(self):
-        del self._ext_modules
-        del self._cythonized_ext_modules
-
-
 def make_extension(name_fmt, module, **kwargs):
     """Helper method to remove the repetition in extension declarations."""
-    source = name_fmt.replace('.', '/') % module + '.' + SOURCE_EXT
+    source = name_fmt.replace('.', '/') % module + '.pyx'
     if not os.path.exists(source):
         raise OSError(source)
     return Extension(
@@ -330,7 +260,7 @@ def gssapi_modules(lst):
     # add in any present enum extension files
     res.extend(ENUM_EXTS)
 
-    return res
+    return cythonize(res, language_level=2)
 
 
 long_desc = re.sub(r'\.\. role:: \w+\(code\)\s*\n\s*.+', '',
@@ -344,7 +274,7 @@ def gssapi_modules(lst):
 
 setup(
     name='gssapi',
-    version='1.7.3',
+    version='1.8.0',
     author='The Python GSSAPI Team',
     author_email='jborean93@gmail.com',
     packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',
@@ -374,8 +304,6 @@ def gssapi_modules(lst):
         'Topic :: Security',
         'Topic :: Software Development :: Libraries :: Python Modules'
     ],
-    distclass=GSSAPIDistribution,
-    cmdclass={'sdist': sdist_gssapi},
     ext_modules=gssapi_modules([
         main_file('misc'),
         main_file('exceptions'),
diff --git a/test-requirements.txt b/test-requirements.txt
index 1480519f..f612f153 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,6 +1,6 @@
+build
 flake8
 parameterized
-Cython
 k5test
 decorator
 mypy==0.971
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 7eeefc40..00000000
--- a/tox.ini
+++ /dev/null
@@ -1,15 +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 = py36,py37,py38
-
-[testenv]
-whitelist_externals=bash
-commands =
-    bash -c "source ./.travis/lib-verify.sh && verify::flake8"
-    python -m unittest
-
-deps = -r{toxinidir}/test-requirements.txt

From d8858319bf0bf5a629b5d15d66fbc06fafef9a76 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Fri, 12 Aug 2022 11:48:49 +1000
Subject: [PATCH 24/49] Fix up build problems with latest change

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml          | 2 +-
 .github/workflows/deploy-docs.yml | 3 +--
 ci/run-on-linux.sh                | 2 +-
 3 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 90b47986..04e2e281 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -284,7 +284,7 @@ jobs:
       with:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         BRANCH: gh-pages
-        FOLDER: ci_docs_build/html
+        FOLDER: ci_docs_build
         TARGET_FOLDER: stable
 
     - name: Create release
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index e17737cb..6b3bf546 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -30,10 +30,9 @@ jobs:
         DISTRO: fedora:latest
 
     - name: Deploy latest docs
-      if: false
       uses: JamesIves/github-pages-deploy-action@3.7.1
       with:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         BRANCH: gh-pages
-        FOLDER: ci_docs_build/html
+        FOLDER: ci_docs_build
         TARGET_FOLDER: latest
diff --git a/ci/run-on-linux.sh b/ci/run-on-linux.sh
index 272a9039..2025db8a 100755
--- a/ci/run-on-linux.sh
+++ b/ci/run-on-linux.sh
@@ -3,7 +3,7 @@
 docker run \
     --rm \
     --hostname test.krbtest.com \
-    --volume "$( pwd )":/tmp/build:z \
+    --volume "$( pwd )":/tmp/build \
     --workdir /tmp/build \
     --env KRB5_VER=${KRB5_VER:-mit} \
     --env FLAKE=${FLAKE:no} \

From aae717dd566f4149c56fab79a23458c76ea1f33b Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 16 Aug 2022 05:41:15 +1000
Subject: [PATCH 25/49] Bump version to 1.8.1 with Python 3.7 requires

This update bumps the `python_requires` to be `>=3.7` as the sdist build
requires PEP 517 support that is somewhat only guaranteed with the pip
that was shipped with Python 3.7 or greater. Since 3.6 has been end of
life since December 2021 and the remaining Linux distributions that
still use it have system provided packages this shouldn't be a surprise.

Also use CentOS 9 Stream in the CI tests to test this on an actual
RHEL-ish host on the version of Python actually supported.

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 15 +++--------
 README.txt               |  5 ++--
 ci/lib.sh                | 57 ++++++++++++++++++++++++++++++----------
 setup.py                 |  5 ++--
 4 files changed, 51 insertions(+), 31 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 04e2e281..730ac21f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -57,8 +57,6 @@ jobs:
           version: cp38-macosx_arm64
         - os: macOS-12
           version: cp37-macosx_x86_64
-        - os: macOS-12
-          version: cp36-macosx_x86_64
 
         - os: windows-2022
           version: cp310-win_amd64
@@ -76,10 +74,6 @@ jobs:
           version: cp37-win_amd64
         - os: windows-2022
           version: cp37-win32
-        - os: windows-2022
-          version: cp36-win_amd64
-        - os: windows-2022
-          version: cp36-win32
 
     steps:
     - name: Set up environment
@@ -136,7 +130,7 @@ jobs:
         name:
         - debian-stable
         - debian-heimdal
-        - centos-stream-8
+        - centos-stream-9
         - fedora-latest
         include:
         - name: debian-stable
@@ -144,8 +138,8 @@ jobs:
         - name: debian-heimdal
           distro: debian:stable
           krb5_ver: heimdal
-        - name: centos-stream-8
-          distro: quay.io/centos/centos:stream8
+        - name: centos-stream-9
+          distro: quay.io/centos/centos:stream9
         - name: fedora-latest
           distro: fedora:latest
           flake: 'yes'
@@ -181,7 +175,6 @@ jobs:
         - win-py-3.9
         - win-py-3.8
         - win-py-3.7
-        - win-py-3.6
         arch:
         - x64
         - x86
@@ -194,8 +187,6 @@ jobs:
           pyenv: '3.8'
         - name: win-py-3.7
           pyenv: '3.7'
-        - name: win-py-3.6
-          pyenv: '3.6'
 
     steps:
     - name: Check out code
diff --git a/README.txt b/README.txt
index 69a34f90..5077840e 100644
--- a/README.txt
+++ b/README.txt
@@ -36,14 +36,15 @@ Basic
 
 * a C compiler (such as GCC)
 
-* Python 3.6+ (older releases support older versions, but are unsupported)
+* Python 3.7+ (older releases support older versions, but are unsupported)
 
 * the `decorator` python package
 
 Compiling from Scratch
 ----------------------
 
-To compile from scratch, you will need Cython >= 0.21.1.
+To compile from scratch, you will need Cython >= 0.29.29 which is automatically
+installed by pip in an isolated build virtual environment.
 
 For Running the Tests
 ---------------------
diff --git a/ci/lib.sh b/ci/lib.sh
index 07408c8a..338434d7 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -16,34 +16,64 @@ lib::setup::debian_install() {
         export GSSAPI_KRB5_MAIN_LIB="/usr/lib/x86_64-linux-gnu/libkrb5.so"
     fi
 
-    apt-get -y install gcc virtualenv python3-{venv,dev}
+    apt-get -y install gcc python3-{venv,dev}
 
-    virtualenv -p $(which python3) .venv
+    python3 -m venv .venv
     source ./.venv/bin/activate
 }
 
-lib::setup::rh_yuminst() {
-    # yum has no update-only verb.  Also: modularity just makes this slower.
-    yum -y --nogpgcheck --disablerepo=\*modul\* install $@
+lib::setup::rh_dnfinst() {
+    # dnf has no update-only verb.  Also: modularity just makes this slower.
+    dnf -y --nogpgcheck --disablerepo=\*modul\* install $@
 }
 
 lib::setup::centos_install() {
-    lib::setup::rh_yuminst python3-{virtualenv,devel}
-    virtualenv -p $(which python3) .venv
+    lib::setup::rh_dnfinst python3-devel
+    python3 -m venv .venv
     source ./.venv/bin/activate
 }
 
 lib::setup::fedora_install() {
     # path to binary here in case Rawhide changes it
-    lib::setup::rh_yuminst redhat-rpm-config \
-        /usr/bin/virtualenv python3-{virtualenv,devel}
-    virtualenv -p $(which python3) .venv
+    lib::setup::rh_dnfinst redhat-rpm-config \
+        python3-devel
+    python3 -m venv .venv
     source ./.venv/bin/activate
 }
 
+lib::setup::gssntlmssp_install() {
+    lib::setup::rh_dnfinst dnf-plugins-core
+    dnf config-manager --set-enabled crb
+
+    lib::setup::rh_dnfinst autoconf automake gettext libtool \
+                    libunistring-devel openssl-devel zlib-devel
+
+    curl -L -s https://github.com/gssapi/gss-ntlmssp/releases/download/v1.1.0/gssntlmssp-1.1.0.tar.gz --output /tmp/gssntlmssp.tar.gz
+    tar xf /tmp/gssntlmssp.tar.gz -C /tmp
+
+    pushd /tmp/gssntlmssp-1.1.0
+
+    autoreconf -f -i
+    ./configure --with-wbclient=no --with-manpages=no
+    make
+    make install
+
+    popd
+
+    echo "gssntlmssp_v1    1.3.6.1.4.1.311.2.2.10    /usr/local/lib/gssntlmssp/gssntlmssp.so" > /etc/gss/mech.d/gssntlmssp.conf
+}
+
 lib::setup::rh_install() {
-    lib::setup::rh_yuminst krb5-{devel,libs,server,workstation} \
-                       which gcc findutils gssntlmssp
+    lib::setup::rh_dnfinst krb5-{devel,libs,server,workstation} \
+                    which gcc findutils
+
+    if grep -q 'release 9' /etc/redhat-release; then
+        # CentOS 9 Stream doesn't have a dnf package for gssntlmssp
+        lib::setup::gssntlmssp_install
+    else
+        lib::setup::rh_dnfinst gssntlmssp
+    fi
+
     export GSSAPI_KRB5_MAIN_LIB="/usr/lib64/libkrb5.so"
 
     if [ -f /etc/fedora-release ]; then
@@ -54,8 +84,7 @@ lib::setup::rh_install() {
 }
 
 lib::setup::macos_install() {
-    sudo pip3 install virtualenv
-    python3 -m virtualenv -p $(which python3) .venv
+    python3 -m venv .venv
     source .venv/bin/activate
 
     export GSSAPI_KRB5_MAIN_LIB="/System/Library/PrivateFrameworks/Heimdal.framework/Heimdal"
diff --git a/setup.py b/setup.py
index 75e71fc9..1c70f4a6 100755
--- a/setup.py
+++ b/setup.py
@@ -274,7 +274,7 @@ def gssapi_modules(lst):
 
 setup(
     name='gssapi',
-    version='1.8.0',
+    version='1.8.1',
     author='The Python GSSAPI Team',
     author_email='jborean93@gmail.com',
     packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',
@@ -287,12 +287,11 @@ def gssapi_modules(lst):
     long_description=long_desc,
     license='LICENSE.txt',
     url="https://github.com/pythongssapi/python-gssapi",
-    python_requires=">=3.6",
+    python_requires=">=3.7",
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Programming Language :: Python',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',

From e4d4d08648712da274a0640222146268907f62b7 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Wed, 26 Oct 2022 03:06:38 +1000
Subject: [PATCH 26/49] Add Python 3.11 wheels

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 13 ++++++++++++-
 setup.py                 |  3 ++-
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 730ac21f..9f98fed7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -43,6 +43,10 @@ jobs:
       fail-fast: false
       matrix:
         include:
+        - os: macOS-12
+          version: cp311-macosx_x86_64
+        - os: macOS-12
+          version: cp311-macosx_arm64
         - os: macOS-12
           version: cp310-macosx_x86_64
         - os: macOS-12
@@ -58,6 +62,10 @@ jobs:
         - os: macOS-12
           version: cp37-macosx_x86_64
 
+        - os: windows-2022
+          version: cp311-win_amd64
+        - os: windows-2022
+          version: cp311-win32
         - os: windows-2022
           version: cp310-win_amd64
         - os: windows-2022
@@ -105,7 +113,7 @@ jobs:
         rm gssapi-*.tar.gz
 
     - name: Build wheel
-      uses: pypa/cibuildwheel@v2.8.1
+      uses: pypa/cibuildwheel@v2.11.1
       env:
         CIBW_ARCHS: all
         CIBW_TEST_SKIP: '*_arm64'
@@ -171,6 +179,7 @@ jobs:
       fail-fast: false
       matrix:
         name:
+        - win-py-3.11
         - win-py-3.10
         - win-py-3.9
         - win-py-3.8
@@ -179,6 +188,8 @@ jobs:
         - x64
         - x86
         include:
+        - name: win-py-3.11
+          pyenv: '3.11'
         - name: win-py-3.10
           pyenv: '3.10'
         - name: win-py-3.9
diff --git a/setup.py b/setup.py
index 1c70f4a6..36c7aafa 100755
--- a/setup.py
+++ b/setup.py
@@ -274,7 +274,7 @@ def gssapi_modules(lst):
 
 setup(
     name='gssapi',
-    version='1.8.1',
+    version='1.8.2',
     author='The Python GSSAPI Team',
     author_email='jborean93@gmail.com',
     packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',
@@ -296,6 +296,7 @@ def gssapi_modules(lst):
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: ISC License (ISCL)',
         'Programming Language :: Python :: Implementation :: CPython',

From 2c3571f54514c5efb74bd92ed1a4fcbedfe79047 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Wed, 26 Oct 2022 03:47:37 +1000
Subject: [PATCH 27/49] Bump GHA actions to latest version

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 38 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9f98fed7..9a4e2185 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,10 +11,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Check out code
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
     
     - name: Select python
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: 3.9
 
@@ -28,7 +28,7 @@ jobs:
         GSSAPI_COMPILER_ARGS: ''
 
     - name: Upload sdist
-      uses: actions/upload-artifact@v2
+      uses: actions/upload-artifact@v3
       with:
         name: artifact
         path: ./dist/*.tar.gz
@@ -99,7 +99,7 @@ jobs:
         echo "C:\Program Files${{ endsWith(matrix.version, '-win32') && ' (x86)' || '' }}\MIT\Kerberos\bin;$PATH" >> $GITHUB_PATH
 
     - name: Download gssapi sdist
-      uses: actions/download-artifact@v2
+      uses: actions/download-artifact@v3
       with:
         name: artifact
         path: ./
@@ -121,7 +121,7 @@ jobs:
         CIBW_BUILD_VERBOSITY: 1
 
     - name: Upload wheel
-      uses: actions/upload-artifact@v2
+      uses: actions/upload-artifact@v3
       with:
         path: ./wheelhouse/*.whl
         name: artifact
@@ -154,10 +154,10 @@ jobs:
 
     steps:
     - name: Check out code
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
 
     - name: Download built project
-      uses: actions/download-artifact@v2
+      uses: actions/download-artifact@v3
       with:
         name: artifact
         path: ./dist
@@ -201,16 +201,16 @@ jobs:
 
     steps:
     - name: Check out code
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
 
     - name: Download built project
-      uses: actions/download-artifact@v2
+      uses: actions/download-artifact@v3
       with:
         name: artifact
         path: ./dist
 
     - name: Install the right python
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: ${{ matrix.pyenv }}
         architecture: ${{ matrix.arch }}
@@ -229,10 +229,10 @@ jobs:
     runs-on: macos-latest
     steps:
     - name: Check out code
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
 
     - name: Download built project
-      uses: actions/download-artifact@v2
+      uses: actions/download-artifact@v3
       with:
         name: artifact
         path: ./dist
@@ -253,10 +253,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Check out code
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
 
     - name: Download built project
-      uses: actions/download-artifact@v2
+      uses: actions/download-artifact@v3
       with:
         name: artifact
         path: ./dist
@@ -268,21 +268,21 @@ jobs:
 
     - name: Get tarball path
       id: tarball
-      run: echo "::set-output name=tarball::`ls tag_build/*.tar.gz | awk -F/ '{print $2}'`"
+      run: echo "tarball=`ls tag_build/*.tar.gz | awk -F/ '{print $2}'`" >> $GITHUB_OUTPUT
 
     - name: Get release checksum path
       id: checksum
-      run: echo "::set-output name=checksum::`ls tag_build/*.sha512sum | awk -F/ '{print $2}'`"
+      run: echo "checksum=`ls tag_build/*.sha512sum | awk -F/ '{print $2}'`" >> $GITHUB_OUTPUT
 
     - name: Upload tagged build artifact
-      uses: actions/upload-artifact@v2
+      uses: actions/upload-artifact@v3
       with:
         path: tag_build/${{ steps.tarball.outputs.tarball }}
         name: artifact
 
     - name: Deploy stable docs
       if: startsWith(github.ref, 'refs/tags/v')
-      uses: JamesIves/github-pages-deploy-action@3.7.1
+      uses: JamesIves/github-pages-deploy-action@4.4.1
       with:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         BRANCH: gh-pages
@@ -323,7 +323,7 @@ jobs:
 
     - name: Deploy to PyPI
       if: startsWith(github.ref, 'refs/tags/v')
-      uses: pypa/gh-action-pypi-publish@v1.1.0
+      uses: pypa/gh-action-pypi-publish@v1.5.1
       with:
         user: __token__
         password: ${{ secrets.pypi_password }}

From ae8f7b054f1c4c2d2a72c5db965f96b0d187dc98 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Wed, 26 Oct 2022 04:00:24 +1000
Subject: [PATCH 28/49] Use just major version

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9a4e2185..d68b94f5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -282,7 +282,7 @@ jobs:
 
     - name: Deploy stable docs
       if: startsWith(github.ref, 'refs/tags/v')
-      uses: JamesIves/github-pages-deploy-action@4.4.1
+      uses: JamesIves/github-pages-deploy-action@v4
       with:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         BRANCH: gh-pages

From b0566667801d72f5f1857a21d057f921d5544d1d Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Fri, 4 Nov 2022 09:13:07 +1000
Subject: [PATCH 29/49] Fix up doc link in README

Signed-off-by: Jordan Borean 
---
 README.txt | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/README.txt b/README.txt
index 5077840e..e2bb8ad2 100644
--- a/README.txt
+++ b/README.txt
@@ -18,12 +18,8 @@ Python-GSSAPI provides both low-level and high level wrappers around the GSSAPI
 C libraries.  While it focuses on the Kerberos mechanism, it should also be
 useable with other GSSAPI mechanisms.
 
-Documentation for the latest released version (including pre-release versions)
-can be found at
-`https://pythongssapi.github.io/python-gssapi/stable `_.
-
-Documentation for the latest commit on main can be found at
-`https://pythongssapi.github.io/python-gssapi/latest `_.
+Documentation can be found at
+`https://pythongssapi.github.io/python-gssapi/ `_.
 
 Requirements
 ============

From 8a0461e6c33820354882e3d3fe90de8fd9bdd843 Mon Sep 17 00:00:00 2001
From: lilinjie 
Date: Wed, 11 Jan 2023 15:49:34 +0800
Subject: [PATCH 30/49] fix typo

Signed-off-by: lilinjie 
---
 gssapi/sec_contexts.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gssapi/sec_contexts.py b/gssapi/sec_contexts.py
index 812a6a3f..adbbf301 100644
--- a/gssapi/sec_contexts.py
+++ b/gssapi/sec_contexts.py
@@ -483,7 +483,7 @@ def delegated_creds(self) -> t.Optional[Credentials]:
         .. warning::
 
             This value will not be preserved across picklings.  These should
-            be separately exported and transfered.
+            be separately exported and transferred.
 
         """
         return self._delegated_creds

From b15b1394925633d1b0f0b4f0b0392c1479317b61 Mon Sep 17 00:00:00 2001
From: lilinjie 
Date: Mon, 3 Apr 2023 17:16:25 +0800
Subject: [PATCH 31/49] fix typo

Signed-off-by: lilinjie 
---
 gssapi/raw/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gssapi/raw/__init__.py b/gssapi/raw/__init__.py
index b18a74b2..0699c419 100644
--- a/gssapi/raw/__init__.py
+++ b/gssapi/raw/__init__.py
@@ -30,7 +30,7 @@
 are in modules of the form `gssapi.raw.ext_xyz`.
 
 All available functions and classes can be accessed directly from this
-module (`gssapi.raw`) -- it is unneccessary to directly import submodules.
+module (`gssapi.raw`) -- it is unnecessary to directly import submodules.
 """
 
 

From 86e99576be166695130fd5281efebf2ec8ee24e3 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Wed, 31 May 2023 13:00:43 +1000
Subject: [PATCH 32/49] Use OIDC for PyPI release

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 9 +++++----
 docs/source/conf.py      | 2 +-
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d68b94f5..0032f9c4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -251,6 +251,10 @@ jobs:
     - windows
 
     runs-on: ubuntu-latest
+    permissions:
+      # IMPORTANT: this permission is mandatory for trusted publishing
+      id-token: write
+
     steps:
     - name: Check out code
       uses: actions/checkout@v3
@@ -323,7 +327,4 @@ jobs:
 
     - name: Deploy to PyPI
       if: startsWith(github.ref, 'refs/tags/v')
-      uses: pypa/gh-action-pypi-publish@v1.5.1
-      with:
-        user: __token__
-        password: ${{ secrets.pypi_password }}
+      uses: pypa/gh-action-pypi-publish@release/v1
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 1644b8cd..b7a88039 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -316,7 +316,7 @@
 
 
 # Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'http://docs.python.org/': None}
+intersphinx_mapping = {'python': ('http://docs.python.org/', None)}
 
 # which docstring to use for the class
 # can be 'class', 'init', or 'both'

From d9200d1018ac916b30433da23898c8c5fbde0f28 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 18 Jul 2023 08:57:11 +1000
Subject: [PATCH 33/49] Fix Cython 3 Compatibility

Fixes compatibility with Cython 3 by fixing up the import so it doesn't
try and do a recursive import. This also increases the upper bound for
Cython to 4.0.0 now that 3.0.0 is compatible.

Signed-off-by: Jordan Borean 
---
 gssapi/raw/_enum_extensions/ext_dce.pyx | 2 +-
 pyproject.toml                          | 2 +-
 setup.py                                | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/gssapi/raw/_enum_extensions/ext_dce.pyx b/gssapi/raw/_enum_extensions/ext_dce.pyx
index fd2989ae..190c8285 100644
--- a/gssapi/raw/_enum_extensions/ext_dce.pyx
+++ b/gssapi/raw/_enum_extensions/ext_dce.pyx
@@ -1,6 +1,6 @@
 from gssapi.raw.cython_types cimport OM_uint32
 
-import gssapi.raw._enum_extensions as ext_registry
+from gssapi.raw import _enum_extensions as ext_registry
 
 
 cdef extern from "python_gssapi_ext.h":
diff --git a/pyproject.toml b/pyproject.toml
index 2f5c208c..5ee26df6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [build-system]
 requires = [
-    "Cython >= 0.29.29, < 3.0.0",  # 0.29.29 includes fixes for Python 3.11
+    "Cython >= 0.29.29, < 4.0.0",  # 0.29.29 includes fixes for Python 3.11
     "setuptools >= 40.6.0",  # Start of PEP 517 support for setuptools
 ]
 build-backend = "setuptools.build_meta"
diff --git a/setup.py b/setup.py
index 36c7aafa..881b9f9f 100755
--- a/setup.py
+++ b/setup.py
@@ -274,7 +274,7 @@ def gssapi_modules(lst):
 
 setup(
     name='gssapi',
-    version='1.8.2',
+    version='1.8.3',
     author='The Python GSSAPI Team',
     author_email='jborean93@gmail.com',
     packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',

From 3f807b150b9250bb13bd5fcb931003e02061b523 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 29 Aug 2023 05:04:04 +1000
Subject: [PATCH 34/49] Added Python 3.12 wheels

Adds wheels for Python 3.12 on macOS and Windows. There are no wheels
for Linux due to policies around linking libraries not included in the
wheel itself.

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 15 +++++++++++++--
 setup.py                 |  1 +
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0032f9c4..a499cadc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,7 +12,7 @@ jobs:
     steps:
     - name: Check out code
       uses: actions/checkout@v3
-    
+
     - name: Select python
       uses: actions/setup-python@v4
       with:
@@ -43,6 +43,10 @@ jobs:
       fail-fast: false
       matrix:
         include:
+        - os: macOS-12
+          version: cp312-macosx_x86_64
+        - os: macOS-12
+          version: cp312-macosx_arm64
         - os: macOS-12
           version: cp311-macosx_x86_64
         - os: macOS-12
@@ -62,6 +66,10 @@ jobs:
         - os: macOS-12
           version: cp37-macosx_x86_64
 
+        - os: windows-2022
+          version: cp312-win_amd64
+        - os: windows-2022
+          version: cp312-win32
         - os: windows-2022
           version: cp311-win_amd64
         - os: windows-2022
@@ -113,7 +121,7 @@ jobs:
         rm gssapi-*.tar.gz
 
     - name: Build wheel
-      uses: pypa/cibuildwheel@v2.11.1
+      uses: pypa/cibuildwheel@v2.15.0
       env:
         CIBW_ARCHS: all
         CIBW_TEST_SKIP: '*_arm64'
@@ -179,6 +187,7 @@ jobs:
       fail-fast: false
       matrix:
         name:
+        - win-py-3.12
         - win-py-3.11
         - win-py-3.10
         - win-py-3.9
@@ -188,6 +197,8 @@ jobs:
         - x64
         - x86
         include:
+        - name: win-py-3.12
+          pyenv: '3.12.0-rc.1'
         - name: win-py-3.11
           pyenv: '3.11'
         - name: win-py-3.10
diff --git a/setup.py b/setup.py
index 881b9f9f..ea8b9145 100755
--- a/setup.py
+++ b/setup.py
@@ -297,6 +297,7 @@ def gssapi_modules(lst):
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
         'Programming Language :: Python :: 3.11',
+        'Programming Language :: Python :: 3.12',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: ISC License (ISCL)',
         'Programming Language :: Python :: Implementation :: CPython',

From e10e7b7f20a2be996f31aa99616d35ad41960b2a Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 29 Aug 2023 09:44:54 +1000
Subject: [PATCH 35/49] Fix up CI doc deployment action

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a499cadc..95c44ea5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -299,10 +299,9 @@ jobs:
       if: startsWith(github.ref, 'refs/tags/v')
       uses: JamesIves/github-pages-deploy-action@v4
       with:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        BRANCH: gh-pages
-        FOLDER: ci_docs_build
-        TARGET_FOLDER: stable
+        branch: gh-pages
+        folder: ci_docs_build
+        target-folder: stable
 
     - name: Create release
       if: startsWith(github.ref, 'refs/tags/v')

From 7c95792408e693591517f94f67e87b438fceaeae Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 29 Aug 2023 10:08:03 +1000
Subject: [PATCH 36/49] Use newer actions for doc deployment workflow

Signed-off-by: Jordan Borean 
---
 .github/workflows/deploy-docs.yml | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 6b3bf546..2bfe12a9 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -8,10 +8,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Check out code
-      uses: actions/checkout@v2
+      uses: actions/checkout@v3
 
     - name: Select python
-      uses: actions/setup-python@v2
+      uses: actions/setup-python@v4
       with:
         python-version: 3.9
 
@@ -30,9 +30,8 @@ jobs:
         DISTRO: fedora:latest
 
     - name: Deploy latest docs
-      uses: JamesIves/github-pages-deploy-action@3.7.1
+      uses: JamesIves/github-pages-deploy-action@v4
       with:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        BRANCH: gh-pages
-        FOLDER: ci_docs_build
-        TARGET_FOLDER: latest
+        branch: gh-pages
+        folder: ci_docs_build
+        target-folder: latest

From b71bf97c9e8b362f8b1b3d43bcc3480a6c3d035a Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 29 Aug 2023 11:13:44 +1000
Subject: [PATCH 37/49] Set explicit write permissions for publish step

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 95c44ea5..2ecbda8c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -263,8 +263,8 @@ jobs:
 
     runs-on: ubuntu-latest
     permissions:
-      # IMPORTANT: this permission is mandatory for trusted publishing
-      id-token: write
+      id-token: write  # Needed for OIDC publishing
+      contents: read|write  # Needed for github-pages-deploy-action and other repo write tasks
 
     steps:
     - name: Check out code

From 3a14a03d0c119ec123dc8ce9cd94714113d711bc Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 29 Aug 2023 11:17:26 +1000
Subject: [PATCH 38/49] The syntax was incorrect so use just write

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2ecbda8c..85636ea5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -263,8 +263,10 @@ jobs:
 
     runs-on: ubuntu-latest
     permissions:
-      id-token: write  # Needed for OIDC publishing
-      contents: read|write  # Needed for github-pages-deploy-action and other repo write tasks
+      # Needed for OIDC publishing
+      id-token: write
+      # Needed for github-pages-deploy-action and other repo write tasks
+      contents: write
 
     steps:
     - name: Check out code

From 5c316ec3c8f99d3df6d689fadff4a391658e026a Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Wed, 30 Aug 2023 07:40:33 +1000
Subject: [PATCH 39/49] Do not use Cython 3 on build deps

Cython 3 generates code which fails on Python that was built with
assertions enabled. This commit adds the upper bound version until this
issue has been fixed.

CI has been updated to test out this specific scenario.

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 38 ++++++++++++++++++++++++++++++++++++++
 pyproject.toml           |  4 +++-
 setup.py                 |  2 +-
 3 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 85636ea5..ad5aeabe 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -134,6 +134,43 @@ jobs:
         path: ./wheelhouse/*.whl
         name: artifact
 
+  # To catch issues like this https://github.com/pythongssapi/python-gssapi/issues/327
+  assertion_build:
+    needs:
+    - build_sdist
+
+    runs-on: ubuntu-latest
+    steps:
+    - name: Download gssapi sdist
+      uses: actions/download-artifact@v3
+      with:
+        name: artifact
+        path: ./dist
+
+    - name: Compile Python with assertions
+      shell: bash
+      run: |
+        PYTHON_VERSION="3.11.5"
+        wget --quiet "https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz"
+        tar xf "Python-${PYTHON_VERSION}.tar.xz"
+        cd "Python-${PYTHON_VERSION}/"
+        ./configure --with-assertions --prefix "${PWD}/../Python-${PYTHON_VERSION}-build"
+        make
+        make install
+        cd ..
+
+        sudo apt-get update
+        DEBIAN_FRONTEND=noninteractive sudo apt-get -y install krb5-user libkrb5-dev
+
+        GSSAPI_VER="$( find ./dist -type f -name 'gssapi-*.tar.gz' -printf "%f\n" | sed -n 's/gssapi-\(.*\)\.tar\.gz/\1/p' )"
+
+        PATH="${PWD}/Python-${PYTHON_VERSION}-build/bin:${PATH}"
+        python3 -m pip install gssapi=="${GSSAPI_VER}" \
+            --find-links "file://${PWD}/dist" \
+            --verbose
+
+        python3 -c "import gssapi"
+
   linux:
     needs:
     - build_sdist
@@ -257,6 +294,7 @@ jobs:
     name: publish
 
     needs:
+    - assertion_build
     - linux
     - macos
     - windows
diff --git a/pyproject.toml b/pyproject.toml
index 5ee26df6..355e10f7 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,8 @@
 [build-system]
 requires = [
-    "Cython >= 0.29.29, < 4.0.0",  # 0.29.29 includes fixes for Python 3.11
+    # 0.29.29 includes fixes for Python 3.11
+    # Cannot use 3 until https://github.com/cython/cython/issues/5665 is fixed
+    "Cython >= 0.29.29, < 3.0.0",
     "setuptools >= 40.6.0",  # Start of PEP 517 support for setuptools
 ]
 build-backend = "setuptools.build_meta"
diff --git a/setup.py b/setup.py
index ea8b9145..a71967f2 100755
--- a/setup.py
+++ b/setup.py
@@ -274,7 +274,7 @@ def gssapi_modules(lst):
 
 setup(
     name='gssapi',
-    version='1.8.3',
+    version='1.8.4',
     author='The Python GSSAPI Team',
     author_email='jborean93@gmail.com',
     packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',

From b674a5742dc6462d313376c9355faec8fbae7e2a Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Mon, 29 Jan 2024 11:31:37 +1000
Subject: [PATCH 40/49] Re-add support for Cython 3

Signed-off-by: Jordan Borean 
---
 pyproject.toml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index 355e10f7..c7d90207 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,8 +1,7 @@
 [build-system]
 requires = [
     # 0.29.29 includes fixes for Python 3.11
-    # Cannot use 3 until https://github.com/cython/cython/issues/5665 is fixed
-    "Cython >= 0.29.29, < 3.0.0",
+    "Cython >= 3.0.3, < 4.0.0",
     "setuptools >= 40.6.0",  # Start of PEP 517 support for setuptools
 ]
 build-backend = "setuptools.build_meta"

From e509231d8590e0fec32f239a6c11c2653e8967bb Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Mon, 29 Jan 2024 12:27:09 +1000
Subject: [PATCH 41/49] Use MyST for markdown docs build

Moves away from recommonmark to MyST when building the markdown docs.
Recommonmark is deprecated and no longer maintained in favour of MyST.
The only change here is to swap up the dependencies and enable the MyST
plugin in the conf.py.

Signed-off-by: Jordan Borean 
---
 docs-requirements.txt                         |  2 +-
 docs/custom_extensions/custom_recommonmark.py |  8 --------
 docs/source/conf.py                           | 10 +++-------
 3 files changed, 4 insertions(+), 16 deletions(-)
 delete mode 100644 docs/custom_extensions/custom_recommonmark.py

diff --git a/docs-requirements.txt b/docs-requirements.txt
index a2223745..8a0d1c8e 100644
--- a/docs-requirements.txt
+++ b/docs-requirements.txt
@@ -1,4 +1,4 @@
+myst-parser  # For parsing markdown docs instead of rst
 Sphinx >= 1.3.1
 sphinx-autoapi
 sphinx-rtd-theme >= 0.2.5b1
-recommonmark >= 0.4.0
diff --git a/docs/custom_extensions/custom_recommonmark.py b/docs/custom_extensions/custom_recommonmark.py
deleted file mode 100644
index f7e767b6..00000000
--- a/docs/custom_extensions/custom_recommonmark.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from recommonmark.parser import CommonMarkParser
-from docutils import nodes
-
-# treats "verbatim" (code without a language specified) as a code sample
-class AllCodeCommonMarkParser(CommonMarkParser):
-    def verbatim(self, text):
-        node = nodes.literal_block(text, text)
-        self.current_node.append(node)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index b7a88039..8d99409a 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -20,8 +20,6 @@
 
 sys.path.insert(0, os.path.abspath('../custom_extensions'))
 
-from custom_recommonmark import AllCodeCommonMarkParser
-
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
@@ -30,6 +28,7 @@
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 extensions = [
+  'myst_parser',
   'sphinx.ext.autodoc',
   'sphinx.ext.intersphinx',
   'sphinx.ext.viewcode',
@@ -47,14 +46,11 @@
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
-# Parsers for different suffixes
-source_parsers = {
-    '.md': AllCodeCommonMarkParser
-}
-
 # The suffix of source filenames.
 source_suffix = ['.rst', '.md']
 
+myst_all_links_external = True
+
 # The encoding of source files.
 #source_encoding = 'utf-8-sig'
 

From b72364da78b847313b9b15e1e9d4b04f8f5d2bed Mon Sep 17 00:00:00 2001
From: Daniel Kahn Gillmor 
Date: Sun, 28 Jan 2024 00:33:09 -0500
Subject: [PATCH 42/49] Enable the use of `mypy --no-explicit-reexport` on
 downstream projects

When testing simple python code like this with mypy for type-safety:

```
import gssapi
gsc:gssapi.SecurityContext
gsc = gssapi.SecurityContext(
       usage='initiate',
       name=gssapi.Name('imap@localhost',
                        gssapi.NameType.hostbased_service))
```

I see these errors:

```
0 $ mypy --no-implicit-reexport ./test.py
test.py:3: error: Name "gssapi.SecurityContext" is not defined  [name-defined]
test.py:4: error: Module "gssapi" does not explicitly export attribute "SecurityContext"  [attr-defined]
test.py:4: error: Module "gssapi" does not explicitly export attribute "Name"  [attr-defined]
test.py:4: error: Module "gssapi" does not explicitly export attribute "NameType"  [attr-defined]
Found 4 errors in 1 file (checked 1 source file)
1 $
```

The same thing happens when using `mypy --strict`.

[a
blogpost](https://til.codeinthehole.com/posts/how-to-handle-convenience-imports-with-mypy/)
suggested that the `__all__` variable in `gssapi/__init__.py` might be
the way to fix this.

I can confirm that it does clear the error, but I'm not enough of a
python module expert to know whether there might be some undesirable
side effects as well to making this change.  Please review!

Tested on debian testing/unstable, with:

- python3  3.11.6-1
- python3-gssapi 1.8.2-1+b2
- mypy 1.8.0-1

Signed-off-by: Daniel Kahn Gillmor 
---
 gssapi/__init__.py | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/gssapi/__init__.py b/gssapi/__init__.py
index cb89f73f..73aa9dfb 100644
--- a/gssapi/__init__.py
+++ b/gssapi/__init__.py
@@ -38,3 +38,17 @@
 from gssapi.mechs import Mechanism  # noqa
 
 from gssapi._utils import set_encoding  # noqa
+
+__all__ = [
+    'AddressType',
+    'Credentials',
+    'IntEnumFlagSet',
+    'Mechanism',
+    'MechType',
+    'Name',
+    'NameType',
+    'OID',
+    'RequirementFlag',
+    'SecurityContext',
+    'set_encoding',
+]

From 6e0b6b16825199aca531779c11de777c9c695c03 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 30 Jan 2024 07:38:05 +1000
Subject: [PATCH 43/49] Update GHA action versions

Updates the GitHub Actions versions to the latest ones available.

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml          | 56 +++++++++++++++++--------------
 .github/workflows/deploy-docs.yml |  4 +--
 .github/workflows/stale.yml       |  2 +-
 3 files changed, 33 insertions(+), 29 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ad5aeabe..3a3898aa 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,10 +11,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Check out code
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Select python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: 3.9
 
@@ -28,9 +28,9 @@ jobs:
         GSSAPI_COMPILER_ARGS: ''
 
     - name: Upload sdist
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
-        name: artifact
+        name: artifact-sdist
         path: ./dist/*.tar.gz
 
   build_wheels:
@@ -107,9 +107,9 @@ jobs:
         echo "C:\Program Files${{ endsWith(matrix.version, '-win32') && ' (x86)' || '' }}\MIT\Kerberos\bin;$PATH" >> $GITHUB_PATH
 
     - name: Download gssapi sdist
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
-        name: artifact
+        name: artifact-sdist
         path: ./
 
     - name: Extract sdist
@@ -121,7 +121,7 @@ jobs:
         rm gssapi-*.tar.gz
 
     - name: Build wheel
-      uses: pypa/cibuildwheel@v2.15.0
+      uses: pypa/cibuildwheel@v2.16.5
       env:
         CIBW_ARCHS: all
         CIBW_TEST_SKIP: '*_arm64'
@@ -129,10 +129,10 @@ jobs:
         CIBW_BUILD_VERBOSITY: 1
 
     - name: Upload wheel
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         path: ./wheelhouse/*.whl
-        name: artifact
+        name: artifact-wheel-${{ matrix.version }}
 
   # To catch issues like this https://github.com/pythongssapi/python-gssapi/issues/327
   assertion_build:
@@ -142,9 +142,9 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Download gssapi sdist
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
-        name: artifact
+        name: artifact-sdist
         path: ./dist
 
     - name: Compile Python with assertions
@@ -199,12 +199,13 @@ jobs:
 
     steps:
     - name: Check out code
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Download built project
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
-        name: artifact
+        pattern: artifact-*
+        merge-multiple: true
         path: ./dist
 
     - name: Test gssapi
@@ -249,16 +250,17 @@ jobs:
 
     steps:
     - name: Check out code
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Download built project
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
-        name: artifact
+        pattern: artifact-*
+        merge-multiple: true
         path: ./dist
 
     - name: Install the right python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: ${{ matrix.pyenv }}
         architecture: ${{ matrix.arch }}
@@ -277,12 +279,13 @@ jobs:
     runs-on: macos-latest
     steps:
     - name: Check out code
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Download built project
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
-        name: artifact
+        pattern: artifact-*
+        merge-multiple: true
         path: ./dist
 
     - name: Test gssapi
@@ -308,12 +311,13 @@ jobs:
 
     steps:
     - name: Check out code
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Download built project
-      uses: actions/download-artifact@v3
+      uses: actions/download-artifact@v4
       with:
-        name: artifact
+        pattern: artifact-*
+        merge-multiple: true
         path: ./dist
 
     - name: Create GitHub release artifact
@@ -330,10 +334,10 @@ jobs:
       run: echo "checksum=`ls tag_build/*.sha512sum | awk -F/ '{print $2}'`" >> $GITHUB_OUTPUT
 
     - name: Upload tagged build artifact
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         path: tag_build/${{ steps.tarball.outputs.tarball }}
-        name: artifact
+        name: release-asset
 
     - name: Deploy stable docs
       if: startsWith(github.ref, 'refs/tags/v')
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index 2bfe12a9..3c6df505 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -8,10 +8,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Check out code
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Select python
-      uses: actions/setup-python@v4
+      uses: actions/setup-python@v5
       with:
         python-version: 3.9
 
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index aef40d7f..d55558d0 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -11,7 +11,7 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-    - uses: actions/stale@v4.0.0
+    - uses: actions/stale@v9.0.0
       id: stale
       with:
         days-before-stale: -1

From cf9a337dabc7bf9fc195248423a3e40577d91d07 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 27 Aug 2024 08:44:20 +1000
Subject: [PATCH 44/49] Fix DCE symbol name logic on macOS

Only use the private Apple symbol name for the DCE extension detection
when using the actual GSS.Framework target. This will allow compiling
this library against a custom MIT krb5 or Heimdal library on macOS with
support for the normal IOV wrapping functions.

Signed-off-by: Jordan Borean 
---
 setup.py | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/setup.py b/setup.py
index a71967f2..6bb2e5bc 100755
--- a/setup.py
+++ b/setup.py
@@ -156,6 +156,7 @@ def get_output(*args, **kwargs):
 ENABLE_SUPPORT_DETECTION = \
     (os.environ.get('GSSAPI_SUPPORT_DETECT', 'true').lower() == 'true')
 
+wrap_iov_symbol_name = 'gss_wrap_iov'
 if ENABLE_SUPPORT_DETECTION:
     import ctypes.util
 
@@ -205,6 +206,9 @@ def get_output(*args, **kwargs):
 
     GSSAPI_LIB = ctypes.CDLL(os.path.join(main_path, main_lib))
 
+    if hasattr(GSSAPI_LIB, '__ApplePrivate_gss_wrap_iov'):
+        wrap_iov_symbol_name = '__ApplePrivate_gss_wrap_iov'
+
 
 def make_extension(name_fmt, module, **kwargs):
     """Helper method to remove the repetition in extension declarations."""
@@ -323,9 +327,7 @@ def gssapi_modules(lst):
         extension_file('rfc5588', 'gss_store_cred'),
         extension_file('rfc5801', 'gss_inquire_saslname_for_mech'),
         extension_file('cred_imp_exp', 'gss_import_cred'),
-        extension_file('dce',
-                       '__ApplePrivate_gss_wrap_iov' if osx_has_gss_framework
-                       else 'gss_wrap_iov'),
+        extension_file('dce', wrap_iov_symbol_name),
         extension_file('dce_aead', 'gss_wrap_aead'),
         extension_file('iov_mic', 'gss_get_mic_iov'),
         extension_file('ggf', 'gss_inquire_sec_context_by_oid'),

From c8504f7792d933c41c7dc4002f87f320a9e5464c Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 17 Sep 2024 09:34:02 +1000
Subject: [PATCH 45/49] Update CI to build CPython 3.13 wheels & drop 3.7

Updates the CI to build 3.13 wheels based on the latest RC and drop the
3.7 support and wheels.

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 24 +++++++++++++-----------
 README.txt               |  4 ++--
 pyproject.toml           |  1 -
 setup.py                 |  6 +++---
 4 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3a3898aa..76ce12bb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -43,6 +43,10 @@ jobs:
       fail-fast: false
       matrix:
         include:
+        - os: macOS-12
+          version: cp313-macosx_x86_64
+        - os: macOS-12
+          version: cp313-macosx_arm64
         - os: macOS-12
           version: cp312-macosx_x86_64
         - os: macOS-12
@@ -63,9 +67,11 @@ jobs:
           version: cp38-macosx_x86_64
         - os: macOS-12
           version: cp38-macosx_arm64
-        - os: macOS-12
-          version: cp37-macosx_x86_64
 
+        - os: windows-2022
+          version: cp313-win_amd64
+        - os: windows-2022
+          version: cp313-win32
         - os: windows-2022
           version: cp312-win_amd64
         - os: windows-2022
@@ -86,10 +92,6 @@ jobs:
           version: cp38-win_amd64
         - os: windows-2022
           version: cp38-win32
-        - os: windows-2022
-          version: cp37-win_amd64
-        - os: windows-2022
-          version: cp37-win32
 
     steps:
     - name: Set up environment
@@ -121,7 +123,7 @@ jobs:
         rm gssapi-*.tar.gz
 
     - name: Build wheel
-      uses: pypa/cibuildwheel@v2.16.5
+      uses: pypa/cibuildwheel@v2.21.0
       env:
         CIBW_ARCHS: all
         CIBW_TEST_SKIP: '*_arm64'
@@ -225,18 +227,20 @@ jobs:
       fail-fast: false
       matrix:
         name:
+        - win-py-3.13
         - win-py-3.12
         - win-py-3.11
         - win-py-3.10
         - win-py-3.9
         - win-py-3.8
-        - win-py-3.7
         arch:
         - x64
         - x86
         include:
+        - name: win-py-3.13
+          pyenv: '3.13.0-rc.2'
         - name: win-py-3.12
-          pyenv: '3.12.0-rc.1'
+          pyenv: '3.12'
         - name: win-py-3.11
           pyenv: '3.11'
         - name: win-py-3.10
@@ -245,8 +249,6 @@ jobs:
           pyenv: '3.9'
         - name: win-py-3.8
           pyenv: '3.8'
-        - name: win-py-3.7
-          pyenv: '3.7'
 
     steps:
     - name: Check out code
diff --git a/README.txt b/README.txt
index e2bb8ad2..f98ca7ec 100644
--- a/README.txt
+++ b/README.txt
@@ -32,14 +32,14 @@ Basic
 
 * a C compiler (such as GCC)
 
-* Python 3.7+ (older releases support older versions, but are unsupported)
+* Python 3.8+ (older releases support older versions, but are unsupported)
 
 * the `decorator` python package
 
 Compiling from Scratch
 ----------------------
 
-To compile from scratch, you will need Cython >= 0.29.29 which is automatically
+To compile from scratch, you will need Cython ``>= 3.0.3, < 4.0.0`` which is automatically
 installed by pip in an isolated build virtual environment.
 
 For Running the Tests
diff --git a/pyproject.toml b/pyproject.toml
index c7d90207..41c0bcf8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,5 @@
 [build-system]
 requires = [
-    # 0.29.29 includes fixes for Python 3.11
     "Cython >= 3.0.3, < 4.0.0",
     "setuptools >= 40.6.0",  # Start of PEP 517 support for setuptools
 ]
diff --git a/setup.py b/setup.py
index 6bb2e5bc..f348dc6b 100755
--- a/setup.py
+++ b/setup.py
@@ -278,7 +278,7 @@ def gssapi_modules(lst):
 
 setup(
     name='gssapi',
-    version='1.8.4',
+    version='1.9.0',
     author='The Python GSSAPI Team',
     author_email='jborean93@gmail.com',
     packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',
@@ -291,17 +291,17 @@ def gssapi_modules(lst):
     long_description=long_desc,
     license='LICENSE.txt',
     url="https://github.com/pythongssapi/python-gssapi",
-    python_requires=">=3.7",
+    python_requires=">=3.8",
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Programming Language :: Python',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
         'Programming Language :: Python :: 3.11',
         'Programming Language :: Python :: 3.12',
+        'Programming Language :: Python :: 3.13',
         'Intended Audience :: Developers',
         'License :: OSI Approved :: ISC License (ISCL)',
         'Programming Language :: Python :: Implementation :: CPython',

From ca046f45fb12bdd3f622bf1012a057a4fe0bf318 Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Thu, 3 Oct 2024 15:17:29 +1000
Subject: [PATCH 46/49] Bump Python 3.13 test version

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 76ce12bb..6c4fd5bf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -123,7 +123,7 @@ jobs:
         rm gssapi-*.tar.gz
 
     - name: Build wheel
-      uses: pypa/cibuildwheel@v2.21.0
+      uses: pypa/cibuildwheel@v2.21.2
       env:
         CIBW_ARCHS: all
         CIBW_TEST_SKIP: '*_arm64'
@@ -238,7 +238,7 @@ jobs:
         - x86
         include:
         - name: win-py-3.13
-          pyenv: '3.13.0-rc.2'
+          pyenv: '3.13.0-rc.3'
         - name: win-py-3.12
           pyenv: '3.12'
         - name: win-py-3.11

From fc7326e6db1ed54fbf80bbc8d6c5da0ad1c1a66b Mon Sep 17 00:00:00 2001
From: Jordan Borean 
Date: Tue, 18 Feb 2025 05:01:17 +1000
Subject: [PATCH 47/49] Update macOS build versions

Update the versions of macOS that is used to build the wheels in CI.
Also removes invalid DNF 5 option now that it is being tested in CI.

Signed-off-by: Jordan Borean 
---
 .github/workflows/ci.yml | 28 +++++++++++++---------------
 ci/lib.sh                |  4 ++--
 2 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6c4fd5bf..ef63ae23 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -43,29 +43,29 @@ jobs:
       fail-fast: false
       matrix:
         include:
-        - os: macOS-12
+        - os: macOS-13
           version: cp313-macosx_x86_64
-        - os: macOS-12
+        - os: macOS-15
           version: cp313-macosx_arm64
-        - os: macOS-12
+        - os: macOS-13
           version: cp312-macosx_x86_64
-        - os: macOS-12
+        - os: macOS-15
           version: cp312-macosx_arm64
-        - os: macOS-12
+        - os: macOS-13
           version: cp311-macosx_x86_64
-        - os: macOS-12
+        - os: macOS-15
           version: cp311-macosx_arm64
-        - os: macOS-12
+        - os: macOS-13
           version: cp310-macosx_x86_64
-        - os: macOS-12
+        - os: macOS-15
           version: cp310-macosx_arm64
-        - os: macOS-12
+        - os: macOS-13
           version: cp39-macosx_x86_64
-        - os: macOS-12
+        - os: macOS-15
           version: cp39-macosx_arm64
-        - os: macOS-12
+        - os: macOS-13
           version: cp38-macosx_x86_64
-        - os: macOS-12
+        - os: macOS-15
           version: cp38-macosx_arm64
 
         - os: windows-2022
@@ -123,10 +123,8 @@ jobs:
         rm gssapi-*.tar.gz
 
     - name: Build wheel
-      uses: pypa/cibuildwheel@v2.21.2
+      uses: pypa/cibuildwheel@v2.22.0
       env:
-        CIBW_ARCHS: all
-        CIBW_TEST_SKIP: '*_arm64'
         CIBW_BUILD: ${{ matrix.version }}
         CIBW_BUILD_VERBOSITY: 1
 
diff --git a/ci/lib.sh b/ci/lib.sh
index 338434d7..a8d29852 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -23,8 +23,8 @@ lib::setup::debian_install() {
 }
 
 lib::setup::rh_dnfinst() {
-    # dnf has no update-only verb.  Also: modularity just makes this slower.
-    dnf -y --nogpgcheck --disablerepo=\*modul\* install $@
+    # dnf has no update-only verb.
+    dnf -y --nogpgcheck install $@
 }
 
 lib::setup::centos_install() {

From 52bd8e9f6c90f885ec332445962d027af6a9b94d Mon Sep 17 00:00:00 2001
From: Aleksey Kondratov 
Date: Mon, 17 Feb 2025 19:02:24 +0300
Subject: [PATCH 48/49] Fix type annotation for gssapi.creds.Credentials name
 property. Add test checks.

Signed-off-by: Aleksey Kondratov 
---
 gssapi/creds.py                 | 4 ++--
 gssapi/tests/test_high_level.py | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/gssapi/creds.py b/gssapi/creds.py
index 4ea53bfd..ea871a91 100644
--- a/gssapi/creds.py
+++ b/gssapi/creds.py
@@ -82,9 +82,9 @@ def __new__(
                       super(Credentials, cls).__new__(cls, base_creds))
 
     @property
-    def name(self) -> rnames.Name:
+    def name(self) -> names.Name:
         """Get the name associated with these credentials"""
-        return t.cast(rnames.Name,
+        return t.cast(names.Name,
                       self.inquire(name=True, lifetime=False, usage=False,
                                    mechs=False).name)
 
diff --git a/gssapi/tests/test_high_level.py b/gssapi/tests/test_high_level.py
index d7c43aaf..ed6a4a0f 100644
--- a/gssapi/tests/test_high_level.py
+++ b/gssapi/tests/test_high_level.py
@@ -224,6 +224,7 @@ def test_inquire(self, str_name, kwargs):
 
         if kwargs['name']:
             self.assertEqual(resp.name, self.name)
+            self.assertIsInstance(resp.name, gssnames.Name)
         else:
             self.assertIsNone(resp.name)
 
@@ -250,6 +251,7 @@ def test_inquire_by_mech(self, str_name, kwargs):
 
         if kwargs['name']:
             self.assertEqual(resp.name, self.name)
+            self.assertIsInstance(resp.name, gssnames.Name)
         else:
             self.assertIsNone(resp.name)
 

From 6cd2011817e9ec2dd3fe2bc703837cdffe9c642a Mon Sep 17 00:00:00 2001
From: Thomas Heavey 
Date: Mon, 16 Jun 2025 10:40:51 -0400
Subject: [PATCH 49/49] remove deprecated license classifier

Should fix #361

Signed-off-by: Thomas Heavey 
---
 setup.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/setup.py b/setup.py
index f348dc6b..e636c506 100755
--- a/setup.py
+++ b/setup.py
@@ -303,7 +303,6 @@ def gssapi_modules(lst):
         'Programming Language :: Python :: 3.12',
         'Programming Language :: Python :: 3.13',
         'Intended Audience :: Developers',
-        'License :: OSI Approved :: ISC License (ISCL)',
         'Programming Language :: Python :: Implementation :: CPython',
         'Programming Language :: Cython',
         'Topic :: Security',