Skip to content

Commit 9a7eebb

Browse files
2026.1 patch 1 release code drop
1 parent 9cec853 commit 9a7eebb

9 files changed

Lines changed: 1065 additions & 26 deletions

P4API.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2828
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2929
*
30-
* $Id: //depot/main/p4-python/P4API.cpp#66 $
30+
* $Id: //depot/main/p4-python/P4API.cpp#67 $
3131
*
3232
* Build instructions:
3333
* Use Distutils - see accompanying setup.py
@@ -208,6 +208,18 @@ static PyObject * P4Adapter_set_env(P4Adapter * self, PyObject *args)
208208
return NULL;
209209
}
210210

211+
static PyObject * P4Adapter_set_var(P4Adapter * self, PyObject *args)
212+
{
213+
const char * var;
214+
const char * val;
215+
216+
if ( PyArg_ParseTuple(args, "ss", &var, &val) ) {
217+
return self->clientAPI->SetVar( var, val );
218+
}
219+
220+
return NULL;
221+
}
222+
211223

212224
static PyObject * P4Adapter_run(P4Adapter * self, PyObject * args)
213225
{
@@ -639,6 +651,8 @@ static PyMethodDef P4Adapter_methods[] = {
639651
"Get values from the Perforce environment"},
640652
{"set_env", (PyCFunction)P4Adapter_set_env, METH_VARARGS,
641653
"Set values in the registry (if available on the platform) for the Perforce environment"},
654+
{"set_var", (PyCFunction)P4Adapter_set_var, METH_VARARGS,
655+
"Set a per-command server variable (e.g. limitMap0) on the underlying client"},
642656
{"run", (PyCFunction)P4Adapter_run, METH_VARARGS,
643657
"Runs a command"},
644658
{"format_spec", (PyCFunction)P4Adapter_formatSpec, METH_VARARGS,

PythonClientAPI.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2525
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2626
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2727
28-
$Id: //depot/main/p4-python/PythonClientAPI.cpp#82 $
28+
$Id: //depot/main/p4-python/PythonClientAPI.cpp#85 $
2929
*******************************************************************************/
3030

3131
#include <Python.h>
@@ -825,6 +825,13 @@ PyObject * PythonClientAPI::Run( const char *cmd, int argc, char * const *argv )
825825
return NULL;
826826
}
827827

828+
// Check for pending exception from callbacks
829+
PyObject *excType, *excValue, *excTb;
830+
if( ui.GetPendingException(&excType, &excValue, &excTb) ) {
831+
PyErr_Restore(excType, excValue, excTb);
832+
return NULL;
833+
}
834+
828835
p4py::P4Result &results = ui.GetResults();
829836

830837
if ( results.ErrorCount() && exceptionLevel ) {
@@ -1075,6 +1082,17 @@ PyObject * PythonClientAPI::SetProtocol( const char * var, const char * val )
10751082
Py_RETURN_NONE;
10761083
}
10771084

1085+
//
1086+
// Sets a per-command server variable (e.g. limitMap). The P4 C++ API clears
1087+
// these after each Run(), so they do not leak into subsequent commands.
1088+
//
1089+
PyObject * PythonClientAPI::SetVar( const char * var, const char * val )
1090+
{
1091+
client.SetVar( var, val );
1092+
1093+
Py_RETURN_NONE;
1094+
}
1095+
10781096
//
10791097
// Gets a protocol value
10801098
//

PythonClientAPI.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2727
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2828
*
29-
* $Id: //depot/main/p4-python/PythonClientAPI.h#45 $
29+
* $Id: //depot/main/p4-python/PythonClientAPI.h#46 $
3030
*
3131
* Build instructions:
3232
* Use Distutils - see accompanying setup.py
@@ -225,6 +225,9 @@ class PythonClientAPI
225225
PyObject * SetProtocol( const char * var, const char *val );
226226
PyObject * GetProtocol( const char * var );
227227

228+
// Per-command server variables
229+
PyObject * SetVar( const char *var, const char *val );
230+
228231
// Exception levels:
229232
//
230233
// 0 - No exceptions raised

PythonClientProgress.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3333
* Description : C++ Subclass for ClientProgress used by Python ClientProgress.
3434
* Allows Perforce API to indicate progress of calls to P4Python
3535
*
36-
* $Id: //depot/main/p4-python/PythonClientProgress.cpp#5 $
36+
* $Id: //depot/main/p4-python/PythonClientProgress.cpp#6 $
3737
*
3838
******************************************************************************/
3939

@@ -60,8 +60,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6060

6161
using namespace std;
6262

63-
PythonClientProgress::PythonClientProgress(PyObject * prog, int type)
64-
: progress(prog)
63+
PythonClientProgress::PythonClientProgress(PythonClientUser *clientUser, PyObject * prog, int type)
64+
: ui(clientUser), progress(prog)
6565
{
6666
EnsurePythonLock guard;
6767

@@ -70,13 +70,15 @@ PythonClientProgress::PythonClientProgress(PyObject * prog, int type)
7070
Py_DECREF( res );
7171
}
7272
else {
73-
cout << "Exception thrown in init" << endl;
74-
PyErr_PrintEx(0);
73+
PyObject *excType, *excValue, *excTb;
74+
PyErr_Fetch(&excType, &excValue, &excTb);
75+
ui->SetPendingException(excType, excValue, excTb);
7576
}
7677
}
7778

7879
PythonClientProgress::~PythonClientProgress()
7980
{
81+
this->ui = NULL;
8082
this->progress = NULL;
8183
}
8284

@@ -89,7 +91,9 @@ void PythonClientProgress::Description( const StrPtr *desc, int units )
8991
Py_DECREF( res );
9092
}
9193
else {
92-
cout << "Exception thrown in setDescription" << endl;
94+
PyObject *excType, *excValue, *excTb;
95+
PyErr_Fetch(&excType, &excValue, &excTb);
96+
ui->SetPendingException(excType, excValue, excTb);
9397
}
9498
}
9599

@@ -102,7 +106,9 @@ void PythonClientProgress::Total( long total )
102106
Py_DECREF( res );
103107
}
104108
else {
105-
cout << "Exception thrown in setTotal" << endl;
109+
PyObject *excType, *excValue, *excTb;
110+
PyErr_Fetch(&excType, &excValue, &excTb);
111+
ui->SetPendingException(excType, excValue, excTb);
106112
}
107113

108114
}
@@ -113,8 +119,10 @@ int PythonClientProgress::Update( long pos )
113119

114120
PyObject * result = PyObject_CallMethod( this->progress , (char*) "update", (char*)"l", pos );
115121
if( result == NULL ) { // exception thrown
116-
cout << "Exception thrown in update" << endl;
117-
return 1; // TODO: Is this the correct value to return in case of an error?
122+
PyObject *excType, *excValue, *excTb;
123+
PyErr_Fetch(&excType, &excValue, &excTb);
124+
ui->SetPendingException(excType, excValue, excTb);
125+
return 1;
118126
}
119127
else {
120128
Py_DECREF( result );
@@ -132,7 +140,9 @@ void PythonClientProgress::Done( int fail )
132140
Py_DECREF( res );
133141
}
134142
else {
135-
cout << "Exception thrown in Done" << endl;
143+
PyObject *excType, *excValue, *excTb;
144+
PyErr_Fetch(&excType, &excValue, &excTb);
145+
ui->SetPendingException(excType, excValue, excTb);
136146
}
137147
}
138148

PythonClientProgress.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,20 @@
2424
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2525
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2626
*
27-
* $Id: //depot/main/p4-python/PythonClientProgress.h#1 $
27+
* $Id: //depot/main/p4-python/PythonClientProgress.h#2 $
2828
*
2929
*/
3030

3131

3232
#ifndef PYTHONCLIENTPROGRESS_H_
3333
#define PYTHONCLIENTPROGRESS_H_
3434

35+
class PythonClientUser;
36+
3537
class PythonClientProgress : public ClientProgress
3638
{
3739
public:
38-
PythonClientProgress(PyObject * progress, int t);
40+
PythonClientProgress(PythonClientUser *ui, PyObject * progress, int t);
3941
virtual ~PythonClientProgress();
4042

4143
public:
@@ -45,6 +47,7 @@ class PythonClientProgress : public ClientProgress
4547
virtual void Done( int fail );
4648

4749
private:
50+
PythonClientUser * ui;
4851
PyObject * progress;
4952
};
5053

PythonClientUser.cpp

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2525
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2626
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2727
28-
$Id: //depot/main/p4-python/PythonClientUser.cpp#37 $
28+
$Id: //depot/main/p4-python/PythonClientUser.cpp#38 $
2929
3030
*******************************************************************************/
3131

@@ -71,10 +71,10 @@ PythonClientUser::PythonClientUser( PythonDebug * dbg, p4py::SpecMgr *s )
7171
track = false;
7272
alive = 1;
7373
apiLevel = atoi( P4Tag::l_client );
74-
74+
7575
Py_INCREF(Py_None);
7676
input = Py_None;
77-
77+
7878
Py_INCREF(Py_None);
7979
resolver = Py_None;
8080

@@ -83,6 +83,10 @@ PythonClientUser::PythonClientUser( PythonDebug * dbg, p4py::SpecMgr *s )
8383

8484
Py_INCREF(Py_None);
8585
progress = Py_None;
86+
87+
pendingExcType = NULL;
88+
pendingExcValue = NULL;
89+
pendingExcTb = NULL;
8690
}
8791

8892
PythonClientUser::~PythonClientUser()
@@ -91,6 +95,9 @@ PythonClientUser::~PythonClientUser()
9195
Py_DECREF(resolver);
9296
Py_DECREF(handler);
9397
Py_DECREF(progress);
98+
Py_XDECREF(pendingExcType);
99+
Py_XDECREF(pendingExcValue);
100+
Py_XDECREF(pendingExcTb);
94101
}
95102

96103
void PythonClientUser::Reset()
@@ -99,6 +106,49 @@ void PythonClientUser::Reset()
99106
// input data is untouched
100107

101108
alive = 1; // yes, we want data from the server
109+
ClearPendingException();
110+
}
111+
112+
void PythonClientUser::SetPendingException(PyObject *type, PyObject *value, PyObject *tb)
113+
{
114+
// Clear any existing pending exception first
115+
Py_XDECREF(pendingExcType);
116+
Py_XDECREF(pendingExcValue);
117+
Py_XDECREF(pendingExcTb);
118+
119+
// Store new exception (PyErr_Fetch already gave us owned references)
120+
pendingExcType = type;
121+
pendingExcValue = value;
122+
pendingExcTb = tb;
123+
}
124+
125+
bool PythonClientUser::GetPendingException(PyObject **type, PyObject **value, PyObject **tb)
126+
{
127+
if (pendingExcType == NULL) {
128+
return false;
129+
}
130+
131+
*type = pendingExcType;
132+
*value = pendingExcValue;
133+
*tb = pendingExcTb;
134+
135+
// Transfer ownership - caller will use PyErr_Restore which steals references
136+
pendingExcType = NULL;
137+
pendingExcValue = NULL;
138+
pendingExcTb = NULL;
139+
140+
return true;
141+
}
142+
143+
void PythonClientUser::ClearPendingException()
144+
{
145+
Py_XDECREF(pendingExcType);
146+
Py_XDECREF(pendingExcValue);
147+
Py_XDECREF(pendingExcTb);
148+
149+
pendingExcType = NULL;
150+
pendingExcValue = NULL;
151+
pendingExcTb = NULL;
102152
}
103153

104154
void PythonClientUser::Finished()
@@ -127,6 +177,9 @@ bool PythonClientUser::CallOutputMethod( const char * method, PyObject * data)
127177

128178
PyObject * result = PyObject_CallMethod( this->handler , (char*) method, (char*)"O", data );
129179
if( result == NULL ) { // exception thrown
180+
PyObject *excType, *excValue, *excTb;
181+
PyErr_Fetch(&excType, &excValue, &excTb);
182+
SetPendingException(excType, excValue, excTb);
130183
alive = 0;
131184
}
132185
else {
@@ -228,7 +281,7 @@ ClientProgress * PythonClientUser::CreateProgress( int type )
228281
return NULL;
229282
}
230283

231-
return new PythonClientProgress(progress, type);
284+
return new PythonClientProgress(this, progress, type);
232285
}
233286

234287
void PythonClientUser::HandleError( Error *e )
@@ -588,14 +641,17 @@ int PythonClientUser::Resolve( ClientMerge *m, Error *e )
588641

589642
PyObject * result = PyObject_CallMethod( this->resolver , (char*)"resolve", (char*)"(O)", mergeData );
590643
if( result == NULL ) { // exception thrown, bug out of here
644+
PyObject *excType, *excValue, *excTb;
645+
PyErr_Fetch(&excType, &excValue, &excTb);
646+
SetPendingException(excType, excValue, excTb);
647+
Py_DECREF(mergeData);
591648
return CMS_QUIT;
592649
}
593-
else {
594-
Py_DECREF( result );
595-
}
596650

597651
if (IsString(result)) {
598652
StrBuf reply = GetPythonString( result );
653+
Py_DECREF( result );
654+
Py_DECREF(mergeData);
599655

600656
if( reply == "ay" ) return CMS_YOURS;
601657
else if( reply == "at" ) return CMS_THEIRS;
@@ -613,6 +669,8 @@ int PythonClientUser::Resolve( ClientMerge *m, Error *e )
613669
}
614670
}
615671
else {
672+
Py_DECREF( result );
673+
Py_DECREF(mergeData);
616674
PyErr_WarnEx( PyExc_UserWarning, "[P4::Resolve] Illegal response : Expected String", 1);
617675
return CMS_QUIT;
618676
}
@@ -666,13 +724,16 @@ int PythonClientUser::Resolve( ClientResolveA *m, int preview, Error *e )
666724

667725
PyObject * result = PyObject_CallMethod( this->resolver , (char*)"actionResolve", (char*)"(O)", mergeData );
668726
if( result == NULL ) { // exception thrown, bug out of here
727+
PyObject *excType, *excValue, *excTb;
728+
PyErr_Fetch(&excType, &excValue, &excTb);
729+
SetPendingException(excType, excValue, excTb);
730+
Py_DECREF(mergeData);
669731
return CMS_QUIT;
670732
}
671-
else {
672-
Py_DECREF( result );
673-
}
674733

675734
StrBuf reply = GetPythonString( result );
735+
Py_DECREF( result );
736+
Py_DECREF(mergeData);
676737

677738
if( reply == "ay" ) return CMS_YOURS;
678739
else if( reply == "at" ) return CMS_THEIRS;

0 commit comments

Comments
 (0)