mpcontribs-client 5.10.5rc0__tar.gz → 5.10.5rc2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {mpcontribs_client-5.10.5rc0/mpcontribs_client.egg-info → mpcontribs_client-5.10.5rc2}/PKG-INFO +1 -1
  2. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/mpcontribs/client/__init__.py +170 -116
  3. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2/mpcontribs_client.egg-info}/PKG-INFO +1 -1
  4. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/mpcontribs_client.egg-info/SOURCES.txt +2 -1
  5. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/requirements/deployment.txt +41 -49
  6. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/requirements/ubuntu-latest_py3.11.txt +2 -2
  7. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/requirements/ubuntu-latest_py3.11_extras.txt +3 -3
  8. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/requirements/ubuntu-latest_py3.12.txt +2 -2
  9. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/requirements/ubuntu-latest_py3.12_extras.txt +3 -3
  10. mpcontribs_client-5.10.5rc2/tests/test_data_structs.py +35 -0
  11. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/LICENSE +0 -0
  12. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/README.md +0 -0
  13. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/mpcontribs_client.egg-info/dependency_links.txt +0 -0
  14. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/mpcontribs_client.egg-info/requires.txt +0 -0
  15. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/mpcontribs_client.egg-info/top_level.txt +0 -0
  16. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/pyproject.toml +0 -0
  17. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/setup.cfg +0 -0
  18. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/tests/conftest.py +0 -0
  19. {mpcontribs_client-5.10.5rc0 → mpcontribs_client-5.10.5rc2}/tests/test_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpcontribs-client
3
- Version: 5.10.5rc0
3
+ Version: 5.10.5rc2
4
4
  Summary: Client library for MPContribs API
5
5
  Author-email: Patrick Huck <phuck@lbl.gov>, The Materials Project <feedback@materialsproject.org>
6
6
  License-Expression: BSD-3-Clause-LBNL
@@ -1,62 +1,62 @@
1
- import io
1
+ import functools
2
+ import gzip
2
3
  import importlib.metadata
3
- import sys
4
+ import io
5
+ import itertools
6
+ import logging
4
7
  import os
5
- import ujson
8
+ import sys
6
9
  import time
7
- import gzip
8
10
  import warnings
9
- import pandas as pd
10
- import numpy as np
11
- import plotly.io as pio
12
- import itertools
13
- import functools
14
- import requests
15
- import logging
16
-
11
+ from base64 import b64decode, b64encode, urlsafe_b64encode
12
+ from collections import defaultdict
13
+ from concurrent.futures import as_completed
14
+ from copy import deepcopy
15
+ from hashlib import md5
17
16
  from inspect import getfullargspec
18
17
  from math import isclose
19
- from requests.exceptions import RequestException
20
- from bson.objectid import ObjectId
21
- from typing import Type
22
- from tqdm.auto import tqdm
23
- from hashlib import md5
24
18
  from pathlib import Path
25
- from copy import deepcopy
26
- from filetype import guess
27
- from flatten_dict import flatten, unflatten
28
- from base64 import b64encode, b64decode, urlsafe_b64encode
19
+ from tempfile import gettempdir
20
+ from typing import Type
29
21
  from urllib.parse import urlparse
30
- from pyisemail import is_email
31
- from collections import defaultdict
32
- from pyisemail.diagnosis import BaseDiagnosis
33
- from swagger_spec_validator.common import SwaggerValidationError
34
- from jsonschema.exceptions import ValidationError
35
- from bravado_core.formatter import SwaggerFormat
22
+
23
+ import numpy as np
24
+ import pandas as pd
25
+ import plotly.io as pio
26
+ import requests
27
+ import ujson
28
+ from boltons.iterutils import remap
36
29
  from bravado.client import SwaggerClient
30
+ from bravado.config import bravado_config_from_config_dict
31
+ from bravado.exception import HTTPNotFound
37
32
  from bravado.requests_client import RequestsClient
38
33
  from bravado.swagger_model import Loader
39
- from bravado.config import bravado_config_from_config_dict
40
- from bravado_core.spec import Spec, build_api_serving_url, _identity
34
+ from bravado_core.formatter import SwaggerFormat
41
35
  from bravado_core.model import model_discovery
42
36
  from bravado_core.resource import build_resources
43
- from bravado.exception import HTTPNotFound
37
+ from bravado_core.spec import Spec, _identity, build_api_serving_url
44
38
  from bravado_core.validate import validate_object
45
- from json2html import Json2Html
46
- from IPython.display import display, HTML, Image, FileLink
47
- from boltons.iterutils import remap
48
- from pymatgen.core import Structure as PmgStructure
49
- from concurrent.futures import as_completed
50
- from requests_futures.sessions import FuturesSession
51
- from urllib3.util.retry import Retry
39
+ from bson.objectid import ObjectId
40
+ from cachetools import LRUCache, cached
41
+ from cachetools.keys import hashkey
42
+ from filetype import guess
52
43
  from filetype.types.archive import Gz
