-
Notifications
You must be signed in to change notification settings - Fork 3
Architecture sketches
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 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: TheFesKvpParser
can be used together with theFesFilterParser
or theOgcCqlParser
, 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 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 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