Full Code of kevin1024/pytest-httpbin for AI

master 60170e377830 cached
24 files
32.4 KB
9.5k tokens
53 symbols
1 requests
Download .txt
Repository: kevin1024/pytest-httpbin
Branch: master
Commit: 60170e377830
Files: 24
Total size: 32.4 KB

Directory structure:
gitextract_mo63menp/

├── .flake8
├── .github/
│   └── workflows/
│       └── main.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── DESCRIPTION.rst
├── LICENSE
├── MANIFEST.in
├── README.md
├── pyproject.toml
├── pytest_httpbin/
│   ├── __init__.py
│   ├── certs/
│   │   ├── README.md
│   │   ├── client.pem
│   │   ├── server.key
│   │   └── server.pem
│   ├── certs.py
│   ├── plugin.py
│   ├── serve.py
│   └── version.py
├── release.py
├── runtests.sh
└── tests/
    ├── conftest.py
    ├── test_httpbin.py
    ├── test_server.py
    └── util.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .flake8
================================================
[flake8]
disable-noqa = True
max-line-length = 88
extend-ignore =
    E203


================================================
FILE: .github/workflows/main.yaml
================================================
name: CI
on:
  push:
    branches:
      - master
    tags:
      - v*
  pull_request:

jobs:
  tox:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        python-version: [3.8, 3.9, '3.10', 3.11, 3.12, 3.13, pypy-3.10]
        os: [macOS-latest, ubuntu-latest, windows-latest]

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set Up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: pip
          allow-prereleases: true
          cache-dependency-path: |
            pyproject.toml
            setup.cfg
            setup.py

      - name: Install
        run: |
          pip install tox

      - name: tox
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: tox -e py,release

      - name: upload dist
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.os }}_${{ matrix.python-version}}_dist
          path: dist

  all-successful:
    # https://github.community/t/is-it-possible-to-require-all-github-actions-tasks-to-pass-without-enumerating-them/117957/4?u=graingert
    runs-on: ubuntu-latest
    needs: [tox]
    permissions:
      id-token: write
    steps:
      - name: Download dists for PyPI
        uses: actions/download-artifact@v4
        with:
          name: ubuntu-latest_3.11_dist
          path: dist

      - name: Display structure of donwloaded files
        run: ls -R

      - name: Publish package
        if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
        uses: pypa/gh-action-pypi-publish@release/v1

      - name: note that all tests succeeded
        run: echo "🎉"


================================================
FILE: .gitignore
================================================
# Backup files
*.~
*.swp

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Distribution / packaging
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
.tox/
.coverage
.cache
nosetests.xml
coverage.xml

# Translations
*.mo

# Sphinx documentation
docs/_build/


================================================
FILE: .pre-commit-config.yaml
================================================
repos:
  - repo: https://github.com/asottile/pyupgrade
    rev: v3.4.0
    hooks:
      - id: pyupgrade
        args: ['--py38-plus']

  - repo: https://github.com/psf/black
    rev: 23.3.0
    hooks:
      - id: black
        args: ['--target-version', 'py38']

  - repo: https://github.com/pycqa/isort
    rev: 5.12.0
    hooks:
      - id: isort

  - repo: https://github.com/pre-commit/pygrep-hooks
    rev: v1.10.0
    hooks:
      - id: python-check-blanket-noqa

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: check-merge-conflict
      - id: check-toml
      - id: check-yaml
      - id: mixed-line-ending

  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v3.0.0-alpha.9-for-vscode
    hooks:
      - id: prettier
        args: [--prose-wrap=always, --print-width=88]

  - repo: https://github.com/myint/autoflake
    rev: v2.1.1
    hooks:
      - id: autoflake
        args:
          - --in-place
          - --remove-all-unused-imports
          - --expand-star-imports
          - --remove-duplicate-keys
          - --remove-unused-variables

  - repo: https://github.com/pycqa/flake8
    rev: 6.0.0
    hooks:
      - id: flake8
        additional_dependencies: [flake8-2020]


================================================
FILE: DESCRIPTION.rst
================================================
pytest-httpbin
==============

httpbin is an amazing web service for testing HTTP libraries. It has several
great endpoints that can test pretty much everything you need in a HTTP
library. The only problem is: maybe you don't want to wait for your tests to
travel across the Internet and back to make assertions against a remote web
service.

Enter pytest-httpbin. Pytest-httpbin creates a pytest "fixture" that is
dependency-injected into your tests. It automatically starts up a HTTP server
in a separate thread running httpbin and provides your test with the URL in the
fixture. Check out this example:

.. code-block:: python

    def test_that_my_library_works_kinda_ok(httpbin):
        assert requests.get(httpbin.url + '/get/').status_code == 200

This replaces a test that might have looked like this before:

.. code-block:: python

    def test_that_my_library_works_kinda_ok():
        assert requests.get('http://httpbin.org/get').status_code == 200

pytest-httpbin also supports https and includes its own CA cert you can use.
Check out `the full documentation`_ on the github page.

