Merge ~hook25/ppa-dev-tools:support_env_credentials into ppa-dev-tools:main

Proposed by Massimiliano Girardi
Status: Superseded
Proposed branch: ~hook25/ppa-dev-tools:support_env_credentials
Merge into: ppa-dev-tools:main
Diff against target: 117 lines (+65/-5)
2 files modified
ppa/lp.py (+37/-4)
tests/test_lp.py (+28/-1)
Reviewer Review Type Date Requested Status
Bryce Harrington Needs Fixing
Canonical Server Reporter Pending
Review via email: mp+443747@code.launchpad.net

This proposal has been superseded by a proposal from 2023-05-31.

Commit message

Add LP_CREDENTIALS login and documentation

Description of the change

This adds the possibility of running ppa-dev-tools automatically by making it retrieve the credentials for Launchpad from the LP_CREDENTIALS environment variable and documents another way to do so via the LP_CREDENTIALS_FILE environment variable

To post a comment you must log in.
Revision history for this message
Bryce Harrington (bryce) wrote :

Good addition, a few nitpicks below but otherwise LGTM.

Other than a couple minor flake8 issues, `make check` passes.

Would you be willing to write a test case to go along with this? We have test_new_connection() that verifies that the login_with() call gets invoked, what we'd need is something analogous to that but that sets the environment variable and verifies that Credentials.from_string() gets invoked instead, and login_with() does NOT get invoked.

review: Needs Fixing
46fdbac... by Massimiliano Girardi

Added test for new login functionality

fcde0db... by Massimiliano Girardi

Minor tweaks to naming and docstrings

Revision history for this message
Massimiliano Girardi (hook25) wrote :

I have a few replies to your comments and implemented your feedback where it was necessary (namely, new test, fixed naming of internal functions, removed useless proxy to (envvar or login))

Unmerged commits

fcde0db... by Massimiliano Girardi

Minor tweaks to naming and docstrings

46fdbac... by Massimiliano Girardi

Added test for new login functionality

fb75733... by Massimiliano Girardi

Add LP_CREDENTIALS login and documentation

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/ppa/lp.py b/ppa/lp.py
2index 17b225a..9149810 100644
3--- a/ppa/lp.py
4+++ b/ppa/lp.py
5@@ -14,11 +14,12 @@
6 High level wrapper object for Launchpad's API
7 """
8
9+import os
10 from contextlib import suppress
11 from functools import lru_cache
12
13 from launchpadlib.launchpad import Launchpad
14-
15+from launchpadlib.credentials import Credentials
16
17 class Lp:
18 """
19@@ -52,8 +53,38 @@ class Lp:
20 else:
21 self._service_root = 'production'
22
23- def _get_instance(self):
24- """Authenticate to Launchpad."""
25+ def _get_instance_from_env(self) -> 'launchpadlib.launchpad.Launchpad | None':
26+ """
27+ Get an instance of _service using `$LP_CREDENTIAL` environment
28+ variable if it is defined, else return None
29+
30+ :rtype: launchpadlib.launchpad.Launchpad | None
31+ :returns: Logged in Launchpad instance if LP_CREDENTIALS is in env
32+ else None
33+ """
34+ lp_cred = os.getenv("LP_CREDENTIALS")
35+ if lp_cred:
36+ cred = Credentials.from_string(lp_cred)
37+ return self._service(
38+ cred, None, None,
39+ service_root=self._service_root,
40+ version='devel'
41+ )
42+ return None
43+
44+ def _get_instance_from_login(self) -> 'launchpadlib.launchpad.Launchpad':
45+ """
46+ Prompts the user to authorize the login of a new credential
47+ or use the cached one if it is available and valid
48+
49+ Note: This uses Launchpad.login_with if service is left to
50+ default, if you have a credential file you can provide
51+ the path to it via the LP_CREDENTIALS_FILE environment
52+ variable and this call will pass without prompting the
53+ user
54+ :rtype: launchpadlib.launchpad.Launchpad
55+ :returns: Logged in Launchpad instance
56+ """
57 return self._service.login_with(
58 application_name=self._app_name,
59 service_root=self._service_root,
60@@ -65,7 +96,9 @@ class Lp:
61 def _instance(self):
62 """Cache LP object."""
63 if not self._real_instance:
64- self._real_instance = self._get_instance()
65+ self._real_instance = (
66+ self._get_instance_from_env() or self._get_instance_from_login()
67+ )
68 return self._real_instance
69
70 @property
71diff --git a/tests/test_lp.py b/tests/test_lp.py
72index 4fe1410..a0c1546 100644
73--- a/tests/test_lp.py
74+++ b/tests/test_lp.py
75@@ -34,7 +34,7 @@ import os.path
76
77 import logging
78 import pytest
79-from mock import Mock
80+from mock import Mock, patch
81 from launchpadlib.launchpad import Launchpad
82
83 sys.path.insert(0, os.path.realpath(
84@@ -75,6 +75,33 @@ def test_new_connection():
85 version='devel',
86 )
87
88+@patch.dict(os.environ, {"LP_CREDENTIALS" : "..."})
89+@patch("ppa.lp.Credentials")
90+def test_new_connection_envvar(credentials_mock):
91+ """Verifies the Lp object will login via envvar if provided."""
92+ mock_launchpad = Mock(spec=Launchpad)
93+
94+ lp = Lp(
95+ application_name=APPNAME,
96+ service=mock_launchpad)
97+
98+ # Cause the lp._instance() internal property to be triggered
99+ logging.debug(lp.me)
100+ logging.debug(lp.people)
101+
102+ # Verify the triggering did not result in a login attempt
103+ mock_launchpad.login_with.assert_not_called()
104+ # Rather that the login was done via envvar
105+ # creating the credentials
106+ credentials_mock.from_string.assert_called_once()
107+ # and the service was created with the given credentials
108+ mock_launchpad.assert_called_once_with(
109+ credentials_mock.from_string.return_value,
110+ None,
111+ None,
112+ service_root="production",
113+ version="devel"
114+ )
115
116 def test_api_root(fakelp):
117 """Ensures we can get LP's API root."""

Subscribers

People subscribed via source and target branches