Skip to content

Commit f0ed057

Browse files
committed
Fix invalid escape sequences in gpMgmt Python sources
Python 3.12 promoted "invalid escape sequence" from a DeprecationWarning (hidden by default) to a SyntaxWarning that is printed whenever the module is compiled. On distributions shipping Python 3.12 or newer -- Rocky Linux 10, Fedora 40+, Ubuntu 24.10+ -- the management utilities therefore print warnings before doing any work: /usr/local/cloudberry-db/lib/python/gppylib/util/ssh_utils.py:268: SyntaxWarning: invalid escape sequence '\ ' /usr/local/cloudberry-db/bin/lib/pexpect/pxssh.py:105: SyntaxWarning: invalid escape sequence '\[' /usr/local/cloudberry-db/bin/lib/pexpect/pxssh.py:109: SyntaxWarning: invalid escape sequence '\$' /usr/local/cloudberry-db/bin/lib/pexpect/pxssh.py:110: SyntaxWarning: invalid escape sequence '\$' The four above come from gpsync/gpssh/gpssh-exkeys and are the ones users see; compiling everything under gpMgmt/ turns up 41 such literals in 16 files. Nothing misbehaves today -- CPython leaves an unrecognised escape in the string as-is, which happens to be what a regex or a shell snippet wants -- but the warnings are noise on stderr, and the Python docs say these sequences will become a SyntaxError in a future release. Fix the literals rather than silencing the warning: * a literal whose every backslash escape is invalid gets an r prefix * a literal that mixes valid and invalid escapes has the invalid backslash doubled instead, since r would also change the meaning of the valid ones Both transformations leave the literal's runtime value byte-for-byte identical, which was verified mechanically: for each touched file the ordered list of str/bytes constants in the AST is unchanged against the parent commit, and compiling the file under -W error::SyntaxWarning is now clean. Two of the rewrites are worth a reviewer's eye: * gpstate_utils.py:79 and replication_slots_utils.py:31 are the mixed-escape case, so they read '\\%' and '\\A' now. * the '\A' in replication_slots_utils.py looks like a stray keystroke in a run of shell line continuations -- the shell it is handed sees "&& A ./demo_cluster.sh" and tries to run A. That is pre-existing behaviour, so this commit preserves it exactly rather than quietly changing what the behave step does; it wants a separate fix. gpMgmt/bin/lib/pexpect is a vendored copy of pexpect 3.3; upstream pexpect made these same literals raw long ago. 27 more files under src/ and contrib/ (mostly gporca and try_convert developer scripts) have the same problem and are left for a follow-up.
1 parent 89baa48 commit f0ed057

16 files changed

Lines changed: 40 additions & 40 deletions

‎gpMgmt/bin/gpload.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ def quote_unident(val):
717717

