Skip to content

Commit 8dfc712

Browse files
committed
New wrapper approach. Chain and Distinct.
1 parent 58efeb1 commit 8dfc712

6 files changed

Lines changed: 170 additions & 114 deletions

File tree

javascript/wrapper.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,29 @@ if (require.main === module) {
2929
// The the commands to execute
3030
const inputData = JSON.parse(commands);
3131

32+
3233
// Debugging statements
33-
//console.log("Commands received from Python:", commands);
34+
//console.log("Commands received from Python:", inputData);
3435
//console.log("inputData[0][0]: ", inputData[0][0]); // returns: init(33)
3536

37+
38+
// Array to store results
39+
let allResults = [];
40+
3641
// Loop through commands
3742
for (const command of inputData) {
3843
// Evaluate each command
39-
lastResult = eval('secrets.' + command);
44+
//lastResult = eval('secrets.' + command);
45+
const result = eval('secrets.' + command);
46+
allResults.push(result);
4047
}
4148

4249
// Return the result of the last command
43-
console.log(JSON.stringify(lastResult));
50+
//console.log(JSON.stringify(lastResult));
51+
console.log(JSON.stringify(allResults));
4452

4553
} catch (error) {
4654
console.error("Error executing command:", error.message);
4755
process.exit(1);
4856
}
49-
}
57+
}

js2pysecrets/decorators.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,61 @@
1-
import json
21
import types
32

4-
from .wrapper import wrapper # Import your wrapper function
3+
from .wrapper import chain
54

65

76
class JsFunction:
8-
def __init__(self, func, test=False):
7+
def __init__(self, func, test=False, distinct=False):
98
self.func = func
109
self.test = test
10+
self.distinct = distinct
1111

12-
def __call__(self, *args, test=False, **kwargs):
12+
def __call__(self, *args, test=False, distinct=False, **kwargs):
1313
def wrapped_func(*args, **kwargs):
1414
args_str = ", ".join(repr(arg) for arg in args)
1515

1616
return f"{self.func.__name__}({args_str})"
1717

18-
data = []
18+
if distinct or self.distinct:
19+
return (
20+
wrapped_func(*args, **kwargs)
21+
if args
22+
else self.func(*args, **kwargs)
23+
)
24+
else:
1925

20-
# DO NOT REMOVE THIS
21-
if test or self.test:
22-
data.append("setRNG('testRandom')")
26+
data = []
2327

24-
data.append(
25-
wrapped_func(*args, **kwargs)
26-
if args
27-
else self.func(*args, **kwargs)
28-
)
28+
# DO NOT REMOVE THIS
29+
if test or self.test:
30+
data.append("setRNG('testRandom')")
31+
32+
data.append(
33+
wrapped_func(*args, **kwargs)
34+
if args
35+
else self.func(*args, **kwargs)
36+
)
2937

30-
json_data = json.dumps(data, indent=None).replace("'", "`")
31-
commands = json_data.encode().hex()
32-
results = wrapper(commands)
38+
# Break this section out into a stand-alone function
39+
# json_data = json.dumps(data, indent=None).replace("'", "`")
40+
# commands = json_data.encode().hex()
41+
# results = wrapper(commands)
42+
# End of section
3343

34-
return results
44+
return get_last_element_or_string(chain(data))
3545

3646
def __get__(self, instance, owner):
3747
return (
3848
self if instance is None else types.MethodType(self, instance)
3949
) # pragma: no cover No idea how to test!
4050

4151

52+
def get_last_element_or_string(result):
53+
if isinstance(result, list):
54+
return result[-1]
55+
else:
56+
return str(result)
57+
58+
4259
class jsNeedless:
4360
def __init__(self, func_name, test=False):
4461
self.func_name = func_name

js2pysecrets/node.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ def random(*args, **kwargs):
1717
pass # pragma: no cover
1818

1919

20+
@JsFunction
21+
def foobar(*args, **kwargs):
22+
pass # pragma: no cover
23+
24+
2025
@JsFunction
2126
def getConfig(*args, **kwargs):
2227
pass # pragma: no cover
@@ -27,13 +32,13 @@ def combine(*args, **kwargs):
2732
pass # pragma: no cover
2833

2934

30-
@jsNeedless
35+
@JsFunction
3136
def init(*args, **kwargs):
3237
pass # pragma: no cover
3338

3439

3540
@jsNeedless
36-
def newShare(*args, **kwargs):
41+
def _reset(*args, **kwargs):
3742
pass # pragma: no cover
3843

3944

js2pysecrets/wrapper.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ def wrapper(input_data):
6868
return js_result
6969

