Skip to content

Commit c0f976d

Browse files
Deployment following 0.46.0 (#1053)
2 parents 118d7db + 5d61330 commit c0f976d

64 files changed

Lines changed: 980 additions & 539 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/testing.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ jobs:
5353
run: |
5454
echo "API_KEY_NAME=$(echo ${{ format('MP_API_KEY_{0}_{1}', matrix.os, matrix.python-version) }} | awk '{gsub(/-|\./, "_"); print}' | tr '[:lower:]' '[:upper:]')" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
5555
56+
- name: Lint with mypy
57+
shell: bash -l {0}
58+
run: python -m mypy mp_api/
59+
5660
- name: Test with pytest
5761
env:
5862
MP_API_KEY: ${{ secrets[env.API_KEY_NAME] }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,4 @@ _autosummary
122122
uv.lock
123123
JANAF_*_data.json
124124
.gemini
125+
.codex

dev/inspect_mcp.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Tool to run the MCP inspector:
44
# https://modelcontextprotocol.io/docs/tools/inspector
55

6-
server_path=$(python -c 'from importlib_resources import files ; print(str((files("mp_api.client") / ".."/ "..").resolve()))')
6+
server_path=$(python -c 'from importlib.resources import files ; print(str((files("mp_api.client") / ".."/ "..").resolve()))')
77

88
fastmcp dev \
99
--python 3.12 \

mp_api/__init__.py

Whitespace-only changes.

mp_api/client/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
"""Primary MAPI module."""
22
from __future__ import annotations
33

4+
import logging
45
import os
56
from importlib.metadata import PackageNotFoundError, version
67

7-
from .core import MPRestError
8-
from .mprester import MPRester
8+
from mp_api.client.core.exceptions import MPRestError
9+
from mp_api.client.mprester import MPRester
10+
11+
__all__ = ["MPRestError", "MPRester"]
912

1013
try:
1114
__version__ = version("mp_api")
1215
except PackageNotFoundError: # pragma: no cover
13-
__version__ = os.getenv("SETUPTOOLS_SCM_PRETEND_VERSION")
16+
__version__ = os.getenv("SETUPTOOLS_SCM_PRETEND_VERSION", "")
17+
18+
logging.getLogger(__name__).addHandler(logging.NullHandler())

mp_api/client/_server_utils.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""Define utilities needed by the MP web server."""
2+
from __future__ import annotations
3+
4+
try:
5+
import flask
6+
except ImportError:
7+
from mp_api.client.core.exceptions import MPRestError
8+
9+
raise MPRestError("`flask` must be installed to use server utilities.")
10+
11+
import requests
12+
13+
from mp_api.client import MPRester
14+
from mp_api.client.core.utils import validate_api_key
15+
16+
SESSION = requests.Session()
17+
18+
19+
def is_localhost() -> bool:
20+
"""Determine if current env is local or production.
21+
22+
Returns:
23+
bool: True if the environment is locally hosted.
24+
"""
25+
return (
26+
True
27+
if not flask.has_request_context()
28+
else flask.request.headers.get("Host", "").startswith(
29+
("localhost:", "127.0.0.1:", "0.0.0.0:")
30+
)
31+
)
32+
33+
34+
def get_consumer() -> dict[str, str]:
35+
"""Identify the consumer associated with the current request.
36+
37+
Returns:
38+
dict of str to str, the headers associated with the consumer
39+
"""
40+
if not flask.has_request_context():
41+
return {}
42+
43+
names = [
44+
"X-Consumer-Id", # kong internal uuid
45+
"X-Consumer-Custom-Id", # api key
46+
"X-Consumer-Username", # <provider>:<email>
47+
"X-Anonymous-Consumer", # is anonymous user?
48+
"X-Authenticated-Groups", # groups this user belongs to
49+
"X-Consumer-Groups", # same as X-Authenticated-Groups
50+
]
51+
headers = flask.request.headers
52+
return {name: headers[name] for name in names if headers.get(name) is not None}
53+
54+
55+
def is_logged_in_user(consumer: dict[str, str] | None = None) -> bool:
56+
"""Check if the client has the necessary headers for an authenticated user.
57+
58+
Args:
59+
consumer (dict of str to str, or None): Headers associated with the consumer
60+
61+
Returns:
62+
bool : True if the consumer is a logged-in user, False otherwise.
63+
"""
64+
c = consumer or get_consumer()
65+
return bool(not c.get("X-Anonymous-Consumer") and c.get("X-Consumer-Id"))
66+
67+
68+
def get_user_api_key(consumer: dict[str, str] | None = None) -> str | None:
69+
"""Get the api key that belongs to the current user.
70+
71+
If running on localhost, api key is obtained from
72+
the environment variable MP_API_KEY.
73+
74+
Args:
75+
consumer (dict of str to str, or None): Headers associated with the consumer
76+
77+
Returns:
78+
str, the API key, or None if no API key could be identified.
79+
"""
80+
c = consumer or get_consumer()
81+
82+
if is_localhost():
83+
return validate_api_key()
84+
elif is_logged_in_user(c):
85+
return c.get("X-Consumer-Custom-Id")
86+
return None
87+
88+
89+
def get_rester(**kwargs) -> MPRester:
90+
"""Create MPRester with headers set for localhost and production compatibility.
91+
92+
Args:
93+
**kwargs : kwargs to pass to MPRester
94+
95+
Returns:
96+
MPRester
97+
"""
98+
if is_localhost():
99+
dev_api_key = get_user_api_key()
100+
SESSION.headers["x-api-key"] = dev_api_key or ""
101+
return MPRester(api_key=dev_api_key, session=SESSION, **kwargs)
102+
103+
return MPRester(headers=get_consumer(), session=SESSION, **kwargs)

mp_api/client/core/_oxygen_evolution.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
from __future__ import annotations
44

55
import json
6+
import warnings
67
from collections.abc import Sequence
78
from io import StringIO
89
from pathlib import Path
9-
from warnings import warn
1010

1111
import numpy as np
1212
import pandas as pd
@@ -16,6 +16,8 @@
1616
from scipy.constants import Avogadro, Boltzmann, atm, elementary_charge
1717
from scipy.interpolate import make_splrep, splev
1818

19+
from mp_api.client.core.exceptions import MPRestWarning
20+
1921
DEFAULT_CACHE_FILE = Path(__file__).absolute().parent / "JANAF_O2_data.json"
2022
# O2 partial pressure at ambient conditions, in MPa
2123
O2_PARTIAL_PRESSURE = 0.21 * atm * 1e-6
@@ -120,10 +122,11 @@ def mu_to_temp_spline(
120122
(mu_arr < min(NIST_JANAF_O2_MU_T["mu-mu_0K"]))
121123
| (mu_arr > max(NIST_JANAF_O2_MU_T["mu-mu_0K"]))
122124
):
123-
warn(
125+
warnings.warn(
124126
"Some of the input chemical potential values are "
125127
"outside the fitting range - extrapolation will be inaccurate.",
126128
stacklevel=2,
129+
category=MPRestWarning,
127130
)
128131
return splev(mu_arr, self.mu_to_temp_spline_params())
129132

0 commit comments

Comments
 (0)