Merge lp:~leonardr/launchpadlib/bug-714043 into lp:launchpadlib

Proposed by Leonard Richardson
Status: Rejected
Rejected by: Martin Pool
Proposed branch: lp:~leonardr/launchpadlib/bug-714043
Merge into: lp:launchpadlib
Diff against target: 169 lines (+59/-39)
3 files modified
src/launchpadlib/NEWS.txt (+13/-0)
src/launchpadlib/__init__.py (+1/-1)
src/launchpadlib/launchpad.py (+45/-38)
To merge this branch: bzr merge lp:~leonardr/launchpadlib/bug-714043
Reviewer Review Type Date Requested Status
Martin Pool (community) Disapprove
Review via email: mp+48842@code.launchpad.net

Description of the change

This branch fixes bug 71403 with two little bits of code:

1. If you call login_with() without specifying a service_root, you get 'staging', but you also get a warning. This lets you avoid mistakes in your code, but makes it obvious what you need to do if you want your changes to persist.

2. If you call login_anonymously() without specifying a service_root, you get 'production'.

Martin, let me know if you like this solution.

To post a comment you must log in.
Revision history for this message
Martin Pool (mbp) wrote :

Hi Leonard, thanks for picking this up.

Can we think of any case in which the user would thank us for defaulting to talking to staging? I think the only case would be if they start up a client and make a lot of changes just assuming it has no effect. I don't know why someone would think that; fairly obviously the library is for talking to Launchpad and has the effect of manipulating objects in Launchpad.

If somebody is randomly poking things, they'll be doing so under their own account and they'll suffer the consequences.

If we default to staging then the first thing people are likely to do is to override it to talk to the real site. People want to write code that actually manipulates Launchpad. If they have bugs, those bugs are going to affect the real lp.

Consistent behaviour between anonymous and authenticated access is desirable, all else being equal.

So, I think it should just default to production.

We can look for precedents in other libraries. I've never encountered one that doesn't talk to the production site by default.

Revision history for this message
Leonard Richardson (leonardr) wrote :

I guess it depends on the development style. When I write a launchpadlib script, I run it against staging until it's debugged, and the last thing I do is switch it to production. This branch would kind of annoy me, but I'd work around it by explicitly passing in service_root='staging' to suppress the warning. If someone else's style is to switching to production immediately, under this branch they'll at least be reminded that they need to do that.

"If somebody is randomly poking things, they'll be doing so under their own account and they'll suffer the consequences." "If they have bugs, those bugs are going to affect the real lp." You're saying these things in a very matter-of-fact way, but it doesn't have to be this way. These are reasons for not defaulting to staging.

I'm not aware of any service-specific clients that can talk to staging servers *at all*, but I'm not up to date on these things. Most web service providers don't publish their staging servers.

I'm happy to put this up for a vote on launchpad-users.

Revision history for this message
Robert Collins (lifeless) wrote :

I think gathering some user input would be good; perhaps a -users
thread as you suggest.

Revision history for this message
Martin Pool (mbp) wrote :

On 9 February 2011 08:19, Robert Collins <email address hidden> wrote:
> I think gathering some user input would be good; perhaps a -users
> thread as you suggest.

+1, maybe also forward it to canonical-tech.

Revision history for this message
Martin Pool (mbp) wrote :

We did have a thread, in which there was a mix of people agreeing it was confusing or annoying, and people saying they think it is nice to have a "safe" mode by default.

Also, since this was first proposed, staging has been down a lot (whereas lpnet has generally been quite reliable), and it seems to me it is definitely moving towards being a non-production-quality internal-use system, and not something users ought to rely on.

