# Path: src/eutils/_internal/sqlitecache.py
# class SQLiteCache(object):
# 
#     ############################################################################
#     ## Exposed methods
#     def __init__(self, db_path, compress_values=True):
#         self.compress_values = compress_values
#         self._con = None
#         self._logger = logging.getLogger(__name__)
#         self._connect(db_path)
# 
#     def expire(self, age):
#         cur = self._execute("DELETE FROM cache WHERE strftime('%s','now') - created > ?", [age])
#         return cur.rowcount
# 
#         ############################################################################
#         ## Special Python methods
#     def __str__(self):
#         return "SQLiteCache(db_path={self._db_path},compress_values={self.compress_values})".format(self=self)
# 
#     def __dir__(self):
#         self._logger.debug("__dir__()")
#         return [key_from(row[0]) for row in self._execute("SELECT key FROM cache", [])]
# 
#     def __getitem__(self, key):
#         self._logger.debug("__getitem__({key})".format(key=key))
#         cur = self._execute("SELECT value,value_compressed FROM cache WHERE key = ?", [key_to(key)])
#         row = cur.fetchone()
#         if row is None:
#             raise KeyError(key)
#         return val_from(*row)
# 
#     def __setitem__(self, key, value):
#         db_val = val_to(value, self.compress_values)
#         self._logger.debug("__setitem__({key},({vlen} bytes))".format(key=key, vlen=len(db_val)))
#         self._execute("INSERT OR REPLACE INTO cache (key,value_compressed,value) VALUES (?,?,?)",
#                       [key_to(key), self.compress_values, db_val])
# 
#     def __delitem__(self, key):
#         self._logger.debug("__delitem__({key})".format(key=key))
#         cur = self._execute("DELETE FROM cache WHERE key = ?", [key_to(key)])
#         if cur.rowcount == 0:
#             raise KeyError(key)
# 
#     def __contains__(self, key):
#         self._logger.debug("__contains__({key})".format(key=key))
#         return self._fetch1v("SELECT EXISTS(SELECT 1 FROM cache WHERE key=? LIMIT 1)", [key_to(key)])
# 
#     ############################################################################
#     ## Internal functions
# 
#     def _connect(self, db_path):
#         assert self._con is None, "already connected"
#         self._con = sqlite3.connect(db_path, isolation_level=None, check_same_thread=False)
#         self._con.text_factory = str
#         self._db_path = db_path
#         self._logger.info("opened " + db_path)
#         sver = self._get_schema_version()
#         self._logger.debug("schema version is " + str(sver))
#         if sver is None:
#             self._execute(
#                 "CREATE TABLE cache (key BLOB PRIMARY KEY, created INTEGER DEFAULT (strftime('%s','now')), value_compressed BOOL, value BLOB)")
#             self._execute("CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)")
#             self._execute("INSERT INTO meta (key, value) VALUES (?,?)", ['schema version', 1])
#             self._logger.debug("created tables")
# 
#     def _get_schema_version(self):
#         if ('meta', ) not in self._execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall():
#             return None
#         return self._fetch1v("SELECT value FROM meta WHERE key = ?", ["schema version"])
# 
#     def _execute(self, query, params=[]):
#         cur = self._con.cursor()
#         self._logger.debug("executing query <{query}> with params <{nvars} vars>".format(
#             query=query,
#             nvars=len(params)))
#         cur.execute(query, params)
#         return cur
# 
#     def _fetch1v(self, query, params=[]):
#         return self._execute(query, params).fetchone()[0]
# 
# Path: src/eutils/_internal/exceptions.py
# class EutilsRequestError(EutilsError):
#     """Raised when NCBI responds with an error, such as when a non-existent
#     database is specified.
# 
#     """
#     pass
# 
# class EutilsNCBIError(EutilsError):
#     """Raised when NCBI returns data that appears to be incorrect or
#     invalid.
# 
#     """
#     pass


import hashlib
import logging
import os
import pickle
import time
import lxml.etree
import requests
from .sqlitecache import SQLiteCache
from .exceptions import EutilsRequestError, EutilsNCBIError
            sleep_time = req_int - (time.monotonic() - self._last_request_clock)
            if sleep_time > 0:
                _logger.debug("sleeping {sleep_time:.3f}".format(sleep_time=sleep_time))
                time.sleep(sleep_time)

        r = requests.post(url, full_args)
        self._last_request_clock = time.monotonic()
        _logger.debug("post({url}, {fas}): {r.status_code} {r.reason}, {len})".format(
            url=url,
            fas=full_args_str,
            r=r,
            len=len(r.text)))

        if not r.ok:
            # TODO: discriminate between types of errors
            if r.headers["Content-Type"] == "application/json":
                json = r.json()
                raise EutilsRequestError('{r.reason} ({r.status_code}): {error}'.format(r=r, error=json["error"]))
            try:
                xml = lxml.etree.fromstring(r.text.encode("utf-8"))
                errornode = xml.find("ERROR")
                errormsg = errornode.text if errornode else "Unknown Error"
                raise EutilsRequestError("{r.reason} ({r.status_code}): {error}".format(r=r, error=errormsg))
            except Exception as ex:
                raise EutilsNCBIError("Error parsing response object from NCBI: {}".format(ex))

        if any(bad_word in r.text for bad_word in ["<error>", "<ERROR>"]):
            if r.text is not None:
                try:
                    xml = lxml.etree.fromstring(r.text.encode("utf-8"))

# Path: src/eutils/_internal/sqlitecache.py
# class SQLiteCache(object):
# 
#     ############################################################################
#     ## Exposed methods
#     def __init__(self, db_path, compress_values=True):
#         self.compress_values = compress_values
#         self._con = None
#         self._logger = logging.getLogger(__name__)
#         self._connect(db_path)
# 
#     def expire(self, age):
#         cur = self._execute("DELETE FROM cache WHERE strftime('%s','now') - created > ?", [age])
#         return cur.rowcount
# 
#         ############################################################################
#         ## Special Python methods
#     def __str__(self):
#         return "SQLiteCache(db_path={self._db_path},compress_values={self.compress_values})".format(self=self)
# 
#     def __dir__(self):
#         self._logger.debug("__dir__()")
#         return [key_from(row[0]) for row in self._execute("SELECT key FROM cache", [])]
# 
#     def __getitem__(self, key):
#         self._logger.debug("__getitem__({key})".format(key=key))
#         cur = self._execute("SELECT value,value_compressed FROM cache WHERE key = ?", [key_to(key)])
#         row = cur.fetchone()
#         if row is None:
#             raise KeyError(key)
#         return val_from(*row)
# 
#     def __setitem__(self, key, value):
#         db_val = val_to(value, self.compress_values)
#         self._logger.debug("__setitem__({key},({vlen} bytes))".format(key=key, vlen=len(db_val)))
#         self._execute("INSERT OR REPLACE INTO cache (key,value_compressed,value) VALUES (?,?,?)",
#                       [key_to(key), self.compress_values, db_val])
# 
#     def __delitem__(self, key):
#         self._logger.debug("__delitem__({key})".format(key=key))
#         cur = self._execute("DELETE FROM cache WHERE key = ?", [key_to(key)])
#         if cur.rowcount == 0:
#             raise KeyError(key)
# 
#     def __contains__(self, key):
#         self._logger.debug("__contains__({key})".format(key=key))
#         return self._fetch1v("SELECT EXISTS(SELECT 1 FROM cache WHERE key=? LIMIT 1)", [key_to(key)])
# 
#     ############################################################################
#     ## Internal functions
# 
#     def _connect(self, db_path):
#         assert self._con is None, "already connected"
#         self._con = sqlite3.connect(db_path, isolation_level=None, check_same_thread=False)
#         self._con.text_factory = str
#         self._db_path = db_path
#         self._logger.info("opened " + db_path)
#         sver = self._get_schema_version()
#         self._logger.debug("schema version is " + str(sver))
#         if sver is None:
#             self._execute(
#                 "CREATE TABLE cache (key BLOB PRIMARY KEY, created INTEGER DEFAULT (strftime('%s','now')), value_compressed BOOL, value BLOB)")
#             self._execute("CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)")
#             self._execute("INSERT INTO meta (key, value) VALUES (?,?)", ['schema version', 1])
#             self._logger.debug("created tables")
# 
#     def _get_schema_version(self):
#         if ('meta', ) not in self._execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall():
#             return None
#         return self._fetch1v("SELECT value FROM meta WHERE key = ?", ["schema version"])
# 
#     def _execute(self, query, params=[]):
#         cur = self._con.cursor()
#         self._logger.debug("executing query <{query}> with params <{nvars} vars>".format(
#             query=query,
#             nvars=len(params)))
#         cur.execute(query, params)
#         return cur
# 
#     def _fetch1v(self, query, params=[]):
#         return self._execute(query, params).fetchone()[0]
# 
# Path: src/eutils/_internal/exceptions.py
# class EutilsRequestError(EutilsError):
#     """Raised when NCBI responds with an error, such as when a non-existent
#     database is specified.
# 
#     """
#     pass
# 
# class EutilsNCBIError(EutilsError):
#     """Raised when NCBI returns data that appears to be incorrect or
#     invalid.
# 
#     """
#     pass


# Path: src/eutils/_internal/queryservice.py
import hashlib
import logging
import os
import pickle
import time
import lxml.etree
import requests
from .sqlitecache import SQLiteCache
from .exceptions import EutilsRequestError, EutilsNCBIError

            sleep_time = req_int - (time.monotonic() - self._last_request_clock)
            if sleep_time > 0:
                _logger.debug("sleeping {sleep_time:.3f}".format(sleep_time=sleep_time))
                time.sleep(sleep_time)

        r = requests.post(url, full_args)
        self._last_request_clock = time.monotonic()
        _logger.debug("post({url}, {fas}): {r.status_code} {r.reason}, {len})".format(
            url=url,
            fas=full_args_str,
            r=r,
            len=len(r.text)))

        if not r.ok:
            # TODO: discriminate between types of errors
            if r.headers["Content-Type"] == "application/json":
                json = r.json()
                raise EutilsRequestError('{r.reason} ({r.status_code}): {error}'.format(r=r, error=json["error"]))
            try:
                xml = lxml.etree.fromstring(r.text.encode("utf-8"))
                errornode = xml.find("ERROR")
                errormsg = errornode.text if errornode else "Unknown Error"
                raise EutilsRequestError("{r.reason} ({r.status_code}): {error}".format(r=r, error=errormsg))
            except Exception as ex:
                raise EutilsNCBIError("Error parsing response object from NCBI: {}".format(ex))

        if any(bad_word in r.text for bad_word in ["<error>", "<ERROR>"]):
            if r.text is not None:
                try:
                    xml = lxml.etree.fromstring(r.text.encode("utf-8"))

                    raise EutilsRequestError("{r.reason} ({r.status_code}): {error}".format(r=r, error=xml.find("ERROR").text))