Skip to content

Architecture sketches

Ricardo Garcia Silva edited this page Feb 8, 2017 · 7 revisions

pyfes architecture planning

There shall be the following entities in pyfes:

  • parsers - That take care of parsing input data into pyfes types
  • pyfes types - Representation of the various types used in FES
  • serializers - That encode the fes types back into text based representations

In addition, there shall be additional helper functions for parsing and serializing from/to FES textual representations. These should simplify usage for external clients.

Lastly, there will be a mechanism for:

  • Registering custom fes:function expressions
  • Creating FilterCapabilities elements

parsers

Parsers convert text based formats, such as XML, into pyfes types. There shall be two types of parsers:

  • filter parsers - these are used for parsing just the filter part of a query (selection clause). This type of parser implements support for a single filter language. Example: FesFilterParser, OgcCqlParser

  • query parsers - these are used for parsing whole query expressions. Query parsers have to deal with the projection, selection and sorting clauses of a query. This type of parser implements support for a single encoding of a query. Example: FesXmlParser, FesKvpParser, JsonParser. These parsers also use the filter parsers. As such a query parser may be associated with multiple filter parsers. Example: The FesKvpParser can be used together with the FesFilterParser or the OgcCqlParser, depending on the filter language in use.

Each parser should specialize in a single format and also a single version of the FES standard. Each parser is implemented as a class with at least:

  • a version attribute specifying which version of FES it can understand;
  • a parser_type attribute specifying which type of parser it is (query or filter);
  • a parse(data) method;
  • query parsers also have a filter_parsers attribute, which should provide a list of the filter parsers that are used by the parser

The API for using a query parser should be something like:

>>> from pyfes.fes20.queryparsers import FesXmlParser 
>>> qp1 = FesXmlParser(etree_parser=None)
>>> qp1.version
'fes_v2.0.0'
>>> qp1.parser_type
'QUERY_PARSER'
>>> qp1.filter_parsers
[FesFilterParser]
>>> query = qp1.parse(data)

The API for a filter parser should be something like:

>>> from pyfes.fes20.filterparsers import FesFilterParser
>>> fp1 = FesFilterParser(etree_parser=None)
>>> fp1.version
'fes_v2.0.0'
>>> fp1.parser_type
'FILTER_PARSER'
>>> filter_ = fp1.parse(data)

Each parser object may accept some individual initialization options. In the previous examples, both parsers accept an etree_parser as an initialization parameter. This could be used to reuse previously instantiated (and configured) parsers for XML format. Other parsers may specify different initialization parameters, as appropriate.

The main interaction that clients will have with parsers will however be mediated by two functions: parse_expression() and parse_filter(). These functions should hide individual parser instances and provide a single point of entry for parsing each type of request (query or filter). They may have a usage like the following:

>>> from pyfes import parsers
>>> query = parsers.parse_query(query_data, **kwargs)
>>> filter_ = parsers.parse_filter(filter_data, **kwargs)

pyfes types

pyfes types implement most FES selection clause entities. They are modeled as classes, with a class for each operator and operand. There are the Expression types:

  • ValueReference
  • Literal
  • Function

And the Operator types:

  • PropertyIsLike
  • PropertyIsLessThan
  • ... all FES operators for filters

Each operator accepts a number of Expression types as parameter with the notable exception of the logic operators, which accept other operators as parameters.

Expression types are mostly string-based, but it would be nice if there were some sort of validation mechanism enforced whenever some of attribute was changed. For example, when a ValueReference is created, it would be interesting to validate its value following the naming rules for GML. This behavior should be defined at initialization time, like in the following example:

>>> from pyfes.expression import ValueReference
>> vr1 = ValueReference("my_value", validators=[validate_gml, validate_xpath])

For the Literal type, it would be interesting to accept a set of known types automatically, instead of only strings. For example, it would be nice to create a Literal with a datetime instance and have its type_ property automatically detected as a xs:datetime.

This type of validation behavior will probably be easier to implement by using python's built-in @property mechanism, or maybe by using descriptors. It would also be interesting if client code could define additional validation functions to apply to Expression types. Something like:

>>> def positive_validator(value):
...     """Checks for a number greater than zero."""
...     if value <= 0:
...         raise AttributeError
...     return True
...
>>>
>>> from pyfes.expression import Literal
>>> vr2 = Literal(-1, validators=[positive_validator])
AttributeError  # validation failed 

pyfes types will typically be created by parsers when parsing data. As such, their API can be verbose. In addition to this, initialization options will be set on the parser objects and then used when parsing. In the previous example, a more realistic use case would be to set the validators on the parser object and then have it validate Literals when parsing. Something like:

>>> expression = parsers.parse_expression(data, 
...                                       literal_validators=[],
...                                       value_reference_validators=[],
...                                       **parser_initialization_kwargs)

Even if the main way to create pyfes types is by using the parsers, it shall still be possible to create pyfes types explicitly, by instantiating them.

serializers

Serializers take care of converting from pyfes types back to textual representation. In a similar way to the parsers, there shall be two types of serializers:

  • filter serializers
  • query serializers