@@ -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+
38337static PyObject *
39338PySass_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