aiida.restapi.common package#

Submodules#

Default configuration for the REST API

This file contains the exceptions that are raised by the RESTapi at the highest level, namely that of the interaction with the client. Their specificity resides into the fact that they return a message that is embedded into the HTTP response.

Example:#

…/api/v1/nodes/ … (TODO compete this with an actual example)

Other errors arising at deeper level, e.g. those raised by the QueryBuilder or internal errors, are not embedded into the HTTP response.

exception aiida.restapi.common.exceptions.RestFeatureNotAvailable[源代码]#

基类:FeatureNotAvailable

If endpoint is not emplemented for given node type

__annotations__ = {}#
__module__ = 'aiida.restapi.common.exceptions'#
exception aiida.restapi.common.exceptions.RestInputValidationError[源代码]#

基类:InputValidationError

If inputs passed in query strings are wrong

__annotations__ = {}#
__module__ = 'aiida.restapi.common.exceptions'#
exception aiida.restapi.common.exceptions.RestValidationError[源代码]#

基类:ValidationError

If validation error in code.

E.g. more than one node available for given uuid

__annotations__ = {}#
__module__ = 'aiida.restapi.common.exceptions'#

Utility functions to work with node “full types” which are unique node identifiers.

A node’s full_type is defined as a string that uniquely defines the node type. A valid full_type is constructed by concatenating the node_type and process_type of a node with the FULL_TYPE_CONCATENATOR. Each segment of the full type can optionally be terminated by a single LIKE_OPERATOR_CHARACTER to indicate that the node_type or process_type should start with that value but can be followed by any amount of other characters. A full type is invalid if it does not contain exactly one FULL_TYPE_CONCATENATOR character. Additionally, each segment can contain at most one occurrence of the LIKE_OPERATOR_CHARACTER and it has to be at the end of the segment.

Examples of valid full types:

‘data.bool.Bool.|’ ‘process.calculation.calcfunction.%|%’ ‘process.calculation.calcjob.CalcJobNode.|aiida.calculations:arithmetic.add’ ‘process.calculation.calcfunction.CalcFunctionNode.|aiida.workflows:codtools.primitive_structure_from_cif’

Examples of invalid full types:

‘data.bool’ # Only a single segment without concatenator ‘data.|bool.Bool.|process.’ # More than one concatenator ‘process.calculation%.calcfunction.|aiida.calculations:arithmetic.add’ # Like operator not at end of segment ‘process.calculation%.calcfunction.%|aiida.calculations:arithmetic.add’ # More than one operator in segment

class aiida.restapi.common.identifiers.Namespace(namespace, path=None, label=None, full_type=None, counter=None, is_leaf=True)[源代码]#

基类:MutableMapping

Namespace that can be used to map the node class hierarchy.

