Skip to content

Commit 1d36970

Browse files
committed
Fix zip operation to correctly interpret code gen results
1 parent e8e92dc commit 1d36970

File tree

4 files changed

+76
-120
lines changed

4 files changed

+76
-120
lines changed

servicex_codegen/code_generator.py

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@
3232
# modification, are permitted provided that the following conditions are met:
3333
#
3434
from abc import ABC, abstractmethod
35-
import os
36-
import zipfile
3735
from collections import namedtuple
38-
from tempfile import TemporaryDirectory
3936

4037
GeneratedFileResult = namedtuple('GeneratedFileResult', 'hash output_dir')
4138

@@ -48,41 +45,7 @@ def __init__(self, message: str):
4845

4946

5047
class CodeGenerator(ABC):
51-
def zipdir(self, path: str, zip_handle: zipfile.ZipFile) -> None:
52-
"""Given a `path` to a directory, zip up its contents into a zip file.
53-
54-
Arguments:
55-
path Path to a local directory. The contents will be put into the zip file
56-
zip_handle The zip file handle to write into.
57-
"""
58-
for root, _, files in os.walk(path):
59-
for file in files:
60-
zip_handle.write(os.path.join(root, file), file)
6148

6249
@abstractmethod
63-
def generate_code(self, query, cache_path: str):
50+
def generate_code(self, query, cache_path: str):
6451
pass
65-
66-
def translate_query_to_zip(self, query: str) -> bytes:
67-
"""Translate a text ast into a zip file as a memory stream
68-
69-
Arguments:
70-
code Text `qastle` version of the input ast generated by func_adl
71-
72-
Returns
73-
bytes Data that if written as a binary output would be a zip file.
74-
"""
75-
76-
# Generate the python code
77-
with TemporaryDirectory() as tempdir:
78-
r = self.generate_code(query, tempdir)
79-
80-
# Zip up everything in the directory - we are going to ship it as back as part
81-
# of the message.
82-
z_filename = os.path.join(str(tempdir), 'joined.zip')
83-
zip_h = zipfile.ZipFile(z_filename, 'w', zipfile.ZIP_DEFLATED)
84-
self.zipdir(r.output_dir, zip_h)
85-
zip_h.close()
86-
87-
with open(z_filename, 'rb') as b_in:
88-
return b_in.read()

servicex_codegen/post_operation.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@
3131
# Redistribution and use in source and binary forms, with or without
3232
# modification, are permitted provided that the following conditions are met:
3333
#
34+
import os
35+
import zipfile
3436
from tempfile import TemporaryDirectory
3537

3638
from flask import request, Response
3739
from flask_restful import Resource
38-
from servicex_codegen.code_generator import CodeGenerator
40+
from servicex_codegen.code_generator import CodeGenerator, GeneratedFileResult
3941

4042

4143
class GeneratedCode(Resource):
@@ -44,12 +46,47 @@ def make_api(cls, code_generator: CodeGenerator):
4446
cls.code_generator = code_generator
4547
return cls
4648

49+
def zipdir(self, path: str, zip_handle: zipfile.ZipFile) -> None:
50+
"""Given a `path` to a directory, zip up its contents into a zip file.
51+
52+
Arguments:
53+
path Path to a local directory. The contents will be put into the zip file
54+
zip_handle The zip file handle to write into.
55+
"""
56+
for root, _, files in os.walk(path):
57+
for file in files:
58+
zip_handle.write(os.path.join(root, file), file)
59+
60+
def stream_generated_code(self, generated_code_result: GeneratedFileResult) -> bytes:
61+
"""Translate a text ast into a zip file as a memory stream
62+
63+
Arguments:
64+
code Text `qastle` version of the input ast generated by func_adl
65+
66+
Returns
67+
bytes Data that if written as a binary output would be a zip file.
68+
"""
69+
70+
# Generate the python code
71+
with TemporaryDirectory() as tempdir:
72+
73+
# Zip up everything in the directory - we are going to ship it as back as part
74+
# of the message.
75+
z_filename = os.path.join(str(tempdir), 'joined.zip')
76+
zip_h = zipfile.ZipFile(z_filename, 'w', zipfile.ZIP_DEFLATED)
77+
self.zipdir(generated_code_result.output_dir, zip_h)
78+
zip_h.close()
79+
80+
with open(z_filename, 'rb') as b_in:
81+
return b_in.read()
82+
4783
def post(self):
4884
try:
4985
with TemporaryDirectory() as tempdir:
5086
code = request.data.decode('utf8')
51-
zip_data = self.code_generator.generate_code(code, cache_path=tempdir)
87+
generated_code_result = self.code_generator.generate_code(code, cache_path=tempdir)
5288

