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

add type annotations and rework some None/unset/default cases #3201

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft
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
13 changes: 12 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
toxenv: [lint, docs-lint, pycodestyle]
toxenv: [lint, docs-lint, pycodestyle, format, mypy]
python-version: [ "3.10" ]
steps:
- uses: actions/checkout@v4
Expand All @@ -26,3 +26,14 @@ jobs:
python -m pip install --upgrade pip
python -m pip install tox
- run: tox -e ${{ matrix.toxenv }}
- name: "Lint support files"
if: ${{ ! matrix.toxenv }}
# github UI shows a green checkmark instead of the original outcome :(
# should probably be called "force-conclusion: success"
continue-on-error: true
env:
# annoying results for pl_PL and fr_FR.. still the only *obvious* choice
LC_ALL: C
run: |
tail +6 THANKS | sort --ignore-case --check
sort --check .gitignore
8 changes: 6 additions & 2 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest] # All OSes pass except Windows because tests need Unix-only fcntl, grp, pwd, etc.
os: [ubuntu-latest, macos-13] # All OSes pass except Windows because tests need Unix-only fcntl, grp, pwd, etc.
python-version:
# CPython <= 3.7 is EoL since 2023-06-27
- "3.7"
Expand All @@ -33,7 +33,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: requirements_test.txt
cache-dependency-path: pyproject.toml
check-latest: true
- name: Install Dependencies
run: |
Expand All @@ -42,3 +42,7 @@ jobs:
- run: tox -e run-module
- run: tox -e run-entrypoint
- run: tox -e py
- name: Install dist dependencies
run: python -m pip install build
- name: build dist
run: python -m build
2 changes: 0 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ include LICENSE
include NOTICE
include README.rst
include THANKS
include requirements_dev.txt
include requirements_test.txt
recursive-include tests *
recursive-include examples *
recursive-include docs *
Expand Down
13 changes: 6 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
build:
virtualenv venv
venv/bin/pip install -e .
venv/bin/pip install -r requirements_dev.txt
venv/bin/pip install -e .[dev,testing]

test:
venv/bin/python setup.py test
venv/bin/python -m pytest

coverage:
venv/bin/python setup.py test --cov
venv/bin/python -m converage run --source=gunicorn -m pytest
venv/bin/python -m converage xml

clean:
@rm -rf .Python MANIFEST build dist venv* *.egg-info *.egg
@find . -type f -name "*.py[co]" -delete
@find . -type d -name "__pycache__" -delete
# unlike rm -rf, git-clean -X will only delete files ignored by git
@git clean -X -f -- .Python MANIFEST build dist "venv*" "*.egg-info" "*.egg" __pycache__

.PHONY: build clean coverage test
6 changes: 6 additions & 0 deletions gunicorn/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from _typeshed import Incomplete

version_info: tuple[int, int, int]
__version__: str
SERVER: str
SERVER_SOFTWARE: str
1 change: 1 addition & 0 deletions gunicorn/__main__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from gunicorn.app.wsgiapp import run as run
Empty file added gunicorn/app/__init__.pyi
Empty file.
8 changes: 6 additions & 2 deletions gunicorn/app/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,12 @@ def get_config_from_filename(self, filename):
if ext in [".py", ".pyc"]:
spec = importlib.util.spec_from_file_location(module_name, filename)
else:
msg = "configuration file should have a valid Python extension.\n"
util.warn(msg)
if filename == getattr(os, "devnull", "/dev/null"):
# unambiguous and generally deliberate. no need to warn in this case.
pass
else:
msg = "configuration file should have a valid Python extension.\n"
util.warn(msg)
loader_ = importlib.machinery.SourceFileLoader(module_name, filename)
spec = importlib.util.spec_from_file_location(module_name, filename, loader=loader_)
mod = importlib.util.module_from_spec(spec)
Expand Down
39 changes: 39 additions & 0 deletions gunicorn/app/base.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from argparse import ArgumentParser, Namespace

from _typeshed import Incomplete
from _typeshed.wsgi import WSGIApplication as _WSGIApplication

from gunicorn import debug as debug
from gunicorn import util as util
from gunicorn.arbiter import Arbiter as Arbiter
from gunicorn.config import Config
from gunicorn.config import get_default_config_file as get_default_config_file

# from abc import abstractmethod, ABCMeta

class BaseApplication:
usage: str | None
cfg: Config
callable: None | _WSGIApplication
prog: str | None
logger: Incomplete
def __init__(self, usage: str | None = ..., prog: str | None = ...) -> None: ...
def do_load_config(self) -> None: ...
def load_default_config(self) -> None: ...
def init(
self, parser: ArgumentParser, opts: Namespace, args: list[str]
) -> None: ...
def load(self) -> _WSGIApplication: ...
def load_config(self) -> None: ...
def reload(self) -> None: ...
def wsgi(self) -> _WSGIApplication: ...
def run(self) -> None: ...

