summaryrefslogtreecommitdiffstats
path: root/pyuno/inc/pyuno.hxx
blob: f7cab36c7327076bc720c0b86b14cb909e2eac62 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#pragma once

#ifndef Py_PYTHON_H
#include <Python.h>
#endif // #ifdef Py_PYTHON_H

#include <com/sun/star/uno/Any.hxx>

namespace com::sun::star::uno { class XComponentContext; }
namespace com::sun::star::uno { template <typename > class Reference; }

/**
   External interface of the Python UNO bridge.

   This is a C++ interface, because the core UNO components
   invocation and proxyfactory are used to implement the bridge.

   This interface is somewhat private and my change in future.

   A scripting framework implementation may use this interface
   to do the necessary conversions.
*/

#if defined LO_DLLIMPLEMENTATION_PYUNO
#define LO_DLLPUBLIC_PYUNO SAL_DLLPUBLIC_EXPORT
#else
#define LO_DLLPUBLIC_PYUNO SAL_DLLPUBLIC_IMPORT
#endif

/** function called by the python runtime to initialize the
    pyuno module.

    preconditions: python has been initialized before and
                   the global interpreter lock is held
*/

extern "C" LO_DLLPUBLIC_PYUNO PyObject* PyInit_pyuno();

namespace pyuno
{

enum NotNull
{
    /** definition of a no acquire enum for ctors
     */
    NOT_NULL
};

/** Helper class for keeping references to python objects.
    BEWARE: Look up every python function you use to check
    whether you get an acquired or not acquired object pointer
    (python terminus for a not acquired object pointer
    is 'borrowed reference'). Use in the acquired pointer cases the
    PyRef( pointer, SAL_NO_ACQUIRE) ctor.

    precondition: python has been initialized before and
    the global interpreter lock is held

*/
class PyRef
{
    PyObject *m;
public:
    PyRef () : m(nullptr) {}
    PyRef( PyObject * p ) : m( p ) { Py_XINCREF( m ); }

    PyRef( PyObject * p, __sal_NoAcquire ) : m( p ) {}

    PyRef( PyObject * p, __sal_NoAcquire, NotNull ) : m( p )
    {
        if (!m)
            throw std::bad_alloc();
    }

    PyRef(const PyRef &r) : m(r.get()) { Py_XINCREF(m); }
    PyRef(PyRef &&r) noexcept : m(r.get()) { r.scratch(); }

    ~PyRef() { Py_XDECREF( m ); }

    PyObject *get() const noexcept { return m; }

    PyObject * getAcquired() const
    {
        Py_XINCREF( m );
        return m;
    }

    PyRef& operator=(const PyRef& r)
    {
        PyObject *tmp = m;
        m = r.getAcquired();
        Py_XDECREF(tmp);
        return *this;
    }

    PyRef& operator=(PyRef&& r)
    {
        PyObject *tmp = m;
        m = r.get();
        r.scratch();
        Py_XDECREF(tmp);
        return *this;
    }

    bool operator == (  const PyRef & r ) const
    {
        return r.get() == m;
    }

    /** clears the reference without decreasing the reference count
        only seldom needed ! */
    void scratch() noexcept
    {
        m = nullptr;
    }

    /** returns 1 when the reference points to a python object python object,
        otherwise 0.
    */
    bool is() const
    {
        return m != nullptr;
    }

    struct Hash
    {
        sal_IntPtr operator () ( const PyRef &r) const { return reinterpret_cast<sal_IntPtr>( r.get() ); }
    };
};

//struct stRuntimeImpl;
typedef struct stRuntimeImpl RuntimeImpl;

enum ConversionMode { ACCEPT_UNO_ANY, REJECT_UNO_ANY };


/** The pyuno::Runtime class keeps the internal state of the python UNO bridge
    for the currently in use python interpreter.

    You may keep a Runtime instance, use it from a different thread, etc. But you must
    make sure to fulfill all preconditions mentioned for the specific methods.
*/

class LO_DLLPUBLIC_PYUNO Runtime
{
    RuntimeImpl *impl;

    /**
        Safely unpacks a Python iterator into a sequence, then
        stores it in an Any. Used internally by pyObject2Any
    */
    bool pyIterUnpack( PyObject *const, css::uno::Any & ) const;
public:
    ~Runtime( );

    /**
        preconditions: python has been initialized before,
        the global interpreter lock is held and pyuno
        has been initialized for the currently used interpreter.

        Note: This method exists for efficiency reasons to save
        lookup costs for any2PyObject and pyObject2Any

        @throw RuntimeException in case the runtime has not been
               initialized before
     */
    Runtime();

    Runtime( const Runtime & );
    Runtime & operator = ( const Runtime & );

    /** Initializes the python-UNO bridge. May be called only once per python interpreter.

        @param ctx the component context is used to instantiate bridge services needed
        for bridging such as invocation, typeconverter, invocationadapterfactory, etc.

        preconditions: python has been initialized before and
        the global interpreter lock is held and pyuno is not
        initialized (see isInitialized() ).

        @throw RuntimeException in case the thread is not attached or the runtime
                                has not been initialized.
    */
    static void initialize(
        const css::uno::Reference< css::uno::XComponentContext > & ctx );

    /** Checks, whether the uno runtime is already initialized in the current python interpreter.

        @throws css::uno::RuntimeException
     */
    static bool isInitialized();

    /** converts something contained in a UNO Any to a Python object

        preconditions: python has been initialized before,
        the global interpreter lock is held and pyuno::Runtime
        has been initialized.

        @throws css::script::CannotConvertException
        @throws css::lang::IllegalArgumentException
        @throws css::uno::RuntimeException
    */
    PyRef any2PyObject (const css::uno::Any &source ) const;

    /** converts a Python object to a UNO any

        preconditions: python has been initialized before,
        the global interpreter lock is held and pyuno
        has been initialized

        @throws css::uno::RuntimeException
    */
    css::uno::Any pyObject2Any (
        const PyRef & source , enum ConversionMode mode = REJECT_UNO_ANY ) const;

    /** extracts a proper uno exception from a given python exception
     */
    css::uno::Any extractUnoException(
        const PyRef & excType, const PyRef & excValue, const PyRef & excTraceback) const;

    /** Returns the internal handle. Should only be used by the module implementation
     */
    RuntimeImpl *getImpl() const { return impl; }
};


/** helper class for attaching the current thread to the python runtime.

    Attaching is done creating a new threadstate for the given interpreter
    and acquiring the global interpreter lock.

    Usage:

    ... don't use python here
    {
        PyThreadAttach guard( PyInterpreterState_Head() );
        {
            ... do whatever python code you want
            {
               PyThreadDetach antiguard;
               ... don't use python here
            }
            ... do whatever python code you want
        }
    }
    ... don't use python here

    Note: The additional scope brackets after the PyThreadAttach are needed,
          e.g. when you would leave them away, dtors of potential pyrefs
          may be called after the thread has detached again.
 */
class LO_DLLPUBLIC_PYUNO PyThreadAttach
{
    PyThreadState *tstate;
    bool m_isNewState;
    PyThreadAttach ( const PyThreadAttach & ) = delete;
    PyThreadAttach & operator = ( const PyThreadAttach & ) = delete;
public:

    /** Creates a new python threadstate and acquires the global interpreter lock.
        precondition: The current thread MUST NOT hold the global interpreter lock.
        postcondition: The global interpreter lock is acquired

        @throws css::uno::RuntimeException
             in case no pythread state could be created
     */
    PyThreadAttach( PyInterpreterState *interp);


    /** Releases the global interpreter lock and destroys the thread state.
     */
    ~PyThreadAttach();
};

/** helper class for detaching the current thread from the python runtime
    to do some blocking, non-python related operation.

    @see PyThreadAttach
*/
class PyThreadDetach
{
    PyThreadState *tstate;
    PyThreadDetach ( const PyThreadDetach & ) = delete;
    PyThreadDetach & operator = ( const PyThreadDetach & ) = delete;

public:
    /** Releases the global interpreter lock.

       precondition: The current thread MUST hold the global interpreter lock.
       postcondition: The current thread does not hold the global interpreter lock anymore.

       @throws css::uno::RuntimeException
    */
    PyThreadDetach();
    /** Acquires the global interpreter lock again
    */
    ~PyThreadDetach();
};

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */