Plugins and extension types

Extension types

An extension type is a set of classes registered under a category and a type name. For example, the PostgreSQL layer is the type postgres of the category layer:

@gws.ext.config.layer('postgres')
class Config(gws.base.database.layer.Config):
    """Layer that shows features from a PostgreSQL table."""


@gws.ext.object.layer('postgres')
class Object(gws.base.database.layer.Object):
    ...
DecoratorRegisters
gws.ext.object.<category>('<type>')the object class
gws.ext.config.<category>('<type>')the configuration class
gws.ext.props.<category>('<type>')the props class, used for client types

A type that is created from the configuration needs a registered Config. A registered Props class is needed only if the props differ from the base class.

The decorated classes get two attributes: extName, the full name like gws.ext.object.layer.postgres, and extType, the type name like postgres.

Categories

The categories are listed in gws/ext/types.txt. An extension implements the interface of its category, usually by extending a base class from gws.base:

CategoryInterfacePurpose
actionActionactions that provide commands
applicationApplicationthe application
authMethodAuthMethodhow credentials arrive: login form, HTTP basic, token
authMultiFactorAdapterAuthMultiFactorAdaptersecond authentication factors: e-mail, TOTP
authProviderAuthProvideruser sources: file, database, LDAP
authSessionManagerAuthSessionManagersession storage
cliNodeCLI commands
databaseProviderDatabaseProviderdatabase connections
exporterExporterfeature export formats
finderFindersearch sources
helperNodeapplication-wide services
layerLayermap layers
legendLegendlegend sources
mapMapmaps
modelModeldata models
modelFieldModelFieldmodel field types
modelValidatorModelValidatorfield validators
modelValueModelValuecomputed field values
modelWidgetModelWidgetclient widgets for fields
owsProviderOwsServiceProviderOWS data sources
owsServiceOwsServiceOWS services: WMS, WMTS, WFS, CSW
printerPrinterprinters
projectProjectprojects
storageProviderStorageProviderbackends for the client storage
templateTemplatetemplate types

To add a category, add it to types.txt and run make.sh spec.

Using extension types

In the configuration, a list or a property of an extension category takes objects with a type key:

models+ { type "postgres" tableName "streets" }

In Config classes, such properties are declared with gws.ext.config.<category>. In code, the object is created with the category as class reference, and the type selects the class:

class Config(gws.Config):
    models: Optional[list[gws.ext.config.model]]
    """Data models."""


class Object(gws.Node):
    def configure(self):
        self.models = self.create_children(gws.ext.object.model, self.cfg('models'))

Helpers (category helper) are application-wide services, configured once in the helpers list. Get them with self.root.app.helper('<type>').

Registration

The decorators do nothing at runtime. The spec generator finds them in the source code, see Specs. So an extension type is available as soon as its module is in a package under gws, or in an external plugin listed in the manifest. The module is imported only when the type is used.

Example

A complete validator plugin, gws/plugin/model_validator/regex/__init__.py:

"""Regex validator for strings."""

import re

import gws
import gws.base.model.validator


@gws.ext.config.modelValidator('regex')
class Config(gws.base.model.validator.Config):
    """Checks that a value matches a regular expression."""

    regex: gws.Regex
    """Regular expression, matched anywhere in the value unless anchored."""


@gws.ext.object.modelValidator('regex')
class Object(gws.base.model.validator.Object):
    regex: str

    def configure(self):
        self.regex = self.cfg('regex')

    def validate(self, field, feature, mc):
        val = feature.attributes.get(field.name)
        if not isinstance(val, str):
            return False
        return re.search(self.regex, val) is not None

CLI commands

A CLI command is a method of a class in the category cli. The class extends gws.Node, the method takes one parameter that extends CliParams:

import gws


class CountParams(gws.CliParams):
    projectUid: str
    """Project uid."""


@gws.ext.object.cli('hello')
class Object(gws.Node):

    @gws.ext.command.cli('helloCount')
    def count(self, p: CountParams):
        """Count the layers of a project."""

        root = gws.load_root()
        ...

The command name helloCount is called as gws hello count -projectUid .... The method docstring is the help text, the parameter fields are the options. CLI objects are not part of the object tree, the command loads the stored configuration itself if it needs it. See cli for an example.