Skip to content

Commit

Permalink
Merge pull request #137 from ithaka/chore/modernize-tooling
Browse files Browse the repository at this point in the history
  • Loading branch information
daneah authored Sep 6, 2024
2 parents 256efdd + 769bc1b commit 1e2b001
Show file tree
Hide file tree
Showing 18 changed files with 63 additions and 56 deletions.
23 changes: 17 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,41 @@ repos:

# Click through to this repository to see what other goodies are available
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
rev: v4.6.0
hooks:
- id: trailing-whitespace # Removes trailing whitespace from lines in all file types
- id: end-of-file-fixer # Fixes last line of all file types
- id: check-merge-conflict # Checks if you're about to commit a file that hasn't had conflicts resolved
- id: no-commit-to-branch # Checks if you're committing to a disallowed branch
args: [--branch, dev]
- id: check-ast # Checks that Python files are valid syntax
- id: check-toml # Checks TOML files for syntax errors

##########
# Python #
##########

# pyupgrade updates older syntax to newer syntax.
# It's particularly handy for updating `.format()` calls to f-strings.
- repo: https://github.com/asottile/pyupgrade
rev: v2.7.4
rev: v3.17.0
hooks:
- id: pyupgrade
args: [--py37-plus]
args: [--py39-plus]

# Run black last on Python code so all changes from previous hooks are reformatted
- repo: https://github.com/psf/black
rev: 21.5b2
rev: 24.8.0
hooks:
- id: black
language_version: python3.7
language_version: python3.9

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

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.1
hooks:
- id: mypy
additional_dependencies: [typing_extensions, types-requests, types-urllib3]
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Changed
- Modernize package quality tooling and configuration

## [8.0.0] - 2024-09-04
### Changed
Expand Down
1 change: 0 additions & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,3 @@ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.ht

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

19 changes: 1 addition & 18 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"members": True,
"show-inheritance": True,
}
autodoc_mock_imports = []

autoclass_content = "both"

# Add any paths that contain templates here, relative to this directory.
Expand Down Expand Up @@ -130,23 +130,6 @@
htmlhelp_basename = "apirondoc"


# -- Options for LaTeX output ------------------------------------------------

latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ build-backend = "setuptools.build_meta"
[tool.black]
line-length = 120
target-version = ['py39', 'py310', 'py311', 'py312']

[tool.isort]
profile = "black"
8 changes: 5 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ commands =
skip_install = True
deps =
black
pyflakes
isort
ruff
commands =
pyflakes {posargs:src tests}
black {posargs:--check src tests}
ruff check {posargs:src tests}
black {posargs:--check --diff src tests}
isort {posargs:--check --diff src tests}
[testenv:typecheck]
deps =
Expand Down
6 changes: 5 additions & 1 deletion src/apiron/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from apiron.client import Timeout
from apiron.endpoint import Endpoint, JsonEndpoint, StreamingEndpoint, StubEndpoint
from apiron.exceptions import APIException, NoHostsAvailableException, UnfulfilledParameterException
from apiron.exceptions import (
APIException,
NoHostsAvailableException,
UnfulfilledParameterException,
)
from apiron.service import DiscoverableService, Service, ServiceBase

