GLODAP 0.1__tar.gz → 0.2__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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
- Name: GLODAP
3
- Version: 0.1
4
- Summary: GLODAP downloader and importer in Python
2
+ Name: glodap
3
+ Version: 0.2
4
+ Summary: GLODAP downloader and importer in Python
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Programming Language :: Python :: 3.10
7
7
  Classifier: Programming Language :: Python :: 3.11
@@ -23,6 +23,12 @@ Dynamic: license-file
23
23
 
24
24
  # glodap
25
25
 
26
+ [![Tests](https://github.com/mvdh7/glodap/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mvdh7/glodap/actions)
27
+ [![pypi badge](https://img.shields.io/pypi/v/GLODAP.svg?style=popout)](https://pypi.org/project/GLODAP/)
28
+ [![Conda Version](https://img.shields.io/conda/vn/conda-forge/glodap.svg)](https://anaconda.org/conda-forge/glodap)
29
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
30
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
31
+
26
32
  Download [GLODAP](https://glodap.info) datasets and import them as pandas DataFrames.
27
33
 
28
34
  ## Install
@@ -70,3 +76,6 @@ The columns are the same as in the original GLODAP .mat files available from [gl
70
76
  The functions `download` and `read` can also be used for finer control, such as
71
77
  specifying a particular GLODAP version rather than using the latest one. See
72
78
  their function docstrings for more information.
79
+
80
+ The SHA256 checksum of each downloaded file is checked before the file is
81
+ written to disk.
@@ -7,6 +7,11 @@ GLODAP.egg-info/dependency_links.txt
7
7
  GLODAP.egg-info/requires.txt
8
8
  GLODAP.egg-info/top_level.txt
9
9
  glodap/__init__.py
10
+ glodap.egg-info/PKG-INFO
11
+ glodap.egg-info/SOURCES.txt
12
+ glodap.egg-info/dependency_links.txt
13
+ glodap.egg-info/requires.txt
14
+ glodap.egg-info/top_level.txt
10
15
  tests/test_import.py
11
16
  tests/test_regions.py
12
17
  tests/test_response.py
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
- Name: GLODAP
3
- Version: 0.1
4
- Summary: GLODAP downloader and importer in Python
2
+ Name: glodap
3
+ Version: 0.2
4
+ Summary: GLODAP downloader and importer in Python
5
5
  Classifier: Development Status :: 4 - Beta
6
6
  Classifier: Programming Language :: Python :: 3.10
7
7
  Classifier: Programming Language :: Python :: 3.11
@@ -23,6 +23,12 @@ Dynamic: license-file
23
23
 
24
24
  # glodap
25
25
 
26
+ [![Tests](https://github.com/mvdh7/glodap/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mvdh7/glodap/actions)
27
+ [![pypi badge](https://img.shields.io/pypi/v/GLODAP.svg?style=popout)](https://pypi.org/project/GLODAP/)
28
+ [![Conda Version](https://img.shields.io/conda/vn/conda-forge/glodap.svg)](https://anaconda.org/conda-forge/glodap)
29
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
30
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
31
+
26
32
  Download [GLODAP](https://glodap.info) datasets and import them as pandas DataFrames.
27
33
 
28
34
  ## Install
@@ -70,3 +76,6 @@ The columns are the same as in the original GLODAP .mat files available from [gl
70
76
  The functions `download` and `read` can also be used for finer control, such as
71
77
  specifying a particular GLODAP version rather than using the latest one. See
72
78
  their function docstrings for more information.
79
+
80
+ The SHA256 checksum of each downloaded file is checked before the file is
81
+ written to disk.
@@ -1,5 +1,11 @@
1
1
  # glodap
2
2
 
3
+ [![Tests](https://github.com/mvdh7/glodap/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mvdh7/glodap/actions)
4
+ [![pypi badge](https://img.shields.io/pypi/v/GLODAP.svg?style=popout)](https://pypi.org/project/GLODAP/)
5
+ [![Conda Version](https://img.shields.io/conda/vn/conda-forge/glodap.svg)](https://anaconda.org/conda-forge/glodap)
6
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
7
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
8
+
3
9
  Download [GLODAP](https://glodap.info) datasets and import them as pandas DataFrames.
4
10
 
5
11
  ## Install
@@ -47,3 +53,6 @@ The columns are the same as in the original GLODAP .mat files available from [gl
47
53
  The functions `download` and `read` can also be used for finer control, such as
48
54
  specifying a particular GLODAP version rather than using the latest one. See
49
55
  their function docstrings for more information.
56
+
57
+ The SHA256 checksum of each downloaded file is checked before the file is
58
+ written to disk.
@@ -40,9 +40,14 @@ The columns are the same as in the original GLODAP .mat files, except:
40
40
  The functions `download` and `read` can also be used for finer control, such as
41
41
  specifying a particular GLODAP version rather than using the latest one. See
42
42
  their function docstrings for more information.
43
+
44
+ The SHA256 checksum of each downloaded file is checked before the file is
45
+ written to disk.
43
46
  """
44
47
 
48
+ import hashlib
45
49
  import os
50
+ import tempfile
46
51
  from warnings import warn
47
52
 
48
53
  import pandas as pd
@@ -52,38 +57,76 @@ from scipy.io import loadmat
52
57
 
53
58
  # Package metadata
54
59
  __author__ = "Humphreys, Matthew P."
55
- __version__ = "0.1"
60
+ __version__ = "0.2"
56
61
 
57
62
  # GLODAP metadata
58
63
  version_latest = "v2.2023"
59
64
  versions = ["v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"]
60
- regions_full = {
65
+ regions = {
61
66
  "arctic": "Arctic_Ocean",
62
67
  "atlantic": "Atlantic_Ocean",
63
68
  "indian": "Indian_Ocean",
64
69
  "pacific": "Pacific_Ocean",
65
- "global": "Merged_Master_File",
66
70
  "world": "Merged_Master_File",
67
71
  }
68
- regions = regions_full.copy()
69
- for k, v in regions_full.items():
70
- regions[k[:3]] = v
72
+ regions_full = regions.copy()
73
+ for k, v in regions.items():
74
+ regions_full[k[:3]] = v
75
+ checksums = {
76
+ "v2.2023": {
77
+ "arc": "a998a8d99db84e9357b21a7e345bbfde62eae77870e651119b4c1929a2f1e2fd",
78
+ "atl": "a3ff7bc4f4ff3a1b0f540886a0eb7c9b3ec52aa4eae2dea57d47ae61f56da56f",
79
+ "ind": "8c9c21d4af9db506b09fdec79e2a311c2dbbf41b8fc4e6a7a855d1d50b3ccb51",
80
+ "pac": "9891160dba6211d2f13923171564a06bbea34cda799f3091148061c32fc6070c",
81
+ "wor": "002881fa71923d15c1bd5aca3b99bc220ad5d1c77a90f130ba1ff0d3910d8766",
82
+ },
83
+ "v2.2022": {
84
+ "arc": "b2cdeeebfb3ff75701ccc15edb22b5c8e1153623aefc6463f9699ae8780edf15",
85
+ "atl": "cffdb28aca195e189b16834a4f9711b90a995ea2b99d95bc6fe498049f975631",
86
+ "ind": "00381b0542ed4e8a39c33ee071fcb2388c1e3b3fb13f76c75fc342b26f4c8a13",
87
+ "pac": "4037f093d89ea422aa62524273ab40ca147934e34637496372e83f2e34fe9a66",
88
+ "wor": "3d7ba5bcf59b0c1f513b6fa8140df0618d984fa7b320899e7f188dd7c6cef565",
89
+ },
90
+ "v2.2021": {
91
+ "arc": "21e511e808b9cc9f5b8532bf3b1c6754bb4ad6c344adb4a498b71967a0b6d217",
92
+ "atl": "22731e474a33d10c3efd3cc6748aafca7e1488495acefe1bbbd26b4105973a21",
93
+ "ind": "d3c43da540e606091a981958b4ce674dbe1ef9777ffee4c90de29bcc74f010a2",
94
+ "pac": "925cf0424f4cd1caad30cfbe57bdc19740c85e715bee06bd298893915cfc5a01",
95
+ "wor": "8329acd8722a3c198455642f8fca29b05f460b149e178fef9e1041f2caa19453",
96
+ },
97
+ "v2.2020": {
98
+ "arc": "de52e1086b61f68d46cb0142e4bcffae18b578f41c2a00bda40a8960959a94b7",
99
+ "atl": "e927d87d873e089311ed5b747195aa150880f59559831bab27a54dcc0ff7ff69",
100
+ "ind": "1d8dfbefb820541d9b10f73d3785dae8653e67166b94666dbb433d3ddfa4e255",
101
+ "pac": "9a75bf66b38341117afaae947af465f8f7e4082a9f773355c1fbadf26484d544",
102
+ "wor": "51e1000101aa61712edb9c0074289bb83c105d17ee8cea575f8e18fa05bde953",
103
+ },
104
+ "v2.2019": {
105
+ "arc": "f972a835601a5b00ac5ffc5093414d5fd2a520bbc7b1c7ff0a7d343852302284",
106
+ "atl": "6ac0826bacbaa43a9813fe18bcb84b1786efb44538b7a2d24e8a0c993ff23193",
107
+ "ind": "aa6695b715cce7658f94911d38a344f1e835ff6bbed8f9dc1548b167983a2967",
108
+ "pac": "88df995e272dd63db55421b911be1f99bfec4cac6f97a73e017c9e275bf29db2",
109
+ "wor": "4637ce5eb560fe020e9fccf0370156bfe19498fc74c2e8b66dee190cc44cff63",
110
+ },
111
+ }
71
112
 
72
113
 
73
114
  def _get_paths(region, version, gpath):
74
- assert region in regions, "`region` not valid!"
115
+ assert region in regions_full, "`region` not valid!"
75
116
  if version is None:
76
117
  version = version_latest
118
+ version = version.lower()
77
119
  assert version in versions, "`version` not valid!"
78
120
  if gpath is None:
79
121
  gpath = os.path.join(os.path.expanduser("~"), ".glodap")
80
- fileregion = regions[region.lower()]
122
+ fileregion = regions_full[region.lower()]
81
123
  filename = f"{fileregion}_{version}.mat"
82
124
  return gpath, fileregion, filename, version
83
125
 
84
126
 
85
- def download(region="world", version=None, gpath=None, do_download=True):
86
- """Download a GLODAP data file and save it locally.
127
+ def download(region="world", version=None, gpath=None, chunk_size=8192):
128
+ """Download a GLODAP data file and save it locally, after checking that it
129
+ has the expected checksum (SHA256).
87
130
 
88
131
  Parameters
89
132
  ----------
@@ -95,7 +138,6 @@ def download(region="world", version=None, gpath=None, do_download=True):
95
138
  "indian" "ind" Indian Ocean
96
139
  "pacific" "pac" Pacific Ocean
97
140
  "world" "wor" Merged Master File
98
- "global" "glo" Merged Master File
99
141
  version : str or None, optional
100
142
  Which GLODAP version to download, by default `None`, in which case the
101
143
  most recent version is downloaded. The options are:
@@ -103,13 +145,8 @@ def download(region="world", version=None, gpath=None, do_download=True):
103
145
  gpath : str of None, optional
104
146
  Where to save the downloaded file, by default `None`, in which case the
105
147
  file is saved at "~/.glodap".
106
- do_download : bool, optional
107
- Whether to actually download the file, by default `True`.
108
-
109
- Returns
110
- -------
111
- int
112
- The status code from the URL request; 200 indicates success.
148
+ chunk_size : int, optional
149
+ Chunk size for streamed file download, by default 8192.
113
150
  """
114
151
  gpath, fileregion, filename, version = _get_paths(region, version, gpath)
115
152
  if not os.path.isdir(gpath):
@@ -118,16 +155,20 @@ def download(region="world", version=None, gpath=None, do_download=True):
118
155
  f"https://glodap.info/glodap_files/{version}/"
119
156
  + f"GLODAP{version}_{fileregion}.mat"
120
157
  )
121
- if do_download:
122
- r = requests.get(url)
123
- if r.status_code == 200:
124
- with open(os.path.join(gpath, filename), "wb") as file:
125
- file.write(r.content)
126
- else:
127
- warn(f"Could not download file - status code {r.status_code}")
128
- else:
129
- r = requests.head(url)
130
- return r.status_code
158
+ hasher = hashlib.new("sha256")
159
+ checksum = checksums[version][region[:3].lower()]
160
+ with tempfile.TemporaryDirectory() as tdir:
161
+ temp_path = os.path.join(tdir, filename)
162
+ with requests.get(url, stream=True) as r:
163
+ r.raise_for_status()
164
+ with open(temp_path, "wb") as f:
165
+ for chunk in r.iter_content(chunk_size=chunk_size):
166
+ f.write(chunk)
167
+ hasher.update(chunk)
168
+ if hasher.hexdigest() == checksum:
169
+ os.rename(temp_path, os.path.join(gpath, filename))
170
+ else:
171
+ warn("File checksum not as expected - download failed.")
131
172
 
132
173
 
133
174
  def read(region="world", version=None, gpath=None, rename_pyco2=True):
@@ -144,7 +185,6 @@ def read(region="world", version=None, gpath=None, rename_pyco2=True):
144
185
  "indian" "ind" Indian Ocean
145
186
  "pacific" "pac" Pacific Ocean
146
187
  "world" "wor" Merged Master File
147
- "global" "glo" Merged Master File
148
188
  version : str or None, optional
149
189
  Which GLODAP version to import, by default `None`, in which case the
150
190
  most recent version is imported. The options are:
@@ -251,12 +291,16 @@ def read(region="world", version=None, gpath=None, rename_pyco2=True):
251
291
  return df
252
292
 
253
293
 
254
- def arctic(gpath=None, rename_pyco2=True):
294
+ def arctic(version=None, gpath=None, rename_pyco2=True):
255
295
  """Import the latest version of the GLODAP Arctic Ocean dataset,
256
296
  downloading it first if it's not already available locally.
257
297
 
258
298
  Parameters
259
299
  ----------
300
+ version : str or None, optional
301
+ Which GLODAP version to import, by default `None`, in which case the
302
+ most recent version is imported. The options are:
303
+ "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
260
304
  gpath : str of None, optional
261
305
  Where to the downloaded file is or should be saved, by default `None`,
262
306
  in which case the file is imported from or saved at "~/.glodap".
@@ -268,15 +312,24 @@ def arctic(gpath=None, rename_pyco2=True):
268
312
  pd.DataFrame
269
313
  The GLODAP Arctic Ocean dataset as a pandas DataFrame.
270
314
  """
271
- return read(region="arctic", gpath=gpath, rename_pyco2=rename_pyco2)
315
+ return read(
316
+ region="arctic",
317
+ version=version,
318
+ gpath=gpath,
319
+ rename_pyco2=rename_pyco2,
320
+ )
272
321
 
273
322
 
274
- def atlantic(gpath=None, rename_pyco2=True):
323
+ def atlantic(version=None, gpath=None, rename_pyco2=True):
275
324
  """Import the latest version of the GLODAP Atlantic Ocean dataset,
276
325
  downloading it first if it's not already available locally.
277
326
 
278
327
  Parameters
279
328
  ----------
329
+ version : str or None, optional
330
+ Which GLODAP version to import, by default `None`, in which case the
331
+ most recent version is imported. The options are:
332
+ "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
280
333
  gpath : str of None, optional
281
334
  Where to the downloaded file is or should be saved, by default `None`,
282
335
  in which case the file is imported from or saved at "~/.glodap".
@@ -288,15 +341,24 @@ def atlantic(gpath=None, rename_pyco2=True):
288
341
  pd.DataFrame
289
342
  The GLODAP Atlantic Ocean dataset as a pandas DataFrame.
290
343
  """
291
- return read(region="atlantic", gpath=gpath, rename_pyco2=rename_pyco2)
344
+ return read(
345
+ region="atlantic",
346
+ version=version,
347
+ gpath=gpath,
348
+ rename_pyco2=rename_pyco2,
349
+ )
292
350
 
293
351
 
294
- def indian(gpath=None, rename_pyco2=True):
352
+ def indian(version=None, gpath=None, rename_pyco2=True):
295
353
  """Import the latest version of the GLODAP Indian Ocean dataset,
296
354
  downloading it first if it's not already available locally.
297
355
 
298
356
  Parameters
299
357
  ----------
358
+ version : str or None, optional
359
+ Which GLODAP version to import, by default `None`, in which case the
360
+ most recent version is imported. The options are:
361
+ "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
300
362
  gpath : str of None, optional
301
363
  Where to the downloaded file is or should be saved, by default `None`,
302
364
  in which case the file is imported from or saved at "~/.glodap".
@@ -308,15 +370,24 @@ def indian(gpath=None, rename_pyco2=True):
308
370
  pd.DataFrame
309
371
  The GLODAP Indian Ocean dataset as a pandas DataFrame.
310
372
  """
311
- return read(region="indian", gpath=gpath, rename_pyco2=rename_pyco2)
373
+ return read(
374
+ region="indian",
375
+ version=version,
376
+ gpath=gpath,
377
+ rename_pyco2=rename_pyco2,
378
+ )
312
379
 
313
380
 
314
- def pacific(gpath=None, rename_pyco2=True):
381
+ def pacific(version=None, gpath=None, rename_pyco2=True):
315
382
  """Import the latest version of the GLODAP Pacific Ocean dataset,
316
383
  downloading it first if it's not already available locally.
317
384
 
318
385
  Parameters
319
386
  ----------
387
+ version : str or None, optional
388
+ Which GLODAP version to import, by default `None`, in which case the
389
+ most recent version is imported. The options are:
390
+ "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
320
391
  gpath : str of None, optional
321
392
  Where to the downloaded file is or should be saved, by default `None`,
322
393
  in which case the file is imported from or saved at "~/.glodap".
@@ -328,15 +399,24 @@ def pacific(gpath=None, rename_pyco2=True):
328
399
  pd.DataFrame
329
400
  The GLODAP Pacific Ocean dataset as a pandas DataFrame.
330
401
  """
331
- return read(region="pacific", gpath=gpath, rename_pyco2=rename_pyco2)
402
+ return read(
403
+ region="pacific",
404
+ version=version,
405
+ gpath=gpath,
406
+ rename_pyco2=rename_pyco2,
407
+ )
332
408
 
333
409
 
334
- def world(gpath=None, rename_pyco2=True):
410
+ def world(version=None, gpath=None, rename_pyco2=True):
335
411
  """Import the latest version of the GLODAP Merged Master File dataset,
336
412
  downloading it first if it's not already available locally.
337
413
 
338
414
  Parameters
339
415
  ----------
416
+ version : str or None, optional
417
+ Which GLODAP version to import, by default `None`, in which case the
418
+ most recent version is imported. The options are:
419
+ "v2.2023", "v2.2022", "v2.2021", "v2.2020", "v2.2019"
340
420
  gpath : str of None, optional
341
421
  Where to the downloaded file is or should be saved, by default `None`,
342
422
  in which case the file is imported from or saved at "~/.glodap".
@@ -348,4 +428,9 @@ def world(gpath=None, rename_pyco2=True):
348
428
  pd.DataFrame
349
429
  The GLODAP Merged Master File as a pandas DataFrame dataset.
350
430
  """
351
- return read(region="world", gpath=gpath, rename_pyco2=rename_pyco2)
431
+ return read(
432
+ region="world",
433
+ version=version,
434
+ gpath=gpath,
435
+ rename_pyco2=rename_pyco2,
436
+ )
@@ -5,8 +5,8 @@ requires = [
5
5
  build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
- name = "GLODAP"
9
- description = "GLODAP downloader and importer in Python "
8
+ name = "glodap"
9
+ description = "GLODAP downloader and importer in Python"
10
10
  readme = "README.md"
11
11
  requires-python = ">= 3.10"
12
12
  dependencies = [
@@ -4,8 +4,8 @@ import glodap
4
4
 
5
5
  def test_region_names():
6
6
  # Check that the first 3 letters of each region name are unique
7
- regions_short = {k[:3] for k in glodap.regions_full}
8
- assert len(regions_short) == len(glodap.regions_full)
7
+ regions_short = {k[:3] for k in glodap.regions}
8
+ assert len(regions_short) == len(glodap.regions)
9
9
 
10
10
 
11
11
  # test_region_names()
@@ -0,0 +1,17 @@
1
+ # %%
2
+ import itertools
3
+ import tempfile
4
+
5
+ import glodap
6
+
7
+
8
+ def test_response():
9
+ with tempfile.TemporaryDirectory() as tdir:
10
+ for region, version in itertools.product(
11
+ glodap.regions, glodap.versions
12
+ ):
13
+ print(region, version)
14
+ glodap.download(region=region, version=version, gpath=tdir)
15
+
16
+
17
+ # test_response()
@@ -1,15 +0,0 @@
1
- # %%
2
- import itertools
3
-
4
- import glodap
5
-
6
-
7
- def test_responses():
8
- for region, version in itertools.product(glodap.regions, glodap.versions):
9
- status = glodap.download(
10
- region=region, version=version, do_download=False
11
- )
12
- assert status == 200, f"Failed on {region} {version}"
13
-
14
-
15
- # test_responses()
File without changes
File without changes
File without changes
File without changes
File without changes