Skip to content
Open
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
6 changes: 5 additions & 1 deletion .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,18 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade flake8 pytest pycodestyle pytest-cov pytest-mock
pip install --upgrade flake8 pytest pycodestyle pytest-cov pytest-mock "mypy==2.3.*"
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Type check with mypy
if: matrix.python-version == '3.11'
run: |
mypy grobid_client
- name: Test with pytest
run: |
pytest tests/ -v --cov=grobid_client --cov-report=xml --cov-report=term-missing
Expand Down
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
include Readme.md
include Readme.md
include grobid_client/py.typed
1 change: 1 addition & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ concurrent processing capabilities for PDF documents, reference strings, and pat
- **Sentence Segmentation**: Layout-aware sentence segmentation capabilities
- **JSON Output**: Convert TEI XML output to structured JSON format with CORD-19-like structure
- **Markdown Output**: Convert TEI XML output to clean Markdown format with structured sections
- **Type Hints**: Ships inline type annotations and a `py.typed` marker (PEP 561) for static type checking

## 📋 Prerequisites

Expand Down
65 changes: 44 additions & 21 deletions grobid_client/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
""" Generic API Client """
from __future__ import annotations

from copy import deepcopy
import json
from typing import Any, Optional, Tuple

import requests

try:
Expand All @@ -16,12 +20,17 @@ class ApiClient(object):
service methods, i.e. ``get``, ``post``, ``put`` and ``delete``.
"""

accept_type = "application/xml"
api_base = None
accept_type: str = "application/xml"
api_base: Optional[str] = None

def __init__(
self, base_url, username=None, api_key=None, status_endpoint=None, timeout=60
):
self,
base_url: str,
username: Optional[str] = None,
api_key: Optional[str] = None,
status_endpoint: Optional[str] = None,
timeout: int = 60,
) -> None:
"""Initialise client.

Args:
Expand All @@ -37,7 +46,7 @@ def __init__(
self.timeout = timeout

@staticmethod
def encode(request, data):
def encode(request: Any, data: Optional[dict]) -> Any:
"""Add request content data to request body, set Content-type header.

Should be overridden by subclasses if not using JSON encoding.
Expand All @@ -58,7 +67,7 @@ def encode(request, data):
return request

@staticmethod
def decode(response):
def decode(response: Any) -> Any:
"""Decode the returned data in the response.

Should be overridden by subclasses if something else than JSON is
Expand All @@ -73,9 +82,9 @@ def decode(response):
try:
return response.json()
except ValueError as e:
return e.message
return e.message # type: ignore[attr-defined] # pre-existing (Python 2 style)

def get_credentials(self):
def get_credentials(self) -> dict:
"""Returns parameters to be added to authenticate the request.

This lives on its own to make it easier to re-implement it if needed.
Expand All @@ -87,14 +96,14 @@ def get_credentials(self):

def call_api(
self,
method,
url,
headers=None,
params=None,
data=None,
files=None,
timeout=None,
):
method: str,
url: str,
headers: Optional[dict] = None,
params: Optional[dict] = None,
data: Optional[dict] = None,
files: Optional[dict] = None,
timeout: Optional[int] = None,
) -> Tuple[requests.Response, int]:
"""Call API.

This returns object containing data, with error details if applicable.
Expand Down Expand Up @@ -130,7 +139,7 @@ def call_api(

return r, r.status_code

def get(self, url, params=None, **kwargs):
def get(self, url: str, params: Optional[dict] = None, **kwargs: Any) -> Tuple[requests.Response, int]:
"""Call the API with a GET request.

Args:
Expand All @@ -142,7 +151,7 @@ def get(self, url, params=None, **kwargs):
"""
return self.call_api("GET", url, params=params, **kwargs)

def delete(self, url, params=None, **kwargs):
def delete(self, url: str, params: Optional[dict] = None, **kwargs: Any) -> Tuple[requests.Response, int]:
"""Call the API with a DELETE request.

Args:
Expand All @@ -154,7 +163,14 @@ def delete(self, url, params=None, **kwargs):
"""
return self.call_api("DELETE", url, params=params, **kwargs)

def put(self, url, params=None, data=None, files=None, **kwargs):
def put(
self,
url: str,
params: Optional[dict] = None,
data: Optional[dict] = None,
files: Optional[dict] = None,
**kwargs: Any,
) -> Tuple[requests.Response, int]:
"""Call the API with a PUT request.

Args:
Expand All @@ -170,7 +186,14 @@ def put(self, url, params=None, data=None, files=None, **kwargs):
"PUT", url, params=params, data=data, files=files, **kwargs
)

def post(self, url, params=None, data=None, files=None, **kwargs):
def post(
self,
url: str,
params: Optional[dict] = None,
data: Optional[dict] = None,
files: Optional[dict] = None,
**kwargs: Any,
) -> Tuple[requests.Response, int]:
"""Call the API with a POST request.

Args:
Expand All @@ -186,7 +209,7 @@ def post(self, url, params=None, data=None, files=None, **kwargs):
method="POST", url=url, params=params, data=data, files=files, **kwargs
)

def service_status(self, **kwargs):
def service_status(self, **kwargs: Any) -> Tuple[requests.Response, int]:
"""Call the API to get the status of the service.

Returns:
Expand Down
Loading
Loading