53
- from filetype.types.image import Jpeg, Png, Gif, Tiff
44
+ from filetype.types.image import Gif, Jpeg, Png, Tiff
45
+ from flatten_dict import flatten, unflatten
46
+ from IPython.display import HTML, FileLink, Image, display
47
+ from json2html import Json2Html
48
+ from jsonschema.exceptions import ValidationError
54
49
  from pint import UnitRegistry
55
50
  from pint.errors import DimensionalityError
56
- from tempfile import gettempdir
57
51
  from plotly.express._chart_types import line as line_chart
58
- from cachetools import cached, LRUCache
59
- from cachetools.keys import hashkey
52
+ from pyisemail import is_email
53
+ from pyisemail.diagnosis import BaseDiagnosis
54
+ from pymatgen.core import Structure as PmgStructure
55
+ from requests.exceptions import RequestException
56
+ from requests_futures.sessions import FuturesSession
57
+ from swagger_spec_validator.common import SwaggerValidationError
58
+ from tqdm.auto import tqdm
59
+ from urllib3.util.retry import Retry
60
60
 
61
61
  try:
62
62
  __version__ = importlib.metadata.version("mpcontribs-client")
@@ -398,8 +398,16 @@ class Table(pd.DataFrame):
398
398
  """
399
399
  df = pd.DataFrame.from_records(
400
400
  dct["data"], columns=dct["columns"], index=dct["index"]
401
- ).apply(pd.to_numeric, errors="ignore")
402
- df.index = pd.to_numeric(df.index, errors="ignore")
401
+ )
402
+ for col in df.columns:
403
+ try:
404
+ df[col] = df[col].apply(pd.to_numeric)
405
+ except Exception as exc:
406
+ continue
407
+ try:
408
+ df.index = pd.to_numeric(df.index)
409
+ except Exception:
410
+ pass
403
411
  labels = dct["attrs"].get("labels", {})
404
412
 
405
413
  if "index" in labels:
@@ -1128,10 +1136,10 @@ class Client(SwaggerClient):
1128
1136
  Returns:
1129
1137
  List of projects
1130
1138
  """
1131
- query = query or {}
1139
+ q = deepcopy(query) or {}
1132
1140
 
1133
- if self.project or "name" in query:
1134
- return [self.get_project(name=query.get("name"), fields=fields)]
1141
+ if self.project or "name" in q:
1142
+ return [self.get_project(name=q.get("name"), fields=fields)]
1135
1143
 
1136
1144
  if term:
1137
1145
 
@@ -1148,41 +1156,78 @@ class Client(SwaggerClient):
1148
1156
  responses = _run_futures(
1149
1157
  [search_future(term)], timeout=timeout, disable=True
1150
1158
  )
1151
- query["name__in"] = responses["search"].get("result", [])
1159
+ q["name__in"] = responses["search"].get("result", [])
1152
1160
 
1153
1161
  if fields:
1154
- query["_fields"] = fields
1162
+ q["_fields"] = fields
1155
1163
  if sort:
1156
- query["_sort"] = sort
1164
+ q["_sort"] = sort
1157
1165
 
1158
- ret = self.projects.queryProjects(**query).result() # first page
1166
+ ret = self.projects.queryProjects(**q).result() # first page
1167
+ """
1168
+ 'ret' type:
1169
+ {
1170
+ "data": [
1171
+ ...
1172
+ ],
1173
+ "has_more": <bool>,
1174
+ "total_count": <int>,
1175
+ "total_pages": <int>
1176
+ }
1177
+ """
1159
1178
  total_count, total_pages = ret["total_count"], ret["total_pages"]
1160
1179
 
1161
1180
  if total_pages < 2:
1162
1181
  return ret["data"]
1163
1182
 
