Package configuration¶
Migrating your CKAN extension's package configuration from legacy setups
(setup.py or setup.cfg) to the modern pyproject.toml format (conforming
to PEP 518 and PEP 621) is a key step in modernization. This transition
simplifies dependencies, standardizes packaging metadata, and prepares your
extension for modern Python packaging tools.
Metadata and Dependencies¶
Below is a comparison showing how legacy packaging files translate into a modern pyproject.toml.
from setuptools import setup, find_packages
setup(
name='ckanext-myextension',
version='0.1.0',
description='A CKAN extension',
long_description='A longer description',
author='John Doe',
author_email='john@example.com',
url='https://github.com/my/ckanext-myextension',
license='AGPL',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
namespace_packages=['ckanext'],
zip_safe=False,
install_requires=[
'requests',
'pandas',
],
)
[metadata]
name = ckanext-myextension
version = 0.1.0
description = A CKAN extension
long_description = file: README.md
long_description_content_type = text/markdown
author = John Doe
author_email = john@example.com
url = https://github.com/my/ckanext-myextension
license = AGPL
[options]
packages = find_namespace: # (1)!
install_requires =
requests
pandas
[options.packages.find]
exclude =
tests
tests.*
- Uses
find_namespace:to automatically discover package directories (includingckanextnamespaces).
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "ckanext-myextension"
version = "0.1.0"
description = "A CKAN extension"
readme = "README.md"
authors = [
{ name = "John Doe", email = "john@example.com" }
]
license = { text = "AGPL" }
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GNU Affero General Public License v3",
]
keywords = [ "CKAN", "extension"]
dependencies = [
"requests",
"pandas",
]
requires-python = ">=3.10"
[project.urls]
Homepage = "https://github.com/my/ckanext-myextension"
[tool.setuptools.packages]
find = {}
Entry Points¶
CKAN extensions register their plugins, command line interfaces, and translation extractors via Python entry points.
Babel Internationalization (i18n)¶
Historically, CKAN extensions specified extraction mappings using the
message_extractors configuration inside setup.py. Integration with
setuptools is still required, but we aim for eventual switch to
pyproject.toml, when babel fully supports
it.
Babel Configuration Migration
Create a babel.cfg file in the root of your extension to specify what files and templates to parse. Move message_extractors configuration from setup.py to babel.cfg. Typically you'll see following lines in the setup.py:
setup(
message_extractors={
"ckanext": [
("**.py", "python", None),
("**.js", "javascript", None),
("**/templates/**.html", "ckan", None),
],
}
)
This translates to the following babel.cfg:
ckanextractor is basically ajinja2extractor that loads all Jinja2 extensions used by CKAN.
Register the custom ckan extractor under [project.entry-points."babel.extractors"] in your pyproject.toml (as shown above).
Update setup.cfg. It already should contain following lines:
[extract_messages]
keywords = translate isPlural
add_comments = TRANSLATORS:
output_file = ckanext/myext/i18n/ckanext-myext.pot
width = 80
[init_catalog]
domain = ckanext-myext
input_file = ckanext/myext/i18n/ckanext-myext.pot
output_dir = ckanext/myext/i18n
[update_catalog]
domain = ckanext-myext
input_file = ckanext/myext/i18n/ckanext-myext.pot
output_dir = ckanext/myext/i18n
previous = true
[compile_catalog]
domain = ckanext-myext
directory = ckanext/myext/i18n
statistics = true
You need to add mapping_file reference to the new babel.cfg under extract_messages section:
Now you can perform extraction using setuptools integration:
You can also use standard pybabel commands, but in this way configuration
from setup.cfg will not be taken into account:
Python namespaces¶
CKAN extensions are relying on legacy namespace definitions. To make package
discoverable and avoid conflicts with other packages, make sure you have
following lines in setup.cfg and pyproject.toml:
- This option has no analogues for
pyproject.tomland must be kept insidesetup.cfg
- This option is responsible for automatic package discovery.
Note
To keep related options in same file, you can move tool.setuptools.packages
from pyproject.toml to setup.cfg
Final version¶
When migration completed, you should have following content in specified files
Content from here is distributed between other files. Still, setup.py with the minimal content is required by setuptools.
This was specified inside setup.py in past. You can keep these options inside setup.py, but moving them to a separate file will may reduce friction if future and reduce dependency on setuptools.
This file contains the options section with legacy namespace-specific configuration that cannot be moved to pyproject.toml. In future, if CKAN extensions move to PEP 420 or stop using namespaces at all, this section can be dropped.
Additionally it contains babel configuration under extract_messages, init_catalog, update_catalog, compile_catalog sections. These sections may move to pyproject.toml when babel will improve its support.
[options]
namespace_packages = ckanext
[extract_messages]
mapping_file = babel.cfg
keywords = translate isPlural
add_comments = TRANSLATORS:
output_file = ckanext/myextension/i18n/ckanext-myextension.pot
width = 80
[init_catalog]
domain = ckanext-myextension
input_file = ckanext/myextension/i18n/ckanext-myextension.pot
output_dir = ckanext/myextension/i18n
[update_catalog]
domain = ckanext-myextension
input_file = ckanext/myextension/i18n/ckanext-myextension.pot
output_dir = ckanext/myextension/i18n
previous = true
[compile_catalog]
domain = ckanext-myextension
directory = ckanext/myextension/i18n
statistics = true
This is the main source for package's metadata. Prioritize it over other formats when adding new package's metadata or configuring additional tool.
[project]
name = "ckanext-myextension"
version = "0.1.0"
description = "Description of the project"
readme = "README.md"
license = {text = "AGPL"}
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
]
keywords = [ "CKAN", ]
dependencies = ["typing_extensions"]
authors = [
{name = "John Doe", email = "john@example.com"},
]
maintainers = [
{name = "John Doe", email = "john@example.com"},
]
[project.urls]
Homepage = "https://github.com/my/ckanext-myextension"
Documentation = "https://my.github.io/ckanext-myextension/"
[project.entry-points."ckan.plugins"]
myextension = "ckanext.myextension.plugin:MyPlugin"
[project.entry-points."babel.extractors"]
ckan = "ckan.lib.extract:extract_ckan"
[project.optional-dependencies]
test = ["pytest-ckan", "pytest-pretty", "pytest-playwright"]
docs = ["zensical"]
dev = ["pytest-ckan", "pytest-pretty", "zensical", "pre-commit", "pytest-playwright"]
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages]
find = {}
Automated Migration with ini2toml¶
If you already have a setup.cfg file, you can automate a large part of the migration using the ini2toml tool. It parses the INI/CFG syntax and translates metadata and options to the equivalent pyproject.toml specifications.
Step 1: Install ini2toml¶
It is recommended to install the tool with the [full] extra options to ensure comments are preserved and formatting is optimized:
Step 2: Convert setup.cfg¶
Run ini2toml targeting your setup.cfg and outputting the result to pyproject.toml:
Converting setup.py directly
ini2toml only supports converting .cfg or .ini files. It cannot parse
or translate python code from setup.py. If your extension only has a
setup.py, you should:
-
convert your
setup.pytosetup.cfgusing a tool like setup-py-upgrade or setutools-py2cfg. -
translate the parameters manually into a new
pyproject.tomlfollowing the examples above.
Step 3: Verify and Clean Up¶
After generating the pyproject.toml file:
-
Review the contents: Ensure that the tool has correctly moved standard metadata into the
[project]table andsetuptools-specific rules into[tool.setuptools]. -
Test build execution: Run standard packaging validations to verify your new configuration:
- Delete legacy configurations: Once verification succeeds, remove
unnecessary section from
setup.cfgandsetup.pyfiles to finalize the modernization.