718718
def match_notice_obj(notice):
719719
# match the formatting errors in notice
720-
r = re.compile("^NOTICE: found (\d+) data formatting errors.*")
720+
r = re.compile(r"^NOTICE: found (\d+) data formatting errors.*")
721721
m = r.match(notice)
722722
if m:
723723
return int(m.group(1))
@@ -2527,7 +2527,7 @@ def create_staging_table(self):
25272527
# should not explicitly specify the DISTRIBUTED BY clause.
25282528
# Only the DISTRIBUTED BY clause can take effect if all selected fields
25292529
# exist in the CREATE TABLE statement.
2530-
dist_column_list = re.match(".*\((.*)\).*", distcols).group(1).split(",")
2530+
dist_column_list = re.match(r".*\((.*)\).*", distcols).group(1).split(",")
25312531
target_column_set = set(element[0] for element in target_columns)
25322532
if set(dist_column_list) <= target_column_set:
25332533
quoted_dist_column = convertListToDelimited(dist_column_list)
@@ -2672,7 +2672,7 @@ def do_update(self,fromname,index):
26722672
#
26732673
update_condition = ' ' + update_condition + ' '
26742674
for name, colType, mapto, seq in self.into_columns:
2675-
regexp = '(?<=[^\w])%s(?=[^\w])' % name
2675+
regexp = r'(?<=[^\w])%s(?=[^\w])' % name
26762676
self.log(self.DEBUG, 'update_condition re: ' + regexp)
26772677
temp_update_condition = update_condition
26782678
updateConditionList = splitIntoLiteralsAndNonLiterals(update_condition)
@@ -2686,7 +2686,7 @@ def do_update(self,fromname,index):
26862686
if update_condition == temp_update_condition:
26872687
# see if column can be undelimited, and try again.
26882688
if len(name) > 2 and name[1:-1] == name[1:-1].lower():
2689-
regexp = '(?<=[^\w])%s(?=[^\w])' % name[1:-1]
2689+
regexp = r'(?<=[^\w])%s(?=[^\w])' % name[1:-1]
26902690
self.log(self.DEBUG, 'update_condition undelimited re: ' + regexp)
26912691
update_condition = re.sub( regexp
26922692
, self.fix_update_cond

‎gpMgmt/bin/gpload_test/gpload/TEST.py‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -319,20 +319,20 @@ def modify_sql_file(num):
319319
if os.path.isfile(file):
320320
for line in fileinput.FileInput(file,inplace=1):
321321
if platform.system() in ['Windows', 'Microsoft']:
322-
line = line.replace("\!gpload ","\!gpload.py")
322+
line = line.replace(r"\!gpload ",r"\!gpload.py")
323323
line = line.replace("gpload ","gpload.py ")
324324
else:
325325
line = line.replace("gpload.py ","gpload ")
326326
# using absolute path
327327
line = re.sub('-h WinnBook.local', '-h '+get_hostname(), line)
328328
line = line.replace("-h localhost",'-h '+get_hostname())
329-
line = re.sub('-p \d+', '-p '+get_port(), line)
329+
line = re.sub(r'-p \d+', '-p '+get_port(), line)
330330
line = re.sub('-p$', '-p '+get_port(), line)
331-
line = re.sub('-h (\d+)\.(\d+)\.(\d+)\.(\d+)', '-h '+get_hostname(), line)
331+
line = re.sub(r'-h (\d+)\.(\d+)\.(\d+)\.(\d+)', '-h '+get_hostname(), line)
332332
if num == 12 or num == 13 or num == 14 or num == 15 or num == 176:
333-
line = re.sub('-U \w+', '-U fake_user', line)
333+
line = re.sub(r'-U \w+', '-U fake_user', line)
334334
else:
335-
line = re.sub('-U \w+', '-U '+user, line)
335+
line = re.sub(r'-U \w+', '-U '+user, line)
336336
print((str(re.sub('\n','',line))))
337337

338338
def windows_path(command):
@@ -350,9 +350,9 @@ def get_port():
350350
if os.path.isfile(file):
351351
f = open(file)
352352
for line in f:
353-
match = re.search('port=\d+',line)
353+
match = re.search(r'port=\d+',line)
354354
if match:
355-
match1 = re.search('\d+', match.group())
355+
match1 = re.search(r'\d+', match.group())
356356
if match1:
357357
return match1.group()
358358
f.close()
@@ -407,7 +407,7 @@ def testQuery00(self):
407407
"0 gpload setup"
408408
for num in range(1,6):
409409
f = open(mkpath('query%d.sql' % num),'w')
410-
f.write("\! gpload -f "+mkpath('config/config_file')+ " -d gptest")
410+
f.write(r"\! gpload -f "+mkpath('config/config_file')+ " -d gptest")
411411
f.close()
412412

413413
def testQuery01(self):

‎gpMgmt/bin/gppylib/programs/test/unit/test_cluster_clsrecoversegment_triples.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,14 +294,14 @@ def test_RecoveryTripletsInPlaceAndNewHosts_getMirrorTriples_should_fail(self):
294294
"gparray": self.three_failedover_segs_gparray_str,
295295
"new_hosts": ['new_1', 'new_2'],
296296
"unreachable_hosts": ['new_1', 'new_2'],
297-
"expected": "Cannot recover. The following recovery target hosts are unreachable: \['new_1', 'new_2'\]"
297+
"expected": r"Cannot recover. The following recovery target hosts are unreachable: \['new_1', 'new_2'\]"
298298
},
299299
{
300300
"name": "some_hosts_unreachable",
301301
"gparray": self.three_failedover_segs_gparray_str,
302302
"new_hosts": ['new_1', 'new_2'],
303303
"unreachable_hosts": ['new_2'],
304-
"expected": "Cannot recover. The following recovery target hosts are unreachable: \['new_2'\]"
304+
"expected": r"Cannot recover. The following recovery target hosts are unreachable: \['new_2'\]"
305305
},
306306
{
307307
"name": "no_peer_for_failed_seg",
@@ -329,7 +329,7 @@ def test_RecoveryTripletsInPlaceAndNewHosts_getMirrorTriples_should_fail(self):
329329
"gparray": self.three_failedover_segs_gparray_str,
330330
"new_hosts": ['new_1','new_2'],
331331
"unreachable_existing_hosts": ['sdw2'],
332-
"expected": "The recovery source segment sdw2 \(content 0\) is unreachable"
332+
"expected": r"The recovery source segment sdw2 \(content 0\) is unreachable"
333333
},
334334
{
335335
"name": "failed_and_live_same_dbid",

‎gpMgmt/bin/gppylib/test/unit/test_unit_database_segment_guc.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ def test_report_fail_format_database(self):
2323

2424
def test_init_with_insufficient_database_values_raises(self):
2525
row = ['contentid', 'guc_name']
26-
with self.assertRaisesRegex(Exception, "must provide \['context', 'guc name', 'value'\]"):
26+
with self.assertRaisesRegex(Exception, r"must provide \['context', 'guc name', 'value'\]"):
2727
DatabaseSegmentGuc(row)
2828

‎gpMgmt/bin/gppylib/test/unit/test_unit_file_segment_guc.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_report_fail_format_file_when_unset(self):
2828

2929
def test_init_with_insufficient_file_values_raises(self):
3030
row = ['contentid', 'guc_name', 'value_from_file']
31-
with self.assertRaisesRegex(Exception, "must provide \['context', 'guc name', 'value', 'dbid'\]"):
31+
with self.assertRaisesRegex(Exception, r"must provide \['context', 'guc name', 'value', 'dbid'\]"):
3232
FileSegmentGuc(row)
3333

3434
def test_when_coordinator_when_integer_dbid_report_success_format_file(self):

‎gpMgmt/bin/gppylib/test/unit/test_unit_gppkg.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test__input_matches_multiple_packages(self):
6969
options, args = parser.parse_args()
7070
self.subject = GpPkgProgram(options, args)
7171
with self.assertRaisesRegex(Exception, "Remove request 'sampl' too broad. "
72-
"Multiple packages match remove request: \( sample.gppkg, sample2.gppkg \)."):
72+
r"Multiple packages match remove request: \( sample.gppkg, sample2.gppkg \)."):
7373
self.subject.run()
7474

7575
self.assertFalse(self.mock_uninstall_package.run.called)

‎gpMgmt/bin/gppylib/test/unit/test_unit_gpsegrecovery.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ def test_complete_workflow(self, mock_pgbasebackup_run, mock_pgbasebackup_init,
301301
self.assertEqual(1, mock_pgrewind_init.call_count)
302302
self.assertEqual(1, mock_pgbasebackup_run.call_count)
303303
self.assertEqual(1, mock_pgbasebackup_init.call_count)
304-
self.assertRegex(gplog.get_logfile(), '/gpsegrecovery.py_\d+\.log')
304+
self.assertRegex(gplog.get_logfile(), r'/gpsegrecovery.py_\d+\.log')
305305

306306
@patch('gppylib.commands.pg.PgRewind.__init__', return_value=None)
307307
@patch('gppylib.commands.pg.PgRewind.run')
@@ -330,7 +330,7 @@ def test_complete_workflow_exception(self, mock_pgbasebackup_run, mock_pgbasebac
330330
self.assertEqual(1, mock_pgrewind_init.call_count)
331331
self.assertEqual(2, mock_pgbasebackup_run.call_count)
332332
self.assertEqual(2, mock_pgbasebackup_init.call_count)
333-
self.assertRegex(gplog.get_logfile(), '/gpsegrecovery.py_\d+\.log')
333+
self.assertRegex(gplog.get_logfile(), r'/gpsegrecovery.py_\d+\.log')
334334

335335
@patch('recovery_base.gplog.setup_tool_logging')
336336
@patch('recovery_base.RecoveryBase.main')

‎gpMgmt/bin/gppylib/test/unit/test_unit_gpsegsetuprecovery.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ def test_complete_workflow(self, mock_execsql, mock_dburl, mock_connect, mock_va
259259
mock_connect.assert_called_once()
260260
mock_execsql.assert_called_once()
261261
#TODO use regex pattern
262-
self.assertRegex(gplog.get_logfile(), '/gpsegsetuprecovery.py_\d+\.log')
262+
self.assertRegex(gplog.get_logfile(), r'/gpsegsetuprecovery.py_\d+\.log')
263263

264264
@patch('gpsegsetuprecovery.ValidationForFullRecovery.validate_failover_data_directory')
265265
@patch('gpsegsetuprecovery.dbconn.connect')
@@ -284,7 +284,7 @@ def test_complete_workflow_exception(self, mock_execsql, mock_dburl, mock_connec
284284
mock_validate_datadir.assert_called_once()
285285
mock_dburl.assert_called_once()
286286
mock_connect.assert_called_once()
287-
self.assertRegex(gplog.get_logfile(), '/gpsegsetuprecovery.py_\d+\.log')
287+
self.assertRegex(gplog.get_logfile(), r'/gpsegsetuprecovery.py_\d+\.log')
288288

289289

290290
@patch('recovery_base.gplog.setup_tool_logging')

‎gpMgmt/bin/gppylib/test/unit/test_unit_package.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def test__execute_target_gphome_mismatch_raises(self):
119119
self.args['to_gphome'] = '/wrong/gphome'
120120
subject = MigratePackages(**self.args)
121121

122-
expected_raise = "The target GPHOME, %s, must match the current \$GPHOME used to launch gppkg." % self.args['to_gphome']
122+
expected_raise = r"The target GPHOME, %s, must match the current \$GPHOME used to launch gppkg." % self.args['to_gphome']
123123
with self.assertRaisesRegex(ExceptionNoStackTraceNeeded, expected_raise):
124124
subject.execute()
125125

‎gpMgmt/bin/gppylib/util/ssh_utils.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ def emptyline(self):
265265
pass
266266

267267
def escapeLine(self, line):
268-
'''Escape occurrences of \ and $ as needed and package the line as an "eval" shell command'''
268+
r'''Escape occurrences of \ and $ as needed and package the line as an "eval" shell command'''
269269
line = line.strip()
270270
if line == 'EOF' or line == 'exit' or line == 'quit':
271271
raise self.SessionCmdExit()

0 commit comments

Comments
 (0)