.. _the full documentation: https://github.com/kevin1024/pytest-httpbin


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014-2023 Kevin McCarthy

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: MANIFEST.in
================================================
# If using Python 2.6 or less, then have to include package data, even though
# it's already declared in setup.py
include pytest_httpbin/certs/*
include DESCRIPTION.rst
recursive-include tests *.py


================================================
FILE: README.md
================================================
# pytest-httpbin

[![Build Status](https://github.com/kevin1024/pytest-httpbin/actions/workflows/main.yaml/badge.svg)](https://github.com/kevin1024/pytest-httpbin/actions/workflows/main.yaml)

[httpbin](https://httpbin.org/) is an amazing web service for testing HTTP libraries. It
has several great endpoints that can test pretty much everything you need in a HTTP
library. The only problem is: maybe you don't want to wait for your tests to travel
across the Internet and back to make assertions against a remote web service (speed),
and maybe you want to work offline (convenience).

Enter **pytest-httpbin**. Pytest-httpbin creates a
[pytest fixture](https://pytest.org/latest/fixture.html) that is dependency-injected
into your tests. It automatically starts up a HTTP server in a separate thread running
httpbin and provides your test with the URL in the fixture. Check out this example:

```python
def test_that_my_library_works_kinda_ok(httpbin):
    assert requests.get(httpbin.url + '/get').status_code == 200
```

This replaces a test that might have looked like this before:

```python
def test_that_my_library_works_kinda_ok():
    assert requests.get('http://httpbin.org/get').status_code == 200
```

If you're making a lot of requests to httpbin, it can radically speed up your tests.

![demo](http://i.imgur.com/heNOQLP.gif)

# HTTPS support

pytest-httpbin also supports HTTPS:

```python
def test_that_my_library_works_kinda_ok(httpbin_secure):
    assert requests.get(httpbin_secure.url + '/get/').status_code == 200
```

It's actually starting 2 web servers in separate threads in the background: one HTTP and
one HTTPS. The servers are started on a random port (see below for fixed port support),
on the loopback interface on your machine. Pytest-httpbin includes a self-signed
certificate. If your library verifies certificates against a CA (and it should), you'll
have to add the CA from pytest-httpbin. The path to the pytest-httpbin CA bundle can by
found like this `python -m pytest_httpbin.certs`.

For example in requests, you can set the `REQUESTS_CA_BUNDLE` python path. You can run
your tests like this:

```bash
REQUESTS_CA_BUNDLE=`python -m pytest_httpbin.certs` py.test tests/
```

# API of the injected object

The injected object has the following attributes:

- url
- port
- host

and the following methods:

- join(string): Returns the results of calling `urlparse.urljoin` with the url from the
  injected server automatically applied as the first argument. You supply the second
  argument

Also, I defined `__add__` on the object to append to `httpbin.url`. This means you can
do stuff like `httpbin + '/get'` instead of `httpbin.url + '/get'`.

## Testing both HTTP and HTTPS endpoints with one test

If you ever find yourself needing to test both the http and https version of and
endpoint, you can use the `httpbin_both` funcarg like this:

```python
def test_that_my_library_works_kinda_ok(httpbin_both):
    assert requests.get(httpbin_both.url + '/get/').status_code == 200
```

Through the magic of pytest parametrization, this function will actually execute twice:
once with an http url and once with an https url.

## Using pytest-httpbin with unittest-style test cases

I have provided 2 additional fixtures to make testing with class-based tests easier. I
have also provided a couple decorators that provide some syntactic sugar around the
pytest method of adding the fixtures to class-based tests. Just add the
`use_class_based_httpbin` and/or `use_class_based_httpbin_secure` class decorators to
your class, and then you can access httpbin using self.httpbin and self.httpbin_secure.

```python
import pytest_httpbin

@pytest_httpbin.use_class_based_httpbin
@pytest_httpbin.use_class_based_httpbin_secure
class TestClassBassedTests(unittest.TestCase):
    def test_http(self):
        assert requests.get(self.httpbin.url + '/get').response

    def test_http_secure(self):
        assert requests.get(self.httpbin_secure.url + '/get').response
```

## Running the server on fixed port

Sometimes a randomized port can be a problem. Worry not, you can fix the port number to
a desired value with the `HTTPBIN_HTTP_PORT` and `HTTPBIN_HTTPS_PORT` environment
variables. If those are defined during pytest plugins are loaded, `httbin` and
`httpbin_secure` fixtures will run on given ports. You can run your tests like this:

```bash
HTTPBIN_HTTP_PORT=8080 HTTPBIN_HTTPS_PORT=8443 py.test tests/
```

## Installation

[![PyPI Version](https://img.shields.io/pypi/v/pytest-httpbin.svg)](https://pypi.org/project/pytest-httpbin/)
[![Supported Versions](https://img.shields.io/pypi/pyversions/pytest-httpbin.svg)](https://pypi.org/project/pytest-httpbin/)

To install from [PyPI](https://pypi.org/project/pytest-httpbin/), all you need to do is
this:

```bash
pip install pytest-httpbin
```

and your tests executed by pytest all will have access to the `httpbin` and
`httpbin_secure` funcargs. Cool right?

## Support and dependencies

pytest-httpbin supports Python 3.8+, and pypy. It will automatically
install httpbin and flask when you install it from PyPI.

[httpbin](https://github.com/postmanlabs/httpbin) itself does not support python 2.6 as
of version 0.6.0, when the Flask-common dependency was added. If you need python 2.6
support pin the httpbin version to 0.5.0

## Running the pytest-httpbin test suite

If you want to run pytest-httpbin's test suite, you'll need to install requests and
pytest, and then use the ./runtests.sh script.

```bash
pip install pytest
./runtests.sh
```

Also, you can use tox to run the tests on all supported python versions:

```bash
pip install tox
tox
```

## Changelog

- 2.1.0
  - Drop support for Python 3.7 (#85)
  - Test against PyPy 3.10 (#77)
  - Add support for CPython 3.13 by regenerating the bundled certificates (#90)
  - Fix an issue where secure POST requests would fail with a connection reset
    by peer (#90)
  - Include a LICENCE
- 2.0.0
  - Drop support for Python 2.6, 2.7, 3.4, 3.5 and 3.6 (#68)
  - Add support for Python 3.7, 3.8, 3.9 and 3.10 (#68)
  - Avoid deprecation warnings and resource warnings (#71)
  - Add support for Python 3.11 and 3.12, drop dependency on six (#76)
- 1.0.2
  - Switch from travis to github actions
  - This will be the last release to support Python 2.6, 2.7 or 3.6
- 1.0.1
  - httpbin_secure: fix redirect Location to have "https://" scheme (#62) - thanks
    @immerrr
  - Include regression tests in pypi tarball (#56) - thanks @kmosiejczuk
- 1.0.0
  - Update included self-signed cert to include IP address in SAN (See #52). Full
    version bump because this could be a breaking change for those depending on the
    certificate missing the IP address in the SAN (as it seems the requests test suite
    does)
  - Only use @pytest.fixture decorator once (thanks @hroncok)
  - Fix a few README typos (thanks @hemberger)
- 0.3.0
  - Allow to run httpbin on fixed port using environment variables (thanks @hroncok)
  - Allow server to be thread.join()ed (thanks @graingert)
  - Add support for Python 3.6 (thanks @graingert)
- 0.2.3:
  - Another attempt to fix #32 (Rare bug, only happens on Travis)
- 0.2.2:
  - Fix bug with python3
- 0.2.1:
  - Attempt to fix strange, impossible-to-reproduce bug with broken SSL certs that only
    happens on Travis (#32) [Bad release, breaks py3]
- 0.2.0:
  - Remove threaded HTTP server. I built it for Requests, but they deleted their
    threaded test since it didn't really work very well. The threaded server seems to
    cause some strange problems with HTTP chunking, so I'll just remove it since nobody
    is using it (I hope)
- 0.1.1:
  - Fix weird hang with SSL on pypy (again)
- 0.1.0:
  - Update server to use multithreaded werkzeug server
- 0.0.7:
  - Update the certificates (they expired)
- 0.0.6:
  - Fix an issue where pypy was hanging when a request was made with an invalid
    certificate
- 0.0.5:
  - Fix broken version parsing in 0.0.4
- 0.0.4:
  - **Bad release: Broken version parsing**
  - Fix `BadStatusLine` error that occurs when sending multiple requests in a single
    session (PR #16). Thanks @msabramo!
  - Fix #9 ("Can't be installed at the same time than pytest?") (PR #14). Thanks
    @msabramo!
  - Add `httpbin_ca_bundle` pytest fixture. With this fixture there is no need to
    specify the bundle on every request, as it will automatically set
    `REQUESTS_CA_BUNDLE` if using [requests](https://docs.python-requests.org/). And you
    don't have to care about where it is located (PR #8). Thanks @t-8ch!
- 0.0.3: Add a couple test fixtures to make testing old class-based test suites easier
- 0.0.2: Fixed a couple bugs with the wsgiref server to bring behavior in line with
  httpbin.org, thanks @jakubroztocil for the bug reports
- 0.0.1: Initial release

## License

The MIT License (MIT)

Copyright (c) 2014-2019 Kevin McCarthy

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: pyproject.toml
================================================
[build-system]
requires = ["setuptools>=61.2"]
build-backend = "setuptools.build_meta"

[project]
name = "pytest-httpbin"
description = "Easily test your HTTP library against a local copy of httpbin"
authors = [{name = "Kevin McCarthy", email = "me@kevinmccarthy.org"}]
license = {text = "MIT"}
classifiers = [
    "Development Status :: 5 - Production/Stable",
    "Intended Audience :: Developers",
    "Topic :: Software Development :: Testing",
    "Topic :: Software Development :: Libraries",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3 :: Only",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
]
keywords = ["pytest-httpbin testing pytest httpbin"]
requires-python = ">=3.8"
dependencies = ["httpbin"]
dynamic = ["version"]

[project.readme]
file = "DESCRIPTION.rst"
content-type = "text/x-rst"

[project.urls]
Homepage = "https://github.com/kevin1024/pytest-httpbin"

[project.entry-points]
pytest11 = {httpbin = "pytest_httpbin.plugin"}

[project.optional-dependencies]
test = [
    "requests",
    "pytest",
]

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
exclude = [
    "contrib",
    "docs",
    "tests*",
]
namespaces = false

[tool.setuptools.dynamic]
version = {attr = "pytest_httpbin.version.__version__"}

[tool.pytest.ini_options]
addopts = "--strict-config --strict-markers"
filterwarnings = [
    "error",
    'ignore:ast\.(Str|NameConstant) is deprecated:DeprecationWarning:_pytest',
]
xfail_strict = true

[tool.tox]
legacy_tox_ini = """
    [tox]
    minversion=3.28.0
    requires=
        virtualenv>=20.13.2
        tox-gh-actions>=2.9.1
    envlist = py38, py39, py310, pypy3

    [testenv]
    package = wheel
    wheel_build_env = .pkg
    extras = test
    commands = pytest -v -s {posargs}
    install_command = python -I -m pip install --use-pep517 {opts} {packages}

    [testenv:release]
    deps =
        build
        twine
    commands =
        {envpython} -m release
        pyproject-build --sdist
        twine check {toxinidir}/dist/*.*
"""


================================================
FILE: pytest_httpbin/__init__.py
================================================
import os

import pytest

from .version import __version__

use_class_based_httpbin = pytest.mark.usefixtures("class_based_httpbin")
use_class_based_httpbin_secure = pytest.mark.usefixtures("class_based_httpbin_secure")


================================================
FILE: pytest_httpbin/certs/README.md
================================================
generated with 'python -m trustme'


================================================
FILE: pytest_httpbin/certs/client.pem
================================================
-----BEGIN CERTIFICATE-----
MIIB0TCCAXegAwIBAgIUScnyyX1CI+ywC6GdKol8IIwuGnkwCgYIKoZIzj0EAwIw
RDEbMBkGA1UECgwSdHJ1c3RtZSB2MS4xLjArZGV2MSUwIwYDVQQLDBxUZXN0aW5n
IENBICNBdXNVcWJaNG81d3pjb0tCMCAXDTAwMDEwMTAwMDAwMFoYDzMwMDAwMTAx
MDAwMDAwWjBEMRswGQYDVQQKDBJ0cnVzdG1lIHYxLjEuMCtkZXYxJTAjBgNVBAsM
HFRlc3RpbmcgQ0EgI0F1c1VxYlo0bzV3emNvS0IwWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAARhrRi78wmZY28t3/y8MTDDCsi7Lzir4WaQm96gf4/9kSolBTFVDUvB
MkSC7Yged+2bWEzTRERZQLf88uiorUnAo0UwQzAdBgNVHQ4EFgQUHymIBJV4gCrA
qv+6Q9pSJFtd7PYwEgYDVR0TAQH/BAgwBgEB/wIBCTAOBgNVHQ8BAf8EBAMCAYYw
CgYIKoZIzj0EAwIDSAAwRQIgLf0sybmdbJoTIgZWrU1k11oecQbdkzh+3jFtNEFn
zYUCIQCRXjIBDZXtyaywk3DgIggByCQxrrB5vjlnyYTd9vNUSw==
-----END CERTIFICATE-----


================================================
FILE: pytest_httpbin/certs/server.key
================================================
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIPNMu1H1DN9x0VLZNzO3BFp5boEGyc80XFaR1ML18uFRoAoGCCqGSM49
AwEHoUQDQgAEiNIfYxmsmjemcRRpcd4qP+x1yONFBZZli7CEKxg9j3x5j1OJPeyC
BQ83kogrxJYLbRjdHUx4VOCEXjffmYhnMA==
-----END EC PRIVATE KEY-----


================================================
FILE: pytest_httpbin/certs/server.pem
================================================
-----BEGIN CERTIFICATE-----
MIICTDCCAfOgAwIBAgIUZ9rBQX/YRZFcqXCIzOSAd1D0IUcwCgYIKoZIzj0EAwIw
RDEbMBkGA1UECgwSdHJ1c3RtZSB2MS4xLjArZGV2MSUwIwYDVQQLDBxUZXN0aW5n
IENBICNBdXNVcWJaNG81d3pjb0tCMCAXDTAwMDEwMTAwMDAwMFoYDzMwMDAwMTAx
MDAwMDAwWjBGMRswGQYDVQQKDBJ0cnVzdG1lIHYxLjEuMCtkZXYxJzAlBgNVBAsM
HlRlc3RpbmcgY2VydCAjLVdQNWpjLTllQ0U0S0JxMjBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABIjSH2MZrJo3pnEUaXHeKj/sdcjjRQWWZYuwhCsYPY98eY9TiT3s
ggUPN5KIK8SWC20Y3R1MeFTghF4335mIZzCjgb4wgbswHQYDVR0OBBYEFCO99Ega
h7pEyFEJVwe09DZzNHDtMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUHymIBJV4
gCrAqv+6Q9pSJFtd7PYwLwYDVR0RAQH/BCUwI4IJbG9jYWxob3N0hwR/AAABhxAA
AAAAAAAAAAAAAAAAAAABMA4GA1UdDwEB/wQEAwIFoDAqBgNVHSUBAf8EIDAeBggr
BgEFBQcDAgYIKwYBBQUHAwEGCCsGAQUFBwMDMAoGCCqGSM49BAMCA0cAMEQCIHB0
imdD2aQuq4DipTvnFJjmT+w8i3D/Pz8X6bPdkJW/AiATl+m4TW4BE5v1ID3ftDhz
ja8s574nAjDAqcSL7otVpQ==
-----END CERTIFICATE-----


================================================
FILE: pytest_httpbin/certs.py
================================================
"""
certs.py
~~~~~~~~

This module returns the preferred default CA certificate bundle.

If you are packaging pytest-httpbin, e.g., for a Linux distribution or a
managed environment, you can change the definition of where() to return a
separately packaged CA bundle.
"""

import os.path


def where():
    """Return the preferred certificate bundle."""
    # vendored bundle inside Requests
    return os.path.join(os.path.dirname(__file__), "certs", "client.pem")


if __name__ == "__main__":
    print(where())


================================================
FILE: pytest_httpbin/plugin.py
================================================
import pytest
from httpbin import app as httpbin_app

from . import certs, serve


@pytest.fixture(scope="session")
def httpbin(request):
    with serve.Server(application=httpbin_app) as server:
        yield server


@pytest.fixture(scope="session")
def httpbin_secure(request):
    with serve.SecureServer(application=httpbin_app) as server:
        yield server


@pytest.fixture(scope="session", params=["http", "https"])
def httpbin_both(request, httpbin, httpbin_secure):
    if request.param == "http":
        return httpbin
    elif request.param == "https":
        return httpbin_secure


@pytest.fixture(scope="class")
def class_based_httpbin(request, httpbin):
    request.cls.httpbin = httpbin


@pytest.fixture(scope="class")
def class_based_httpbin_secure(request, httpbin_secure):
    request.cls.httpbin_secure = httpbin_secure


@pytest.fixture(scope="function")
def httpbin_ca_bundle(monkeypatch):
    monkeypatch.setenv("REQUESTS_CA_BUNDLE", certs.where())


================================================
FILE: pytest_httpbin/serve.py
================================================
import os
import ssl
import threading
from urllib.parse import urljoin
from wsgiref.handlers import SimpleHandler
from wsgiref.simple_server import WSGIRequestHandler, WSGIServer, make_server

CERT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "certs")


class ServerHandler(SimpleHandler):
    server_software = "Pytest-HTTPBIN/0.1.0"
    http_version = "1.1"

    def cleanup_headers(self):
        SimpleHandler.cleanup_headers(self)
        self.headers["Connection"] = "Close"

    def close(self):
        try:
            self.request_handler.log_request(
                self.status.split(" ", 1)[0], self.bytes_sent
            )
        finally:
            SimpleHandler.close(self)


class Handler(WSGIRequestHandler):
    def handle(self):
        """Handle a single HTTP request"""

        self.raw_requestline = self.rfile.readline()
        if not self.parse_request():  # An error code has been sent, just exit
            return

        handler = ServerHandler(
            self.rfile, self.wfile, self.get_stderr(), self.get_environ()
        )
        handler.request_handler = self  # backpointer for logging
        handler.run(self.server.get_app())

    def get_environ(self):
        """
        wsgiref simple server adds content-type text/plain to everything, this
        removes it if it's not actually in the headers.
        """
        # Note: Can't use super since this is an oldstyle class in python 2.x
        environ = WSGIRequestHandler.get_environ(self).copy()
        if self.headers.get("content-type") is None:
            del environ["CONTENT_TYPE"]
        return environ


class SecureWSGIServer(WSGIServer):
    def get_request(self):
        socket, address = super().get_request()
        try:
            socket.settimeout(1.0)
            context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
            context.load_cert_chain(
                os.path.join(CERT_DIR, "server.pem"),
                os.path.join(CERT_DIR, "server.key"),
            )
            return (
                context.wrap_socket(
                    socket, server_side=True, suppress_ragged_eofs=True
                ),
                address,
            )
        except Exception as e:
            print("pytest-httpbin server hit an exception serving request: %s" % e)
            print("attempting to ignore so the rest of the tests can run")
            raise

    def setup_environ(self):
        super().setup_environ()
        self.base_environ["HTTPS"] = "yes"


class Server:
    """
    HTTP server running a WSGI application in its own thread.
    """

    port_envvar = "HTTPBIN_HTTP_PORT"

    def __init__(self, host="127.0.0.1", port=0, application=None, **kwargs):
        self.app = application
        if self.port_envvar in os.environ:
            port = int(os.environ[self.port_envvar])
        self._server = make_server(
            host, port, self.app, handler_class=Handler, **kwargs
        )
        self.host = self._server.server_address[0]
        self.port = self._server.server_address[1]
        self.protocol = "http"

        self._thread = threading.Thread(
            name=self.__class__,
            target=self._server.serve_forever,
        )

    def __del__(self):
        if hasattr(self, "_server"):
            self.stop()

    def start(self):
        self._thread.start()

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, *args, **kwargs):
        self.stop()
        suppress_exc = self._server.__exit__(*args, **kwargs)
        self._thread.join()
        return suppress_exc

    def __add__(self, other):
        return self.url + other

    def stop(self):
        self._server.shutdown()

    @property
    def url(self):
        return f"{self.protocol}://{self.host}:{self.port}"

    def join(self, url, allow_fragments=True):
        return urljoin(self.url, url, allow_fragments=allow_fragments)


class SecureServer(Server):
    port_envvar = "HTTPBIN_HTTPS_PORT"

    def __init__(self, host="127.0.0.1", port=0, application=None, **kwargs):
        kwargs["server_class"] = SecureWSGIServer
        super().__init__(host, port, application, **kwargs)
        self.protocol = "https"


================================================
FILE: pytest_httpbin/version.py
================================================
__version__ = "2.1.0"


================================================
FILE: release.py
================================================
import os
import pathlib
import shutil
import sys


def main():
    dist = pathlib.Path(__file__).parent / "dist"
    shutil.rmtree(dist, ignore_errors=True)
    dist.mkdir()
    shutil.copy(os.environ["TOX_PACKAGE"], dist)


if __name__ == "__main__":
    sys.exit(main())


================================================
FILE: runtests.sh
================================================
#!/bin/bash

py.test $1 -v -s


================================================
FILE: tests/conftest.py
================================================
import pytest


@pytest.fixture(autouse=True, scope="function")
def httpbin_ca_bundle_autoused(httpbin_ca_bundle):
    pass


================================================
FILE: tests/test_httpbin.py
================================================
import ssl
import sys
import unittest
import urllib.request

import pytest
import requests.exceptions
import urllib3

import pytest_httpbin.certs


def test_httpbin_gets_injected(httpbin):
    assert httpbin.url


def test_httpbin_accepts_get_requests(httpbin):
    assert requests.get(httpbin.url + "/get").status_code == 200


def test_httpbin_secure_accepts_get_requests(httpbin_secure):
    assert requests.get(httpbin_secure.url + "/get").status_code == 200


def test_httpbin_secure_accepts_lots_of_get_requests(httpbin_secure):
    for i in range(10):
        assert requests.get(httpbin_secure.url + "/get").status_code == 200


def test_httpbin_accepts_lots_of_get_requests_in_single_session(httpbin):
    session = requests.Session()

    for i in range(10):
        assert session.get(httpbin.url + "/get").status_code == 200


def test_httpbin_both(httpbin_both):
    # this test will get called twice, once with an http url, once with an
    # https url
    assert requests.get(httpbin_both.url + "/get").status_code == 200


def test_httpbin_join(httpbin):
    assert httpbin.join("foo") == httpbin.url + "/foo"


def test_httpbin_str(httpbin):
    assert httpbin + "/foo" == httpbin.url + "/foo"


def test_chunked_encoding(httpbin):
    assert requests.get(httpbin.url + "/stream/20").status_code == 200


@pytest.mark.xfail(
    condition=sys.version_info < (3, 8) and ssl.OPENSSL_VERSION_INFO >= (3, 0, 0),
    reason="fails on python3.7 openssl 3+",
    raises=requests.exceptions.SSLError,
)
def test_chunked_encoding_secure(httpbin_secure):
    assert requests.get(httpbin_secure.url + "/stream/20").status_code == 200


@pytest_httpbin.use_class_based_httpbin
@pytest_httpbin.use_class_based_httpbin_secure
class TestClassBassedTests(unittest.TestCase):
    def test_http(self):
        assert requests.get(self.httpbin.url + "/get").status_code == 200

    def test_http_secure(self):
        assert requests.get(self.httpbin_secure.url + "/get").status_code == 200


def test_with_urllib2(httpbin_secure):
    url = httpbin_secure.url + "/get"
    context = ssl.create_default_context(cafile=pytest_httpbin.certs.where())
    with urllib.request.urlopen(url, context=context) as response:
        assert response.getcode() == 200


def test_with_urllib3(httpbin_secure):
    with urllib3.PoolManager(
        cert_reqs="CERT_REQUIRED",
        ca_certs=pytest_httpbin.certs.where(),
    ) as pool:
        pool.request(
            "POST", httpbin_secure.url + "/post", {"key1": "value1", "key2": "value2"}
        )


================================================
FILE: tests/test_server.py
================================================
import contextlib
import os
import re
import socket

import pytest
import requests.exceptions
from httpbin import app as httpbin_app
from util import get_raw_http_response

from pytest_httpbin import serve


def test_content_type_header_not_automatically_added(httpbin):
    """
    The server was automatically adding this for some reason, see issue #5
    """
    resp = requests.get(httpbin + "/headers").json()["headers"]
    assert "Content-Type" not in resp


def test_unicode_data(httpbin):
    """
    UTF-8 was not getting recognized for what it was and being encoded as if it
    was binary, see issue #7
    """
    resp = requests.post(
        httpbin + "/post",
        data="оживлённым".encode(),
        headers={
            "content-type": "text/html; charset=utf-8",
        },
    )
    assert resp.json()["data"] == "оживлённым"


def test_server_should_be_http_1_1(httpbin):
    """
    The server should speak HTTP/1.1 since we live in the future, see issue #6
    """
    resp = get_raw_http_response(httpbin.host, httpbin.port, "/get")
    assert resp.startswith(b"HTTP/1.1")


def test_dont_crash_on_certificate_problems(httpbin_secure):
    with pytest.raises(requests.exceptions.SSLError):
        # this request used to hang
        requests.get(httpbin_secure + "/get", verify=True, cert=__file__)

    # and this request would never happen
    requests.get(
        httpbin_secure + "/get",
        verify=True,
    )


def test_dont_crash_on_handshake_timeout(httpbin_secure, capsys):
    with socket.socket() as sock:
        sock.connect((httpbin_secure.host, httpbin_secure.port))
        # this request used to hang
        assert sock.recv(1) == b""

    assert (
        re.match(
            r"pytest-httpbin server hit an exception serving request:.* The "
            "handshake operation timed out\nattempting to ignore so the rest "
            "of the tests can run\n",
            capsys.readouterr().out,
        )
        is not None
    )

    # and this request would never happen
    requests.get(
        httpbin_secure + "/get",
        verify=True,
    )


@pytest.mark.parametrize("protocol", ("http", "https"))
def test_fixed_port_environment_variables(protocol):
    """
    Note that we cannot test the fixture here because it is session scoped
    and was already started. Thus, let's just test a new Server instance.
    """
    if protocol == "http":
        server_cls = serve.Server
        envvar = "HTTPBIN_HTTP_PORT"
    elif protocol == "https":
        server_cls = serve.SecureServer
        envvar = "HTTPBIN_HTTPS_PORT"
    else:
        raise RuntimeError(f"Unexpected protocol param: {protocol}")

    # just have different port to avoid adrress already in use
    # if the second test run too fast after the first one (happens on pypy)
    port = 12345 + len(protocol)
    server = contextlib.nullcontext()

    try:
        envvar_original = os.environ.get(envvar, None)
        os.environ[envvar] = str(port)
        server = server_cls(application=httpbin_app)
        assert server.port == port
    finally:
        # if we don't do this, it blocks:
        with server:
            pass

        # restore the original environ:
        if envvar_original is None:
            del os.environ[envvar]
        else:
            os.environ[envvar] = envvar_original


def test_redirect_location_is_https_for_secure_server(httpbin_secure):
    assert httpbin_secure.url.startswith("https://")
    response = requests.get(
        httpbin_secure + "/redirect-to?url=/html", allow_redirects=False
    )
    assert response.status_code == 302
    assert response.headers.get("Location")
    assert response.headers["Location"] == "/html"


================================================
FILE: tests/util.py
================================================
import socket


def get_raw_http_response(host, port, path):
    CRLF = b"\r\n"

    request = [
        b"GET " + path.encode("ascii") + b" HTTP/1.1",
        b"Host: " + host.encode("ascii"),
        b"Connection: Close",
        b"",
        b"",
    ]

    # Connect to the server
    with socket.socket() as s:
        s.connect((host, port))

        # Send an HTTP request
        s.send(CRLF.join(request))

        # Get the response (in several parts, if necessary)
        response = b""
        buffer = s.recv(4096)
        while buffer:
            response += buffer
            buffer = s.recv(4096)

        return response
Download .txt
gitextract_mo63menp/

├── .flake8
├── .github/
│   └── workflows/
│       └── main.yaml
├── .gitignore
├── .pre-commit-config.yaml
├── DESCRIPTION.rst
├── LICENSE
├── MANIFEST.in
├── README.md
├── pyproject.toml
├── pytest_httpbin/
│   ├── __init__.py
│   ├── certs/
│   │   ├── README.md
│   │   ├── client.pem
│   │   ├── server.key
│   │   └── server.pem
│   ├── certs.py
│   ├── plugin.py
│   ├── serve.py
│   └── version.py
├── release.py
├── runtests.sh
└── tests/
    ├── conftest.py
    ├── test_httpbin.py
    ├── test_server.py
    └── util.py
Download .txt
SYMBOL INDEX (53 symbols across 8 files)

FILE: pytest_httpbin/certs.py
  function where (line 15) | def where():

FILE: pytest_httpbin/plugin.py
  function httpbin (line 8) | def httpbin(request):
  function httpbin_secure (line 14) | def httpbin_secure(request):
  function httpbin_both (line 20) | def httpbin_both(request, httpbin, httpbin_secure):
  function class_based_httpbin (line 28) | def class_based_httpbin(request, httpbin):
  function class_based_httpbin_secure (line 33) | def class_based_httpbin_secure(request, httpbin_secure):
  function httpbin_ca_bundle (line 38) | def httpbin_ca_bundle(monkeypatch):

FILE: pytest_httpbin/serve.py
  class ServerHandler (line 11) | class ServerHandler(SimpleHandler):
    method cleanup_headers (line 15) | def cleanup_headers(self):
    method close (line 19) | def close(self):
  class Handler (line 28) | class Handler(WSGIRequestHandler):
    method handle (line 29) | def handle(self):
    method get_environ (line 42) | def get_environ(self):
  class SecureWSGIServer (line 54) | class SecureWSGIServer(WSGIServer):
    method get_request (line 55) | def get_request(self):
    method setup_environ (line 75) | def setup_environ(self):
  class Server (line 80) | class Server:
    method __init__ (line 87) | def __init__(self, host="127.0.0.1", port=0, application=None, **kwargs):
    method __del__ (line 103) | def __del__(self):
    method start (line 107) | def start(self):
    method __enter__ (line 110) | def __enter__(self):
    method __exit__ (line 114) | def __exit__(self, *args, **kwargs):
    method __add__ (line 120) | def __add__(self, other):
    method stop (line 123) | def stop(self):
    method url (line 127) | def url(self):
    method join (line 130) | def join(self, url, allow_fragments=True):
  class SecureServer (line 134) | class SecureServer(Server):
    method __init__ (line 137) | def __init__(self, host="127.0.0.1", port=0, application=None, **kwargs):

FILE: release.py
  function main (line 7) | def main():

FILE: tests/conftest.py
  function httpbin_ca_bundle_autoused (line 5) | def httpbin_ca_bundle_autoused(httpbin_ca_bundle):

FILE: tests/test_httpbin.py
  function test_httpbin_gets_injected (line 13) | def test_httpbin_gets_injected(httpbin):
  function test_httpbin_accepts_get_requests (line 17) | def test_httpbin_accepts_get_requests(httpbin):
  function test_httpbin_secure_accepts_get_requests (line 21) | def test_httpbin_secure_accepts_get_requests(httpbin_secure):
  function test_httpbin_secure_accepts_lots_of_get_requests (line 25) | def test_httpbin_secure_accepts_lots_of_get_requests(httpbin_secure):
  function test_httpbin_accepts_lots_of_get_requests_in_single_session (line 30) | def test_httpbin_accepts_lots_of_get_requests_in_single_session(httpbin):
  function test_httpbin_both (line 37) | def test_httpbin_both(httpbin_both):
  function test_httpbin_join (line 43) | def test_httpbin_join(httpbin):
  function test_httpbin_str (line 47) | def test_httpbin_str(httpbin):
  function test_chunked_encoding (line 51) | def test_chunked_encoding(httpbin):
  function test_chunked_encoding_secure (line 60) | def test_chunked_encoding_secure(httpbin_secure):
  class TestClassBassedTests (line 66) | class TestClassBassedTests(unittest.TestCase):
    method test_http (line 67) | def test_http(self):
    method test_http_secure (line 70) | def test_http_secure(self):
  function test_with_urllib2 (line 74) | def test_with_urllib2(httpbin_secure):
  function test_with_urllib3 (line 81) | def test_with_urllib3(httpbin_secure):

FILE: tests/test_server.py
  function test_content_type_header_not_automatically_added (line 14) | def test_content_type_header_not_automatically_added(httpbin):
  function test_unicode_data (line 22) | def test_unicode_data(httpbin):
  function test_server_should_be_http_1_1 (line 37) | def test_server_should_be_http_1_1(httpbin):
  function test_dont_crash_on_certificate_problems (line 45) | def test_dont_crash_on_certificate_problems(httpbin_secure):
  function test_dont_crash_on_handshake_timeout (line 57) | def test_dont_crash_on_handshake_timeout(httpbin_secure, capsys):
  function test_fixed_port_environment_variables (line 81) | def test_fixed_port_environment_variables(protocol):
  function test_redirect_location_is_https_for_secure_server (line 117) | def test_redirect_location_is_https_for_secure_server(httpbin_secure):

FILE: tests/util.py
  function get_raw_http_response (line 4) | def get_raw_http_response(host, port, path):
Condensed preview — 24 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (36K chars).
[
  {
    "path": ".flake8",
    "chars": 75,
    "preview": "[flake8]\ndisable-noqa = True\nmax-line-length = 88\nextend-ignore =\n    E203\n"
  },
  {
    "path": ".github/workflows/main.yaml",
    "chars": 1763,
    "preview": "name: CI\non:\n  push:\n    branches:\n      - master\n    tags:\n      - v*\n  pull_request:\n\njobs:\n  tox:\n    runs-on: ${{ ma"
  },
  {
    "path": ".gitignore",
    "chars": 449,
    "preview": "# Backup files\n*.~\n*.swp\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distrib"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 1257,
    "preview": "repos:\n  - repo: https://github.com/asottile/pyupgrade\n    rev: v3.4.0\n    hooks:\n      - id: pyupgrade\n        args: ['"
  },
  {
    "path": "DESCRIPTION.rst",
    "chars": 1170,
    "preview": "pytest-httpbin\n==============\n\nhttpbin is an amazing web service for testing HTTP libraries. It has several\ngreat endpoi"
  },
  {
    "path": "LICENSE",
    "chars": 1086,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014-2023 Kevin McCarthy\n\nPermission is hereby granted, free of charge, to any pers"
  },
  {
    "path": "MANIFEST.in",
    "chars": 198,
    "preview": "# If using Python 2.6 or less, then have to include package data, even though\n# it's already declared in setup.py\ninclud"
  },
  {
    "path": "README.md",
    "chars": 9889,
    "preview": "# pytest-httpbin\n\n[![Build Status](https://github.com/kevin1024/pytest-httpbin/actions/workflows/main.yaml/badge.svg)](h"
  },
  {
    "path": "pyproject.toml",
    "chars": 2239,
    "preview": "[build-system]\nrequires = [\"setuptools>=61.2\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"pytest-httpbin"
  },
  {
    "path": "pytest_httpbin/__init__.py",
    "chars": 220,
    "preview": "import os\n\nimport pytest\n\nfrom .version import __version__\n\nuse_class_based_httpbin = pytest.mark.usefixtures(\"class_bas"
  },
  {
    "path": "pytest_httpbin/certs/README.md",
    "chars": 35,
    "preview": "generated with 'python -m trustme'\n"
  },
  {
    "path": "pytest_httpbin/certs/client.pem",
    "chars": 692,
    "preview": "-----BEGIN CERTIFICATE-----\nMIIB0TCCAXegAwIBAgIUScnyyX1CI+ywC6GdKol8IIwuGnkwCgYIKoZIzj0EAwIw\nRDEbMBkGA1UECgwSdHJ1c3RtZSB"
  },
  {
    "path": "pytest_httpbin/certs/server.key",
    "chars": 227,
    "preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIPNMu1H1DN9x0VLZNzO3BFp5boEGyc80XFaR1ML18uFRoAoGCCqGSM49\nAwEHoUQDQgAEiNIfYxmsmjem"
  },
  {
    "path": "pytest_httpbin/certs/server.pem",
    "chars": 859,
    "preview": "-----BEGIN CERTIFICATE-----\nMIICTDCCAfOgAwIBAgIUZ9rBQX/YRZFcqXCIzOSAd1D0IUcwCgYIKoZIzj0EAwIw\nRDEbMBkGA1UECgwSdHJ1c3RtZSB"
  },
  {
    "path": "pytest_httpbin/certs.py",
    "chars": 513,
    "preview": "\"\"\"\ncerts.py\n~~~~~~~~\n\nThis module returns the preferred default CA certificate bundle.\n\nIf you are packaging pytest-htt"
  },
  {
    "path": "pytest_httpbin/plugin.py",
    "chars": 979,
    "preview": "import pytest\nfrom httpbin import app as httpbin_app\n\nfrom . import certs, serve\n\n\n@pytest.fixture(scope=\"session\")\ndef "
  },
  {
    "path": "pytest_httpbin/serve.py",
    "chars": 4236,
    "preview": "import os\nimport ssl\nimport threading\nfrom urllib.parse import urljoin\nfrom wsgiref.handlers import SimpleHandler\nfrom w"
  },
  {
    "path": "pytest_httpbin/version.py",
    "chars": 22,
    "preview": "__version__ = \"2.1.0\"\n"
  },
  {
    "path": "release.py",
    "chars": 274,
    "preview": "import os\nimport pathlib\nimport shutil\nimport sys\n\n\ndef main():\n    dist = pathlib.Path(__file__).parent / \"dist\"\n    sh"
  },
  {
    "path": "runtests.sh",
    "chars": 30,
    "preview": "#!/bin/bash\n\npy.test $1 -v -s\n"
  },
  {
    "path": "tests/conftest.py",
    "chars": 124,
    "preview": "import pytest\n\n\n@pytest.fixture(autouse=True, scope=\"function\")\ndef httpbin_ca_bundle_autoused(httpbin_ca_bundle):\n    p"
  },
  {
    "path": "tests/test_httpbin.py",
    "chars": 2541,
    "preview": "import ssl\nimport sys\nimport unittest\nimport urllib.request\n\nimport pytest\nimport requests.exceptions\nimport urllib3\n\nim"
  },
  {
    "path": "tests/test_server.py",
    "chars": 3708,
    "preview": "import contextlib\nimport os\nimport re\nimport socket\n\nimport pytest\nimport requests.exceptions\nfrom httpbin import app as"
  },
  {
    "path": "tests/util.py",
    "chars": 641,
    "preview": "import socket\n\n\ndef get_raw_http_response(host, port, path):\n    CRLF = b\"\\r\\n\"\n\n    request = [\n        b\"GET \" + path."
  }
]

About this extraction

This page contains the full source code of the kevin1024/pytest-httpbin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 24 files (32.4 KB), approximately 9.5k tokens, and a symbol index with 53 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!