From 85c4df4e2245d66b0c895e99ab1919755be2aca6 Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Sun, 28 Jul 2024 20:11:20 +0300 Subject: [PATCH 1/5] Adjusting documentation --- .github/workflows/docs.yaml | 4 +- arangoasync/http.py | 94 +++++++++++++++++++++---------------- arangoasync/request.py | 61 ++++++++++-------------- arangoasync/response.py | 51 +++++++------------- docs/conf.py | 6 +++ docs/specs.rst | 45 ++---------------- pyproject.toml | 2 +- 7 files changed, 106 insertions(+), 157 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 6e430a5..cdb762a 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -24,5 +24,5 @@ jobs: - name: Install dependencies run: pip install .[dev] - - name: Generate Sphinx HTML - run: python -m sphinx -b html -W docs docs/_build + - name: Run Sphinx doctest + run: python -m sphinx -b doctest docs docs/_build diff --git a/arangoasync/http.py b/arangoasync/http.py index 75df659..35db1ad 100644 --- a/arangoasync/http.py +++ b/arangoasync/http.py @@ -15,19 +15,31 @@ class HTTPClient(ABC): # pragma: no cover """Abstract base class for HTTP clients. + Custom HTTP clients should inherit from this class. + + Example: + .. code-block:: python + + class MyCustomHTTPClient(HTTPClient): + def create_session(self, host): + pass + async def send_request(self, session, request): + pass """ @abstractmethod def create_session(self, host: str) -> Any: """Return a new session given the base host URL. - This method must be overridden by the user. + Note: + This method must be overridden by the user. + + Args: + host (str): ArangoDB host URL. - :param host: ArangoDB host URL. - :type host: str - :returns: Requests session object. - :rtype: Any + Returns: + Requests session object. """ raise NotImplementedError @@ -39,37 +51,36 @@ async def send_request( ) -> Response: """Send an HTTP request. - This method must be overridden by the user. + Note: + This method must be overridden by the user. - :param session: Session object. - :type session: Any - :param request: HTTP request. - :type request: arangoasync.request.Request - :returns: HTTP response. - :rtype: arangoasync.response.Response + Args: + session (Any): Client session object. + request (Request): HTTP request. + + Returns: + Response: HTTP response. """ raise NotImplementedError class AioHTTPClient(HTTPClient): - """HTTP client implemented on top of [aiohttp](https://docs.aiohttp.org/en/stable/). - - :param connector: Supports connection pooling. - By default, 100 simultaneous connections are supported, with a 60-second timeout - for connection reusing after release. - :type connector: aiohttp.BaseConnector | None - :param timeout: Timeout settings. - 300s total timeout by default for a complete request/response operation. - :type timeout: aiohttp.ClientTimeout | None - :param read_bufsize: Size of read buffer (64KB default). - :type read_bufsize: int - :param auth: HTTP authentication helper. - Should be used for specifying authorization data in client API. - :type auth: aiohttp.BasicAuth | None - :param compression_threshold: Will compress requests to the server if - the size of the request body (in bytes) is at least the value of this - option. - :type compression_threshold: int + """HTTP client implemented on top of aiohttp_. + + Args: + connector (aiohttp.BaseConnector | None): Supports connection pooling. + By default, 100 simultaneous connections are supported, with a 60-second + timeout for connection reusing after release. + timeout (aiohttp.ClientTimeout | None): Client timeout settings. + 300s total timeout by default for a complete request/response operation. + read_bufsize (int): Size of read buffer (64KB default). + auth (aiohttp.BasicAuth | None): HTTP authentication helper. + Should be used for specifying authorization data in client API. + compression_threshold (int): Will compress requests to the server if the size + of the request body (in bytes) is at least the value of this option. + + .. _aiohttp: + https://docs.aiohttp.org/en/stable/ """ def __init__( @@ -95,11 +106,12 @@ def __init__( def create_session(self, host: str) -> ClientSession: """Return a new session given the base host URL. - :param host: ArangoDB host URL. Typically, the address and port of a coordinator - (e.g. "http://127.0.0.1:8529"). - :type host: str - :returns: Session object. - :rtype: aiohttp.ClientSession + Args: + host (str): ArangoDB host URL. Must not include any paths. Typically, this + is the address and port of a coordinator (e.g. "http://127.0.0.1:8529"). + + Returns: + aiohttp.ClientSession: Session object, used to send future requests. """ return ClientSession( base_url=host, @@ -116,12 +128,12 @@ async def send_request( ) -> Response: """Send an HTTP request. - :param session: Session object. - :type session: aiohttp.ClientSession - :param request: HTTP request. - :type request: arangoasync.request.Request - :returns: HTTP response. - :rtype: arangoasync.response.Response + Args: + session (aiohttp.ClientSession): Session object used to make the request. + request (Request): HTTP request. + + Returns: + Response: HTTP response. """ method = request.method endpoint = request.endpoint diff --git a/arangoasync/request.py b/arangoasync/request.py index a6d7396..bd68e4a 100644 --- a/arangoasync/request.py +++ b/arangoasync/request.py @@ -11,7 +11,7 @@ class Method(Enum): - """HTTP methods.""" + """HTTP methods enum: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS""" GET = auto() POST = auto() @@ -25,31 +25,19 @@ class Method(Enum): class Request: """HTTP request. - :param method: HTTP method. - :type method: request.Method - :param endpoint: API endpoint. - :type endpoint: str - :param headers: Request headers. - :type headers: dict | None - :param params: URL parameters. - :type params: dict | None - :param data: Request payload. - :type data: Any - :param deserialize: Whether the response body should be deserialized. - :type deserialize: bool - - :ivar method: HTTP method. - :vartype method: request.Method - :ivar endpoint: API endpoint, for example "_api/version". - :vartype endpoint: str - :ivar headers: Request headers. - :vartype headers: dict | None - :ivar params: URL (query) parameters. - :vartype params: dict | None - :ivar data: Request payload. - :vartype data: Any - :ivar deserialize: Whether the response body should be deserialized. - :vartype deserialize: bool + Args: + method (Method): HTTP method. + endpoint (str): API endpoint. + headers (dict | None): Request headers. + params (dict | None): URL parameters. + data (str | None): Request payload. + + Attributes: + method (Method): HTTP method. + endpoint (str): API endpoint. + headers (dict | None): Request headers. + params (dict | None): URL parameters. + data (str | None): Request payload. """ __slots__ = ( @@ -58,7 +46,6 @@ class Request: "headers", "params", "data", - "deserialize", ) def __init__( @@ -68,23 +55,22 @@ def __init__( headers: Optional[Headers] = None, params: Optional[Params] = None, data: Optional[str] = None, - deserialize: bool = True, ) -> None: self.method: Method = method self.endpoint: str = endpoint self.headers: Headers = self._normalize_headers(headers) self.params: Params = self._normalize_params(params) self.data: Optional[str] = data - self.deserialize: bool = deserialize @staticmethod def _normalize_headers(headers: Optional[Headers]) -> Headers: """Normalize request headers. - :param headers: Request headers. - :type headers: dict | None - :returns: Normalized request headers. - :rtype: dict + Parameters: + headers (dict | None): Request headers. + + Returns: + dict: Normalized request headers. """ driver_header = f"arangoasync/{__version__}" normalized_headers: Headers = { @@ -103,10 +89,11 @@ def _normalize_headers(headers: Optional[Headers]) -> Headers: def _normalize_params(params: Optional[Params]) -> Params: """Normalize URL parameters. - :param params: URL parameters. - :type params: dict | None - :returns: Normalized URL parameters. - :rtype: dict + Parameters: + params (dict | None): URL parameters. + + Returns: + dict: Normalized URL parameters. """ normalized_params: Params = {} diff --git a/arangoasync/response.py b/arangoasync/response.py index 85ace65..a60d753 100644 --- a/arangoasync/response.py +++ b/arangoasync/response.py @@ -11,39 +11,24 @@ class Response: """HTTP response. - :param method: HTTP method. - :type method: request.Method - :param url: API URL. - :type url: str - :param headers: Response headers. - :type headers: dict | None - :param status_code: Response status code. - :type status_code: int - :param status_text: Response status text. - :type status_text: str - :param raw_body: Raw response body. - :type raw_body: str + Parameters: + method (Method): HTTP method. + url (str): API URL. + headers (dict | None): Response headers. + status_code (int): Response status code. + status_text (str): Response status text. + raw_body (bytes): Raw response body. - :ivar method: HTTP method. - :vartype method: request.Method - :ivar url: API URL. - :vartype url: str - :ivar headers: Response headers. - :vartype headers: dict | None - :ivar status_code: Response status code. - :vartype status_code: int - :ivar status_text: Response status text. - :vartype status_text: str - :ivar raw_body: Raw response body. - :vartype raw_body: str - :ivar body: Response body after processing. - :vartype body: Any - :ivar error_code: Error code from ArangoDB server. - :vartype error_code: int - :ivar error_message: Error message from ArangoDB server. - :vartype error_message: str - :ivar is_success: True if response status code was 2XX. - :vartype is_success: bool + Attributes: + method (Method): HTTP method. + url (str): API URL. + headers (dict | None): Response headers. + status_code (int): Response status code. + status_text (str): Response status text. + raw_body (bytes): Raw response body. + error_code (int | None): Error code from ArangoDB server. + error_message (str | None): Error message from ArangoDB server. + is_success (bool | None): True if response status code was 2XX. """ __slots__ = ( @@ -52,7 +37,6 @@ class Response: "headers", "status_code", "status_text", - "body", "raw_body", "error_code", "error_message", @@ -76,7 +60,6 @@ def __init__( self.raw_body: bytes = raw_body # Populated later - self.body: Optional[str] = None self.error_code: Optional[int] = None self.error_message: Optional[str] = None self.is_success: Optional[bool] = None diff --git a/docs/conf.py b/docs/conf.py index 159264c..78b9956 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,6 +12,7 @@ "sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.viewcode", + "sphinx.ext.napoleon", "sphinx.ext.intersphinx", ] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] @@ -19,7 +20,12 @@ master_doc = "index" autodoc_member_order = "bysource" +autodoc_typehints = "none" intersphinx_mapping = { "aiohttp": ("https://docs.aiohttp.org/en/stable/", None), } + +napoleon_google_docstring = True +napoleon_numpy_docstring = False +napoleon_attr_annotations = True diff --git a/docs/specs.rst b/docs/specs.rst index 6645d64..eb39e72 100644 --- a/docs/specs.rst +++ b/docs/specs.rst @@ -4,50 +4,11 @@ API Specification This page contains the specification for all classes and methods available in python-arango-async. -.. _AioHTTPClient: - -AioHTTPClient -================= - -.. autoclass:: arangoasync.http.AioHTTPClient - :members: - -.. _DefaultHTTPClient: - -DefaultHTTPClient -================= - -.. autoclass:: arangoasync.http.DefaultHTTPClient - :members: - -.. _HTTPClient: - -HTTPClient -========== - -.. autoclass:: arangoasync.http.HTTPClient +.. automodule:: arangoasync.http :members: -.. _Method: - -Method -======= - -.. autoclass:: arangoasync.request.Method +.. automodule:: arangoasync.request :members: -.. _Request: - -Request -======= - -.. autoclass:: arangoasync.request.Request - :members: - -.. _Response: - -Response -======== - -.. autoclass:: arangoasync.response.Response +.. automodule:: arangoasync.response :members: diff --git a/pyproject.toml b/pyproject.toml index da47073..2e8cb7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ dev = [ "pytest-asyncio>=0.23.8", "pytest-cov>=5.0", "sphinx>=7.3", - "sphinx_rtd_theme", + "sphinx_rtd_theme>=2.0.02", "types-setuptools", ] From 8cec66bfb86bac623b874123c47c1968b7e674d8 Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Sun, 28 Jul 2024 20:16:55 +0300 Subject: [PATCH 2/5] RTD theme version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2e8cb7c..cd09627 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ dev = [ "pytest-asyncio>=0.23.8", "pytest-cov>=5.0", "sphinx>=7.3", - "sphinx_rtd_theme>=2.0.02", + "sphinx_rtd_theme>=2.0", "types-setuptools", ] From 53439a0fb207b4de4ee1e596a5d963db4a387aa7 Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Sun, 28 Jul 2024 20:50:07 +0300 Subject: [PATCH 3/5] Adding circle ci config --- .circleci/config.yml | 71 +++++++++++++++++++++++++++++++++- tests/static/cluster-3.12.conf | 15 +++++++ tests/static/keyfile | 1 + tests/static/single-3.12.conf | 13 +++++++ 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 tests/static/cluster-3.12.conf create mode 100644 tests/static/keyfile create mode 100644 tests/static/single-3.12.conf diff --git a/.circleci/config.yml b/.circleci/config.yml index ea266e0..3d174a8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,14 +1,30 @@ version: 2.1 +executors: + python-container: + docker: + - image: cimg/python:3.12 + python-vm: + machine: + - image: ubuntu-2204:current + workflows: ci: jobs: - lint + - test: + name: Python (<< matrix.python_version >>) - ArangoDB (<< matrix.arangodb_license >>, << matrix.arangodb_version >> << matrix.arangodb_config >>) + matrix: + parameters: + python_version: ["3.10"] + arangodb_config: ["single"] + arangodb_license: ["community"] + arangodb_version: ["3.12"] jobs: lint: - docker: - - image: python:latest + executor: python-container + resource_class: small steps: - checkout - run: @@ -26,3 +42,54 @@ jobs: - run: name: Run mypy command: mypy ./arangoasync + test: + parameters: + python_version: + type: string + arangodb_config: + type: string + arangodb_license: + type: string + arangodb_version: + type: string + executor: python-vm + steps: + - checkout + - run: + name: Setup ArangoDB + command: | + chmod +x starter.sh + ./starter.sh ${{ matrix.arangodb_config }} ${{ matrix.arangodb_license }} ${{ matrix.arangodb_version }} + - restore_cache: + key: pip-and-local-cache + - run: + name: Setup Python + command: | + pyenv --version + pyenv install -f << parameters.python_version >> + pyenv global << parameters.python_version >> + - run: + name: Install Dependencies + command: pip install -e .[dev] + - run: docker ps -a + - run: docker logs arango + - run: + name: Run pytest + command: | + mkdir test-results + + args=("--junitxml=test-results/junit.xml" "--log-cli-level=DEBUG" "--host" "localhost" "--port=8529") + if [ << parameters.arangodb_config >> = "cluster" ]; then + args+=("--cluster" "--port=8539" "--port=8549") + fi + + if [ << parameters.arangodb_license >> = "enterprise" ]; then + args+=("--enterprise") + fi + + echo "Running pytest with args: ${args[@]}" + pytest --cov=arango --cov-report=xml --cov-report term-missing --color=yes --code-highlight=yes "${args[@]}" + - store_artifacts: + path: test-results + - store_test_results: + path: test-results diff --git a/tests/static/cluster-3.12.conf b/tests/static/cluster-3.12.conf new file mode 100644 index 0000000..d33e07a --- /dev/null +++ b/tests/static/cluster-3.12.conf @@ -0,0 +1,15 @@ +[starter] +mode = cluster +local = true +address = 0.0.0.0 +port = 8528 + +[auth] +jwt-secret = /tests/static/keyfile + +[args] +all.database.password = passwd +all.database.extended-names = true +all.log.api-enabled = true +all.javascript.allow-admin-execute = true +all.server.options-api = admin diff --git a/tests/static/keyfile b/tests/static/keyfile new file mode 100644 index 0000000..d97c5ea --- /dev/null +++ b/tests/static/keyfile @@ -0,0 +1 @@ +secret diff --git a/tests/static/single-3.12.conf b/tests/static/single-3.12.conf new file mode 100644 index 0000000..d5df3aa --- /dev/null +++ b/tests/static/single-3.12.conf @@ -0,0 +1,13 @@ +[starter] +mode = single +address = 0.0.0.0 +port = 8528 + +[auth] +jwt-secret = /tests/static/keyfile + +[args] +all.database.password = passwd +all.database.extended-names = true +all.javascript.allow-admin-execute = true +all.server.options-api = admin From bb25dd1491e1c42a35ce7365fa0401b251bbf5cd Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Sun, 28 Jul 2024 20:56:57 +0300 Subject: [PATCH 4/5] Install rtd requirements --- .readthedocs.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 04a8cd1..cb6f501 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -20,5 +20,6 @@ sphinx: # to build your documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: - install: - - requirements: docs/requirements.txt + install: + - method: pip + path: .[dev] From 7101d4f3e44081acccb49bfb47dd63ce77f327bb Mon Sep 17 00:00:00 2001 From: Alex Petenchea Date: Sun, 28 Jul 2024 20:59:25 +0300 Subject: [PATCH 5/5] requirements.txt no longer needed --- docs/requirements.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 483a4e9..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -sphinx_rtd_theme