Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

include file() is not relative to the file doing the including #202

Open
Soren025 opened this issue Oct 7, 2014 · 13 comments
Open

include file() is not relative to the file doing the including #202

Soren025 opened this issue Oct 7, 2014 · 13 comments

Comments

@Soren025
Copy link

Soren025 commented Oct 7, 2014

When writing plugins for Bukkit, the plugin's data folder is located at "plugins/[plugin name]".

In order to make use of "include file()" I must enter in that entire path before hand such that it looks like this "foo = { include file("plugins/[plugin name]/bar.conf }. I want the include to implicitly assume the "plugins/<plugin name/" to keep my config files clean.

I have have a feeling It can be done by implementing "ConfigIncluder" but I have not figured out how to do this.

@havocp
Copy link
Collaborator

havocp commented Oct 7, 2014

http://typesafehub.github.io/config/latest/api/com/typesafe/config/ConfigIncluder.html exists, but I don't have a good example to point to. The general outline of how this works is that you set an includer on your ConfigParseOptions, then the library is going to call yourIncluder.withFallback(defaultIncluder) and you can capture the default includer in the result of your withFallback, which allows you to chain to it when you implement the include method.

@Soren025
Copy link
Author

After a few tries to get what you said to work, I have figured out a solution that seems to work for what I wanted.

instead of 'include file("plugins/[plugin name]/foo.conf")' I can use 'include "foo.conf"'
public static class Includer implements ConfigIncluder {
    @Override
    public ConfigIncluder withFallback(ConfigIncluder fallback) {
        return this;
    }

    @Override
    public ConfigObject include(ConfigIncludeContext context, String what) {
        File file = new File("plugins/[plugin name]/" + what);
        return ConfigFactory.parseFile(file).root();
    }
}

Thanks for your input

@havocp
Copy link
Collaborator

havocp commented Oct 10, 2014

That looks right to me. If you wanted to chain to the regular default includer, you would add a field ConfigIncluder fallback, then in withFallback you copy your includer but with that field set. And then implement whatever logic you want for when to use it, perhaps if the plugins file doesn't exist you chain to the default includer or something.

There are also extra interfaces for implementing file() etc if I remember right. ConfigIncluderFile etc.

@Soren025
Copy link
Author

I am unsure what I missed, but I tested it with and without the includer I made and 'include "foo.conf"' seems to do exactly what I want in both cases. (that is look for 'foo.conf' relative to the file the 'include' was found in)

I have to be missing something, if this is the case then I never needed to make a custom includer in the first place. It could be that I always used 'include file("foo.conf")' before and never tried it without the file().

Am I missing something?

@havocp
Copy link
Collaborator

havocp commented Oct 11, 2014

https://github.com/typesafehub/config/blob/master/HOCON.md#include-semantics-locating-resources is how it's supposed to work (maybe it even does!)

Yes, foo.conf is supposed to be found relative to the file that included foo.conf. I apologize for not catching right away that's what you were doing.

According to the spec this does appear to be only if it's bare "foo.conf" and not file("foo.conf") but according to the code it looks like it's trying to make file("foo.conf") work the same way (which does make sense to me). See https://github.com/typesafehub/config/blob/master/config/src/main/java/com/typesafe/config/impl/Parseable.java#L522 for example.

The spec is a little unclear even to me; it says the bare include "foo.conf" would look adjacent to the including file or resource, but it just doesn't really say what file() should do.

I guess the first step is to figure out what the current code actually does and whether we have tests for it. Then we can figure out how to clarify the spec. The spec may end up having to document whatever the code does, unless the code is doing something utterly useless that nobody could be relying on.

If I put a throw new AssertionError("HERE") in ParseableFile.relativeTo where it gets an adjacent file, I get the following tests failing:

[error] Test com.typesafe.config.impl.PublicApiTest.includesCanBeMissingThoughFileCannot failed: HERE
[error] Test com.typesafe.config.impl.PublicApiTest.includersAreUsedWithFiles failed: HERE
[error] Test com.typesafe.config.impl.PublicApiTest.includersAreUsedRecursivelyWithFiles failed: HERE
[error] Test com.typesafe.config.impl.PublicApiTest.fullIncluderNotUsedWithoutNewSyntax failed: HERE
[error] Test com.typesafe.config.impl.PublicApiTest.includersAreUsedRecursivelyWithURL failed: HERE
[error] Test com.typesafe.config.impl.EquivalentsTest.testEquivalents failed: HERE

None of those sound like they are directly testing this feature in normal usage, though, so I wouldn't say we've proven it works.

@havocp havocp changed the title How does one set the root directory for "include file()" (if any) include file() is not relative to the file doing the including Mar 5, 2015
havocp added a commit that referenced this issue Mar 5, 2015
At present, we don't make a file() include relative to the
file doing the including. This is discussed in #202.
We should most likely change this behavior, but this
commit documents and tests it.
havocp added a commit that referenced this issue Mar 7, 2015
At present, we don't make a file() include relative to the
file doing the including. This is discussed in #202.
We should most likely change this behavior, but this
commit documents and tests it.
@havocp
Copy link
Collaborator

