hapiclient 0.2.5__tar.gz → 0.2.7__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.
- {hapiclient-0.2.5 → hapiclient-0.2.7}/CHANGES.txt +18 -1
- {hapiclient-0.2.5 → hapiclient-0.2.7}/PKG-INFO +59 -35
- hapiclient-0.2.5/hapiclient.egg-info/PKG-INFO → hapiclient-0.2.7/README.md +43 -44
- {hapiclient-0.2.5 → hapiclient-0.2.7}/hapiclient/__init__.py +10 -1
- {hapiclient-0.2.5 → hapiclient-0.2.7}/hapiclient/hapi.py +139 -43
- {hapiclient-0.2.5 → hapiclient-0.2.7}/hapiclient/hapitime.py +53 -18
- {hapiclient-0.2.5 → hapiclient-0.2.7}/hapiclient/util.py +42 -21
- hapiclient-0.2.5/README.md → hapiclient-0.2.7/hapiclient.egg-info/PKG-INFO +68 -30
- {hapiclient-0.2.5 → hapiclient-0.2.7}/hapiclient.egg-info/SOURCES.txt +7 -6
- {hapiclient-0.2.5 → hapiclient-0.2.7}/setup.py +6 -3
- {hapiclient-0.2.5/hapiclient → hapiclient-0.2.7}/test/test_chunking.py +85 -57
- hapiclient-0.2.7/test/test_datetime2hapitime.py +32 -0
- hapiclient-0.2.7/test/test_hapi_data_requests.py +224 -0
- hapiclient-0.2.7/test/test_hapi_metadata_requests.py +145 -0
- hapiclient-0.2.7/test/test_hapitime2datetime.py +102 -0
- hapiclient-0.2.7/test/test_hapitime_reformat.py +83 -0
- hapiclient-0.2.7/test/test_logging.py +137 -0
- hapiclient-0.2.5/hapiclient/test/__init__.py +0 -0
- hapiclient-0.2.5/hapiclient/test/compare.py +0 -240
- hapiclient-0.2.5/hapiclient/test/test_hapi.py +0 -310
- hapiclient-0.2.5/hapiclient/test/test_hapitime2datetime.py +0 -124
- hapiclient-0.2.5/hapiclient/test/test_hapitime_reformat.py +0 -79
- {hapiclient-0.2.5 → hapiclient-0.2.7}/LICENSE.txt +0 -0
- {hapiclient-0.2.5 → hapiclient-0.2.7}/MANIFEST.in +0 -0
- {hapiclient-0.2.5 → hapiclient-0.2.7}/hapiclient.egg-info/dependency_links.txt +0 -0
- {hapiclient-0.2.5 → hapiclient-0.2.7}/hapiclient.egg-info/requires.txt +1 -1
- {hapiclient-0.2.5 → hapiclient-0.2.7}/hapiclient.egg-info/top_level.txt +0 -0
- {hapiclient-0.2.5 → hapiclient-0.2.7}/setup.cfg +0 -0
|
@@ -10,7 +10,7 @@ v0.0.6:
|
|
|
10
10
|
2018-10-04 -- Improve dateticks.py
|
|
11
11
|
2018-10-04 -- Add hapi_demo.ipynb
|
|
12
12
|
2018-10-04 -- Other documentation improvements
|
|
13
|
-
v0.0.7:
|
|
13
|
+
v0.0.7:
|
|
14
14
|
2018-10-06 -- Improve documentation
|
|
15
15
|
2018-10-06 -- Add reader tests
|
|
16
16
|
2018-10-23 -- Improve dateticks and tests
|
|
@@ -94,3 +94,20 @@ v0.2.4:
|
|
|
94
94
|
v0.2.5:
|
|
95
95
|
2022-06-30 -- 10df388 Update docstring for hapi()
|
|
96
96
|
2022-07-12 -- 460c30b Final Unicode/Windows fixes
|
|
97
|
+
v0.2.6b:
|
|
98
|
+
2022-08-08 -- 508fb20-3f1168c Add datetime2hapitime
|
|
99
|
+
2022-08-08 -- 8bfef2f Allow start and stop to be None; https://github.com/hapi-server/client-python/issues/10
|
|
100
|
+
2022-09-21 -- bc70f3c CSV read failure; https://github.com/hapi-server/client-python/issues/62
|
|
101
|
+
2022-09-22 -- 373fdca Catch other CSV read failures
|
|
102
|
+
v0.2.6b1:
|
|
103
|
+
2023-02-04 -- 2ef7cff Unicode fix; Log string fix
|
|
104
|
+
2024-05-11 -- fba4ad3 Testing Updates
|
|
105
|
+
v0.2.6:
|
|
106
|
+
2023-05-24 -- Use token for PyPi in Makefile
|
|
107
|
+
v0.2.7b1:
|
|
108
|
+
2025-03-01 -- Unrecognized ISO 8601 time format: '00:00:00.Z' https://github.com/hapi-server/client-python/issues/76
|
|
109
|
+
2025-03-01 -- 'infer_datetime_format' is deprecated ... https://github.com/hapi-server/client-python/issues/78
|
|
110
|
+
2026-04-08 -- Improve NaN handling https://github.com/hapi-server/client-python/issues/88
|
|
111
|
+
2026-05-02 -- Logging and testing https://github.com/hapi-server/client-python/issues/89
|
|
112
|
+
v0.2.7:
|
|
113
|
+
2026--05-02-- Support for Python 2.7 dropped. This is last release with support for 3.5 through 3.8.
|
|
@@ -1,14 +1,27 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: hapiclient
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Interface to Heliophysics data server API
|
|
5
5
|
Home-page: http://pypi.python.org/pypi/hapiclient/
|
|
6
6
|
Author: Bob Weigel
|
|
7
7
|
Author-email: rweigel@gmu.edu
|
|
8
8
|
License: LICENSE.txt
|
|
9
|
-
Platform: UNKNOWN
|
|
10
9
|
Description-Content-Type: text/markdown
|
|
11
10
|
License-File: LICENSE.txt
|
|
11
|
+
Requires-Dist: urllib3
|
|
12
|
+
Requires-Dist: joblib
|
|
13
|
+
Requires-Dist: isodate>=0.6.0
|
|
14
|
+
Requires-Dist: pandas>=0.23
|
|
15
|
+
Requires-Dist: numpy>=1.14.3
|
|
16
|
+
Dynamic: author
|
|
17
|
+
Dynamic: author-email
|
|
18
|
+
Dynamic: description
|
|
19
|
+
Dynamic: description-content-type
|
|
20
|
+
Dynamic: home-page
|
|
21
|
+
Dynamic: license
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
Dynamic: requires-dist
|
|
24
|
+
Dynamic: summary
|
|
12
25
|
|
|
13
26
|
[](https://zenodo.org/badge/latestdoi/93170857)
|
|
14
27
|
[](https://app.travis-ci.com/hapi-server/client-python)
|
|
@@ -30,9 +43,9 @@ The optional [hapiplot package](https://github.com/hapi-server/plot-python) prov
|
|
|
30
43
|
To install `hapiplot`, use
|
|
31
44
|
|
|
32
45
|
```bash
|
|
33
|
-
pip install hapiplot --upgrade
|
|
46
|
+
python -m pip install hapiplot --upgrade
|
|
34
47
|
# or
|
|
35
|
-
pip install 'git+https://github.com/hapi-server/plot-python' --upgrade
|
|
48
|
+
python -m pip install 'git+https://github.com/hapi-server/plot-python' --upgrade
|
|
36
49
|
```
|
|
37
50
|
|
|
38
51
|
See the [Appendix](#appendix) for a fail-safe installation method.
|
|
@@ -51,6 +64,7 @@ start = '2003-09-01T00:00:00'
|
|
|
51
64
|
stop = '2003-12-01T00:00:00'
|
|
52
65
|
parameters = 'DST1800'
|
|
53
66
|
opts = {'logging': True}
|
|
67
|
+
# See misc/hapi_logging_demo.py for example of using conventional Python logging.
|
|
54
68
|
|
|
55
69
|
# Get data
|
|
56
70
|
data, meta = hapi(server, dataset, parameters, start, stop, **opts)
|
|
@@ -93,11 +107,45 @@ returns the [Numpy N-D array](https://docs.scipy.org/doc/numpy-1.15.1/user/quick
|
|
|
93
107
|
|
|
94
108
|
The HAPI client data model is intentionally basic. There is an ongoing discussion of a data model for Heliophysics data among the [PyHC community](https://heliopython.org/). When this data model is complete, a function that converts `data` and `meta` to that data model will be included in the `hapiclient` package.
|
|
95
109
|
|
|
96
|
-
# Development
|
|
110
|
+
# Development and Testing
|
|
111
|
+
|
|
112
|
+
## Testing Python versions available with tox-env
|
|
113
|
+
|
|
114
|
+
As of 2026-04-14, Python 3.8+ is available with tox-env.
|
|
97
115
|
|
|
98
116
|
```bash
|
|
99
117
|
git clone https://github.com/hapi-server/client-python
|
|
100
|
-
cd client-python; pip install -e .
|
|
118
|
+
cd client-python; python -m pip install -e .
|
|
119
|
+
|
|
120
|
+
# Run a specific test on current Python version (default is log_level=INFO
|
|
121
|
+
# for hapiclient logger and test logger when executed this way).
|
|
122
|
+
python test/test_hapi_data_requests.py
|
|
123
|
+
|
|
124
|
+
# Run a specific test on a specific Python version with all logging
|
|
125
|
+
tox -e py311 -- test/test_hapi_data_requests.py
|
|
126
|
+
# Set log level for test logger
|
|
127
|
+
tox -e py311 -- test/test_hapi_data_requests.py --log-level=INFO
|
|
128
|
+
|
|
129
|
+
# Run a specific test on all Python versions set by tox.ini `envlist`
|
|
130
|
+
tox -- test/test_hapi_data_requests.py
|
|
131
|
+
|
|
132
|
+
# Run all tests in ./test
|
|
133
|
+
tox
|
|
134
|
+
|
|
135
|
+
# Run long-running tests
|
|
136
|
+
tox -e long-test
|
|
137
|
+
|
|
138
|
+
# Run long-running tests on a specific Python version
|
|
139
|
+
tox -e long-test --override testenv:long-test.basepython=python3.11
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Testing Python versions available with Anaconda
|
|
143
|
+
|
|
144
|
+
As of 2026-04-14, Python 3.5+ is available with Anaconda.
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
git clone https://github.com/hapi-server/client-python
|
|
148
|
+
cd client-python; python -m pip install -e .
|
|
101
149
|
```
|
|
102
150
|
|
|
103
151
|
or, create an isolated Anaconda installation (downloads and installs latest Miniconda3) using
|
|
@@ -108,7 +156,7 @@ make install PYTHON=python3.6
|
|
|
108
156
|
```
|
|
109
157
|
|
|
110
158
|
The command `pip install -e .` creates symlinks so that the local package is
|
|
111
|
-
used instead of an installed package. You may need to execute `pip uninstall hapiclient` to ensure the local package is used. To check the version installed, use `pip list | grep hapiclient`.
|
|
159
|
+
used instead of an installed package. You may need to execute `python -m pip uninstall hapiclient` to ensure the local package is used. To check the version installed, use `python -m pip list | grep hapiclient`.
|
|
112
160
|
|
|
113
161
|
To run tests before a commit, execute
|
|
114
162
|
|
|
@@ -118,9 +166,8 @@ make repository-test
|
|
|
118
166
|
|
|
119
167
|
To run an individual unit test in a Python session, use, e.g.,
|
|
120
168
|
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
test_reader_short()
|
|
169
|
+
```bash
|
|
170
|
+
cd test; python test_hapitime_reformat.py
|
|
124
171
|
```
|
|
125
172
|
|
|
126
173
|
# Contact
|
|
@@ -128,27 +175,4 @@ test_reader_short()
|
|
|
128
175
|
Submit bug reports and feature requests on the [repository issue
|
|
129
176
|
tracker](https://github.com/hapi-server/client-python/issues>).
|
|
130
177
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
Fail-safe installation
|
|
134
|
-
|
|
135
|
-
Python command line:
|
|
136
|
-
|
|
137
|
-
```python
|
|
138
|
-
import os
|
|
139
|
-
print(os.popen("pip install hapiclient").read())
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The above executes and displays the output of the operating system
|
|
143
|
-
command `pip install hapiclient` using the shell environment
|
|
144
|
-
associated with that installation of Python.
|
|
145
|
-
|
|
146
|
-
This method addresses a problem that is sometimes encountered when
|
|
147
|
-
attempting to use `pip` packages in Anaconda. To use a `pip` package
|
|
148
|
-
in Anaconda, one must use the version of `pip` installed with Anaconda
|
|
149
|
-
(it is usually under a subdirectory with the name `anaconda/`) as
|
|
150
|
-
opposed to the one installed with the operating system. To see the
|
|
151
|
-
location of ``pip`` used in a given Python session, enter
|
|
152
|
-
`print(os.popen("which pip").read())`.
|
|
153
|
-
|
|
154
|
-
|
|
178
|
+
Before submitting a pull request, please post an issue with the proposed changes for discussion prior to developing the pull request.
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: hapiclient
|
|
3
|
-
Version: 0.2.5
|
|
4
|
-
Summary: Interface to Heliophysics data server API
|
|
5
|
-
Home-page: http://pypi.python.org/pypi/hapiclient/
|
|
6
|
-
Author: Bob Weigel
|
|
7
|
-
Author-email: rweigel@gmu.edu
|
|
8
|
-
License: LICENSE.txt
|
|
9
|
-
Platform: UNKNOWN
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE.txt
|
|
12
|
-
|
|
13
1
|
[](https://zenodo.org/badge/latestdoi/93170857)
|
|
14
2
|
[](https://app.travis-ci.com/hapi-server/client-python)
|
|
15
3
|
|
|
@@ -30,9 +18,9 @@ The optional [hapiplot package](https://github.com/hapi-server/plot-python) prov
|
|
|
30
18
|
To install `hapiplot`, use
|
|
31
19
|
|
|
32
20
|
```bash
|
|
33
|
-
pip install hapiplot --upgrade
|
|
21
|
+
python -m pip install hapiplot --upgrade
|
|
34
22
|
# or
|
|
35
|
-
pip install 'git+https://github.com/hapi-server/plot-python' --upgrade
|
|
23
|
+
python -m pip install 'git+https://github.com/hapi-server/plot-python' --upgrade
|
|
36
24
|
```
|
|
37
25
|
|
|
38
26
|
See the [Appendix](#appendix) for a fail-safe installation method.
|
|
@@ -51,6 +39,7 @@ start = '2003-09-01T00:00:00'
|
|
|
51
39
|
stop = '2003-12-01T00:00:00'
|
|
52
40
|
parameters = 'DST1800'
|
|
53
41
|
opts = {'logging': True}
|
|
42
|
+
# See misc/hapi_logging_demo.py for example of using conventional Python logging.
|
|
54
43
|
|
|
55
44
|
# Get data
|
|
56
45
|
data, meta = hapi(server, dataset, parameters, start, stop, **opts)
|
|
@@ -93,11 +82,45 @@ returns the [Numpy N-D array](https://docs.scipy.org/doc/numpy-1.15.1/user/quick
|
|
|
93
82
|
|
|
94
83
|
The HAPI client data model is intentionally basic. There is an ongoing discussion of a data model for Heliophysics data among the [PyHC community](https://heliopython.org/). When this data model is complete, a function that converts `data` and `meta` to that data model will be included in the `hapiclient` package.
|
|
95
84
|
|
|
96
|
-
# Development
|
|
85
|
+
# Development and Testing
|
|
86
|
+
|
|
87
|
+
## Testing Python versions available with tox-env
|
|
88
|
+
|
|
89
|
+
As of 2026-04-14, Python 3.8+ is available with tox-env.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone https://github.com/hapi-server/client-python
|
|
93
|
+
cd client-python; python -m pip install -e .
|
|
94
|
+
|
|
95
|
+
# Run a specific test on current Python version (default is log_level=INFO
|
|
96
|
+
# for hapiclient logger and test logger when executed this way).
|
|
97
|
+
python test/test_hapi_data_requests.py
|
|
98
|
+
|
|
99
|
+
# Run a specific test on a specific Python version with all logging
|
|
100
|
+
tox -e py311 -- test/test_hapi_data_requests.py
|
|
101
|
+
# Set log level for test logger
|
|
102
|
+
tox -e py311 -- test/test_hapi_data_requests.py --log-level=INFO
|
|
103
|
+
|
|
104
|
+
# Run a specific test on all Python versions set by tox.ini `envlist`
|
|
105
|
+
tox -- test/test_hapi_data_requests.py
|
|
106
|
+
|
|
107
|
+
# Run all tests in ./test
|
|
108
|
+
tox
|
|
109
|
+
|
|
110
|
+
# Run long-running tests
|
|
111
|
+
tox -e long-test
|
|
112
|
+
|
|
113
|
+
# Run long-running tests on a specific Python version
|
|
114
|
+
tox -e long-test --override testenv:long-test.basepython=python3.11
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Testing Python versions available with Anaconda
|
|
118
|
+
|
|
119
|
+
As of 2026-04-14, Python 3.5+ is available with Anaconda.
|
|
97
120
|
|
|
98
121
|
```bash
|
|
99
122
|
git clone https://github.com/hapi-server/client-python
|
|
100
|
-
cd client-python; pip install -e .
|
|
123
|
+
cd client-python; python -m pip install -e .
|
|
101
124
|
```
|
|
102
125
|
|
|
103
126
|
or, create an isolated Anaconda installation (downloads and installs latest Miniconda3) using
|
|
@@ -108,7 +131,7 @@ make install PYTHON=python3.6
|
|
|
108
131
|
```
|
|
109
132
|
|
|
110
133
|
The command `pip install -e .` creates symlinks so that the local package is
|
|
111
|
-
used instead of an installed package. You may need to execute `pip uninstall hapiclient` to ensure the local package is used. To check the version installed, use `pip list | grep hapiclient`.
|
|
134
|
+
used instead of an installed package. You may need to execute `python -m pip uninstall hapiclient` to ensure the local package is used. To check the version installed, use `python -m pip list | grep hapiclient`.
|
|
112
135
|
|
|
113
136
|
To run tests before a commit, execute
|
|
114
137
|
|
|
@@ -118,9 +141,8 @@ make repository-test
|
|
|
118
141
|
|
|
119
142
|
To run an individual unit test in a Python session, use, e.g.,
|
|
120
143
|
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
test_reader_short()
|
|
144
|
+
```bash
|
|
145
|
+
cd test; python test_hapitime_reformat.py
|
|
124
146
|
```
|
|
125
147
|
|
|
126
148
|
# Contact
|
|
@@ -128,27 +150,4 @@ test_reader_short()
|
|
|
128
150
|
Submit bug reports and feature requests on the [repository issue
|
|
129
151
|
tracker](https://github.com/hapi-server/client-python/issues>).
|
|
130
152
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
Fail-safe installation
|
|
134
|
-
|
|
135
|
-
Python command line:
|
|
136
|
-
|
|
137
|
-
```python
|
|
138
|
-
import os
|
|
139
|
-
print(os.popen("pip install hapiclient").read())
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
The above executes and displays the output of the operating system
|
|
143
|
-
command `pip install hapiclient` using the shell environment
|
|
144
|
-
associated with that installation of Python.
|
|
145
|
-
|
|
146
|
-
This method addresses a problem that is sometimes encountered when
|
|
147
|
-
attempting to use `pip` packages in Anaconda. To use a `pip` package
|
|
148
|
-
in Anaconda, one must use the version of `pip` installed with Anaconda
|
|
149
|
-
(it is usually under a subdirectory with the name `anaconda/`) as
|
|
150
|
-
opposed to the one installed with the operating system. To see the
|
|
151
|
-
location of ``pip`` used in a given Python session, enter
|
|
152
|
-
`print(os.popen("which pip").read())`.
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
Before submitting a pull request, please post an issue with the proposed changes for discussion prior to developing the pull request.
|
|
@@ -4,12 +4,21 @@ from hapiclient.hapi import hapi
|
|
|
4
4
|
# Allow "from hapiclient import hapitime2datetime"
|
|
5
5
|
from hapiclient.hapitime import hapitime2datetime
|
|
6
6
|
|
|
7
|
+
# Allow "from hapiclient import datetime2hapitime"
|
|
8
|
+
from hapiclient.hapitime import datetime2hapitime
|
|
9
|
+
|
|
7
10
|
# Allow "from hapiclient import HAPIError"
|
|
8
11
|
from hapiclient.util import HAPIError
|
|
9
12
|
|
|
10
|
-
__version__ = '0.2.
|
|
13
|
+
__version__ = '0.2.7'
|
|
11
14
|
|
|
12
15
|
import sys
|
|
16
|
+
import platform
|
|
17
|
+
|
|
18
|
+
if sys.version_info < (3, 10) and platform.system() == "Darwin":
|
|
19
|
+
import warnings
|
|
20
|
+
warnings.filterwarnings("ignore", message=".*urllib3.*OpenSSL.*")
|
|
21
|
+
|
|
13
22
|
if sys.version_info[0] < 3:
|
|
14
23
|
# Python 2.7
|
|
15
24
|
reload(sys)
|
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
import json
|
|
5
5
|
import time
|
|
6
6
|
import pickle
|
|
7
|
+
import logging as _logging
|
|
7
8
|
import warnings
|
|
8
9
|
from datetime import datetime, timedelta
|
|
9
10
|
|
|
@@ -13,7 +14,7 @@ import numpy as np
|
|
|
13
14
|
from joblib import Parallel, delayed
|
|
14
15
|
|
|
15
16
|
from hapiclient.hapitime import hapitime2datetime, hapitime_reformat
|
|
16
|
-
from hapiclient.util import setopts, log, warning, error
|
|
17
|
+
from hapiclient.util import setopts, log, warning, error, configure_logging
|
|
17
18
|
from hapiclient.util import urlopen, urlretrieve, jsonparse, unicode_error_message
|
|
18
19
|
|
|
19
20
|
|
|
@@ -176,7 +177,7 @@ def hapiopts():
|
|
|
176
177
|
def hapi(*args, **kwargs):
|
|
177
178
|
"""Request data from a HAPI server.
|
|
178
179
|
|
|
179
|
-
Version: 0.2.
|
|
180
|
+
Version: 0.2.7
|
|
180
181
|
|
|
181
182
|
|
|
182
183
|
Examples
|
|
@@ -194,10 +195,10 @@ def hapi(*args, **kwargs):
|
|
|
194
195
|
A comma-separated list of parameters in `dataset`
|
|
195
196
|
start: str
|
|
196
197
|
The start time of the requested data
|
|
197
|
-
stop: str
|
|
198
|
+
stop: str or None
|
|
198
199
|
The end time of the requested data; end times are exclusive - the
|
|
199
200
|
last data record returned by a HAPI server should have a timestamp
|
|
200
|
-
before `start`.
|
|
201
|
+
before `start`. If `None`, `stopDate` is used.
|
|
201
202
|
options: dict
|
|
202
203
|
`logging` (``False``) - Log to console
|
|
203
204
|
|
|
@@ -250,7 +251,7 @@ def hapi(*args, **kwargs):
|
|
|
250
251
|
Cadence = PT1M and request for
|
|
251
252
|
|
|
252
253
|
start/stop=1999-11-12T00:10:00/stop=1999-11-12T12:09:00
|
|
253
|
-
|
|
254
|
+
|
|
254
255
|
Chunk size is P1D and requested time range < 1/2 of this
|
|
255
256
|
=> Default behavior
|
|
256
257
|
|
|
@@ -337,14 +338,18 @@ def hapi(*args, **kwargs):
|
|
|
337
338
|
START = START + 'Z'
|
|
338
339
|
if nin > 4:
|
|
339
340
|
STOP = args[4]
|
|
340
|
-
if STOP[-1] != 'Z':
|
|
341
|
+
if STOP is not None and STOP[-1] != 'Z':
|
|
341
342
|
# TODO: Consider warning.
|
|
342
343
|
STOP = STOP + 'Z'
|
|
343
344
|
|
|
344
345
|
# Override defaults
|
|
345
346
|
opts = setopts(hapiopts(), kwargs)
|
|
346
347
|
|
|
347
|
-
assert (opts['logging']
|
|
348
|
+
assert (isinstance(opts['logging'], bool) or hasattr(opts['logging'], 'write')), \
|
|
349
|
+
"logging keyword must be True, False, or a file-like object"
|
|
350
|
+
|
|
351
|
+
configure_logging(opts)
|
|
352
|
+
|
|
348
353
|
assert (opts['cache'] in [True, False]), "cache keyword must be True of False"
|
|
349
354
|
assert (opts['usecache'] in [True, False]), "usecache keyword must be True of False"
|
|
350
355
|
assert (opts['format'] in ['binary', 'csv']), "format keyword must be 'csv' or 'binary'"
|
|
@@ -408,7 +413,7 @@ def hapi(*args, **kwargs):
|
|
|
408
413
|
# urld = url subdirectory of cachedir to store files from SERVER
|
|
409
414
|
urld = cachedir(opts["cachedir"], SERVER)
|
|
410
415
|
|
|
411
|
-
if opts["cachedir"]: log('
|
|
416
|
+
if opts["cachedir"]: log('cache subdirectory = %s' % urld, opts)
|
|
412
417
|
|
|
413
418
|
urljson = SERVER + '/info?id=' + DATASET
|
|
414
419
|
|
|
@@ -422,6 +427,12 @@ def hapi(*args, **kwargs):
|
|
|
422
427
|
|
|
423
428
|
if nin == 5: # Data requested
|
|
424
429
|
|
|
430
|
+
if STOP is None:
|
|
431
|
+
log('STOP was given as None. Getting stopDate for dataset.', opts)
|
|
432
|
+
meta = hapi(SERVER, DATASET)
|
|
433
|
+
STOP = meta['stopDate']
|
|
434
|
+
log('Using STOP = {STOP}', opts)
|
|
435
|
+
|
|
425
436
|
tic_totalTime = time.time()
|
|
426
437
|
|
|
427
438
|
# URL to get CSV (will be used if binary response is not available)
|
|
@@ -552,10 +563,13 @@ def hapi(*args, **kwargs):
|
|
|
552
563
|
|
|
553
564
|
backend = 'sequential'
|
|
554
565
|
if opts['parallel']:
|
|
566
|
+
# Note that this does not often lead to significant speed-up.
|
|
567
|
+
# Needs further testing and optimization.
|
|
555
568
|
backend = 'threading'
|
|
556
|
-
# multiprocessing
|
|
557
|
-
#
|
|
558
|
-
#
|
|
569
|
+
# multiprocessing not working.
|
|
570
|
+
#backend = 'multiprocessing'
|
|
571
|
+
# loky works, but not speed-up.
|
|
572
|
+
#backend = 'loky'
|
|
559
573
|
|
|
560
574
|
log('backend = {}'.format(backend), opts)
|
|
561
575
|
|
|
@@ -657,14 +671,18 @@ def hapi(*args, **kwargs):
|
|
|
657
671
|
if opts["usecache"] and os.path.isfile(fnamenpy):
|
|
658
672
|
# Read cached data file.
|
|
659
673
|
log('Reading %s ' % fnamenpy.replace(urld + '/', ''), opts)
|
|
674
|
+
tic = time.time()
|
|
660
675
|
f = open(fnamenpy, 'rb')
|
|
661
676
|
data = np.load(f)
|
|
662
677
|
f.close()
|
|
678
|
+
toc = time.time() - tic
|
|
663
679
|
# There is a possibility that the fnamenpy file existed but
|
|
664
680
|
# fnamepklx was not found (b/c removed). In this case, the meta
|
|
665
681
|
# returned will not have all of the "x_" information inserted below.
|
|
666
682
|
# Code that uses this information needs to account for this.
|
|
667
683
|
meta['x_totalTime'] = time.time() - tic_totalTime
|
|
684
|
+
meta['x_readTime'] = tic - tic_totalTime
|
|
685
|
+
meta['x_downloadTime'] = 0
|
|
668
686
|
return data, meta
|
|
669
687
|
|
|
670
688
|
cformats = ['csv', 'binary'] # client formats
|
|
@@ -728,7 +746,7 @@ def hapi(*args, **kwargs):
|
|
|
728
746
|
try:
|
|
729
747
|
data = np.fromfile(fnamebin, dtype=dt)
|
|
730
748
|
except:
|
|
731
|
-
error('Could not read
|
|
749
|
+
error('Malformed response? Could not read: {}'.format(urlbin))
|
|
732
750
|
else:
|
|
733
751
|
from io import BytesIO
|
|
734
752
|
log('Writing %s to buffer' % urlbin.replace(urld + '/', ''), opts)
|
|
@@ -740,7 +758,8 @@ def hapi(*args, **kwargs):
|
|
|
740
758
|
try:
|
|
741
759
|
data = np.frombuffer(buff.read(), dtype=dt)
|
|
742
760
|
except:
|
|
743
|
-
error('Could not read
|
|
761
|
+
error('Malformed response? Could not read: {}'.format(urlbin))
|
|
762
|
+
|
|
744
763
|
|
|
745
764
|
# Handle Unicode
|
|
746
765
|
time_name = meta['parameters'][0]['name']
|
|
@@ -753,7 +772,7 @@ def hapi(*args, **kwargs):
|
|
|
753
772
|
# with Unicode, it automatically converts Unicode chars to
|
|
754
773
|
# slash encoded ASCII.
|
|
755
774
|
name = str(name)
|
|
756
|
-
if isinstance(dt[i][1], str) and 'U' in dto[i][1]:
|
|
775
|
+
if data[name].size > 0 and isinstance(dt[i][1], str) and 'U' in dto[i][1]:
|
|
757
776
|
# Decode data.
|
|
758
777
|
datanew[name] = np.char.decode(data[name])
|
|
759
778
|
else:
|
|
@@ -763,6 +782,8 @@ def hapi(*args, **kwargs):
|
|
|
763
782
|
else:
|
|
764
783
|
# HAPI CSV
|
|
765
784
|
|
|
785
|
+
file_empty = False
|
|
786
|
+
|
|
766
787
|
if opts["cache"]:
|
|
767
788
|
log('Writing %s to %s' % (urlcsv, fnamecsv.replace(urld + '/', '')), opts)
|
|
768
789
|
tic0 = time.time()
|
|
@@ -770,38 +791,78 @@ def hapi(*args, **kwargs):
|
|
|
770
791
|
toc0 = time.time() - tic0
|
|
771
792
|
log('Reading and parsing %s' % fnamecsv.replace(urld + '/', ''), opts)
|
|
772
793
|
tic = time.time()
|
|
794
|
+
if os.path.getsize(fnamecsv) == 0:
|
|
795
|
+
file_empty = True
|
|
796
|
+
data = np.array([], dtype=dt)
|
|
773
797
|
else:
|
|
774
798
|
from io import StringIO
|
|
775
|
-
|
|
799
|
+
log('Writing %s to buffer' % urlcsv.replace(urld + '/', ''), opts)
|
|
776
800
|
tic0 = time.time()
|
|
777
801
|
fnamecsv = StringIO(urlopen(urlcsv).read().decode())
|
|
802
|
+
fnamecsv.seek(0, os.SEEK_END)
|
|
803
|
+
if fnamecsv.tell() == 0:
|
|
804
|
+
file_empty = True
|
|
805
|
+
data = np.array([], dtype=dt)
|
|
806
|
+
else:
|
|
807
|
+
fnamecsv.seek(0)
|
|
778
808
|
toc0 = time.time() - tic0
|
|
779
809
|
log('Parsing StringIO buffer.', opts)
|
|
780
810
|
tic = time.time()
|
|
781
811
|
|
|
782
|
-
if
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
812
|
+
if file_empty == False:
|
|
813
|
+
if not missing_length:
|
|
814
|
+
# All string and isotime parameters have a length in metadata.
|
|
815
|
+
if opts['method'] == 'numpy':
|
|
816
|
+
try:
|
|
817
|
+
data = np.genfromtxt(fnamecsv,
|
|
818
|
+
dtype=dt,
|
|
819
|
+
delimiter=',',
|
|
820
|
+
replace_space=' ',
|
|
821
|
+
deletechars='',
|
|
822
|
+
encoding='utf-8')
|
|
823
|
+
except:
|
|
824
|
+
error('Malformed response? Could not read response: {}'.format(urlcsv))
|
|
825
|
+
if opts['method'] == '' or opts['method'] == 'pandas':
|
|
826
|
+
# Read file into Pandas DataFrame
|
|
827
|
+
csv_kwargs = {
|
|
828
|
+
'sep': ',',
|
|
829
|
+
'header': None,
|
|
830
|
+
'encoding': 'utf-8',
|
|
831
|
+
'skipinitialspace': True,
|
|
832
|
+
'keep_default_na': False,
|
|
833
|
+
'na_values': ['NaN', 'nan', 'Nan', 'naN', ' "NaN"', ' "nan"', ' "Nan"', ' "naN"', '"NaN"', '"nan"', '"Nan"', '"naN"']
|
|
834
|
+
}
|
|
835
|
+
"""
|
|
836
|
+
Note that this does not handle trailing whitespace after
|
|
837
|
+
any of the na_values. (There is no skiptrailingspace option).
|
|
838
|
+
Stripping trailing whitespace would require adding something
|
|
839
|
+
like
|
|
840
|
+
def strip_field(x):
|
|
841
|
+
# Strip whitespace and normalize NaN values.
|
|
842
|
+
x = x.strip()
|
|
843
|
+
return np.nan if x.lower() == "nan" else x
|
|
844
|
+
|
|
845
|
+
ncols = cols[-1][1] + 1
|
|
846
|
+
csv_kwargs["converters"] = {i: strip_field for i in range(ncols)}
|
|
847
|
+
"""
|
|
848
|
+
try:
|
|
849
|
+
df = pandas.read_csv(fnamecsv, **csv_kwargs)
|
|
850
|
+
except:
|
|
851
|
+
error('Malformed response? Could not read response: {}'.format(urlcsv))
|
|
852
|
+
|
|
853
|
+
# Allocate output N-D array (It is not possible to pass dtype=dt
|
|
854
|
+
# as computed to pandas.read_csv; pandas dtype is different
|
|
855
|
+
# from numpy's dtype.)
|
|
856
|
+
data = np.ndarray(shape=(len(df)), dtype=dt)
|
|
857
|
+
# Insert data from dataframe 'df' columns into N-D array 'data'
|
|
858
|
+
for i in range(0, len(pnames)):
|
|
859
|
+
shape = np.append(len(data), psizes[i])
|
|
860
|
+
# In numpy 1.8.2 and Python 2.7, this throws an error
|
|
861
|
+
# for no apparent reason. Works as expected in numpy 1.10.4
|
|
862
|
+
data[pnames[i]] = np.squeeze(
|
|
863
|
+
np.reshape(df.values[:, np.arange(cols[i][0], cols[i][1] + 1)], shape))
|
|
864
|
+
else:
|
|
865
|
+
data = parse_missing_length(fnamecsv, dt, cols, psizes, pnames, ptypes, opts)
|
|
805
866
|
|
|
806
867
|
toc = time.time() - tic
|
|
807
868
|
|
|
@@ -835,7 +896,14 @@ def hapi(*args, **kwargs):
|
|
|
835
896
|
pickle.dump(meta, f, protocol=2)
|
|
836
897
|
|
|
837
898
|
log('Writing %s' % fnamenpy, opts)
|
|
838
|
-
|
|
899
|
+
with warnings.catch_warnings():
|
|
900
|
+
# Ignore warning that occurs when saving Unicode data.
|
|
901
|
+
warnings.filterwarnings("ignore",
|
|
902
|
+
message=r"Stored array in format 3\.0.*",
|
|
903
|
+
category=UserWarning,
|
|
904
|
+
module=r"numpy\.lib\.format",
|
|
905
|
+
)
|
|
906
|
+
np.save(fnamenpy, data)
|
|
839
907
|
|
|
840
908
|
meta['x_totalTime'] = time.time() - tic_totalTime
|
|
841
909
|
|
|
@@ -933,9 +1001,28 @@ def parse_missing_length(fnamecsv, dt, cols, psizes, pnames, ptypes, opts):
|
|
|
933
1001
|
if opts['method'] == 'numpy' or opts['method'] == 'numpynolength':
|
|
934
1002
|
# If requested method was numpy, use numpynolength method.
|
|
935
1003
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1004
|
+
ncols = cols[-1][1] + 1
|
|
1005
|
+
|
|
1006
|
+
def normalize_field(value):
|
|
1007
|
+
if isinstance(value, bytes):
|
|
1008
|
+
value = value.decode('utf-8')
|
|
1009
|
+
value = value.strip()
|
|
1010
|
+
if len(value) >= 2 and value[0] == '"' and value[-1] == '"':
|
|
1011
|
+
value = value[1:-1].strip()
|
|
1012
|
+
if value.lower() == 'nan':
|
|
1013
|
+
return 'nan'
|
|
1014
|
+
return value
|
|
1015
|
+
|
|
1016
|
+
converters = {i: normalize_field for i in range(ncols)}
|
|
1017
|
+
|
|
1018
|
+
table = np.genfromtxt(fnamecsv,
|
|
1019
|
+
dtype=None,
|
|
1020
|
+
deletechars='',
|
|
1021
|
+
replace_space=' ',
|
|
1022
|
+
delimiter=',',
|
|
1023
|
+
encoding='utf-8',
|
|
1024
|
+
converters=converters)
|
|
1025
|
+
|
|
939
1026
|
# table is a 1-D array. Each element is a row in the file.
|
|
940
1027
|
# - If the data types are not the same for each column,
|
|
941
1028
|
# the elements are tuples with length equal to the number
|
|
@@ -980,8 +1067,17 @@ def parse_missing_length(fnamecsv, dt, cols, psizes, pnames, ptypes, opts):
|
|
|
980
1067
|
if opts['method'] == '' or opts['method'] == 'pandas' or opts['method'] == 'pandasnolength':
|
|
981
1068
|
# If requested method was pandas, use pandasnolength method.
|
|
982
1069
|
|
|
1070
|
+
# TODO: Duplicate code.
|
|
983
1071
|
# Read file into Pandas DataFrame
|
|
984
|
-
|
|
1072
|
+
csv_kwargs = {
|
|
1073
|
+
'sep': ',',
|
|
1074
|
+
'header': None,
|
|
1075
|
+
'encoding': 'utf-8',
|
|
1076
|
+
'skipinitialspace': True,
|
|
1077
|
+
'keep_default_na': False,
|
|
1078
|
+
'na_values': ['NaN', 'nan', 'Nan', 'naN', ' "NaN"', ' "nan"', ' "Nan"', ' "naN"', '"NaN"', '"nan"', '"Nan"', '"naN"']
|
|
1079
|
+
}
|
|
1080
|
+
df = pandas.read_csv(fnamecsv, **csv_kwargs)
|
|
985
1081
|
|
|
986
1082
|
# Allocate output N-D array (It is not possible to pass dtype=dt
|
|
987
1083
|
# as computed to pandas.read_csv, so need to create new ND array.)
|