summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGard Spreemann <gspr@nonempty.org>2021-08-14 20:32:06 +0200
committerGard Spreemann <gspr@nonempty.org>2021-08-14 20:32:06 +0200
commitb70a5361202307c26bac18d15453b5193a22f11c (patch)
treec49271442b968d62596e4d68f7c6a793b0b82134
parentdf3d444e8af6a9be6c7f546982483894767fab22 (diff)
Clean out directory in preparation for introducing upstream git
Upstream has introduced a git repository since the last Debian version. This has an incompatible history with our upstream branch, so nuke everything in favor of the new upstream git.
-rw-r--r--CONTRIBUTING.rst51
-rw-r--r--LICENSE.txt177
-rw-r--r--MANIFEST.in10
-rw-r--r--PKG-INFO84
-rw-r--r--README.rst62
-rw-r--r--cdsapi.egg-info/PKG-INFO84
-rw-r--r--cdsapi.egg-info/SOURCES.txt19
-rw-r--r--cdsapi.egg-info/dependency_links.txt1
-rw-r--r--cdsapi.egg-info/requires.txt2
-rw-r--r--cdsapi.egg-info/top_level.txt1
-rw-r--r--cdsapi.egg-info/zip-safe1
-rw-r--r--cdsapi/__init__.py23
-rw-r--r--cdsapi/api.py639
-rwxr-xr-xexample-era5.py26
-rwxr-xr-xexample-glaciers.py19
-rw-r--r--setup.cfg22
-rw-r--r--setup.py66
-rw-r--r--tests/requirements.txt9
-rw-r--r--tests/test_api.py20
-rw-r--r--tox.ini16
20 files changed, 0 insertions, 1332 deletions
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
deleted file mode 100644
index d750f33..0000000
--- a/CONTRIBUTING.rst
+++ /dev/null
@@ -1,51 +0,0 @@
-
-.. highlight:: console
-
-How to develop
---------------
-
-Install the package following README.rst and then install development dependencies::
-
- $ pip install -U -r tests/requirements-dev.txt
-
-Unit tests can be run with `pytest <https://pytest.org>`_ with::
-
- $ pytest -v --flakes --cov=cdsapi --cov-report=html --cache-clear
-
-Coverage can be checked opening in a browser the file ``htmlcov/index.html`` for example with::
-
- $ open htmlcov/index.html
-
-Code quality control checks can be run with::
-
- $ pytest -v --pep8 --mccabe
-
-The complete python versions tests are run via `tox <https://tox.readthedocs.io>`_ with::
-
- $ tox
-
-Please ensure the coverage at least stays the same before you submit a pull request.
-
-
-Dependency management
----------------------
-
-Update the `requirements-tests.txt` file with versions with::
-
- pip-compile -U -o tests/requirements-tests.txt setup.py tests/requirements-tests.in # -U is optional
-
-
-Release procedure
------------------
-
-Quality check release::
-
- $ git status
- $ check-manifest
- $ tox
-
-Release with zest.releaser::
-
- $ prerelease
- $ release
- $ postrelease
diff --git a/LICENSE.txt b/LICENSE.txt
deleted file mode 100644
index f433b1a..0000000
--- a/LICENSE.txt
+++ /dev/null
@@ -1,177 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index e95932d..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,10 +0,0 @@
-include *.in
-include *.rst
-include *.txt
-include *.py
-include LICENSE
-include tox.ini
-recursive-include cdsapi *.py
-recursive-include tests *.in
-recursive-include tests *.py
-recursive-include tests *.txt
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index 56670de..0000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,84 +0,0 @@
-Metadata-Version: 1.1
-Name: cdsapi
-Version: 0.4.0
-Summary: Climate Data Store API
-Home-page: https://software.ecmwf.int/stash/projects/CDS/repos/cdsapi
-Author: ECMWF
-Author-email: software.support@ecmwf.int
-License: Apache 2.0
-Description:
- Install
- -------
-
- Install via `pip` with::
-
- $ pip install cdsapi
-
-
- Configure
- ---------
-
- Get your UID and API key from the CDS portal at the address https://cds.climate.copernicus.eu/user
- and write it into the configuration file, so it looks like::
-
- $ cat ~/.cdsapirc
- url: https://cds.climate.copernicus.eu/api/v2
- key: <UID>:<API key>
- verify: 0
-
- Remember to agree to the Terms and Conditions of every dataset that you intend to download.
-
-
- Test
- ----
-
- Perform a small test retrieve of ERA5 data::
-
- $ python
- >>> import cdsapi
- >>> cds = cdsapi.Client()
- >>> cds.retrieve('reanalysis-era5-pressure-levels', {
- "variable": "temperature",
- "pressure_level": "1000",
- "product_type": "reanalysis",
- "date": "2017-12-01/2017-12-31",
- "time": "12:00",
- "format": "grib"
- }, 'download.grib')
- >>>
-
-
- License
- -------
-
- Copyright 2018 - 2019 European Centre for Medium-Range Weather Forecasts (ECMWF)
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- In applying this licence, ECMWF does not waive the privileges and immunities
- granted to it by virtue of its status as an intergovernmental organisation nor
- does it submit to any jurisdiction.
-
-Platform: UNKNOWN
-Classifier: Development Status :: 3 - Alpha
-Classifier: Intended Audience :: Developers
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Operating System :: OS Independent
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 166dac0..0000000
--- a/README.rst
+++ /dev/null
@@ -1,62 +0,0 @@
-
-Install
--------
-
-Install via `pip` with::
-
- $ pip install cdsapi
-
-
-Configure
----------
-
-Get your UID and API key from the CDS portal at the address https://cds.climate.copernicus.eu/user
-and write it into the configuration file, so it looks like::
-
- $ cat ~/.cdsapirc
- url: https://cds.climate.copernicus.eu/api/v2
- key: <UID>:<API key>
- verify: 0
-
-Remember to agree to the Terms and Conditions of every dataset that you intend to download.
-
-
-Test
-----
-
-Perform a small test retrieve of ERA5 data::
-
- $ python
- >>> import cdsapi
- >>> cds = cdsapi.Client()
- >>> cds.retrieve('reanalysis-era5-pressure-levels', {
- "variable": "temperature",
- "pressure_level": "1000",
- "product_type": "reanalysis",
- "date": "2017-12-01/2017-12-31",
- "time": "12:00",
- "format": "grib"
- }, 'download.grib')
- >>>
-
-
-License
--------
-
-Copyright 2018 - 2019 European Centre for Medium-Range Weather Forecasts (ECMWF)
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-In applying this licence, ECMWF does not waive the privileges and immunities
-granted to it by virtue of its status as an intergovernmental organisation nor
-does it submit to any jurisdiction.
diff --git a/cdsapi.egg-info/PKG-INFO b/cdsapi.egg-info/PKG-INFO
deleted file mode 100644
index 56670de..0000000
--- a/cdsapi.egg-info/PKG-INFO
+++ /dev/null
@@ -1,84 +0,0 @@
-Metadata-Version: 1.1
-Name: cdsapi
-Version: 0.4.0
-Summary: Climate Data Store API
-Home-page: https://software.ecmwf.int/stash/projects/CDS/repos/cdsapi
-Author: ECMWF
-Author-email: software.support@ecmwf.int
-License: Apache 2.0
-Description:
- Install
- -------
-
- Install via `pip` with::
-
- $ pip install cdsapi
-
-
- Configure
- ---------
-
- Get your UID and API key from the CDS portal at the address https://cds.climate.copernicus.eu/user
- and write it into the configuration file, so it looks like::
-
- $ cat ~/.cdsapirc
- url: https://cds.climate.copernicus.eu/api/v2
- key: <UID>:<API key>
- verify: 0
-
- Remember to agree to the Terms and Conditions of every dataset that you intend to download.
-
-
- Test
- ----
-
- Perform a small test retrieve of ERA5 data::
-
- $ python
- >>> import cdsapi
- >>> cds = cdsapi.Client()
- >>> cds.retrieve('reanalysis-era5-pressure-levels', {
- "variable": "temperature",
- "pressure_level": "1000",
- "product_type": "reanalysis",
- "date": "2017-12-01/2017-12-31",
- "time": "12:00",
- "format": "grib"
- }, 'download.grib')
- >>>
-
-
- License
- -------
-
- Copyright 2018 - 2019 European Centre for Medium-Range Weather Forecasts (ECMWF)
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- In applying this licence, ECMWF does not waive the privileges and immunities
- granted to it by virtue of its status as an intergovernmental organisation nor
- does it submit to any jurisdiction.
-
-Platform: UNKNOWN
-Classifier: Development Status :: 3 - Alpha
-Classifier: Intended Audience :: Developers
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Operating System :: OS Independent
diff --git a/cdsapi.egg-info/SOURCES.txt b/cdsapi.egg-info/SOURCES.txt
deleted file mode 100644
index 961a41b..0000000
--- a/cdsapi.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-CONTRIBUTING.rst
-LICENSE.txt
-MANIFEST.in
-README.rst
-example-era5.py
-example-glaciers.py
-setup.cfg
-setup.py
-tox.ini
-cdsapi/__init__.py
-cdsapi/api.py
-cdsapi.egg-info/PKG-INFO
-cdsapi.egg-info/SOURCES.txt
-cdsapi.egg-info/dependency_links.txt
-cdsapi.egg-info/requires.txt
-cdsapi.egg-info/top_level.txt
-cdsapi.egg-info/zip-safe
-tests/requirements.txt
-tests/test_api.py \ No newline at end of file
diff --git a/cdsapi.egg-info/dependency_links.txt b/cdsapi.egg-info/dependency_links.txt
deleted file mode 100644
index 8b13789..0000000
--- a/cdsapi.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/cdsapi.egg-info/requires.txt b/cdsapi.egg-info/requires.txt
deleted file mode 100644
index 58ac32b..0000000
--- a/cdsapi.egg-info/requires.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-requests>=2.5.0
-tqdm
diff --git a/cdsapi.egg-info/top_level.txt b/cdsapi.egg-info/top_level.txt
deleted file mode 100644
index e9998de..0000000
--- a/cdsapi.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-cdsapi
diff --git a/cdsapi.egg-info/zip-safe b/cdsapi.egg-info/zip-safe
deleted file mode 100644
index 8b13789..0000000
--- a/cdsapi.egg-info/zip-safe
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/cdsapi/__init__.py b/cdsapi/__init__.py
deleted file mode 100644
index c9e9d4e..0000000
--- a/cdsapi/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2018 European Centre for Medium-Range Weather Forecasts (ECMWF)
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# In applying this licence, ECMWF does not waive the privileges and immunities
-# granted to it by virtue of its status as an intergovernmental organisation nor
-# does it submit to any jurisdiction.
-
-from __future__ import absolute_import, division, print_function, unicode_literals
-
-from . import api
-
-Client = api.Client
diff --git a/cdsapi/api.py b/cdsapi/api.py
deleted file mode 100644
index eb82e34..0000000
--- a/cdsapi/api.py
+++ /dev/null
@@ -1,639 +0,0 @@
-# (C) Copyright 2018 ECMWF.
-#
-# This software is licensed under the terms of the Apache Licence Version 2.0
-# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
-# In applying this licence, ECMWF does not waive the privileges and immunities
-# granted to it by virtue of its status as an intergovernmental organisation nor
-# does it submit to any jurisdiction.
-
-from __future__ import absolute_import, division, print_function, unicode_literals
-
-import json
-import time
-import os
-import logging
-import uuid
-import requests
-
-try:
- from urllib.parse import urljoin
-except ImportError:
- from urlparse import urljoin
-
-from tqdm import tqdm
-
-
-def bytes_to_string(n):
- u = ["", "K", "M", "G", "T", "P"]
- i = 0
- while n >= 1024:
- n /= 1024.0
- i += 1
- return "%g%s" % (int(n * 10 + 0.5) / 10.0, u[i])
-
-
-def read_config(path):
- config = {}
- with open(path) as f:
- for l in f.readlines():
- if ":" in l:
- k, v = l.strip().split(":", 1)
- if k in ("url", "key", "verify"):
- config[k] = v.strip()
- return config
-
-
-def toJSON(obj):
-
- to_json = getattr(obj, "toJSON", None)
- if callable(to_json):
- return to_json()
-
- if isinstance(obj, (list, tuple)):
- return [toJSON(x) for x in obj]
-
- if isinstance(obj, dict):
- r = {}
- for k, v in obj.items():
- r[k] = toJSON(v)
- return r
-
- return obj
-
-
-class Result(object):
- def __init__(self, client, reply):
-
- self.reply = reply
-
- self._url = client.url
-
- self.session = client.session
- self.robust = client.robust
- self.verify = client.verify
- self.cleanup = client.delete
-
- self.debug = client.debug
- self.info = client.info
- self.warning = client.warning
- self.error = client.error
- self.sleep_max = client.sleep_max
- self.retry_max = client.retry_max
-
- self.timeout = client.timeout
- self.progress = client.progress
-
- self._deleted = False
-
- def toJSON(self):
- r = dict(
- resultType="url",
- contentType=self.content_type,
- contentLength=self.content_length,
- location=self.location,
- )
- return r
-
- def _download(self, url, size, target):
-
- if target is None:
- target = url.split("/")[-1]
-
- self.info("Downloading %s to %s (%s)", url, target, bytes_to_string(size))
- start = time.time()
-
- mode = "wb"
- total = 0
- sleep = 10
- tries = 0
- headers = None
-
- while tries < self.retry_max:
-
- r = self.robust(self.session.get)(
- url,
- stream=True,
- verify=self.verify,
- headers=headers,
- timeout=self.timeout,
- )
- try:
- r.raise_for_status()
-
- with tqdm(
- total=size,
- unit_scale=True,
- unit_divisor=1024,
- unit="B",
- disable=not self.progress,
- leave=False,
- ) as pbar:
- pbar.update(total)
- with open(target, mode) as f:
- for chunk in r.iter_content(chunk_size=1024):
- if chunk:
- f.write(chunk)
- total += len(chunk)
- pbar.update(len(chunk))
-
- except requests.exceptions.ConnectionError as e:
- self.error("Download interupted: %s" % (e,))
- finally:
- r.close()
-
- if total >= size:
- break
-
- self.error(
- "Download incomplete, downloaded %s byte(s) out of %s" % (total, size)
- )
- self.warning("Sleeping %s seconds" % (sleep,))
- time.sleep(sleep)
- mode = "ab"
- total = os.path.getsize(target)
- sleep *= 1.5
- if sleep > self.sleep_max:
- sleep = self.sleep_max
- headers = {"Range": "bytes=%d-" % total}
- tries += 1
- self.warning("Resuming download at byte %s" % (total,))
-
- if total != size:
- raise Exception(
- "Download failed: downloaded %s byte(s) out of %s" % (total, size)
- )
-
- elapsed = time.time() - start
- if elapsed:
- self.info("Download rate %s/s", bytes_to_string(size / elapsed))
-
- return target
-
- def download(self, target=None):
- return self._download(self.location, self.content_length, target)
-
- @property
- def content_length(self):
- return int(self.reply["content_length"])
-
- @property
- def location(self):
- return urljoin(self._url, self.reply["location"])
-
- @property
- def content_type(self):
- return self.reply["content_type"]
-
- def __repr__(self):
- return "Result(content_length=%s,content_type=%s,location=%s)" % (
- self.content_length,
- self.content_type,
- self.location,
- )
-
- def check(self):
- self.debug("HEAD %s", self.location)
- metadata = self.robust(self.session.head)(
- self.location, verify=self.verify, timeout=self.timeout
- )
- metadata.raise_for_status()
- self.debug(metadata.headers)
- return metadata
-
- def update(self, request_id=None):
- if request_id is None:
- request_id = self.reply["request_id"]
- task_url = "%s/tasks/%s" % (self._url, request_id)
- self.debug("GET %s", task_url)
-
- result = self.robust(self.session.get)(task_url, verify=self.verify)
- result.raise_for_status()
- self.reply = result.json()
-
- def delete(self):
-
- if self._deleted:
- return
-
- if "request_id" in self.reply:
- rid = self.reply["request_id"]
-
- task_url = "%s/tasks/%s" % (self._url, rid)
- self.debug("DELETE %s", task_url)
-
- delete = self.session.delete(task_url, verify=self.verify)
- self.debug("DELETE returns %s %s", delete.status_code, delete.reason)
-
- try:
- delete.raise_for_status()
- except Exception:
- self.warning(
- "DELETE %s returns %s %s",
- task_url,
- delete.status_code,
- delete.reason,
- )
-
- self._deleted = True
-
- def __del__(self):
- try:
- if self.cleanup:
- self.delete()
- except Exception as e:
- print(e)
-
-
-class Client(object):
-
- logger = logging.getLogger("cdsapi")
-
- def __init__(
- self,
- url=os.environ.get("CDSAPI_URL"),
- key=os.environ.get("CDSAPI_KEY"),
- quiet=False,
- debug=False,
- verify=None,
- timeout=60,
- progress=True,
- full_stack=False,
- delete=True,
- retry_max=500,
- sleep_max=120,
- wait_until_complete=True,
- info_callback=None,
- warning_callback=None,
- error_callback=None,
- debug_callback=None,
- metadata=None,
- forget=False,
- session=requests.Session(),
- ):
-
- if not quiet:
-
- if debug:
- level = logging.DEBUG
- else:
- level = logging.INFO
-
- logging.basicConfig(
- level=level, format="%(asctime)s %(levelname)s %(message)s"
- )
-
- dotrc = os.environ.get("CDSAPI_RC", os.path.expanduser("~/.cdsapirc"))
-
- if url is None or key is None:
- if os.path.exists(dotrc):
- config = read_config(dotrc)
-
- if key is None:
- key = config.get("key")
-
- if url is None:
- url = config.get("url")
-
- if verify is None:
- verify = int(config.get("verify", 1))
-
- if url is None or key is None or key is None:
- raise Exception("Missing/incomplete configuration file: %s" % (dotrc))
-
- self.url = url
- self.key = key
-
- self.quiet = quiet
- self.progress = progress and not quiet
-
- self.verify = True if verify else False
- self.timeout = timeout
- self.sleep_max = sleep_max
- self.retry_max = retry_max
- self.full_stack = full_stack
- self.delete = delete
- self.last_state = None
- self.wait_until_complete = wait_until_complete
-
- self.debug_callback = debug_callback
- self.warning_callback = warning_callback
- self.info_callback = info_callback
- self.error_callback = error_callback
-
- self.session = session
- self.session.auth = tuple(self.key.split(":", 2))
-
- self.metadata = metadata
- self.forget = forget
-
- self.debug(
- "CDSAPI %s",
- dict(
- url=self.url,
- key=self.key,
- quiet=self.quiet,
- verify=self.verify,
- timeout=self.timeout,
- progress=self.progress,
- sleep_max=self.sleep_max,
- retry_max=self.retry_max,
- full_stack=self.full_stack,
- delete=self.delete,
- metadata=self.metadata,
- forget=self.forget,
- ),
- )
-
- def retrieve(self, name, request, target=None):
- result = self._api("%s/resources/%s" % (self.url, name), request, "POST")
- if target is not None:
- result.download(target)
- return result
-
- def service(self, name, mimic_ui=False, *args, **kwargs):
- self.delete = False # Don't delete results
- name = "/".join(name.split("."))
- # To mimic the CDS ui the request should be populated directly with the kwargs
- if mimic_ui:
- request = kwargs
- else:
- request = dict(args=args, kwargs=kwargs)
-
- if self.metadata:
- request["_cds_metadata"] = self.metadata
- request = toJSON(request)
- result = self._api(
- "%s/tasks/services/%s/clientid-%s" % (self.url, name, uuid.uuid4().hex),
- request,
- "PUT",
- )
- return result
-
- def workflow(self, code, *args, **kwargs):
- workflow_name = kwargs.pop("workflow_name", "application")
- params = dict(code=code, args=args, kwargs=kwargs, workflow_name=workflow_name)
- return self.service("tool.toolbox.orchestrator.run_workflow", params)
-
- def status(self, context=None):
- url = "%s/status.json" % (self.url,)
- r = self.session.get(url, verify=self.verify)
- r.raise_for_status()
- return r.json()
-
- def _status(self, url):
- try:
- status = self.status(url)
-
- info = status.get("info", [])
- if not isinstance(info, list):
- info = [info]
- for i in info:
- self.info("%s", i)
-
- warning = status.get("warning", [])
- if not isinstance(warning, list):
- warning = [warning]
- for w in warning:
- self.warning("%s", w)
-
- except Exception:
- pass
-
- def _api(self, url, request, method):
-
- self._status(url)
-
- session = self.session
-
- self.info("Sending request to %s", url)
- self.debug("%s %s %s", method, url, json.dumps(request))
-
- if method == "PUT":
- action = session.put
- else:
- action = session.post
-
- result = self.robust(action)(
- url, json=request, verify=self.verify, timeout=self.timeout
- )
-
- if self.forget:
- return result
-
- reply = None
-
- try:
- result.raise_for_status()
- reply = result.json()
- except Exception:
-
- if reply is None:
- try:
- reply = result.json()
- except Exception:
- reply = dict(message=result.text)
-
- self.debug(json.dumps(reply))
-
- if "message" in reply:
- error = reply["message"]
-
- if "context" in reply and "required_terms" in reply["context"]:
- e = [error]
- for t in reply["context"]["required_terms"]:
- e.append(
- "To access this resource, you first need to accept the terms"
- "of '%s' at %s" % (t["title"], t["url"])
- )
- error = ". ".join(e)
- raise Exception(error)
- else:
- raise
-
- if not self.wait_until_complete:
- return Result(self, reply)
-
- sleep = 1
-
- while True:
-
- self.debug("REPLY %s", reply)
-
- if reply["state"] != self.last_state:
- self.info("Request is %s" % (reply["state"],))
- self.last_state = reply["state"]
-
- if reply["state"] == "completed":
- self.debug("Done")
-
- if "result" in reply:
- return reply["result"]
-
- return Result(self, reply)
-
- if reply["state"] in ("queued", "running"):
- rid = reply["request_id"]
-
- self.debug("Request ID is %s, sleep %s", rid, sleep)
- time.sleep(sleep)
- sleep *= 1.5
- if sleep > self.sleep_max:
- sleep = self.sleep_max
-
- task_url = "%s/tasks/%s" % (self.url, rid)
- self.debug("GET %s", task_url)
-
- result = self.robust(session.get)(
- task_url, verify=self.verify, timeout=self.timeout
- )
- result.raise_for_status()
- reply = result.json()
- continue
-
- if reply["state"] in ("failed",):
- self.error("Message: %s", reply["error"].get("message"))
- self.error("Reason: %s", reply["error"].get("reason"))
- for n in (
- reply.get("error", {})
- .get("context", {})
- .get("traceback", "")
- .split("\n")
- ):
- if n.strip() == "" and not self.full_stack:
- break
- self.error(" %s", n)
- raise Exception(
- "%s. %s."
- % (reply["error"].get("message"), reply["error"].get("reason"))
- )
-
- raise Exception("Unknown API state [%s]" % (reply["state"],))
-
- def info(self, *args, **kwargs):
- if self.info_callback:
- self.info_callback(*args, **kwargs)
- else:
- self.logger.info(*args, **kwargs)
-
- def warning(self, *args, **kwargs):
- if self.warning_callback:
- self.warning_callback(*args, **kwargs)
- else:
- self.logger.warning(*args, **kwargs)
-
- def error(self, *args, **kwargs):
- if self.error_callback:
- self.error_callback(*args, **kwargs)
- else:
- self.logger.error(*args, **kwargs)
-
- def debug(self, *args, **kwargs):
- if self.debug_callback:
- self.debug_callback(*args, **kwargs)
- else:
- self.logger.debug(*args, **kwargs)
-
- def _download(self, results, targets=None):
-
- if isinstance(results, Result):
- if targets:
- path = targets.pop(0)
- else:
- path = None
- return results.download(path)
-
- if isinstance(results, (list, tuple)):
- return [self._download(x, targets) for x in results]
-
- if isinstance(results, dict):
-
- if "location" in results and "contentLength" in results:
- reply = dict(
- location=results["location"],
- content_length=results["contentLength"],
- content_type=results.get("contentType"),
- )
-
- if targets:
- path = targets.pop(0)
- else:
- path = None
-
- return Result(self, reply).download(path)
-
- r = {}
- for k, v in results.items():
- r[v] = self._download(v, targets)
- return r
-
- return results
-
- def download(self, results, targets=None):
- if targets:
- # Make a copy
- targets = [t for t in targets]
- return self._download(results, targets)
-
- def remote(self, url):
- r = requests.head(url)
- reply = dict(
- location=url,
- content_length=r.headers["Content-Length"],
- content_type=r.headers["Content-Type"],
- )
- return Result(self, reply)
-
- def robust(self, call):
- def retriable(code, reason):
-
- if code in [
- requests.codes.internal_server_error,
- requests.codes.bad_gateway,
- requests.codes.service_unavailable,
- requests.codes.gateway_timeout,
- requests.codes.too_many_requests,
- requests.codes.request_timeout,
- ]:
- return True
-
- return False
-
- def wrapped(*args, **kwargs):
- tries = 0
- while tries < self.retry_max:
- try:
- r = call(*args, **kwargs)
- except (
- requests.exceptions.ConnectionError,
- requests.exceptions.ReadTimeout,
- ) as e:
- r = None
- self.warning(
- "Recovering from connection error [%s], attemps %s of %s",
- e,
- tries,
- self.retry_max,
- )
-
- if r is not None:
- if not retriable(r.status_code, r.reason):
- return r
- try:
- self.warning(r.json()["reason"])
- except Exception:
- pass
- self.warning(
- "Recovering from HTTP error [%s %s], attemps %s of %s",
- r.status_code,
- r.reason,
- tries,
- self.retry_max,
- )
-
- tries += 1
-
- self.warning("Retrying in %s seconds", self.sleep_max)
- time.sleep(self.sleep_max)
- self.info("Retrying now...")
-
- return wrapped
diff --git a/example-era5.py b/example-era5.py
deleted file mode 100755
index db42268..0000000
--- a/example-era5.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-
-# (C) Copyright 2018 ECMWF.
-#
-# This software is licensed under the terms of the Apache Licence Version 2.0
-# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
-# In applying this licence, ECMWF does not waive the privileges and immunities
-# granted to it by virtue of its status as an intergovernmental organisation nor
-# does it submit to any jurisdiction.
-
-import cdsapi
-
-c = cdsapi.Client()
-
-r = c.retrieve(
- "reanalysis-era5-single-levels",
- {
- "variable": "2t",
- "product_type": "reanalysis",
- "date": "2012-12-01",
- "time": "14:00",
- "format": "netcdf",
- },
-)
-
-r.download("test.nc")
diff --git a/example-glaciers.py b/example-glaciers.py
deleted file mode 100755
index 538f9f6..0000000
--- a/example-glaciers.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env python
-
-# (C) Copyright 2018 ECMWF.
-#
-# This software is licensed under the terms of the Apache Licence Version 2.0
-# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
-# In applying this licence, ECMWF does not waive the privileges and immunities
-# granted to it by virtue of its status as an intergovernmental organisation nor
-# does it submit to any jurisdiction.
-
-import cdsapi
-
-c = cdsapi.Client()
-
-c.retrieve(
- "insitu-glaciers-elevation-mass",
- {"variable": "elevation_change", "format": "tgz"},
- "dowload.data",
-)
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index fcab8c2..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,22 +0,0 @@
-[bdist_wheel]
-universal = 1
-
-[aliases]
-test = pytest
-
-[tool:pytest]
-norecursedirs =
- build
- dist
- .tox
- .eggs
-pep8maxlinelength = 109
-mccabe-complexity = 10
-
-[coverage:run]
-branch = True
-
-[egg_info]
-tag_build =
-tag_date = 0
-
diff --git a/setup.py b/setup.py
deleted file mode 100644
index a8f746a..0000000
--- a/setup.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright 2018 European Centre for Medium-Range Weather Forecasts (ECMWF)
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# In applying this licence, ECMWF does not waive the privileges and immunities
-# granted to it by virtue of its status as an intergovernmental organisation nor
-# does it submit to any jurisdiction.
-
-
-import io
-import os.path
-
-import setuptools
-
-
-def read(fname):
- file_path = os.path.join(os.path.dirname(__file__), fname)
- return io.open(file_path, encoding="utf-8").read()
-
-
-version = "0.4.0"
-
-
-setuptools.setup(
- name="cdsapi",
- version=version,
- author="ECMWF",
- author_email="software.support@ecmwf.int",
- license="Apache 2.0",
- url="https://software.ecmwf.int/stash/projects/CDS/repos/cdsapi",
- description="Climate Data Store API",
- long_description=read("README.rst"),
- packages=setuptools.find_packages(),
- include_package_data=True,
- install_requires=[
- "requests>=2.5.0",
- "tqdm",
- ],
- zip_safe=True,
- classifiers=[
- "Development Status :: 3 - Alpha",
- "Intended Audience :: Developers",
- "Programming Language :: Python",
- "Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.7",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.4",
- "Programming Language :: Python :: 3.5",
- "Programming Language :: Python :: 3.6",
- "Programming Language :: Python :: Implementation :: CPython",
- "Programming Language :: Python :: Implementation :: PyPy",
- "Operating System :: OS Independent",
- ],
-)
diff --git a/tests/requirements.txt b/tests/requirements.txt
deleted file mode 100644
index ad28cc6..0000000
--- a/tests/requirements.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-pytest-cov
-pytest-env
-pytest-flakes
-pytest-mccabe
-pytest-pep8
-pytest-runner
-pytest
-requests
diff --git a/tests/test_api.py b/tests/test_api.py
deleted file mode 100644
index c3f3766..0000000
--- a/tests/test_api.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import cdsapi
-import os
-
-def test_request():
-
- c = cdsapi.Client()
-
- r = c.retrieve(
- "reanalysis-era5-single-levels",
- {
- "variable": "2t",
- "product_type": "reanalysis",
- "date": "2012-12-01",
- "time": "12:00"
- },
- )
-
- r.download("test.grib")
-
- assert os.path.getsize("test.grib") == 2076600
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 1d50849..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,16 +0,0 @@
-[tox]
-envlist = qc, py36, py35, py34, py27, pypy3, pypy, deps
-
-[testenv]
-setenv = PYTHONPATH = {toxinidir}
-deps = -r{toxinidir}/tests/requirements-tests.txt
-commands = pytest -v --flakes --cache-clear --basetemp={envtmpdir} {posargs}
-
-[testenv:qc]
-# needed for pytest-cov
-usedevelop = true
-commands = pytest -v --pep8 --mccabe --cov=cdsapi --cov-report=html --cache-clear {posargs}
-
-[testenv:deps]
-deps =
-commands = python setup.py test