Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lint: apply black #103

Merged
merged 1 commit into from
Dec 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
[flake8]
max-line-length = 160
; https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#flake8
max-line-length = 88
extend-ignore = E203
4 changes: 4 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ repos:
hooks:
- id: isort
name: isort (python)
- repo: https://github.com/psf/black
rev: 21.12b0
hooks:
- id: black
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/miketheman/pytest-socket/main.svg)](https://results.pre-commit.ci/latest/github/miketheman/pytest-socket/main)
[![Maintainability](https://api.codeclimate.com/v1/badges/1608a75b1c3a20211992/maintainability)](https://codeclimate.com/github/miketheman/pytest-socket/maintainability)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmiketheman%2Fpytest-socket.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmiketheman%2Fpytest-socket?ref=badge_shield)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

A plugin to use with Pytest to disable or restrict `socket` calls during
tests to ensure network calls are prevented.
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ socket = 'pytest_socket'

[tool.isort]
known_first_party = ['pytest_socket', 'conftest', 'test_socket']
# https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#profilegcm
profile = "black"

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
81 changes: 45 additions & 36 deletions pytest_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,45 @@ def __init__(self, *args, **kwargs):
class SocketConnectBlockedError(RuntimeError):
def __init__(self, allowed, host, *args, **kwargs):
if allowed:
allowed = ','.join(allowed)
allowed = ",".join(allowed)
super(SocketConnectBlockedError, self).__init__(
f'A test tried to use socket.socket.connect() with host "{host}" (allowed: "{allowed}").'
"A test tried to use socket.socket.connect() "
f'with host "{host}" (allowed: "{allowed}").'
)


def pytest_addoption(parser):
group = parser.getgroup('socket')
group = parser.getgroup("socket")
group.addoption(
'--disable-socket',
action='store_true',
dest='disable_socket',
help='Disable socket.socket by default to block network calls.'
"--disable-socket",
action="store_true",
dest="disable_socket",
help="Disable socket.socket by default to block network calls.",
)
group.addoption(
'--allow-hosts',
dest='allow_hosts',
metavar='ALLOWED_HOSTS_CSV',
help='Only allow specified hosts through socket.socket.connect((host, port)).'
"--allow-hosts",
dest="allow_hosts",
metavar="ALLOWED_HOSTS_CSV",
help="Only allow specified hosts through socket.socket.connect((host, port)).",
)
group.addoption(
'--allow-unix-socket',
action='store_true',
dest='allow_unix_socket',
help='Allow calls if they are to Unix domain sockets'
"--allow-unix-socket",
action="store_true",
dest="allow_unix_socket",
help="Allow calls if they are to Unix domain sockets",
)


@pytest.fixture
def socket_disabled(pytestconfig):
""" disable socket.socket for duration of this test function """
"""disable socket.socket for duration of this test function"""
disable_socket(allow_unix_socket=pytestconfig.__socket_allow_unix_socket)
yield


@pytest.fixture
def socket_enabled(pytestconfig):
""" enable socket.socket for duration of this test function """
"""enable socket.socket for duration of this test function"""
enable_socket()
yield

Expand All @@ -67,11 +68,11 @@ def _is_unix_socket(family) -> bool:


def disable_socket(allow_unix_socket=False):
""" disable socket.socket to disable the Internet. useful in testing.
"""
"""disable socket.socket to disable the Internet. useful in testing."""

class GuardedSocket(socket.socket):
""" socket guard to disable socket creation (from pytest-socket) """
"""socket guard to disable socket creation (from pytest-socket)"""

def __new__(cls, family=-1, type=-1, proto=-1, fileno=None):
if _is_unix_socket(family) and allow_unix_socket:
return super().__new__(cls, family, type, proto, fileno)
Expand All @@ -82,20 +83,26 @@ def __new__(cls, family=-1, type=-1, proto=-1, fileno=None):


def enable_socket():
""" re-enable socket.socket to enable the Internet. useful in testing.
"""
"""re-enable socket.socket to enable the Internet. useful in testing."""
socket.socket = _true_socket


def pytest_configure(config):
config.addinivalue_line("markers", "disable_socket(): Disable socket connections for a specific test")
config.addinivalue_line("markers", "enable_socket(): Enable socket connections for a specific test")
config.addinivalue_line("markers", "allow_hosts([hosts]): Restrict socket connection to defined list of hosts")
config.addinivalue_line(
"markers", "disable_socket(): Disable socket connections for a specific test"
)
config.addinivalue_line(
"markers", "enable_socket(): Enable socket connections for a specific test"
)
config.addinivalue_line(
"markers",
"allow_hosts([hosts]): Restrict socket connection to defined list of hosts",
)

# Store the global configs in the `pytest.Config` object.
config.__socket_disabled = config.getoption('--disable-socket')
config.__socket_allow_unix_socket = config.getoption('--allow-unix-socket')
config.__socket_allow_hosts = config.getoption('--allow-hosts')
config.__socket_disabled = config.getoption("--disable-socket")
config.__socket_allow_unix_socket = config.getoption("--allow-unix-socket")
config.__socket_allow_hosts = config.getoption("--allow-hosts")


def pytest_runtest_setup(item) -> None:
Expand All @@ -108,12 +115,16 @@ def pytest_runtest_setup(item) -> None:
"""

# If test has the `enable_socket` marker, we accept this as most explicit.
if 'socket_enabled' in item.fixturenames or item.get_closest_marker('enable_socket'):
if "socket_enabled" in item.fixturenames or item.get_closest_marker(
"enable_socket"
):
enable_socket()
return

# If the test has the `disable_socket` marker, it's explicitly disabled.
if 'socket_disabled' in item.fixturenames or item.get_closest_marker('disable_socket'):
if "socket_disabled" in item.fixturenames or item.get_closest_marker(
"disable_socket"
):
disable_socket(item.config.__socket_allow_unix_socket)
return

Expand All @@ -127,7 +138,7 @@ def pytest_runtest_setup(item) -> None:

def _resolve_allow_hosts(item):
"""Resolve `allow_hosts` behaviors."""
mark_restrictions = item.get_closest_marker('allow_hosts')
mark_restrictions = item.get_closest_marker("allow_hosts")
cli_restrictions = item.config.__socket_allow_hosts
hosts = None
if mark_restrictions:
Expand Down Expand Up @@ -156,10 +167,9 @@ def host_from_connect_args(args):


def socket_allow_hosts(allowed=None, allow_unix_socket=False):
""" disable socket.socket.connect() to disable the Internet. useful in testing.
"""
"""disable socket.socket.connect() to disable the Internet. useful in testing."""
if isinstance(allowed, str):
allowed = allowed.split(',')
allowed = allowed.split(",")
if not isinstance(allowed, list):
return

Expand All @@ -174,7 +184,6 @@ def guarded_connect(inst, *args):


def _remove_restrictions():
""" restore socket.socket.* to allow access to the Internet. useful in testing.
"""
"""restore socket.socket.* to allow access to the Internet. useful in testing."""
socket.socket = _true_socket
socket.socket.connect = _true_connect
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

pytest_plugins = 'pytester'
pytest_plugins = "pytester"


unix_sockets_only = pytest.mark.skipif(
Expand Down
18 changes: 12 additions & 6 deletions tests/test_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

@unix_sockets_only
def test_asynctest(testdir):
testdir.makepyfile("""
testdir.makepyfile(
"""
import socket
import asynctest

Expand All @@ -18,14 +19,16 @@ async def test_that_a_coroutine_runs(self):

async def test_inet_is_blocked(self):
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
""")
"""
)
result = testdir.runpytest("--disable-socket", "--allow-unix-socket")
result.assert_outcomes(passed=1, skipped=0, failed=1)


@unix_sockets_only
def test_starlette(testdir):
testdir.makepyfile("""
testdir.makepyfile(
"""
from pytest_socket import disable_socket
from starlette.responses import HTMLResponse
from starlette.testclient import TestClient
Expand All @@ -45,14 +48,16 @@ def test_app():
client = TestClient(app)
response = client.get('/')
assert response.status_code == 200
""")
"""
)
result = testdir.runpytest("--disable-socket", "--allow-unix-socket")
result.assert_outcomes(passed=1, skipped=0, failed=0)


@unix_sockets_only
def test_httpx_fails(testdir):
testdir.makepyfile("""
testdir.makepyfile(
"""
import pytest
import httpx

Expand All @@ -64,6 +69,7 @@ def anyio_backend():
async def test_httpx():
async with httpx.AsyncClient() as client:
await client.get("http://www.example.com/")
""")
"""
)
result = testdir.runpytest("--disable-socket", "--allow-unix-socket")
assert_socket_blocked(result)
7 changes: 5 additions & 2 deletions tests/test_combinations.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ def test_remote_not_allowed_fails():
result = testdir.runpytest()
result.assert_outcomes(passed=4, failed=1)
result.stdout.fnmatch_lines(
"*SocketConnectBlockedError: A test tried to use socket.socket.connect() with host*"
"*SocketConnectBlockedError: "
"A test tried to use socket.socket.connect() with host*"
)


Expand Down Expand Up @@ -72,5 +73,7 @@ def test_inet_connect():
sock.connect(('{httpbin.host}', {httpbin.port}))
"""
)
result = testdir.runpytest("--disable-socket", "--allow-unix-socket", f"--allow-hosts={httpbin.host}")
result = testdir.runpytest(
"--disable-socket", "--allow-unix-socket", f"--allow-hosts={httpbin.host}"
)
result.assert_outcomes(passed=2)
Loading