Skip to content

Commit

Permalink
2024.1 release code drop
Browse files Browse the repository at this point in the history
This release of P4Python supports build against P4API 2024.1
  • Loading branch information
hjain-perforce committed Jul 22, 2024
1 parent 87045d5 commit bf7146a
Show file tree
Hide file tree
Showing 16 changed files with 380 additions and 417 deletions.
332 changes: 12 additions & 320 deletions LICENSE.txt

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ include Version
include p4test.py
include job_trigger.py
include tools/*.py
include pyproject.toml
67 changes: 58 additions & 9 deletions P4.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
This uses the Python type P4API.P4Adapter, which is a wrapper for the
Perforce ClientApi object.
$Id: //depot/main/p4-python/P4.py#110 $
$Id: //depot/main/p4-python/P4.py#112 $
#*******************************************************************************
# Copyright (c) 2007-2010, Perforce Software, Inc. All rights reserved.
Expand Down Expand Up @@ -43,30 +43,43 @@
See accompanying LICENSE.txt including for redistribution permission.
"""

import sys, datetime
import sys, datetime, time
import re
import shutil
from contextlib import contextmanager
import uuid, tempfile
import os, os.path, platform
import subprocess
import threading

# P4Exception - some sort of error occurred
class P4Exception(Exception):
"""Exception thrown by P4 in case of Perforce errors or warnings"""

def __init__(self, value):
Exception.__init__(self)

super().__init__(value)
if isinstance(value, (list, tuple)) and len(value) > 2:
self.value = value[0]
self.errors = value[1]
self.warnings = value[2]
if len(value[1]) > 0 or len(value[2]) > 0:
self.value = value[0]
self.errors = value[1]
self.warnings = value[2]
else:
self.value = value[0]
self.errors = [re.sub(r'\[.*?\] ', '', str(self.value).split("\n")[0])]
self.warnings = value[2]
else:
self.value = value

def __str__(self):
return str(self.value)
if self.errors:
return str(self.errors[0])
elif self.warnings:
return str(self.warnings[0])
else:
return re.sub(r'\[.*?\] ', '', str(self.value).split("\n")[0])

def __repr__(self):
return f"{self.__class__.__name__}({str(self)!r})"

def __reduce__(self):
if hasattr(self, 'errors'):
Expand Down Expand Up @@ -1103,6 +1116,42 @@ def __check_version(pathToFile):
raise Exception("{} must be at least 2015.1, not {}".format(program, version))

raise Exception("Unknown P4 output : {}".format(output) )


class PyKeepAlive:
def __init__(self):
self.__thread = None
self.__alive = 1

def isAlive(self):
return self.__alive

# private member function
def __poll(self):
while True:
if self.isAlive() == 0:
self.__alive = 0
return 0
time.sleep(0.5)

# private member function
def __executeAll(self):
if self.__thread and self.__thread.is_alive():
pass

elif self.__thread and not self.__thread.is_alive():
self.__thread.join()

else:
self.__thread = threading.Thread(target=self.__poll)
self.__thread.daemon = True
self.__thread.start()

return self.__alive

def __call__(self):
return self.__executeAll()


if __name__ == "__main__":
p4 = P4()
Expand Down
30 changes: 29 additions & 1 deletion P4API.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: //depot/main/p4-python/P4API.cpp#63 $
* $Id: //depot/main/p4-python/P4API.cpp#64 $
*
* Build instructions:
* Use Distutils - see accompanying setup.py
Expand Down Expand Up @@ -57,6 +57,7 @@
#include "PythonMessage.h"
#include "PythonTypes.h"
#include "debug.h"
#include "PythonKeepAlive.h"

// #include <alloca.h>

Expand Down Expand Up @@ -586,6 +587,31 @@ static PyObject * P4Adapter_getTunable(P4Adapter * self, PyObject *args)
return NULL;
}

// ==================
// ==== SetBreak ====
// ==================

static PyObject* P4Adapter_setBreak(P4Adapter* self, PyObject* args) {
PyObject* py_callable;

// Parse the arguments
if (!PyArg_ParseTuple(args, "O", &py_callable)) {
return NULL;
}

// Check if the parsed object is callable
if (!PyCallable_Check(py_callable)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}

// Create an object pointer of PythonKeepAlive and pass py_callable
PythonKeepAlive* cb = new PythonKeepAlive(py_callable);
self->clientAPI->SetBreak(cb);

Py_RETURN_NONE;
}

#if PY_MAJOR_VERSION >= 3

static PyObject * P4Adapter_convert(P4Adapter * self, PyObject *args)
Expand Down Expand Up @@ -631,6 +657,8 @@ static PyMethodDef P4Adapter_methods[] = {
"Sets a tunable to the specified value"},
{"get_tunable", (PyCFunction)P4Adapter_getTunable, METH_VARARGS,
"Returns the value for this tunable or 0"},
{"setbreak", (PyCFunction)P4Adapter_setBreak, METH_VARARGS,
"Set the break callback"},

#if PY_MAJOR_VERSION >= 3
{"__convert", (PyCFunction)P4Adapter_convert, METH_VARARGS,
Expand Down
11 changes: 10 additions & 1 deletion PythonClientAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
$Id: //depot/main/p4-python/PythonClientAPI.cpp#78 $
$Id: //depot/main/p4-python/PythonClientAPI.cpp#79 $
*******************************************************************************/