7070
except json.JSONDecodeError as e:
71-
# print("Python error decoding JSON:", e)
72-
# print("Raw stdout content:", result.stdout)
71+
print("Python error decoding JSON:", e)
72+
print("Raw stdout content:", result.stdout)
7373
warning_message = "Python error decoding JSON: " + str(e)
7474
warnings.warn(warning_message, category=Warning)
7575
return None
@@ -87,3 +87,10 @@ def wrapper(input_data):
8787
warning_message = "JavaScript error: " + js_error
8888
warnings.warn(warning_message, category=Warning)
8989
return None
90+
91+
92+
def chain(commands):
93+
json_data = json.dumps(commands, indent=None).replace("'", "`")
94+
encoded_commands = json_data.encode().hex()
95+
results = wrapper(encoded_commands)
96+
return results

tests/test_node.py

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

tests/test_wrapper.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from js2pysecrets.decorators import JsFunction, jsNeedless
2+
from js2pysecrets.wrapper import wrapper
3+
4+
import js2pysecrets.node as node
5+
import pytest
6+
import warnings
7+
8+
9+
def not_supported(decorated_function):
10+
try:
11+
decorated_function()
12+
# If the function didn't raise an exception, return False
13+
return False
14+
except Exception as e:
15+
# If an exception was raised, ensure it matches the expected message
16+
expected_message = (
17+
"Calling subsequent JavaScript functions are not supported. -or- "
18+
"The JavaScript function isn't necessary for the Python version."
19+
)
20+
return str(e) == expected_message
21+
22+
23+
@JsFunction
24+
def invalidFunction(*args, **kwargs):
25+
pass
26+
27+
28+
@jsNeedless
29+
def needless(*args, **kwargs):
30+
pass # pragma: no cover
31+
32+
33+
def test_JsFunction():
34+
assert node.random(128) != node.random(32)
35+
36+
37+
def test_jsNeedless():
38+
assert not_supported(lambda: needless(33, "blue"))
39+
40+
41+
def test_Distinct():
42+
command = node.share("aabb", 6, 3, distinct=True)
43+
assert command == "share('aabb', 6, 3)"
44+
45+
46+
def test_Randomness():
47+
count = 0
48+
num_trials = 10
49+
for _ in range(num_trials):
50+
# Simulate random behavior
51+
rand1 = node.random(16)
52+
rand2 = node.random(16)
53+
if int(rand1, 16) == int(rand2, 16):
54+
count += 1
55+
assert count < num_trials, "Randomness test failed"
56+
57+
58+
def test_TestRNG():
59+
count = 0
60+
num_trials = 10
61+
for _ in range(num_trials):
62+
# Simulate random behavior
63+
rand1 = node.random(16, test=True)
64+
rand2 = node.random(16, test=True)
65+
if int(rand1, 16) == int(rand2, 16):
66+
count += 1
67+
assert count == num_trials, "Test RNG Failed"
68+
69+
70+
@pytest.mark.filterwarnings("ignore:invalidFunction")
71+
def test_invalidFunction():
72+
with warnings.catch_warnings(record=True) as caught_warnings:
73+
invalidFunction(1, 2, 3)
74+
# Check if any warnings were raised
75+
assert len(caught_warnings) == 1
76+
assert issubclass(caught_warnings[0].category, Warning)
77+
assert "not a function" in str(caught_warnings[0].message)
78+
79+
80+
@pytest.mark.filterwarnings("ignore:node.share")
81+
def test_CalledProcessError():
82+
with warnings.catch_warnings(record=True) as caught_warnings:
83+
node.share("hello world", 3, 3)
84+
# Check if any warnings were raised
85+
assert len(caught_warnings) == 1
86+
assert issubclass(caught_warnings[0].category, Warning)
87+
assert "Invalid hex character" in str(caught_warnings[0].message)
88+
89+
90+
# @pytest.mark.filterwarnings("ignore:fail")
91+
# def test_JSONDecodeError():
92+
# with warnings.catch_warnings(record=True) as caught_warnings:
93+
# node.fail()
94+
# # Check if any warnings were raised
95+
# assert len(caught_warnings) == 1
96+
# assert issubclass(caught_warnings[0].category, Warning)
97+
# assert "error decoding JSON" in str(caught_warnings[0].message)
98+
99+
100+
# @pytest.mark.filterwarnings("ignore:wrapper")
101+
# def test_JSONDecodeError():
102+
# with warnings.catch_warnings(record=True) as caught_warnings:
103+
# wrapper("hello world")
104+
# # Check if any warnings were raised
105+
# assert len(caught_warnings) == 1
106+
# assert issubclass(caught_warnings[0].category, Warning)
107+
# assert "error decoding JSON" in str(caught_warnings[0].message)
108+
# # assert True

0 commit comments

Comments
 (0)