def _request(self, n, k): """Constructs the query string for this :class:`Query` object for the specified paging limits and then returns the response from the REST API as a python object. Args: n (int): page number of the results to return. k (int): number of datasets per page. """ if len(self.responses) == 0: #We are making the very first request, finalize the query. self.finalize() import json from six.moves import urllib urlopen = urllib.request.urlopen url = "{0}{1},{2}".format(server, self.matchbook(), self._directives(n, k)) rawresp = urlopen(url).read().decode("utf-8") try: response = json.loads(rawresp) except: # pragma: no cover #We can't easily simulate network failure... msg.err("{}\n\n{}".format(url, rawresp)) return #If this is the first request, then save the number of results in the #query. if len(self.responses) == 0: self._N = int(next(iter(response.keys())).split()[-1]) self.responses[n] = response
def _get_response(url, session=None, page=None): """ Retrieve JSON data from URL with retries. Args: url (str): url to retrieve session (requests.Session): optional, a Session object to perform the request page (int): optional, metadata holding the page number of the request. Useful when this method is called using a thread pool. Returns: tuple: if page is specified, (is_ok, response, page), otherwise (is_ok, response), where `is_ok` is True if the request completed successfully, False otherwise, `response` contains the JSON response or the error information if the request was unsuccessful, `page` is the page number metadata. """ if not session: session = requests.Session() retries = Retry(total=5, backoff_factor=10, status_forcelist=[500], connect=0) session.mount('http://', HTTPAdapter(max_retries=retries)) try: rawresp = session.get(url) is_ok = rawresp.ok response = rawresp.json() except (ReqConnError, RetryError) as ex: # pragma: no cover is_ok = False response = ex.args _msg.err("{}\n\n{}".format(url, response)) if page is not None: return is_ok, response, page return is_ok, response
def _check_input(string): """Check if the input string contains invalid string""" forbidden_chars = ( '"', "@", "\\", "~", "/", ) # characters that will cause LUX crash if any([c in string for c in forbidden_chars]): msg.err( f"Input {string} contains one or more of the forbidden characters: {forbidden_chars}" ) return False else: return True
def cast(atype, keyword, value): """Casts the specified value to a python type, using the AFLOW type as a reference. .. note:: Unfortunately, some of the AFLOW type names are not descriptive or unique enough to make general rule casting possible. Instead, we have to encode some exceptions directly in this module. Args: atype (str): name of the AFLOW type. keyword (str): name of the keyword that the value is associated with. value: object (usually a string) to cast into python types. """ if value is None: return castmap = { "string": str, "strings": _strings, "number": _number, "numbers": _numbers, "forces": _forces, "kpoints": _kpoints, "positions_cartesian": _forces, "positions_fractional": _forces, "spind": _numbers, "stoich": _stoich, "ldau_TLUJ": _ldau_TLUJ, "None": lambda v: v, None: lambda v: v, } try: if keyword not in exceptions: return castmap[atype](value) else: return castmap[keyword](value) except: msg.err("Cannot cast {}; unknown format.".format(value))
def set_manual_matchbook(self, matchbook): """Statelessly set the matchbook string part of the aflow query set the self._matchbook to matchbook and make it final matchbook should be a string. The method does not enforce type check. Use it only when the default keyword method messes up """ if not isinstance(matchbook, str): msg.err( (f"Manual input query must be a string, not {type(matchbook)}." "The matchbook is not set")) self._matchbook = None self._final = False return self if _check_input(matchbook): self._matchbook = matchbook self._final = True else: self._matchbook = None return self
def filter(self, keyword): """Adds a search term to the current filter list. Calling :meth:`filter` multiple times will join the final filters together using logical *and*. Args: keyword (aflow.keywords.Keyword): that encapsulates the AFLUX request language logic. New version: allowing keyword as a single string containing filters """ if self._final_check(): self._N = None if isinstance(keyword, str): # normal string if _check_input(keyword): self.filters.append(keyword) elif hasattr(keyword, "func"): # Is a sympy symbol expr = _expr_to_strings(keyword) self.filters.append(expr) else: msg.err(("Query.filter() takes either " "boolean expression or string," f" not {type(keyword)}")) return self