Skip to content

Commit ef3958d

Browse files
committed
#55 Fixed the empty input bug
1 parent 13fa38a commit ef3958d

File tree

5 files changed

+41
-14
lines changed

5 files changed

+41
-14
lines changed

doc/changes/unreleased.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@ Code name: tbd
66

77
### Bugs
88

9-
n/a
9+
* #55: Not possible to create StandaloneMockContext with empty input list
10+
11+
### Features
12+
13+
* #56: Made script_code_wrapper_function parameter of the MockMetaData optional.

exasol_udf_mock_python/mock_context.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import List, Tuple, Iterator, Iterable, Any, Optional, Union
2+
from collections.abc import Sized
23
from functools import wraps
34

45
import pandas as pd
@@ -133,24 +134,30 @@ def emit(self, *args) -> None:
133134
self._current_context.emit(*args)
134135

135136

136-
def get_scalar_input(inp: Any) -> Iterable[Tuple[Any, ...]]:
137+
def get_scalar_input(inp: Any) -> Iterable[Iterable[Any]]:
137138
"""
138139
Figures out if the SCALAR parameters are provided as a scalar value or a tuple
139140
and also if there is a wrapping container around.
140-
Unless the parameters are already in a wrapping container returns parameters as a tuple wrapped
141-
into a one-item list, e.g [(param1[, param2, ...)]. Otherwise, returns the original input.
141+
Unless the parameters are already in a wrapping Sized container, returns parameters as an iterable
142+
wrapped into a one-item list, e.g [(param1, [param2, ...])]. Otherwise, returns the original input.
142143
143144
:param inp: Input parameters.
144145
"""
145146

146-
if isinstance(inp, Iterable) and not isinstance(inp, str):
147-
row1 = next(iter(inp))
148-
if isinstance(row1, Iterable) and not isinstance(row1, str):
149-
return inp
150-
else:
151-
return [inp]
152-
else:
153-
return [(inp,)]
147+
if inp is not None:
148+
if (not isinstance(inp, Iterable)) or isinstance(inp, str):
149+
return [(inp,)]
150+
try:
151+
row1 = next(iter(inp))
152+
if (not isinstance(row1, Iterable)) or isinstance(row1, str):
153+
return [inp]
154+
elif not isinstance(inp, Sized):
155+
return list(inp)
156+
else:
157+
return inp
158+
except StopIteration:
159+
pass
160+
return [tuple()]
154161

155162

156163
class StandaloneMockContext(UDFContext):

exasol_udf_mock_python/mock_meta_data.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ def __init__(
4242
self._current_user = current_user
4343
self._current_schema = current_schema
4444
self._scope_user = scope_user
45-
self._script_code = self._extract_script_code(script_code_wrapper_function)
45+
self._script_code = (None if script_code_wrapper_function is None
46+
else self._extract_script_code(script_code_wrapper_function))
4647
self._connection_id = connection_id
4748
self._database_name = database_name
4849
self._database_version = database_version

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ keywords = ['exasol', 'udf', 'mock', 'testing']
1919
[tool.poetry.dependencies]
2020
python = "^3.8"
2121
pandas = "^1.4"
22-
numpy = "^1.22"
22+
numpy = ">=1.26.0"
2323
dill = ">=0.3.7"
2424

2525
[tool.poetry.dev-dependencies]

tests/test_mock_context_standalone.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ def test_attr_scalar(context_scalar_return):
6363
assert context_scalar_return.t == 5
6464

6565

66+
@pytest.mark.parametrize('inp', [None, [], [[]]])
67+
def test_context_empty_input(inp):
68+
meta_data = MockMetaData(
69+
script_code_wrapper_function=udf_wrapper,
70+
input_type='SCALAR',
71+
input_columns=[],
72+
output_type='RETURNS',
73+
output_columns=[Column('t', int, 'INTEGER')]
74+
)
75+
_ = StandaloneMockContext(inp, meta_data)
76+
# There is nothing to test here apart from successful creation of the
77+
# context object. This internally has some checks that need to be passed.
78+
pass
79+
80+
6681
def test_next(context_set_emits):
6782
assert context_set_emits.next()
6883
assert context_set_emits.t1 == 6

0 commit comments

Comments
 (0)