docs: improve API documentation
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Anna “CyberTailor” 2024-04-22 21:32:28 +05:00
parent d436a4a906
commit 8d945293aa
Signed by: CyberTaIlor
GPG Key ID: E7B76EDC50864BB1
20 changed files with 143 additions and 43 deletions

1
.gitignore vendored
View File

@ -7,7 +7,6 @@ __pycache__
*.pyo
docs/_build
docs/api
dist
venv

View File

@ -0,0 +1,8 @@
.. SPDX-FileCopyrightText: 2024 Anna <cyber@sysrq.in>
.. SPDX-License-Identifier: CC0-1.0
repology\_client.constants
==========================
.. automodule:: repology_client.constants
:members:

View File

@ -0,0 +1,17 @@
.. SPDX-FileCopyrightText: 2024 Anna <cyber@sysrq.in>
.. SPDX-License-Identifier: CC0-1.0
repology\_client.exceptions
===========================
repology\_client.exceptions
---------------------------
.. automodule:: repology_client.exceptions
:members:
repology\_client.exceptions.resolve
-----------------------------------
.. automodule:: repology_client.exceptions.resolve
:members:

View File

@ -0,0 +1,33 @@
.. SPDX-FileCopyrightText: 2024 Anna <cyber@sysrq.in>
.. SPDX-License-Identifier: CC0-1.0
repology\_client
================
repology\_client
----------------
.. automodule:: repology_client
.. rubric:: High-level Functions
.. autofunction:: get_packages
.. autofunction:: get_projects
.. autofunction:: resolve_package
.. rubric:: Low-level Functions
.. autofunction:: api
repology\_client.exp
--------------------
.. autodata:: exp
.. rubric:: High-level Functions
.. autofunction:: distromap
.. rubric:: Low-level Functions
.. autofunction:: api

View File

@ -0,0 +1,8 @@
.. SPDX-FileCopyrightText: 2024 Anna <cyber@sysrq.in>
.. SPDX-License-Identifier: CC0-1.0
repology\_client.types
======================
.. automodule:: repology_client.types
:members:

View File

@ -0,0 +1,8 @@
.. SPDX-FileCopyrightText: 2024 Anna <cyber@sysrq.in>
.. SPDX-License-Identifier: CC0-1.0
repology\_client.utils
======================
.. automodule:: repology_client.utils
:members:

View File

