Skip to content

Commit 56bde21

Browse files
committed
Copy in py-eval from swift-e-lab
1 parent d2e5ae6 commit 56bde21

File tree

13 files changed

+383
-0
lines changed

13 files changed

+383
-0
lines changed

work/py-eval/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
py-eval
2+
*.o

work/py-eval/Makefile

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
# Edit Python settings here
3+
# PYTHON_HOME = ${HOME}/sfw/Python-2.7.10
4+
# PYTHON_VERSION_MAJOR = 2
5+
# PYTHON_VERSION_MINOR = 7
6+
# PYTHON_VERSION_SUFFIX =
7+
8+
PYTHON_HOME = ${HOME}/sfw/Python-3.6.1
9+
PYTHON_VERSION_MAJOR = 3
10+
PYTHON_VERSION_MINOR = 6
11+
PYTHON_VERSION_SUFFIX = m
12+
# End Python settings
13+
14+
PYTHON_VERSION = $(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)$(PYTHON_VERSION_SUFFIX)
15+
INCLUDES = -I $(PYTHON_HOME)/include/python$(PYTHON_VERSION)
16+
LIBS = -L $(PYTHON_HOME)/lib -lpython$(PYTHON_VERSION)
17+
RPATHS = -Wl,-rpath -Wl,$(PYTHON_HOME)/lib
18+
19+
CFLAGS = -g -Wall -O0 -fPIC -DPYTHON_VERSION_MAJOR=$(PYTHON_VERSION_MAJOR) $(INCLUDES) -fdiagnostics-color=always
20+
21+
SRCS = main.c py-eval.c io.c
22+
OBJS = $(patsubst %.c,%.o,$(SRCS))
23+
24+
all: py-eval
25+
26+
$(OBJS): Makefile # Rebuild everything if Makefile changes
27+
28+
py-eval: $(OBJS)
29+
gcc -fPIC -o $(@) $(OBJS) \
30+
$(LIBS) \
31+
$(RPATHS)
32+
33+
clean:
34+
@ rm -fv $(OBJS) py-eval

work/py-eval/README.adoc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
= Simple C-Python Example
3+
4+
The program is called py-eval. Its arguments are:
5+
6+
1. Zero or more Python scripts that are executed with no return value.
7+
2. Any number of hyphen tokens - , which provides a _New Python Interpreter_ at each point.
8+
2. Exactly one Python script that returns a string.
9+
10+
This has been tested with Python 2.7.10 and Python 3.6m.
11+
12+
For more verbose output, set verbose=true in py-eval.c
13+
14+
You may need to set environment variable PYTHONHOME at runtime to agree with PYTHON_HOME in the Makefile.
15+
16+
Transcript:
17+
----
18+
# edit Makefile for Python installation settings
19+
make
20+
export PYTHONHOME=...
21+
./py-eval py/import-io.py py/hello.py
22+
----
23+
24+
To test the exception handling capabilities, do:
25+
----
26+
./py-eval py/err.py py/hello.py
27+
----
28+
29+
To test the _New Python Interpreter_ feature, compare:
30+
----
31+
cd py
32+
../py-eval x2.py print_x.py hello.py
33+
2
34+
'Hello'
35+
----
36+
37+
with the same thing with added - :
38+
39+
----
40+
cd py
41+
../py-eval x2.py - print_x.py hello.py
42+
43+
PYTHON EXCEPTION:
44+
<class 'NameError'>
45+
name 'x' is not defined
46+
py-eval: python code failed.
47+
----