__abstractmethods__ = frozenset({})#
__delitem__(key)[源代码]#
__dict__ = mappingproxy({'__module__': 'aiida.restapi.common.identifiers', '__doc__': 'Namespace that can be used to map the node class hierarchy.', 'namespace_separator': '.', 'mapping_path_to_label': {'node': 'Node', 'node.data': 'Data', 'node.process': 'Process', 'node.process.calculation': 'Calculation', 'node.process.calculation.calcjob': 'Calculation job', 'node.process.calculation.calcfunction': 'Calculation function', 'node.process.workflow': 'Workflow', 'node.process.workflow.workchain': 'Work chain', 'node.process.workflow.workfunction': 'Work function'}, 'process_full_type_mapping': {'process.calculation.calcjob.': 'process.calculation.calcjob.CalcJobNode.|aiida.calculations:{plugin_name}.%', 'process.calculation.calcfunction.': 'process.calculation.calcfunction.CalcFunctionNode.|aiida.calculations:{plugin_name}.%', 'process.workflow.workfunction.': 'process.workflow.workfunction.WorkFunctionNode.|aiida.workflows:{plugin_name}.%', 'process.workflow.workchain.': 'process.workflow.workchain.WorkChainNode.|aiida.workflows:{plugin_name}.%'}, 'process_full_type_mapping_unplugged': {'process.calculation.calcjob.': 'process.calculation.calcjob.CalcJobNode.|{plugin_name}.%', 'process.calculation.calcfunction.': 'process.calculation.calcfunction.CalcFunctionNode.|{plugin_name}.%', 'process.workflow.workfunction.': 'process.workflow.workfunction.WorkFunctionNode.|{plugin_name}.%', 'process.workflow.workchain.': 'process.workflow.workchain.WorkChainNode.|{plugin_name}.%'}, '__str__': <function Namespace.__str__>, '__init__': <function Namespace.__init__>, '_infer_full_type': <function Namespace._infer_full_type>, '__iter__': <function Namespace.__iter__>, '__len__': <function Namespace.__len__>, '__delitem__': <function Namespace.__delitem__>, '__getitem__': <function Namespace.__getitem__>, '__setitem__': <function Namespace.__setitem__>, 'is_leaf': <property object>, 'get_description': <function Namespace.get_description>, 'create_namespace': <function Namespace.create_namespace>, '__dict__': <attribute '__dict__' of 'Namespace' objects>, '__weakref__': <attribute '__weakref__' of 'Namespace' objects>, '__abstractmethods__': frozenset(), '_abc_impl': <_abc._abc_data object>, '__annotations__': {}})#
__getitem__(key)[源代码]#
__init__(namespace, path=None, label=None, full_type=None, counter=None, is_leaf=True)[源代码]#

Construct a new node class namespace.

__iter__()[源代码]#
__len__()[源代码]#
__module__ = 'aiida.restapi.common.identifiers'#
__setitem__(key, port)[源代码]#
__str__()[源代码]#

Return str(self).

__weakref__#

list of weak references to the object (if defined)

_abc_impl = <_abc._abc_data object>#
_infer_full_type(full_type)[源代码]#

Infer the full type based on the current namespace path and the given full type of the leaf.

create_namespace(name, **kwargs)[源代码]#

Create and return a new Namespace in this Namespace.

If the name is namespaced, the sub Namespaces will be created recursively, except if one of the namespaces is already occupied at any level by a Port in which case a ValueError will be thrown

参数:
  • name – name (potentially namespaced) of the port to create and return

  • kwargs – constructor arguments that will be used only for the construction of the terminal Namespace

返回:

Namespace

Raises:

ValueError if any sub namespace is occupied by a non-Namespace port

get_description()[源代码]#

Return a dictionary with a description of the ports this namespace contains.

Nested PortNamespaces will be properly recursed and Ports will print their properties in a list

返回:

a dictionary of descriptions of the Ports contained within this PortNamespace

property is_leaf#
mapping_path_to_label = {'node': 'Node', 'node.data': 'Data', 'node.process': 'Process', 'node.process.calculation': 'Calculation', 'node.process.calculation.calcfunction': 'Calculation function', 'node.process.calculation.calcjob': 'Calculation job', 'node.process.workflow': 'Workflow', 'node.process.workflow.workchain': 'Work chain', 'node.process.workflow.workfunction': 'Work function'}#
namespace_separator = '.'#
process_full_type_mapping = {'process.calculation.calcfunction.': 'process.calculation.calcfunction.CalcFunctionNode.|aiida.calculations:{plugin_name}.%', 'process.calculation.calcjob.': 'process.calculation.calcjob.CalcJobNode.|aiida.calculations:{plugin_name}.%', 'process.workflow.workchain.': 'process.workflow.workchain.WorkChainNode.|aiida.workflows:{plugin_name}.%', 'process.workflow.workfunction.': 'process.workflow.workfunction.WorkFunctionNode.|aiida.workflows:{plugin_name}.%'}#
process_full_type_mapping_unplugged = {'process.calculation.calcfunction.': 'process.calculation.calcfunction.CalcFunctionNode.|{plugin_name}.%', 'process.calculation.calcjob.': 'process.calculation.calcjob.CalcJobNode.|{plugin_name}.%', 'process.workflow.workchain.': 'process.workflow.workchain.WorkChainNode.|{plugin_name}.%', 'process.workflow.workfunction.': 'process.workflow.workfunction.WorkFunctionNode.|{plugin_name}.%'}#
aiida.restapi.common.identifiers.construct_full_type(node_type, process_type)[源代码]#