@ -49,17 +49,17 @@ intersphinx_mapping = {
'aiohttp': ('https://docs.aiohttp.org/en/stable', None),
}
autodoc_default_flags = [
'members',
'show-inheritance',
'inherited-members',
'undoc-members',
]
autosummary_generate = False
autodoc_default_options = {
'show-inheritance': True,
'undoc-members': True,
'member-order': 'bysource',
}
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = 'aiohttp_theme'
html_theme = 'alabaster'
html_permalinks_icon = '#'
html_theme_options = {
'description': 'Asynchronous Python wrapper for Repology API',

View File

@ -15,7 +15,7 @@ Prerequisites
The following dependencies are used by this library:
* :external+aiohttp:doc:`aiohttp <index>`
* `pydantic`_
* `pydantic`_ (with direct use of pydantic-core)
.. _pydantic: https://pydantic.dev/

View File

@ -6,6 +6,9 @@ API Reference
.. autosummary::
:toctree: api
:recursive:
repology_client
repology_client.constants
repology_client.exceptions
repology_client.types
repology_client.utils

View File

@ -8,7 +8,10 @@ Release Notes
-----
* **New**: :py:func:`repology_client.exp.distromap` function for
``/api/experimental/distromap`` endpoint
``/api/experimental/distromap`` endpoint.
* Parse JSON with possibly faster Pydantic parser, since we already use this
library.
* Improve API documentation and switch to Alabaster HTML theme.
0.1.0
-----

View File

@ -40,7 +40,7 @@ test = [
"pytest-recording",
]
docs = [
"aiohttp-theme",
"alabaster",
"sphinx",
"sphinx-prompt",
]

View File

@ -18,7 +18,9 @@ from repology_client._client.experimental import (
distromap,
)
#: Asynchronous wrapper for experimental endpoints of Repology API.
exp = SimpleNamespace()
exp.api = api_exp
exp.distromap = distromap

View File

@ -22,7 +22,7 @@ async def _call(location: str, params: dict | None = None, *,
:param location: URL location
:param params: URL query string parameters
:param session: :external+aiohttp:py:module:`aiohttp` client session
:param session: :external+aiohttp:py:mod:`aiohttp` client session
:raises repology_client.exceptions.EmptyResponse: on empty response
:raises aiohttp.ClientResponseError: on HTTP errors
@ -49,7 +49,7 @@ async def _json_api(base_url: str, endpoint: str | None = None,
:param base: base API URL
:param endpoint: API endpoint
:param params: URL query string parameters
:param session: :external+aiohttp:py:module:`aiohttp` client session
:param session: :external+aiohttp:py:mod:`aiohttp` client session
:raises repology_client.exceptions.EmptyResponse: on empty response
:raises aiohttp.ClientResponseError: on HTTP errors

View File

@ -22,11 +22,11 @@ async def api(endpoint: str, params: dict | None = None, *,
:param endpoint: API endpoint (example: ``/distromap``)
:param params: URL query string parameters
:param session: :external+aiohttp:py:module:`aiohttp` client session
:param session: :external+aiohttp:py:mod:`aiohttp` client session
:raises repology_client.exceptions.EmptyResponse: on empty response
:raises repology_client.exceptions.InvalidInput: on invalid endpoint
parameter
parameter
:raises aiohttp.ClientResponseError: on HTTP errors
:raises json.JSONDecodeError: on JSON decode failure
@ -44,11 +44,11 @@ async def distromap(fromrepo: str, torepo: str, *,
:param fromrepo: first repository
:param torepo: second repository
:param session: :external+aiohttp:py:module:`aiohttp` client session
:param session: :external+aiohttp:py:mod:`aiohttp` client session
:raises repology_client.exceptions.EmptyResponse: on empty response
:raises repology_client.exceptions.InvalidInput: if repositories are no
different or one of them is an empty string
different or one of them is an empty string
:raises aiohttp.ClientResponseError: on HTTP errors
:returns: decoded API response

View File

@ -31,18 +31,20 @@ async def resolve_package(repo: str, name: str,
name into ``/api/v1/project/<project>`` project information.
If you disable autoresolve, ambigous package names will raise the
:py:class:`MultipleProjectsFound` exception. It will however contain all
matching project names, so you can continue.
:py:class:`repology_client.exceptions.resolve.MultipleProjectsFound`
exception. It will however contain all matching project names, so you can
continue.
:param repo: repository name on Repology
:param name: package name in the repository
:param name_type: which name is used, "source" or "binary"
:param autoresolve: enable automatic ambiguity resolution
:param session: :external+aiohttp:py:mod:`aiohttp` client session
:raises repology_client.exceptions.resolve.MultipleProjectsFound: on
ambigous package names when automatic resolution is disabled
ambigous package names when automatic resolution is disabled
:raises repology_client.exceptions.resolve.ProjectNotFound: on failed
resolve resulting in the "404 Not Found" HTTP error
resolve resulting in the "404 Not Found" HTTP error
:raises aiohttp.ClientResponseError: on HTTP errors (except 404)
:raises ValueError: on JSON decode failure

View File

@ -30,11 +30,11 @@ async def api(endpoint: str, params: dict | None = None, *,
:param endpoint: API endpoint (example: ``/projects``)
:param params: URL query string parameters
:param session: :external+aiohttp:py:module:`aiohttp` client session
:param session: :external+aiohttp:py:mod:`aiohttp` client session
:raises repology_client.exceptions.EmptyResponse: on empty response
:raises repology_client.exceptions.InvalidInput: on invalid endpoint
parameter
parameter
:raises aiohttp.ClientResponseError: on HTTP errors
:raises ValueError: on JSON decode failure
@ -51,11 +51,11 @@ async def get_packages(project: str, *,
single project.
:param project: project name on Repology
:param session: :external+aiohttp:py:module:`aiohttp` client session
:param session: :external+aiohttp:py:mod:`aiohttp` client session
:raises repology_client.exceptions.EmptyResponse: on empty response
:raises repology_client.exceptions.InvalidInput: if ``project`` is an empty
string
string
:raises aiohttp.ClientResponseError: on HTTP errors
:returns: set of packages
@ -81,7 +81,7 @@ async def get_projects(start: str = "", end: str = "", count: int = 200, *,
:param start: name of the first project to start with
:param end: name of the last project to end with
:param count: maximum number of projects to fetch
:param session: :external+aiohttp:py:module:`aiohttp` client session
:param session: :external+aiohttp:py:mod:`aiohttp` client session
:raises repology_client.exceptions.EmptyResponse: on empty response
:raises aiohttp.ClientResponseError: on HTTP errors

View File

@ -6,29 +6,29 @@ Hardcoded constants for rapidly-changing Repology API. What could possibly go
wrong?
"""
# Library package name.
#: Library package name.
PACKAGE = "repology-client"
# Library version.
#: Library version.
VERSION = "0.1.0"
# Library homepage.
#: Library homepage.
HOMEPAGE = "https://repology-client.sysrq.in"
# Library's User-agent header
#: Library's User-agent header
USER_AGENT = f"Mozilla/5.0 (compatible; {PACKAGE}/{VERSION}; +{HOMEPAGE})"
# Base URL for API v1 requests.
#: Base URL for API v1 requests.
API_V1_URL = "https://repology.org/api/v1"
# Base URL for Experimental API requests.
#: Base URL for Experimental API requests.
API_EXP_URL = "https://repology.org/api/experimental"
# Base URL for the "Project by package name" tool.
#: Base URL for the "Project by package name" tool.
TOOL_PROJECT_BY_URL = "https://repology.org/tools/project-by"
# Maximum number of projects API can return.
#: Maximum number of projects API can return.
MAX_PROJECTS = 200
# Number of projects, starting from which you should use bulk export instead.
#: Number of projects, starting from which you should use bulk export instead.
HARD_LIMIT = 5_000

View File

@ -20,7 +20,14 @@ class PackageResolveException(RepologyException):
if message is None:
message = f"Exception occured while resolving {pkg!s}"
super().__init__(message)
self.pkg = pkg
self._pkg = pkg
@property
def pkg(self) -> _ResolvePkg:
"""
Underlying :py:class:`repology_client.types._ResolvePkg` object.
"""
return self._pkg
class ProjectNotFound(PackageResolveException):
@ -39,11 +46,18 @@ class MultipleProjectsFound(PackageResolveException):
Raised if Repology was requested to get project by package name without
automatic ambiguity resolution and responded with multiple results.
Instances of this exception contain all package names returned by Repology.
Instances of this exception contain all project names returned by Repology.
"""
def __init__(self, pkg: _ResolvePkg, names: Iterable[str]):
message = f"Multiple projects found for {pkg!s}"
super().__init__(pkg, message)
self.names = tuple(names)
self._names = tuple(names)
@property
def names(self) -> tuple[str, ...]:
"""
Project names returned by Repology.
"""
return self._names

View File

@ -10,6 +10,8 @@ from pydantic.dataclasses import dataclass
_DistromapPackages = tuple[str, ...]
_DistromapGroups = tuple[_DistromapPackages, _DistromapPackages]
#: Intermapping of packages between two repositories.
Distromap = tuple[_DistromapGroups, ...]

View File

@ -17,10 +17,11 @@ from repology_client.constants import USER_AGENT
class limit():
"""
Based on `this StackOverflow answer
<https://stackoverflow.com/questions/35196974/aiohttp-set-maximum-number-of-requests-per-second/62503115>`_.
Decorator to set a limit on requests per second.
Set a limit on requests per second.
Based on `this StackOverflow answer`__.
__ https://stackoverflow.com/a/62503115/4257264
"""
def __init__(self, calls: int, period: float):
@ -63,7 +64,7 @@ async def ensure_session(
"""
Create a new client session, if necessary, and close it on exit.
:param session: :external+aiohttp:py:module:`aiohttp` client session
:param session: :external+aiohttp:py:mod:`aiohttp` client session
"""
keep_session = True