work/py-eval/io.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
#include <sys/stat.h>
6+
7+
char*
8+
slurp(const char* filename)
9+
{
10+
FILE* file = fopen(filename, "r");
11+
if (file == NULL)
12+
{
13+
printf("slurp(): could not read from: %s\n", filename);
14+
return NULL;
15+
}
16+
17+
struct stat s;
18+
int rc = stat(filename, &s);
19+
if (rc != 0)
20+
{
21+
printf("slurp(): could not stat: %s\n", filename);
22+
return NULL;
23+
}
24+
25+
size_t length = s.st_size;
26+
char* result = malloc(length+1);
27+
if (result == NULL)
28+
{
29+
printf("slurp(): could not allocate memory for: %s\n", filename);
30+
return NULL;
31+
}
32+
33+
char* p = result;
34+
size_t actual = fread(p, sizeof(char), length, file);
35+
if (actual != length)
36+
{
37+
printf("could not read all %zi bytes from file: %s\n",
38+
length, filename);
39+
free(result);
40+
return NULL;
41+
}
42+
result[length] = '\0';
43+
44+
fclose(file);
45+
return result;
46+
}
47+
48+
void
49+
chomp(char* s)
50+
{
51+
size_t length = strlen(s);
52+
if (s[length-1] == '\n')
53+
s[length-1] = '\0';
54+
}

work/py-eval/io.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
#pragma once
3+
4+
char* slurp(const char* filename);
5+
6+
void chomp(char* s);

work/py-eval/main.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
2+
#include <stdarg.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
7+
#include "io.h"
8+
#include "py-eval.h"
9+
10+
char* usage =
11+
"usage: py-eval [code_files]* expr_file\n"
12+
"use - to reset the interpreter\n"
13+
"see the README\n";
14+
15+
static void crash(char* fmt, ...);
16+
static void do_python_code(char* code_file);
17+
static void do_python_eval(char* expr_file);
18+
19+
int
20+
main(int argc, char* argv[])
21+
{
22+
if (argc == 1) crash(usage);
23+
24+
python_init();
25+
26+
int cf; // current file
27+
for (cf = 1; cf < argc-1; cf++)
28+
{
29+
char* code_file = argv[cf];
30+
if (strcmp(code_file, "-") == 0)
31+
{
32+
python_reset();
33+
continue;
34+
}
35+
do_python_code(code_file);
36+
}
37+
38+
do_python_eval(argv[cf]);
39+
40+
// Clean up
41+
python_finalize();
42+
exit(EXIT_SUCCESS);
43+
}
44+
45+
static void
46+
do_python_code(char* code_file)
47+
{
48+
char* code = slurp(code_file);
49+
chomp(code);
50+
if (code == NULL) crash("failed to read: %s", code_file);
51+
bool rc = python_code(code);
52+
free(code);
53+
if (!rc) crash("python code failed.");
54+
}
55+
56+
static void
57+
do_python_eval(char* expr_file)
58+
{
59+
// Read Python expr file
60+
if (strcmp(expr_file, "-") == 0)
61+
crash("expr file cannot be -");
62+
char* expr = slurp(expr_file);
63+
if (expr == NULL) crash("failed to read: %s", expr_file);
64+
chomp(expr);
65+
66+
// Do Python eval
67+
char* result;
68+
bool rc = python_eval(expr, &result);
69+
free(expr);
70+
if (!rc) crash("python expr failed.");
71+
printf("%s\n", result);
72+
}
73+
74+
static void
75+
crash(char* fmt, ...)
76+
{
77+
printf("py-eval: ");
78+
va_list ap;
79+
va_start(ap, fmt);
80+
vprintf(fmt, ap);
81+
va_end(ap);
82+
printf("\n");
83+
84+
exit(EXIT_FAILURE);
85+
}

