Skip to content

Commit c2fbec7

Browse files
authored
Merge pull request #636 from huard/fix-635
Create output directory before estimating available space on device
2 parents 626e2cf + afdb9a6 commit c2fbec7

File tree

2 files changed

+49
-21
lines changed

2 files changed

+49
-21
lines changed

pywps/inout/storage/file.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ def __init__(self, output_path, output_url, copy_function=None):
6969
self.copy_function = copy_function
7070

7171
def _do_store(self, output):
72+
"""Copy output to final storage location.
73+
74+
- Create output directory
75+
- Check available file space
76+
- Create output file name, taking care of possible duplicates
77+
- Copy / link output in work directory to output directory
78+
- Return store type, output path and output URL
79+
"""
7280
import platform
7381
import math
7482
import tempfile
@@ -78,6 +86,11 @@ def _do_store(self, output):
7886

7987
request_uuid = output.uuid or uuid.uuid1()
8088

89+
# Create a target folder for each request
90+
target = os.path.join(self.target, str(request_uuid))
91+
if not os.path.exists(target):
92+
os.makedirs(target)
93+
8194
# st.blksize is not available in windows, skips the validation on windows
8295
if platform.system() != 'Windows':
8396
file_block_size = os.stat(file_name).st_blksize
@@ -91,11 +104,6 @@ def _do_store(self, output):
91104
if avail_size < actual_file_size:
92105
raise NotEnoughStorage('Not enough space in {} to store {}'.format(self.target, file_name))
93106

94-
# create a target folder for each request
95-
target = os.path.join(self.target, str(request_uuid))
96-
if not os.path.exists(target):
97-
os.makedirs(target)
98-
99107
# build output name
100108
output_name, suffix = _build_output_name(output)
101109
# build tempfile in case of duplicates
@@ -116,7 +124,7 @@ def _do_store(self, output):
116124
url = self.url("{}/{}".format(request_uuid, just_file_name))
117125
LOGGER.info('File output URI: {}'.format(url))
118126

119-
return (STORE_TYPE.PATH, output_name, url)
127+
return STORE_TYPE.PATH, output_name, url
120128

121129
@staticmethod
122130
def copy(src, dst, copy_function=None):
@@ -134,7 +142,7 @@ def copy(src, dst, copy_function=None):
134142
try:
135143
os.link(src, dst)
136144
except Exception:
137-
LOGGER.warn("Could not create hardlink. Fallback to copy.")
145+
LOGGER.warning("Could not create hardlink. Fallback to copy.")
138146
FileStorage.copy(src, dst)
139147
else:
140148
shutil.copy2(src, dst)

tests/test_storage.py

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,53 @@
22
# Copyright 2018 Open Source Geospatial Foundation and others #
33
# licensed under MIT, Please consult LICENSE.txt for details #
44
##################################################################
5+
import pytest
56

67
from pywps.inout.storage.builder import StorageBuilder
78
from pywps.inout.storage.file import FileStorage
89
from pywps.inout.storage.s3 import S3Storage
910

1011
from pywps import configuration
1112

13+
from pathlib import Path
1214
import unittest
15+
import tempfile
1316

14-
class StorageBuilderTests(unittest.TestCase):
17+
18+
@pytest.fixture
19+
def fake_output(tmp_path):
20+
class FakeOutput(object):
21+
"""Fake output object for testing."""
22+
def __init__(self):
23+
self.identifier = "fake_output"
24+
self.file = self._get_file()
25+
self.uuid = None
26+
27+
def _get_file(self):
28+
fn = tmp_path / 'file.tiff'
29+
fn.touch()
30+
return str(fn.absolute())
31+
32+
return FakeOutput()
33+
34+
35+
class TestStorageBuilder():
1536

1637
def test_default_storage(self):
1738
storage = StorageBuilder.buildStorage()
18-
self.assertIsInstance(storage, FileStorage)
19-
39+
assert isinstance(storage, FileStorage)
2040

2141
def test_s3_storage(self):
2242
configuration.CONFIG.set('server', 'storagetype', 's3')
2343
storage = StorageBuilder.buildStorage()
24-
self.assertIsInstance(storage, S3Storage)
25-
26-
def load_tests(loader=None, tests=None, pattern=None):
27-
"""Load local tests
28-
"""
29-
if not loader:
30-
loader = unittest.TestLoader()
31-
suite_list = [
32-
loader.loadTestsFromTestCase(StorageBuilderTests)
33-
]
34-
return unittest.TestSuite(suite_list)
44+
assert isinstance(storage, S3Storage)
45+
46+
def test_recursive_directory_creation(self, fake_output):
47+
"""Test that outputpath is created."""
48+
configuration.CONFIG.set('server', 'storagetype', 'file')
49+
outputpath = Path(tempfile.gettempdir()) / "a" / "b" / "c"
50+
configuration.CONFIG.set('server', 'outputpath', str(outputpath))
51+
storage = StorageBuilder.buildStorage()
52+
53+
storage.store(fake_output)
54+
assert outputpath.exists()

0 commit comments

Comments
 (0)