ckanext-editable-config


Nameckanext-editable-config JSON
Version 0.0.5 PyPI version JSON
download
home_pagehttps://github.com/ckan/ckanext-editable-config
SummaryNone
upload_time2024-04-24 09:21:21
maintainerNone
docs_urlNone
authorSergey Motornyuk
requires_pythonNone
licenseAGPL
keywords ckan
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            [![Tests](https://github.com/DataShades/ckanext-editable-config/workflows/Tests/badge.svg?branch=main)](https://github.com/DataShades/ckanext-editable-config/actions)

# ckanext-editable-config

Edit CKAN configuration in runtime.

This plugin registers a set of API action for overriding config options and
applying the changes to application without restarting the web server.

## Example

Change application's title:

* from Python:
  ```python
  tk.get_action("editable_config_update")(
    {"ignore_auth": True},
    {
      "change": {
        "ckan.site_title": "Updated title"
      },
    },
  )
  ```

* from CLI:
  ```sh
  ckanapi action editable_config_update change:'{"ckan.site_title": "Updated via ckanapi"}'
  ```

* from browser:
  ```javascript
  await fetch("/api/action/editable_config_update", {
    method: "POST",
    body: JSON.stringify({change: {"ckan.site_title": "Updated from JS"}}),
    headers: {"content-type": "application/json"},
  })
  ```

## Content

* [Requirements](#requirements)
* [Installation](#installation)
* [Usage](#usage)
* [Config settings](#config-settings)
* [API actions](#api-actions)
* [Troubleshooting](#troubleshooting)

## Requirements

Compatibility with core CKAN versions:

| CKAN version  | Compatible? |
|---------------|-------------|
| 2.9 and below | no          |
| 2.10          | yes         |
| master        | yes         |


## Installation

To install ckanext-editable-config:

1. Install the extension:
   ```sh
   pip install ckanext-editable-config
   ```

1. Add `editable_config` to the `ckan.plugins`.

## Usage

All the config options declared with the `editable: true` flag can be overriden
when the plugin is enabled. Every time the option is updated, it's new value
passed to the declared validators of the option. If any validator raises an
error, no changes happen. As long as option has proper validators configured,
it's safe to change its value.

There are 3 types of changes:

* `change`: option gets a new value.
* `revert`: option gets its previous value. For example, `ckan.site_title`
  intially has the value `CKAN`. Then you applied a `change` to it, setting
  title to `Updated title`. `revert` will set `ckan.site_title` back to
  `CKAN`. `revert` itself is a change, so double-revert doesn't change
  anything. It's not like `CTRL+Z`, rather it just swaps current and previous
  values. Only the latest and one before the latest changes are kept.
* `reset`: remove all customizations from the config option and reset it to the
  state defined in the config file.

The only action you really need to remember is `editable_config_update`. It
accepts 3 collections:

* `change`: dictionary with option names and their new values
* `revert`: list of options that will be reverted
* `reset`: list of options that will be reset to the state of config file.

For example, if you want to change the value of the site title to `Updated
title`, revert site description to the previous value(whatever it was) and
reset all customizations of the About page, call the `editable_config_update`:
```python
tk.get_action("editable_config_update")(
    {"ignore_auth": True},
    {
        "change": {"ckan.site_title": "Updated title"},
        "revert": ["ckan.site_description"],
        "reset": ["ckan.site_about"],
    },
)
```

**Note**: all actions require sysadmin access.

Check [API actions](#api-actions) and [troubleshooting](#troubleshooting)
sections below if you need something more sophisticated.

## Config settings

```ini

# Additional options that can be changed via API even if they don't have
# `editable` flag enabled. Use it only when you are sure that changinging the
# option value won't break the application. For example, adding
# `ckan.datasets_per_page` here is relatively safe, because it is validated
# as an integer(but it's not 100% safe, because it's possible to assign
# negative number to this option and validators won't complain).
# (optional, default: )
ckanext.editable_config.options.extra_editable = ckan.datasets_per_page ckan.auth.user_create_groups

# Narrow down the list of editable config options. If this option is not
# empty, only `editable` options that are listed here can be changed via API.
# Attempt to change any other option, disregarding its `editable` flag, will
# cause a validation error.
# (optional, default: )
ckanext.editable_config.options.whitelist = ckan.site_title ckan.site_description

# Disable `editable` flag for the specified options. It's not possible to
# change the value of option mentioned here, even if it's `editable`.
# (optional, default: )
ckanext.editable_config.options.blacklist = ckan.site_title ckan.site_description

# Minimal number of seconds between two consequent change detection cycles.
# Basically, if you set 60 as a value, plugin will check config chnages once
# in a minute. In this way you can reduce number of DB queries and avoid
# unnecesarry checks when static files are served by CKAN.  Note, these
# queries are pretty fast(1ms), so you won't notice any significant
# performance improvement by setting any non-zero value here. And it means
# that any config overrides applied by API actions may not be visible
# immediately - you'll have to wait `charge_timeout` seconds in worst case.
# (optional, default: 0)
ckanext.editable_config.charge_timeout = 10

# Additional validators that are used when config option overrides are
# applied. Use this option if CKAN validators are not strict enough and you
# see the way to break the application by providing valid values for options.
# (optional, default: {})
ckanext.editable_config.additional_validators = {"ckan.site_title": "less_than_100 do_not_contain_exclamation_mark"}

# Remove "Config" tab from CKAN's Admin UI.
# (optional, default: false)
ckanext.editable_config.disable_admin_config_tab = True

# Replace "Config" tab from CKAN's Admin UI with basic form for editable
# config options.
# (optional, default: false)
ckanext.editable_config.replace_admin_config_tab = True

# Automatically convert any existing config overrides added via CKAN's Admin
# UI into editable config overrides.
# (optional, default: false)
ckanext.editable_config.convert_core_overrides = True

```

## API actions

### `editable_config_last_check`

Date and time of the last change detection cycle.

Returns:
* `last_check`(`str`): ISO datetime

Example:
```sh
$ ckanapi action editable_config_last_check
{
  "last_check": "2023-07-26T08:24:48.013121"
}

```
### `editable_config_list`

All editable config options. Every modified option includes dictionary
containing override details.

Returns:
* editable options(`dict[str, Any]`): key: option name; value: dictionary with
  current value and optional modification details

Example:
```sh
$ ckanapi action editable_config_list
{
  "ckan.site_about": {
    "option": null,
    "value": ""
  },
  "ckan.site_title": {
    "option": {
      "key": "ckan.site_title",
      "prev_value": "CKAN",
      "updated_at": "2023-07-25T21:44:52.434211",
      "value": "Updated title"
    },
    "value": "Updated title"
  }
}

```

### `editable_config_change`

Change multiple config options using `options` mapping with pairs of options
name and option value.

Returns:
* updated options(`dict[str, Any]`): key: option name; value: dictionary
  with updated option details

Example:
```sh
$ ckanapi action editable_config_change options='{"ckan.site_title": "Updated"}'
{
  "ckan.site_title": {
    "key": "ckan.site_title",
    "prev_value": "CKAN",
    "updated_at": "2023-07-26T08:28:04.988247",
    "value": "Updated"
  }
}

```

### `editable_config_revert`

Swap current and previous value of the option using `keys` list of option names.

Returns:
* updated options(`dict[str, Any]`): key: option name; value: dictionary with
  option details after revert

Example:
```sh
$ ckanapi action editable_config_revert keys=ckan.site_title
{
  "ckan.site_title": {
    "key": "ckan.site_title",
    "prev_value": "Updated",
    "updated_at": "2023-07-26T08:28:59.667917",
    "value": "CKAN"
  }
}
```

### `editable_config_reset`

Remove optio modifications using `keys` list of option names.

Returns:
* removed options(`dict[str, Any]`): key: option name; value: dictionary with
  removed option details

Example:
```sh
$ ckanapi action editable_config_reset keys=ckan.site_title
{
  "ckan.site_title": {
    "key": "ckan.site_title",
    "prev_value": "Updated",
    "updated_at": "2023-07-26T08:28:59.667917",
    "value": "CKAN"
  }
}

```

### `editable_config_update`

Combine `change`, `revert` and `reset` actions into a single action. Accepts
`change` dictionary, `revert` list, and `reset` list. Swiss-knife that exists
merely for bulk operations.

Returns:
* updated option(`dict[str, Any]`): key: option name; value: dictionary
  with current value and optional modification details

Example
```sh
$ ckanapi action editable_config_update \
$    change='{"ckan.site_about": "Updated via ckanapi"}' \
$    revert=ckan.site_title \
$    reset=ckan.site_custom_css
{
  "change": {
    "ckan.site_about": {
      "key": "ckan.site_about",
      "prev_value": "Updated via ckanapi",
      "updated_at": "2023-07-26T08:35:25.462359",
      "value": "Updated via ckanapi"
    }
  },
  "reset": {
    "ckan.site_custom_css": {
      "key": "ckan.site_custom_css",
      "prev_value": "",
      "updated_at": "2023-07-26T08:34:26.372150",
      "value": "body{color: red;}"
    }
  },
  "revert": {
    "ckan.site_title": {
      "key": "ckan.site_title",
      "prev_value": "CKAN",
      "updated_at": "2023-07-26T08:35:25.536150",
      "value": "Updated"
    }
  }
}

```

## Troubleshooting

> Changing `debug` or beaker-related options has no effect.

Most of options are checked by CKAN in runtime, when request is made. But
beaker settings, debug flag and a number of other options are used when WSGI
application is created and these options cannot be changed in runtime at the
moment. CKAN core requires some essential changes in initialization workflow
and additional safety measures before it become possible.

We'll consider expediency of this possibility in future, but for now this
problem has a low priority, so some options just should never be changed in
runtime.

> Changing `ckan.plugins` works with actions/validators/helpers, but not with
> middlewares or blueprints.

It falls under the previous problem. A number of plugin hooks are used when
application is initialized, namely: middlewares, blueprints, CLI commands and
translations. There is no reliable way to reset these items in runtime. So only
plugins that are not using corresponding interfaces can be enabled/disabled via
editable config.

But even if plugin can be safely enabled, it's not recommended. Modifying
plugins list via editable config may lead to changes in plugins order, which in
turn may cause other hard-to-track bugs. Finally, there is no way to tell
whether all the requirements of plugin are satisfied, so enabling/disabling it
in runtime potentially can break the whole application.

> Some options(ones that customized via built in AdminUI of CKAN) cannot be
> changed.

This plugin is not compatible with the CKAN's built-in AdminUI Config
form. Results are unpredictable when they are combined, so prefer to `Reset`
built-in config form and never use it if you going to rely on this plugin. Or,
at least, do not change the config option managed by AdminUI using the plugin's
API. There is an option that automatically saves any modifications from native
AdminUI as editable config options and then resets native AdminUI:
`ckanext.editable_config.convert_core_overrides`.


> Option saved with a value that prevent further modifications/breaks application.

You can either clean `editable_config_option` table removing all customizations
or just "bad" ones. Or you can combine
[ckanapi](https://pypi.org/project/ckanapi/) and environment variable
`CKANEXT_EDITABLE_CONFIG_DISABLE` in order to disable `editable_config` effect
and remove overrides via API:
```sh
CKANEXT_EDITABLE_CONFIG_DISABLE=1 ckanapi action editable_config_revert keys=BAD_OPTION_NAME
```


## License

[AGPL](https://www.gnu.org/licenses/agpl-3.0.en.html)

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/ckan/ckanext-editable-config",
    "name": "ckanext-editable-config",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "CKAN",
    "author": "Sergey Motornyuk",
    "author_email": "sergey.motornyuk@linkdigital.com.au",
    "download_url": "https://files.pythonhosted.org/packages/15/88/31cced656d0bb10f1c72bec6c231740120a69a0980c27e73c8ccb6862303/ckanext_editable_config-0.0.5.tar.gz",
    "platform": null,
    "description": "[![Tests](https://github.com/DataShades/ckanext-editable-config/workflows/Tests/badge.svg?branch=main)](https://github.com/DataShades/ckanext-editable-config/actions)\n\n# ckanext-editable-config\n\nEdit CKAN configuration in runtime.\n\nThis plugin registers a set of API action for overriding config options and\napplying the changes to application without restarting the web server.\n\n## Example\n\nChange application's title:\n\n* from Python:\n  ```python\n  tk.get_action(\"editable_config_update\")(\n    {\"ignore_auth\": True},\n    {\n      \"change\": {\n        \"ckan.site_title\": \"Updated title\"\n      },\n    },\n  )\n  ```\n\n* from CLI:\n  ```sh\n  ckanapi action editable_config_update change:'{\"ckan.site_title\": \"Updated via ckanapi\"}'\n  ```\n\n* from browser:\n  ```javascript\n  await fetch(\"/api/action/editable_config_update\", {\n    method: \"POST\",\n    body: JSON.stringify({change: {\"ckan.site_title\": \"Updated from JS\"}}),\n    headers: {\"content-type\": \"application/json\"},\n  })\n  ```\n\n## Content\n\n* [Requirements](#requirements)\n* [Installation](#installation)\n* [Usage](#usage)\n* [Config settings](#config-settings)\n* [API actions](#api-actions)\n* [Troubleshooting](#troubleshooting)\n\n## Requirements\n\nCompatibility with core CKAN versions:\n\n| CKAN version  | Compatible? |\n|---------------|-------------|\n| 2.9 and below | no          |\n| 2.10          | yes         |\n| master        | yes         |\n\n\n## Installation\n\nTo install ckanext-editable-config:\n\n1. Install the extension:\n   ```sh\n   pip install ckanext-editable-config\n   ```\n\n1. Add `editable_config` to the `ckan.plugins`.\n\n## Usage\n\nAll the config options declared with the `editable: true` flag can be overriden\nwhen the plugin is enabled. Every time the option is updated, it's new value\npassed to the declared validators of the option. If any validator raises an\nerror, no changes happen. As long as option has proper validators configured,\nit's safe to change its value.\n\nThere are 3 types of changes:\n\n* `change`: option gets a new value.\n* `revert`: option gets its previous value. For example, `ckan.site_title`\n  intially has the value `CKAN`. Then you applied a `change` to it, setting\n  title to `Updated title`. `revert` will set `ckan.site_title` back to\n  `CKAN`. `revert` itself is a change, so double-revert doesn't change\n  anything. It's not like `CTRL+Z`, rather it just swaps current and previous\n  values. Only the latest and one before the latest changes are kept.\n* `reset`: remove all customizations from the config option and reset it to the\n  state defined in the config file.\n\nThe only action you really need to remember is `editable_config_update`. It\naccepts 3 collections:\n\n* `change`: dictionary with option names and their new values\n* `revert`: list of options that will be reverted\n* `reset`: list of options that will be reset to the state of config file.\n\nFor example, if you want to change the value of the site title to `Updated\ntitle`, revert site description to the previous value(whatever it was) and\nreset all customizations of the About page, call the `editable_config_update`:\n```python\ntk.get_action(\"editable_config_update\")(\n    {\"ignore_auth\": True},\n    {\n        \"change\": {\"ckan.site_title\": \"Updated title\"},\n        \"revert\": [\"ckan.site_description\"],\n        \"reset\": [\"ckan.site_about\"],\n    },\n)\n```\n\n**Note**: all actions require sysadmin access.\n\nCheck [API actions](#api-actions) and [troubleshooting](#troubleshooting)\nsections below if you need something more sophisticated.\n\n## Config settings\n\n```ini\n\n# Additional options that can be changed via API even if they don't have\n# `editable` flag enabled. Use it only when you are sure that changinging the\n# option value won't break the application. For example, adding\n# `ckan.datasets_per_page` here is relatively safe, because it is validated\n# as an integer(but it's not 100% safe, because it's possible to assign\n# negative number to this option and validators won't complain).\n# (optional, default: )\nckanext.editable_config.options.extra_editable = ckan.datasets_per_page ckan.auth.user_create_groups\n\n# Narrow down the list of editable config options. If this option is not\n# empty, only `editable` options that are listed here can be changed via API.\n# Attempt to change any other option, disregarding its `editable` flag, will\n# cause a validation error.\n# (optional, default: )\nckanext.editable_config.options.whitelist = ckan.site_title ckan.site_description\n\n# Disable `editable` flag for the specified options. It's not possible to\n# change the value of option mentioned here, even if it's `editable`.\n# (optional, default: )\nckanext.editable_config.options.blacklist = ckan.site_title ckan.site_description\n\n# Minimal number of seconds between two consequent change detection cycles.\n# Basically, if you set 60 as a value, plugin will check config chnages once\n# in a minute. In this way you can reduce number of DB queries and avoid\n# unnecesarry checks when static files are served by CKAN.  Note, these\n# queries are pretty fast(1ms), so you won't notice any significant\n# performance improvement by setting any non-zero value here. And it means\n# that any config overrides applied by API actions may not be visible\n# immediately - you'll have to wait `charge_timeout` seconds in worst case.\n# (optional, default: 0)\nckanext.editable_config.charge_timeout = 10\n\n# Additional validators that are used when config option overrides are\n# applied. Use this option if CKAN validators are not strict enough and you\n# see the way to break the application by providing valid values for options.\n# (optional, default: {})\nckanext.editable_config.additional_validators = {\"ckan.site_title\": \"less_than_100 do_not_contain_exclamation_mark\"}\n\n# Remove \"Config\" tab from CKAN's Admin UI.\n# (optional, default: false)\nckanext.editable_config.disable_admin_config_tab = True\n\n# Replace \"Config\" tab from CKAN's Admin UI with basic form for editable\n# config options.\n# (optional, default: false)\nckanext.editable_config.replace_admin_config_tab = True\n\n# Automatically convert any existing config overrides added via CKAN's Admin\n# UI into editable config overrides.\n# (optional, default: false)\nckanext.editable_config.convert_core_overrides = True\n\n```\n\n## API actions\n\n### `editable_config_last_check`\n\nDate and time of the last change detection cycle.\n\nReturns:\n* `last_check`(`str`): ISO datetime\n\nExample:\n```sh\n$ ckanapi action editable_config_last_check\n{\n  \"last_check\": \"2023-07-26T08:24:48.013121\"\n}\n\n```\n### `editable_config_list`\n\nAll editable config options. Every modified option includes dictionary\ncontaining override details.\n\nReturns:\n* editable options(`dict[str, Any]`): key: option name; value: dictionary with\n  current value and optional modification details\n\nExample:\n```sh\n$ ckanapi action editable_config_list\n{\n  \"ckan.site_about\": {\n    \"option\": null,\n    \"value\": \"\"\n  },\n  \"ckan.site_title\": {\n    \"option\": {\n      \"key\": \"ckan.site_title\",\n      \"prev_value\": \"CKAN\",\n      \"updated_at\": \"2023-07-25T21:44:52.434211\",\n      \"value\": \"Updated title\"\n    },\n    \"value\": \"Updated title\"\n  }\n}\n\n```\n\n### `editable_config_change`\n\nChange multiple config options using `options` mapping with pairs of options\nname and option value.\n\nReturns:\n* updated options(`dict[str, Any]`): key: option name; value: dictionary\n  with updated option details\n\nExample:\n```sh\n$ ckanapi action editable_config_change options='{\"ckan.site_title\": \"Updated\"}'\n{\n  \"ckan.site_title\": {\n    \"key\": \"ckan.site_title\",\n    \"prev_value\": \"CKAN\",\n    \"updated_at\": \"2023-07-26T08:28:04.988247\",\n    \"value\": \"Updated\"\n  }\n}\n\n```\n\n### `editable_config_revert`\n\nSwap current and previous value of the option using `keys` list of option names.\n\nReturns:\n* updated options(`dict[str, Any]`): key: option name; value: dictionary with\n  option details after revert\n\nExample:\n```sh\n$ ckanapi action editable_config_revert keys=ckan.site_title\n{\n  \"ckan.site_title\": {\n    \"key\": \"ckan.site_title\",\n    \"prev_value\": \"Updated\",\n    \"updated_at\": \"2023-07-26T08:28:59.667917\",\n    \"value\": \"CKAN\"\n  }\n}\n```\n\n### `editable_config_reset`\n\nRemove optio modifications using `keys` list of option names.\n\nReturns:\n* removed options(`dict[str, Any]`): key: option name; value: dictionary with\n  removed option details\n\nExample:\n```sh\n$ ckanapi action editable_config_reset keys=ckan.site_title\n{\n  \"ckan.site_title\": {\n    \"key\": \"ckan.site_title\",\n    \"prev_value\": \"Updated\",\n    \"updated_at\": \"2023-07-26T08:28:59.667917\",\n    \"value\": \"CKAN\"\n  }\n}\n\n```\n\n### `editable_config_update`\n\nCombine `change`, `revert` and `reset` actions into a single action. Accepts\n`change` dictionary, `revert` list, and `reset` list. Swiss-knife that exists\nmerely for bulk operations.\n\nReturns:\n* updated option(`dict[str, Any]`): key: option name; value: dictionary\n  with current value and optional modification details\n\nExample\n```sh\n$ ckanapi action editable_config_update \\\n$    change='{\"ckan.site_about\": \"Updated via ckanapi\"}' \\\n$    revert=ckan.site_title \\\n$    reset=ckan.site_custom_css\n{\n  \"change\": {\n    \"ckan.site_about\": {\n      \"key\": \"ckan.site_about\",\n      \"prev_value\": \"Updated via ckanapi\",\n      \"updated_at\": \"2023-07-26T08:35:25.462359\",\n      \"value\": \"Updated via ckanapi\"\n    }\n  },\n  \"reset\": {\n    \"ckan.site_custom_css\": {\n      \"key\": \"ckan.site_custom_css\",\n      \"prev_value\": \"\",\n      \"updated_at\": \"2023-07-26T08:34:26.372150\",\n      \"value\": \"body{color: red;}\"\n    }\n  },\n  \"revert\": {\n    \"ckan.site_title\": {\n      \"key\": \"ckan.site_title\",\n      \"prev_value\": \"CKAN\",\n      \"updated_at\": \"2023-07-26T08:35:25.536150\",\n      \"value\": \"Updated\"\n    }\n  }\n}\n\n```\n\n## Troubleshooting\n\n> Changing `debug` or beaker-related options has no effect.\n\nMost of options are checked by CKAN in runtime, when request is made. But\nbeaker settings, debug flag and a number of other options are used when WSGI\napplication is created and these options cannot be changed in runtime at the\nmoment. CKAN core requires some essential changes in initialization workflow\nand additional safety measures before it become possible.\n\nWe'll consider expediency of this possibility in future, but for now this\nproblem has a low priority, so some options just should never be changed in\nruntime.\n\n> Changing `ckan.plugins` works with actions/validators/helpers, but not with\n> middlewares or blueprints.\n\nIt falls under the previous problem. A number of plugin hooks are used when\napplication is initialized, namely: middlewares, blueprints, CLI commands and\ntranslations. There is no reliable way to reset these items in runtime. So only\nplugins that are not using corresponding interfaces can be enabled/disabled via\neditable config.\n\nBut even if plugin can be safely enabled, it's not recommended. Modifying\nplugins list via editable config may lead to changes in plugins order, which in\nturn may cause other hard-to-track bugs. Finally, there is no way to tell\nwhether all the requirements of plugin are satisfied, so enabling/disabling it\nin runtime potentially can break the whole application.\n\n> Some options(ones that customized via built in AdminUI of CKAN) cannot be\n> changed.\n\nThis plugin is not compatible with the CKAN's built-in AdminUI Config\nform. Results are unpredictable when they are combined, so prefer to `Reset`\nbuilt-in config form and never use it if you going to rely on this plugin. Or,\nat least, do not change the config option managed by AdminUI using the plugin's\nAPI. There is an option that automatically saves any modifications from native\nAdminUI as editable config options and then resets native AdminUI:\n`ckanext.editable_config.convert_core_overrides`.\n\n\n> Option saved with a value that prevent further modifications/breaks application.\n\nYou can either clean `editable_config_option` table removing all customizations\nor just \"bad\" ones. Or you can combine\n[ckanapi](https://pypi.org/project/ckanapi/) and environment variable\n`CKANEXT_EDITABLE_CONFIG_DISABLE` in order to disable `editable_config` effect\nand remove overrides via API:\n```sh\nCKANEXT_EDITABLE_CONFIG_DISABLE=1 ckanapi action editable_config_revert keys=BAD_OPTION_NAME\n```\n\n\n## License\n\n[AGPL](https://www.gnu.org/licenses/agpl-3.0.en.html)\n",
    "bugtrack_url": null,
    "license": "AGPL",
    "summary": null,
    "version": "0.0.5",
    "project_urls": {
        "Homepage": "https://github.com/ckan/ckanext-editable-config"
    },
    "split_keywords": [
        "ckan"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7bb418136c9a779eb1270b33030a3826c2cb90330ae4578d78bad9f589dc0904",
                "md5": "2803ef113465cb2d81b937408b4ea9d5",
                "sha256": "64f0bc3543eb2fc11bb78216cc75b909add2d119f0afe4a798712616b843a026"
            },
            "downloads": -1,
            "filename": "ckanext_editable_config-0.0.5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2803ef113465cb2d81b937408b4ea9d5",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 42154,
            "upload_time": "2024-04-24T09:21:18",
            "upload_time_iso_8601": "2024-04-24T09:21:18.876657Z",
            "url": "https://files.pythonhosted.org/packages/7b/b4/18136c9a779eb1270b33030a3826c2cb90330ae4578d78bad9f589dc0904/ckanext_editable_config-0.0.5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "158831cced656d0bb10f1c72bec6c231740120a69a0980c27e73c8ccb6862303",
                "md5": "360ad041b0db63cddbc93a5a4b5c5645",
                "sha256": "b5b36e7698cc12f3b25387d4e8e108c5c416ff08aa03d20545ffb3501a96b3cf"
            },
            "downloads": -1,
            "filename": "ckanext_editable_config-0.0.5.tar.gz",
            "has_sig": false,
            "md5_digest": "360ad041b0db63cddbc93a5a4b5c5645",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 39680,
            "upload_time": "2024-04-24T09:21:21",
            "upload_time_iso_8601": "2024-04-24T09:21:21.027017Z",
            "url": "https://files.pythonhosted.org/packages/15/88/31cced656d0bb10f1c72bec6c231740120a69a0980c27e73c8ccb6862303/ckanext_editable_config-0.0.5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-24 09:21:21",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ckan",
    "github_project": "ckanext-editable-config",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "requirements": [],
    "lcname": "ckanext-editable-config"
}
        
Elapsed time: 0.24605s