havocp commented Mar 7, 2015

I added PR #277 which tests and documents the current behavior. Right now only the "heuristic" includes (without file()) do the "look adjacent" trick. I think we should probably change this, though it would technically be a semantic ABI break. But the code needs to be reshuffled a bit to make this change, it isn't 100% trivial. In the meantime the situation is "works as designed, but design decision was kinda bad."

@DanieleSassoli
Copy link

DanieleSassoli commented Dec 21, 2016

any updates on this?
I'm having problems including a configuration from a file located in a parent directory, but I don't have problems when including from the same directory or children directory... any idea?

@havocp
Copy link
Collaborator

havocp commented Dec 22, 2016

No idea offhand. I guess you are trying a thing like include "../foo.conf" ? I don't think there's a test case for that in the test suite, I don't know whether it works, I think it should work in the same cases that include "./foo.conf" does, but perhaps it does not if nobody has ever tried it until now.

@wuservices
Copy link

After a bunch of attempts, I'm fairly certain that include "../foo.conf does not work. Unfortunately, due to #122, this makes it hard to add a magic default that would load an environment.conf from certain places relative to the project or in the user's homedir. I'll probably load environment.conf using -Dconfig.file, then add include "application" to that instead.

@javadba
Copy link

javadba commented Dec 14, 2018

Referencing a local path is not working as follows:

For the following configuratoin:

sqls = {
  scoring = {
    scoringSummarySql = """select MatchMethod, F1, rsF1, Precision, Recall, TP, FP, FN from scoring"""
 }

This works if the above snippet is directly inside the calling file - but gives the following error if using the following include directive:

include "matching-sqls.conf"

String: 117: Could not resolve substitution to a value: ${sqls.scoring.scoringSummarySql}
com.typesafe.config.ConfigException$UnresolvedSubstitution: String: 117: Could not resolve substitution to a value: ${sqls.scoring.scoringSummarySql}

I am fine to do any kind of relative or local path for includes - but is anything working presently?

@anilkumarmyla
Copy link

I'm having problems including a configuration from a file located in a parent directory, but I don't have problems when including from the same directory or children directory... any idea?

I've found traversing the whole resource path easier to include a file from parent directory

Let's say you have a/b/c/d.conf doing an include "../../e.conf" to actually include a/e.conf, it doesn't work. You need to do include "/a/e.conf"

@yunjiangster
Copy link

yunjiangster commented Jul 26, 2021

This is a major bug that my project cannot live with, since we'd like all our include paths to be relative within a git repository. Here is an easy way to reproduce the bug:

  1. clone https://github.com/vlad2/hocon-config-printer and install
  2. create the following directory structure and files

cat subdir/a.conf

{
a: b
}

cat subdir/b.conf

{
include file("a.conf")
c: ${a}
}

  1. hocon-config-printer subdir/b.conf -resolve

Exception in thread "main" com.typesafe.config.ConfigException$UnresolvedSubstitution: subdir/b.conf: 3: Could not resolve substitution to a value: ${a}
at com.typesafe.config.impl.ConfigReference.resolveSubstitutions(ConfigReference.java:104)
at com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:183)
at com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146)
at com.typesafe.config.impl.SimpleConfigObject$ResolveModifier.modifyChildMayThrow(SimpleConfigObject.java:380)
at com.typesafe.config.impl.SimpleConfigObject.modifyMayThrow(SimpleConfigObject.java:313)
at com.typesafe.config.impl.SimpleConfigObject.resolveSubstitutions(SimpleConfigObject.java:399)
at com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:183)
at com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146)
at com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:235)
at com.typesafe.config.impl.SimpleConfig.resolveWith(SimpleConfig.java:79)
at com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:69)
at com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:64)
at com.typesafe.config.impl.SimpleConfig.resolve(SimpleConfig.java:42)
at ro.vdin.configprinter.ConfigPrinter.printConfig(ConfigPrinter.java:15)
at ro.vdin.configprinter.ConfigPrinterMain.main(ConfigPrinterMain.java:24)
Caused by: com.typesafe.config.impl.AbstractConfigValue$NotPossibleToResolve: was not possible to resolve
at com.typesafe.config.impl.ResolveContext.realResolve(ResolveContext.java:180)
at com.typesafe.config.impl.ResolveContext.resolve(ResolveContext.java:146)
at com.typesafe.config.impl.ConfigReference.resolveSubstitutions(ConfigReference.java:88)
... 14 more

  1. running the parser under subdir does work:

hocon-config-printer b.conf -resolve

{
"a" : "b",
"c" : "b"
}

@adampauls
Copy link

Is there even a reasonable workaround? I think bare include "foo/bar.conf" is not platform independent because of the "/"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants