Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: adds linting for docstrings #136

Merged
merged 2 commits into from
Mar 27, 2024
Merged
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
1 change: 0 additions & 1 deletion examples/connect/dash/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ def update_page(_):
Dash example application that shows user information and
the first few rows from a table hosted in Databricks.
"""

session_token = flask.request.headers.get("Posit-Connect-User-Session-Token")
credentials_provider = viewer_credentials_provider(user_session_token=session_token)

Expand Down
1 change: 0 additions & 1 deletion examples/connect/shiny-python/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def server(input: Inputs, output: Outputs, session: Session):
Shiny for Python example application that shows user information and
the first few rows from a table hosted in Databricks.
"""

session_token = session.http_conn.headers.get("Posit-Connect-User-Session-Token")
credentials_provider = viewer_credentials_provider(user_session_token=session_token)

Expand Down
47 changes: 41 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,54 @@ 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"
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"

[tool.ruff.format]
docstring-code-format = true
docstring-code-line-length = 20

[tool.ruff.lint]
select = ["D"]
ignore = [
# 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 [quarotdoc](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"
1 change: 1 addition & 0 deletions src/posit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""The Posit SDK."""
5 changes: 5 additions & 0 deletions src/posit/connect/auth.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
"""Provides authentication functionality."""

from requests import PreparedRequest
from requests.auth import AuthBase

from .config import Config


class Auth(AuthBase):
"""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
111 changes: 74 additions & 37 deletions src/posit/connect/client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Contains the Client class."""

from __future__ import annotations

from requests import Response, Session
Expand All @@ -13,6 +15,8 @@


class Client:
"""Main interface for Posit Connect."""

def __init__(
self,
api_key: Optional[str] = None,
Expand Down Expand Up @@ -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).
Expand All @@ -89,89 +118,97 @@ 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.
Returns
-------
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.
Returns
-------
Response: A requests.Response object.

"""
url = urls.append_path(self.config.url, path)
return self.session.get(url, **kwargs)

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.
Returns
-------
Response: A requests.Response object.

"""
url = urls.append_path(self.config.url, path)
return self.session.post(url, **kwargs)

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.
Returns
-------
Response: A requests.Response object.

"""
url = urls.append_path(self.config.url, path)
return self.session.put(url, **kwargs)

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.
Returns
-------
Response: A requests.Response object.

"""
url = urls.append_path(self.config.url, path)
return self.session.patch(url, **kwargs)

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.
Returns
-------
Response: A requests.Response object.

"""
url = urls.append_path(self.config.url, path)
Expand Down
32 changes: 20 additions & 12 deletions src/posit/connect/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Client configuration."""

import os

from typing import Optional
Expand All @@ -6,13 +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
Raises
------
ValueError: If CONNECT_API_KEY is not set or invalid

Returns:
The API key
Returns
-------
str
"""
value = os.environ.get("CONNECT_API_KEY")
if not value:
Expand All @@ -23,15 +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.
Raises
------
ValueError: If CONNECT_SERVER is not set or invalid

Returns:
The endpoint.
Returns
-------
str
"""
value = os.environ.get("CONNECT_SERVER")
if not value:
Expand All @@ -42,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
Expand Down
Loading
Loading