Skip to content

Commit 9b79b77

Browse files
authored
Merge pull request #11 from dreamfactorysoftware/3.0-beta
3.0 beta
2 parents 55b337b + e3fc415 commit 9b79b77

9 files changed

Lines changed: 411 additions & 53 deletions

File tree

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"v8js",
1010
"php",
1111
"python",
12+
"python3",
1213
"nodejs"
1314
],
1415
"license": [
@@ -32,7 +33,7 @@
3233
},
3334
"require": {
3435
"dreamfactory/df-core": "~0.14.0",
35-
"dreamfactory/df-system": "~0.1.0"
36+
"dreamfactory/df-system": "~0.2.0"
3637
},
3738
"autoload": {
3839
"psr-4": {

config/df.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
'nodejs_path' => env('DF_NODEJS_PATH'),
99
// path to the installed python executable
1010
'python_path' => env('DF_PYTHON_PATH'),
11+
// path to the installed python3 executable
12+
'python3_path' => env('DF_PYTHON3_PATH'),
1113
// protocol to use for Node.js and Python when making internal calls back to DreamFactory
1214
'default_protocol' => env('DF_SCRIPTING_DEFAULT_PROTOCOL', 'http'), // http or https
1315
],

src/Components/ScriptEngineManager.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use DreamFactory\Core\Script\Engines\NodeJs;
77
use DreamFactory\Core\Script\Engines\Php;
88
use DreamFactory\Core\Script\Engines\Python;
9+
use DreamFactory\Core\Script\Engines\Python3;
910
use DreamFactory\Core\Script\Engines\V8Js;
1011
use InvalidArgumentException;
1112

@@ -95,6 +96,15 @@ public function __construct($app)
9596
return new Python($config);
9697
},
9798
],
99+
[
100+
'name' => 'python3',
101+
'label' => 'Python3',
102+
'description' => 'Script handler using native Python3.',
103+
'sandboxed' => false,
104+
'factory' => function ($config){
105+
return new Python3($config);
106+
},
107+
],
98108
[
99109
'name' => 'v8js',
100110
'label' => 'V8js',

src/Engines/Python.php

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,32 +35,35 @@ public function __construct(array $settings = [])
3535
protected function enrobeScript($script, array &$data = [], array $platform = [])
3636
{
3737
$jsonEvent = $this->safeJsonEncode($data, false);
38+
3839
$jsonPlatform = json_encode($platform, JSON_UNESCAPED_SLASHES);
39-
40+
4041
$jsonPlatform = str_replace(
41-
[
42-
":null,", ":null",
43-
":false,", ":false",
44-
":true,", ":true"
45-
], [
46-
":None,", ":None",
47-
":False,", ":False",
48-
":True,", ":True"
49-
],
50-
$jsonPlatform
51-
);
52-
53-
$jsonEvent = str_replace(
54-
[
42+
[
5543
":null,", ":null",
5644
":false,", ":false",
5745
":true,", ":true"
58-
], [
46+
],
47+
[
5948
":None,", ":None",
6049
":False,", ":False",
6150
":True,", ":True"
62-
],
63-
$jsonEvent
51+
],
52+
$jsonPlatform
53+
);
54+
55+
$jsonEvent = str_replace(
56+
[
57+
":null,", ":null",
58+
":false,", ":false",
59+
":true,", ":true"
60+
],
61+
[
62+
":None,", ":None",
63+
":False,", ":False",
64+
":True,", ":True"
65+
],
66+
$jsonEvent
6467
);
6568

6669
$protocol = config('df.scripting.default_protocol', 'http');
@@ -215,4 +218,4 @@ protected function checkOutputStringForData($output)
215218
{
216219
return ((strlen($output) > 10) && (false !== strpos($output, 'request')));
217220
}
218-
}
221+
}