work/py-eval/py-eval.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
2+
#include <Python.h>
3+
4+
#include <stdarg.h>
5+
#include <stdio.h>
6+
7+
#include "py-eval.h"
8+
9+
static void
10+
verbose(char* fmt, ...)
11+
{
12+
static bool verbose = false;
13+
14+
if (!verbose) return;
15+
16+
va_list ap;
17+
va_start(ap, fmt);
18+
vprintf(fmt, ap);
19+
printf("\n");
20+
va_end(ap);
21+
}
22+
23+
static bool
24+
handle_python_exception(void)
25+
{
26+
printf("\n");
27+
printf("PYTHON EXCEPTION:\n");
28+
29+
#if PYTHON_VERSION_MAJOR >= 3
30+
31+
PyObject *exc,*val,*tb;
32+
PyErr_Fetch(&exc,&val,&tb);
33+
PyObject_Print(exc, stdout, Py_PRINT_RAW);
34+
printf("\n");
35+
PyObject_Print(val, stdout, Py_PRINT_RAW);
36+
printf("\n");
37+
38+
#else // Python 2
39+
40+
PyErr_Print();
41+
42+
#endif
43+
44+
return false;
45+
}
46+
47+
static bool
48+
handle_python_non_string(PyObject* o)
49+
{
50+
printf("python: expression did not return a string!\n");
51+
fflush(stdout);
52+
printf("python: expression evaluated to: ");
53+
PyObject_Print(o, stdout, 0);
54+
printf("\n");
55+
return false;
56+
}
57+
58+
static PyObject* main_module = NULL;
59+
static PyObject* main_dict = NULL;
60+
static PyObject* local_dict = NULL;
61+
62+
static bool initialized = false;
63+
64+
bool
65+
python_init()
66+
{
67+
if (initialized) return true;
68+
verbose("python: initializing...");
69+
Py_InitializeEx(1);
70+
main_module = PyImport_AddModule("__main__");
71+
if (main_module == NULL) return handle_python_exception();
72+
main_dict = PyModule_GetDict(main_module);
73+
if (main_dict == NULL) return handle_python_exception();
74+
local_dict = PyDict_New();
75+
if (local_dict == NULL) return handle_python_exception();
76+
initialized = true;
77+
return true;
78+
}
79+
80+
static char* python_result_default = "NOTHING";
81+
82+
/**
83+
@param persist: If true, retain the Python interpreter,
84+
else finalize it
85+
@param code: The multiline string of Python code.
86+
87+
@return true on success, false on error
88+
*/
89+
bool
90+
python_code(const char* code)
91+
{
92+
// Execute code:
93+
verbose("python: code: %s", code);
94+
PyRun_String(code, Py_file_input, main_dict, local_dict);
95+
if (PyErr_Occurred()) return handle_python_exception();
96+
return true;
97+
}
98+
99+
/**
100+
The expr is evaluated to the returned result
101+
@param output: Store result pointer here
102+
*/
103+
bool python_eval(const char* expr, char** output)
104+
{
105+
char* result = python_result_default;
106+
107+
// Evaluate expression:
108+
verbose("python: expr: %s", expr);
109+
PyObject* o = PyRun_String(expr, Py_eval_input, main_dict, local_dict);
110+
if (o == NULL) return handle_python_exception();
111+
112+
// Convert Python result to C string
113+
int pc = PyArg_Parse(o, "s", &result);
114+
if (pc != 1) return handle_python_non_string(o);
115+
verbose("python: result: %s\n", result);
116+
*output = strdup(result);
117+
118+
// Clean up and return:
119+
Py_DECREF(o);
120+
121+
return true;
122+
}
123+
124+
bool
125+
python_reset()
126+
{
127+
python_finalize();
128+
return python_init();
129+
}
130+
131+
void
132+
python_finalize()
133+
{
134+
Py_Finalize();
135+
initialized = false;
136+
}

work/py-eval/py-eval.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
#pragma once
3+
4+
#include <stdbool.h>
5+
6+
bool python_init(void);
7+
8+
bool python_reset(void);
9+
10+
bool python_code(const char* code);
11+
12+
bool python_eval(const char* expression, char** output);
13+
14+
void python_finalize(void);

work/py-eval/py/err.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2+

work/py-eval/py/hello.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
repr("Hello")

0 commit comments

Comments
 (0)