-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Improve config #6467
Improve config #6467
Conversation
5bbc6f2
to
88a1c6c
Compare
88a1c6c
to
5f9a44b
Compare
4e6fefd
to
05bfd69
Compare
Recommend removing (note, I'm not talking about the modes, just the calls that actually require changes to code anyway) |
AFAICT this is good to go. @smotornyuk we need some docs right? I see a http://docs.ckan.org/en/latest/maintaining/configuration.html and a notice on the changelog. Happy to help proofreading and reviewing it. |
@amercader , there is quite a big section at the end of configuration.rst that contains a source of |
@smotornyuk my apologies you are right. I'll review that and merge afterwards |
@smotornyuk I took the liberty of re-writing and re-organizing some bits of the documentation in 67c126e. Please let me know if you are happy with all these changes. Additionally here are some more questions that came up from my review:
|
Yes, thanks for the documentation updates.
|
@smotornyuk great, I've made 1 and 3 clear in the docs, thanks for taking care of 2 If these tests are green I'll merge it |
Great stuff, thanks @smotornyuk ! |
Check updated configuration docs.
Extra details:
This PR introduces a new
IConfigDeclaration
interface which, in turn, changes some aspects of using the config in CKAN.Note these changes are completely compatible with existing extensions and no changes are required by default. But below I'll explain what changes may be done in order to make extensions even better.
IConfigDeclaration
Declaration
This interface provides the single method
def declare_config_options(self, declaration: Declaration, key: Key) -> None: ...
.declaration
object is used to "declare" config option. Declared options can have a default value, can be validated, etc. Undeclared options are ones that are used in the config file, but has no corresponding declaration. Undeclared options behave exactly as they did before. Actually, before this PR every single config option (ckan.site_url
,ckan.plugins
) was undeclared, so you already know, how they work.In order to declare an option, we have to specify its "key" (name). That's it, any option will turn into declared via simple line:
Everything else is completely optional, but can be useful sometimes:
Default value. All the declared options have
None
as the default value. You can change it. Note, declared "default" value has no effect in existing code. I.e,config.get(option)
will ignore it. I'll explain how to use declared defaults in the following sectionValidators. Just like in scheming, you can specify validators via string. There is two restrictions: validators must be idempotent(passing the value through the validators multiple times must produce the same result) and declared default must be valid(default value also can be validated in some cases)
Descriptions. Explain the purpose of the config option
sqlalchemy.url = postgresql://ckan_default:pass@localhost/ckan_test
- it's rather not a default value, but a placeholder, that can be adapted to the current environment.Ignored/internal values. Some config values added in code, thus a value from the config file is ignored. Some values(
__file__
,testing
,SECRET_KEY
), can be set in the config file, but CKAN does not expect that and we cannot guarantee correct behavior when these variables are added to the config file. Nevertheless, if something can be found in the configuration object, it must be declared.Huge definitions. If you want to declare more than 5-10 options, it's better to put declaration into a dictionary and use
declaration.load_dict
:That's how I defined CKAN core config options. You even may want to do something like:
Check the documentation of
ckan config describe
CLI command below. It explains how to get examples of such dictionaries.Key
Why do we need a second parameter,
key
? It's a special object, that can be used in order to construct config option names. It can be compared with a string and produces quite an expected result:But the main reason why I've added
Key
is dynamic patterns:In this way, we can declare groups of options. For example, everything in the config file with the prefix
sqlalchemy.
will be passed down to the engine constructor. We can declare this fact:The only argument of the
dynamic
method is the name of the dynamic group. It has no special meaning and is used only for debugging.How it changes CKAN
By default, it doesn't change anything. I expect it to change in the future, but for now, I want this functionality to be used explicitly.
There are two ways of using declarations in code.
Config object
There are three new methods added to the
CKANConfig
(toolkit.config
):config.safe(OPTION_NAME)
. Returns either the current value of the config option if it is present, or declared a default. The following two expressions are the same, but usingsafe
allows us to re-use the same default value everywhereWhen using
safe
with the undeclared option,None
is used as default.config.normalized(OPTION_NAME)
. Similar to thesafe
, but returns validated valueconfig.subset(key_with_dynamic_part)
. Simplified way to get group of options. It expectsKey
as an argument, so I've addedkey
to the toolkit:Config modes
There are three new boolean configuration options, which are disabled by default:
config.safe
. When enabled, copy the default value from the declaration into the config object, when the corresponding config option is missing. In this modeconfig.safe(key)
identical toconfig[key]
and does no extra work. It's disabled because in safe mode all the declared options have value(None
in the worst case). It means thatconfig.get('ckan.datasets_per_page', DEFAULT)
will never returnDEFAULT
, becauseckan.datasets_per_page
always has value in safe mode. Even though it's not a problem in most cases, it can break some extensions.config.normalized
. Similar to the previous one, but transforms config options in addition. For example, the declared booleandebug
option will turn intoTrue
, while without normalized mode it has a value of"true"
. In this way we can avoid such errors:config.normalized(key)
is identical toconfig[key]
and does no extra workconfig.strict
. Performs validation of config options when the application is loaded. If there are any invalid config options, CKAN won't start and errors will be printed to the output. Note, that strict mode does not assume safe mode. That means that if you haven't specifiedckan.datasets_per_page
config option in your config file, in safe mode CKAN will start, but in the un-safe mode, it will print thatckan.datasets_per_page
is not a valid integer. Actually, that's the main reason why it's not enabled by default.I expect that extensions will migrate to
config.safe/normalized
during the next few years and then we can enable all these modes by default. After that, we'll replaceconfig.safe/normalized
with the direct access to config options(config[key]
) and make these modes permanent.Documentation bonus
I've exported declarations to the doc-builder and now we are using declared defaults instead of the hardcoded ones. BTW, some defaults in the docs were out of date :(
CLI
There isa new branch of commands available in CLI:
ckan config
declaration [PLUGIN...]
. Prints an example of configuration for the plugin(based on it's config declarations)prints something like
Ideally, one will use this command before enabling the plugin and copy output to the config file.
Core declarations can be printed in that way:
and now this command is used internally when generating CKAN config (
ckan generate config
). This means we don't have to update config template anymore. All the declared options will appear there automaticallydescribe [PLUGIN...]
. Print the config declaration for the given plugin. Mainly can be used as an example for writing own declarations.prints
Just as in
declaration
, use--core
if you want to see core declarations.In addition, you can print examples of YAML, JSON, TOML, or python's dict declarations, that can be used together with the
declaration.load_dict
:search
. Search for the config option using patternprints
if you want to include declarations from the disabled plugins, add them via
-i
flag:--with-default
/--with-current
flags print default/current values.--no-custom
flag filters out all the options, which currently have a value that is different from the default.--custom-only
flag shows only customized (non-default) optionsundeclared
. Shows all the options available in the config, that are not declared by any of enabled pluginsIf you know that some of the options are declared by disabled plugins, you can use
-i
flag in order to reduce the output:validate
. Allows you to verify that your configuration is valid even if strict mode is disabled. As in the previous command, use-i
if you want to validate in addition configuration of disabled plugins:Note, that you'll likely get some errors that will disappear in safe mode. I've explained it in the
config.strict
description.