class Application(BaseApplication):
def chdir(self) -> None: ...
def get_config_from_filename(self, filename: str) -> Config: ...
def get_config_from_module_name(self, module_name: str) -> Config: ...
def load_config_from_module_name_or_filename(self, location: str) -> Config: ...
def load_config_from_file(self, filename: str) -> Config: ...
def load_config(self) -> None: ...
def run(self) -> None: ...
13 changes: 13 additions & 0 deletions gunicorn/app/pasterapp.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from _typeshed import Incomplete
from _typeshed.wsgi import WSGIApplication as _WSGIApplication

from gunicorn.app.wsgiapp import WSGIApplication as WSGIApplication
from gunicorn.config import get_default_config_file as get_default_config_file

def get_wsgi_app(
config_uri: str, name: Incomplete | None = ..., defaults: Incomplete | None = ...
) -> _WSGIApplication: ...
def has_logging_config(config_file: str) -> bool: ...
def serve(
app: _WSGIApplication, global_conf: dict[str, Incomplete], **local_conf: Incomplete
) -> None: ...
20 changes: 20 additions & 0 deletions gunicorn/app/wsgiapp.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from argparse import ArgumentParser, Namespace

from _typeshed import Incomplete
from _typeshed.wsgi import WSGIApplication as _WSGIApplication

from gunicorn import util as util
from gunicorn.app.base import Application as Application
from gunicorn.errors import ConfigError as ConfigError

class WSGIApplication(Application):
app_uri: Incomplete
def init(
self, parser: ArgumentParser, opts: Namespace, args: list[str]
) -> None: ...
def load_config(self) -> None: ...
def load_wsgiapp(self) -> _WSGIApplication: ...
def load_pasteapp(self) -> _WSGIApplication: ...
def load(self) -> _WSGIApplication: ...

def run(prog: str | None = ...) -> None: ...
5 changes: 1 addition & 4 deletions gunicorn/arbiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,7 @@ def init_signals(self):
os.close(p)

# initialize the pipe
self.PIPE = pair = os.pipe()
for p in pair:
util.set_non_blocking(p)
util.close_on_exec(p)
self.PIPE = util.pipe2()

self.log.close_on_exec()

Expand Down
73 changes: 73 additions & 0 deletions gunicorn/arbiter.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from signal import Signals
from types import FrameType
from typing import Any, Dict, List, Tuple

from _typeshed import Incomplete

from gunicorn import SERVER_SOFTWARE as SERVER_SOFTWARE
from gunicorn import __version__ as __version__
from gunicorn import sock as sock
from gunicorn import systemd as systemd
from gunicorn import util as util
from gunicorn.app.base import BaseApplication
from gunicorn.errors import AppImportError as AppImportError
from gunicorn.errors import HaltServer as HaltServer
from gunicorn.pidfile import Pidfile as Pidfile
from gunicorn.sock import BaseSocket as BaseSocket
from gunicorn.workers.base import Worker as Worker

class Arbiter:
WORKER_BOOT_ERROR: int
APP_LOAD_ERROR: int
START_CTX: dict[Any, Any]
LISTENERS: list[Any]
WORKERS: dict[int, Worker]
PIPE: list[tuple[BaseSocket, BaseSocket]]
SIG_QUEUE: list[Any]
SIGNALS: Incomplete
SIG_NAMES: Incomplete
log: Incomplete
pidfile: Incomplete
systemd: bool
worker_age: int
reexec_pid: int
master_pid: int
master_name: str
def __init__(self, app: BaseApplication) -> None: ...
num_workers: Incomplete
app: BaseApplication
cfg: Incomplete
worker_class: Incomplete
address: Incomplete
timeout: Incomplete
proc_name: Incomplete
def setup(self, app: BaseApplication) -> None: ...
pid: int
def start(self) -> None: ...
def init_signals(self) -> None: ...
def signal(self, sig: Signals, frame: FrameType | None) -> None: ...
def run(self) -> None: ...
def handle_chld(self, sig: Signals, frame: FrameType | None) -> None: ...
def handle_hup(self) -> None: ...
def handle_term(self) -> None: ...
def handle_int(self) -> None: ...
def handle_quit(self) -> None: ...
def handle_ttin(self) -> None: ...
def handle_ttou(self) -> None: ...
def handle_usr1(self) -> None: ...
def handle_usr2(self) -> None: ...
def handle_winch(self) -> None: ...
def maybe_promote_master(self) -> None: ...
def wakeup(self) -> None: ...
def halt(self, reason: Incomplete | None = ..., exit_status: int = ...) -> None: ...
def sleep(self) -> None: ...
def stop(self, graceful: bool = ...) -> None: ...
def reexec(self) -> None: ...
def reload(self) -> None: ...
def murder_workers(self) -> None: ...
def reap_workers(self) -> None: ...
def manage_workers(self) -> None: ...
def spawn_worker(self) -> None: ...
def spawn_workers(self) -> None: ...
def kill_workers(self, sig: Signals) -> None: ...
def kill_worker(self, pid: int, sig: Signals) -> None: ...
Loading
Loading