Skip to content

Commit

Permalink
fix: Improved typing
Browse files Browse the repository at this point in the history
  • Loading branch information
constantinius committed Nov 26, 2021
1 parent 10e38cd commit 2272b3b
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 71 deletions.
62 changes: 31 additions & 31 deletions pygeofilter/parsers/fes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,53 +13,53 @@
from ... import ast
from ... import values
from ...util import parse_datetime, parse_duration
from .util import XMLParser, handle, handle_namespace
from .util import XMLParser, handle, handle_namespace, Element
from .gml import is_temporal, parse_temporal


class FESBaseParser(XMLParser):
@handle('Filter')
def filter_(self, node, predicate):
def filter_(self, node: Element, predicate):
return predicate

@handle('And')
def and_(self, node, lhs, rhs):
def and_(self, node: Element, lhs, rhs):
return ast.And(lhs, rhs)

@handle('Or')
def or_(self, node, lhs, rhs):
def or_(self, node: Element, lhs, rhs):
return ast.Or(lhs, rhs)

@handle('Not')
def not_(self, node, lhs):
def not_(self, node: Element, lhs):
return ast.Not(lhs)

@handle('PropertyIsEqualTo')
def property_is_equal_to(self, node, lhs, rhs):
def property_is_equal_to(self, node: Element, lhs, rhs):
return ast.Equal(lhs, rhs)

@handle('PropertyIsNotEqualTo')
def property_is_not_equal_to(self, node, lhs, rhs):
def property_is_not_equal_to(self, node: Element, lhs, rhs):
return ast.NotEqual(lhs, rhs)

@handle('PropertyIsLessThan')
def property_is_less_than(self, node, lhs, rhs):
def property_is_less_than(self, node: Element, lhs, rhs):
return ast.LessThan(lhs, rhs)

@handle('PropertyIsGreaterThan')
def property_is_greater_than(self, node, lhs, rhs):
def property_is_greater_than(self, node: Element, lhs, rhs):
return ast.GreaterThan(lhs, rhs)

@handle('PropertyIsLessThanOrEqualTo')
def property_is_less_than_or_equal_to(self, node, lhs, rhs):
def property_is_less_than_or_equal_to(self, node: Element, lhs, rhs):
return ast.LessEqual(lhs, rhs)

@handle('PropertyIsGreaterThanOrEqualTo')
def property_is_greater_than_or_equal_to(self, node, lhs, rhs):
def property_is_greater_than_or_equal_to(self, node: Element, lhs, rhs):
return ast.GreaterEqual(lhs, rhs)

@handle('PropertyIsLike')
def property_is_like(self, node, lhs, rhs):
def property_is_like(self, node: Element, lhs, rhs):
return ast.Like(
lhs,
rhs,
Expand All @@ -71,65 +71,65 @@ def property_is_like(self, node, lhs, rhs):
)

@handle('PropertyIsNull')
def property_is_null(self, node, lhs):
def property_is_null(self, node: Element, lhs):
return ast.IsNull(lhs, not_=False)

@handle('PropertyIsBetween')
def property_is_between(self, node, lhs, low, high):
def property_is_between(self, node: Element, lhs, low, high):
return ast.Between(lhs, low, high, False)

@handle('LowerBoundary', 'UpperBoundary')
def boundary(self, node, expression):
def boundary(self, node: Element, expression):
return expression

@handle('Equals')
def geometry_equals(self, node, lhs, rhs):
def geometry_equals(self, node: Element, lhs, rhs):
return ast.GeometryEquals(lhs, rhs)

@handle('Disjoint')
def geometry_disjoint(self, node, lhs, rhs):
def geometry_disjoint(self, node: Element, lhs, rhs):
return ast.GeometryDisjoint(lhs, rhs)

@handle('Touches')
def geometry_touches(self, node, lhs, rhs):
def geometry_touches(self, node: Element, lhs, rhs):
return ast.GeometryTouches(lhs, rhs)

@handle('Within')
def geometry_within(self, node, lhs, rhs):
def geometry_within(self, node: Element, lhs, rhs):
return ast.GeometryWithin(lhs, rhs)

@handle('Overlaps')
def geometry_overlaps(self, node, lhs, rhs):
def geometry_overlaps(self, node: Element, lhs, rhs):
return ast.GeometryOverlaps(lhs, rhs)

@handle('Crosses')
def geometry_crosses(self, node, lhs, rhs):
def geometry_crosses(self, node: Element, lhs, rhs):
return ast.GeometryCrosses(lhs, rhs)

@handle('Intersects')
def geometry_intersects(self, node, lhs, rhs):
def geometry_intersects(self, node: Element, lhs, rhs):
return ast.GeometryIntersects(lhs, rhs)

@handle('Contains')
def geometry_contains(self, node, lhs, rhs):
def geometry_contains(self, node: Element, lhs, rhs):
return ast.GeometryContains(lhs, rhs)

@handle('DWithin')
def distance_within(self, node, lhs, rhs, distance_and_units):
def distance_within(self, node: Element, lhs, rhs, distance_and_units):
distance, units = distance_and_units
return ast.DistanceWithin(lhs, rhs, distance, units)

@handle('Beyond')
def distance_beyond(self, node, lhs, rhs, distance_and_units):
def distance_beyond(self, node: Element, lhs, rhs, distance_and_units):
distance, units = distance_and_units
return ast.DistanceBeyond(lhs, rhs, distance, units)

@handle('Distance')
def distance(self, node):
def distance(self, node: Element):
return (float(node.text), node.attrib['uom'])

# @handle('BBOX')
# def geometry_bbox(self, node, lhs, rhs):
# def geometry_bbox(self, node: Element, lhs, rhs):
# # TODO: ast.BBox() seems incompatible
# pass

Expand Down Expand Up @@ -165,23 +165,23 @@ def literal(self, node):
return value

@handle_namespace(NAMESPACE_PRE_32, False)
def gml_pre_32(self, node):
def gml_pre_32(self, node: Element):
if is_temporal(node):
return parse_temporal(node, NSMAP_PRE_32)

return values.Geometry(parse_pre_v32(node))

@handle_namespace(NAMESPACE_32, False)
def gml_32(self, node):
def gml_32(self, node: Element):
if is_temporal(node):
return parse_temporal(node, NSMAP_32)

return values.Geometry(parse_v32(node))

@handle_namespace(NAMESPACE_33_CE, False)
def gml_33_ce(self, node):
def gml_33_ce(self, node: Element):
return values.Geometry(parse_v33_ce(node))

@handle_namespace(NAMESPACE_GEORSS, False)
def georss(self, node):
def georss(self, node: Element):
return values.Geometry(parse_georss(node))
22 changes: 14 additions & 8 deletions pygeofilter/parsers/fes/gml.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
from datetime import date, datetime, timedelta
from typing import Dict, Union
from lxml import etree

from ... import values
from ...util import parse_duration, parse_datetime
from .util import Element

Temporal = Union[date, datetime, timedelta, values.Interval]

def _parse_time_position(node, nsmap):

def _parse_time_position(node: Element, nsmap: Dict[str, str]) -> datetime:
return parse_datetime(node.text)


def _parse_time_instant(node, nsmap):
def _parse_time_instant(node: Element, nsmap: Dict[str, str]) -> datetime:
position = node.xpath('gml:timePosition', namespaces=nsmap)[0]
return _parse_time_position(position, nsmap)


def _parse_time_period(node, nsmap):
def _parse_time_period(node: Element,
nsmap: Dict[str, str]) -> values.Interval:
begin = node.xpath(
'gml:begin/gml:TimeInstant/gml:timePosition|gml:beginPosition',
namespaces=nsmap
Expand All @@ -28,11 +34,11 @@ def _parse_time_period(node, nsmap):
)


def _parse_valid_time(node, nsmap):
return parse_temporal(node[0])
def _parse_valid_time(node: Element, nsmap: Dict[str, str]) -> Temporal:
return parse_temporal(node[0], nsmap)


def _parse_duration(node, nsmap):
def _parse_duration(node: Element, nsmap: Dict[str, str]) -> timedelta:
return parse_duration(node.text)


Expand All @@ -45,10 +51,10 @@ def _parse_duration(node, nsmap):
}


def is_temporal(node):
def is_temporal(node: Element) -> bool:
return etree.QName(node).localname in PARSER_MAP


def parse_temporal(node, nsmap):
def parse_temporal(node: Element, nsmap: Dict[str, str]) -> Temporal:
parser = PARSER_MAP[etree.QName(node).localname]
return parser(node, nsmap)
16 changes: 11 additions & 5 deletions pygeofilter/parsers/fes/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@

from lxml import etree

from ... import ast
from .v11 import FES11Parser
from .v20 import FES20Parser
from .util import Element, ElementTree


def parse(xml: Union[str, etree._Element]):
def parse(xml: Union[str, Element, ElementTree]) -> ast.Node:
if isinstance(xml, str):
xml = etree.fromstring(xml)
root = etree.fromstring(xml)
else:
root = xml

# decide upon namespace which parser to use
namespace = etree.QName(xml).namespace
namespace = etree.QName(root).namespace
if namespace == FES11Parser.namespace:
return FES11Parser().parse(xml)
return FES11Parser().parse(root)
elif namespace == FES20Parser.namespace:
return FES20Parser().parse(xml)
return FES20Parser().parse(root)

raise ValueError(f'Unsupported namespace {namespace}')
12 changes: 7 additions & 5 deletions pygeofilter/parsers/fes/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
from ... import ast


Element = etree._Element
ElementTree = etree._ElementTree
ParseInput = Union[etree._Element, etree._ElementTree, str]


class NodeParsingError(ValueError):
pass

Expand Down Expand Up @@ -83,18 +88,15 @@ def __init__(cls, name, bases, dct):
cls.namespace_map = namespace_map


ParseInput = Union[etree._Element, etree._ElementTree, str]


class XMLParser(metaclass=XMLParserMeta):
namespace: Optional[str] = None
tag_map: dict
namespace_map: dict

def parse(self, input_: ParseInput) -> ast.Node:
if isinstance(input_, etree._Element):
if isinstance(input_, Element):
root = input_
elif isinstance(input_, etree._ElementTree):
elif isinstance(input_, ElementTree):
root = input_.getroot()
else:
root = etree.fromstring(input_)
Expand Down
14 changes: 9 additions & 5 deletions pygeofilter/parsers/fes/v11.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
from ... import ast
from .base import FESBaseParser
from .util import handle, ParseInput
from .util import handle, ParseInput, Element


class FES11Parser(FESBaseParser):
namespace = 'http://www.opengis.net/ogc'

@handle('Add')
def add(self, node, lhs, rhs):
def add(self, node: Element, lhs: ast.ScalarAstType,
rhs: ast.ScalarAstType) -> ast.Node:
return ast.Add(lhs, rhs)

@handle('Sub')
def sub(self, node, lhs, rhs):
def sub(self, node: Element, lhs: ast.ScalarAstType,
rhs: ast.ScalarAstType) -> ast.Node:
return ast.Sub(lhs, rhs)

@handle('Mul')
def mul(self, node, lhs, rhs):
def mul(self, node: Element, lhs: ast.ScalarAstType,
rhs: ast.ScalarAstType) -> ast.Node:
return ast.Mul(lhs, rhs)

@handle('Div')
def div(self, node, lhs, rhs):
def div(self, node: Element, lhs: ast.ScalarAstType,
rhs: ast.ScalarAstType) -> ast.Node:
return ast.Div(lhs, rhs)


Expand Down
Loading

0 comments on commit 2272b3b

Please sign in to comment.