89+
zip_data = self.stream_generated_code(generated_code_result)
5390
# Send the response back to you-know-what.
5491
response = Response(
5592
response=zip_data,

tests/test_code_generator.py

Lines changed: 0 additions & 70 deletions
This file was deleted.

tests/test_post_operation.py

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,53 @@
2525
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2626
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2727
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
import io
29+
import os
30+
import zipfile
31+
from tempfile import TemporaryDirectory
2832

2933
from servicex_codegen import create_app
30-
from servicex_codegen.code_generator import GenerateCodeException
34+
from servicex_codegen.code_generator import GenerateCodeException, GeneratedFileResult
35+
36+
37+
def get_zipfile_data(zip_data: bytes):
38+
with zipfile.ZipFile(io.BytesIO(zip_data)) as thezip:
39+
for zipinfo in thezip.infolist():
40+
with thezip.open(zipinfo) as thefile:
41+
yield zipinfo.filename, thefile
42+
43+
44+
def check_zip_file(zip_data: bytes, expected_file_count):
45+
names = []
46+
for name, data in get_zipfile_data(zip_data):
47+
names.append(name)
48+
print(name)
49+
assert len(names) == expected_file_count
3150

3251

3352
class TestPostOperation:
3453
def test_post_good_query(self, mocker):
3554
"""Produce code for a simple good query"""
3655

37-
mock_ast_translator = mocker.Mock()
38-
mock_ast_translator.generate_code = mocker.Mock(return_value="hi")
56+
with TemporaryDirectory() as tempdir, \
57+
open(os.path.join(tempdir, "baz.txt"), 'w'),\
58+
open(os.path.join(tempdir, "foo.txt"), 'w'):
3959

40-
config = {
41-
'TARGET_BACKEND': 'uproot'
42-
}
43-
app = create_app(config, provided_translator=mock_ast_translator)
44-
client = app.test_client()
45-
select_stmt = "(call ResultTTree (call Select (call SelectMany (call EventDataset (list 'localds://did_01')" # noqa: E501
60+
mock_ast_translator = mocker.Mock()
61+
mock_ast_translator.generate_code = mocker.Mock(
62+
return_value=GeneratedFileResult(hash="1234", output_dir=tempdir)
63+
)
4664

47-
response = client.post("/servicex/generated-code", data=select_stmt)
65+
config = {
66+
'TARGET_BACKEND': 'uproot'
67+
}
68+
app = create_app(config, provided_translator=mock_ast_translator)
69+
client = app.test_client()
70+
select_stmt = "(call ResultTTree (call Select (call SelectMany (call EventDataset (list 'localds://did_01')" # noqa: E501
71+
72+
response = client.post("/servicex/generated-code", data=select_stmt)
4873
assert response.status_code == 200
74+
check_zip_file(response.data, 2)
4975
# Capture the temporary directory that was generated
5076
cache_dir = mock_ast_translator.generate_code.call_args[1]['cache_path']
5177
mock_ast_translator.generate_code.assert_called_with(select_stmt,

0 commit comments

Comments
 (0)