Merge lp:~thomir-deactivatedaccount/bzr/add-global-config into lp:bzr

Proposed by Thomi Richards
Status: Work in progress
Proposed branch: lp:~thomir-deactivatedaccount/bzr/add-global-config
Merge into: lp:bzr
Diff against target: 342 lines (+112/-27)
4 files modified
bzrlib/config.py (+46/-13)
bzrlib/tests/test_config.py (+22/-6)
bzrlib/tests/test_win32utils.py (+17/-1)
bzrlib/win32utils.py (+27/-7)
To merge this branch: bzr merge lp:~thomir-deactivatedaccount/bzr/add-global-config
Reviewer Review Type Date Requested Status
Vincent Ladeuil Needs Fixing
Jelmer Vernooij (community) Approve
Review via email: mp+69592@code.launchpad.net

Description of the change

Code not finished yet - working on making a system-wide config file (/etc/bazaar/bazaar.conf under Linux).

To post a comment you must log in.
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Things I know still need to be done:
1) to consider: rename GlobalStore to UserStore, and SystemGlobalStore to GlobalStore. no one outside bzrlib.config and associated unit tests use this class directly, so the rename should be trivial.

2) Test new windows path function.

3) System-wide config file path for Mac OS X? Haven't found any portable way to work out where this should go. Do macs have /etc/?

6048. By Thomi Richards

Fixed docstring for config.system_config_dir

Revision history for this message
Matthew Fuller (fullermd) wrote :

> (/etc/bazaar/bazaar.conf under Linux).

On BSD, there's going to be a strong preference for /usr/local/etc/.
This probably calls for it being settable somehow in the install
process at least...

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

> On BSD, there's going to be a strong preference for /usr/local/etc/.
> This probably calls for it being settable somehow in the install
> process at least...

Okay, that's fine. What's the best way to do this?

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

On 29 July 2011 04:52, Matthew Fuller <email address hidden> wrote:
>> (/etc/bazaar/bazaar.conf under Linux).
>
> On BSD, there's going to be a strong preference for /usr/local/etc/.
> This probably calls for it being settable somehow in the install
> process at least...

If you use distutils.sysconfig or similar, it should use whatever
prefix Python was compiled with.

6049. By Thomi Richards

Added unit tests for new method in win32utils.py

6050. By Thomi Richards

Added unit tests to test the new path functions for win32 and freebsd.

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

Thanks for this, Thomi.

+ elif sys.platform.startswith('freebsd'):
+ return osutils.pathjoin(sysconfig.PREFIX, 'etc', 'bazaar')
+ else:
+ return osutils.pathjoin('/etc', "bazaar")

Ideally we would get the whole path to '...etc' from Python, rather than special-casing on the platform.

But I don't actually see anything that points to /etc in sysconfig.get_config_vars() so we might need to either find some other module that tells us where it is, or just hardcode it, in which case I suppose your patch is the best thing.

The docstring also needs to be updated.

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Hi Martin,

Thanks for your feedback - I'll update the docstring and try and find some programmatic way to get the /etc/ path.

I also need to work out if my unit tests pass under Windows. Is there a CI server that can run these tests for me?

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

+class SystemGlobalStore(LockableIniFileStore):
+
+ def __init__(self, possible_transports=None):
+ t = transport.get_transport_from_path(system_config_dir(),
+ possible_transports=possible_transports)
+ super(SystemGlobalStore, self).__init__(t, 'bazaar.conf')

The point of passing possible_transports is basically so that we can reuse some connection objects, and that's mostly important for remote transports, which this won't be at present. I think the current approach of manually passing it down is not working very well and we should probably look at putting it into the library state instead.

[tweak] However, for this patch, I don't think it's either used or particularly useful (since making new local transports is cheap), so you could just remove the parameter.

[suggestion] I'm not a big fan of creating new classes that only have different initial state, not different behaviour, so I would probably just make this a factory method.

Aside from that, this looks great. It could be good to get a review from vila too.

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

On 2 August 2011 12:51, Thomi Richards <email address hidden> wrote:
> Hi Martin,
>
> Thanks for your feedback - I'll update the docstring and try and find some programmatic way to get the /etc/ path.
>
> I also need to work out if my unit tests pass under Windows. Is there a CI server that can run these tests for me?

There is Babune <http://babune.ladeuil.net:24842/> which will catch
any post-landing regressions, but I don't think it is yet able to test
arbitrary unlanded branches.

I have Wine, Python installed under that, and then just a script
'winepython' that does

exec wine ~/.wine/drive_c/Python26/python.exe "$@"

and then I can say 'winepython ./bzr selftest config' etc.

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

> +class SystemGlobalStore(LockableIniFileStore):
> +
> + def __init__(self, possible_transports=None):
> + t = transport.get_transport_from_path(system_config_dir(),
> + possible_transports=possible_transports)
> + super(SystemGlobalStore, self).__init__(t, 'bazaar.conf')
>
> The point of passing possible_transports is basically so that we can reuse
> some connection objects, and that's mostly important for remote transports,
> which this won't be at present. I think the current approach of manually
> passing it down is not working very well and we should probably look at
> putting it into the library state instead.
>
> [tweak] However, for this patch, I don't think it's either used or
> particularly useful (since making new local transports is cheap), so you could
> just remove the parameter.

That sounds sensible. In fact, there's not a single place inside all of bzrlib where a transport is passed in. I've taken the liberty of removing that parameter from all the Store classes. If that was the wrong thing to do, please let me know.

>
> [suggestion] I'm not a big fan of creating new classes that only have
> different initial state, not different behaviour, so I would probably just
> make this a factory method.
>

That also sounds sensible. Since the Stores are an internal API (we want people to be using the Stack classes, right?), I guess I should follow naming convention and call these "_get_global_store" etc?

Also, I've renamed the GlobalStore to UserStore, and the "SystemGlobalStore" to just "GlobalStore", except that after the change above none of these will be visible, since they'll all come out of factory functions.

BranchStore is a bit more difficult, as it uses the branch lock for locking. I could keep the BranchStore class as-is, or I could wrap that up inside a factory function as well, which would make everything look a lot neater, but I'm not sure what your coding standards are regarding nested classes....

Finally, the win32utils unit tests fail, so I have some work to do there...

6051. By Thomi Richards

Several changes to clean up global config work:
 * Store classes (GlobalStore et al) are now factory functions.
 * What was previously called a "Global store" is now called a "User Store".
 * similarly, what was the "System Global Store" is now just the "Global Store"

6052. By Thomi Richards

Forgot to update docstring for system_config_dir()

6053. By Thomi Richards

Windows version 6 (WIndows 7, Server 2008) puts the common app data folder in a different place. Updated docstrings and fixed unit test.

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Unit tests now pass on Windows 7. I don't have access to anything older. However, it's a little inconvenient that the common appdata folder changes between windows versions. Perhaps a viable alternative would be to put the global app config file in the bzr installation directory under windows?

I'm not sure how that would work with non-standalone installations...

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

> That also sounds sensible. Since the Stores are an internal API (we want people to be using the Stack classes, right?), I guess I should follow naming convention and call these "_get_global_store" etc?
>
> Also, I've renamed the GlobalStore to UserStore, and the "SystemGlobalStore" to just "GlobalStore", except that after the change above none of these will be visible, since they'll all come out of factory functions.
>
> BranchStore is a bit more difficult, as it uses the branch lock for locking. I could keep the BranchStore class as-is, or I could wrap that up inside a factory function as well, which would make everything look a lot neater, but I'm not sure what your coding standards are regarding nested classes....
>
> Finally, the win32utils unit tests fail, so I have some work to do there...

On naming:

We definitely shouldn't change the meaning of an existing name like
GlobalStore, even if it's not intended to be exposed. It's much
better for out-of-date code to get eg a NameError than to run the
wrong thing.

If the class really has actually different behaviour, like
BranchStore, it deserves to be a real different class. But if it's
basically the same class just with different constructor parameters, I
wonder if it's worthwhile. Perhaps for simplicity it is better just
to leave them all as classes. I may be getting a bit bikesheddy here
so don't block on it.

m

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Hi Michael,

I'd like to get this right, so I'll probably bug you later this evening if I get time...

> On naming:
>
> We definitely shouldn't change the meaning of an existing name like
> GlobalStore, even if it's not intended to be exposed. It's much
> better for out-of-date code to get eg a NameError than to run the
> wrong thing.
>

The branch as it currently stands will give you that behavior. Nobody should be creating the Store classes directly (nobody is within bzrlib, I don't know about external projects), and now if they try they'll get a NameError.

> If the class really has actually different behaviour, like
> BranchStore, it deserves to be a real different class. But if it's
> basically the same class just with different constructor parameters, I
> wonder if it's worthwhile.
So how about this, for the branch store: replace the BranchStore class with:

def _get_branch_store(branch):
    class BranchStore(IniStore)
        # otherstuff in here from BranchStore class
    return BranchStore(branch)

This has the advantage that: A) it keeps it inline with the other factory functions. B) it hides the different implementation of the BranchStore class, and marks the BranchStore object as being a private thing that other code shouldn't use.

> Perhaps for simplicity it is better just
> to leave them all as classes. I may be getting a bit bikesheddy here
> so don't block on it.
>
Well, I'd like to get it right - I'm preparing to do some more things with sloecode & Bazaar, so learning the bzr code is helpful to me.

What do you think?

6054. By Thomi Richards

Hid BranchStore class behind _get_branch_store() factory function.

Revision history for this message
Vincent Ladeuil (vila) wrote :

Thanks for working on this !

Did you read http://doc.bazaar.canonical.com/devnotes/configuration.html which describes the current implementation, the stack-based implementation and the steps needed to fully deploy it and then the additional features we want to take into account ? This will probably clarifies some of the issues your encountered so far.

> Things I know still need to be done:
> 1) to consider: rename GlobalStore to UserStore, and SystemGlobalStore to
> GlobalStore. no one outside bzrlib.config and associated unit tests use this
> class directly, so the rename should be trivial.

Two remarks:
1 - No one use them because these classes are there to help the transition to stack based configs which is not finished yet.
2 - GlobalStore is targeted at bazaar.conf, UserStore will probably use user.conf and SystemStore may use /etc/bazaar.conf or whatever we decide.

I didn't introduce the system-wide config file because I wanted to minimize the migration issues (section names, section matching, etc), see the Compatibility section in the URL above.

>
> 2) Test new windows path function.
>
> 3) System-wide config file path for Mac OS X? Haven't found any portable way
> to work out where this should go. Do macs have /etc/?

Revision history for this message
Vincent Ladeuil (vila) wrote :

>
> > Perhaps for simplicity it is better just
> > to leave them all as classes. I may be getting a bit bikesheddy here
> > so don't block on it.

+1 on leaving them as classes. I think the plan is to use a registry rather than factory methods which is also why I made all the classes use transports whether they were using local files or not.

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Hi Vincent,

On 5 August 2011 04:13, Vincent Ladeuil <email address hidden> wrote:
>
> +1 on leaving them as classes. I think the plan is to use a registry rather than factory methods which is also why I made all the classes use transports whether they were using local files or not.
>

I'll revert them back to classes, and re-instate the
possible_transports parameter.

--
Thomi Richards

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Hi Vincent,

On 5 August 2011 04:09, Vincent Ladeuil <email address hidden> wrote:
> Did you read http://doc.bazaar.canonical.com/devnotes/configuration.html which describes the current implementation, the stack-based implementation and the steps needed to fully deploy it and then the additional features we want to take into account ? This will probably clarifies some of the issues your encountered so far.

No, I didn't see that. It certainly clears up a lot of questions I had ;)

> 2 - GlobalStore is targeted at bazaar.conf, UserStore will probably use user.conf and SystemStore may use /etc/bazaar.conf or whatever we decide.

...just to be clear, GlobarStore looks at ~/.bazaar/bazaar.conf? So
what does UserStore do?

Cheers,

--
Thomi Richards

6055. By Thomi Richards

Reverted changes after feedback from Vila.

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Hi,

After reading your devnotes it's all a lot clearer.

I think the branch now contains everything as it should be. I'm happy to keep tweaking things, if you can see something that's not quite correct.

Cheers,

Revision history for this message
Vincent Ladeuil (vila) wrote :

> No, I didn't see that. It certainly clears up a lot of questions I had ;)

Cool.

>
> > 2 - GlobalStore is targeted at bazaar.conf, UserStore will probably use
> user.conf and SystemStore may use /etc/bazaar.conf or whatever we decide.
>
> ...just to be clear, GlobarStore looks at ~/.bazaar/bazaar.conf?

Yes and is currently defined to accept the [DEFAULT] section and any arbitrary ones used by various plugins and provides no way to match section names as paths.

> So
> what does UserStore do?

UserStore is planned to be associated with ~/.bazaar/user.conf (or defaults.conf) and allows section names to be matched against paths.

Going from one to the other will require some migration path.

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

That hack for /etc is a bit icky, but (modulo just matching all bsds, which I will tweak) I can't think of a cleaner way, and we shouldn't block on it. If someone comes up with a better api or complains that on some other systems it ought to be elsewhere, we can tweak it.

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

So I'm just trying to test this interactively, and it's not obviously hooking in to the things I might like to configure, such as locks.steal_dead, debug_flags, 'config --scope global' etc. Perhaps we should merge it and hook it up later? I would be reluctant to actually document this as a new feature until it's at least mostly working.

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

> So I'm just trying to test this interactively, and it's not obviously hooking
> in to the things I might like to configure, such as locks.steal_dead,
> debug_flags, 'config --scope global' etc. Perhaps we should merge it and hook
> it up later? I would be reluctant to actually document this as a new feature
> until it's at least mostly working.

The last time I checked the only config option that used the new stack-based API was the "editor" option.

Unfortunately I've been busy with work & real life, but I intend to get back to this at some point in the future. However, I'm not totally clear on what the future direction is, so if you're happy with the code as-is, the best option might be to merge and work from there.

Cheers,

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

I'd be interested to hear how this fits in with Vincent's current plans for config, but other than that I think we should just land it.

It seems reasonable enough in general, and we can always tweak it further.

review: Approve
Revision history for this message
Vincent Ladeuil (vila) wrote :

Roughly speaking (summarising my previous remarks here): it's a bit early to land as is.

It introduces one more config file that will be accessed for each option query (http://pad.lv/832042).

It introduces a new file with a semantic which is not the one we want (regarding section filtering/ordering http://pad.lv/832046).

It doesn't address the defaults/overrides distinction we want to add there (which will require two distinct files, adding another queried config file for each option)

The rest seems fine, in particular:

We need to have platform specific paths for the system-wide config file.

We need to add this system-wide file at the right place in the stack definitions.

The new store *is* hooked in the right places but since this mp is based on an old trunk version, this can observed only for already migrated options. As such, we can't even measure its impact with -Econfig_stats.

So I'd say, either we land the path handling stuff only or we wait for the bugs mentioned above to be fixed before reconsidering this mp.

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

I'd really like to get this feature in, and to not have this work stalled.

Vincent's away at the moment, but what do you think, Thomi, do you want to have a go at those changes suggested by Vincent or should someone pick it up?

> It introduces one more config file that will be accessed for each option query (http://pad.lv/832042).

That bug's important to fix, but I don't think it needs to strictly serialize landing this. Reading a global file will make the penalty for multiple lookups linearly worse, but hopefully it doesn't block it entirely.

> It introduces a new file with a semantic which is not the one we want (regarding section filtering/ordering http://pad.lv/832046).

Right, but again it doesn't seem wrong so much as just not yet fixing things we'd like to fix in existing files.

> It doesn't address the defaults/overrides distinction we want to add there (which will require two distinct files, adding another queried config file for each option)

I'm not sure I understand that. Is this to say you want one global configuration that is a low priority, and another that overrides everything else?

Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Hi Martin,

On 14 October 2011 20:03, Martin Pool <email address hidden> wrote:

> I'd really like to get this feature in, and to not have this work stalled.
>
> Vincent's away at the moment, but what do you think, Thomi, do you want to
> have a go at those changes suggested by Vincent or should someone pick it
> up?
>
>
My day-job is getting pretty busy this time of year (just a few weeks until
the end of semester), but I'll see what I can do. The biggest issue stopping
this work going ahead right now is that I don't have a clear idea of what
needs to happen to get it merged. I don't understand the desired config file
semantic, and that seems like a separate piece of work to me.

If you know what needs to be done, please let me know and I'll try and find
an evening or two in which to clean up the code.

Cheers,

--
Thomi Richards

Revision history for this message
Martin Packman (gz) wrote :

I think underlying config changes that are confusing the status of this proposal are gradually getting resolved. For now though, I'm going to move this off the review queue, but will poke Vincent later to make sure we know what else we need before global configs can move forward.

Revision history for this message
Vincent Ladeuil (vila) wrote :

For the record, I've proposed https://code.launchpad.net/~vila/bzr/832046-globs-store-ordered/+merge/86734 which address one of the pre-requisites.

Unmerged revisions

6055. By Thomi Richards

Reverted changes after feedback from Vila.

6054. By Thomi Richards

Hid BranchStore class behind _get_branch_store() factory function.

6053. By Thomi Richards

Windows version 6 (WIndows 7, Server 2008) puts the common app data folder in a different place. Updated docstrings and fixed unit test.

6052. By Thomi Richards

Forgot to update docstring for system_config_dir()

6051. By Thomi Richards

Several changes to clean up global config work:
 * Store classes (GlobalStore et al) are now factory functions.
 * What was previously called a "Global store" is now called a "User Store".
 * similarly, what was the "System Global Store" is now just the "Global Store"

6050. By Thomi Richards

Added unit tests to test the new path functions for win32 and freebsd.

6049. By Thomi Richards

Added unit tests for new method in win32utils.py

6048. By Thomi Richards

Fixed docstring for config.system_config_dir

6047. By Thomi Richards

LocationStack and BranchStack now use system global store as well.

6046. By Thomi Richards

First pass attempt at global config file implementation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bzrlib/config.py'
--- bzrlib/config.py 2011-08-02 01:10:27 +0000
+++ bzrlib/config.py 2011-08-05 04:17:29 +0000
@@ -55,7 +55,7 @@
55 turns on create_signatures.55 turns on create_signatures.
56create_signatures - this option controls whether bzr will always create56create_signatures - this option controls whether bzr will always create
57 gpg signatures or not on commits. There is an unused57 gpg signatures or not on commits. There is an unused
58 option which in future is expected to work if 58 option which in future is expected to work if
59 branch settings require signatures.59 branch settings require signatures.
60log_format - this option sets the default log format. Possible values are60log_format - this option sets the default log format. Possible values are
61 long, short, line, or a plugin can register new formats.61 long, short, line, or a plugin can register new formats.
@@ -72,6 +72,7 @@
72up=pull72up=pull
73"""73"""
7474
75from distutils import sysconfig
75import os76import os
76import string77import string
77import sys78import sys
@@ -452,7 +453,7 @@
452 return None453 return None
453454
454 def acceptable_keys(self):455 def acceptable_keys(self):
455 """Comma separated list of key patterns acceptable to 456 """Comma separated list of key patterns acceptable to
456 verify-signatures command"""457 verify-signatures command"""
457 result = self._acceptable_keys()458 result = self._acceptable_keys()
458 return result459 return result
@@ -1533,6 +1534,26 @@
1533 base = os.path.expanduser("~")1534 base = os.path.expanduser("~")
1534 return osutils.pathjoin(base, ".bazaar")1535 return osutils.pathjoin(base, ".bazaar")
15351536
1537def system_config_dir():
1538 """Return system-wide configuration directory.
1539
1540 By default this is %ALLUSERSAPPDATA%/bazaar/2.0 on Windows, /etc/bazaar
1541 on Mac OS X and Linux, and {PREFIX}/etc/bazaar on BSD, where "{PREFIX}" is
1542 the path prefix python was installed with (usually "/usr/local").
1543 """
1544 if sys.platform == 'win32':
1545 # environ variables on Windows are in user encoding/mbcs. So decode
1546 # before using one
1547 base = win32utils.get_system_appdata_location()
1548 if base is None:
1549 raise errors.BzrError('Could not determine system-wide application'
1550 'data directory. Do you have ALLUSERSAPPDATA'
1551 'set?')
1552 return osutils.pathjoin(base, 'bazaar', '2.0')
1553 elif sys.platform.startswith('freebsd'):
1554 return osutils.pathjoin(sysconfig.PREFIX, 'etc', 'bazaar')
1555 else:
1556 return osutils.pathjoin('/etc', "bazaar")
15361557
1537def config_filename():1558def config_filename():
1538 """Return per-user configuration ini file filename."""1559 """Return per-user configuration ini file filename."""
@@ -1608,11 +1629,11 @@
16081629
1609 Only used when none is set in the environment or the id file.1630 Only used when none is set in the environment or the id file.
16101631
1611 This only returns an email address if we can be fairly sure the 1632 This only returns an email address if we can be fairly sure the
1612 address is reasonable, ie if /etc/mailname is set on unix.1633 address is reasonable, ie if /etc/mailname is set on unix.
16131634
1614 This doesn't use the FQDN as the default domain because that may be 1635 This doesn't use the FQDN as the default domain because that may be
1615 slow, and it doesn't use the hostname alone because that's not normally 1636 slow, and it doesn't use the hostname alone because that's not normally
1616 a reasonable address.1637 a reasonable address.
1617 """1638 """
1618 if sys.platform == 'win32':1639 if sys.platform == 'win32':
@@ -1794,7 +1815,7 @@
1794 :param user: login (optional)1815 :param user: login (optional)
17951816
1796 :param path: the absolute path on the server (optional)1817 :param path: the absolute path on the server (optional)
1797 1818
1798 :param realm: the http authentication realm (optional)1819 :param realm: the http authentication realm (optional)
17991820
1800 :return: A dict containing the matching credentials or None.1821 :return: A dict containing the matching credentials or None.
@@ -1938,7 +1959,7 @@
19381959
1939 :param path: the absolute path on the server (optional)1960 :param path: the absolute path on the server (optional)
19401961
1941 :param ask: Ask the user if there is no explicitly configured username 1962 :param ask: Ask the user if there is no explicitly configured username
1942 (optional)1963 (optional)
19431964
1944 :param default: The username returned if none is defined (optional).1965 :param default: The username returned if none is defined (optional).
@@ -2074,7 +2095,7 @@
2074 :param override_existing: Raise KeyErorr if False and something has2095 :param override_existing: Raise KeyErorr if False and something has
2075 already been registered for that key. If True, ignore if there2096 already been registered for that key. If True, ignore if there
2076 is an existing key (always register the new value).2097 is an existing key (always register the new value).
2077 :param fallback: Whether this credential store should be 2098 :param fallback: Whether this credential store should be
2078 used as fallback.2099 used as fallback.
2079 """2100 """
2080 return super(CredentialStoreRegistry,2101 return super(CredentialStoreRegistry,
@@ -2094,7 +2115,7 @@
2094 :param override_existing: If True, replace the existing object2115 :param override_existing: If True, replace the existing object
2095 with the new one. If False, if there is already something2116 with the new one. If False, if there is already something
2096 registered with the same key, raise a KeyError2117 registered with the same key, raise a KeyError
2097 :param fallback: Whether this credential store should be 2118 :param fallback: Whether this credential store should be
2098 used as fallback.2119 used as fallback.
2099 """2120 """
2100 return super(CredentialStoreRegistry, self).register_lazy(2121 return super(CredentialStoreRegistry, self).register_lazy(
@@ -2575,6 +2596,12 @@
2575 possible_transports=possible_transports)2596 possible_transports=possible_transports)
2576 super(GlobalStore, self).__init__(t, 'bazaar.conf')2597 super(GlobalStore, self).__init__(t, 'bazaar.conf')
25772598
2599class SystemGlobalStore(LockableIniFileStore):
2600
2601 def __init__(self, possible_transports=None):
2602 t = transport.get_transport_from_path(system_config_dir(),
2603 possible_transports=possible_transports)
2604 super(SystemGlobalStore, self).__init__(t, 'bazaar.conf')
25782605
2579class LocationStore(LockableIniFileStore):2606class LocationStore(LockableIniFileStore):
25802607
@@ -2822,9 +2849,11 @@
2822class GlobalStack(_CompatibleStack):2849class GlobalStack(_CompatibleStack):
28232850
2824 def __init__(self):2851 def __init__(self):
2825 # Get a GlobalStore2852 # Get a GlobalStore and a system-wide global store:
2826 gstore = GlobalStore()2853 gstore = GlobalStore()
2827 super(GlobalStack, self).__init__([gstore.get_sections], gstore)2854 swgstore = SystemGlobalStore()
2855 super(GlobalStack, self)\
2856 .__init__([gstore.get_sections, swgstore.get_sections], gstore)
28282857
28292858
2830class LocationStack(_CompatibleStack):2859class LocationStack(_CompatibleStack):
@@ -2836,8 +2865,10 @@
2836 lstore = LocationStore()2865 lstore = LocationStore()
2837 matcher = LocationMatcher(lstore, location)2866 matcher = LocationMatcher(lstore, location)
2838 gstore = GlobalStore()2867 gstore = GlobalStore()
2868 swgstore = SystemGlobalStore()
2839 super(LocationStack, self).__init__(2869 super(LocationStack, self).__init__(
2840 [matcher.get_sections, gstore.get_sections], lstore)2870 [matcher.get_sections, gstore.get_sections, swgstore.get_sections],
2871 lstore)
28412872
2842class BranchStack(_CompatibleStack):2873class BranchStack(_CompatibleStack):
28432874
@@ -2846,8 +2877,10 @@
2846 lstore = LocationStore()2877 lstore = LocationStore()
2847 matcher = LocationMatcher(lstore, branch.base)2878 matcher = LocationMatcher(lstore, branch.base)
2848 gstore = GlobalStore()2879 gstore = GlobalStore()
2880 swgstore = SystemGlobalStore()
2849 super(BranchStack, self).__init__(2881 super(BranchStack, self).__init__(
2850 [matcher.get_sections, bstore.get_sections, gstore.get_sections],2882 [matcher.get_sections, bstore.get_sections, gstore.get_sections,
2883 swgstore.get_sections],
2851 bstore)2884 bstore)
2852 self.branch = branch2885 self.branch = branch
28532886
28542887
=== modified file 'bzrlib/tests/test_config.py'
--- bzrlib/tests/test_config.py 2011-07-11 10:53:46 +0000
+++ bzrlib/tests/test_config.py 2011-08-05 04:17:29 +0000
@@ -17,6 +17,7 @@
17"""Tests for finding and reading the bzr config file[s]."""17"""Tests for finding and reading the bzr config file[s]."""
18# import system imports here18# import system imports here
19from cStringIO import StringIO19from cStringIO import StringIO
20from distutils import sysconfig
20import os21import os
21import sys22import sys
22import threading23import threading
@@ -545,8 +546,20 @@
545 'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')546 'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
546 self.bzr_home = \547 self.bzr_home = \
547 'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'548 'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
549 # Windows ver 6 (Windows 7, Server 2008) uses a different location
550 # from earlier versions:
551 if sys.getwindowsversion().major == 6:
552 self.sys_config_dir = r'C:/ProgramData/bazaar/2.0'
553 else:
554 self.sys_config_dir = \
555 r'C:/Documents and Settings/All Users/Application Data'
548 else:556 else:
549 self.bzr_home = '/home/bogus/.bazaar'557 self.bzr_home = '/home/bogus/.bazaar'
558 if sys.platform.startswith('freebsd'):
559 self.sys_config_dir = osutils.pathjoin(sysconfig.PREFIX, 'etc',
560 'bazaar')
561 else:
562 self.sys_config_dir = '/etc/bazaar'
550563
551 def test_config_dir(self):564 def test_config_dir(self):
552 self.assertEqual(config.config_dir(), self.bzr_home)565 self.assertEqual(config.config_dir(), self.bzr_home)
@@ -567,6 +580,9 @@
567 self.assertEqual(config.xdg_cache_dir(),580 self.assertEqual(config.xdg_cache_dir(),
568 '/home/bogus/.cache')581 '/home/bogus/.cache')
569582
583 def test_sys_config_dir(self):
584 self.assertEqual(config.system_config_dir(), self.sys_config_dir)
585
570586
571class TestXDGConfigDir(tests.TestCaseInTempDir):587class TestXDGConfigDir(tests.TestCaseInTempDir):
572 # must be in temp dir because config tests for the existence of the bazaar588 # must be in temp dir because config tests for the existence of the bazaar
@@ -3175,7 +3191,7 @@
3175 conf = config.AuthenticationConfig(_file=StringIO(3191 conf = config.AuthenticationConfig(_file=StringIO(
3176 'foo = bar\xff'))3192 'foo = bar\xff'))
3177 self.assertRaises(errors.ConfigContentError, conf._get_config)3193 self.assertRaises(errors.ConfigContentError, conf._get_config)
3178 3194
3179 def test_missing_auth_section_header(self):3195 def test_missing_auth_section_header(self):
3180 conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))3196 conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3181 self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')3197 self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
@@ -3371,8 +3387,8 @@
3371 port=99, path='/foo',3387 port=99, path='/foo',
3372 realm='realm')3388 realm='realm')
3373 CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',3389 CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
3374 'verify_certificates': False, 'scheme': 'scheme', 3390 'verify_certificates': False, 'scheme': 'scheme',
3375 'host': 'host', 'port': 99, 'path': '/foo', 3391 'host': 'host', 'port': 99, 'path': '/foo',
3376 'realm': 'realm'}3392 'realm': 'realm'}
3377 self.assertEqual(CREDENTIALS, credentials)3393 self.assertEqual(CREDENTIALS, credentials)
3378 credentials_from_disk = config.AuthenticationConfig().get_credentials(3394 credentials_from_disk = config.AuthenticationConfig().get_credentials(
@@ -3386,8 +3402,8 @@
3386 self.assertIs(None, conf._get_config().get('name'))3402 self.assertIs(None, conf._get_config().get('name'))
3387 credentials = conf.get_credentials(host='host', scheme='scheme')3403 credentials = conf.get_credentials(host='host', scheme='scheme')
3388 CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':3404 CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
3389 'password', 'verify_certificates': True, 3405 'password', 'verify_certificates': True,
3390 'scheme': 'scheme', 'host': 'host', 'port': None, 3406 'scheme': 'scheme', 'host': 'host', 'port': None,
3391 'path': None, 'realm': None}3407 'path': None, 'realm': None}
3392 self.assertEqual(CREDENTIALS, credentials)3408 self.assertEqual(CREDENTIALS, credentials)
33933409
@@ -3662,7 +3678,7 @@
36623678
3663 def test_auto_user_id(self):3679 def test_auto_user_id(self):
3664 """Automatic inference of user name.3680 """Automatic inference of user name.
3665 3681
3666 This is a bit hard to test in an isolated way, because it depends on3682 This is a bit hard to test in an isolated way, because it depends on
3667 system functions that go direct to /etc or perhaps somewhere else.3683 system functions that go direct to /etc or perhaps somewhere else.
3668 But it's reasonable to say that on Unix, with an /etc/mailname, we ought3684 But it's reasonable to say that on Unix, with an /etc/mailname, we ought
36693685
=== modified file 'bzrlib/tests/test_win32utils.py'
--- bzrlib/tests/test_win32utils.py 2011-06-14 01:26:41 +0000
+++ bzrlib/tests/test_win32utils.py 2011-08-05 04:17:29 +0000
@@ -38,7 +38,7 @@
38Win32RegistryFeature = features.ModuleAvailableFeature('_winreg')38Win32RegistryFeature = features.ModuleAvailableFeature('_winreg')
39CtypesFeature = features.ModuleAvailableFeature('ctypes')39CtypesFeature = features.ModuleAvailableFeature('ctypes')
40Win32comShellFeature = features.ModuleAvailableFeature('win32com.shell')40Win32comShellFeature = features.ModuleAvailableFeature('win32com.shell')
41Win32ApiFeature = features.ModuleAvailableFeature('win32api') 41Win32ApiFeature = features.ModuleAvailableFeature('win32api')
4242
4343
44# Tests44# Tests
@@ -231,6 +231,22 @@
231 encoding = osutils.get_user_encoding()231 encoding = osutils.get_user_encoding()
232 self.assertPathsEqual(lad, env.decode(encoding))232 self.assertPathsEqual(lad, env.decode(encoding))
233233
234 def test_system_appdata_not_using_environment(self):
235 # Test that we aren't falling back to the environment
236 first = win32utils.get_system_appdata_location()
237 self.overrideEnv("ALLUSERSAPPDATA", None)
238 self.assertPathsEqual(first, win32utils.get_system_appdata_location())
239
240 def test_system_appdata_matches_environment(self):
241 # ALLUSERSAPPDATA typically only exists on Vista, so we only attempt to
242 # compare when it exists.
243 auad = win32utils.get_system_appdata_location()
244 env = os.environ.get("ALLUSERSAPPDATA")
245 if env:
246 # XXX - See bug 262874, which asserts the correct encoding is 'mbcs'
247 encoding = osutils.get_user_encoding()
248 self.assertPathsEqual(auad, env.decode(encoding))
249
234250
235class TestLocationsPywin32(TestLocationsCtypes):251class TestLocationsPywin32(TestLocationsCtypes):
236252
237253
=== modified file 'bzrlib/win32utils.py'
--- bzrlib/win32utils.py 2011-06-11 01:11:43 +0000
+++ bzrlib/win32utils.py 2011-08-05 04:17:29 +0000
@@ -93,6 +93,7 @@
93CSIDL_APPDATA = 0x001A # Application Data folder93CSIDL_APPDATA = 0x001A # Application Data folder
94CSIDL_LOCAL_APPDATA = 0x001c# <user name>\Local Settings\Application Data (non roaming)94CSIDL_LOCAL_APPDATA = 0x001c# <user name>\Local Settings\Application Data (non roaming)
95CSIDL_PERSONAL = 0x0005 # My Documents folder95CSIDL_PERSONAL = 0x0005 # My Documents folder
96CSIDL_COMMON_APPDATA = 0x0023 # C:\Documents and Settings\All Users\Application Data
9697
97# from winapi C headers98# from winapi C headers
98MAX_PATH = 26099MAX_PATH = 260
@@ -291,6 +292,25 @@
291 return get_appdata_location()292 return get_appdata_location()
292293
293294
295def get_system_appdata_location():
296 """Return system-wide local Application Data location.
297
298 This returns the location of the system wide (i.e.- not per-user)
299 application data folder. This can be one of several locations, depending on
300 your platform. Common locations include "C:/ProgramData" and
301 "C:/Documents and Settings/All Users/Application Data".
302
303 Returned value can be unicode or plain string.
304 To convert plain string to unicode use
305 s.decode(osutils.get_user_encoding())
306 (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
307 """
308 system_appdata = _get_sh_special_folder_path(CSIDL_COMMON_APPDATA)
309 if system_appdata:
310 return system_appdata
311 return os.environ.get('ALLUSERSAPPDATA')
312
313
294def get_home_location():314def get_home_location():
295 """Return user's home location.315 """Return user's home location.
296 Assume on win32 it's the <My Documents> folder.316 Assume on win32 it's the <My Documents> folder.
@@ -537,19 +557,19 @@
537 """557 """
538 # First, spit the command line558 # First, spit the command line
539 s = cmdline.Splitter(command_line, single_quotes_allowed=single_quotes_allowed)559 s = cmdline.Splitter(command_line, single_quotes_allowed=single_quotes_allowed)
540 560
541 # Bug #587868 Now make sure that the length of s agrees with sys.argv 561 # Bug #587868 Now make sure that the length of s agrees with sys.argv
542 # we do this by simply counting the number of arguments in each. The counts should 562 # we do this by simply counting the number of arguments in each. The counts should
543 # agree no matter what encoding sys.argv is in (AFAIK) 563 # agree no matter what encoding sys.argv is in (AFAIK)
544 # len(arguments) < len(sys.argv) should be an impossibility since python gets 564 # len(arguments) < len(sys.argv) should be an impossibility since python gets
545 # args from the very same PEB as does GetCommandLineW565 # args from the very same PEB as does GetCommandLineW
546 arguments = list(s)566 arguments = list(s)
547 567
548 # Now shorten the command line we get from GetCommandLineW to match sys.argv568 # Now shorten the command line we get from GetCommandLineW to match sys.argv
549 if len(arguments) < len(argv):569 if len(arguments) < len(argv):
550 raise AssertionError("Split command line can't be shorter than argv")570 raise AssertionError("Split command line can't be shorter than argv")
551 arguments = arguments[len(arguments) - len(argv):]571 arguments = arguments[len(arguments) - len(argv):]
552 572
553 # Carry on to process globs (metachars) in the command line573 # Carry on to process globs (metachars) in the command line
554 # expand globs if necessary574 # expand globs if necessary
555 # TODO: Use 'globbing' instead of 'glob.glob', this gives us stuff like575 # TODO: Use 'globbing' instead of 'glob.glob', this gives us stuff like