@@ -303,6 +303,37 @@ static union Sass_Value* _exception_to_sass_error() {
303303 return retv;
304304}
305305
306+ static PyObject* _exception_to_bytes () {
307+ /* Grabs a Bytes instance for you to PySass_Bytes_AS_STRING.
308+ Remember to Py_DECREF the object later!
309+ TODO: This is a terrible violation of DRY, see above.
310+ */
311+ PyObject* retv = NULL ;
312+ PyObject* etype = NULL ;
313+ PyObject* evalue = NULL ;
314+ PyObject* etb = NULL ;
315+ PyErr_Fetch (&etype, &evalue, &etb);
316+ PyErr_NormalizeException (&etype, &evalue, &etb);
317+ {
318+ PyObject* traceback_mod = PyImport_ImportModule (" traceback" );
319+ PyObject* traceback_parts = PyObject_CallMethod (
320+ traceback_mod, " format_exception" , " OOO" , etype, evalue, etb
321+ );
322+ PyList_Insert (traceback_parts, 0 , PyUnicode_FromString (" \n " ));
323+ PyObject* joinstr = PyUnicode_FromString (" " );
324+ PyObject* result = PyUnicode_Join (joinstr, traceback_parts);
325+ retv = PyUnicode_AsEncodedString (result, " UTF-8" , " strict" );
326+ Py_DECREF (traceback_mod);
327+ Py_DECREF (traceback_parts);
328+ Py_DECREF (joinstr);
329+ Py_DECREF (result);
330+ }
331+ Py_DECREF (etype);
332+ Py_DECREF (evalue);
333+ Py_DECREF (etb);
334+ return retv;
335+ }
336+
306337static union Sass_Value* _to_sass_value (PyObject* value) {
307338 union Sass_Value* retv = NULL ;
308339 PyObject* types_mod = PyImport_ImportModule (" sass" );
@@ -404,6 +435,107 @@ static void _add_custom_functions(
404435 sass_option_set_c_functions (options, fn_list);
405436}
406437
438+ Sass_Import_List _call_py_importer_f (
439+ const char * path,
440+ Sass_Importer_Entry cb,
441+ struct Sass_Compiler * comp
442+ ) {
443+ PyObject* pyfunc = (PyObject*)sass_importer_get_cookie (cb);
444+ PyObject* py_path = PyUnicode_FromString (path);
445+ PyObject* py_result = NULL ;
446+ PyObject *iterator;
447+ PyObject *import_item;
448+ Sass_Import_List sass_imports = NULL ;
449+ Py_ssize_t i;
450+
451+ py_result = PyObject_CallObject (pyfunc, PySass_IF_PY3 (" y" , " s" ), py_path);
452+
453+ if (!py_result) {
454+ sass_imports = sass_make_import_list (1 );
455+ sass_imports[0 ] = sass_make_import_entry (path, 0 , 0 );
456+
457+ PyObject* exc = _exception_to_bytes ();
458+ char * err = PySass_Bytes_AS_STRING (exc);
459+
460+ sass_import_set_error (sass_imports[0 ],
461+ err,
462+ 0 , 0 );
463+
464+ Py_XDECREF (exc);
465+ Py_XDECREF (py_result);
466+ return sass_imports;
467+ }
468+
469+ if (py_result == Py_None) {
470+ Py_XDECREF (py_result);
471+ return 0 ;
472+ }
473+
474+ sass_imports = sass_make_import_list (PyList_Size (py_result));
475+
476+ iterator = PyObject_GetIter (py_result);
477+ while (import_item = PyIter_Next (iterator)) {
478+ char * path_str = NULL ; /* XXX: Memory leak? */
479+ char * source_str = NULL ;
480+ char * sourcemap_str = NULL ;
481+
482+ /* TODO: Switch statement and error handling for default case. Better way? */
483+ if ( PyTuple_GET_SIZE (import_item) == 1 ) {
484+ PyArg_ParseTuple (import_item, " es" ,
485+ 0 , &path_str);
486+ } else if ( PyTuple_GET_SIZE (import_item) == 2 ) {
487+ PyArg_ParseTuple (import_item, " eses" ,
488+ 0 , &path_str, 0 , &source_str);
489+ } else if ( PyTuple_GET_SIZE (import_item) == 3 ) {
490+ PyArg_ParseTuple (import_item, " eseses" ,
491+ 0 , &path_str, 0 , &source_str, 0 , &sourcemap_str);
492+ }
493+
494+ /* We need to give copies of these arguments; libsass handles
495+ deallocation of them later, whereas path_str is left flapping
496+ in the breeze -- it's treated const, so that's okay. */
497+ if ( source_str ) source_str = strdup (source_str);
498+ if ( sourcemap_str ) sourcemap_str = strdup (sourcemap_str);
499+
500+ sass_imports[i] = sass_make_import_entry (path_str, source_str, sourcemap_str);
501+
502+ Py_XDECREF (import_item);
503+ }
504+
505+ Py_XDECREF (iterator);
506+ Py_XDECREF (py_result);
507+
508+ return sass_imports;
509+ }
510+
511+ static void _add_custom_importers (
512+ struct Sass_Options * options, PyObject* custom_importers
513+ ) {
514+ Py_ssize_t i;
515+ Sass_Importer_List importer_list;
516+
517+ if ( custom_importers == Py_None ) {
518+ return ;
519+ }
520+
521+ importer_list = sass_make_importer_list (PyList_Size (custom_importers));
522+
523+ for (i = 0 ; i < PyList_GET_SIZE (custom_importers); i += 1 ) {
524+ PyObject* item = PyList_GET_ITEM (custom_importers, i);
525+ int priority = 0 ;
526+ PyObject* import_function = NULL ;
527+
528+ PyArg_ParseTuple (item, " iO" ,
529+ &priority, &import_function);
530+
531+ importer_list[i] = sass_make_importer (_call_py_importer_f,
532+ priority,
533+ import_function);
534+ }
535+
536+ sass_option_set_c_importers (options, importer_list);
537+ }
538+
407539static PyObject *
408540PySass_compile_string (PyObject *self, PyObject *args) {
409541 struct Sass_Context *ctx;
@@ -414,13 +546,14 @@ PySass_compile_string(PyObject *self, PyObject *args) {
414546 Sass_Output_Style output_style;
415547 int source_comments, error_status, precision, indented;
416548 PyObject *custom_functions;
549+ PyObject *custom_importers;
417550 PyObject *result;
418-
551+
419552 if (!PyArg_ParseTuple (args,
420- PySass_IF_PY3 (" yiiyiOi " , " siisiOi " ),
553+ PySass_IF_PY3 (" yiiyiOiO " , " siisiOiO " ),
421554 &string, &output_style, &source_comments,
422555 &include_paths, &precision,
423- &custom_functions, &indented)) {
556+ &custom_functions, &indented, &custom_importers )) {
424557 return NULL ;
425558 }
426559
@@ -432,7 +565,8 @@ PySass_compile_string(PyObject *self, PyObject *args) {
432565 sass_option_set_precision (options, precision);
433566 sass_option_set_is_indented_syntax_src (options, indented);
434567 _add_custom_functions (options, custom_functions);
435-
568+ _add_custom_importers (options, custom_importers);
569+
436570 sass_compile_data_context (context);
437571
438572 ctx = sass_data_context_get_context (context);
@@ -444,6 +578,7 @@ PySass_compile_string(PyObject *self, PyObject *args) {
444578 (short int ) !error_status,
445579 error_status ? error_message : output_string
446580 );
581+
447582 sass_delete_data_context (context);
448583 return result;
449584}
@@ -457,13 +592,15 @@ PySass_compile_filename(PyObject *self, PyObject *args) {
457592 const char *error_message, *output_string, *source_map_string;
458593 Sass_Output_Style output_style;
459594 int source_comments, error_status, precision;
460- PyObject *source_map_filename, *custom_functions, *result;
461-
595+ PyObject *source_map_filename, *custom_functions, *custom_importers,
596+ *result;
597+
462598 if (!PyArg_ParseTuple (args,
463- PySass_IF_PY3 (" yiiyiOO " , " siisiOO " ),
599+ PySass_IF_PY3 (" yiiyiOOO " , " siisiOOO " ),
464600 &filename, &output_style, &source_comments,
465601 &include_paths, &precision,
466- &source_map_filename, &custom_functions)) {
602+ &source_map_filename, &custom_functions,
603+ &custom_importers)) {
467604 return NULL ;
468605 }
469606
@@ -487,6 +624,7 @@ PySass_compile_filename(PyObject *self, PyObject *args) {
487624 sass_option_set_include_path (options, include_paths);
488625 sass_option_set_precision (options, precision);
489626 _add_custom_functions (options, custom_functions);
627+ _add_custom_importers (options, custom_importers);
490628
491629 sass_compile_file_context (context);
492630
0 commit comments