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

Proposed by Massimiliano Girardi
Status: Merged
Merge reported by: Bryce Harrington
Merged at revision: fcde0dbd81222edc71971caefa268e54ed7a5d60
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 Approve
Canonical Server Reporter Pending
Review via email: mp+443897@code.launchpad.net

This proposal supersedes a proposal from 2023-05-29.

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 : Posted in a previous version of this proposal

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
Revision history for this message
Massimiliano Girardi (hook25) wrote : Posted in a previous version of this proposal

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))

Revision history for this message
Bryce Harrington (bryce) wrote :

Looks great, I've squashed the updates and pushed to the main branch:

To git+ssh://git.launchpad.net/ppa-dev-tools
   9dbaa6f..33b9284 main -> main

review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches