Edgewall Software
Modify

Opened 17 years ago

Closed 14 years ago

Last modified 9 years ago

#5525 closed enhancement (fixed)

Allow trac.ini to include other files

Reported by: anonymous Owned by: Remy Blank
Priority: normal Milestone: 0.12
Component: general Version: 0.11
Severity: normal Keywords:
Cc: osimons Branch:
Release Notes:
API Changes:
Internal Changes:

Description

Support an include directive in the configuration file syntax.

include "trac-ldap.ini"

will recursively load the directives in trac-ldap.ini .

Rationale: this will make it possible to split the configuration into multiple files.

TracAdmin will be able to write to a tracadmin.ini, included by trac.ini (included eg at the bottom to give it higher priority).

This is necessary to manage trac configuration programmatically.

My use case is to install trac plugins with a plugin.ini snippet using puppet (this requires no manual intervention). Currently I am merging all these small .ini files into one big trac.ini, but this also means that any changes TracAdmin writes to the file are lost.

Attachments (4)

5525-multiple-inherit-r8738.patch (12.6 KB ) - added by Remy Blank 14 years ago.
Proof of concept for multiple file inclusion in trac.ini.
5525-multiple-inherit-r8738.2.patch (14.8 KB ) - added by Remy Blank 14 years ago.
Use [inherit] file with a comma-separated list of paths
5525-multiple-inherit-r8741.patch (16.3 KB ) - added by Remy Blank 14 years ago.
Refreshed patch with better documentation.
5525-multiple-inherit-r8902.patch (18.4 KB ) - added by Remy Blank 14 years ago.
Resolve relatve paths relative to the file that contains them.

Download all attachments as: .zip

Change History (51)

comment:1 by Emmanuel Blot, 17 years ago

Milestone: 0.11
Resolution: worksforme
Status: newclosed

This is already implemented in 0.11, you can use the inherit section, such as in:

[inherit]
file = /another/path/trac.ini

comment:2 by anonymous, 17 years ago

This is great.

Is it also possible to change the path TracAdmin writes to?

in reply to:  2 comment:3 by Emmanuel Blot, 17 years ago

Replying to anonymous:

Is it also possible to change the path TracAdmin writes to?

Which one: .../conf/trac.ini?
No, it is not.

comment:4 by haizaar@…, 16 years ago

Is globbing supported? I.e. can I do something like this:

[inherit]
file = /etc/trac/*.ini

comment:5 by haizaar@…, 16 years ago

Resolution: worksforme
Status: closedreopened

Problem: Can not include more then one file:

$>  cat /etc/trac/trac-graphviz.ini
[components]
graphviz.* = enabled
$>  cat /etc/trac/trac-toc-macro.ini
[components]
tractop.* = enabled
$> cat $PWD/conf/trac.ini
...
[inherit]
file = /etc/trac/trac-graphviz.ini
file = /etc/trac/trac-toc-macro.ini

Only the last mentioned file is included. Trac-0.11

in reply to:  5 ; comment:6 by Emmanuel Blot, 16 years ago

Replying to haizaar@haizaar.com:

Problem: Can not include more then one file:

This is a limitation of the Ini file parser, and the .ini file format: you cannot use the same key more than once within the same section. I guess this limitation could be overcomed with a list of files, such as:

file = a.ini, b.ini

in reply to:  6 comment:7 by Martijn Pieters <trac.edgewall.org@…>, 15 years ago

Replying to eblot:

I guess this limitation could be overcomed with a list of files, such as: file = a.ini, b.ini

The buildout convention is space separation:

    file = a.ini b.ini

This has the added advantage that it's easy to spread this across multiple lines:

    file =
        a.ini
        b.ini

Config-file editors don't have to remember to insert commas, and the code reading the option simply has to use .split() to turn this into a list.

comment:8 by osimons, 15 years ago

Milestone: 0.11
Resolution: worksforme
Status: reopenedclosed
Version: 0.11

Following #8290/[8243] for upcoming 0.11.5, the config parser supports chaining of config files. This should support much the same need as including more than one file in same ini file. The chaining also implies ordering, and it will use the option on a first-come-first-served basis as it traverses the chain up from project trac.ini and ending in the default provided component Option properties.

by Remy Blank, 14 years ago

Proof of concept for multiple file inclusion in trac.ini.

comment:9 by Remy Blank, 14 years ago

The patch above is a proof of concept on trunk for multiple file inclusion in trac.ini via the [inherit] section. It should be completely backward-compatible with the current functionality, and all tests pass. But I should really add more tests to make sure everything works correctly.

Oh, there's already one issue: on initenv, the options that are in the inherited files are re-included. I'll have to look at that. But it still shows the general idea.

Thoughts?

comment:10 by Christian Boos, 14 years ago

Resolution: worksforme
Status: closedreopened

It would be cool to be able to trace for any given setting in which trac.ini file it was defined. That would help a lot for debugging if this information get shown in the /about page (as a tooltip). Maybe it would even help for the problem you mention above.

What about the PathOption concerns raised by osimons?

in reply to:  10 ; comment:11 by Remy Blank, 14 years ago

Replying to cboos:

It would be cool to be able to trace for any given setting in which trac.ini file it was defined. That would help a lot for debugging if this information get shown in the /about page (as a tooltip). Maybe it would even help for the problem you mention above.

Good idea, I'll see how I can do that.

What about the PathOption concerns raised by osimons?

The current functionality always resolves PathOptions relative to the environment trac.ini (the _base_filename attribute). This patch does exactly the same. Did we want something else?

in reply to:  11 ; comment:12 by osimons, 14 years ago

Replying to rblank:

Replying to cboos:

What about the PathOption concerns raised by osimons?

The current functionality always resolves PathOptions relative to the environment trac.ini (the _base_filename attribute). This patch does exactly the same. Did we want something else?

No it doesn't work like that. If the project config itself has it (or read from options registry), then yes. If a parent defines the option then the parent resolves it relative to its own location whereever that may be.

Also, the patch ignores templates_dir and plugins_dir that are also under [inherit]. A config section like the one in the test-case won't work, and using --inherit we won't have any way of naming them either. Nor ordering parents by indicating which is to be preferred if same setting is defined in more than one parent. If anything, the multiple parents should be a list like:

[inherit]
file = trac-site1.ini, trac-site2.ini

A change that just converts [inherit] file = from Option to ListOption is something that would work well - provided the rules for evaluating options don't change (or are at least discussed and handled).

comment:13 by osimons, 14 years ago

BTW, my suggestion really requires a new PathListOption type.. - unless that is already available in trunk (that I still don't have running)… :-)

in reply to:  12 comment:14 by Christian Boos, 14 years ago

Replying to osimons:

Replying to rblank:

Replying to cboos:

What about the PathOption concerns raised by osimons?

The current functionality always resolves PathOptions relative to the environment trac.ini (the _base_filename attribute). This patch does exactly the same. Did we want something else?

No it doesn't work like that. If the project config itself has it (or read from options registry), then yes. If a parent defines the option then the parent resolves it relative to its own location whereever that may be.

Well, then it should, I think. The behavior suggested by Remy is more flexible, as otherwise you won't be able to specify a project-relative path from a parent config file (e.g. specifying "the authz_policy.conf file is by default to one from the environment's config folder" in a global.ini file). I find the (current) behavior less useful, as in this case you could as well use an absolute path.

… the multiple parents should be a list like:

[inherit]
file = trac-site1.ini, trac-site2.ini

I would also prefer it this way, the ordering is made more intuitive, and this should avoid the problems with (or special casing) templates_dir and plugins_dir.

in reply to:  12 comment:15 by Remy Blank, 14 years ago

Replying to osimons:

No it doesn't work like that. If the project config itself has it (or read from options registry), then yes. If a parent defines the option then the parent resolves it relative to its own location whereever that may be.

Ah, yes, by bad. Only default options are resolved relative to trac.ini in Section.getpath(), not the options actually parsed. I also prefer resolving relative to trac.ini in any case, as that's what we do for templates_dir and plugins_dir (well, actually they are resolved relative to the environment, not trac.ini).

Also, the patch ignores templates_dir and plugins_dir that are also under [inherit].

I have fixed that in my sandbox already.

A config section like the one in the test-case won't work

Can you explain that? The test case passes, so I'm missing something.

and using --inherit we won't have any way of naming them either.

That's true, I just generate keys with the names "file01", "file02", and so on.

Nor ordering parents by indicating which is to be preferred if same setting is defined in more than one parent.

This is handled by sorting the parent configs by their key in [inherit], so the "file01" parent will always be evaluated before the "file02" parent. That's also how Mercurial handles stuff in hgrc.

If anything, the multiple parents should be a list like:

That would be another option, yes. I'm ok with using that instead of alphabetically-ordered keys, let's just hope that nobody uses paths with a comma ;-)

by Remy Blank, 14 years ago

Use [inherit] file with a comma-separated list of paths

comment:16 by Remy Blank, 14 years ago

The second patch uses [inherit] file with a comma-separated list of paths, and also fixes environment creation.

comment:17 by osimons, 14 years ago

Just note that the docs for getpath() has more or less "forever" been:

Return the value of the specified option as a path name, relative to
the location of the configuration file the option is defined in.

The example cboos used can also be turned around, so that if I have a plugin that uses and refers to various shared resources, each project can just include the shared config and that configuration and its resources will be self-contained - regardless of where each project is located on disk. We can't make both examples work, so that leaves us to decide on one of them.

If we change that behaviour, we need to carefully consider the possible consequences for existing installations. I have no idea how widespread the use of relative paths are in shared configurations (I don't have any, so either way is fine by me personally). The good thing is that the inherited path option only really started working in 0.11.5dev….

in reply to:  17 ; comment:18 by Remy Blank, 14 years ago

Owner: changed from Jonas Borgström to Remy Blank
Status: reopenednew

Replying to osimons:

If we change that behaviour, we need to carefully consider the possible consequences for existing installations. I have no idea how widespread the use of relative paths are in shared configurations (I don't have any, so either way is fine by me personally).

The truth is, relative paths specified with --inherit have never actually worked completely correctly, that's why I have added a call to os.path.abspath() in [8703] (see #8724).

in reply to:  18 comment:19 by osimons, 14 years ago

Replying to rblank:

The truth is, relative paths specified with --inherit have never actually worked completely correctly, that's why I have added a call to os.path.abspath() in [8703] (see #8724).

Hmm. Haven't seen that changeset. I have close to 2000 projects running that inherits from relative locations, so that must obviously be wrong…. I suspect the mentioned ticket is not aware of the 'relativeness' being relative to the config and not to current location for running trac-admin or project.

I'm tempted to ask for that changeset to be reverted…

comment:20 by Remy Blank, 14 years ago

Inheriting from relative locations worked, but specifying them in --inherit was very counter-intuitive, as the the path given to --inherit had to be specified relative to the not-yet created environment's trac.ini. The norm, when using various Unix tools, is that relative paths are specified relative to the current working directory. That's probably what happened to the reporter of #8724, and it also happened to me while trying to find out what the issue was.

I kept trying to do:

trac-admin $ENV my_env --inherit=global.ini

and was wondering why a [ticket-workflow] section kept appearing in my trac.ini. Finally I found out that the following worked:

trac-admin $ENV my_env --inherit=../../global.ini

and then I understood what happened.

I'm ok with reverting [8703], but we need to document the option better, preferably in the help text for trac-admin $ENV initenv.

in reply to:  20 ; comment:21 by osimons, 14 years ago

Replying to rblank:

I'm ok with reverting [8703], but we need to document the option better, preferably in the help text for trac-admin $ENV initenv.

Thanks, and agreed on docs. Running trac-admin help I see the --inherit option isn't even mentioned at all. Likely my fault from back when I made it, but I blame the awkwardness of adding lengthy and informative help to the console application… :-)

comment:22 by osimons, 14 years ago

Either that, or we can just look for the file at the obvious locations and calculate the new correct relative path ourselves (if not specified correctly and pointing to an existing file)?

in reply to:  21 comment:23 by Remy Blank, 14 years ago

Replying to osimons:

Thanks, and agreed on docs. Running trac-admin help I see the --inherit option isn't even mentioned at all. Likely my fault from back when I made it, but I blame the awkwardness of adding lengthy and informative help to the console application… :-)

Oh, right, nice excuse. Ever looked at trac-admin in 0.12? All nice and clean ;-)

Either that, or we can just look for the file at the obvious locations and calculate the new correct relative path ourselves (if not specified correctly and pointing to an existing file)?

On second thought, changing the current behavior (like I did in [8703]) is a bad idea, as many people (and their scripts) are likely to depend on it. So I will revert the change and improve the documentation.

comment:24 by Remy Blank, 14 years ago

The change has been reverted in [8740] (on 0.11-stable), and the resolution of relative paths has been added in [8741] (on trunk).

by Remy Blank, 14 years ago

Refreshed patch with better documentation.

comment:25 by Remy Blank, 14 years ago

Milestone: 0.12

comment:26 by osimons, 14 years ago

Good going, Remy. Obviously still not able to test it, but hey… it reads well :-) I suppose the only open question is the change for how paths are resolved. I see the attraction of resolving all values relative to the environment and its config, but I'm not convinced we gain so much from it - other than breaking existing installations. I'll give in to the majority though, if you both think this needs to change.

in reply to:  26 ; comment:27 by Remy Blank, 14 years ago

Replying to osimons:

I suppose the only open question is the change for how paths are resolved. I see the attraction of resolving all values relative to the environment and its config, but I'm not convinced we gain so much from it - other than breaking existing installations.

Will it break your 2000 installations?

I have no strong preference either, but always resolving from the primary trac.ini makes the code simpler, so +0.5 for the change.

Christian, your last word?

in reply to:  27 ; comment:28 by osimons, 14 years ago

Replying to rblank:

Will it break your 2000 installations?

Perhaps, but easily fixable anyway. However, not so sure as it is mostly used for [inherit] file = for chainable configurations - and according to patch this still looks like it is resolved relative to the location of the currently parsed config and not the base config (ie resolved relative to location of self.filename, and no _base_filename is carried through anymore seeing that is no longer needed to resolve a PathOption). Seeing this option is special, I do actually think it makes sense that a path for this option is always evaluated relative to the location of the file where it is defined - and not by the varying locations of projects that consumes it. If so, I'll have no problem with changing the regular PathOption to always be project-config-relative.

in reply to:  28 comment:29 by Remy Blank, 14 years ago

Replying to osimons:

and according to patch this still looks like it is resolved relative to the location of the currently parsed config and not the base config

Mmh, you're right, the patch is actually still "incorrect" in that respect. Well, let's wait for Christian's vote, then I'll implement the correct variant.

comment:30 by Remy Blank, 14 years ago

Actually, I would prefer even another option: relative paths in config files are always resolved relative to the environment directory (instead of the directory containing the config file). I think that's the easiest to understand for the admin.

comment:31 by Christian Boos, 14 years ago

I'd still want to think a bit more about this.

comment:32 by osimons, 14 years ago

Cc: osimons added

comment:33 by Christian Boos, 14 years ago

So to sum up my point of view on this topic:

  • my personal intuition would be to view [inherit] file = ... as a kind of content inclusion: it doesn't matter where the included files are located, everything works as if the content of the inherited files would be included textually in the current conf file. That means that relative paths would be relative to the current .ini or the current environment, whatever we decided for this point.
  • now I'm sure the opposite position held by osimons is valid as well: consider that when inheriting files, we would inherit "self-contained" complex configurations, that could eventually be relocated. So relative paths would be relative to the file in which the setting is written. In his experience, that's useful for maintaining large sets of instances. This is not something I'm familiar with, and I can only rely on his advice here.

So while the first point of view is maybe the simplest and most easy to explain/implement, it's perhaps not the most useful in practice. We should perhaps stick with the second approach and document it better if needed.

comment:34 by osimons, 14 years ago

Yup. Agree.

Not mentioned in the summary though is the messy upgrade and experiences of existing installations when new code runs and evaluates all inherited relative paths 'wrong'. Do we issue warnings? Do we try to detect 'wrong' configs? How do we explain and document the change to those potentially affected by the change? A new logic that don't know or care where a setting is read from will not likely be aware of any such potential problem anyway….

I just don't see a compelling reason to change the current way of evaluating. On the other hand, perhaps it is just me using relative paths in inherited config files, and if so there will be no better time to change than now… :-)

in reply to:  33 comment:35 by Remy Blank, 14 years ago

Replying to cboos:

So while the first point of view is maybe the simplest and most easy to explain/implement, it's perhaps not the most useful in practice. We should perhaps stick with the second approach and document it better if needed.

I hear you. I suppose it depends what is done more often: referencing files relative to the environment (which will remain impossible with this approach) or moving configuration trees around.

Simon, do you use AccountManager in your installations? How do you reference the htpasswd files? I assume you set it in each individual trac.ini file. This could become a single entry in an included file if resolving was done relative to the environment.

Then again, anything that changes the current behavior will require work to migrate. Too bad for simplicity.

by Remy Blank, 14 years ago

Resolve relatve paths relative to the file that contains them.

comment:36 by Remy Blank, 14 years ago

The patch above resolves relative paths in [inherit] file and in PathOptions relative to the directory containing the file. Testing welcome.

As for the result… Well, I'm not happy at all:

  • It's not (reasonably) possible to use the cache for Section.getpath(), so PathOptions are uncached.
  • The logic in Section.getpath() is ugly even without caching, especially compared to what it would be when resolving relative to the base file.

So we have a functionality that was introduced only recently, that is complicated to explain, and complicated to implement and maintain. I'll ask one last time if everyone is sure that's what we want. After that I'll shut up.

(Let's just say this path resolution functionality is not something I'll ever touch again, not even with a ten-foot pole. Simon, I hope you're ready to support it in the future.)

in reply to:  36 ; comment:37 by osimons, 14 years ago

Replying to rblank:

  • It's not (reasonably) possible to use the cache for Section.getpath(), so PathOptions are uncached.

As far as I can see, there is nothing related to how relative paths are evaluated that is affected by this. A setting is just as cacheable (or not) whether it evaluates to A or B. Seeing we check timestamps and reload if any files have changed, won't a path evaluation be cacheable regardless of old path-evaluation, or this move from simple chained configs to tree structure with multipe inherits at multiple levels?

  • The logic in Section.getpath() is ugly even without caching, especially compared to what it would be when resolving relative to the base file.

I can see that it requires some more lines of code to evaluate in proper path context.

So we have a functionality that was introduced only recently, that is complicated to explain, and complicated to implement and maintain. I'll ask one last time if everyone is sure that's what we want. After that I'll shut up.

I'll survive either. I don't have any settings or structures that I can't make the transition - and also seeing I'm more-than-average aware of the change and its implications of course… I'll bend to majority vote if you and cboos feel strongly about this. If so though, note that any patch really should include an upgrade script to change any existing relative settings - or however we select to deal with the change for upgraded installs.

(Let's just say this path resolution functionality is not something I'll ever touch again, not even with a ten-foot pole. Simon, I hope you're ready to support it in the future.)

Right - and if you do change, I suppose you'll be doing all the support for the installations that break? :-)

in reply to:  37 comment:38 by Remy Blank, 14 years ago

Replying to osimons:

As far as I can see, there is nothing related to how relative paths are evaluated that is affected by this. A setting is just as cacheable (or not) whether it evaluates to A or B.

Not quite. The issue is more subtle, and comes from the fact that using get() or getpath() on a setting will return different values for relative paths (the former will return the setting unchanged, the latter prefixes it with the base path). If you call get() first, the unchanged value will be cached. How would you retrieve the correct value in getpath() after that? (Note that we have exactly such a case in our unit tests.) You would have to cache not only the "raw" value, but also the base path, so that you can reconstruct the correct path in getpath().

That's why getpath() is currently uncached (in trunk), is uncached in the patch, and should probably stay so. This has nothing to do with multiple inclusion. It's not a big issue, but still, it adds complexity.

I'll bend to majority vote if you and cboos feel strongly about this.

I do feel somewhat strongly about this, for the following reason: I have the feeling that the current algorithm was a sub-optimal decision, making the functionality more difficult to understand and maintain. Waiting much longer will make it impossible to change at all, so we will be stuck with it. My question was a last "sanity" check, and as promised, I'll shut up afterward. (Note that I would have interpreted a lack of reply as "yes, that's what we want", as you were clear about it and Christian was only a little ambiguous :-)

If so though, note that any patch really should include an upgrade script to change any existing relative settings - or however we select to deal with the change for upgraded installs.

This could probably be done, yes. I'd have to think about it some more.

Right - and if you do change, I suppose you'll be doing all the support for the installations that break? :-)

Sure I will! Have been here for over a year and been supporting the consequences of my mistakes all along ;-)

Joking apart, this comment wasn't meant to sound aggressive, and I hope it didn't come across as such. The thing is, it takes me a tremendous amount of energy to implement something I'm not convinced of, because I keep seeing the (simpler|cooler|more useful|faster|whatever) alternative and have to resist the urge of doing that. That's why I would stay away from that code. It's just one of my bad character traits ;-)

comment:39 by Christian Boos, 14 years ago

Well, thinking again about all this… attachment:5525-multiple-inherit-r8741.patch is:

in reply to:  39 comment:40 by Christian Boos, 14 years ago

Replying to cboos:

Well, thinking again about all this… attachment:5525-multiple-inherit-r8741.patch is:

So let's go for this solution.

comment:41 by Remy Blank, 14 years ago

Resolution: fixed
Status: newclosed

Patch (with a few more fixes) applied in [9037].

comment:42 by Ryan J Ollos, 9 years ago

It appears there is an unused protected attribute _cache in [9037#file11]: tags/trac-1.0.2/trac/config.py@:293-294#L264. Could that be removed?

comment:43 by Remy Blank, 9 years ago

Indeed, this looks unused. Sure, feel free to remove it, if all tests still pass.

comment:44 by Peter Suter, 9 years ago

Perhaps some other variable was meant to be cleared, e.g. self._sections = {} or for k in self._sections: self._sections[k]._cache = {}?

# config inherits parent.ini containing [a] b=x
config.get('a', 'b') # caches 'x' in config._sections['a']._cache['b']
# change parent.ini to [a] b=y
config.parse_if_needed() # does not invalidate that cache
config.get('a', 'b') # should return 'y' but returns cached 'x'
Last edited 9 years ago by Peter Suter (previous) (diff)

comment:45 by Remy Blank, 9 years ago

That's totally possible, but it's been too long for me to remember. The best would be to add a test case like you suggest, and fix the code if the test fails.

comment:46 by Peter Suter, 9 years ago

Thanks. I created #11902 and will attach a patch.

comment:47 by Ryan J Ollos, 9 years ago

Thanks for the feedback and additional investigation. I'll test the patch in #11902.

Modify Ticket

Change Properties
Set your email in Preferences
Action
as closed The owner will remain Remy Blank.
The resolution will be deleted. Next status will be 'reopened'.
to The owner will be changed from Remy Blank to the specified user.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.