Python target

Python is Nirum’s most actively maintained target language. If you want to evaluate Nirum’s every feature Python would be the best choice.

Settings

Nirum’s Python target provides its own settings. The below settings can be configured in package.toml’s targets.python.* fields.

name (required): PyPI distribution name

Whereas Nirum packages don’t have a package name, generated Python packages need its own unique name. They are called by several terms: package name, distribution name, PyPI handle, etc. It’s used to refer the package to install in pip install command, and placed following https://pypi.python.org/pypi/ URL.

# Example
[targets.python]
name = "py-foobar"  # will be submitted to: pypi.python.org/pypi/py-foobar

minimum_runtime: Ensure Python runtime library’s minimum version

Generated Python object code depends on Python nirum package, the runtime library for Nirum. As it’s separately distributed, you might face with subtle incompatibilities between versions. In order to prevent such incompatibilities by ensuring the minimum version, Python target provides a minimum_runtime option. It takes a version string of nirum package which follows Semantic Versioning.

# Example
[targets.python]
name = "py-foobar"
minimum_runtime = "0.3.9"  # requires nirum >= 0.3.9

The configured version specifier goes to install_requires list of setup.py script.

renames: Rename module paths

Sometimes you may need to use other name in Python than package names defined by Nirum IDL. For example, you may choose a general term statistics for a module name, but need to use an other name in Python since it’s reserved by Python standard library. In case, renames configuration replace package names when it’s compiled to Python.

It’s a table of strings where keys are module paths to be replaced and values are module paths to replace with. Note that keys and values are not Python import paths but Nirum IDL module paths. That means you can’t use names like __foo__ because Nirum IDL doesn’t allow more than twice continued underscores/hyphens.

The following example replaces statistics to rpc.statistics:

[targets.python.renames]
statistics = "rpc.statistics"

The renames table is recursively applied to submodules. If you have 4 modules and submodules like statistics, statistics.products, statistics.users, and statistics.users.friends, they are renamed to rpc.statistics.products, rpc.statistics, rpc.statistics.products, rpc.statistics.users, and rpc.statistics.users.friends.

Though it’s applied only from root modules to submodules. Even if there’re some matched module paths in the middle they aren’t renamed. For example, whereas statistics.foo is renamed to rpc.statistics.foo, foo.statistics is remained without renaming.

Names to be replaced can contain periods. For example, the following example renames foo.bar.baz to new-name.baz:

[targets.python.renames]
"foo.bar" = "new-name"  # Note that the key is quoted.

Entry points

All generated Python modules and classes are registered as setuptools’ entry points so that an application can discover them at runtime. There are two groups:

nirum.modules : It maps Nirum modules to Python modules. Nirum module paths are normalized to avoid underscores and upper letters and use hyphens and lower letters instead, e.g., foo-bar.baz. The table works well with renames settings as well.

nirum.classes : It maps Nirum types (including services) to Python classes. Nirum type names are qualified and their leading module paths are also normalized (the same rule to nirum.modules applies here).

Here’s an example to discover Python modules generated from Nirum modules.

>>> import pkg_resources  # provided by setuptools
>>> module_entry_points = list(pkg_resources.iter_entry_points('nirum.modules'))
>>> module_entry_points
[EntryPoint.parse('foo = foo'),
 EntryPoint.parse('foo.bar = foo.bar'),
 EntryPoint.parse('qux = qux'),
 ...]
>>> module_entry_points[1]
EntryPoint.parse('foo.bar = foo.bar')
>>> module_entry_points[0].resolve()

Note that you need to call .resolve() method as well.

Finding a Python class generated from a Nirum type can be in a similar way:

>>> list(pkg_resources.iter_entry_points('nirum.classes', 'foo.NoSuchName'))
[]
>>> list(pkg_resources.iter_entry_points('nirum.classes', 'foo.bar.some-type'))
[EntryPoint.parse('foo.bar.some-type = foo.bar:SomeType')]
>>> _[0].resolve()

As you can see, the second parameter of pkg_resources.iter_entry_points() purposes to filter entry points by the given name.