Skip to content

Commit 2c41388

Browse files
committed
Implement custom functions. Resolves #13.
1 parent b043a03 commit 2c41388

File tree

5 files changed

+859
-12
lines changed

5 files changed

+859
-12
lines changed

Makefile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# This is to speed up development time.
2+
# Usage:
3+
# Needed once:
4+
# $ virtualenv venv
5+
# $ . venv/bin/activate
6+
# $ pip install -e .`
7+
# $ pip install werkzeug
8+
# Once that is done, to rebuild simply:
9+
# $ make -j 4 && python -m unittest sasstests
10+
11+
PY_HEADERS := -I/usr/include/python2.7
12+
C_SOURCES := $(wildcard libsass/*.c)
13+
C_OBJECTS = $(patsubst libsass/%.c,build2/libsass/c/%.o,$(C_SOURCES))
14+
CPP_SOURCES := $(wildcard libsass/*.cpp)
15+
CPP_OBJECTS = $(patsubst libsass/%.cpp,build2/libsass/cpp/%.o,$(CPP_SOURCES))
16+
17+
all: _sass.so
18+
19+
build2/libsass/c/%.o: libsass/%.c
20+
@mkdir -p build2/libsass/c/
21+
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I./libsass $(PY_HEADERS) -c $^ -o $@ -c -O2 -fPIC -std=c++0x -Wall -Wno-parentheses
22+
23+
build2/libsass/cpp/%.o: libsass/%.cpp
24+
@mkdir -p build2/libsass/cpp/
25+
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I./libsass $(PY_HEADERS) -c $^ -o $@ -c -O2 -fPIC -std=c++0x -Wall -Wno-parentheses
26+
27+
build2/pysass.o: pysass.cpp
28+
@mkdir -p build2
29+
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I./libsass $(PY_HEADERS) -c $^ -o $@ -c -O2 -fPIC -std=c++0x -Wall -Wno-parentheses
30+
31+
_sass.so: $(C_OBJECTS) $(CPP_OBJECTS) build2/pysass.o
32+
g++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro $^ -L./libsass -o $@ -fPIC -lstdc++
33+
34+
.PHONY: clean
35+
clean:
36+
rm -rf build2 _sass.so
37+

pysass.cpp

Lines changed: 309 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,305 @@ static struct PySass_Pair PySass_output_style_enum[] = {
3535
{NULL}
3636
};
3737

38+
static PyObject* _to_py_value(const union Sass_Value* value) {
39+
PyObject* retv = NULL;
40+
PyObject* types_mod = PyImport_ImportModule("sassutils.sass_types");
41+
PyObject* sass_comma = PyObject_GetAttrString(types_mod, "SASS_SEPARATOR_COMMA");
42+
PyObject* sass_space = PyObject_GetAttrString(types_mod, "SASS_SEPARATOR_SPACE");
43+
44+
switch (sass_value_get_tag(value)) {
45+
case SASS_NULL:
46+
retv = Py_None;
47+
Py_INCREF(retv);
48+
break;
49+
case SASS_BOOLEAN:
50+
retv = PyBool_FromLong(sass_boolean_get_value(value));
51+
break;
52+
case SASS_STRING:
53+
retv = PyUnicode_FromString(sass_string_get_value(value));
54+
break;
55+
case SASS_NUMBER:
56+
retv = PyObject_CallMethod(
57+
types_mod,
58+
"SassNumber",
59+
PySass_IF_PY3("dy", "ds"),
60+
sass_number_get_value(value),
61+
sass_number_get_unit(value)
62+
);
63+
break;
64+
case SASS_COLOR:
65+
retv = PyObject_CallMethod(
66+
types_mod,
67+
"SassColor",
68+
"dddd",
69+
sass_color_get_r(value),
70+
sass_color_get_g(value),
71+
sass_color_get_b(value),
72+
sass_color_get_a(value)
73+
);
74+
break;
75+
case SASS_LIST: {
76+
size_t i = 0;
77+
PyObject* items = PyTuple_New(sass_list_get_length(value));
78+
PyObject* separator = sass_comma;
79+
switch (sass_list_get_separator(value)) {
80+
case SASS_COMMA:
81+
separator = sass_comma;
82+
break;
83+
case SASS_SPACE:
84+
separator = sass_space;
85+
break;
86+
}
87+
for (i = 0; i < sass_list_get_length(value); i += 1) {
88+
PyTuple_SetItem(
89+
items,
90+
i,
91+
_to_py_value(sass_list_get_value(value, i))
92+
);
93+
}
94+
retv = PyObject_CallMethod(
95+
types_mod, "SassList", "OO", items, separator
96+
);
97+
break;
98+
}
99+
case SASS_MAP: {
100+
size_t i = 0;
101+
PyObject* items = PyTuple_New(sass_map_get_length(value));
102+
for (i = 0; i < sass_map_get_length(value); i += 1) {
103+
PyObject* kvp = PyTuple_New(2);
104+
PyTuple_SetItem(
105+
kvp, 0, _to_py_value(sass_map_get_key(value, i))
106+
);
107+
PyTuple_SetItem(
108+
kvp, 1, _to_py_value(sass_map_get_value(value, i))
109+
);
110+
PyTuple_SetItem(items, i, kvp);
111+
}
112+
retv = PyObject_CallMethod(types_mod, "SassMap", "(O)", items);
113+
Py_DECREF(items);
114+
break;
115+
}
116+
case SASS_ERROR:
117+
case SASS_WARNING:
118+
/* @warning and @error cannot be passed */
119+
break;
120+
}
121+
122+
if (retv == NULL) {
123+
PyErr_SetString(PyExc_TypeError, "Unexpected sass type");
124+
}
125+
126+
Py_DECREF(types_mod);
127+
Py_DECREF(sass_comma);
128+
Py_DECREF(sass_space);
129+
return retv;
130+
}
131+
132+
static union Sass_Value* _to_sass_value(PyObject* value) {
133+
union Sass_Value* retv = NULL;
134+
PyObject* types_mod = PyImport_ImportModule("sassutils.sass_types");
135+
PyObject* sass_number_t = PyObject_GetAttrString(types_mod, "SassNumber");
136+
PyObject* sass_color_t = PyObject_GetAttrString(types_mod, "SassColor");
137+
PyObject* sass_list_t = PyObject_GetAttrString(types_mod, "SassList");
138+
PyObject* sass_warning_t = PyObject_GetAttrString(types_mod, "SassWarning");
139+
PyObject* sass_error_t = PyObject_GetAttrString(types_mod, "SassError");
140+
PyObject* sass_comma = PyObject_GetAttrString(types_mod, "SASS_SEPARATOR_COMMA");
141+
PyObject* sass_space = PyObject_GetAttrString(types_mod, "SASS_SEPARATOR_SPACE");
142+
143+
if (value == Py_None) {
144+
retv = sass_make_null();
145+
} else if (PyBool_Check(value)) {
146+
retv = sass_make_boolean(value == Py_True);
147+
} else if (PyUnicode_Check(value)) {
148+
PyObject* bytes = PyUnicode_AsEncodedString(value, "UTF-8", "strict");
149+
retv = sass_make_string(PySass_Bytes_AS_STRING(bytes));
150+
Py_DECREF(bytes);
151+
} else if (PySass_Bytes_Check(value)) {
152+
retv = sass_make_string(PySass_Bytes_AS_STRING(value));
153+
} else if (PyDict_Check(value)) {
154+
size_t i = 0;
155+
Py_ssize_t pos = 0;
156+
PyObject* d_key = NULL;
157+
PyObject* d_value = NULL;
158+
retv = sass_make_map(PyDict_Size(value));
159+
while (PyDict_Next(value, &pos, &d_key, &d_value)) {
160+
sass_map_set_key(retv, i, _to_sass_value(d_key));
161+
sass_map_set_value(retv, i, _to_sass_value(d_value));
162+
i += 1;
163+
}
164+
} else if (PyObject_IsInstance(value, sass_number_t)) {
165+
PyObject* d_value = PyObject_GetAttrString(value, "value");
166+
PyObject* unit = PyObject_GetAttrString(value, "unit");
167+
PyObject* bytes = PyUnicode_AsEncodedString(unit, "UTF-8", "strict");
168+
retv = sass_make_number(
169+
PyFloat_AsDouble(d_value), PySass_Bytes_AS_STRING(bytes)
170+
);
171+
Py_DECREF(d_value);
172+
Py_DECREF(unit);
173+
Py_DECREF(bytes);
174+
} else if (PyObject_IsInstance(value, sass_color_t)) {
175+
PyObject* r_value = PyObject_GetAttrString(value, "r");
176+
PyObject* g_value = PyObject_GetAttrString(value, "g");
177+
PyObject* b_value = PyObject_GetAttrString(value, "b");
178+
PyObject* a_value = PyObject_GetAttrString(value, "a");
179+
retv = sass_make_color(
180+
PyFloat_AsDouble(r_value),
181+
PyFloat_AsDouble(g_value),
182+
PyFloat_AsDouble(b_value),
183+
PyFloat_AsDouble(a_value)
184+
);
185+
Py_DECREF(r_value);
186+
Py_DECREF(g_value);
187+
Py_DECREF(b_value);
188+
Py_DECREF(a_value);
189+
} else if (PyObject_IsInstance(value, sass_list_t)) {
190+
Py_ssize_t i = 0;
191+
PyObject* items = PyObject_GetAttrString(value, "items");
192+
PyObject* separator = PyObject_GetAttrString(value, "separator");
193+
/* TODO: I don't really like this, maybe move types to C */
194+
Sass_Separator sep = SASS_COMMA;
195+
if (separator == sass_comma) {
196+
sep = SASS_COMMA;
197+
} else if (separator == sass_space) {
198+
sep = SASS_SPACE;
199+
} else {
200+
assert(0);
201+
}
202+
retv = sass_make_list(PyTuple_Size(items), sep);
203+
for (i = 0; i < PyTuple_Size(items); i += 1) {
204+
sass_list_set_value(
205+
retv, i, _to_sass_value(PyTuple_GET_ITEM(items, i))
206+
);
207+
}
208+
Py_DECREF(items);
209+
Py_DECREF(separator);
210+
} else if (PyObject_IsInstance(value, sass_warning_t)) {
211+
PyObject* msg = PyObject_GetAttrString(value, "msg");
212+
PyObject* bytes = PyUnicode_AsEncodedString(msg, "UTF-8", "strict");
213+
retv = sass_make_warning(PySass_Bytes_AS_STRING(bytes));
214+
Py_DECREF(msg);
215+
Py_DECREF(bytes);
216+
} else if (PyObject_IsInstance(value, sass_error_t)) {
217+
PyObject* msg = PyObject_GetAttrString(value, "msg");
218+
PyObject* bytes = PyUnicode_AsEncodedString(msg, "UTF-8", "strict");
219+
retv = sass_make_error(PySass_Bytes_AS_STRING(bytes));
220+
Py_DECREF(msg);
221+
Py_DECREF(bytes);
222+
}
223+
224+
if (retv == NULL) {
225+
PyObject* type = PyObject_Type(value);
226+
PyObject* type_name = PyObject_GetAttrString(type, "__name__");
227+
PyObject* fmt = PyUnicode_FromString(
228+
"Unexpected type: `{0}`.\n"
229+
"Expected one of:\n"
230+
"- None\n"
231+
"- bool\n"
232+
"- str\n"
233+
"- SassNumber\n"
234+
"- SassColor\n"
235+
"- SassList\n"
236+
"- dict\n"
237+
"- SassMap\n"
238+
"- SassWarning\n"
239+
"- SassError\n"
240+
);
241+
PyObject* format_meth = PyObject_GetAttrString(fmt, "format");
242+
PyObject* result = PyObject_CallFunctionObjArgs(
243+
format_meth, type_name, NULL
244+
);
245+
PyObject* bytes = PyUnicode_AsEncodedString(result, "UTF-8", "strict");
246+
retv = sass_make_error(PySass_Bytes_AS_STRING(bytes));
247+
Py_DECREF(type);
248+
Py_DECREF(type_name);
249+
Py_DECREF(fmt);
250+
Py_DECREF(format_meth);
251+
Py_DECREF(result);
252+
Py_DECREF(bytes);
253+
}
254+
255+
Py_DECREF(types_mod);
256+
Py_DECREF(sass_number_t);
257+
Py_DECREF(sass_color_t);
258+
Py_DECREF(sass_list_t);
259+
Py_DECREF(sass_warning_t);
260+
Py_DECREF(sass_error_t);
261+
Py_DECREF(sass_comma);
262+
Py_DECREF(sass_space);
263+
return retv;
264+
}
265+
266+
static union Sass_Value* _call_py_f(
267+
const union Sass_Value* sass_args, void* cookie
268+
) {
269+
size_t i;
270+
PyObject* pyfunc = (PyObject*)cookie;
271+
PyObject* py_args = PyTuple_New(sass_list_get_length(sass_args));
272+
PyObject* py_result = NULL;
273+
union Sass_Value* sass_result = NULL;
274+
275+
for (i = 0; i < sass_list_get_length(sass_args); i += 1) {
276+
union Sass_Value* sass_arg = sass_list_get_value(sass_args, i);
277+
PyObject* py_arg = NULL;
278+
if (!(py_arg = _to_py_value(sass_arg))) goto done;
279+
PyTuple_SetItem(py_args, i, py_arg);
280+
}
281+
282+
if (!(py_result = PyObject_CallObject(pyfunc, py_args))) goto done;
283+
sass_result = _to_sass_value(py_result);
284+
285+
done:
286+
if (sass_result == NULL) {
287+
PyObject* etype = NULL;
288+
PyObject* evalue = NULL;
289+
PyObject* etb = NULL;
290+
{
291+
PyErr_Fetch(&etype, &evalue, &etb);
292+
PyObject* traceback_mod = PyImport_ImportModule("traceback");
293+
PyObject* traceback_parts = PyObject_CallMethod(
294+
traceback_mod, "format_exception", "OOO", etype, evalue, etb
295+
);
296+
PyList_Insert(traceback_parts, 0, PyUnicode_FromString("\n"));
297+
PyObject* joinstr = PyUnicode_FromString("");
298+
PyObject* result = PyUnicode_Join(joinstr, traceback_parts);
299+
PyObject* bytes = PyUnicode_AsEncodedString(
300+
result, "UTF-8", "strict"
301+
);
302+
sass_result = sass_make_error(PySass_Bytes_AS_STRING(bytes));
303+
Py_DECREF(traceback_mod);
304+
Py_DECREF(traceback_parts);
305+
Py_DECREF(joinstr);
306+
Py_DECREF(result);
307+
Py_DECREF(bytes);
308+
}
309+
}
310+
Py_XDECREF(py_args);
311+
Py_XDECREF(py_result);
312+
return sass_result;
313+
}
314+
315+
316+
static void _add_custom_functions(
317+
struct Sass_Options* options, PyObject* custom_functions
318+
) {
319+
Py_ssize_t i;
320+
Sass_C_Function_List fn_list = sass_make_function_list(
321+
PyList_Size(custom_functions)
322+
);
323+
for (i = 0; i < PyList_GET_SIZE(custom_functions); i += 1) {
324+
PyObject* signature_and_func = PyList_GET_ITEM(custom_functions, i);
325+
PyObject* signature = PyTuple_GET_ITEM(signature_and_func, 0);
326+
PyObject* func = PyTuple_GET_ITEM(signature_and_func, 1);
327+
Sass_C_Function_Callback fn = sass_make_function(
328+
PySass_Bytes_AS_STRING(signature),
329+
_call_py_f,
330+
func
331+
);
332+
sass_function_set_list_entry(fn_list, i, fn);
333+
}
334+
sass_option_set_c_functions(options, fn_list);
335+
}
336+
38337
static PyObject *
39338
PySass_compile_string(PyObject *self, PyObject *args) {
40339
struct Sass_Context *ctx;
@@ -44,12 +343,14 @@ PySass_compile_string(PyObject *self, PyObject *args) {
44343
const char *error_message, *output_string;
45344
Sass_Output_Style output_style;
46345
int source_comments, error_status, precision;
346+
PyObject *custom_functions;
47347
PyObject *result;
48348

49349
if (!PyArg_ParseTuple(args,
50-
PySass_IF_PY3("yiiyyi", "siissi"),
350+
PySass_IF_PY3("yiiyyiO", "siissiO"),
51351
&string, &output_style, &source_comments,
52-
&include_paths, &image_path, &precision)) {
352+
&include_paths, &image_path, &precision,
353+
&custom_functions)) {
53354
return NULL;
54355
}
55356

@@ -60,6 +361,7 @@ PySass_compile_string(PyObject *self, PyObject *args) {
60361
sass_option_set_include_path(options, include_paths);
61362
sass_option_set_image_path(options, image_path);
62363
sass_option_set_precision(options, precision);
364+
_add_custom_functions(options, custom_functions);
63365

64366
sass_compile_data_context(context);
65367

@@ -85,12 +387,13 @@ PySass_compile_filename(PyObject *self, PyObject *args) {
85387
const char *error_message, *output_string, *source_map_string;
86388
Sass_Output_Style output_style;
87389
int source_comments, error_status, precision;
88-
PyObject *source_map_filename, *result;
390+
PyObject *source_map_filename, *custom_functions, *result;
89391

90392
if (!PyArg_ParseTuple(args,
91-
PySass_IF_PY3("yiiyyiO", "siissiO"),
393+
PySass_IF_PY3("yiiyyiOO", "siissiOO"),
92394
&filename, &output_style, &source_comments,
93-
&include_paths, &image_path, &precision, &source_map_filename)) {
395+
&include_paths, &image_path, &precision,
396+
&source_map_filename, &custom_functions)) {
94397
return NULL;
95398
}
96399

@@ -114,6 +417,7 @@ PySass_compile_filename(PyObject *self, PyObject *args) {
114417
sass_option_set_include_path(options, include_paths);
115418
sass_option_set_image_path(options, image_path);
116419
sass_option_set_precision(options, precision);
420+
_add_custom_functions(options, custom_functions);
117421

118422
sass_compile_file_context(context);
119423

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy