Skip to content

Commit 5693ea7

Browse files
committed
Implemented Dict type for return in python loader.
1 parent 590fc30 commit 5693ea7

File tree

10 files changed

+414
-5
lines changed

10 files changed

+414
-5
lines changed

source/loaders/CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ option(OPTION_BUILD_LOADERS_JSM "Build JavaScript SpiderMonkey 4.8 loader plugin
2929
option(OPTION_BUILD_LOADERS_JS "Build JavaScript V8 5.1+ loader plugin." OFF)
3030
option(OPTION_BUILD_LOADERS_MOCK "Build mock loader loader plugin." ON)
3131
option(OPTION_BUILD_LOADERS_NODE "Build NodeJS 8.11.1 JavaScript Runtime loader plugin." OFF)
32-
option(OPTION_BUILD_LOADERS_PY "Build Python 3.5 C API loader plugin." OFF)
33-
option(OPTION_BUILD_LOADERS_RB "Build Ruby 2.3 C API loader plugin." OFF)
32+
option(OPTION_BUILD_LOADERS_PY "Build Python 3.7 C API loader plugin." OFF)
33+
option(OPTION_BUILD_LOADERS_RB "Build Ruby 2.5 C API loader plugin." OFF)
3434
option(OPTION_BUILD_LOADERS_RPC "Build cURL Remote Procedure Call loader plugin." OFF)
3535
option(OPTION_BUILD_LOADERS_WASM "Build WebAssembly Virtual Machine loader plugin." OFF)
3636

@@ -41,8 +41,8 @@ add_subdirectory(jsm_loader) # Mozilla's SpiderMonkey 4.8 JavaScript Engine
4141
add_subdirectory(js_loader) # Google's V8 5.1 JavaScript Engine
4242
add_subdirectory(node_loader) # NodeJS 8.11.1 JavaScript Runtime
4343
add_subdirectory(mock_loader) # Mock loader plugin without dependencies (core testing)
44-
add_subdirectory(py_loader) # Python 3.5 C API
45-
add_subdirectory(rb_loader) # Ruby 2.3 C API
44+
add_subdirectory(py_loader) # Python 3.7 C API
45+
add_subdirectory(rb_loader) # Ruby 2.5 C API
4646
add_subdirectory(file_loader) # File System
4747
add_subdirectory(rpc_loader) # cURL Remote Procedure Call
4848
add_subdirectory(wasm_loader) # WebAssembly Virtual Machine

source/loaders/py_loader/source/py_loader_impl.c

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ type_id py_loader_impl_get_return_type(PyObject * result)
186186
{
187187
return TYPE_ARRAY;
188188
}
189+
else if (PyDict_Check(result))
190+
{
191+
return TYPE_MAP;
192+
}
189193
else if (PyCapsule_CheckExact(result))
190194
{
191195
return TYPE_PTR;
@@ -297,6 +301,86 @@ value py_loader_impl_return(PyObject * result, type_id id)
297301
array_value[iterator] = py_loader_impl_return(element, py_loader_impl_get_return_type(element));
298302
}
299303
}
304+
else if (id == TYPE_MAP)
305+
{
306+
Py_ssize_t key_iterator, iterator, keys_size, length = 0;
307+
value * map_value;
308+
PyObject * keys;
309+
310+
keys = PyDict_Keys(result);
311+
keys_size = PyList_Size(keys);
312+
313+
for (iterator = 0; iterator < keys_size; ++iterator)
314+
{
315+
PyObject * key = PyList_GetItem(keys, iterator);
316+
317+
#if PY_MAJOR_VERSION == 2
318+
if (PyString_Check(key))
319+
{
320+
++length;
321+
}
322+
#elif PY_MAJOR_VERSION == 3
323+
if (PyUnicode_Check(key))
324+
{
325+
++length;
326+
}
327+
#endif
328+
}
329+
330+
v = value_create_map(NULL, (size_t)length);
331+
332+
map_value = value_to_map(v);
333+
334+
for (iterator = 0, key_iterator = 0; iterator < keys_size; ++iterator)
335+
{
336+
char * key_str = NULL;
337+
338+
Py_ssize_t key_length = 0;
339+
340+
PyObject * element, * key;
341+
342+
value * array_value;
343+
344+
key = PyList_GetItem(keys, iterator);
345+
346+
#if PY_MAJOR_VERSION == 2
347+
if (PyString_Check(key))
348+
{
349+
if (PyString_AsStringAndSize(key, &key_str, &key_length) == -1)
350+
{
351+
if (PyErr_Occurred() != NULL)
352+
{
353+
loader_impl_py py_impl = loader_impl_get(py_func->impl);
354+
355+
py_loader_impl_error_print(py_impl);
356+
}
357+
}
358+
}
359+
#elif PY_MAJOR_VERSION == 3
360+
if (PyUnicode_Check(key))
361+
{
362+
key_str = PyUnicode_AsUTF8AndSize(key, &key_length);
363+
}
364+
#endif
365+
366+
/* Allow only string keys by the moment */
367+
if (key_str != NULL)
368+
{
369+
element = PyDict_GetItem(result, key);
370+
371+
map_value[key_iterator] = value_create_array(NULL, 2);
372+
373+
array_value = value_to_array(map_value[key_iterator]);
374+
375+
array_value[0] = value_create_string(key_str, (size_t)key_length);
376+
377+
/* TODO: Review recursion overflow */
378+
array_value[1] = py_loader_impl_return(element, py_loader_impl_get_return_type(element));
379+
380+
++key_iterator;
381+
}
382+
}
383+
}
300384
else if (id == TYPE_PTR)
301385
{
302386
void * ptr = NULL;
@@ -643,7 +727,8 @@ int py_loader_impl_initialize_inspect_types(loader_impl impl, loader_impl_py py_
643727

644728
{ TYPE_STRING, "str" },
645729
{ TYPE_BUFFER, "bytes" },
646-
{ TYPE_ARRAY, "list" }
730+
{ TYPE_ARRAY, "list" },
731+
{ TYPE_MAP, "dict" }
647732
};
648733

649734
size_t index, size = sizeof(type_id_name_pair) / sizeof(type_id_name_pair[0]);

source/scripts/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ add_subdirectory(web)
2626
add_subdirectory(landing)
2727
add_subdirectory(model)
2828
add_subdirectory(pointer)
29+
add_subdirectory(dicty)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#
2+
# Configure python project
3+
#
4+
5+
py_project(dicty 0.1.0)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python3
2+
3+
import dicty
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env python3
2+
3+
def nice_dict():
4+
return { 'hello': 'world', 'asd': 123, 'efg': 3.4 };
5+
6+
def non_supported_dict():
7+
return { 3244: 'world', 'asd': 123 };

source/tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ add_subdirectory(metacall_clear_test)
131131
add_subdirectory(metacall_python_class_test)
132132
add_subdirectory(metacall_python_gc_test)
133133
add_subdirectory(metacall_python_open_test)
134+
add_subdirectory(metacall_python_dict_test)
134135
add_subdirectory(metacall_python_model_test)
135136
add_subdirectory(metacall_python_pointer_test)
136137
add_subdirectory(metacall_map_test)
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Check if this loader is enabled
2+
if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY OR NOT OPTION_BUILD_LOADERS_NODE OR NOT OPTION_BUILD_LOADERS_CS OR NOT OPTION_BUILD_SCRIPTS OR NOT OPTION_BUILD_SCRIPTS_PY OR NOT OPTION_BUILD_PORTS OR NOT OPTION_BUILD_PORTS_PY)
3+
return()
4+
endif()
5+
6+
#
7+
# Executable name and options
8+
#
9+
10+
# Target name
11+
set(target metacall-python-dict-test)
12+
message(STATUS "Test ${target}")
13+
14+
#
15+
# Compiler warnings
16+
#
17+
18+
include(Warnings)
19+
20+
#
21+
# Compiler security
22+
#
23+
24+
include(SecurityFlags)
25+
26+
#
27+
# Sources
28+
#
29+
30+
set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}")
31+
set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source")
32+
33+
set(sources
34+
${source_path}/main.cpp
35+
${source_path}/metacall_python_dict_test.cpp
36+
)
37+
38+
# Group source files
39+
set(header_group "Header Files (API)")
40+
set(source_group "Source Files")
41+
source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$"
42+
${header_group} ${headers})
43+
source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$"
44+
${source_group} ${sources})
45+
46+
#
47+
# Create executable
48+
#
49+
50+
# Build executable
51+
add_executable(${target}
52+
${sources}
53+
)
54+
55+
# Create namespaced alias
56+
add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target})
57+
58+
#
59+
# Project options
60+
#
61+
62+
set_target_properties(${target}
63+
PROPERTIES
64+
${DEFAULT_PROJECT_OPTIONS}
65+
FOLDER "${IDE_FOLDER}"
66+
)
67+
68+
#
69+
# Include directories
70+
#
71+
72+
target_include_directories(${target}
73+
PRIVATE
74+
${DEFAULT_INCLUDE_DIRECTORIES}
75+
${PROJECT_BINARY_DIR}/source/include
76+
)
77+
78+
#
79+
# Libraries
80+
#
81+
82+
target_link_libraries(${target}
83+
PRIVATE
84+
${DEFAULT_LIBRARIES}
85+
86+
GTest
87+
88+
${META_PROJECT_NAME}::version
89+
${META_PROJECT_NAME}::preprocessor
90+
${META_PROJECT_NAME}::environment
91+
${META_PROJECT_NAME}::format
92+
${META_PROJECT_NAME}::log
93+
${META_PROJECT_NAME}::memory
94+
${META_PROJECT_NAME}::adt
95+
${META_PROJECT_NAME}::reflect
96+
${META_PROJECT_NAME}::dynlink
97+
${META_PROJECT_NAME}::detour
98+
${META_PROJECT_NAME}::serial
99+
${META_PROJECT_NAME}::configuration
100+
${META_PROJECT_NAME}::loader
101+
${META_PROJECT_NAME}::metacall
102+
)
103+
104+
#
105+
# Compile definitions
106+
#
107+
108+
target_compile_definitions(${target}
109+
PRIVATE
110+
${DEFAULT_COMPILE_DEFINITIONS}
111+
)
112+
113+
#
114+
# Compile options
115+
#
116+
117+
target_compile_options(${target}
118+
PRIVATE
119+
${DEFAULT_COMPILE_OPTIONS}
120+
)
121+
122+
#
123+
# Linker options
124+
#
125+
126+
target_link_libraries(${target}
127+
PRIVATE
128+
${DEFAULT_LINKER_OPTIONS}
129+
)
130+
131+
#
132+
# Define test
133+
#
134+
135+
add_test(NAME ${target}
136+
COMMAND $<TARGET_FILE:${target}>
137+
)
138+
139+
#
140+
# Define test properties
141+
#
142+
143+
set_property(TEST ${target}
144+
PROPERTY LABELS ${target}
145+
)
146+
147+
include(TestEnvironmentVariables)
148+
149+
test_environment_variables(${target}
150+
""
151+
${TESTS_ENVIRONMENT_VARIABLES}
152+
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* MetaCall Library by Parra Studios
3+
* A library for providing a foreign function interface calls.
4+
*
5+
* Copyright (C) 2016 - 2019 Vicente Eduardo Ferrer Garcia <[email protected]>
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*
19+
*/
20+
21+
#include <gmock/gmock.h>
22+
23+
int main(int argc, char * argv[])
24+
{
25+
::testing::InitGoogleMock(&argc, argv);
26+
27+
return RUN_ALL_TESTS();
28+
}

0 commit comments

Comments
 (0)