Skip to content

Commit 7d562d5

Browse files
committed
docs: Add documentation and lint
1 parent 4cada00 commit 7d562d5

File tree

6 files changed

+62
-20
lines changed

6 files changed

+62
-20
lines changed

src/artifacts-helper/codespaces_artifacts_helper_keyring/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ indent-width = 4
5353

5454
[tool.ruff.lint]
5555
extend-select = ["B", "C4", "D", "E", "F", "I", "W", "Q"]
56+
ignore = ["D102"]
5657

5758
[tool.ruff.lint.pydocstyle]
5859
convention = "google"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
"""Provides a keyring backend for the Codespaces Artifacts Helper."""
2+
13
from .artifacts_helper_credential_provider import ArtifactsHelperCredentialProvider
24
from .keyring_backend import CodespacesArtifactsHelperKeyringBackend

src/artifacts-helper/codespaces_artifacts_helper_keyring/src/codespaces_artifacts_helper_keyring/artifacts_helper_credential_provider.py

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
# --------------------------------------------------------------------------------------------
2-
# Copyright (c) Microsoft Corporation. All rights reserved.
3-
# Licensed under the MIT License. See LICENSE in the project root for license information.
4-
# --------------------------------------------------------------------------------------------
1+
"""Wrapper to interface with the artifacts authentication helper."""
52

63
from __future__ import absolute_import
74

@@ -18,38 +15,80 @@
1815

1916
@dataclass
2017
class Credentials:
18+
"""A set of credentials, consisting of a username and password."""
19+
2120
username: str
2221
password: str
2322

2423

2524
class ArtifactsHelperCredentialProviderError(RuntimeError):
26-
pass
25+
"""Generic error for ArtifactsHelperCredentialProvider."""
2726

2827

2928
class ArtifactsHelperCredentialProvider:
29+
"""A wrapper retrieve credentials from the artifacts authentication helper.
30+
31+
The authentication helper should be installed from
32+
https://github.com/microsoft/ado-codespaces-auth.
33+
34+
Attributes:
35+
DEFAULT_AUTH_HELPER_PATH: The default path to the authentication helper
36+
executable.
37+
38+
Raises:
39+
ArtifactsHelperCredentialProviderError: When the credentials could not be
40+
retrieved.
41+
"""
42+
3043
DEFAULT_AUTH_HELPER_PATH = "~/ado-auth-helper"
3144

3245
def __init__(
3346
self,
3447
auth_helper_path: Union[os.PathLike, str] = DEFAULT_AUTH_HELPER_PATH,
3548
timeout: int = 30,
3649
):
50+
"""Initialise the provider.
51+
52+
Args:
53+
auth_helper_path: The path to the authentication helper executable, or the
54+
name of the executable if it is in the PATH. Defaults to
55+
DEFAULT_AUTH_HELPER_PATH.
56+
57+
timeout: The timeout in seconds for calling the authentication helper and
58+
any HTTP requests made to test credentials. Defaults to 30.
59+
"""
3760
self.auth_tool_path = self.resolve_auth_helper_path(auth_helper_path)
3861
self.timeout = timeout
3962

4063
@staticmethod
4164
def resolve_auth_helper_path(
4265
auth_helper_path: Union[os.PathLike, str],
4366
) -> Optional[str]:
67+
"""Resolve the path to the authentication helper executable.
68+
69+
Returns:
70+
The path to the authentication helper executable, or `None` if it is not
71+
executable or not found.
72+
"""
4473
return shutil.which(str(Path(auth_helper_path).expanduser()), mode=os.X_OK)
4574

4675
@classmethod
4776
def auth_helper_installed(cls, auth_helper_path: Union[os.PathLike, str]) -> bool:
77+
"""Check whether the authentication helper is installed and executable."""
4878
return cls.resolve_auth_helper_path(auth_helper_path) is not None
4979

50-
def get_credentials(self, url) -> Optional[Credentials]:
51-
# Public feed short circuit: return nothing if not getting credentials for the upload endpoint
52-
# (which always requires auth) and the endpoint is public (can authenticate without credentials).
80+
def get_credentials(self, url: str) -> Optional[Credentials]:
81+
"""Get credentials for the given URL.
82+
83+
Args:
84+
url: The URL to retrieve credentials for.
85+
86+
Returns:
87+
The credentials for the URL, or `None` if no credentials could be retrieved.
88+
"""
89+
# Public feed short circuit: return nothing if not getting credentials for the
90+
# upload endpoint (which always requires auth) and the endpoint is public (can
91+
# authenticate without credentials).
5392
if not self._is_upload_endpoint(url) and self._can_authenticate(url, None):
5493
return None
5594