src/Engines/Python3.php

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
<?php
2+
3+
namespace DreamFactory\Core\Script\Engines;
4+
5+
use Cache;
6+
7+
class Python3 extends ExecutedEngine
8+
{
9+
//*************************************************************************
10+
// Methods
11+
//*************************************************************************
12+
13+
/**
14+
* {@inheritdoc}
15+
*/
16+
public function __construct(array $settings = [])
17+
{
18+
if (!isset($settings['command_name'])) {
19+
$settings['command_name'] = 'python3';
20+
}
21+
if (!isset($settings['command_path'])) {
22+
$settings['command_path'] = config('df.scripting.python3_path');
23+
}
24+
if (!isset($settings['file_extension'])) {
25+
$settings['file_extension'] = 'py';
26+
}
27+
if (!isset($settings['supports_inline_execution'])) {
28+
$settings['supports_inline_execution'] = true;
29+
$settings['inline_arguments'] = '-c';
30+
}
31+
32+
parent::__construct($settings);
33+
}
34+
35+
protected function enrobeScript($script, array &$data = [], array $platform = [])
36+
{
37+
38+
$jsonEvent = $this->safeJsonEncode($data, false);
39+
40+
$jsonPlatform = json_encode($platform, JSON_UNESCAPED_SLASHES);
41+
42+
$jsonPlatform = str_replace(
43+
[
44+
":null,", ":null",
45+
":false,", ":false",
46+
":true,", ":true"
47+
],
48+
[
49+
":None,", ":None",
50+
":False,", ":False",
51+
":True,", ":True"
52+
],
53+
$jsonPlatform
54+
);
55+
56+
$jsonEvent = str_replace(
57+
[
58+
":null,", ":null",
59+
":false,", ":false",
60+
":true,", ":true"
61+
],
62+
[
63+
":None,", ":None",
64+
":False,", ":False",
65+
":True,", ":True"
66+
],
67+
$jsonEvent
68+
);
69+
70+
$protocol = config('df.scripting.default_protocol', 'http');
71+
$https = array_get($_SERVER, 'HTTPS');
72+
if ((!empty($https) && ('off' != $https)) || (443 == array_get($_SERVER, 'SERVER_PORT'))) {
73+
$protocol = 'https';
74+
}
75+
$token = uniqid();
76+
$tokenCache = [
77+
'app_id' => array_get($platform, 'session.app.id'),
78+
'user_id' => array_get($platform, 'session.user.id')
79+
];
80+
Cache::add('script-token:'.$token, $tokenCache, 5); // script should not take longer than 5 minutes to run
81+
82+
if (empty($script)) {
83+
$script = 'pass;';
84+
}
85+
$scriptLines = explode("\n", $script);
86+
87+
echo $jsonEvent;
88+
$enrobedScript = <<<python
89+
import http.client, json;
90+
from munch import munchify, unmunchify;
91+
92+
eventJson = $jsonEvent;
93+
print(eventJson);
94+
platformJson = $jsonPlatform;
95+
print(platformJson);
96+
97+
_event = munchify(eventJson);
98+
_platform = munchify(platformJson);
99+
100+
101+
__protocol = '$protocol';
102+
__host = _event.request.headers.host;
103+
__headers = {
104+
'x-dreamfactory-script-token':'$token'
105+
};
106+
107+
class Api:
108+
def __init__(self, host, header, protocol):
109+
self.host = host;
110+
self.header = header;
111+
self.protocol = protocol;
112+
113+
def get(self, path, options=''):
114+
return self.call('GET', path, '', options);
115+
116+
def post(self, path, payload='', options=''):
117+
return self.call('POST', path, payload, options);
118+
119+
def put(self, path, payload='', options=''):
120+
return self.call('PUT', path, payload, options);
121+
122+
def patch(self, path, payload='', options=''):
123+
return self.call('PATCH', path, payload, options);
124+
125+
def delete(self, path, payload='', options=''):
126+
return self.call('DELETE', path, payload, options);
127+
128+
def call(self, verb, path, payload='', options={}):
129+
if(type(options) is dict):
130+
options = munchify(options);
131+
if(options and ('headers' in options)):
132+
header = options.headers;
133+
elif(options and ('parameters' in options)):
134+
header = munchify({});
135+
elif(options):
136+
header = options;
137+
else:
138+
header = munchify({});
139+
140+
path = self.cleanPath(path);
141+
if(options and ('parameters' in options)):
142+
for key in options.parameters:
143+
if(path.find('?') == -1):
144+
path = path + '?' + str(key) + '=' + str(options.parameters[key]);
145+
else:
146+
path = path + '&' + str(key) + '=' + str(options.parameters[key]);
147+
148+
conn = self.getConnection(path);
149+
if(self.isInternalApi(path)):
150+
for key in self.header:
151+
header[key] = self.header[key];
152+
153+
conn.request(verb, path, payload, header);
154+
response = conn.getresponse();
155+
return response;
156+
157+
def getConnection(self, path):
158+
host = self.getHost(path);
159+
if(self.getProtocol(path) == 'https'):
160+
return http.client.HTTPSConnection(host);
161+
else:
162+
return http.client.HTTPConnection(host);
163+
164+
def getHost(self, path):
165+
path = path.strip();
166+
if(path[0:7] == 'http://'):
167+
path = path[7:];
168+
return path[0:path.find('/')];
169+
elif(path[0:8] == 'https://'):
170+
path = path[8:];
171+
return path[0:path.find('/')];
172+
else:
173+
return self.host;
174+
175+
def getProtocol(self, path):
176+
if(path[0:7] == 'http://'):
177+
return 'http';
178+
elif(path[0:8] == 'https://'):
179+
return 'https';
180+
else:
181+
return self.protocol;
182+
183+
def isInternalApi(self, path):
184+
path = path.strip();
185+
return False if (path[0:7] == 'http://' or path[0:8] == 'https://') else True;
186+
187+
def cleanPath(self, path):
188+
path = path.strip();
189+
if(self.isInternalApi(path)):
190+
if(path[0:1] != '/'):
191+
path = '/'+path;
192+
if(path[0:8] != '/api/v2/'):
193+
path = '/api/v2'+path;
194+
195+
return path;
196+
197+
_platform.api = Api(__host, __headers, __protocol);
198+
199+
try:
200+
def my_closure(event, platform):
201+
python;
202+
foreach ($scriptLines as $sl) {
203+
$enrobedScript .= "\n " . $sl;
204+
}
205+
206+
$enrobedScript .= <<<python
207+
208+
_event.script_result = my_closure(_event, _platform);
209+
except Exception as e:
210+
_event.script_result = {'error':str(e)};
211+
_event.exception = str(e)
212+
213+
print(json.dumps(_event));
214+
python;
215+
$enrobedScript = trim($enrobedScript);
216+
217+
return $enrobedScript;
218+
}
219+
220+
/** @inheritdoc */
221+
protected function checkOutputStringForData($output)
222+
{
223+
return ((strlen($output) > 10) && (false !== strpos($output, 'request')));
224+
}
225+
}

src/Enums/ScriptLanguages.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ class ScriptLanguages extends FactoryEnum
3131
* @var string
3232
*/
3333
const PYTHON = 'py';
34+
/**
35+
* @var string
36+
*/
37+
const PYTHON3 = 'py';
3438
/**
3539
* @var string
3640
*/

src/Models/Python3Config.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace DreamFactory\Core\Script\Models;
4+
5+
/**
6+
* PythonConfig
7+
*
8+
*/
9+
class Python3Config extends ScriptConfig
10+
{
11+
public static function getType()
12+
{
13+
return 'python3';
14+
}
15+
}

0 commit comments

Comments
 (0)