1164
- query.update(
1183
+ q.update(
1165
1184
  {
1166
- field: ",".join(query[field])
1185
+ field: ",".join(q[field])
1167
1186
  for field in ["name__in", "_fields"]
1168
- if field in query
1187
+ if field in q
1169
1188
  }
1170
1189
  )
1171
1190
 
1172
1191
  queries = []
1173
1192
 
1174
1193
  for page in range(2, total_pages + 1):
1175
- queries.append(deepcopy(query))
1194
+ queries.append(deepcopy(q))
1176
1195
  queries[-1]["page"] = page
1177
1196
 
1178
1197
  futures = [
1179
- self._get_future(i, q, rel_url="projects") for i, q in enumerate(queries)
1198
+ self._get_future(i, _q, rel_url="projects") for i, _q in enumerate(queries)
1180
1199
  ]
1181
1200
  responses = _run_futures(futures, total=total_count, timeout=timeout)
1201
+ """
1202
+ 'responses' type:
1203
+ {
1204
+ "0": {
1205
+ "result": {
1206
+ "data": [
1207
+ ...
1208
+ ],
1209
+ "has_more": <bool>,
1210
+ "total_count": <int>,
1211
+ "total_pages": <int>
1212
+ },
1213
+ "count": <int>
1214
+ },
1215
+ "1": ...
1216
+ }
1217
+ """
1182
1218
 
1183
- ret["data"].extend([resp["result"]["data"] for resp in responses.values()])
1184
-
1185
- return ret["data"]
1219
+ return list(
1220
+ itertools.chain.from_iterable(
1221
+ [
1222
+ ret["data"],
1223
+ itertools.chain.from_iterable(
1224
+ # did not hit early return, guaranteed
1225
+ # to have additional pages w/ data
1226
+ map(lambda x: x["result"]["data"], iter(responses.values()))
1227
+ ),
1228
+ ]
1229
+ )
1230
+ )
1186
1231
 
1187
1232
  def create_project(
1188
1233
  self, name: str, title: str, authors: str, description: str, url: str
@@ -1584,25 +1629,25 @@ class Client(SwaggerClient):
1584
1629
  )
1585
1630
 
1586
1631
  tic = time.perf_counter()
1587
- query = query or {}
1632
+ q = deepcopy(query) or {}
1588
1633
 
1589
1634
  if self.project:
1590
- query["project"] = self.project
1635
+ q["project"] = self.project
1591
1636
 
1592
- name = query["project"]
1593
- cids = list(self.get_all_ids(query).get(name, {}).get("ids", set()))
1637
+ name = q["project"]
1638
+ cids = list(self.get_all_ids(q).get(name, {}).get("ids", set()))
1594
1639
 
1595
1640
  if not cids:
1596
1641
  logger.info(f"There aren't any contributions to delete for {name}")
1597
1642
  return
1598
1643
 
1599
1644
  total = len(cids)
1600
- query = {"id__in": cids}
1601
- _, total_pages = self.get_totals(query=query)
1602
- queries = self._split_query(query, op="delete", pages=total_pages)
1603
- futures = [self._get_future(i, q, op="delete") for i, q in enumerate(queries)]
1645
+ id_query = {"id__in": cids}
1646
+ _, total_pages = self.get_totals(query=id_query)
1647
+ queries = self._split_query(id_query, op="delete", pages=total_pages)
1648
+ futures = [self._get_future(i, _q, op="delete") for i, _q in enumerate(queries)]
1604
1649
  _run_futures(futures, total=total, timeout=timeout)
1605
- left, _ = self.get_totals(query=query)
1650
+ left, _ = self.get_totals(query=id_query)
1606
1651
  deleted = total - left
1607
1652
  self.init_columns(name=name)
1608
1653
  self._reinit()
@@ -1638,16 +1683,16 @@ class Client(SwaggerClient):
1638
1683
  if op not in ops:
1639
1684
  raise MPContribsClientError(f"`op` has to be one of {ops}")
1640
1685
 
1641
- query = query or {}
1642
- if self.project and "project" not in query:
1643
- query["project"] = self.project
1686
+ q = deepcopy(query) or {}
1687
+ if self.project and "project" not in q:
1688
+ q["project"] = self.project
1644
1689
 
1645
1690
  skip_keys = {"per_page", "_fields", "format", "_sort"}
1646
- query = {k: v for k, v in query.items() if k not in skip_keys}
1647
- query["_fields"] = [] # only need totals -> explicitly request no fields
1648
- queries = self._split_query(query, resource=resource, op=op) # don't paginate
1691
+ q = {k: v for k, v in q.items() if k not in skip_keys}
1692
+ q["_fields"] = [] # only need totals -> explicitly request no fields
1693
+ queries = self._split_query(q, resource=resource, op=op) # don't paginate
1649
1694
  futures = [
1650
- self._get_future(i, q, rel_url=resource) for i, q in enumerate(queries)
1695
+ self._get_future(i, _q, rel_url=resource) for i, _q in enumerate(queries)
1651
1696
  ]
1652
1697
  responses = _run_futures(futures, timeout=timeout, desc="Totals")
1653
1698
 
@@ -1759,11 +1804,11 @@ class Client(SwaggerClient):
1759
1804
  )
1760
1805
 
1761
1806
  ret = {}
1762
- query = query or {}
1763
- if self.project and "project" not in query:
1764
- query["project"] = self.project
1807
+ q = deepcopy(query) or {}
1808
+ if self.project and "project" not in q:
1809
+ q["project"] = self.project
1765
1810
 
