diff --git a/pyproject.toml b/pyproject.toml index 7124e338..4589ecbe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,9 +19,7 @@ classifiers = [ "Typing :: Typed", ] dynamic = ["version"] -dependencies = [ - "requests>=2.31.0,<3" -] +dependencies = ["requests>=2.31.0,<3"] [project.urls] Source = "https://github.com/posit-dev/posit-sdk-py" @@ -29,9 +27,7 @@ Issues = "https://github.com/posit-dev/posit-sdk-py/issues" [tool.pytest.ini_options] testpaths = ["tests"] -addopts = [ - "--import-mode=importlib", -] +addopts = ["--import-mode=importlib"] [tool.setuptools_scm] version_file = "src/posit/_version.py" @@ -43,16 +39,34 @@ docstring-code-line-length = 20 [tool.ruff.lint] select = ["D"] ignore = [ - 'D107', - 'D203', - 'D212', - 'D213', - 'D402', - 'D413', - 'D415', - 'D416', - 'D417' + # NumPy style docstring convention with noted exceptions. + # https://docs.astral.sh/ruff/faq/#does-ruff-support-numpy-or-google-style-docstrings + # + # This docstring style works with [quartdoc](https://machow.github.io/quartodoc/get-started/overview.html). + # + 'D101', # TODO(#135) implement docstring for public class + 'D103', # TODO(#135) implement docstring for public functions + 'D104', # TODO(#135) implement docstring for public package + 'D105', # TODO(#135) implement docstring for magic methods + 'D107', + 'D203', + 'D212', + 'D213', + 'D100', # TODO(#135) implement docstring for public modules + 'D102', # TODO(#135) implement docstring for public methods + 'D401', # TODO(#135) fix imperative mood warnings + 'D402', + 'D413', + 'D415', + 'D416', + 'D417', + 'D418', # The Python Language Server can accomdate documentation for individual methods. + # TODO(#135) resarch D418 and determine if we should continue ignoring it. ] +[tool.ruff.lint.per-file-ignores] +"examples/*" = ["D"] +"tests/*" = ["D"] + [tool.ruff.lint.pydocstyle] convention = "numpy" diff --git a/src/posit/__init__.py b/src/posit/__init__.py index e69de29b..32a10a33 100644 --- a/src/posit/__init__.py +++ b/src/posit/__init__.py @@ -0,0 +1 @@ +"""The Posit SDK.""" diff --git a/src/posit/connect/auth.py b/src/posit/connect/auth.py index ee760fe0..1bb61f4e 100644 --- a/src/posit/connect/auth.py +++ b/src/posit/connect/auth.py @@ -1,3 +1,5 @@ +"""Provides authentication functionality.""" + from requests import PreparedRequest from requests.auth import AuthBase @@ -5,17 +7,12 @@ class Auth(AuthBase): - """_summary_ - - Parameters - ---------- - AuthBase : _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ _type_ - _description_ - """ + """Handles authentication for API requests.""" def __init__(self, config: Config) -> None: self._config = config def __call__(self, r: PreparedRequest) -> PreparedRequest: + """Add authorization header to the request.""" r.headers["Authorization"] = f"Key {self._config.api_key}" return r diff --git a/src/posit/connect/client.py b/src/posit/connect/client.py index 6ae8fadf..412ab0fe 100644 --- a/src/posit/connect/client.py +++ b/src/posit/connect/client.py @@ -1,3 +1,5 @@ +"""Contains the Client class.""" + from __future__ import annotations from requests import Response, Session @@ -13,6 +15,8 @@ class Client: + """Main interface for Posit Connect.""" + def __init__( self, api_key: Optional[str] = None, @@ -42,42 +46,67 @@ def __init__( @property def connect_version(self): + """The server version. + + Return: + str + """ if self._server_settings is None: self._server_settings = self.get("server_settings").json() return self._server_settings["version"] @property def me(self) -> User: + """The connected user. + + Returns + ------- + User + """ return me.get(self.config, self.session) @property def oauth(self) -> OAuthIntegration: + """An OAuthIntegration. + + Returns + ------- + OAuthIntegration + """ return OAuthIntegration(config=self.config, session=self.session) @property def users(self) -> Users: + """The users resource interface. + + Returns + ------- + Users + """ return Users(config=self.config, session=self.session) @property def content(self) -> Content: + """The content resource interface. + + Returns + ------- + Content + """ return Content(config=self.config, session=self.session) def __del__(self): - """ - Close the session when the Client instance is deleted. - """ + """Close the session when the Client instance is deleted.""" if hasattr(self, "session") and self.session is not None: self.session.close() def __enter__(self): - """ - Enter method for using the client as a context manager. - """ + """Enter method for using the client as a context manager.""" return self def __exit__(self, exc_type, exc_value, exc_tb): """ - Closes the session if it exists. + Close the session if it exists. Args: exc_type: The type of the exception raised (if any). @@ -89,31 +118,33 @@ def __exit__(self, exc_type, exc_value, exc_tb): def request(self, method: str, path: str, **kwargs) -> Response: """ - Sends an HTTP request to the specified path using the given method. + Send an HTTP request. + + A facade for requests.Session.request. Args: method (str): The HTTP method to use for the request. - path (str): The path to send the request to. - **kwargs: Additional keyword arguments to pass to the underlying session's request method. + path (str): Appended to the url object attribute. + **kwargs: Additional keyword arguments passed to requests.Session.post. Returns ------- - Response: The response object containing the server's response to the request. + Response: A requests.Response object. """ url = urls.append_path(self.config.url, path) return self.session.request(method, url, **kwargs) def get(self, path: str, **kwargs) -> Response: """ - Send a GET request to the specified path. + Send a GET request. Args: - path (str): The path to send the request to. - **kwargs: Additional keyword arguments to be passed to the underlying session's `get` method. + path (str): Appended to the configured base url. + **kwargs: Additional keyword arguments passed to requests.Session.get. Returns ------- - Response: The response object. + Response: A requests.Response object. """ url = urls.append_path(self.config.url, path) @@ -121,15 +152,15 @@ def get(self, path: str, **kwargs) -> Response: def post(self, path: str, **kwargs) -> Response: """ - Send a POST request to the specified path. + Send a POST request. Args: - path (str): The path to send the request to. - **kwargs: Additional keyword arguments to be passed to the underlying session's `post` method. + path (str): Appended to the configured base url. + **kwargs: Additional keyword arguments passed to requests.Session.post. Returns ------- - Response: The response object. + Response: A requests.Response object. """ url = urls.append_path(self.config.url, path) @@ -137,15 +168,15 @@ def post(self, path: str, **kwargs) -> Response: def put(self, path: str, **kwargs) -> Response: """ - Send a PUT request to the specified path. + Send a PUT request. Args: - path (str): The path to send the request to. - **kwargs: Additional keyword arguments to be passed to the underlying session's `put` method. + path (str): Appended to the configured base url. + **kwargs: Additional keyword arguments passed to requests.Session.put. Returns ------- - Response: The response object. + Response: A requests.Response object. """ url = urls.append_path(self.config.url, path) @@ -153,15 +184,15 @@ def put(self, path: str, **kwargs) -> Response: def patch(self, path: str, **kwargs) -> Response: """ - Send a PATCH request to the specified path. + Send a PATCH request. Args: - path (str): The path to send the request to. - **kwargs: Additional keyword arguments to be passed to the underlying session's `patch` method. + path (str): Appended to the configured base url. + **kwargs: Additional keyword arguments passed to requests.Session.patch. Returns ------- - Response: The response object. + Response: A requests.Response object. """ url = urls.append_path(self.config.url, path) @@ -169,15 +200,15 @@ def patch(self, path: str, **kwargs) -> Response: def delete(self, path: str, **kwargs) -> Response: """ - Send a DELETE request to the specified path. + Send a DELETE request. Args: - path (str): The path to send the request to. - **kwargs: Additional keyword arguments to be passed to the underlying session's `delete` method. + path (str): Appended to the configured base url. + **kwargs: Additional keyword arguments passed to requests.Session.delete. Returns ------- - Response: The response object. + Response: A requests.Response object. """ url = urls.append_path(self.config.url, path) diff --git a/src/posit/connect/config.py b/src/posit/connect/config.py index f7142277..c82c4260 100644 --- a/src/posit/connect/config.py +++ b/src/posit/connect/config.py @@ -1,3 +1,5 @@ +"""Client configuration.""" + import os from typing import Optional @@ -6,15 +8,17 @@ def _get_api_key() -> str: - """Gets the API key from the environment variable 'CONNECT_API_KEY'. + """Return the system configured api key. + + Reads the environment variable 'CONNECT_API_KEY'. Raises ------ - ValueError: if CONNECT_API_KEY is not set or invalid + ValueError: If CONNECT_API_KEY is not set or invalid Returns ------- - The API key + str """ value = os.environ.get("CONNECT_API_KEY") if not value: @@ -25,17 +29,17 @@ def _get_api_key() -> str: def _get_url() -> str: - """Gets the endpoint from the environment variable 'CONNECT_SERVER'. + """Return the system configured url. - The `requests` library uses 'endpoint' instead of 'server'. We will use 'endpoint' from here forward for consistency. + Reads the environment variable 'CONNECT_SERVER'. Raises ------ - ValueError: if CONNECT_SERVER is not set or invalid. + ValueError: If CONNECT_SERVER is not set or invalid Returns ------- - The endpoint. + str """ value = os.environ.get("CONNECT_SERVER") if not value: @@ -46,7 +50,7 @@ def _get_url() -> str: class Config: - """Derived configuration properties""" + """Configuration object.""" def __init__( self, api_key: Optional[str] = None, url: Optional[str] = None diff --git a/src/posit/connect/content.py b/src/posit/connect/content.py index eee60274..16d132e3 100644 --- a/src/posit/connect/content.py +++ b/src/posit/connect/content.py @@ -1,3 +1,5 @@ +"""Provides the Content resource interface.""" + from __future__ import annotations from typing import List, Optional, overload @@ -12,6 +14,19 @@ class ContentItem(Resource): + """A piece of content. + + Parameters + ---------- + Resource : _type_ + _description_ + + Returns + ------- + _type_ + _description_ + """ + @property def guid(self) -> str: return self.get("guid") # type: ignore diff --git a/src/posit/connect/external/databricks.py b/src/posit/connect/external/databricks.py index df4044ec..186dce0e 100644 --- a/src/posit/connect/external/databricks.py +++ b/src/posit/connect/external/databricks.py @@ -12,8 +12,9 @@ # https://github.com/databricks/databricks-sql-python/blob/v3.1.0/src/databricks/sql/auth/authenticators.py # In order to keep compatibility with the Databricks SDK class CredentialsProvider(abc.ABC): - """CredentialsProvider is the protocol (call-side interface) - for authenticating requests to Databricks REST APIs + """Protocol Databricks authentication. + + A call-side interface for the Databricks Rest API. """ @abc.abstractmethod