1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_PY_ERROR_H
25 #define PXR_BASE_TF_PY_ERROR_H
26 
27 /// \file tf/error.h
28 /// Provide facilities for error handling in script.
29 
30 #include "pxr/pxr.h"
31 
32 #include "pxr/base/tf/api.h"
33 #include "pxr/base/tf/errorMark.h"
34 
35 #include <boost/python/default_call_policies.hpp>
36 
37 PXR_NAMESPACE_OPEN_SCOPE
38 
39 /// Converts any \a TfError objects in \a m into python exceptions.  User code
40 /// should generally not have to call this.  User code should generally not
41 /// have to call this, unless it's manually bridging between C++ & Python.
42 TF_API
43 bool TfPyConvertTfErrorsToPythonException(TfErrorMark const &m);
44 
45 /// Convert the current python exception to \a TfError objects and post them
46 /// to the error system.  User code should generally not have to call this,
47 /// unless it's manually bridging between C++ & Python.
48 TF_API
49 void TfPyConvertPythonExceptionToTfErrors();
50 
51 /// \class TfPyRaiseOnError
52 ///
53 /// A boost.python call policy class which, when applied to a wrapped
54 /// function, will create an error mark before calling the function, and check
55 /// that error mark after the function has completed.  If any TfErrors have
56 /// occurred, they will be raised as python exceptions.
57 ///
58 /// This facility does not need to be used by clients in general.  It is only
59 /// required for wrapped functions and methods that do not appear directly in an
60 /// extension module.  For instance, the map and sequence proxy objects use
61 /// this, since they are created on the fly.
62 template <typename Base = boost::python::default_call_policies>
63 struct TfPyRaiseOnError : Base
64 {
65   public:
66 
67     // This call policy provides a customized argument_package.  We need to do
68     // this to store the TfErrorMark that we use to collect TfErrors that
69     // occurred during the call and convert them to a python exception at the
70     // end.  It doesn't work to do this in the precall() and postcall()
71     // because if the call itself throws a c++ exception, the postcall() isn't
72     // executed and we can't destroy the TfErrorMark, leaving it dangling.
73     // Using the argument_package solves this since it is a local variable it
74     // will be destroyed whether or not the call throws.  This is not really a
75     // publicly documented boost.python feature, however.  :-/
76     template <class BaseArgs>
77     struct ErrorMarkAndArgs {
ErrorMarkAndArgsTfPyRaiseOnError::ErrorMarkAndArgs78         /* implicit */ErrorMarkAndArgs(BaseArgs base_) : base(base_) {}
79         operator const BaseArgs &() const { return base; }
80         operator BaseArgs &() { return base; }
81         BaseArgs base;
82         TfErrorMark errorMark;
83     };
84     typedef ErrorMarkAndArgs<typename Base::argument_package> argument_package;
85 
86     /// Default constructor.
TfPyRaiseOnErrorTfPyRaiseOnError87     TfPyRaiseOnError() {}
88 
89     // Only accept our argument_package type, since we must ensure that we're
90     // using it so we track a TfErrorMark.
precallTfPyRaiseOnError91     bool precall(argument_package const &a) { return Base::precall(a); }
92 
93     // Only accept our argument_package type, since we must ensure that we're
94     // using it so we track a TfErrorMark.
postcallTfPyRaiseOnError95     PyObject *postcall(argument_package const &a, PyObject *result) {
96         result = Base::postcall(a, result);
97         if (result && TfPyConvertTfErrorsToPythonException(a.errorMark)) {
98             Py_DECREF(result);
99             result = NULL;
100         }
101         return result;
102     }
103 };
104 
105 struct Tf_PyErrorClearer {
Tf_PyErrorClearerTf_PyErrorClearer106     Tf_PyErrorClearer() : clearOnDestruction(true) {}
~Tf_PyErrorClearerTf_PyErrorClearer107     ~Tf_PyErrorClearer() {
108         if (clearOnDestruction)
109             mark.Clear();
110     }
DismissTf_PyErrorClearer111     void Dismiss() { clearOnDestruction = false; }
112     TfErrorMark mark;
113     bool clearOnDestruction;
114 };
115 
116 PXR_NAMESPACE_CLOSE_SCOPE
117 
118 #endif // PXR_BASE_TF_PY_ERROR_H
119