1766
- [query.pop(k, None) for k in ["page", "per_page", "_fields"]]
1811
+ [q.pop(k, None) for k in ["page", "per_page", "_fields"]]
1767
1812
  id_fields = {"project", "id", "identifier"}
1768
1813
 
1769
1814
  if data_id_fields:
@@ -1771,10 +1816,10 @@ class Client(SwaggerClient):
1771
1816
  f"data.{data_id_field}" for data_id_field in data_id_fields.values()
1772
1817
  )
1773
1818
 
1774
- query["_fields"] = list(id_fields | components)
1775
- _, total_pages = self.get_totals(query=query, timeout=timeout)
1776
- queries = self._split_query(query, op=op, pages=total_pages)
1777
- futures = [self._get_future(i, q) for i, q in enumerate(queries)]
1819
+ q["_fields"] = list(id_fields | components)
1820
+ _, total_pages = self.get_totals(query=q, timeout=timeout)
1821
+ queries = self._split_query(q, op=op, pages=total_pages)
1822
+ futures = [self._get_future(i, _q) for i, _q in enumerate(queries)]
1778
1823
  responses = _run_futures(futures, timeout=timeout, desc="Identifiers")
1779
1824
 
1780
1825
  for resp in responses.values():
@@ -1870,15 +1915,15 @@ class Client(SwaggerClient):
1870
1915
  Returns:
1871
1916
  List of contributions
1872
1917
  """
1873
- query = query or {}
1918
+ q: dict = deepcopy(query) or {}
1874
1919
 
1875
- if self.project and "project" not in query:
1876
- query["project"] = self.project
1920
+ if self.project and "project" not in q:
1921
+ q["project"] = self.project
1877
1922
 
1878
1923
  if paginate:
1879
1924
  cids = [
1880
1925
  idx
1881
- for v in self.get_all_ids(query).values()
1926
+ for v in self.get_all_ids(q).values()
1882
1927
  for idx in (v.get("ids") or [])
1883
1928
  ]
1884
1929
 
@@ -1889,17 +1934,26 @@ class Client(SwaggerClient):
1889
1934
  cids_query = {"id__in": cids, "_fields": fields, "_sort": sort}
1890
1935
  _, total_pages = self.get_totals(query=cids_query)
1891
1936
  queries = self._split_query(cids_query, pages=total_pages)
1892
- futures = [self._get_future(i, q) for i, q in enumerate(queries)]
1893
- responses = _run_futures(futures, total=total, timeout=timeout)
1894
- ret = {"total_count": 0, "data": []}
1895
-
1896
- for resp in responses.values():
1897
- result = resp["result"]
1898
- ret["data"].extend(result["data"])
1899
- ret["total_count"] += result["total_count"]
1937
+ futures = [self._get_future(i, _q) for i, _q in enumerate(queries)]
1938
+ responses = [
1939
+ resp
1940
+ for resp in _run_futures(futures, total=total, timeout=timeout).values()
1941
+ if resp.get("result")
1942
+ ]
1943
+ ret = {
1944
+ "total_count": sum(
1945
+ resp["result"].get("total_count", 0) for resp in responses
1946
+ ),
1947
+ "data": list(
1948
+ itertools.chain.from_iterable(
1949
+ [resp["result"].get("data", []) for resp in responses]
1950
+ )
1951
+ ),
1952
+ }
1953
+
1900
1954
  else:
1901
1955
  ret = self.contributions.queryContributions(
1902
- _fields=fields, _sort=sort, **query
1956
+ _fields=fields, _sort=sort, **q
1903
1957
  ).result()
1904
1958
 
1905
1959
  return ret
@@ -1925,22 +1979,22 @@ class Client(SwaggerClient):
1925
1979
  if "data" in data:
1926
1980
  self._is_serializable_dict(data["data"])
1927
1981
 
1928
- query = query or {}
1982
+ q = deepcopy(query) or {}
1929
1983
 
1930
1984
  if self.project:
1931
- if "project" in query and self.project != query["project"]:
1985
+ if "project" in q and self.project != q["project"]:
1932
1986
  raise MPContribsClientError(
1933
1987
  f"client initialized with different project {self.project}!"
1934
1988
  )
1935
- query["project"] = self.project
1989
+ q["project"] = self.project
1936
1990
  else:
1937
- if not query or "project" not in query:
1991
+ if not q or "project" not in q:
1938
1992
  raise MPContribsClientError(
1939
1993
  "initialize client with project, or include project in query!"
1940
1994
  )
1941
1995
 
1942
- name = query["project"]
1943
- cids = list(self.get_all_ids(query).get(name, {}).get("ids", set()))
1996
+ name = q["project"]
1997
+ cids = list(self.get_all_ids(q).get(name, {}).get("ids", set()))
1944
1998
 
1945
1999
  if not cids:
1946
2000
  raise MPContribsClientError(
@@ -1956,8 +2010,8 @@ class Client(SwaggerClient):
1956
2010
  _, total_pages = self.get_totals(query=cids_query)
1957
2011
  queries = self._split_query(cids_query, op="update", pages=total_pages)
1958
2012
  futures = [
1959
- self._get_future(i, q, op="update", data=data)
1960
- for i, q in enumerate(queries)
2013
+ self._get_future(i, _q, op="update", data=data)
2014
+ for i, _q in enumerate(queries)
1961
2015
  ]
1962
2016
  responses = _run_futures(futures, total=total, timeout=timeout)
1963
2017
  updated = sum(resp["count"] for _, resp in responses.items())
@@ -2019,23 +2073,23 @@ class Client(SwaggerClient):
2019
2073
  "initialize client with project, or include project in query!"
2020
2074
  )
2021
2075
 
2022
- query = query or {}
2076
+ q = deepcopy(query) or {}
2023
2077
 
2024
2078
  if self.project:
2025
- query["project"] = self.project
2079
+ q["project"] = self.project
2026
2080
 
2027
2081
  try:
2028
2082
  resp = self.projects.getProjectByName(
2029
- pk=query["project"], _fields=["is_public", "is_approved"]
2083
+ pk=q["project"], _fields=["is_public", "is_approved"]
2030
2084
  ).result()
2031
2085
  except HTTPNotFound:
2032
2086
  raise MPContribsClientError(
2033
- f"project `{query['project']}` not found or access denied!"
2087
+ f"project `{q['project']}` not found or access denied!"
2034
2088
  )
2035
2089
 
2036
2090
  if not recursive and resp["is_public"] == is_public:
2037
2091
  return {
2038
- "warning": f"`is_public` already set to {is_public} for `{query['project']}`."
2092
+ "warning": f"`is_public` already set to {is_public} for `{q['project']}`."
2039
2093
  }
2040
2094
 
2041
2095
  ret = {}
@@ -2043,19 +2097,19 @@ class Client(SwaggerClient):
2043
2097
  if resp["is_public"] != is_public:
2044
2098
  if is_public and not resp["is_approved"]:
2045
2099
  raise MPContribsClientError(
2046
- f"project `{query['project']}` is not approved yet!"
2100
+ f"project `{q['project']}` is not approved yet!"
2047
2101
  )
2048
2102
 
2049
2103
  resp = self.projects.updateProjectByName(
2050
- pk=query["project"], project={"is_public": is_public}
2104
+ pk=q["project"], project={"is_public": is_public}
2051
2105
  ).result()
2052
2106
  ret["published"] = resp["count"] == 1
2053
2107
 
2054
2108
  if recursive:
2055
- query = query or {}
2056
- query["is_public"] = not is_public
2109
+ q = deepcopy(query) or {}
2110
+ q["is_public"] = not is_public
2057
2111
  ret["contributions"] = self.update_contributions(
2058
- {"is_public": is_public}, query=query, timeout=timeout
2112
+ {"is_public": is_public}, query=q, timeout=timeout
2059
2113
  )
2060
2114
 
2061
2115
  return ret
@@ -2450,7 +2504,7 @@ class Client(SwaggerClient):
2450
2504
  Number of new downloads written to disk.
2451
2505
  """
2452
2506
  start = time.perf_counter()
2453
- query = query or {}
2507
+ q = deepcopy(query) or {}
2454
2508
  include = include or []
2455
2509
  outdir = Path(outdir) or Path(".")
2456
2510
  outdir.mkdir(parents=True, exist_ok=True)
@@ -2458,8 +2512,8 @@ class Client(SwaggerClient):
2458
2512
  if include and not components:
2459
2513
  raise MPContribsClientError(f"`include` must be subset of {COMPONENTS}!")
2460
2514
 
2461
- all_ids = self.get_all_ids(query, include=list(components), timeout=timeout)
2462
- fmt = query.get("format", "json")
2515
+ all_ids = self.get_all_ids(q, include=list(components), timeout=timeout)
2516
+ fmt = q.get("format", "json")
2463
2517
  contributions, components_loaded = [], defaultdict(dict)
2464
2518
 
2465
2519
  for name, values in all_ids.items():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpcontribs-client
3
- Version: 5.10.5rc0
3
+ Version: 5.10.5rc2
4
4
  Summary: Client library for MPContribs API
5
5
  Author-email: Patrick Huck <phuck@lbl.gov>, The Materials Project <feedback@materialsproject.org>