Return the full type, which uniquely identifies any Node with the given node_type and process_type.

参数:
  • node_type – the node_type of the Node

  • process_type – the process_type of the Node

返回:

the full type, which is a unique identifier

aiida.restapi.common.identifiers.get_full_type_filters(full_type)[源代码]#

Return the QueryBuilder filters that will return all Nodes identified by the given full_type.

参数:

full_type – the full_type unique node identifier

返回:

dictionary of filters to be passed for the filters keyword in QueryBuilder.append

抛出:
  • ValueError – if the full_type is invalid

  • TypeError – if the full_type is not a string type

aiida.restapi.common.identifiers.get_node_namespace(user_pk=None, count_nodes=False)[源代码]#

Return the full namespace of all available nodes in the current database.

返回:

complete node Namespace

aiida.restapi.common.identifiers.load_entry_point_from_full_type(full_type)[源代码]#

Return the loaded entry point for the given full_type unique node identifier.

参数:

full_type – the full_type unique node identifier

抛出:
aiida.restapi.common.identifiers.validate_full_type(full_type)[源代码]#

Validate that the full_type is a valid full type unique node identifier.

参数:

full_type – a Node full type

抛出:
  • ValueError – if the full_type is invalid

  • TypeError – if the full_type is not a string type

Util methods

class aiida.restapi.common.utils.CustomJSONProvider(app: Flask)[源代码]#

基类:DefaultJSONProvider

Custom json encoder for serialization. This has to be provided to the Flask app in order to replace the default encoder.

__annotations__ = {}#
__module__ = 'aiida.restapi.common.utils'#
default(obj, **kwargs)[源代码]#

Override serialization of DefaultJSONProvider for datetime and bytes objects.

参数:

obj – Object e.g. dict, list that will be serialized.

返回:

Serialized object as a string.

class aiida.restapi.common.utils.Utils(**kwargs)[源代码]#

基类:object

A class that gathers all the utility functions for parsing URI, validating request, pass it to the translator, and building HTTP response

An istance of Utils has to be included in the api class so that the configuration parameters used to build the api are automatically accessible by the methods of Utils without the need to import them from the config.py file.

__dict__ = mappingproxy({'__module__': 'aiida.restapi.common.utils', '__doc__': 'A class that gathers all the utility functions for parsing URI,\n    validating request, pass it to the translator, and building HTTP response\n\n    An istance of Utils has to be included in the api class so that the\n    configuration parameters used to build the api are automatically\n    accessible by the methods of Utils without the need to import them from\n    the config.py file.\n\n    ', 'op_conv_map': {'=': '==', '!=': '!==', '=in=': 'in', '=notin=': '!in', '>': '>', '<': '<', '>=': '>=', '<=': '<=', '=like=': 'like', '=ilike=': 'ilike'}, '__init__': <function Utils.__init__>, 'strip_api_prefix': <function Utils.strip_api_prefix>, 'split_path': <staticmethod(<function Utils.split_path>)>, 'parse_path': <function Utils.parse_path>, 'validate_request': <function Utils.validate_request>, 'paginate': <function Utils.paginate>, 'build_headers': <function Utils.build_headers>, 'build_response': <staticmethod(<function Utils.build_response>)>, 'build_datetime_filter': <staticmethod(<function Utils.build_datetime_filter>)>, 'build_translator_parameters': <function Utils.build_translator_parameters>, 'parse_query_string': <function Utils.parse_query_string>, '__dict__': <attribute '__dict__' of 'Utils' objects>, '__weakref__': <attribute '__weakref__' of 'Utils' objects>, '__annotations__': {}})#
__init__(**kwargs)[源代码]#

Sets internally the configuration parameters

__module__ = 'aiida.restapi.common.utils'#
__weakref__#

