Reference Guide  2.5.0
common_meta_arg_metadata.py
1 # -----------------------------------------------------------------------------
2 # BSD 3-Clause License
3 #
4 # Copyright (c) 2022-2024, Science and Technology Facilities Council
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice,
14 # this list of conditions and the following disclaimer in the documentation
15 # and/or other materials provided with the distribution.
16 #
17 # * Neither the name of the copyright holder nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 # POSSIBILITY OF SUCH DAMAGE.
33 # -----------------------------------------------------------------------------
34 # Author R. W. Ford, STFC Daresbury Lab
35 
36 '''Module containing the abstract CommonMetaArgMetadata class which
37 captures the metadata associated with an LFRic meta_arg
38 argument. Supports the creation, modification and Fortran output of
39 such an argument.
40 
41 '''
42 from abc import ABC, abstractmethod
43 
44 from psyclone.domain.lfric.kernel.common_arg_metadata import CommonArgMetadata
45 from psyclone.errors import InternalError
46 
47 
49  '''Abstract class to capture common aspects of LFRic kernel metadata.
50 
51  :param Optional[str] datatype: the datatype of this argument.
52  :param Optional[str] access: the way the kernel accesses this \
53  argument.
54 
55  '''
56  # Whether the class captures vector metadata.
57  vector = False
58  # Dummy values to keep pylint happy for the class methods.
59  form_arg_index = 0
60  vector_length_arg_index = 0
61  datatype_arg_index = 1
62  access_arg_index = 2
63  function_space_arg_index = 3
64  form = ""
65  check_name = ""
66  nargs = 1
67 
68  def __init__(self, datatype, access):
69  super().__init__()
70  self.datatypedatatypedatatypedatatype = datatype
71  self.accessaccessaccessaccess = access
72 
73  @classmethod
74  @abstractmethod
75  def _get_metadata(cls, fparser2_tree):
76  '''Extract the required metadata from the fparser2 tree and return it
77  as strings. Also check that the metadata is in the expected
78  form (but do not check the metadata values as that is done
79  separately).'''
80 
81  @classmethod
82  def create_from_fparser2(cls, fparser2_tree):
83  '''Create an instance of the class from an fparser2 tree.
84 
85  :param fparser2_tree: fparser2 tree containing the metadata \
86  for this argument.
87  :type fparser2_tree: :py:class:`fparser.two.Fortran2003.Part_Ref`
88 
89  :returns: an instance of the class.
90  :rtype: subclass of \
91  :py:class:`psyclone.domain.lfric.kernel.CommonMetaArgMetadata`
92 
93  '''
94  args = cls._get_metadata_get_metadata(fparser2_tree)
95  cls.check_remaining_argscheck_remaining_args(fparser2_tree, *args)
96  return cls(*args)
97 
98  @classmethod
99  def check_first_arg(cls, fparser2_tree):
100  '''Check that the first metadata argument has the expected value.
101 
102  :param fparser2_tree: the metadata encoded in an fparser2_tree
103  :type fparser2_tree: :py:class:`fparser.two.Fortran2003.Part_Ref` | \
104  :py:class:`fparser.two.Fortran2003.Structure_Constructor`
105 
106  :raises ValueError: if the first metadata argument has an \
107  incorrect value.
108 
109  '''
110  idx = cls.form_arg_indexform_arg_index
111  form = cls.get_argget_arg(fparser2_tree, idx)
112  word = "as"
113  if cls.vectorvector:
114  form = form.split("*")[0].strip()
115  word = "in"
116  if not form.lower() == cls.formform.lower():
117  raise ValueError(
118  f"Metadata for '{cls.check_name}' kernel arguments should "
119  f"have '{cls.form}' {word} the first metadata "
120  f"property, but found '{form}'.")
121 
122  @classmethod
123  def check_remaining_args(cls, fparser2_tree, *metadata_args):
124  '''Check that the remaining untested metadata arguments have the
125  expected value. If they do not then re-raise the exception
126  from the class constructor, adding in positional information
127  and the metadata arguments to make it clearer where the
128  exception occured.
129 
130  :param fparser2_tree: the metadata encoded in an fparser2_tree.
131  :type fparser2_tree: :py:class:`fparser.two.Fortran2003.Part_Ref` or \
132  :py:class:`fparser.two.Fortran2003.Structure_Constructor`
133  :param metadata_args: the metadata arguments required to \
134  create an instance of the class provided by the 'cls' \
135  argument.
136  :type metadata_args: unwrapped dict
137 
138  :raises ValueError: if the metadata has an incorrect value.
139  :raises InternalError: if an unrecognised exception message is found.
140 
141  '''
142  try:
143  _ = cls(*metadata_args)
144  except ValueError as info:
145  message = str(info)
146  if "datatype descriptor" in message:
147  index = cls.datatype_arg_indexdatatype_arg_index
148  elif "access descriptor" in message:
149  index = cls.access_arg_indexaccess_arg_index
150  elif "function space" in message:
151  index = cls.function_space_arg_indexfunction_space_arg_index
152  elif "mesh_arg" in message:
153  index = cls.mesh_arg_index
154  elif "function_space_to" in message:
155  index = cls.function_space_to_arg_index
156  elif "function_space_from" in message:
157  index = cls.function_space_from_arg_index
158  else:
159  raise InternalError(
160  f"Unexpected error message found '{message}'") from info
161  raise ValueError(f"At argument index '{index}' for metadata "
162  f"'{str(fparser2_tree)}'. {message}") from info
163 
164  # pylint: disable=arguments-differ
165  @classmethod
166  def check_nargs(cls, fparser2_tree):
167  '''Check that the metadata has the expected number of arguments,
168  otherwise an exception is raised.
169 
170  :param fparser2_tree: fparser2 tree capturing a metadata argument.
171  :type fparser2_tree: :py:class:`fparser.two.Fortran2003.Part_Ref` | \
172  :py:class:`fparser.two.Fortran2003.Structure_Constructor`
173 
174  '''
175  super().check_nargs(fparser2_tree, cls.nargsnargs)
176  # pylint: enable=arguments-differ
177 
178  @classmethod
179  def get_vector_length(cls, fparser2_tree):
180  '''Retrieves the vector length metadata value found within the
181  supplied fparser2 tree and checks that it is valid.
182 
183  :param fparser2_tree: fparser2 tree capturing the required metadata.
184  :type fparser2_tree: :py:class:`fparser.two.Fortran2003.Part_Ref`
185 
186  :returns: the vector length value extracted from the fparser2 tree.
187  :rtype: str
188 
189  :raises TypeError: if the vector length metadata is not in the \
190  expected form.
191 
192  '''
193  vector_datatype = CommonArgMetadata.get_arg(
194  fparser2_tree, cls.vector_length_arg_indexvector_length_arg_index)
195  components = vector_datatype.split("*")
196  if len(components) != 2:
197  raise TypeError(
198  f"The vector length metadata should be in the form "
199  f"'form*vector_length' but found '{vector_datatype}'.")
200  vector_length = components[1].strip()
201  return vector_length
202 
203  @property
204  def datatype(self):
205  '''
206  :returns: the datatype for this metadata argument.
207  :rtype: str
208  '''
209  return self._datatype_datatype
210 
211  @staticmethod
212  @abstractmethod
213  def check_datatype(value):
214  '''Check that value is a valid datatype.'''
215 
216  @datatype.setter
217  def datatype(self, value):
218  '''
219  :param str value: set the datatype to the \
220  specified value.
221  '''
222  self.check_datatypecheck_datatype(value)
223  self._datatype_datatype = value.lower()
224 
225  @staticmethod
226  @abstractmethod
227  def check_access(value):
228  '''Check that value is a valid value for the access descriptor.'''
229 
230  @property
231  def access(self):
232  '''
233  :returns: the access descriptor for this argument.
234  :rtype: str
235  '''
236  return self._access_access
237 
238  @access.setter
239  def access(self, value):
240  '''
241  :param str value: set the access descriptor to the \
242  specified value.
243 
244  '''
245  self.check_accesscheck_access(value)
246  self._access_access = value.lower()
247 
248 
249 __all__ = ["CommonMetaArgMetadata"]