6
6
  License-Expression: BSD-3-Clause-LBNL
@@ -13,4 +13,5 @@ requirements/ubuntu-latest_py3.11_extras.txt
13
13
  requirements/ubuntu-latest_py3.12.txt
14
14
  requirements/ubuntu-latest_py3.12_extras.txt
15
15
  tests/conftest.py
16
- tests/test_client.py
16
+ tests/test_client.py
17
+ tests/test_data_structs.py
@@ -2,7 +2,7 @@
2
2
  # This file is autogenerated by pip-compile with Python 3.11
3
3
  # by the following command:
4
4
  #
5
- # pip-compile --output-file=MPContribs/mpcontribs-client/requirements/deployment.txt MPContribs/mpcontribs-client/setup.py python/requirements.txt
5
+ # pip-compile --output-file=MPContribs/mpcontribs-client/requirements/deployment.txt MPContribs/mpcontribs-client/pyproject.toml python/requirements.txt
6
6
  #
7
7
  arrow==1.4.0
8
8
  # via isoduration
@@ -15,14 +15,14 @@ attrs==25.4.0
15
15
  bibtexparser==1.4.3
16
16
  # via pymatgen
17
17
  boltons==25.0.0
18
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
18
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
19
19
  bravado==12.0.1
20
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
20
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
21
21
  bravado-core==6.1.1
22
22
  # via bravado
23
- cachetools==6.2.2
24
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
25
- certifi==2025.11.12
23
+ cachetools==6.2.6
24
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
25
+ certifi==2026.1.4
26
26
  # via requests
27
27
  charset-normalizer==3.4.4
28
28
  # via requests
@@ -39,14 +39,14 @@ dnspython==2.8.0
39
39
  executing==2.2.1
40
40
  # via stack-data
41
41
  filetype==1.2.0
42
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
42
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
43
43
  flatten-dict==0.4.2
44
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
44
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
45
45
  flexcache==0.3
46
46
  # via pint
47
47
  flexparser==0.4
48
48
  # via pint
49
- fonttools==4.61.0
49
+ fonttools==4.61.1
50
50
  # via matplotlib
51
51
  fqdn==1.5.1
52
52
  # via jsonschema
@@ -56,18 +56,18 @@ idna==3.11
56
56
  # requests
57
57
  importlib-resources==6.5.2
58
58
  # via swagger-spec-validator
59
- ipython==9.8.0
60
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
59
+ ipython==9.9.0
60
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
61
61
  ipython-pygments-lexers==1.1.1
62
62
  # via ipython
63
63
  isoduration==20.11.0
64
64
  # via jsonschema
65
65
  jedi==0.19.2
66
66
  # via ipython
67
- joblib==1.5.2
67
+ joblib==1.5.3
68
68
  # via pymatgen
69
69
  json2html==1.3.0
70
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
70
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
71
71
  jsonpointer==3.0.0
72
72
  # via jsonschema
73
73
  jsonref==1.1.0
@@ -76,13 +76,13 @@ jsonref==1.1.0
76
76
  # swagger-spec-validator
77
77
  jsonschema-specifications==2025.9.1
78
78
  # via bravado-core
79
- jsonschema[format-nongpl]==4.25.1
79
+ jsonschema[format-nongpl]==4.26.0
80
80
  # via jsonschema
81
81
  kiwisolver==1.4.9
82
82
  # via matplotlib
83
83
  lark==1.3.1
84
84
  # via rfc3987-syntax
85
- matplotlib==3.10.7
85
+ matplotlib==3.10.8
86
86
  # via
87
87
  # -r python/requirements.txt
88
88
  # pymatgen
@@ -98,7 +98,7 @@ msgpack==1.1.2
98
98
  # via
99
99
  # bravado
100
100
  # bravado-core
101
- narwhals==2.13.0
101
+ narwhals==2.15.0
102
102
  # via plotly
103
103
  networkx==3.6.1
104
104
  # via pymatgen
@@ -108,37 +108,37 @@ numpy==1.26.4
108
108
  # contourpy
109
109
  # matplotlib
110
110
  # monty
111
- # mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
111
+ # mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
112
112
  # pandas
113
113
  # pymatgen
114
114
  # scipy
115
115
  # spglib
116
116
  orjson==3.11.5
117
117
  # via pymatgen
118
- packaging==25.0
118
+ packaging==26.0
119
119
  # via
120
120
  # matplotlib
121
121
  # plotly
122
122
  palettable==3.3.3
123
123
  # via pymatgen
124
- pandas==2.3.3
124
+ pandas==3.0.0
125
125
  # via
126
126
  # -r python/requirements.txt
127
- # mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
127
+ # mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
128
128
  # pymatgen
129
129
  parso==0.8.5
