diff --git a/.vscode/settings.json b/.vscode/settings.json
index ae6332a..63dc3ab 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -16,7 +16,7 @@
"statusBarItem.hoverBackground": "#5ce637",
"statusBar.foreground": "#15202b"
},
- "python.pythonPath": ".venv/bin/python",
+ "python.pythonPath": ".venv/bin/python3",
"python.analysis.openFilesOnly": false,
"python.analysis.memory.keepLibraryLocalVariables": true,
"python.autoComplete.addBrackets": true,
diff --git a/api/app/core/middleware.py b/api/app/core/middleware.py
index 8805e67..a3fb708 100644
--- a/api/app/core/middleware.py
+++ b/api/app/core/middleware.py
@@ -3,7 +3,6 @@
import time
# Third Party
-# Local Imports
from loguru import logger
from starlette.middleware.base import BaseHTTPMiddleware
diff --git a/api/app/main.py b/api/app/main.py
index 2dfa8ba..aa46ffb 100755
--- a/api/app/main.py
+++ b/api/app/main.py
@@ -7,9 +7,9 @@
import time
# Third Party
-# Third Party Imports
from fastapi import FastAPI
from fastapi import __version__ as fastapi_version
+from fastapi.responses import JSONResponse
from loguru import logger
from starlette.middleware.cors import CORSMiddleware
from starlette.middleware.gzip import GZipMiddleware
@@ -19,9 +19,9 @@
from starlette_prometheus import PrometheusMiddleware, metrics
# Local
-# Local Imports
import bel.core.settings as settings
from bel.__version__ import __version__ as version
+from bel.api.core.middleware import StatsMiddleware
from bel.api.endpoints.bel import router as bel_router
from bel.api.endpoints.belspec import router as belspec_router
from bel.api.endpoints.info import router as info_router
@@ -29,7 +29,6 @@
from bel.api.endpoints.orthology import router as orthology_router
from bel.api.endpoints.pubmed import router as pubmed_router
from bel.api.endpoints.terms import router as terms_router
-from core.middleware import StatsMiddleware
logger.remove()
@@ -58,6 +57,22 @@
version=version,
)
+# Added user_flag to HTTPException
+@app.exception_handler(HTTPException)
+async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
+ headers = getattr(exc, "headers", None)
+ if headers:
+ return JSONResponse(
+ {"detail": exc.detail, "user_flag": exc.user_flag},
+ status_code=exc.status_code,
+ headers=headers,
+ )
+ else:
+ return JSONResponse(
+ {"detail": exc.detail, "user_flag": exc.user_flag}, status_code=exc.status_code
+ )
+
+
logger.info("Starting BEL API")
logger.info(f"Fast API Version: {fastapi_version}")
diff --git a/bel/api/core/exceptions.py b/bel/api/core/exceptions.py
new file mode 100644
index 0000000..ab383fe
--- /dev/null
+++ b/bel/api/core/exceptions.py
@@ -0,0 +1,17 @@
+# Standard Library
+from typing import Any, Dict, Optional
+
+# Third Party
+from fastapi.exceptions import HTTPException as FastAPIHTTPException
+
+
+class HTTPException(FastAPIHTTPException):
+ def __init__(
+ self,
+ status_code: int,
+ detail: Any = None,
+ user_flag: bool = False,
+ headers: Optional[Dict[str, Any]] = None,
+ ) -> None:
+ super().__init__(status_code=status_code, detail=detail, headers=headers)
+ self.user_flag = user_flag
diff --git a/bel/api/core/jwt.py b/bel/api/core/jwt.py
new file mode 100644
index 0000000..f520708
--- /dev/null
+++ b/bel/api/core/jwt.py
@@ -0,0 +1,69 @@
+# Standard Library
+from datetime import datetime, timedelta
+
+# Third Party
+import jwt
+from loguru import logger
+
+# Local
+from bel.Config import config
+
+jwt_algorithm = "HS256"
+
+
+def jwt_create(userid, payload, expiration=None):
+ """Create a JSON Web Token
+ payload: dictionary to be added to JWT
+ expiration: number of seconds from now to expire token -- defaults to 3600 seconds
+
+ """
+
+ if expiration:
+ exp = datetime.utcnow() + timedelta(seconds=expiration)
+ else:
+ exp = datetime.utcnow() + timedelta(seconds=3600)
+
+ additional_payload = {
+ "sub": userid,
+ "exp": exp,
+ "iat": datetime.utcnow(),
+ }
+
+ logger.debug("UserId: ", userid, " Payload: ", payload)
+
+ payload.update(additional_payload)
+ token = jwt.encode(
+ payload, config["secrets"]["bel_api"]["shared_secret"], algorithm=jwt_algorithm
+ )
+
+ return token.decode("utf-8")
+
+
+def jwt_validate(token):
+ """Validates JSON Web Token
+ Returns:
+ valid: boolean - true if valid token
+ token_payload: dict of token payload
+ """
+ try:
+ jwt.decode(token, config["secrets"]["bel_api"]["shared_secret"], algorithm=jwt_algorithm)
+ return True
+ except Exception as e:
+ return False
+
+
+def jwt_extract(token):
+ logger.debug("In JWT Extract")
+ try:
+ return (
+ jwt.decode(
+ token, config["secrets"]["bel_api"]["shared_secret"], algorithm=jwt_algorithm
+ ),
+ "",
+ )
+ except jwt.ExpiredSignatureError:
+ logger.debug("JWT expired")
+ return None, "JWT expired"
+ except Exception as e:
+ logger.debug("JWT extraction error ", e)
+ return None, e
diff --git a/bel/api/core/middleware.py b/bel/api/core/middleware.py
new file mode 100644
index 0000000..a3fb708
--- /dev/null
+++ b/bel/api/core/middleware.py
@@ -0,0 +1,43 @@
+# Standard Library
+import re
+import time
+
+# Third Party
+from loguru import logger
+from starlette.middleware.base import BaseHTTPMiddleware
+
+
+@logger.catch
+class StatsMiddleware(BaseHTTPMiddleware):
+ """Get duration of request"""
+
+ async def dispatch(self, request, call_next):
+
+ url = str(request.url)
+ method = str(request.method)
+ t0 = time.time()
+
+ response = await call_next(request)
+
+ url = url.rstrip("/")
+ route_name = url.split("/")[-1]
+
+ # logger.info("Skipping status/metrics", url=url, route_name=route_name)
+
+ if method == "OPTIONS" or route_name in ["status", "metrics", "ping"]:
+ return response
+ else:
+ t1 = time.time()
+
+ duration = f"{(t1 - t0) * 1000:.0f}"
+
+ logger.opt(exception=True).info(
+ "Request Metrics {duration_ms} ms, status_code: {status_code} {method} {url}",
+ # "Request Metrics {duration_ms} ms, status_code: {status_code}",
+ duration_ms=duration,
+ status_code=response.status_code,
+ method=method,
+ url=url,
+ )
+
+ return response
diff --git a/bel/api/endpoints/bel.py b/bel/api/endpoints/bel.py
index aef9a41..70fc086 100644
--- a/bel/api/endpoints/bel.py
+++ b/bel/api/endpoints/bel.py
@@ -4,11 +4,8 @@
from typing import List, Optional
# Third Party
-# Third Party Imports
import fastapi
from fastapi import APIRouter, Depends, Query
-
-# Local Imports
from loguru import logger
# Local
diff --git a/bel/api/endpoints/belspec.py b/bel/api/endpoints/belspec.py
index 6e6c1bc..c4aa7bc 100644
--- a/bel/api/endpoints/belspec.py
+++ b/bel/api/endpoints/belspec.py
@@ -1,15 +1,15 @@
"""belspec endpoints"""
-# Third Party Imports
+
# Third Party
import fastapi
-from fastapi import APIRouter, Depends, HTTPException, Query
+from fastapi import APIRouter, Depends, Query
from fastapi.responses import PlainTextResponse
from loguru import logger
# Local
-# Local Imports
import bel.belspec.crud
+from bel.api.core.exceptions import HTTPException
from bel.schemas.belspec import BelSpec, BelSpecVersions, EnhancedBelSpec
router = APIRouter()
@@ -40,7 +40,9 @@ def get_belspec_version(
belspec = bel.belspec.crud.get_enhanced_belspec(version)
if not belspec:
- raise HTTPException(status_code=404, detail=f"No BEL specification for version {version}")
+ raise HTTPException(
+ status_code=404, detail=f"No BEL specification for version {version}", user_flag=True
+ )
return belspec
@@ -61,7 +63,9 @@ def get_enhanced_belspec(
if not enhanced_belspec:
raise HTTPException(
- status_code=404, detail=f"No enhanced BEL specification for version {version}"
+ status_code=404,
+ detail=f"No enhanced BEL specification for version {version}",
+ user_flag=True,
)
return enhanced_belspec
diff --git a/bel/api/endpoints/info.py b/bel/api/endpoints/info.py
index 75f50ec..34e42cc 100644
--- a/bel/api/endpoints/info.py
+++ b/bel/api/endpoints/info.py
@@ -6,13 +6,11 @@
import re
# Third Party
-# Third Party Imports
import fastapi
from fastapi import APIRouter, Depends
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
import bel.terms.terms
from bel.__version__ import __version__ as bel_lib_version
diff --git a/bel/api/endpoints/nanopubs.py b/bel/api/endpoints/nanopubs.py
index 15f4bc0..f7c6a7b 100644
--- a/bel/api/endpoints/nanopubs.py
+++ b/bel/api/endpoints/nanopubs.py
@@ -3,14 +3,13 @@
from typing import List
# Third Party
-# Third Party Imports
import fastapi
-from fastapi import APIRouter, Depends, HTTPException
+from fastapi import APIRouter, Depends
from loguru import logger
# Local
-# Local Imports
import bel.nanopub.validate
+from bel.api.core.exceptions import HTTPException
from bel.schemas.nanopubs import NanopubR
router = APIRouter()
@@ -33,7 +32,7 @@ def nanopub_validation(nanopub: NanopubR, validation_level: str = "complete"):
# data = json.loads(data)
if not nanopub:
- raise HTTPException(400, detail=f"No nanopub provided")
+ raise HTTPException(400, detail=f"No nanopub provided", user_flag=True)
nanopub = bel.nanopub.validate.validate(nanopub, validation_level=validation_level)
diff --git a/bel/api/endpoints/orthology.py b/bel/api/endpoints/orthology.py
index da201d4..e0ea6df 100644
--- a/bel/api/endpoints/orthology.py
+++ b/bel/api/endpoints/orthology.py
@@ -4,13 +4,11 @@
from typing import List
# Third Party
-# Third Party Imports
import fastapi
from fastapi import APIRouter, Depends, File, Query, UploadFile
from loguru import logger
# Local
-# Local Imports
import bel.terms.orthologs
router = APIRouter()
diff --git a/bel/api/endpoints/pubmed.py b/bel/api/endpoints/pubmed.py
index c485891..54615c4 100644
--- a/bel/api/endpoints/pubmed.py
+++ b/bel/api/endpoints/pubmed.py
@@ -1,14 +1,14 @@
"""pubmed endpoints"""
-# Third Party Imports
+
# Third Party
import fastapi
-from fastapi import APIRouter, Depends, HTTPException, Query
+from fastapi import APIRouter, Depends, Query
from loguru import logger
# Local
-# Local Imports
import bel.nanopub.pubmed
+from bel.api.core.exceptions import HTTPException
router = APIRouter()
@@ -25,6 +25,8 @@ def get_pubmed_info(
pubmed = bel.nanopub.pubmed.get_pubmed_for_beleditor(pmid, pubmed_only=pubmed_only)
if pubmed is None:
- raise HTTPException(status_code=404, detail=f"No Pubmed response for {pmid}")
+ raise HTTPException(
+ status_code=404, detail=f"No Pubmed response for {pmid}", user_flag=True
+ )
return pubmed
diff --git a/bel/api/endpoints/resources.py b/bel/api/endpoints/resources.py
index 2d1336c..1b09936 100644
--- a/bel/api/endpoints/resources.py
+++ b/bel/api/endpoints/resources.py
@@ -4,16 +4,14 @@
from typing import List, Optional
# Third Party
-# Third Party Imports
import fastapi
-from fastapi import APIRouter, Body, Depends, File, HTTPException, Query, UploadFile
+from fastapi import APIRouter, Body, Depends, File, Query, UploadFile
from loguru import logger
# Local
import bel.resources.manage
-
-# Local Imports
import bel.terms.terms
+from bel.api.core.exceptions import HTTPException
from bel.schemas.terms import Term, TermCompletionResponse
router = APIRouter()
diff --git a/bel/api/endpoints/terms.py b/bel/api/endpoints/terms.py
index dfa59ad..53aa0b2 100644
--- a/bel/api/endpoints/terms.py
+++ b/bel/api/endpoints/terms.py
@@ -4,14 +4,13 @@
from typing import List
# Third Party
-# Third Party Imports
import fastapi
-from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
+from fastapi import APIRouter, Depends, File, Query, UploadFile
from loguru import logger
# Local
-# Local Imports
import bel.terms.terms
+from bel.api.core.exceptions import HTTPException
from bel.schemas.terms import Term, TermCompletionResponse
router = APIRouter()
@@ -67,7 +66,7 @@ def get_terms(term_id: str):
terms = bel.terms.terms.get_terms(term_id)
if not terms:
- raise HTTPException(status_code=404, detail=f"Term {term_id} not found")
+ raise HTTPException(status_code=404, detail=f"Term {term_id} not found", user_flag=True)
return terms
@@ -79,7 +78,7 @@ def get_term_equivalents(term_id: str):
equivalents = bel.terms.terms.get_equivalents(term_id)
if not equivalents:
- raise HTTPException(status_code=404, detail=f"Term {term_id} not found")
+ raise HTTPException(status_code=404, detail=f"Term {term_id} not found", user_flag=True)
return equivalents
diff --git a/bel/belspec/crud.py b/bel/belspec/crud.py
index 3918564..9ce15ab 100644
--- a/bel/belspec/crud.py
+++ b/bel/belspec/crud.py
@@ -4,12 +4,9 @@
# Third Party
import cachetools
import semver
-
-# Third Party Imports
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
import bel.db.arangodb as arangodb
from bel.belspec.enhance import create_ebnf_parser, create_enhanced_specification
diff --git a/bel/belspec/enhance.py b/bel/belspec/enhance.py
index 5868f95..ab0cfe4 100644
--- a/bel/belspec/enhance.py
+++ b/bel/belspec/enhance.py
@@ -5,11 +5,9 @@
from typing import Any, List, Mapping
# Third Party
-# Third Party Imports
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
diff --git a/bel/belspec/specifications.py b/bel/belspec/specifications.py
index 4c93641..c0d3b1b 100644
--- a/bel/belspec/specifications.py
+++ b/bel/belspec/specifications.py
@@ -4,12 +4,9 @@
# Third Party
import cachetools
-
-# Third Party Imports
import yaml
# Local
-# Local Imports
import bel.belspec.crud
from bel.core.utils import http_client
diff --git a/bel/cli/scripts.py b/bel/cli/scripts.py
index 2c213ce..de33f6c 100644
--- a/bel/cli/scripts.py
+++ b/bel/cli/scripts.py
@@ -7,14 +7,11 @@
# Third Party
import typer
-
-# Third Party Imports
import yaml
from loguru import logger
from typer import Argument, Option
# Local
-# Local Imports
import bel.core.settings as settings
import bel.core.utils as utils
import bel.db.arangodb
diff --git a/bel/core/logging_setup.py b/bel/core/logging_setup.py
index 2a2c361..9421a47 100644
--- a/bel/core/logging_setup.py
+++ b/bel/core/logging_setup.py
@@ -1,127 +1,126 @@
-# Local Imports
-# Standard Library
-import sys
-
-# Third Party
-from loguru import logger
-
-logger.add(
- sys.stderr,
- serialize=True,
- colorize=True,
- format="{time} {level} {file} {line} {message}",
- filter="my_module",
- level="INFO",
-)
-
-
-# # Standard Library
-# # from loguru import logger
-# import datetime
-# from loguru import logger
-# import sys
-
-# # Third Party Imports
-# from loguru import logger
-# from loguru import logger._frames
-# from
-# import jsonlogger
-
-# # Local Imports
-# import bel.core.settings as settings
-
-# """Notes
-
-# Example on how to setup gunicorn well: http://stevetarver.github.io/2017/05/10/python-falcon-logging.html
-
-# """
-
-# JSON_INDENT = 4
-
-
-# class CustomJsonFormatter(jsonlogger.JsonFormatter):
-# """Customize python json logger to match structlog"""
-
-# def add_fields(self, log_record, record, message_dict):
-# super(CustomJsonFormatter, self).add_fields(log_record, record, message_dict)
-# if not log_record.get("timestamp"):
-# # this doesn't use record.created, so it is slightly off
-# now = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
-# log_record["timestamp"] = now
-# if log_record.get("level"):
-# log_record["level"] = log_record["level"].upper()
-# else:
-# log_record["level"] = record.levelname
-
-
-# def add_structlog_app_context(logger, method_name, event_dict):
-# f, name = structlog._frames._find_first_app_frame_and_name(["logging", __name__])
-# event_dict["file"] = f.f_code.co_filename
-# event_dict["line"] = f.f_lineno
-# event_dict["function"] = f.f_code.co_name
-
-# return event_dict
-
-
-# # Configure Stdlib logging
-# formatter = CustomJsonFormatter("(timestamp) (level) (name) (message)", json_indent=JSON_INDENT)
-# handler = logging.StreamHandler(sys.stdout)
-# handler.setFormatter(formatter)
-
-# root_logger = logging.getLogger()
-# root_logger.addHandler(handler)
-# root_logger.setLevel(settings.LOG_LEVEL)
-
-# bel_logger = logging.getLogger("bel")
-# bel_logger.addHandler(handler)
-# bel_logger.setLevel(settings.LOG_LEVEL)
-
-# belapi_logger = logging.getLogger("belapi")
-# belapi_logger.addHandler(handler)
-# belapi_logger.setLevel(settings.LOG_LEVEL)
-
-# # Turn off uvicorn access logging by default
-# uvicorn_logger = logging.getLogger("uvicorn")
-# uvicorn_logger.setLevel("WARNING")
-
-# uvicornerror_logger = logging.getLogger("uvicorn.error")
-# uvicornerror_logger.setLevel("WARNING")
-
-# uvicornasgi_logger = logging.getLogger("uvicorn.asgi")
-# uvicornasgi_logger.setLevel("WARNING")
-
-# uvicornaccess_logger = logging.getLogger("uvicorn.access")
-# uvicornaccess_logger.setLevel("WARNING")
-
-# elasticsearch_logger = logging.getLogger("elasticsearch")
-# elasticsearch_logger.setLevel("WARNING")
-
-# urllib3_logger = logging.getLogger("urllib3")
-# urllib3_logger.setLevel("WARNING")
-
-# httpx_logger = logging.getLogger("httpx")
-# httpx_logger.setLevel("WARNING")
-
-# fastapi_logger = logging.getLogger("fastapi")
-# fastapi_logger.setLevel("WARNING")
-
-# websockets_logger = logging.getLogger("websockets")
-# websockets_logger.setLevel("WARNING")
-
-# structlog.configure(
-# processors=[
-# structlog.stdlib.filter_by_level,
-# structlog.stdlib.add_logger_name,
-# structlog.stdlib.add_log_level,
-# structlog.processors.StackInfoRenderer(),
-# structlog.dev.set_exc_info,
-# add_structlog_app_context,
-# structlog.processors.format_exc_info,
-# structlog.processors.TimeStamper(fmt="iso"),
-# structlog.stdlib.render_to_log_kwargs,
-# ],
-# wrapper_class=structlog.stdlib.BoundLogger,
-# context_class=dict, # or OrderedDict if the runtime's dict is unordered (e.g. Python <3.6)
-# logger_factory=structlog.stdlib.LoggerFactory(),
-# cache_logger_on_first_use=True,
-# )
+# Standard Library
+import sys
+
+# Third Party
+from loguru import logger
+
+logger.add(
+ sys.stderr,
+ serialize=True,
+ colorize=True,
+ format="{time} {level} {file} {line} {message}",
+ filter="my_module",
+ level="INFO",
+)
+
+
+# # Standard Library
+# # from loguru import logger
+# import datetime
+# from loguru import logger
+# import sys
+
+#
+# from loguru import logger
+# from loguru import logger._frames
+# from
+# import jsonlogger
+
+#
+# import bel.core.settings as settings
+
+# """Notes
+
+# Example on how to setup gunicorn well: http://stevetarver.github.io/2017/05/10/python-falcon-logging.html
+
+# """
+
+# JSON_INDENT = 4
+
+
+# class CustomJsonFormatter(jsonlogger.JsonFormatter):
+# """Customize python json logger to match structlog"""
+
+# def add_fields(self, log_record, record, message_dict):
+# super(CustomJsonFormatter, self).add_fields(log_record, record, message_dict)
+# if not log_record.get("timestamp"):
+# # this doesn't use record.created, so it is slightly off
+# now = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
+# log_record["timestamp"] = now
+# if log_record.get("level"):
+# log_record["level"] = log_record["level"].upper()
+# else:
+# log_record["level"] = record.levelname
+
+
+# def add_structlog_app_context(logger, method_name, event_dict):
+# f, name = structlog._frames._find_first_app_frame_and_name(["logging", __name__])
+# event_dict["file"] = f.f_code.co_filename
+# event_dict["line"] = f.f_lineno
+# event_dict["function"] = f.f_code.co_name
+
+# return event_dict
+
+
+# # Configure Stdlib logging
+# formatter = CustomJsonFormatter("(timestamp) (level) (name) (message)", json_indent=JSON_INDENT)
+# handler = logging.StreamHandler(sys.stdout)
+# handler.setFormatter(formatter)
+
+# root_logger = logging.getLogger()
+# root_logger.addHandler(handler)
+# root_logger.setLevel(settings.LOG_LEVEL)
+
+# bel_logger = logging.getLogger("bel")
+# bel_logger.addHandler(handler)
+# bel_logger.setLevel(settings.LOG_LEVEL)
+
+# belapi_logger = logging.getLogger("belapi")
+# belapi_logger.addHandler(handler)
+# belapi_logger.setLevel(settings.LOG_LEVEL)
+
+# # Turn off uvicorn access logging by default
+# uvicorn_logger = logging.getLogger("uvicorn")
+# uvicorn_logger.setLevel("WARNING")
+
+# uvicornerror_logger = logging.getLogger("uvicorn.error")
+# uvicornerror_logger.setLevel("WARNING")
+
+# uvicornasgi_logger = logging.getLogger("uvicorn.asgi")
+# uvicornasgi_logger.setLevel("WARNING")
+
+# uvicornaccess_logger = logging.getLogger("uvicorn.access")
+# uvicornaccess_logger.setLevel("WARNING")
+
+# elasticsearch_logger = logging.getLogger("elasticsearch")
+# elasticsearch_logger.setLevel("WARNING")
+
+# urllib3_logger = logging.getLogger("urllib3")
+# urllib3_logger.setLevel("WARNING")
+
+# httpx_logger = logging.getLogger("httpx")
+# httpx_logger.setLevel("WARNING")
+
+# fastapi_logger = logging.getLogger("fastapi")
+# fastapi_logger.setLevel("WARNING")
+
+# websockets_logger = logging.getLogger("websockets")
+# websockets_logger.setLevel("WARNING")
+
+# structlog.configure(
+# processors=[
+# structlog.stdlib.filter_by_level,
+# structlog.stdlib.add_logger_name,
+# structlog.stdlib.add_log_level,
+# structlog.processors.StackInfoRenderer(),
+# structlog.dev.set_exc_info,
+# add_structlog_app_context,
+# structlog.processors.format_exc_info,
+# structlog.processors.TimeStamper(fmt="iso"),
+# structlog.stdlib.render_to_log_kwargs,
+# ],
+# wrapper_class=structlog.stdlib.BoundLogger,
+# context_class=dict, # or OrderedDict if the runtime's dict is unordered (e.g. Python <3.6)
+# logger_factory=structlog.stdlib.LoggerFactory(),
+# cache_logger_on_first_use=True,
+# )
diff --git a/bel/core/mail.py b/bel/core/mail.py
index 6eae103..5c2c92b 100644
--- a/bel/core/mail.py
+++ b/bel/core/mail.py
@@ -2,12 +2,10 @@
from typing import List
# Third Party
-# Third Party Imports
import requests
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
@@ -26,7 +24,7 @@ def send_simple_email(to: List[str], subject: str, body: str, body_html: str = "
r = requests.post(
f"{settings.MAIL_API}/messages",
- auth=("api", settings.MAIL_API_KEY),
+ auth=("api", settings.MAIL_API_TOKEN),
data=data,
)
diff --git a/bel/core/settings.py b/bel/core/settings.py
index 629ffc0..cdd1c99 100644
--- a/bel/core/settings.py
+++ b/bel/core/settings.py
@@ -43,7 +43,7 @@ def getenv_boolean(var_name, default=False):
# Mailgun settings
MAIL_API = os.getenv("MAIL_API", default=None)
-MAIL_API_KEY = os.getenv("MAIL_API_KEY")
+MAIL_API_TOKEN = os.getenv("MAIL_API_TOKEN")
MAIL_FROM = os.getenv("MAIL_FROM")
# Auth Settings
@@ -130,6 +130,7 @@ def getenv_boolean(var_name, default=False):
# Boost these namespaces in term search results and completions
BEL_BOOST_NAMESPACES = json.loads(os.getenv("BEL_BOOST_NAMESPACES", default="[]"))
+
if not BEL_BOOST_NAMESPACES:
BEL_BOOST_NAMESPACES = ["HGNC", "MGI", "RGD", "ZFIN", "CHEBI", "GO", "TAX"]
diff --git a/bel/core/utils.py b/bel/core/utils.py
index e242468..6e04610 100644
--- a/bel/core/utils.py
+++ b/bel/core/utils.py
@@ -13,11 +13,8 @@
from typing import Any, List, Mapping, Optional, Tuple
# Third Party
-# Local Imports
import dateutil
import httpx
-
-# Third Party Imports
import ulid
from cityhash import CityHash64
from loguru import logger
diff --git a/bel/db/arangodb.py b/bel/db/arangodb.py
index c64327e..2222d5c 100644
--- a/bel/db/arangodb.py
+++ b/bel/db/arangodb.py
@@ -4,7 +4,6 @@
from typing import List, Mapping, Optional
# Third Party
-# Third Party Imports
import arango
import arango.client
import arango.database
@@ -13,7 +12,6 @@
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
from bel.core.utils import _create_hash
@@ -52,7 +50,9 @@ def get_user_credentials(username, password):
def get_client(url=None, port=None, username=None, password=None):
"""Get arango client db handle"""
- url = boltons.iterutils.first([url, settings.ARANGO_URL, "http://localhost:8529"])
+ url = boltons.iterutils.first(
+ [url, settings.ARANGO_URL, "http://localhost:8529"] # DevSkim: ignore DS137138
+ ) # DevSkim: ignore DS137138
(username, password) = get_user_credentials(username, password)
try:
@@ -267,12 +267,12 @@ def get_bel_handles(client, username=None, password=None):
if bel_db.has_collection(bel_config_name):
bel_config_coll = bel_db.collection(bel_config_name)
else:
- bel_config_coll = bel_db.create_collection(bel_config_name, index_bucket_count=64)
+ bel_config_coll = bel_db.create_collection(bel_config_name)
if bel_db.has_collection(bel_validations_name):
bel_validations_coll = bel_db.collection(bel_validations_name)
else:
- bel_validations_coll = bel_db.create_collection(bel_validations_name, index_bucket_count=64)
+ bel_validations_coll = bel_db.create_collection(bel_validations_name)
return {
"bel_db": bel_db,
@@ -350,9 +350,16 @@ def batch_load_docs(db, doc_iterator, on_duplicate: str = "replace"):
if counter % batch_size == 0:
# logger.debug(f"Bulk import arangodb: {counter}")
for collection_name in docs:
- collections[collection_name].import_bulk(
- docs[collection_name], on_duplicate=on_duplicate, halt_on_error=False
- )
+ try:
+ results = collections[collection_name].import_bulk(
+ docs[collection_name], on_duplicate=on_duplicate, halt_on_error=False
+ )
+
+ except Exception as e:
+ logger.exception(
+ f"Problem loading arangodb using import_bulk - error: {str(e)}"
+ )
+
docs[collection_name] = []
if counter % 1000000 == 0:
@@ -369,6 +376,8 @@ def batch_load_docs(db, doc_iterator, on_duplicate: str = "replace"):
except arango.exceptions.DocumentInsertError as e:
logger.error(f"Problem with arango bulk import: {str(e)}")
+ return counter
+
def arango_id_to_key(_id):
"""Remove illegal chars from potential arangodb _key (id) or return hashed key if > 60 chars
diff --git a/bel/db/elasticsearch.py b/bel/db/elasticsearch.py
index fd0995f..aa1d8cc 100644
--- a/bel/db/elasticsearch.py
+++ b/bel/db/elasticsearch.py
@@ -2,14 +2,12 @@
import os
# Third Party
-# Third Party Imports
import elasticsearch.helpers
import yaml
from elasticsearch import Elasticsearch
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
cur_dir_name = os.path.dirname(os.path.realpath(__file__))
diff --git a/bel/db/redis.py b/bel/db/redis.py
index 619d9b5..962004f 100644
--- a/bel/db/redis.py
+++ b/bel/db/redis.py
@@ -3,12 +3,10 @@
from typing import List, Tuple
# Third Party
-# Third Party Imports
import redis
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
redis_db = redis.Redis(host=settings.REDIS_HOST, port=settings.REDIS_PORT, db=0)
diff --git a/bel/lang/ast.py b/bel/lang/ast.py
index cc70720..6caff73 100644
--- a/bel/lang/ast.py
+++ b/bel/lang/ast.py
@@ -11,13 +11,11 @@
from typing import Any, List, Mapping, Optional, Tuple, Union
# Third Party
-# Third Party Imports
import yaml
from loguru import logger
from pydantic import BaseModel, Field
# Local
-# Local Imports
import bel.core.settings as settings
import bel.db.arangodb
import bel.terms.orthologs
@@ -337,7 +335,7 @@ def print_tree(self, indent=0):
else:
print("\t" * (indent + 1) + arg.print_tree())
- def subcomponents(self, subcomponents):
+ def subcomponents(self, subcomponents=None):
"""Generate subcomponents of the BEL subject or object
Args:
@@ -348,13 +346,17 @@ def subcomponents(self, subcomponents):
List[str]: subcomponents of BEL subject or object
"""
+ if subcomponents is None:
+ subcomponents = []
+
for arg in self.args:
if arg.__class__.__name__ == "Function":
subcomponents.append(arg.to_string())
- if arg.function_type == "primary":
- arg.subcomponents(subcomponents)
+ arg.subcomponents(subcomponents)
else:
subcomponents.append(arg.to_string())
+ if hasattr(arg, "entity") and arg.entity.nsval.label:
+ subcomponents.append(f"{arg.entity.nsval.namespace}:{arg.entity.nsval.label}")
return subcomponents
@@ -363,14 +365,17 @@ def subcomponents(self, subcomponents):
# Argument objects #
#####################
class Arg(object):
- def __init__(self, parent=None, span: Span = None):
+ def __init__(self, version: str = "latest", parent=None, span: Union[Span, NsArgSpan] = None):
self.optional = False
self.type = "Arg"
+ self.version = version
self.parent = parent
self.siblings = []
+ self.belspec = get_enhanced_belspec(self.version)
+
# https://github.com/belbio/bel/issues/13
# used for sorting arguments (position, modifier, modifier-specific sort parameter)
@@ -410,10 +415,11 @@ class NSArg(Arg):
"""Parsed NSArg value"""
def __init__(self, entity: BelEntity, parent=None, span: NsArgSpan = None):
- Arg.__init__(self, parent, span)
+ Arg.__init__(self, parent)
self.entity = entity
-
+ self.span: NsArgSpan = span
+ self.parent = parent
self.type = "NSArg"
def canonicalize(
@@ -493,6 +499,8 @@ def __init__(self, value, span: Span = None, parent=None):
Arg.__init__(self, parent, span)
self.value = value
self.type = "StrArg"
+ self.span: Span = span
+ self.parent = parent
def update(self, value: str):
"""Update to new BEL Entity"""
@@ -906,6 +914,31 @@ def validate(self):
if arg and arg.type in ["Function"]:
self.errors.extend(arg.validate(errors=[]))
+ def subcomponents(self, subcomponents=None):
+ """Generate subcomponents of the BEL subject or object
+
+ Args:
+ AST
+ subcomponents: Pass an empty list to start a new subcomponents request
+
+ Returns:
+ List[str]: subcomponents of BEL subject or object
+ """
+
+ if subcomponents is None:
+ subcomponents = []
+
+ for arg in self.args:
+ if arg.__class__.__name__ == "Function":
+ subcomponents.append(arg.to_string())
+ arg.subcomponents(subcomponents)
+ else:
+ subcomponents.append(arg.to_string())
+ if hasattr(arg, "entity") and arg.entity.nsval.label:
+ subcomponents.append(f"{arg.entity.nsval.namespace}:{arg.entity.nsval.label}")
+
+ return subcomponents
+
def to_string(self, fmt: str = "medium") -> str:
"""Convert AST object to string
diff --git a/bel/lang/belobj.py b/bel/lang/belobj.py
index 33b547a..108add5 100644
--- a/bel/lang/belobj.py
+++ b/bel/lang/belobj.py
@@ -6,11 +6,9 @@
from typing import Any, List, Mapping, Optional, Union
# Third Party
-# Third Party Imports
from loguru import logger
# Local
-# Local Imports
import bel.belspec.crud
import bel.core.settings as settings
import bel.terms.terms
diff --git a/bel/lang/migrate_1_2.py b/bel/lang/migrate_1_2.py
index cceb231..a33ee3c 100644
--- a/bel/lang/migrate_1_2.py
+++ b/bel/lang/migrate_1_2.py
@@ -9,11 +9,9 @@
import json
# Third Party
-# Third Party Imports
from loguru import logger
# Local
-# Local Imports
import bel.belspec.crud
import bel.core.settings as settings
from bel.belspec.crud import get_enhanced_belspec
diff --git a/bel/nanopub/files.py b/bel/nanopub/files.py
index e0b4dad..de0b4c4 100644
--- a/bel/nanopub/files.py
+++ b/bel/nanopub/files.py
@@ -15,7 +15,6 @@
from typing import Any, Iterable, List, Mapping, Tuple
# Third Party
-# Third Party Imports
import click
import yaml
from loguru import logger
diff --git a/bel/nanopub/nanopubs.py b/bel/nanopub/nanopubs.py
index 5f8cdd9..793d349 100644
--- a/bel/nanopub/nanopubs.py
+++ b/bel/nanopub/nanopubs.py
@@ -4,12 +4,9 @@
# Third Party
from cityhash import CityHash64
-
-# Third Party Imports
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
import bel.lang.belobj
from bel.core.utils import http_client
diff --git a/bel/nanopub/pubmed.py b/bel/nanopub/pubmed.py
index 798545a..bf6b7fa 100755
--- a/bel/nanopub/pubmed.py
+++ b/bel/nanopub/pubmed.py
@@ -18,13 +18,10 @@
# Third Party
import cachetools
import httpx
-
-# Third Party Imports
from loguru import logger
from lxml import etree
# Local
-# Local Imports
import bel.core.settings as settings
import bel.terms.terms
from bel.core.utils import http_client, url_path_param_quoting
diff --git a/bel/nanopub/validate.py b/bel/nanopub/validate.py
index 1c13a8a..5c0f3db 100644
--- a/bel/nanopub/validate.py
+++ b/bel/nanopub/validate.py
@@ -4,11 +4,9 @@
from typing import List, Tuple
# Third Party
-# Third Party Imports
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
import bel.core.utils
import bel.lang.belobj
diff --git a/bel/resources/manage.py b/bel/resources/manage.py
index 15862ba..d46410a 100644
--- a/bel/resources/manage.py
+++ b/bel/resources/manage.py
@@ -5,13 +5,10 @@
from typing import List
# Third Party
-# Third Party Imports
from loguru import logger
# Local
import bel.core.mail
-
-# Local Imports
import bel.core.settings as settings
import bel.core.utils
import bel.db.arangodb as arangodb
diff --git a/bel/resources/namespace.py b/bel/resources/namespace.py
index f09e854..d1ada0c 100644
--- a/bel/resources/namespace.py
+++ b/bel/resources/namespace.py
@@ -7,13 +7,11 @@
from typing import IO, Optional
# Third Party
-# Third Party Imports
import cachetools
from arango import ArangoError
from loguru import logger
# Local
-# Local Imports
import bel.core.mail
import bel.core.settings as settings
import bel.db.elasticsearch as elasticsearch
@@ -98,7 +96,7 @@ def load_terms(
result = {"state": "Succeeded", "messages": []}
- statistics = {
+ metadata["statistics"] = {
"entities_count": 0,
"synonyms_count": 0,
"entity_types": defaultdict(int),
@@ -118,14 +116,13 @@ def load_terms(
namespace = metadata["namespace"]
version = metadata["version"]
-
- ################################################################################
- # Elasticsearch index processing
- ################################################################################
es_version = version.replace("T", "").replace("-", "").replace(":", "")
index_prefix = f"{settings.TERMS_INDEX}_{namespace.lower()}"
index_name = f"{index_prefix}_{es_version}"
+ ################################################################################
+ # Elasticsearch index processing
+ ################################################################################
# Create index with mapping
if force or prior_version != version:
elasticsearch.create_terms_index(index_name)
@@ -137,11 +134,9 @@ def load_terms(
return result
- terms_iterator = terms_iterator_for_elasticsearch(f, index_name, statistics)
- elasticsearch.bulk_load_docs(terms_iterator)
-
# Using side effect to get statistics from terms_iterator_for_elasticsearch on purpose
- metadata["statistics"] = copy.deepcopy(statistics)
+ terms_iterator = terms_iterator_for_elasticsearch(f, index_name, metadata)
+ elasticsearch.bulk_load_docs(terms_iterator)
# Remove old namespace index
index_names = elasticsearch.get_all_index_names()
@@ -228,6 +223,7 @@ def terms_iterator_for_arangodb(f: IO, version: str):
term["_key"] = term_db_key
term["version"] = version
+
# Add term record to terms collection
yield (terms_coll_name, term)
@@ -321,7 +317,7 @@ def terms_iterator_for_arangodb(f: IO, version: str):
yield equiv_edge
-def terms_iterator_for_elasticsearch(f: IO, index_name: str, statistics: dict):
+def terms_iterator_for_elasticsearch(f: IO, index_name: str, metadata: dict):
"""Add index_name to term documents for bulk load"""
species_list = settings.BEL_FILTER_SPECIES
@@ -336,15 +332,15 @@ def terms_iterator_for_elasticsearch(f: IO, index_name: str, statistics: dict):
term = term["term"]
# Collect statistics
- statistics["entities_count"] += 1
- statistics["synonyms_count"] += len(term.get("synonyms", []))
+ metadata["statistics"]["entities_count"] += 1
+ metadata["statistics"]["synonyms_count"] += len(term.get("synonyms", []))
for entity_type in term.get("entity_types", []):
- statistics["entity_types"][entity_type] += 1
+ metadata["statistics"]["entity_types"][entity_type] += 1
for annotation_type in term.get("annotation_types", []):
- statistics["annotation_types"][annotation_type] += 1
+ metadata["statistics"]["annotation_types"][annotation_type] += 1
for equivalence in term.get("equivalence_keys", []):
ns, id_ = equivalence.split(":", 1)
- statistics["equivalenced_namespaces"][ns] += 1
+ metadata["statistics"]["equivalenced_namespaces"][ns] += 1
# Skip if species not listed in config species_list
species_key = term.get("species_key", None)
diff --git a/bel/resources/ortholog.py b/bel/resources/ortholog.py
index 0f23c97..06c091e 100644
--- a/bel/resources/ortholog.py
+++ b/bel/resources/ortholog.py
@@ -6,12 +6,10 @@
from typing import IO, Mapping, Optional
# Third Party
-# Third Party Imports
from arango import ArangoError
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
import bel.core.utils
import bel.db.arangodb as arangodb
diff --git a/bel/schemas/bel.py b/bel/schemas/bel.py
index b5accc2..7dc3629 100644
--- a/bel/schemas/bel.py
+++ b/bel/schemas/bel.py
@@ -6,12 +6,10 @@
from typing import Any, List, Mapping, Optional, Tuple, Union
# Third Party
-# Third Party Imports
from loguru import logger
from pydantic import BaseModel, Field, root_validator
# Local
-# Local Imports
import bel.core.settings as settings
import bel.db.arangodb
import bel.terms.orthologs
diff --git a/bel/schemas/belspec.py b/bel/schemas/belspec.py
index 360f689..cd23c38 100644
--- a/bel/schemas/belspec.py
+++ b/bel/schemas/belspec.py
@@ -1,4 +1,3 @@
-# Third Party Imports
# Standard Library
import enum
from typing import Any, List, Mapping, Optional, Union
diff --git a/bel/schemas/config.py b/bel/schemas/config.py
index ded5077..ac058a5 100644
--- a/bel/schemas/config.py
+++ b/bel/schemas/config.py
@@ -6,7 +6,6 @@
from typing import Any, List, Mapping, Optional, Tuple, Union
# Third Party
-# Third Party Imports
from pydantic import BaseModel, Field, root_validator
diff --git a/bel/schemas/info.py b/bel/schemas/info.py
index 5dbceda..013a95f 100644
--- a/bel/schemas/info.py
+++ b/bel/schemas/info.py
@@ -1,4 +1,3 @@
-# Third Party Imports
# Standard Library
from typing import Optional, Union
@@ -8,12 +7,3 @@
class Version(BaseModel):
version: str
-
-
-class Status(BaseModel):
- state: str
- belapi_version: str
- bel_version: str
- # settings: dict
- # elasticsearch_stats: dict
- fastapi_version: str
diff --git a/bel/schemas/msg.py b/bel/schemas/msg.py
index 52c715b..a626f9a 100644
--- a/bel/schemas/msg.py
+++ b/bel/schemas/msg.py
@@ -2,7 +2,6 @@
from typing import Optional
# Third Party
-# Third Party Imports
from pydantic import BaseModel
diff --git a/bel/schemas/nanopubs.py b/bel/schemas/nanopubs.py
index bb44deb..0f55291 100644
--- a/bel/schemas/nanopubs.py
+++ b/bel/schemas/nanopubs.py
@@ -3,7 +3,6 @@
from typing import Any, List, Mapping, Optional, Union
# Third Party
-# Third Party Imports
import pydantic
from pydantic import AnyUrl, BaseModel, Field, HttpUrl, validator
@@ -71,7 +70,7 @@ class Metadata(BaseModel):
gd_status: Optional[str]
gd_createTS: Optional[str]
gd_updateTS: Optional[str]
- gd_validation: Optional[ValidationErrors] = {}
+ gd_validation: Optional[ValidationErrors]
gd_hash: Optional[str] = Field(
"",
title="Nanopub hash",
diff --git a/bel/schemas/terms.py b/bel/schemas/terms.py
index 691b213..cf1008e 100644
--- a/bel/schemas/terms.py
+++ b/bel/schemas/terms.py
@@ -1,4 +1,3 @@
-# Third Party Imports
# Standard Library
import enum
from typing import Any, List, Mapping, Optional, Union
@@ -7,7 +6,6 @@
from pydantic import BaseModel, Field, HttpUrl
# Local
-# Local Imports
from bel.schemas.constants import AnnotationTypesEnum, EntityTypesEnum
Key = str # Type alias for NsVal Key values
diff --git a/bel/terms/orthologs.py b/bel/terms/orthologs.py
index 48c903f..7ecb6f6 100644
--- a/bel/terms/orthologs.py
+++ b/bel/terms/orthologs.py
@@ -3,12 +3,9 @@
# Third Party
import cachetools
-
-# Third Party Imports
from loguru import logger
# Local
-# Local Imports
import bel.db.arangodb
import bel.terms.terms
from bel.db.arangodb import ortholog_edges_name, ortholog_nodes_name, resources_db
diff --git a/bel/terms/terms.py b/bel/terms/terms.py
index 9c00e8e..aa051e5 100644
--- a/bel/terms/terms.py
+++ b/bel/terms/terms.py
@@ -9,7 +9,6 @@
from loguru import logger
# Local
-# Local Imports
import bel.core.settings as settings
from bel.core.utils import asyncify, namespace_quoting, split_key_label
from bel.db.arangodb import arango_id_to_key, resources_db, terms_coll_name
@@ -48,7 +47,7 @@ def get_terms(term_key: Key) -> List[Term]:
)
]
- term_key = term_key.replace("'", "\\'")
+ term_key = term_key.replace("'", "") # Keys can't have single quotes in them-r
query = f"""
FOR term in {terms_coll_name}
FILTER term.key == '{term_key}' OR '{term_key}' in term.alt_keys OR '{term_key}' in term.obsolete_keys
@@ -63,7 +62,11 @@ def get_terms(term_key: Key) -> List[Term]:
if namespace == "EG":
return []
- (namespace, label) = term_key.split(":", 1)
+ try:
+ (namespace, label) = term_key.split(":", 1)
+ except Exception:
+ return [] # no results - not a valid value
+
query = f"""
for doc in {terms_coll_name}
filter doc.namespace == "{namespace}"
diff --git a/poetry.lock b/poetry.lock
index acd660d..9c0a964 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -106,7 +106,7 @@ description = "Python package for providing Mozilla's CA Bundle."
name = "certifi"
optional = false
python-versions = "*"
-version = "2020.11.8"
+version = "2020.12.5"
[[package]]
category = "main"
@@ -229,7 +229,7 @@ description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
name = "h11"
optional = false
python-versions = "*"
-version = "0.9.0"
+version = "0.11.0"
[[package]]
category = "main"
@@ -282,14 +282,14 @@ marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_versi
name = "importlib-metadata"
optional = false
python-versions = ">=3.6"
-version = "3.1.0"
+version = "3.1.1"
[package.dependencies]
zipp = ">=0.5"
[package.extras]
-docs = ["sphinx", "rst.linker"]
-testing = ["packaging", "pep517", "unittest2", "importlib-resources (>=1.3)"]
+docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
[[package]]
category = "main"
@@ -355,7 +355,7 @@ description = "Powerful and Pythonic XML processing library combining libxml2/li
name = "lxml"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
-version = "4.6.1"
+version = "4.6.2"
[package.extras]
cssselect = ["cssselect (>=0.7)"]
@@ -410,11 +410,10 @@ description = "Core utilities for Python packages"
name = "packaging"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-version = "20.4"
+version = "20.7"
[package.dependencies]
pyparsing = ">=2.0.2"
-six = "*"
[[package]]
category = "dev"
@@ -473,7 +472,7 @@ description = "Data validation and settings management using python 3.6 type hin
name = "pydantic"
optional = false
python-versions = ">=3.6"
-version = "1.7.2"
+version = "1.7.3"
[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
@@ -497,7 +496,7 @@ description = "Pygments is a syntax highlighting package written in Python."
name = "pygments"
optional = false
python-versions = ">=3.5"
-version = "2.7.2"
+version = "2.7.3"
[[package]]
category = "main"
@@ -934,7 +933,7 @@ version = "1.12.1"
[[package]]
category = "main"
description = "Backport of pathlib-compatible object wrapper for zip files"
-marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\" or python_version >= \"3.5\" and python_version < \"3.8\" and (python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\")"
+marker = "python_version >= \"3.5\" and python_version < \"3.8\" or python_version < \"3.8\""
name = "zipp"
optional = false
python-versions = ">=3.6"
@@ -990,8 +989,8 @@ cachetools = [
{file = "cachetools-4.1.1.tar.gz", hash = "sha256:bbaa39c3dede00175df2dc2b03d0cf18dd2d32a7de7beb68072d13043c9edb20"},
]
certifi = [
- {file = "certifi-2020.11.8-py2.py3-none-any.whl", hash = "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd"},
- {file = "certifi-2020.11.8.tar.gz", hash = "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"},
+ {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
+ {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
]
chardet = [
{file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
@@ -1065,8 +1064,8 @@ gunicorn = [
{file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"},
]
h11 = [
- {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"},
- {file = "h11-0.9.0.tar.gz", hash = "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1"},
+ {file = "h11-0.11.0-py2.py3-none-any.whl", hash = "sha256:ab6c335e1b6ef34b205d5ca3e228c9299cc7218b049819ec84a388c2525e5d87"},
+ {file = "h11-0.11.0.tar.gz", hash = "sha256:3c6c61d69c6f13d41f1b80ab0322f1872702a3ba26e12aa864c928f6a43fbaab"},
]
httpcore = [
{file = "httpcore-0.12.2-py3-none-any.whl", hash = "sha256:420700af11db658c782f7e8fda34f9dcd95e3ee93944dd97d78cb70247e0cd06"},
@@ -1081,8 +1080,8 @@ idna = [
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
]
importlib-metadata = [
- {file = "importlib_metadata-3.1.0-py2.py3-none-any.whl", hash = "sha256:590690d61efdd716ff82c39ca9a9d4209252adfe288a4b5721181050acbd4175"},
- {file = "importlib_metadata-3.1.0.tar.gz", hash = "sha256:d9b8a46a0885337627a6430db287176970fff18ad421becec1d64cfc763c2099"},
+ {file = "importlib_metadata-3.1.1-py3-none-any.whl", hash = "sha256:6112e21359ef8f344e7178aa5b72dc6e62b38b0d008e6d3cb212c5b84df72013"},
+ {file = "importlib_metadata-3.1.1.tar.gz", hash = "sha256:b0c2d3b226157ae4517d9625decf63591461c66b3a808c2666d538946519d170"},
]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@@ -1124,42 +1123,43 @@ loguru = [
{file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
]
lxml = [
- {file = "lxml-4.6.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:4b7572145054330c8e324a72d808c8c8fbe12be33368db28c39a255ad5f7fb51"},
- {file = "lxml-4.6.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:302160eb6e9764168e01d8c9ec6becddeb87776e81d3fcb0d97954dd51d48e0a"},
- {file = "lxml-4.6.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d4ad7fd3269281cb471ad6c7bafca372e69789540d16e3755dd717e9e5c9d82f"},
- {file = "lxml-4.6.1-cp27-cp27m-win32.whl", hash = "sha256:189ad47203e846a7a4951c17694d845b6ade7917c47c64b29b86526eefc3adf5"},
- {file = "lxml-4.6.1-cp27-cp27m-win_amd64.whl", hash = "sha256:56eff8c6fb7bc4bcca395fdff494c52712b7a57486e4fbde34c31bb9da4c6cc4"},
- {file = "lxml-4.6.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:23c83112b4dada0b75789d73f949dbb4e8f29a0a3511647024a398ebd023347b"},
- {file = "lxml-4.6.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0e89f5d422988c65e6936e4ec0fe54d6f73f3128c80eb7ecc3b87f595523607b"},
- {file = "lxml-4.6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2358809cc64394617f2719147a58ae26dac9e21bae772b45cfb80baa26bfca5d"},
- {file = "lxml-4.6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:be1ebf9cc25ab5399501c9046a7dcdaa9e911802ed0e12b7d620cd4bbf0518b3"},
- {file = "lxml-4.6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:4fff34721b628cce9eb4538cf9a73d02e0f3da4f35a515773cce6f5fe413b360"},
- {file = "lxml-4.6.1-cp35-cp35m-win32.whl", hash = "sha256:475325e037fdf068e0c2140b818518cf6bc4aa72435c407a798b2db9f8e90810"},
- {file = "lxml-4.6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f98b6f256be6cec8dd308a8563976ddaff0bdc18b730720f6f4bee927ffe926f"},
- {file = "lxml-4.6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:be7c65e34d1b50ab7093b90427cbc488260e4b3a38ef2435d65b62e9fa3d798a"},
- {file = "lxml-4.6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d18331ea905a41ae71596502bd4c9a2998902328bbabd29e3d0f5f8569fabad1"},
- {file = "lxml-4.6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3d9b2b72eb0dbbdb0e276403873ecfae870599c83ba22cadff2db58541e72856"},
- {file = "lxml-4.6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d20d32cbb31d731def4b1502294ca2ee99f9249b63bc80e03e67e8f8e126dea8"},
- {file = "lxml-4.6.1-cp36-cp36m-win32.whl", hash = "sha256:d182eada8ea0de61a45a526aa0ae4bcd222f9673424e65315c35820291ff299c"},
- {file = "lxml-4.6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:c0dac835c1a22621ffa5e5f999d57359c790c52bbd1c687fe514ae6924f65ef5"},
- {file = "lxml-4.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d84d741c6e35c9f3e7406cb7c4c2e08474c2a6441d59322a00dcae65aac6315d"},
- {file = "lxml-4.6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8862d1c2c020cb7a03b421a9a7b4fe046a208db30994fc8ff68c627a7915987f"},
- {file = "lxml-4.6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3a7a380bfecc551cfd67d6e8ad9faa91289173bdf12e9cfafbd2bdec0d7b1ec1"},
- {file = "lxml-4.6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:2d6571c48328be4304aee031d2d5046cbc8aed5740c654575613c5a4f5a11311"},
- {file = "lxml-4.6.1-cp37-cp37m-win32.whl", hash = "sha256:803a80d72d1f693aa448566be46ffd70882d1ad8fc689a2e22afe63035eb998a"},
- {file = "lxml-4.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:24e811118aab6abe3ce23ff0d7d38932329c513f9cef849d3ee88b0f848f2aa9"},
- {file = "lxml-4.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e311a10f3e85250910a615fe194839a04a0f6bc4e8e5bb5cac221344e3a7891"},
- {file = "lxml-4.6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a71400b90b3599eb7bf241f947932e18a066907bf84617d80817998cee81e4bf"},
- {file = "lxml-4.6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:211b3bcf5da70c2d4b84d09232534ad1d78320762e2c59dedc73bf01cb1fc45b"},
- {file = "lxml-4.6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e65c221b2115a91035b55a593b6eb94aa1206fa3ab374f47c6dc10d364583ff9"},
- {file = "lxml-4.6.1-cp38-cp38-win32.whl", hash = "sha256:d6f8c23f65a4bfe4300b85f1f40f6c32569822d08901db3b6454ab785d9117cc"},
- {file = "lxml-4.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:573b2f5496c7e9f4985de70b9bbb4719ffd293d5565513e04ac20e42e6e5583f"},
- {file = "lxml-4.6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:1d87936cb5801c557f3e981c9c193861264c01209cb3ad0964a16310ca1b3301"},
- {file = "lxml-4.6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2d5896ddf5389560257bbe89317ca7bcb4e54a02b53a3e572e1ce4226512b51b"},
- {file = "lxml-4.6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9b06690224258db5cd39a84e993882a6874676f5de582da57f3df3a82ead9174"},
- {file = "lxml-4.6.1-cp39-cp39-win32.whl", hash = "sha256:bb252f802f91f59767dcc559744e91efa9df532240a502befd874b54571417bd"},
- {file = "lxml-4.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ecaef52fd9b9535ae5f01a1dd2651f6608e4ec9dc136fc4dfe7ebe3c3ddb230"},
- {file = "lxml-4.6.1.tar.gz", hash = "sha256:c152b2e93b639d1f36ec5a8ca24cde4a8eefb2b6b83668fcd8e83a67badcb367"},
+ {file = "lxml-4.6.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f"},
+ {file = "lxml-4.6.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d"},
+ {file = "lxml-4.6.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2"},
+ {file = "lxml-4.6.2-cp27-cp27m-win32.whl", hash = "sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e"},
+ {file = "lxml-4.6.2-cp27-cp27m-win_amd64.whl", hash = "sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e"},
+ {file = "lxml-4.6.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2"},
+ {file = "lxml-4.6.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe"},
+ {file = "lxml-4.6.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388"},
+ {file = "lxml-4.6.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80"},
+ {file = "lxml-4.6.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b"},
+ {file = "lxml-4.6.2-cp35-cp35m-win32.whl", hash = "sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8"},
+ {file = "lxml-4.6.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780"},
+ {file = "lxml-4.6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af"},
+ {file = "lxml-4.6.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37"},
+ {file = "lxml-4.6.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98"},
+ {file = "lxml-4.6.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d"},
+ {file = "lxml-4.6.2-cp36-cp36m-win32.whl", hash = "sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf"},
+ {file = "lxml-4.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939"},
+ {file = "lxml-4.6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e"},
+ {file = "lxml-4.6.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711"},
+ {file = "lxml-4.6.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089"},
+ {file = "lxml-4.6.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01"},
+ {file = "lxml-4.6.2-cp37-cp37m-win32.whl", hash = "sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2"},
+ {file = "lxml-4.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc"},
+ {file = "lxml-4.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d"},
+ {file = "lxml-4.6.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3"},
+ {file = "lxml-4.6.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644"},
+ {file = "lxml-4.6.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308"},
+ {file = "lxml-4.6.2-cp38-cp38-win32.whl", hash = "sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505"},
+ {file = "lxml-4.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a"},
+ {file = "lxml-4.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931"},
+ {file = "lxml-4.6.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03"},
+ {file = "lxml-4.6.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5"},
+ {file = "lxml-4.6.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a"},
+ {file = "lxml-4.6.2-cp39-cp39-win32.whl", hash = "sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75"},
+ {file = "lxml-4.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf"},
+ {file = "lxml-4.6.2.tar.gz", hash = "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc"},
]
markupsafe = [
{file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
@@ -1216,8 +1216,8 @@ mypy-extensions = [
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
packaging = [
- {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"},
- {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
+ {file = "packaging-20.7-py2.py3-none-any.whl", hash = "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376"},
+ {file = "packaging-20.7.tar.gz", hash = "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236"},
]
pathspec = [
{file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
@@ -1239,36 +1239,36 @@ py = [
{file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"},
]
pydantic = [
- {file = "pydantic-1.7.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dfaa6ed1d509b5aef4142084206584280bb6e9014f01df931ec6febdad5b200a"},
- {file = "pydantic-1.7.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:2182ba2a9290964b278bcc07a8d24207de709125d520efec9ad6fa6f92ee058d"},
- {file = "pydantic-1.7.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:0fe8b45d31ae53d74a6aa0bf801587bd49970070eac6a6326f9fa2a302703b8a"},
- {file = "pydantic-1.7.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:01f0291f4951580f320f7ae3f2ecaf0044cdebcc9b45c5f882a7e84453362420"},
- {file = "pydantic-1.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:4ba6b903e1b7bd3eb5df0e78d7364b7e831ed8b4cd781ebc3c4f1077fbcb72a4"},
- {file = "pydantic-1.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b11fc9530bf0698c8014b2bdb3bbc50243e82a7fa2577c8cfba660bcc819e768"},
- {file = "pydantic-1.7.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:a3c274c49930dc047a75ecc865e435f3df89715c775db75ddb0186804d9b04d0"},
- {file = "pydantic-1.7.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:c68b5edf4da53c98bb1ccb556ae8f655575cb2e676aef066c12b08c724a3f1a1"},
- {file = "pydantic-1.7.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:95d4410c4e429480c736bba0db6cce5aaa311304aea685ebcf9ee47571bfd7c8"},
- {file = "pydantic-1.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a2fc7bf77ed4a7a961d7684afe177ff59971828141e608f142e4af858e07dddc"},
- {file = "pydantic-1.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9572c0db13c8658b4a4cb705dcaae6983aeb9842248b36761b3fbc9010b740f"},
- {file = "pydantic-1.7.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:f83f679e727742b0c465e7ef992d6da4a7e5268b8edd8fdaf5303276374bef52"},
- {file = "pydantic-1.7.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:e5fece30e80087d9b7986104e2ac150647ec1658c4789c89893b03b100ca3164"},
- {file = "pydantic-1.7.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:ce2d452961352ba229fe1e0b925b41c0c37128f08dddb788d0fd73fd87ea0f66"},
- {file = "pydantic-1.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:fc21a37ff3f545de80b166e1735c4172b41b017948a3fb2d5e2f03c219eac50a"},
- {file = "pydantic-1.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c9760d1556ec59ff745f88269a8f357e2b7afc75c556b3a87b8dda5bc62da8ba"},
- {file = "pydantic-1.7.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c1673633ad1eea78b1c5c420a47cd48717d2ef214c8230d96ca2591e9e00958"},
- {file = "pydantic-1.7.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:388c0c26c574ff49bad7d0fd6ed82fbccd86a0473fa3900397d3354c533d6ebb"},
- {file = "pydantic-1.7.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ab1d5e4d8de00575957e1c982b951bffaedd3204ddd24694e3baca3332e53a23"},
- {file = "pydantic-1.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:f045cf7afb3352a03bc6cb993578a34560ac24c5d004fa33c76efec6ada1361a"},
- {file = "pydantic-1.7.2-py3-none-any.whl", hash = "sha256:6665f7ab7fbbf4d3c1040925ff4d42d7549a8c15fe041164adfe4fc2134d4cce"},
- {file = "pydantic-1.7.2.tar.gz", hash = "sha256:c8200aecbd1fb914e1bd061d71a4d1d79ecb553165296af0c14989b89e90d09b"},
+ {file = "pydantic-1.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd"},
+ {file = "pydantic-1.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a4143c8d0c456a093387b96e0f5ee941a950992904d88bc816b4f0e72c9a0009"},
+ {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:d8df4b9090b595511906fa48deda47af04e7d092318bfb291f4d45dfb6bb2127"},
+ {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:514b473d264671a5c672dfb28bdfe1bf1afd390f6b206aa2ec9fed7fc592c48e"},
+ {file = "pydantic-1.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:dba5c1f0a3aeea5083e75db9660935da90216f8a81b6d68e67f54e135ed5eb23"},
+ {file = "pydantic-1.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59e45f3b694b05a69032a0d603c32d453a23f0de80844fb14d55ab0c6c78ff2f"},
+ {file = "pydantic-1.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5b24e8a572e4b4c18f614004dda8c9f2c07328cb5b6e314d6e1bbd536cb1a6c1"},
+ {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:b2b054d095b6431cdda2f852a6d2f0fdec77686b305c57961b4c5dd6d863bf3c"},
+ {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:025bf13ce27990acc059d0c5be46f416fc9b293f45363b3d19855165fee1874f"},
+ {file = "pydantic-1.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6e3874aa7e8babd37b40c4504e3a94cc2023696ced5a0500949f3347664ff8e2"},
+ {file = "pydantic-1.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e682f6442ebe4e50cb5e1cfde7dda6766fb586631c3e5569f6aa1951fd1a76ef"},
+ {file = "pydantic-1.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:185e18134bec5ef43351149fe34fda4758e53d05bb8ea4d5928f0720997b79ef"},
+ {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:f5b06f5099e163295b8ff5b1b71132ecf5866cc6e7f586d78d7d3fd6e8084608"},
+ {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:24ca47365be2a5a3cc3f4a26dcc755bcdc9f0036f55dcedbd55663662ba145ec"},
+ {file = "pydantic-1.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:d1fe3f0df8ac0f3a9792666c69a7cd70530f329036426d06b4f899c025aca74e"},
+ {file = "pydantic-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f6864844b039805add62ebe8a8c676286340ba0c6d043ae5dea24114b82a319e"},
+ {file = "pydantic-1.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ecb54491f98544c12c66ff3d15e701612fc388161fd455242447083350904730"},
+ {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:ffd180ebd5dd2a9ac0da4e8b995c9c99e7c74c31f985ba090ee01d681b1c4b95"},
+ {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8d72e814c7821125b16f1553124d12faba88e85405b0864328899aceaad7282b"},
+ {file = "pydantic-1.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:475f2fa134cf272d6631072554f845d0630907fce053926ff634cc6bc45bf1af"},
+ {file = "pydantic-1.7.3-py3-none-any.whl", hash = "sha256:38be427ea01a78206bcaf9a56f835784afcba9e5b88fbdce33bbbfbcd7841229"},
+ {file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"},
]
pydocstyle = [
{file = "pydocstyle-5.1.1-py3-none-any.whl", hash = "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678"},
{file = "pydocstyle-5.1.1.tar.gz", hash = "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325"},
]
pygments = [
- {file = "Pygments-2.7.2-py3-none-any.whl", hash = "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"},
- {file = "Pygments-2.7.2.tar.gz", hash = "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0"},
+ {file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"},
+ {file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"},
]
pyjwt = [
{file = "PyJWT-1.7.1-py2.py3-none-any.whl", hash = "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e"},
diff --git a/tests/conftest.py b/tests/conftest.py
index 84ef2f8..b324ef8 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -3,7 +3,6 @@
import sys
# Third Party
-# Local Imports
import dotenv
import pytest
diff --git a/tests/lang/test_ast.py b/tests/lang/test_ast.py
index 833f53b..ff05fad 100644
--- a/tests/lang/test_ast.py
+++ b/tests/lang/test_ast.py
@@ -1,4 +1,3 @@
-# Local Imports
# Standard Library
import json
import pprint
@@ -16,7 +15,7 @@
@pytest.mark.skip(msg="Cannot handle escaped quote")
-def test_ast_parse():
+def test_ast_parse_escaped_quote():
# Bad quote
# assertion = AssertionStr(entire='complex(SCOMP:"Test named\" complex", p(HGNC:"207"!"AKT1 Test), p(HGNC:207!"Test"), loc(nucleus)) increases p(HGNC:EGF) increases p(hgnc : "here I am" ! X)')
@@ -825,3 +824,32 @@ def test_ast_canonicalization_2():
print("Canonicalized", ast.to_string())
assert ast.to_string() == expected
+
+
+def test_ast_subcomponents_simple():
+
+ test_input = "path(DO:0080600!COVID-19)"
+ assertion = AssertionStr(entire=test_input)
+
+ ast = bel.lang.ast.BELAst(assertion=assertion)
+
+ subcomponents = ast.subcomponents()
+
+ print("Subcomponents", subcomponents)
+
+ assert subcomponents == ["path(DO:0080600!COVID-19)", "DO:0080600!COVID-19", "DO:COVID-19"]
+
+
+def test_ast_subcomponents_complex():
+
+ test_input = """rxn(reactants(complex(p(HGNC:5241!HSPA8), p(HGNC:6501!LAMP2), p(reactome:R-HSA-9622845.1!"HSP90AA1, HSP90AB1"), loc(GO:0005829!cytosol))), products(complex(p(HGNC:6501!LAMP2), p(reactome:R-HSA-9622845.1!"HSP90AA1, HSP90AB1"), loc(GO:0005765!"lysosomal membrane")), p(HGNC:5241!HSPA8, loc(GO:0005829!cytosol))))"""
+
+ assertion = AssertionStr(entire=test_input)
+
+ ast = bel.lang.ast.BELAst(assertion=assertion)
+
+ subcomponents = ast.subcomponents()
+
+ pprint.pprint(subcomponents)
+
+ assert subcomponents == ["path(DO:0080600!COVID-19)", "DO:0080600!COVID-19", "DO:COVID-19"]
diff --git a/tests/lang/test_parse.py b/tests/lang/test_parse.py
index a13131a..30dde6e 100644
--- a/tests/lang/test_parse.py
+++ b/tests/lang/test_parse.py
@@ -1,4 +1,3 @@
-# Local Imports
# Third Party
import pytest
diff --git a/tests/nanopub/test_nanopub_validation.py b/tests/nanopub/test_nanopub_validation.py
index 0b1ae7b..2612efd 100644
--- a/tests/nanopub/test_nanopub_validation.py
+++ b/tests/nanopub/test_nanopub_validation.py
@@ -5,7 +5,6 @@
import pytest
# Local
-# Local Imports
import bel.nanopub.validate
from bel.schemas.nanopubs import NanopubR
diff --git a/tests/nanopub/test_pubmed.py b/tests/nanopub/test_pubmed.py
index 2ca9244..c6b841d 100644
--- a/tests/nanopub/test_pubmed.py
+++ b/tests/nanopub/test_pubmed.py
@@ -3,7 +3,6 @@
import time
# Local
-# Local Imports
import bel.nanopub.pubmed
diff --git a/tests/terms/test_orthologs.py b/tests/terms/test_orthologs.py
index b7d80d9..68a7815 100644
--- a/tests/terms/test_orthologs.py
+++ b/tests/terms/test_orthologs.py
@@ -1,4 +1,3 @@
-# Local Imports
# Third Party
import pytest
diff --git a/tests/terms/test_terms.py b/tests/terms/test_terms.py
index a572e2a..c0454d5 100644
--- a/tests/terms/test_terms.py
+++ b/tests/terms/test_terms.py
@@ -1,4 +1,3 @@
-# Local Imports
# Third Party
import pytest
diff --git a/tests/test_schema.py b/tests/test_schema.py
index dde3b65..f976636 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1,4 +1,3 @@
-# Local Imports
# Local
from bel.schemas.bel import AssertionStr, BelEntity, NsVal