(In the interim, Leonard has moved on to other projects and I'm not sure he'll chase this up, but I'm going to comment here for other people who might look at the bug or mp. I may pick this up myself eventually.)

So my overall vote, for what it's worth, is to reject this and to make lplib actually default to the real service. Clients must write code that is adequately safe when it's actually used.

review: Disapprove
Revision history for this message
Martin Pool (mbp) wrote :

For the sake of getting this off my queue and making a decision, I'm going to reject it. If someone else wants to pick it up, or even repropose this branch, please do. I hope we still fix this bug.

Unmerged revisions

117. By Leonard Richardson

Added NEWS.

116. By Leonard Richardson

Initial implementation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/launchpadlib/NEWS.txt'
--- src/launchpadlib/NEWS.txt 2011-01-18 21:57:09 +0000
+++ src/launchpadlib/NEWS.txt 2011-02-07 20:53:52 +0000
@@ -2,6 +2,19 @@
2NEWS for launchpadlib2NEWS for launchpadlib
3=====================3=====================
44
51.9.5 (2010-02-07)
6==================
7
8- Fixed a bug that prevented the deprecated get_token_and_login code
9 from working, and required that users of get_token_and_login get a
10 new token on every usage.
11
12- Added a warning to users when their code runs against staging and
13 they might not realize it.
14
15- By default, anonymous read-only access runs against the production
16 server, since there's no chance of the client making mistakes.
17
51.9.4 (2010-01-18)181.9.4 (2010-01-18)
6==================19==================
720
821
=== modified file 'src/launchpadlib/__init__.py'
--- src/launchpadlib/__init__.py 2011-01-18 22:33:53 +0000
+++ src/launchpadlib/__init__.py 2011-02-07 20:53:52 +0000
@@ -14,4 +14,4 @@
14# You should have received a copy of the GNU Lesser General Public License14# You should have received a copy of the GNU Lesser General Public License
15# along with launchpadlib. If not, see <http://www.gnu.org/licenses/>.15# along with launchpadlib. If not, see <http://www.gnu.org/licenses/>.
1616
17__version__ = '1.9.4'17__version__ = '1.9.5'
1818
=== modified file 'src/launchpadlib/launchpad.py'
--- src/launchpadlib/launchpad.py 2011-01-18 21:57:09 +0000
+++ src/launchpadlib/launchpad.py 2011-02-07 20:53:52 +0000
@@ -31,6 +31,7 @@
31 ScalarValue, # Re-import for client convenience31 ScalarValue, # Re-import for client convenience
32 ServiceRoot,32 ServiceRoot,
33 )33 )
34from lazr.restfulclient.authorize.oauth import SystemWideConsumer
34from lazr.restfulclient._browser import RestfulHttp35from lazr.restfulclient._browser import RestfulHttp
35from launchpadlib.credentials import (36from launchpadlib.credentials import (
36 AccessToken,37 AccessToken,
@@ -309,16 +310,18 @@
309 get_token_and_login() is removed, this code can be streamlined310 get_token_and_login() is removed, this code can be streamlined
310 and moved into its other call site, login_with().311 and moved into its other call site, login_with().
311 """312 """
312
313 if isinstance(consumer_name, Consumer):313 if isinstance(consumer_name, Consumer):
314 # Create the credentials with no Consumer, then set its .consumer314 consumer = consumer_name
315 # property directly.
316 credentials = Credentials(None)
317 credentials.consumer = consumer_name
318 else:315 else:
319 # Have the Credentials constructor create the Consumer316 # Create a system-wide consumer. lazr.restfulclient won't
320 # automatically.317 # do this automatically, but launchpadlib's default is to
321 credentials = Credentials(consumer_name)318 # do a desktop-wide integration.
319 consumer = SystemWideConsumer(consumer_name)
320
321 # Create the credentials with no Consumer, then set its .consumer
322 # property directly.
323 credentials = Credentials(None)
324 credentials.consumer = consumer
322 if authorization_engine is None:325 if authorization_engine is None:
323 authorization_engine = cls.authorization_engine_factory(326 authorization_engine = cls.authorization_engine_factory(
324 service_root, consumer_name, None, allow_access_levels)327 service_root, consumer_name, None, allow_access_levels)
@@ -335,13 +338,30 @@
335 credential_store.credential_save_failed,338 credential_store.credential_save_failed,
336 "credential_store")339 "credential_store")
337340
338 credentials = authorization_engine(credentials, credential_store)341 # Try to get the credentials out of the credential store.
342 cached_credentials = credential_store.load(
343 authorization_engine.unique_consumer_id)
344 if cached_credentials is None:
345 # They're not there. Acquire new credentials using the
346 # authorization engine.
347 credentials = authorization_engine(credentials, credential_store)
348 else:
349 # We acquired credentials. But, the application name
350 # wasn't stored along with the credentials, because in a
351 # desktop integration scenario, a single set of
352 # credentials may be shared by many applications. We need
353 # to set the application name for this specific instance
354 # of the credentials.
355 credentials = cached_credentials
356 credentials.consumer.application_name = (
357 authorization_engine.application_name)
358
339 return cls(credentials, authorization_engine, credential_store,359 return cls(credentials, authorization_engine, credential_store,
340 service_root, cache, timeout, proxy_info, version)360 service_root, cache, timeout, proxy_info, version)
341361
342 @classmethod362 @classmethod
343 def login_anonymously(363 def login_anonymously(
344 cls, consumer_name, service_root=uris.STAGING_SERVICE_ROOT,364 cls, consumer_name, service_root=uris.LPNET_SERVICE_ROOT,
345 launchpadlib_dir=None, timeout=None, proxy_info=None,365 launchpadlib_dir=None, timeout=None, proxy_info=None,
346 version=DEFAULT_VERSION):366 version=DEFAULT_VERSION):
347 """Get access to Launchpad without providing any credentials."""367 """Get access to Launchpad without providing any credentials."""
@@ -355,7 +375,7 @@
355375
356 @classmethod376 @classmethod
357 def login_with(cls, application_name=None,377 def login_with(cls, application_name=None,
358 service_root=uris.STAGING_SERVICE_ROOT,378 service_root=None,
359 launchpadlib_dir=None, timeout=None, proxy_info=None,379 launchpadlib_dir=None, timeout=None, proxy_info=None,
360 authorization_engine=None, allow_access_levels=None,380 authorization_engine=None, allow_access_levels=None,
361 max_failed_attempts=None, credentials_file=None,381 max_failed_attempts=None, credentials_file=None,
@@ -457,6 +477,15 @@
457 :rtype: `Launchpad`477 :rtype: `Launchpad`
458478
459 """479 """
480 if service_root is None:
481 service_root = uris.STAGING_SERVICE_ROOT
482 staging_warning = (
483 "Running against the Launchpad staging server. Changes you "
484 "make will be reverted, and the data you read may be "
485 "slightly out of date. To run against production, pass "
486 'service_root="production" into login_with().')
487 warnings.warn(staging_warning)
488
460 (service_root, launchpadlib_dir, cache_path,489 (service_root, launchpadlib_dir, cache_path,
461 service_root_dir) = cls._get_paths(service_root, launchpadlib_dir)490 service_root_dir) = cls._get_paths(service_root, launchpadlib_dir)
462491
@@ -513,33 +542,11 @@
513 "allow_access_levels", allow_access_levels,542 "allow_access_levels", allow_access_levels,
514 authorization_engine.allow_access_levels)543 authorization_engine.allow_access_levels)
515544
516 credentials = credential_store.load(545 return cls._authorize_token_and_login(
517 authorization_engine.unique_consumer_id)546 authorization_engine.consumer, service_root,
518547 cache_path, timeout, proxy_info, authorization_engine,
519 if credentials is None:548 allow_access_levels, credential_store,
520 # Credentials were not found in the local store. Go549 credential_save_failed, version)
521 # through the authorization process.
522 launchpad = cls._authorize_token_and_login(
523 authorization_engine.consumer, service_root,
524 cache_path, timeout, proxy_info, authorization_engine,
525 allow_access_levels, credential_store,
526 credential_save_failed, version)
527 else:
528 # The application name wasn't stored locally, because in a
529 # desktop integration scenario, a single set of
530 # credentials may be shared by many applications. We need
531 # to set the application name for this specific instance
532 # of the credentials.
533 credentials.consumer.application_name = (
534 authorization_engine.application_name)
535
536 # Credentials were found in the local store. Create a
537 # Launchpad object.
538 launchpad = cls(
539 credentials, authorization_engine, credential_store,
540 service_root=service_root, cache=cache_path, timeout=timeout,
541 proxy_info=proxy_info, version=version)
542 return launchpad
543550
544 @classmethod551 @classmethod
545 def _warn_of_deprecated_login_method(cls, name):552 def _warn_of_deprecated_login_method(cls, name):

Subscribers

People subscribed via source and target branches