130
130
  # via jedi
131
131
  pexpect==4.9.0
132
132
  # via ipython
133
- pillow==12.0.0
133
+ pillow==12.1.0
134
134
  # via matplotlib
135
135
  pint==0.25.2
136
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
136
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
137
137
  platformdirs==4.5.1
138
138
  # via pint
139
- plotly==6.5.0
139
+ plotly==6.5.2
140
140
  # via
141
- # mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
141
+ # mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
142
142
  # pymatgen
143
143
  prompt-toolkit==3.0.52
144
144
  # via ipython
@@ -151,12 +151,12 @@ pygments==2.19.2
151
151
  # ipython
152
152
  # ipython-pygments-lexers
153
153
  pyisemail==2.0.1
154
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
154
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
155
155
  pymatgen==2025.10.7
156
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
157
- pymongo==4.15.5
158
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
159
- pyparsing==3.2.5
156
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
157
+ pymongo==4.16.0
158
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
159
+ pyparsing==3.3.2
160
160
  # via
161
161
  # bibtexparser
162
162
  # matplotlib
@@ -168,9 +168,7 @@ python-dateutil==2.9.0.post0
168
168
  # matplotlib
169
169
  # pandas
170
170
  pytz==2025.2
171
- # via
172
- # bravado-core
173
- # pandas
171
+ # via bravado-core
174
172
  pyyaml==6.0.3
175
173
  # via
176
174
  # bravado
@@ -187,7 +185,7 @@ requests==2.32.5
187
185
  # pymatgen
188
186
  # requests-futures
189
187
  requests-futures==1.0.2
190
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
188
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
191
189
  rfc3339-validator==0.1.4
192
190
  # via jsonschema
193
191
  rfc3986-validator==0.1.1
@@ -198,18 +196,14 @@ rpds-py==0.30.0
198
196
  # via
199
197
  # jsonschema
200
198
  # referencing
201
- ruamel-yaml==0.18.16
199
+ ruamel-yaml==0.19.1
202
200
  # via
203
201
  # monty
204
202
  # pymatgen
205
- ruamel-yaml-clib==0.2.15
206
- # via ruamel-yaml
207
- scipy==1.16.3
203
+ scipy==1.17.0
208
204
  # via
209
205
  # -r python/requirements.txt
210
206
  # pymatgen
211
- semantic-version==2.10.0
212
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
213
207
  simplejson==3.20.2
214
208
  # via
215
209
  # bravado
@@ -221,21 +215,21 @@ six==1.17.0
221
215
  # flatten-dict
222
216
  # python-dateutil
223
217
  # rfc3339-validator
224
- spglib==2.6.0
218
+ spglib==2.7.0
225
219
  # via pymatgen
226
220
  stack-data==0.6.3
227
221
  # via ipython
228
222
  swagger-spec-validator==3.0.4
229
223
  # via
230
224
  # bravado-core
231
- # mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
225
+ # mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
232
226
  sympy==1.14.0
233
227
  # via pymatgen
234
228
  tabulate==0.9.0
235
229
  # via pymatgen
236
230
  tqdm==4.67.1
237
231
  # via
238
- # mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
232
+ # mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
239
233
  # pymatgen
240
234
  traitlets==5.14.3
241
235
  # via
@@ -251,19 +245,17 @@ typing-extensions==4.15.0
251
245
  # referencing
252
246
  # spglib
253
247
  # swagger-spec-validator
254
- tzdata==2025.2
255
- # via
256
- # arrow
257
- # pandas
248
+ tzdata==2025.3
249
+ # via arrow
258
250
  ujson==5.11.0
259
- # via mpcontribs-client (MPContribs/mpcontribs-client/setup.py)
251
+ # via mpcontribs-client (MPContribs/mpcontribs-client/pyproject.toml)
260
252
  uncertainties==3.2.3
261
253
  # via pymatgen
262
254
  uri-template==1.3.0
263
255
  # via jsonschema
264
- urllib3==2.6.1
256
+ urllib3==2.6.3
265
257
  # via requests
266
- wcwidth==0.2.14
258
+ wcwidth==0.5.0
267
259
  # via prompt-toolkit
268
260
  webcolors==25.10.0
269
261
  # via jsonschema
@@ -20,7 +20,7 @@ bravado==12.0.1
20
20
  # via mpcontribs-client (pyproject.toml)
21
21
  bravado-core==6.1.1
22
22
  # via bravado
23
- cachetools==6.2.4
23
+ cachetools==6.2.5
24
24
  # via mpcontribs-client (pyproject.toml)
25
25
  certifi==2026.1.4
26
26
  # via requests
@@ -249,7 +249,7 @@ uri-template==1.3.0
249
249
  # via jsonschema
250
250
  urllib3==2.6.3
251
251
  # via requests
252
- wcwidth==0.3.1
252
+ wcwidth==0.4.0
253
253
  # via prompt-toolkit
254
254
  webcolors==25.10.0
255
255
  # via jsonschema
@@ -24,7 +24,7 @@ bravado==12.0.1
24
24
  # mpcontribs-client (pyproject.toml)
25
25
  bravado-core==6.1.1
26
26
  # via bravado
27
- cachetools==6.2.4
27
+ cachetools==6.2.5
28
28
  # via
29
29
  # mpcontribs-client
30
30
  # mpcontribs-client (pyproject.toml)
@@ -34,7 +34,7 @@ charset-normalizer==3.4.4
34
34
  # via requests
35
35
  contourpy==1.3.3
36
36
  # via matplotlib
37
- coverage[toml]==7.13.1
37
+ coverage[toml]==7.13.2
38
38
  # via pytest-cov
39
39
  cycler==0.12.1
40
40
  # via matplotlib
@@ -335,7 +335,7 @@ uri-template==1.3.0
335
335
  # via jsonschema
336
336
  urllib3==2.6.3
337
337
  # via requests
338
- wcwidth==0.3.1
338
+ wcwidth==0.4.0
339
339
  # via prompt-toolkit
340
340
  webcolors==25.10.0
341
341
  # via jsonschema
@@ -20,7 +20,7 @@ bravado==12.0.1
20
20
  # via mpcontribs-client (pyproject.toml)
21
21
  bravado-core==6.1.1
22
22
  # via bravado
23
- cachetools==6.2.4
23
+ cachetools==6.2.5
24
24
  # via mpcontribs-client (pyproject.toml)
25
25
  certifi==2026.1.4
26
26
  # via requests
@@ -248,7 +248,7 @@ uri-template==1.3.0
248
248
  # via jsonschema
249
249
  urllib3==2.6.3
250
250
  # via requests
251
- wcwidth==0.3.1
251
+ wcwidth==0.4.0
252
252
  # via prompt-toolkit
253
253
  webcolors==25.10.0
254
254
  # via jsonschema
@@ -24,7 +24,7 @@ bravado==12.0.1
24
24
  # mpcontribs-client (pyproject.toml)
25
25
  bravado-core==6.1.1
26
26
  # via bravado
27
- cachetools==6.2.4
27
+ cachetools==6.2.5
28
28
  # via
29
29
  # mpcontribs-client
30
30
  # mpcontribs-client (pyproject.toml)
@@ -34,7 +34,7 @@ charset-normalizer==3.4.4
34
34
  # via requests
35
35
  contourpy==1.3.3
36
36
  # via matplotlib
37
- coverage[toml]==7.13.1
37
+ coverage[toml]==7.13.2
38
38
  # via pytest-cov
39
39
  cycler==0.12.1
40
40
  # via matplotlib
@@ -334,7 +334,7 @@ uri-template==1.3.0
334
334
  # via jsonschema
335
335
  urllib3==2.6.3
336
336
  # via requests
337
- wcwidth==0.3.1
337
+ wcwidth==0.4.0
338
338
  # via prompt-toolkit
339
339
  webcolors==25.10.0
340
340
  # via jsonschema
@@ -0,0 +1,35 @@
1
+ from mpcontribs.client import Table
2
+
3
+
4
+ def test_table():
5
+
6
+ data = {
7
+ "first_name": ["Todd", "Willie", "Mike"],
8
+ "family_name": ["Bonzalez", "Dustice", "Truk"],
9
+ "age": [31, 24, 28],
10
+ "batting_average": [0.777, 0.5, 0.81],
11
+ }
12
+ test_table = Table(data)
13
+
14
+ # Calling `as_dict` transforms the data in a `Table`
15
+ table_as_dict = Table(test_table.copy()).as_dict()
16
+ assert all(
17
+ table_as_dict.get(k) for k in ("attrs", "columns", "data", "index", "name")
18
+ )
19
+ table_info = test_table.info()
20
+ assert {y.strip() for y in table_info["columns"].split(",")} == set(
21
+ test_table.columns
22
+ )
23
+ assert table_info["nrows"] == len(test_table)
24
+
25
+ table_roundtrip = Table.from_dict(table_as_dict)
26
+
27
+ # `tolist()` needed to compare base python types
28
+ for t in (test_table, table_roundtrip):
29
+ assert all(
30
+ isinstance(v, str)
31
+ for col in ("family_name", "first_name")
32
+ for v in t[col].tolist()
33
+ )
34
+ assert all(isinstance(v, int) for v in t.age.tolist())
35
+ assert all(isinstance(v, float) for v in t.batting_average.tolist())