Skip to content

Commit 31ac0a6

Browse files
2025.2 release code drop
1 parent 8806654 commit 31ac0a6

File tree

5 files changed

+166
-35
lines changed

5 files changed

+166
-35
lines changed

PythonClientAPI.cpp

Lines changed: 13 additions & 3 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/PythonClientAPI.cpp#80 $
28+
$Id: //depot/main/p4-python/PythonClientAPI.cpp#81 $
2929
*******************************************************************************/
3030

3131
#include <Python.h>
@@ -707,7 +707,12 @@ PyObject * PythonClientAPI::ConnectOrReconnect()
707707
Error e;
708708

709709
ResetFlags();
710-
client.Init( &e );
710+
711+
{
712+
ReleasePythonLock guard; // Release GIL during connect
713+
client.Init( &e );
714+
}
715+
711716
if ( e.Test() && exceptionLevel ) {
712717
Except( "P4.connect()", &e );
713718
return NULL;
@@ -751,7 +756,12 @@ PyObject * PythonClientAPI::Disconnect()
751756
}
752757

753758
Error e;
754-
client.Final( &e );
759+
760+
{
761+
ReleasePythonLock guard; // Release GIL during disconnect
762+
client.Final( &e );
763+
}
764+
755765
ResetFlags();
756766

757767
// Clear the specdef cache.

PythonClientUser.h

Lines changed: 2 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/PythonClientUser.h#19 $
29+
* $Id: //depot/main/p4-python/PythonClientUser.h#20 $
3030
*
3131
* Build instructions:
3232
* Use Distutils - see accompanying setup.py
@@ -64,6 +64,7 @@ class PythonClientUser: public ClientUser, public KeepAlive
6464

6565
virtual ClientProgress *CreateProgress(int);
6666
virtual int ProgressIndicator();
67+
virtual int CanParallelProgress() override { return 1; }
6768

6869
virtual void Finished();
6970

RELNOTES.txt

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Release Notes for
22
P4Python, P4 API for Python
33

4-
Version 2025.1
4+
Version 2025.2
55

66
Introduction
77

@@ -34,14 +34,6 @@ Important Product Rebrand Notice
3434

3535
--------------------------------------------------------------------------
3636

37-
Important End-of-Life Notice
38-
39-
This release of P4Python would be the last to support and test against
40-
Python 3.9, which is in EOL status. This is part of our commitment to
41-
focus on supported technology platforms.
42-
43-
--------------------------------------------------------------------------
44-
4537
Installation
4638

4739
Installation via pip
@@ -58,8 +50,8 @@ Installation
5850

5951
python3 -m pip install --upgrade p4python
6052

61-
P4Python is distributed as a binary wheels for Python 3.9, 3.10,
62-
3.11, 3.12 and 3.13. In order for the binary wheel to be used, the ABI
53+
P4Python is distributed as a binary wheels for Python 3.10,
54+
3.11, 3.12, 3.13 and 3.14. In order for the binary wheel to be used, the ABI
6355
tag needs to match. This often requires updated pip, to do so issue:
6456

6557
python3 -m pip install --upgrade pip
@@ -69,23 +61,38 @@ Installation
6961
P4Python can be installed via Linux packages available from:
7062
https://www.perforce.com/perforce-packages
7163

72-
Following packages are available:
64+
The easiest way to install P4Python is using the meta-package:
65+
66+
For RHEL/Rocky Linux
67+
sudo yum install perforce-p4python3
68+
69+
For Ubuntu/Debian
70+
sudo apt-get install perforce-p4python3
71+
72+
The perforce-p4python3 meta-package automatically installs the correct
73+
version for your system's default Python 3 installation.
7374

74-
RHEL/Rocky Linux 8
75+
If you need to install P4Python for a specific Python version,
76+
the following packages are available:
77+
78+
RHEL/Rocky Linux 8
7579
perforce-p4python3-python3.8
7680
perforce-p4python3-python3.9
77-
81+
7882
RHEL/Rocky Linux 9
7983
perforce-p4python3-python3.9
8084

81-
Ubuntu 20.04
85+
RHEL/Rocky Linux 10
86+
perforce-p4python3-python3.12
87+
88+
Ubuntu 20.04
8289
perforce-p4python3-python3.8
8390
perforce-p4python3-python3.9
8491

85-
Ubuntu 22.04
92+
Ubuntu 22.04
8693
perforce-p4python3-python3.10
87-
88-
Ubuntu 24.04
94+
95+
Ubuntu 24.04
8996
perforce-p4python3-python3.12
9097

9198
Installing P4Python for Maya.
@@ -124,28 +131,28 @@ Compatibility Statements
124131

125132
Server Compatibility
126133

127-
This release of P4Python supports the 2025.1 P4 Server.
134+
This release of P4Python supports the 2025.2 P4 Server.
128135
Older releases might work but are not supported.
129136

130137
API Compatibility
131138

132-
This release of P4Python requires at least 2025.1 P4 C/C++ API
133-
(2025.1/2761706). Older releases will not compile and are not supported.
139+
This release of P4Python requires at least 2025.2 P4 C/C++ API
140+
(2025.2/2852709). Older releases will not compile and are not supported.
134141

135142
Python Compatibility
136143

137144
This release of P4Python is supported building from source with
138-
Python 3.9, 3.10, 3.11, 3.12 and 3.13.
145+
Python 3.10, 3.11, 3.12, 3.13 and 3.14.
139146

140147
For detailed compatibility, please check the following table:
141148

142149
Python Release | P4Python Release
143150
======================================
144-
3.9 | 2021.1 or later
145151
3.10 | 2022.1 or later
146152
3.11 | 2022.2 or later
147153
3.12 | 2023.1 or later
148154
3.13 | 2024.2 or later
155+
3.14 | 2025.2 or later
149156

150157
OpenSSL Compatibility
151158

@@ -155,7 +162,7 @@ Compatibility Statements
155162
Beginning with the 2017.1 release of the P4 C/C++ API, the dependency on
156163
OpenSSL is now enforced and the SSL stub library has been removed.
157164
Executables linked against the P4 C/C++ API libraries must also be linked against
158-
real OpenSSL libraries: The latest 3.0.x or 1.1.1 patch is recommended
165+
real OpenSSL libraries: The latest 3.x.x or 1.1.1 patch is recommended
159166

160167
Platform Compatibility
161168

@@ -170,10 +177,10 @@ Compatibility Statements
170177
Linux kernel 2.6+ for Intel(x86, x86_64)
171178
Ubuntu 20.04, 22.04, 24.04
172179
CentOS 8
173-
Rocky Linux 9.1
180+
Rocky Linux 9.1, 10
174181
Linux kernel 2.6+ for ARM(aarch64)
175182
Ubuntu 20.04, 22.04, 24.04
176-
Rocky Linux 9.4
183+
Rocky Linux 9.4, 10
177184

178185
The above platforms are tested and subject to regression testing on a
179186
frequent basis. Errors or bugs discovered in these platforms are prioritized
@@ -309,11 +316,41 @@ Key to symbols used in change notes below.
309316

310317
--------------------------------------------------------------------------
311318

319+
New functionalities in 2025.2 (2025.2/2863679) (2025/12/03)
320+
321+
#2842576 (Job #128780)
322+
Built P4Python with P4API 2025.2 (2025.2/2852709)
323+
324+
#2846296 (Job #128672)
325+
Added support for python version 3.14
326+
327+
#2836086 (Job #110913)
328+
Added support for per-file CreateProgress in the Python API
329+
by implementing CanParallelProgress() in PythonClientUser
330+
enabling parallel progress operations.
331+
332+
#2832930 (Job #127506)
333+
Added support for RPM packages on RHEL 10, Rocky Linux 10
334+
and other EL10-compatible distributions.
335+
336+
Bugs fixed in 2025.2
337+
338+
#2837109 (Job #124136)
339+
run_merge() now correctly reflects the merge tool’s exit code
340+
instead of always returning True.
341+
342+
#2850324 (Job #129466)
343+
Fixed an issue where P4Python held the Python GIL during blocking
344+
network operations in connect() and disconnect(), preventing other
345+
threads and async workflows from running.
346+
347+
--------------------------------------------------------------------------
348+
312349
New functionalities in 2025.1 (2025.1/2767466) (2025/05/21)
313350

314351
#2757777 (Job #125785)
315352
Built P4Python with P4 C/C++ API 2025.1 (2025.1/2761706)
316-
353+
317354
Bugs fixed in 2025.1
318355

319356
#2744582 (Job #123978)

SpecMgr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2424
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2525
26-
$Id: //depot/main/p4-python/SpecMgr.cpp#59 $
26+
$Id: //depot/main/p4-python/SpecMgr.cpp#60 $
2727
*******************************************************************************/
2828

2929
/*******************************************************************************
@@ -116,6 +116,7 @@ struct specdata {
116116
"Backup;code:319;type:select;len:10;val:enable/disable;;"
117117
"View;code:311;fmt:C;type:wlist;words:2;len:64;;"
118118
"ChangeView;code:317;type:llist;len:64;;"
119+
"LimitView;code:320;fmt:C;type:llist;len:64;;"
119120
},
120121
{
121122
"depot",
@@ -241,6 +242,7 @@ struct specdata {
241242
"LastPush;code:859;fmt:L;len:10;;"
242243
"DepotMap;code:860;type:wlist;words:2;len:64;;"
243244
"ArchiveLimits;code:862;type:wlist;words:2;len:64;;"
245+
"RemoteCharset;code:863;opt:optional;type:line;len:32;;"
244246
},
245247
{
246248
"repo",

p4test.py

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,6 @@ def testOutputHandler( self ):
684684
# test the resetting
685685
self.p4.handler = None
686686
self.assertEqual( self.p4.handler, None )
687-
self.assertEqual( sys.getrefcount(h), 2 )
688687

689688
self.p4.connect()
690689
self._setClient()
@@ -717,14 +716,12 @@ def outputMessage(self, msg):
717716
self._doSubmit("Failed to submit the add", change)
718717

719718
h = MyOutputHandler()
720-
self.assertEqual( sys.getrefcount(h), 2 )
721719
self.p4.handler = h
722720

723721
self.assertEqual( len(self.p4.run_files('...')), 0, "p4 does not return empty list")
724722
self.assertEqual( len(h.statOutput), len(files), "Less files than expected")
725723
self.assertEqual( len(h.messageOutput), 0, "Messages unexpected")
726724
self.p4.handler = None
727-
self.assertEqual( sys.getrefcount(h), 2 )
728725

729726
def testProgress( self ):
730727
self.p4.connect()
@@ -1461,6 +1458,90 @@ def isAlive(self):
14611458
self.assertEqual(100, total_files, "Setbreak is not working")
14621459
self.p4.disconnect()
14631460

1461+
def testMergeToolReturnCode(self):
1462+
testDir = 'test_merge_return'
1463+
testAbsoluteDir = os.path.join(self.client_root, testDir)
1464+
os.mkdir(testAbsoluteDir)
1465+
1466+
self.p4.connect()
1467+
self.assertTrue(self.p4.connected(), "Not connected")
1468+
self._setClient()
1469+
1470+
# Create initial file
1471+
file = "merge_test.txt"
1472+
fname = os.path.join(testAbsoluteDir, file)
1473+
with open(fname, "w") as f:
1474+
f.write("Line 1\n")
1475+
textFile = testDir + "/" + file
1476+
self.p4.run_add(textFile)
1477+
1478+
change = self.p4.fetch_change()
1479+
change._description = "Initial version"
1480+
self._doSubmit("Failed to submit initial", change)
1481+
1482+
# Create second revision
1483+
self.p4.run_edit(textFile)
1484+
with open(fname, "w") as f:
1485+
f.write("Line 1\nLine 2\n")
1486+
1487+
change = self.p4.fetch_change()
1488+
change._description = "Second version"
1489+
self._doSubmit("Failed to submit second", change)
1490+
1491+
# Sync back and create conflict
1492+
self.p4.run_sync(textFile + "#1")
1493+
self.p4.run_edit(textFile)
1494+
with open(fname, "w") as f:
1495+
f.write("Line 1\nDifferent Line 2\n")
1496+
self.p4.run_sync(textFile)
1497+
1498+
# Test with failing merge tool
1499+
import tempfile
1500+
import stat
1501+
1502+
# Create a fake merge tool that always fails
1503+
failing_merge_script = os.path.join(tempfile.gettempdir(), "failing_merge.sh")
1504+
with open(failing_merge_script, "w") as f:
1505+
f.write("#!/bin/bash\n >&2\nexit 1\n")
1506+
os.chmod(failing_merge_script, stat.S_IRWXU)
1507+
1508+
class MergeTestResolver(P4.Resolver):
1509+
def __init__(self, testObject):
1510+
self.t = testObject
1511+
self.merge_result = None
1512+
1513+
def resolve(self, mergeData):
1514+
# Set P4MERGE to our failing script
1515+
old_p4merge = os.environ.get('P4MERGE', '')
1516+
os.environ['P4MERGE'] = failing_merge_script
1517+
1518+
try:
1519+
# Call run_merge and verify it returns False when merge tool fails
1520+
self.merge_result = mergeData.run_merge()
1521+
1522+
# Assert that run_merge() correctly detects merge tool failure
1523+
self.t.assertFalse(self.merge_result,
1524+
f"run_merge() should return False when merge tool fails with exit code 1, but returned {self.merge_result}")
1525+
1526+
# Additional assertion to verify the type
1527+
self.t.assertIsInstance(self.merge_result, bool,
1528+
f"run_merge() should return a boolean, but returned {type(self.merge_result)}")
1529+
1530+
finally:
1531+
# Restore original P4MERGE
1532+
if old_p4merge:
1533+
os.environ['P4MERGE'] = old_p4merge
1534+
elif 'P4MERGE' in os.environ:
1535+
del os.environ['P4MERGE']
1536+
1537+
return "at" # Accept theirs to complete the resolve
1538+
1539+
resolver = MergeTestResolver(self)
1540+
self.p4.run_resolve(resolver=resolver)
1541+
1542+
# Clean up
1543+
if os.path.exists(failing_merge_script):
1544+
os.unlink(failing_merge_script)
14641545

14651546

14661547
if __name__ == '__main__':

0 commit comments

Comments
 (0)