#include <Python.h>
Expand Down Expand Up @@ -1266,3 +1266,12 @@ void PythonClientAPI::RunCmd(const char *cmd, ClientUser *ui, int argc, char * c
SetCmdRun();

}

void PythonClientAPI::SetBreak(PythonKeepAlive* cb)
{
if( IsConnected() ) {
debug.debug(P4PYDBG_COMMANDS, "[P4] Establish the callback" );
client.SetBreak(cb);
}

}
7 changes: 6 additions & 1 deletion PythonClientAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: //depot/main/p4-python/PythonClientAPI.h#43 $
* $Id: //depot/main/p4-python/PythonClientAPI.h#44 $
*
* Build instructions:
* Use Distutils - see accompanying setup.py
Expand All @@ -38,6 +38,8 @@
#ifndef PYTHON_CLIENT_API_H
#define PYTHON_CLIENT_API_H

#include "PythonKeepAlive.h"

class Enviro;
class PythonClientAPI
{
Expand Down Expand Up @@ -232,6 +234,9 @@ class PythonClientAPI
void Except( const char *func, Error *e );
void Except( const char *func, const char *msg, const char *cmd );

// SetBreak
void SetBreak( PythonKeepAlive* cb );

public:
// setter/getter methods and attributes

Expand Down
70 changes: 70 additions & 0 deletions PythonKeepAlive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*******************************************************************************
Copyright (c) 2024, Perforce Software, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

/*******************************************************************************
* Name : PythonKeepAlive.cpp
*
* Author : Himanshu Jain <[email protected]>
*
* Description : C++ subclass for KeepAlive used by Python KeepAlive.
* Allows Perforce API to implement a customized IsAlive function
*
* $Id: //depot/main/p4-python/PythonKeepAlive.cpp#1 $
*
******************************************************************************/

#include <Python.h>
#include <keepalive.h>
#include "PythonThreadGuard.h"
#include "PythonKeepAlive.h"

PythonKeepAlive::PythonKeepAlive(PyObject* callable) : py_callable(callable) {
// Increment the reference count for the callable
Py_XINCREF(py_callable);
}

PythonKeepAlive::~PythonKeepAlive() {
// Decrement the reference count for the callable
Py_XDECREF(py_callable);
}

int PythonKeepAlive::IsAlive() {

EnsurePythonLock guard;

if (py_callable && PyCallable_Check(py_callable)) {
PyObject* result = PyObject_CallObject(py_callable, NULL);
if (result != NULL && PyLong_Check(result) && PyLong_AsLong(result) == 0) {
Py_DECREF(result);
return( 0 ); // To terminate the connection
}

return( 1 );
}

return( 1 ); // Default to true if callable is not set or failed
}
46 changes: 46 additions & 0 deletions PythonKeepAlive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* PythonKeepAlive. Subclass of P4::KeepAlive
*
* Copyright (c) 2024, Perforce Software, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: //depot/main/p4-python/PythonKeepAlive.h#1 $
*
*/

#ifndef PYTHON_KEEP_ALIVE_H
#define PYTHON_KEEP_ALIVE_H

class PythonKeepAlive : public KeepAlive {
public:
PythonKeepAlive(PyObject* callable);
~PythonKeepAlive();

virtual int IsAlive() override;
virtual int PollMs() override { return 0; }

private:
PyObject* py_callable;
};

#endif // PYTHON_KEEP_ALIVE_H
Loading

0 comments on commit bf7146a

Please sign in to comment.