Skip to content
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
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ jobs:
uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v1
with:
submodules: false
toxdeps: "'tox<4'"
envs: |
- linux: codestyle
- windows: py38-test-cli
Expand Down
25 changes: 23 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = 'setuptools.build_meta'
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"

[project]
name = "tox-pypi-filter"
authors = [
{name = "Thomas Robitaille", email = "thomas.robitaille@gmail.com"},
{name = "Stuart Mumford", email = "stuart@cadair.com"},
]
description = "Implement a --pypi-filter option for tox"
readme = "README.rst"
requires-python = ">=3.8"
license = {text = "BSD-2-Clause"}
dependencies = [
"tox>=4.0.9",
"pypicky>=0.5"
]
dynamic = ["version"]

[project.entry-points."tox"]
pypi-filter = "tox_pypi_filter.plugin"

[tool.setuptools_scm]
29 changes: 0 additions & 29 deletions setup.cfg

This file was deleted.

2 changes: 0 additions & 2 deletions setup.py

This file was deleted.

88 changes: 50 additions & 38 deletions tox_pypi_filter/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@
import urllib.parse
import urllib.request
from textwrap import indent
from typing import Any

from tox.plugin import impl
from tox.tox_env.api import ToxEnv
from tox.config.cli.parser import ToxParser
from tox.config.sets import EnvConfigSet
from tox.session.state import State

import pluggy
from pkg_resources import DistributionNotFound, get_distribution

try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
pass

hookimpl = pluggy.HookimplMarker("tox")

HELP = ("Specify version constraints for packages which are then applied by "
"setting up a proxy PyPI server. If giving multiple constraints, you "
Expand All @@ -25,24 +30,31 @@
"http://, https://, or ftp://).")


@hookimpl
def tox_addoption(parser):
parser.add_argument('--pypi-filter', dest='pypi_filter', help=HELP)
parser.add_testenv_attribute('pypi_filter', 'string', help=HELP)
@impl
def tox_add_option(parser: ToxParser) -> None:
parser.add_argument('--pypi-filter', action="store", type=str, of_type=str)


SERVER_PROCESS = {}
SERVER_URLS = {}


@impl
def tox_add_env_config(env_conf: EnvConfigSet, state: State) -> None:
env_conf.add_config('pypi_filter', default=None, desc=HELP, of_type=str)


@hookimpl
def tox_testenv_create(venv, action):
# Skip the environment used for creating the tarball
if venv.name == ".package":
@impl
def tox_on_install(tox_env: ToxEnv, arguments: Any, section: str, of_type: str) -> None:
# Do not set the environment variable for the custom index if we are in the .pkg step
if tox_env.name == ".pkg":
return

global SERVER_PROCESS
global SERVER_PROCESS, SERVER_URLS

pypi_filter = venv.envconfig.config.option.pypi_filter or venv.envconfig.pypi_filter
pypi_filter_config = tox_env.conf.load("pypi_filter")
pypi_filter_cli = tox_env.options.pypi_filter
pypi_filter = pypi_filter_cli or pypi_filter_config

if not pypi_filter:
return
Expand All @@ -61,44 +73,44 @@ def tox_testenv_create(venv, action):

# If we get a blank set of requirements then we don't do anything.
with open(reqfile, "r") as fobj:
contents = fobj.read()
contents = fobj.readlines()
contents = list(filter(lambda line: not line.startswith("#"), contents))
if not contents:
return
contents = "\n".join(contents)

# Find available port
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 0))
port = sock.getsockname()[1]
sock.close()
# We might have already setup the process for this env at an earlier call of this function.
if tox_env.name not in SERVER_PROCESS:
# Find available port
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 0))
port = sock.getsockname()[1]
sock.close()

# Run pypicky
print(f"{venv.name}: Starting tox-pypi-filter server with the following requirements:")
print(indent(contents.strip(), ' '))
# Run pypicky
print(f"{tox_env.name}: Starting tox-pypi-filter server with the following requirements:")
print(indent(contents.strip(), ' '))

SERVER_PROCESS[venv.name] = subprocess.Popen([sys.executable, '-m', 'pypicky',
reqfile, '--port', str(port), '--quiet'])
SERVER_URLS[tox_env.name] = f'http://localhost:{port}'
SERVER_PROCESS[tox_env.name] = subprocess.Popen([sys.executable, '-m', 'pypicky',
reqfile, '--port', str(port), '--quiet'])

# FIXME: properly check that the server has started up
time.sleep(2)

venv.envconfig.config.indexserver['default'].url = f'http://localhost:{port}'
# If PIP_INDEX_URL is configured in config it will conflict
if "PIP_INDEX_URL" in tox_env.conf.load("setenv"):
raise ValueError("Can not use tox-pypi-filter if already setting the PIP_INDEX_URL env var.")

# Add the index url to the env vars just for this install (as oppsed to the global config)
tox_env.environment_variables["PIP_INDEX_URL"] = SERVER_URLS[tox_env.name]


@hookimpl
def tox_runtest_post(venv):
@impl
def tox_env_teardown(tox_env):
global SERVER_PROCESS

proc = SERVER_PROCESS.pop(venv.name, None)
proc = SERVER_PROCESS.pop(tox_env.name, None)
if proc:
print(f"{venv.name}: Shutting down tox-pypi-filter server")
print(f"{tox_env.name}: Shutting down tox-pypi-filter server")
proc.terminate()


@hookimpl
def tox_cleanup(session):
global SERVER_PROCESS

for venv, process in SERVER_PROCESS.items():
print(f"{venv}: Shutting down tox-pypi-filter server.")
process.terminate()
SERVER_PROCESS = {}