list of weak references to the object (if defined)

static build_datetime_filter(dtobj)[源代码]#

This function constructs a filter for a datetime object to be in a certain datetime interval according to the precision.

The interval is [reference_datetime, reference_datetime + delta_time], where delta_time is a function fo the required precision.

This function should be used to replace a datetime filter based on the equality operator that is inehrently “picky” because it implies matching two datetime objects down to the microsecond or something, by a “tolerant” operator which checks whether the datetime is in an interval.

返回:

a suitable entry of the filter dictionary

build_headers(rel_pages=None, url=None, total_count=None)[源代码]#

Construct the header dictionary for an HTTP response. It includes related pages, total count of results (before pagination).

参数:
  • rel_pages – a dictionary defining related pages (first, prev, next, last)

  • url – (string) the full url, i.e. the url that the client uses to get Rest resources

static build_response(status=200, headers=None, data=None)[源代码]#

Build the response

参数:
  • status – status of the response, e.g. 200=OK, 400=bad request

  • headers – dictionary for additional header k,v pairs, e.g. X-total-count=<number of rows resulting from query>

  • data – a dictionary with the data returned by the Resource

返回:

a Flask response object

build_translator_parameters(field_list)[源代码]#

Takes a list of elements resulting from the parsing the query_string and elaborates them in order to provide translator-compliant instructions

参数:

field_list – a (nested) list of elements resulting from parsing the query_string

返回:

the filters in the

op_conv_map = {'!=': '!==', '<': '<', '<=': '<=', '=': '==', '=ilike=': 'ilike', '=in=': 'in', '=like=': 'like', '=notin=': '!in', '>': '>', '>=': '>='}#
paginate(page, perpage, total_count)[源代码]#

Calculates limit and offset for the reults of a query, given the page and the number of restuls per page. Moreover, calculates the last available page and raises an exception if the required page exceeds that limit. If number of rows==0, only page 1 exists :param page: integer number of the page that has to be viewed :param perpage: integer defining how many results a page contains :param total_count: the total number of rows retrieved by the query :return: integers: limit, offset, rel_pages

parse_path(path_string, parse_pk_uuid=None)[源代码]#

Takes the path and parse it checking its validity. Does not parse “io”, “content” fields. I do not check the validity of the path, since I assume that this is done by the Flask routing methods.

参数:
  • path_string – the path string

  • parse_id_uuid – if ‘pk’ (‘uuid’) expects an integer (uuid starting pattern)

返回:

resource_type (string) page (integer) node_id (string: uuid starting pattern, int: pk) query_type (string))

parse_query_string(query_string)[源代码]#

Function that parse the querystring, extracting infos for limit, offset, ordering, filters, attribute and extra projections. :param query_string (as obtained from request.query_string) :return: parsed values for the querykeys

static split_path(path)[源代码]#
参数:

path – entire path contained in flask request

返回:

list of each element separated by ‘/’

strip_api_prefix(path)[源代码]#

Removes the PREFIX from an URL path. PREFIX must be defined in the config.py file:

PREFIX = "/api/v2"
path = "/api/v2/calculations/page/2"
strip_api_prefix(path) ==> "/calculations/page/2"
参数:

path – the URL path string

返回:

the same URL without the prefix

validate_request(limit=None, offset=None, perpage=None, page=None, query_type=None, is_querystring_defined=False)[源代码]#

Performs various checks on the consistency of the request. Add here all the checks that you want to do, except validity of the page number that is done in paginate(). Future additional checks must be added here

aiida.restapi.common.utils.close_thread_connection(wrapper=None, enabled=None, adapter=None, proxy=<class 'FunctionWrapper'>)[源代码]#

Close the profile’s storage connection, for the current thread.

This decorator can be used for router endpoints. It is needed due to the server running in threaded mode, i.e., creating a new thread for each incoming request, and leaving connections unreleased.

Note, this is currently hard-coded to the PsqlDosBackend storage backend.

aiida.restapi.common.utils.list_routes()[源代码]#

List available routes