__all__ = [
Expand Down
4 changes: 3 additions & 1 deletion src/apiron/client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import annotations

import collections
import logging
import random
from typing import Any, TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from urllib import parse

import requests
Expand All @@ -11,6 +12,7 @@

if TYPE_CHECKING:
import apiron # pragma: no cover

from apiron.exceptions import NoHostsAvailableException

LOGGER = logging.getLogger(__name__)
Expand Down
1 change: 0 additions & 1 deletion src/apiron/endpoint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
from apiron.endpoint.streaming import StreamingEndpoint
from apiron.endpoint.stub import StubEndpoint


__all__ = ["Endpoint", "JsonEndpoint", "StreamingEndpoint", "StubEndpoint"]
6 changes: 3 additions & 3 deletions src/apiron/endpoint/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import string
import sys
import warnings
from collections.abc import Iterable
from functools import partial, update_wrapper
from typing import Any, Callable, Iterable, TypeVar, TYPE_CHECKING
from typing import TYPE_CHECKING, Any, Callable, TypeVar

if TYPE_CHECKING: # pragma: no cover
if sys.version_info >= (3, 10):
Expand All @@ -21,10 +22,9 @@
import requests
from urllib3.util import retry

from apiron import client, Timeout
from apiron import Timeout, client
from apiron.exceptions import UnfulfilledParameterException


LOGGER = logging.getLogger(__name__)


Expand Down
9 changes: 5 additions & 4 deletions src/apiron/endpoint/json.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import collections
from typing import Any, Dict, Iterable, Optional
from collections.abc import Iterable
from typing import Any, Optional

from apiron.endpoint.endpoint import Endpoint

Expand All @@ -14,7 +15,7 @@ def __init__(
*args,
path: str = "/",
default_method: str = "GET",
default_params: Optional[Dict[str, Any]] = None,
default_params: Optional[dict[str, Any]] = None,
required_params: Optional[Iterable[str]] = None,
preserve_order: bool = False,
):
Expand All @@ -23,7 +24,7 @@ def __init__(
)
self.preserve_order = preserve_order

def format_response(self, response) -> Dict[str, Any]:
def format_response(self, response) -> dict[str, Any]:
"""
Extracts JSON data from the response
Expand All @@ -40,5 +41,5 @@ def format_response(self, response) -> Dict[str, Any]:
return response.json(object_pairs_hook=collections.OrderedDict if self.preserve_order else None)

@property
def required_headers(self) -> Dict[str, str]:
def required_headers(self) -> dict[str, str]:
return {"Accept": "application/json"}
2 changes: 1 addition & 1 deletion src/apiron/endpoint/streaming.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Iterable
from collections.abc import Iterable

from apiron.endpoint.endpoint import Endpoint

Expand Down
2 changes: 1 addition & 1 deletion src/apiron/endpoint/stub.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Any
from typing import Any, Optional

from apiron.endpoint import Endpoint

Expand Down
5 changes: 1 addition & 4 deletions src/apiron/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from typing import Set


class APIException(Exception):
pass

Expand All @@ -12,6 +9,6 @@ def __init__(self, service_name: str):


class UnfulfilledParameterException(APIException):
def __init__(self, endpoint_path: str, unfulfilled_params: Set[str]):
def __init__(self, endpoint_path: str, unfulfilled_params: set[str]):
message = f"The {endpoint_path} endpoint was called without required parameters: {unfulfilled_params}"
super().__init__(message)
1 change: 0 additions & 1 deletion src/apiron/service/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from apiron.service.base import Service, ServiceBase
from apiron.service.discoverable import DiscoverableService


__all__ = ["Service", "ServiceBase", "DiscoverableService"]
14 changes: 7 additions & 7 deletions src/apiron/service/base.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from typing import Any, Dict, List, Set
from typing import Any

from apiron import Endpoint


class ServiceMeta(type):
@property
def required_headers(cls) -> Dict[str, str]:
def required_headers(cls) -> dict[str, str]:
return cls().required_headers

@property
def endpoints(cls) -> Set[Endpoint]:
def endpoints(cls) -> set[Endpoint]:
return {attr for attr_name, attr in cls.__dict__.items() if isinstance(attr, Endpoint)}

def __str__(cls) -> str:
Expand All @@ -20,12 +20,12 @@ def __repr__(cls) -> str:


class ServiceBase(metaclass=ServiceMeta):
required_headers: Dict[str, Any] = {}
required_headers: dict[str, Any] = {}
auth = ()
proxies: Dict[str, str] = {}
proxies: dict[str, str] = {}

@classmethod
def get_hosts(cls) -> List[str]:
def get_hosts(cls) -> list[str]:
"""
The fully-qualified hostnames that correspond to this service.
These are often determined by asking a load balancer or service discovery mechanism.
Expand All @@ -48,7 +48,7 @@ class Service(ServiceBase):
domain: str

@classmethod
def get_hosts(cls) -> List[str]:
def get_hosts(cls) -> list[str]:
"""
The fully-qualified hostnames that correspond to this service.
These are often determined by asking a load balancer or service discovery mechanism.
Expand Down
11 changes: 8 additions & 3 deletions src/apiron/service/discoverable.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
from typing import List, Type
from typing import Protocol

from apiron.service.base import ServiceBase


class Resolver(Protocol):
@staticmethod
def resolve(service_name: str) -> list[str]: ...


class DiscoverableService(ServiceBase):
"""
A Service whose hosts are determined via a host resolver.
Expand All @@ -11,11 +16,11 @@ class DiscoverableService(ServiceBase):
and returns a list of host names that correspond to that service.
"""

host_resolver_class: Type
host_resolver_class: type[Resolver]
service_name: str

@classmethod
def get_hosts(cls) -> List[str]:
def get_hosts(cls) -> list[str]:
return cls.host_resolver_class.resolve(cls.service_name)

def __str__(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest
from urllib3.util import retry

from apiron import client, NoHostsAvailableException, Timeout
from apiron import NoHostsAvailableException, Timeout, client


@pytest.fixture
Expand Down

0 comments on commit 1e2b001

Please sign in to comment.