Пример #1
0
    def test_task_init(self):
        """
        Creating an instance of a patched celery.Task
            will yield a patched instance
        """
        task = celery.Task()

        # Assert instance methods are patched
        self.assertIsInstance(task.__init__, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(task.apply, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(task.apply_async, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(task.run, wrapt.BoundFunctionWrapper)
Пример #2
0
    def test_patch_task(self):
        """
        When celery.Task is patched
            we patch the __init__, apply, apply_async, and run methods
        """
        # Assert base class methods are patched
        self.assertIsInstance(celery.Task.__init__, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(celery.Task.apply, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(celery.Task.apply_async, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(celery.Task.run, wrapt.BoundFunctionWrapper)

        # Create an instance of a Task
        task = celery.Task()

        # Assert instance methods are patched
        self.assertIsInstance(task.__init__, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(task.apply, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(task.apply_async, wrapt.BoundFunctionWrapper)
        self.assertIsInstance(task.run, wrapt.BoundFunctionWrapper)
class Wkhtmltopdf(object):
    '''Wkhtmltopdf class container to use the robust wkhtmltopdf library which is
    capable of generating a PDF from HTML, CSS, and JavaScript using a modified
    WebKit engine. This extension allows you to easily incorporate this functionality
    into your Flask app.

    In addition to the dependencies automatically installed, you must manually
    download the appropriate wkhtmltopdf command line tool from
    http://wkhtmltopdf.org/downloads.html

    The main function render_template_to_pdf() works similar to Flask's built-in
    render_template() function and in fact utilizes some of the same underlying
    functions. However, as the name suggests, it will return a pdf instead of
    a rendered webpage.

    To initialize, pass your flask app's object to Flask-WkHTMLtoPDF::

        from flask_wkhtmltopdf import Wkhtmltopdf

        app = Flask(__name__)
        wkhtmltopdf = Wkhtmltopdf(app)

    Then pass the template to the render_template_to_pdf() function. You can
    pass Jinja2 params just like with render_template()::

        render_template_to_pdf('test.html', download=True, save=False, param='hello')

    Celery, an asynchronous task queue, is highly suggested when using Flask-WkHTMLtoPDF as rendering
    the PDF can be resource heavy and take an unacceptable amount of time to generate. To
    enable Celery, set 'WKHTMLTOPDF_USE_CELERY = True' in your Flask app's config.

    You must add three variables to your Flask app's config::

        WKHTMLTOPDF_BIN_PATH = r'C:\Program Files\wkhtmltopdf\bin'
        PDF_DIR_PATH =  os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static', 'pdf')



    '''
    use_celery = False

    def __init__(self, app=None):
        if app is not None:
            self._init_app(app)

    def _init_app(self, app):
        '''Initalizes the app with Flask-WkHTMLtoPDF.

        :param app: The Flask application object.

        '''

        self.use_celery = app.config.get('WKHTMLTOPDF_USE_CELERY', False)
        self.add_path = app.config.get('WKHTMLTOPDF_BIN_PATH', None)
        self.pdf_dir_path = app.config.get('PDF_DIR_PATH', None)


    #checks to see if condition is true before applying decorator.
    def _maybe_decorate(condition, decorator):
        return decorator if condition else lambda x: x


    @_maybe_decorate(use_celery, celery.Task())
    def render_template_to_pdf(self, template_name_or_list, save=False, download=False, wkhtmltopdf_args=None, **context):
        '''Renders a template from the template folder with the given
        context and produces a pdf. As this can be resource intensive, the function
        can easily be decorated with celery.task() by setting the WKHTMLTOPDF_USE_CELERY to True.

        :param template_name_or_list:    The name of the template to be
                                         rendered, or an iterable with template names.
                                         The first one existing will be rendered.
        :param save:    Specifies whether to save the temporary pdf generated. Defaults to False.
        :param download:    Specifies if the pdf should be displayed in the browser
                            or downloaded as an attachment. Defaults to False (in browser).
        :param context:    The variables that should be available in the
                           context of the Jinja2 template.
        :param wkhtmltopdf_args: Optional list of arguments to send to wkhtmltopdf (list of -- options)
        '''

        #Get the system's PATH and add wkhtmltopdf to it if necessary
        path = os.getenv("PATH")
        if "wkhtmltopdf" not in path:
            if self.add_path is None:
                raise ValueError('WKHTMLTOPDF_BIN_PATH config variable must be set in the Flask app or added to the OS PATH')
            os.environ["PATH"] += os.pathsep + self.add_path


        #render appropriate template and write to a temp file
        rendered = render_template(template_name_or_list, **context).encode('utf-8')
        with tempfile.NamedTemporaryFile(suffix='.html', dir=os.path.dirname(__file__), delete=False, mode='w') as temp_html:
            temp_html.write(rendered)

        #Checks to see if the pdf directory exists and generates a random pdf name
        if self.pdf_dir_path is None:
            raise ValueError('PDF_DIR_PATH config variable must be set in the Flask app')
        if not os.path.isdir(self.pdf_dir_path):
            os.makedirs(self.pdf_dir_path)
        with tempfile.NamedTemporaryFile(suffix='.pdf', dir=self.pdf_dir_path, delete=False) as temp_pdf:
            pass

        cli_options = ""
        
        # Parse argument list supplied and add them as options to wkhtmltopdf
        if wkhtmltopdf_args:
            for argument in wkhtmltopdf_args:
                if argument.startswith('--'):
                    cli_options += ' ' + argument
                else:
                    cli_options += ' --' + argument

        #Run wkhtmltopdf via the appropriate subprocess call
        wkhtmltopdfargs = "wkhtmltopdf" + cli_options + " " + temp_html.name + " " + temp_pdf.name

        #A work around for python 2.6
        try:
            subprocess.check_output(wkhtmltopdfargs, shell=True)
        except:
            def check_output(*popenargs, **kwargs):
                process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
                output, unused_err = process.communicate()
                retcode = process.poll()
                if retcode:
                    cmd = kwargs.get("args")
                    if cmd is None:
                        cmd = popenargs[0]
                    error = subprocess.CalledProcessError(retcode, cmd)
                    error.output = output
                    raise error
                return output
            subprocess.check_output = check_output
            subprocess.check_output(wkhtmltopdfargs, shell=True)

        #Remove the temporary files created
        os.remove(temp_html.name)

        with open(temp_pdf.name, 'rb') as f:
            binary_pdf = f.read()

        response = make_response(binary_pdf)
        response.headers['Content-Type'] = 'application/pdf'
        if download is True:
            response.headers['Content-Disposition'] = 'attachment; filename=%s.pdf' % temp_pdf.name
        else:
            response.headers['Content-Disposition'] = 'inline; filename=%s.pdf' % temp_pdf.name

        if save is False:
            os.remove(temp_pdf.name)

        return response