various fixes (#706)

This commit is contained in:
Tom Kralidis
2021-06-09 06:09:32 -04:00
committed by GitHub
parent 023f24d26b
commit 48efbdec53
7 changed files with 252 additions and 148 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ Depends: ${python3:Depends},
python3-unicodecsv,
python3-yaml,
${misc:Depends}
Suggests: python3-elasticsearch, python3-fiona, python3-geojson, python3-pygeometa, python3-pyproj, python3-rasterio
Suggests: python3-babel, python3-elasticsearch, python3-fiona, python3-geojson, python3-pygeometa, python3-pyproj, python3-rasterio
Description: pygeoapi provides an API to geospatial data.
.
This package the pygeoapi module for Python 3.
+1 -1
View File
@@ -198,7 +198,7 @@ resources:
options:
DATA_ENCODING: COMPLEX_PACKING
format:
name: GRIB2
name: GRIB
mimetype: application/x-grib2
test-data:
+132 -72
View File
@@ -33,32 +33,29 @@
Returns content from plugins and sets responses.
"""
import asyncio
from collections import OrderedDict
from copy import deepcopy
from datetime import datetime, timezone
from functools import partial
import json
import logging
import os
import uuid
import re
import asyncio
from typing import Any, Tuple, Union
import urllib.parse
from copy import deepcopy
from typing import Union, Any
from collections import OrderedDict
import uuid
from dateutil.parser import parse as dateparse
from shapely.wkt import loads as shapely_loads
from shapely.errors import WKTReadingError
import pytz
from shapely.errors import WKTReadingError
from shapely.wkt import loads as shapely_loads
from pygeoapi import __version__
from pygeoapi import l10n
from pygeoapi import __version__, l10n
from pygeoapi.linked_data import (geojson2geojsonld, jsonldify,
jsonldify_collection)
from pygeoapi.log import setup_logger
from pygeoapi.process.base import (
ProcessorExecuteError
)
from pygeoapi.process.base import ProcessorExecuteError
from pygeoapi.plugin import load_plugin, PLUGINS
from pygeoapi.provider.base import (
ProviderGenericError, ProviderConnectionError, ProviderNotFoundError,
@@ -68,6 +65,7 @@ from pygeoapi.provider.base import (
from pygeoapi.provider.tile import (ProviderTileNotFoundError,
ProviderTileQueryError,
ProviderTilesetIdNotFoundError)
from pygeoapi.util import (dategetter, DATETIME_FORMAT,
filter_dict_by_key_value, get_provider_by_type,
get_provider_default, get_typed_value, JobStatus,
@@ -119,7 +117,8 @@ OGC_RELTYPES_BASE = 'http://www.opengis.net/def/rel/ogc/1.0'
def pre_process(func):
""" Decorator that transforms an incoming Request instance specific to the
"""
Decorator that transforms an incoming Request instance specific to the
web framework (i.e. Flask or Starlette) into a generic :class:`APIRequest`
instance.
@@ -140,7 +139,8 @@ def pre_process(func):
class APIRequest:
""" Transforms an incoming server-specific Request into an object
"""
Transforms an incoming server-specific Request into an object
with some generic helper methods and properties.
.. note:: Typically, this instance is created automatically by the
@@ -229,7 +229,8 @@ class APIRequest:
@classmethod
def with_data(cls, request, supported_locales) -> 'APIRequest':
""" Factory class method to create an `APIRequest` instance with data.
"""
Factory class method to create an `APIRequest` instance with data.
If the request body is required, an `APIRequest` should always be
instantiated using this class method. The reason for this is, that the
@@ -242,6 +243,7 @@ class APIRequest:
:param supported_locales: List or set of supported Locale instances.
:returns: An `APIRequest` instance with data.
"""
api_req = cls(request, supported_locales)
if hasattr(request, 'data'):
# Set data from Flask request
@@ -256,11 +258,13 @@ class APIRequest:
@staticmethod
def _get_params(request):
""" Extracts the query parameters from the `Request` object.
"""
Extracts the query parameters from the `Request` object.
:param request: A Flask or Starlette Request instance
:returns: `ImmutableMultiDict` or empty `dict`
:returns: `ImmutableMultiDict` or empty `dict`
"""
if hasattr(request, 'args'):
# Return ImmutableMultiDict from Flask request
return request.args
@@ -271,14 +275,16 @@ class APIRequest:
return {}
def _get_locale(self, headers, supported_locales):
""" Detects locale from "lang=<language>" param or `Accept-Language`
"""
Detects locale from "lang=<language>" param or `Accept-Language`
header. Returns a tuple of (raw, locale) if found in params or headers.
Returns a tuple of (raw default, default locale) if not found.
:param headers: A dict with Request headers
:param supported_locales: List or set of supported Locale instances
:returns: A tuple of (str, Locale)
:param headers: A dict with Request headers
:param supported_locales: List or set of supported Locale instances
:returns: A tuple of (str, Locale)
"""
raw = None
try:
default_locale = l10n.str2locale(supported_locales[0])
@@ -310,7 +316,7 @@ class APIRequest:
Get `Request` format type from query parameters or headers.
:param headers: Dict of Request headers
:returns: format value or None if not found/specified
:returns: format value or None if not found/specified
"""
# Optional f=html or f=json query param
@@ -333,22 +339,23 @@ class APIRequest:
@property
def data(self) -> bytes:
""" Returns the additional data send with the Request (bytes). """
"""Returns the additional data send with the Request (bytes)"""
return self._data
@property
def params(self):
""" Returns the Request query parameters dict. """
"""Returns the Request query parameters dict"""
return self._args
@property
def path_info(self):
""" Returns the web server request path info part. """
"""Returns the web server request path info part"""
return self._path_info
@property
def locale(self) -> l10n.Locale:
""" Returns the user-defined locale from the request object.
"""
Returns the user-defined locale from the request object.
If no locale has been defined or if it is invalid,
the default server locale is returned.
@@ -360,34 +367,40 @@ class APIRequest:
to the :func:`l10n.get_plugin_locale` function, so that
the best match for the provider can be determined.
:returns: babel.core.Locale
:returns: babel.core.Locale
"""
return self._locale
@property
def raw_locale(self) -> Union[str, None]:
""" Returns the raw locale string from the `Request` object.
"""
Returns the raw locale string from the `Request` object.
If no "lang" query parameter or `Accept-Language` header was found,
`None` is returned.
Pass this value to the :func:`l10n.get_plugin_locale` function to let
the provider determine a best match for the locale, which may be
different from the locale used by pygeoapi's UI.
:returns: a locale string or None
:returns: a locale string or None
"""
return self._raw_locale
@property
def format(self) -> Union[str, None]:
""" Returns the content type format from the
"""
Returns the content type format from the
request query parameters or headers.
:returns: Format name or None
:returns: Format name or None
"""
return self._format
def get_linkrel(self, format_: str) -> str:
""" Returns the hyperlink relationship (rel) attribute value for
"""
Returns the hyperlink relationship (rel) attribute value for
the given API format string.
The string is compared against the request format and if it matches,
@@ -396,25 +409,28 @@ class APIRequest:
the relationship 'self' is returned as well (JSON is the default).
:param format_: The format to compare the request format against.
:returns: A string 'self' or 'alternate'.
:returns: A string 'self' or 'alternate'.
"""
fmt = format_.lower()
if fmt == self._format or (fmt == F_JSON and not self._format):
return 'self'
return 'alternate'
def is_valid(self, additional_formats=None) -> bool:
""" Returns True if:
"""
Returns True if:
- the format is not set (None)
- the requested format is supported
- the requested format exists in a list if additional formats
.. note:: Format names are matched in a case-insensitive manner.
:param additional_formats: Optional additional supported formats list
:returns: A boolean
:param additional_formats: Optional additional supported formats list
:returns: bool
"""
if not self._format:
return True
if self._format in FORMAT_TYPES.keys():
@@ -425,7 +441,8 @@ class APIRequest:
def get_response_headers(self, force_lang: l10n.Locale = None,
force_type: str = None) -> dict:
""" Prepares and returns a dictionary with Response object headers.
"""
Prepares and returns a dictionary with Response object headers.
This method always adds a 'Content-Language' header, where the value
is determined by the 'lang' query parameter or 'Accept-Language'
@@ -444,10 +461,11 @@ class APIRequest:
If an API response always needs to be in the same
language, 'force_lang' should be set to that language.
:param force_lang: An optional Content-Language header override.
:param force_type: An optional Content-Type header override.
:returns: A header dict
:param force_lang: An optional Content-Language header override.
:param force_type: An optional Content-Type header override.
:returns: A header dict
"""
headers = HEADERS.copy()
l10n.set_response_language(headers, force_lang or self._locale)
if force_type:
@@ -505,9 +523,10 @@ class API:
@pre_process
@jsonldify
def landing_page(self, request: Union[APIRequest, Any]):
def landing_page(self,
request: Union[APIRequest, Any]) -> Tuple[dict, int, str]:
"""
Provide API
Provide API landing page
:param request: A request object
@@ -594,7 +613,8 @@ class API:
return headers, 200, to_json(fcm, self.pretty_print)
@pre_process
def openapi(self, request: Union[APIRequest, Any], openapi):
def openapi(self, request: Union[APIRequest, Any],
openapi) -> Tuple[dict, int, str]:
"""
Provide OpenAPI document
@@ -626,7 +646,8 @@ class API:
return headers, 200, openapi.read()
@pre_process
def conformance(self, request: Union[APIRequest, Any]):
def conformance(self,
request: Union[APIRequest, Any]) -> Tuple[dict, int, str]:
"""
Provide conformance definition
@@ -652,7 +673,8 @@ class API:
@pre_process
@jsonldify
def describe_collections(self, request: Union[APIRequest, Any], dataset=None): # noqa
def describe_collections(self, request: Union[APIRequest, Any],
dataset=None) -> Tuple[dict, int, str]:
"""
Provide collection metadata
@@ -996,7 +1018,8 @@ class API:
@pre_process
@jsonldify
def get_collection_queryables(self, request: Union[APIRequest, Any], dataset=None): # noqa
def get_collection_queryables(self, request: Union[APIRequest, Any],
dataset=None) -> Tuple[dict, int, str]:
"""
Provide collection queryables
@@ -1413,7 +1436,8 @@ class API:
return headers, 200, to_json(content, self.pretty_print)
@pre_process
def get_collection_item(self, request: Union[APIRequest, Any], dataset, identifier): # noqa
def get_collection_item(self, request: Union[APIRequest, Any],
dataset, identifier) -> Tuple[dict, int, str]:
"""
Get a single collection item
@@ -1553,7 +1577,8 @@ class API:
@pre_process
@jsonldify
def get_collection_coverage(self, request: Union[APIRequest, Any], dataset): # noqa
def get_collection_coverage(self, request: Union[APIRequest, Any],
dataset) -> Tuple[dict, int, str]:
"""
Returns a subset of a collection coverage
@@ -1692,7 +1717,9 @@ class API:
@pre_process
@jsonldify
def get_collection_coverage_domainset(self, request: Union[APIRequest, Any], dataset): # noqa
def get_collection_coverage_domainset(
self, request: Union[APIRequest, Any],
dataset) -> Tuple[dict, int, str]:
"""
Returns a collection coverage domainset
@@ -1743,7 +1770,9 @@ class API:
@pre_process
@jsonldify
def get_collection_coverage_rangetype(self, request: Union[APIRequest, Any], dataset): # noqa
def get_collection_coverage_rangetype(
self, request: Union[APIRequest, Any],
dataset) -> Tuple[dict, int, str]:
"""
Returns a collection coverage rangetype
@@ -1793,7 +1822,8 @@ class API:
@pre_process
@jsonldify
def get_collection_tiles(self, request: Union[APIRequest, Any], dataset=None): # noqa
def get_collection_tiles(self, request: Union[APIRequest, Any],
dataset=None) -> Tuple[dict, int, str]:
"""
Provide collection tiles
@@ -1896,9 +1926,10 @@ class API:
@pre_process
@jsonldify
def get_collection_tiles_data(self, request: Union[APIRequest, Any],
dataset=None, matrix_id=None,
z_idx=None, y_idx=None, x_idx=None):
def get_collection_tiles_data(
self, request: Union[APIRequest, Any],
dataset=None, matrix_id=None,
z_idx=None, y_idx=None, x_idx=None) -> Tuple[dict, int, str]:
"""
Get collection items tiles
@@ -1979,8 +2010,9 @@ class API:
@pre_process
@jsonldify
def get_collection_tiles_metadata(self, request: Union[APIRequest, Any],
dataset=None, matrix_id=None):
def get_collection_tiles_metadata(
self, request: Union[APIRequest, Any],
dataset=None, matrix_id=None) -> Tuple[dict, int, str]:
"""
Get collection items tiles
@@ -2060,7 +2092,8 @@ class API:
@pre_process
@jsonldify
def describe_processes(self, request: Union[APIRequest, Any], process=None): # noqa
def describe_processes(self, request: Union[APIRequest, Any],
process=None) -> Tuple[dict, int, str]:
"""
Provide processes metadata
@@ -2152,7 +2185,8 @@ class API:
return headers, 200, to_json(response, self.pretty_print)
@pre_process
def get_process_jobs(self, request: Union[APIRequest, Any], process_id, job_id=None): # noqa
def get_process_jobs(self, request: Union[APIRequest, Any],
process_id, job_id=None) -> Tuple[dict, int, str]:
"""
Get process jobs
@@ -2255,7 +2289,8 @@ class API:
return headers, 200, to_json(serialized_jobs, self.pretty_print)
@pre_process
def execute_process(self, request: Union[APIRequest, Any], process_id):
def execute_process(self, request: Union[APIRequest, Any],
process_id) -> Tuple[dict, int, str]:
"""
Execute process
@@ -2372,7 +2407,8 @@ class API:
return headers, http_status, to_json(response, self.pretty_print)
@pre_process
def get_process_job_result(self, request: Union[APIRequest, Any], process_id, job_id): # noqa
def get_process_job_result(self, request: Union[APIRequest, Any],
process_id, job_id) -> Tuple[dict, int, str]:
"""
Get result of job (instance of a process)
@@ -2454,8 +2490,10 @@ class API:
return headers, 200, content
def delete_process_job(self, process_id, job_id):
def delete_process_job(self, process_id, job_id) -> Tuple[dict, int, str]:
"""
Delete a process job
:param process_id: process identifier
:param job_id: job identifier
@@ -2493,14 +2531,17 @@ class API:
return {}, http_status, response
@pre_process
def get_collection_edr_query(self, request: Union[APIRequest, Any],
dataset, instance, query_type):
def get_collection_edr_query(
self, request: Union[APIRequest, Any],
dataset, instance, query_type) -> Tuple[dict, int, str]:
"""
Queries collection EDR
:param request: APIRequest instance with query params
:param dataset: dataset name
:param instance: instance name
:param query_type: EDR query type
:returns: tuple of headers, status code, content
"""
@@ -2616,7 +2657,15 @@ class API:
@pre_process
@jsonldify
def get_stac_root(self, request: Union[APIRequest, Any]):
def get_stac_root(
self, request: Union[APIRequest, Any]) -> Tuple[dict, int, str]:
"""
Provide STAC root page
:param request: APIRequest instance with query params
:returns: tuple of headers, status code, content
"""
if not request.is_valid():
return self.get_format_exception(request)
@@ -2663,7 +2712,15 @@ class API:
@pre_process
@jsonldify
def get_stac_path(self, request: Union[APIRequest, Any], path):
def get_stac_path(self, request: Union[APIRequest, Any],
path) -> Tuple[dict, int, str]:
"""
Provide STAC resource path
:param request: APIRequest instance with query params
:returns: tuple of headers, status code, content
"""
if not request.is_valid():
return self.get_format_exception(request)
@@ -2744,7 +2801,8 @@ class API:
headers.pop('Content-Type', None)
return headers, 200, stac_data
def get_exception(self, status, headers, format_, code, description):
def get_exception(self, status, headers, format_, code,
description) -> Tuple[dict, int, str]:
"""
Exception handler
@@ -2772,13 +2830,15 @@ class API:
return headers, status, content
def get_format_exception(self, request):
""" Returns a format exception.
def get_format_exception(self, request) -> Tuple[dict, int, str]:
"""
Returns a format exception.
:param request: An APIRequest instance.
:returns: tuple of (headers, status, message)
:returns: tuple of (headers, status, message)
"""
# Content-Language is in the system locale (ignore language settings)
headers = request.get_response_headers(SYSTEM_LOCALE)
msg = f'Invalid format: {request.format}'
@@ -2786,7 +2846,7 @@ class API:
400, headers, F_JSON, 'InvalidParameterValue', msg)
def validate_bbox(value=None):
def validate_bbox(value=None) -> list:
"""
Helper function to validate bbox parameter
@@ -2822,7 +2882,7 @@ def validate_bbox(value=None):
return bbox
def validate_datetime(resource_def, datetime_=None):
def validate_datetime(resource_def, datetime_=None) -> str:
"""
Helper function to validate temporal parameter
+9 -5
View File
@@ -97,14 +97,18 @@ if (OGC_SCHEMAS_LOCATION is not None and
def get_response(result: tuple):
""" Creates a Flask Response object and updates matching headers.
:param result: The result of the API call.
This should be a tuple of (headers, status, content).
:returns: A Response instance.
"""
Creates a Flask Response object and updates matching headers.
:param result: The result of the API call.
This should be a tuple of (headers, status, content).
:returns: A Response instance.
"""
headers, status, content = result
response = make_response(content, status)
if headers:
response.headers = headers
return response
+98 -62
View File
@@ -54,17 +54,21 @@ class LocaleError(Exception):
def str2locale(value, silent: bool = False) -> Union[Locale, None]:
""" Converts a web locale or language tag into a Babel Locale instance.
"""
Converts a web locale or language tag into a Babel Locale instance.
.. note:: If `value` already is a Locale, it is returned as-is.
:param value: A string containing a (web) locale (e.g. 'fr-CH')
:param value: A string containing a (web) locale (e.g. 'fr-CH')
or language tag (e.g. 'de').
:param silent: If True (default = False), no errors will be raised
when parsing failed. Instead, `None` will be returned.
:returns: babel.core.Locale or None
:raises: LocaleError
:param silent: If True (default = False), no errors will be raised
when parsing failed. Instead, `None` will be returned.
:returns: babel.core.Locale or None
:raises: LocaleError
"""
if isinstance(value, Locale):
return value
@@ -75,10 +79,12 @@ def str2locale(value, silent: bool = False) -> Union[Locale, None]:
try:
loc = Locale.parse(value.strip().replace('-', '_'))
except (ValueError, AttributeError):
except (ValueError, AttributeError) as err:
LOGGER.warning(err)
if not silent:
raise LocaleError(f"invalid locale '{value}'")
except _UnknownLocaleError as err:
LOGGER.warning(err)
if not silent:
raise LocaleError(err)
else:
@@ -89,20 +95,25 @@ def str2locale(value, silent: bool = False) -> Union[Locale, None]:
def locale2str(value: Locale) -> str:
""" Converts a Babel Locale instance into a web locale string.
:param value: babel.core.Locale
:returns: A string containing a web locale (e.g. 'fr-CH')
or language tag (e.g. 'de').
:raises: LocaleError
"""
Converts a Babel Locale instance into a web locale string.
:param value: babel.core.Locale
:returns: A string containing a web locale (e.g. 'fr-CH')
or language tag (e.g. 'de').
:raises: LocaleError
"""
if not isinstance(value, Locale):
raise LocaleError(f"'{value}' is not of type {Locale.__name__}")
return str(value).replace('_', '-')
def best_match(accept_languages, available_locales) -> Locale:
""" Takes an Accept-Languages string (from header or request query params)
"""
Takes an Accept-Languages string (from header or request query params)
and finds the best matching locale from a list of available locales.
This function provides a framework-independent alternative to the
@@ -120,19 +131,21 @@ def best_match(accept_languages, available_locales) -> Locale:
or unknown locale is ignored. However, if no
`available_locales` are specified, a `LocaleError` is raised.
:param accept_languages: A Locale or string with one or more languages.
This can be as simple as "de" for example,
but it's also possible to include a territory
(e.g. "en-US" or "fr_BE") or even a complex
string with quality values, e.g.
"fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5".
:param available_locales: A list containing the available locales.
For example, a pygeoapi provider might only
support ["de", "en"].
Locales in the list can be specified as strings
(e.g. "nl-NL") or `Locale` instances.
:returns: babel.core.Locale
:raises: LocaleError
:param accept_languages: A Locale or string with one or more languages.
This can be as simple as "de" for example,
but it's also possible to include a territory
(e.g. "en-US" or "fr_BE") or even a complex
string with quality values, e.g.
"fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5".
:param available_locales: A list containing the available locales.
For example, a pygeoapi provider might only
support ["de", "en"].
Locales in the list can be specified as strings
(e.g. "nl-NL") or `Locale` instances.
:returns: babel.core.Locale
:raises: LocaleError
"""
def get_match(locale_, available_locales_):
@@ -158,6 +171,7 @@ def best_match(accept_languages, available_locales) -> Locale:
if isinstance(accept_languages, Locale):
# If a Babel Locale was used as input, transform back into a string
accept_languages = locale2str(accept_languages)
if not isinstance(accept_languages, str):
# If `accept_languages` is not a string, ignore it
LOGGER.debug(f"ignoring invalid accept-languages '{accept_languages}'")
@@ -234,12 +248,15 @@ def translate(value, language: Union[Locale, str]):
If `language` is not a string or Locale, a LocaleError is raised.
:param value: A value to translate. Typically either a string or
a language struct dictionary.
:param language: A locale string (e.g. "en-US" or "en") or Babel Locale.
:returns: A translated string or the original value.
:raises: LocaleError
:param value: A value to translate. Typically either a string or
a language struct dictionary.
:param language: A locale string (e.g. "en-US" or "en") or Babel Locale.
:returns: A translated string or the original value.
:raises: LocaleError
"""
nested_dicts = isinstance(value, dict) and any(isinstance(v, dict)
for v in value.values())
if not isinstance(value, dict) or nested_dicts:
@@ -274,16 +291,18 @@ def translate(value, language: Union[Locale, str]):
def translate_struct(struct, locale_: Locale, is_config: bool = False):
""" Returns a copy of a given dict or list, where all language structs
"""
Returns a copy of a given dict or list, where all language structs
are filtered on the given locale, i.e. all language structs are replaced
by translated values for the best matching locale.
:param struct: A dict or list (of dicts) to filter/translate.
:param locale_: The Babel Locale to filter on.
:param is_config: If True, the struct is treated as a pygeoapi config.
This means that the first 2 levels won't be translated
and the translated struct is cached for speed.
:returns: A translated dict or list
:param struct: A dict or list (of dicts) to filter/translate.
:param locale_: The Babel Locale to filter on.
:param is_config: If True, the struct is treated as a pygeoapi config.
This means that the first 2 levels won't be translated
and the translated struct is cached for speed.
:returns: A translated dict or list
"""
def _translate_dict(obj, level: int = 0):
@@ -335,8 +354,9 @@ def locale_from_headers(headers) -> str:
:param headers: Mapping of request headers.
:returns: locale string or None
:returns: locale string or None
"""
lang = {k.lower(): v for k, v in headers.items()}.get('accept-language')
if lang:
LOGGER.debug(f"Got locale '{lang}' from 'Accept-Language' header")
@@ -350,10 +370,11 @@ def locale_from_params(params) -> str:
web locales (e.g. "en-US") or basic language tags (e.g. "en").
A value of `None` is returned if the locale was not found or invalid.
:param params: Mapping of request query parameters.
:param params: Mapping of request query parameters.
:returns: locale string or None
:returns: locale string or None
"""
lang = params.get(QUERY_PARAM)
if lang:
LOGGER.debug(f"Got locale '{lang}' from query parameter '{QUERY_PARAM}'") # noqa
@@ -361,15 +382,18 @@ def locale_from_params(params) -> str:
def set_response_language(headers: dict, *locale_: Locale):
""" Sets the Content-Language on the given HTTP response headers dict.
:param headers: A dict of HTTP response headers.
:param locale_: The Babel Locale(s) to which to set the
Content-Language header.
Multiple locales can be set for this header.
Note that duplicates will be removed.
:raises: LocaleError if no valid Babel Locale was found.
"""
Sets the Content-Language on the given HTTP response headers dict.
:param headers: A dict of HTTP response headers.
:param locale_: The Babel Locale(s) to which to set the
Content-Language header.
Multiple locales can be set for this header.
Note that duplicates will be removed.
:raises: LocaleError if no valid Babel Locale was found.
"""
if not hasattr(headers, '__setitem__'):
LOGGER.warning(f"Cannot set headers on object '{headers}'")
return
@@ -388,19 +412,24 @@ def set_response_language(headers: dict, *locale_: Locale):
if not locales:
raise LocaleError('no valid locales set')
loc_str = ', '.join(locales)
LOGGER.debug(f'Setting Content-Language to {loc_str}')
headers['Content-Language'] = loc_str
def add_locale(url, locale_):
""" Adds a locale query parameter (e.g. 'lang=en-US') to a URL.
def add_locale(url, locale_) -> str:
"""
Adds a locale query parameter (e.g. 'lang=en-US') to a URL.
If `locale_` is None or an empty string, the URL will be returned as-is.
:param url: The web page URL (may contain query string).
:param url: The web page URL (may contain query string).
:param locale_: The web locale or language tag to append to the query.
:returns: A new URL with a 'lang=<locale>' query parameter.
:raises: requests.exceptions.MissingSchema
:returns: A new URL with a 'lang=<locale>' query parameter.
:raises: requests.exceptions.MissingSchema
"""
loc = str2locale(locale_, True)
if not loc:
# Validation of locale failed
@@ -428,12 +457,15 @@ def add_locale(url, locale_):
def get_locales(config: dict) -> list:
""" Reads the configured locales/languages from the given configuration.
"""
Reads the configured locales/languages from the given configuration.
The first Locale in the returned list should be the default locale.
:param config: A pygeaapi configuration dict
:returns: A list of supported Locale instances
:param config: A pygeaapi configuration dict
:returns: A list of supported Locale instances
"""
srv_cfg = config.get('server', {})
lang = srv_cfg.get('languages', srv_cfg.get('language', []))
@@ -451,16 +483,20 @@ def get_locales(config: dict) -> list:
raise LocaleError('Bad value in supported server language(s)')
def get_plugin_locale(config: dict, requested_locale: Union[str, None]) -> Union[Locale, None]: # noqa
""" Returns the supported locale (best match) for a plugin
def get_plugin_locale(
config: dict,
requested_locale: Union[str, None]) -> Union[Locale, None]:
"""
Returns the supported locale (best match) for a plugin
based on the requested raw locale string.
Returns None if the plugin does not support any locales.
Returns the default (= first) locale that the plugin supports
if no match for the requested locale could be found.
:param config: The plugin definition
:param requested_locale: The requested locale string (or None)
:param config: The plugin definition
:param requested_locale: The requested locale string (or None)
"""
plugin_name = f"{config.get('name', '')} plugin".strip()
if not requested_locale:
LOGGER.debug(f'No requested locale for {plugin_name}')
+10 -6
View File
@@ -1,7 +1,7 @@
# =================================================================
#
# Authors: Francesco Bartoli <xbartolone@gmail.com>
#
# Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Francesco Bartoli
# Copyright (c) 2020 Tom Kralidis
@@ -76,14 +76,18 @@ api_ = API(CONFIG)
def get_response(result: tuple) -> Response:
""" Creates a Starlette Response object and updates matching headers.
:param result: The result of the API call.
This should be a tuple of (headers, status, content).
:returns: A Response instance.
"""
Creates a Starlette Response object and updates matching headers.
:param result: The result of the API call.
This should be a tuple of (headers, status, content).
:returns: A Response instance.
"""
headers, status, content = result
response = Response(content=content, status_code=status)
if headers is not None:
response.headers.update(headers)
return response
+1 -1
View File
@@ -1,3 +1,4 @@
Babel
click<8
Flask
pyproj
@@ -8,4 +9,3 @@ rasterio
shapely
tinydb
unicodecsv
Babel