File: test_downloaders.py

package info (click to toggle)
python-cartopy 0.21.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 14,668 kB
  • sloc: python: 15,101; makefile: 166; javascript: 66; sh: 6
file content (178 lines) | stat: -rw-r--r-- 6,758 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# Copyright Cartopy Contributors
#
# This file is part of Cartopy and is released under the LGPL license.
# See COPYING and COPYING.LESSER in the root of the repository for full
# licensing details.

import contextlib
import os
from unittest import mock

import pytest

import cartopy
import cartopy.io as cio
from cartopy.io.shapereader import NEShpDownloader


def test_Downloader_data():
    di = cio.Downloader('https://testing.com/{category}/{name}.zip',
                        os.path.join('{data_dir}', '{category}',
                                     'shape.shp'),
                        '/project/foobar/{category}/sample.shp')

    replacement_dict = {'category': 'example',
                        'name': 'test',
                        'data_dir': os.path.join('/wibble', 'foo', 'bar')}

    assert di.url(replacement_dict) == 'https://testing.com/example/test.zip'

    assert (di.target_path(replacement_dict) ==
            os.path.join('/wibble', 'foo', 'bar', 'example', 'shape.shp'))

    assert (di.pre_downloaded_path(replacement_dict) ==
            '/project/foobar/example/sample.shp')


@contextlib.contextmanager
def config_replace(replacement_dict):
    """
    Provides a context manager to replace the ``cartopy.config['downloaders']``
    dict with the given dictionary. Great for testing purposes!

    """
    with contextlib.ExitStack() as stack:
        stack.callback(cartopy.config.__setitem__, 'downloaders',
                       cartopy.config['downloaders'])
        cartopy.config['downloaders'] = replacement_dict

        yield


@pytest.fixture
def download_to_temp(tmp_path_factory):
    """
    Context manager which defaults the "data_dir" to a temporary directory
    which is automatically cleaned up on exit.

    """
    with contextlib.ExitStack() as stack:
        stack.callback(cartopy.config.__setitem__, 'downloaders',
                       cartopy.config['downloaders'].copy())
        stack.callback(cartopy.config.__setitem__, 'data_dir',
                       cartopy.config['data_dir'])

        tmp_dir = tmp_path_factory.mktemp('cartopy_data')
        cartopy.config['data_dir'] = str(tmp_dir)

        yield tmp_dir


def test_from_config():
    generic_url = 'https://example.com/generic_ne/{name}.zip'

    land_downloader = cio.Downloader(generic_url, '', '')
    generic_ne_downloader = cio.Downloader(generic_url, '', '')

    ocean_spec = ('shapefile', 'natural_earth', '110m', 'physical', 'ocean')
    land_spec = ('shapefile', 'natural_earth', '110m', 'physical', 'land')
    generic_spec = ('shapefile', 'natural_earth')

    target_config = {land_spec: land_downloader,
                     generic_spec: generic_ne_downloader,
                     }

    with config_replace(target_config):
        # ocean spec is not explicitly in the config, but a subset of it is,
        # so check that an appropriate downloader is returned
        r = cio.Downloader.from_config(ocean_spec)

        # check the resulting download item produces a sensible url.
        assert (r.url({'name': 'ocean'}) ==
                'https://example.com/generic_ne/ocean.zip')

        r = cio.Downloader.from_config(land_spec)
        assert r is land_downloader


@pytest.mark.network
def test_downloading_simple_ascii(download_to_temp):
    # downloads a file from the Google APIs. (very high uptime and file will
    # always be there - if this goes down, most of the internet would break!)
    # to test the downloading mechanisms.
    file_url = 'https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/{name}.js'

    format_dict = {'name': 'jquery'}

    target_template = str(download_to_temp / '{name}.txt')
    tmp_fname = target_template.format(**format_dict)

    dnld_item = cio.Downloader(file_url, target_template)

    assert dnld_item.target_path(format_dict) == tmp_fname

    with pytest.warns(cio.DownloadWarning):
        assert dnld_item.path(format_dict) == tmp_fname

    with open(tmp_fname) as fh:
        fh.readline()
        assert fh.readline() == " * jQuery JavaScript Library v1.8.2\n"

    # check that calling path again doesn't try re-downloading
    with mock.patch.object(dnld_item, 'acquire_resource',
                           wraps=dnld_item.acquire_resource) as counter:
        assert dnld_item.path(format_dict) == tmp_fname
    counter.assert_not_called()


@pytest.mark.network
def test_natural_earth_downloader(tmp_path):
    # downloads a file to a temporary location, and uses that temporary
    # location, then:
    #   * Checks that the file is only downloaded once even when calling
    #     multiple times
    #   * Checks that shapefiles have all the necessary files when downloaded
    #   * Checks that providing a path in a download item gets used rather
    #     than triggering another download
    shp_path_template = str(tmp_path / '{category}_{resolution}_{name}.shp')

    # picking a small-ish file to speed up download times, the file itself
    # isn't important - it is the download mechanism that is.
    format_dict = {'category': 'physical',
                   'name': 'rivers_lake_centerlines',
                   'resolution': '110m'}

    dnld_item = NEShpDownloader(target_path_template=shp_path_template)

    # check that the file gets downloaded the first time path is called
    with mock.patch.object(dnld_item, 'acquire_resource',
                           wraps=dnld_item.acquire_resource) as counter:
        with pytest.warns(cartopy.io.DownloadWarning, match="Downloading:"):
            shp_path = dnld_item.path(format_dict)
    counter.assert_called_once()

    assert shp_path_template.format(**format_dict) == shp_path

    # check that calling path again doesn't try re-downloading
    with mock.patch.object(dnld_item, 'acquire_resource',
                           wraps=dnld_item.acquire_resource) as counter:
        assert dnld_item.path(format_dict) == shp_path
    counter.assert_not_called()

    # check that we have the shp and the shx
    exts = ['.shp', '.shx']
    for ext in exts:
        stem = os.path.splitext(shp_path)[0]
        assert os.path.exists(stem + ext), \
            f"Shapefile's {ext} file doesn't exist in {stem}{ext}"

    # check that providing a pre downloaded path actually works
    pre_dnld = NEShpDownloader(target_path_template='/not/a/real/file.txt',
                               pre_downloaded_path_template=shp_path)

    # check that the pre_dnld downloader doesn't re-download, but instead
    # uses the path of the previously downloaded item
    with mock.patch.object(pre_dnld, 'acquire_resource',
                           wraps=pre_dnld.acquire_resource) as counter:
        assert pre_dnld.path(format_dict) == shp_path
    counter.assert_not_called()