@@ -87,16 +126,19 @@ def _get_jwt_from_helper(self) -> str:
87126
return stdout.strip()
88127
else:
89128
raise ArtifactsHelperCredentialProviderError(
90-
f"Failed to get credentials: No output from subprocess {self.auth_tool_path}"
129+
"Failed to get credentials: "
130+
f"No output from subprocess {self.auth_tool_path}"
91131
)
92132

93133
except subprocess.CalledProcessError as e:
94134
raise ArtifactsHelperCredentialProviderError(
95-
f"Failed to get credentials: Process {self.auth_tool_path} exited with code {e.returncode}. Error: {e.stderr}"
135+
f"Failed to get credentials: Process {self.auth_tool_path} exited with "
136+
f"code {e.returncode}. Error: {e.stderr}"
96137
) from e
97138
except subprocess.TimeoutExpired as e:
98139
raise ArtifactsHelperCredentialProviderError(
99-
f"Failed to get credentials: Process {self.auth_tool_path} timed out after {self.timeout} seconds"
140+
f"Failed to get credentials: Process {self.auth_tool_path} timed out "
141+
f"after {self.timeout} seconds"
100142
) from e
101143

102144
def _get_credentials_from_jwt(self, jwt_str: str) -> Credentials:

src/artifacts-helper/codespaces_artifacts_helper_keyring/src/codespaces_artifacts_helper_keyring/keyring_backend.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Keyring backend for Codespaces Artifacts Helper."""
2+
13
import warnings
24
from typing import Optional, Type
35
from urllib.parse import urlsplit
@@ -10,6 +12,8 @@
1012

1113

1214
class CodespacesArtifactsHelperKeyringBackend(KeyringBackend):
15+
"""A keyring backend for Azure Artifacts using the ADO Codespaces Auth Helper."""
16+
1317
SUPPORTED_NETLOC = (
1418
"pkgs.dev.azure.com",
1519
"pkgs.visualstudio.com",

src/artifacts-helper/codespaces_artifacts_helper_keyring/tests/test_artifacts_helper_credential_provider.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@
55
from typing import Optional, Union
66

77
import pytest
8-
from codespaces_artifacts_helper_keyring import (
9-
ArtifactsHelperCredentialProvider,
10-
)
8+
from codespaces_artifacts_helper_keyring import ArtifactsHelperCredentialProvider
119
from codespaces_artifacts_helper_keyring.artifacts_helper_credential_provider import (
1210
ArtifactsHelperCredentialProviderError,
1311
)
1412

1513

1614
class TestArtifactsHelperWrapper(unittest.TestCase):
1715
SUPPORTED_HOST = "https://pkgs.dev.azure.com/"
18-
TEST_JWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cG4iOiJ1cG5AY29udG9zby5jb20iLCJ1bmlxdWVfbmFtZSI6Im5hbWVAY29udG9zby5jb20ifQ.srKYrr5B0i29XERHsvE6mqZpLBzyyrX-gUKe9OHZODw"
16+
TEST_JWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cG4iOiJ1cG5AY29udG9zby5jb20iLCJ1bmlxdWVfbmFtZSI6Im5hbWVAY29udG9zby5jb20ifQ.srKYrr5B0i29XERHsvE6mqZpLBzyyrX-gUKe9OHZODw" # noqa: E501
1917
TEST_JWT_USERNAME = "name@contoso.com"
2018

2119
def __init__(self, *args, **kwargs) -> None:

src/artifacts-helper/codespaces_artifacts_helper_keyring/tests/test_keyring_backend.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
# --------------------------------------------------------------------------------------------
2-
# Copyright (c) Microsoft Corporation. All rights reserved.
3-
# Licensed under the MIT License. See License.txt in the project root for license information.
4-
# --------------------------------------------------------------------------------------------
5-
61
import keyring
72
import keyring.backend
83
import keyring.backends.chainer

0 commit comments

Comments
 (0)