4040from .logger import TestgresLogger
4141
4242from .utils import \
43+ eprint , \
4344 get_bin_path , \
4445 file_tail , \
4546 pg_version_ge , \
@@ -80,14 +81,18 @@ def __init__(self, name=None, port=None, base_dir=None, use_logging=False):
8081 use_logging: enable python logging.
8182 """
8283
83- # public
84+ # basic
8485 self .host = '127.0.0.1'
8586 self .name = name or generate_app_name ()
8687 self .port = port or reserve_port ()
8788 self .base_dir = base_dir
8889
90+ # defaults for __exit__()
91+ self .cleanup_on_good_exit = TestgresConfig .node_cleanup_on_good_exit
92+ self .cleanup_on_bad_exit = TestgresConfig .node_cleanup_on_bad_exit
93+ self .shutdown_max_attempts = 3
94+
8995 # private
90- self ._should_rm_dirs = base_dir is None
9196 self ._should_free_port = port is None
9297 self ._use_logging = use_logging
9398 self ._logger = None
@@ -100,12 +105,23 @@ def __enter__(self):
100105 return self
101106
102107 def __exit__ (self , type , value , traceback ):
103- # stop node if necessary
104- self .cleanup ()
105-
106- # free port if necessary
107108 self .free_port ()
108109
110+ got_exception = value is not None
111+ c1 = self .cleanup_on_good_exit and not got_exception
112+ c2 = self .cleanup_on_bad_exit and got_exception
113+
114+ attempts = self .shutdown_max_attempts
115+
116+ if c1 or c2 :
117+ self .cleanup (attempts )
118+ else :
119+ self ._try_shutdown (attempts )
120+
121+ @property
122+ def pid (self ):
123+ return self .get_pid ()
124+
109125 @property
110126 def master (self ):
111127 return self ._master
@@ -126,6 +142,22 @@ def utils_log_name(self):
126142 def pg_log_name (self ):
127143 return os .path .join (self .logs_dir , PG_LOG_FILE )
128144
145+ def _try_shutdown (self , max_attempts ):
146+ attempts = 0
147+
148+ # try stopping server N times
149+ while attempts < max_attempts :
150+ try :
151+ self .stop ()
152+ break # OK
153+ except ExecUtilException :
154+ pass # one more time
155+ except Exception :
156+ # TODO: probably kill stray instance
157+ eprint ('cannot stop node {}' .format (self .name ))
158+
159+ attempts += 1
160+
129161 def _assign_master (self , master ):
130162 """NOTE: this is a private method!"""
131163
@@ -163,8 +195,6 @@ def _create_recovery_conf(self, username):
163195 self .append_conf (RECOVERY_CONF_FILE , line )
164196
165197 def _prepare_dirs (self ):
166- """NOTE: this is a private method!"""
167-
168198 if not self .base_dir :
169199 self .base_dir = tempfile .mkdtemp ()
170200
@@ -175,23 +205,17 @@ def _prepare_dirs(self):
175205 os .makedirs (self .logs_dir )
176206
177207 def _maybe_start_logger (self ):
178- """NOTE: this is a private method!"""
179-
180208 if self ._use_logging :
181- # spawn new logger if it doesn't exist or stopped
209+ # spawn new logger if it doesn't exist or is stopped
182210 if not self ._logger or not self ._logger .is_alive ():
183211 self ._logger = TestgresLogger (self .name , self .pg_log_name )
184212 self ._logger .start ()
185213
186214 def _maybe_stop_logger (self ):
187- """NOTE: this is a private method!"""
188-
189215 if self ._logger :
190216 self ._logger .stop ()
191217
192218 def _format_verbose_error (self , message = None ):
193- """NOTE: this is a private method!"""
194-
195219 # list of important files + N of last lines
196220 files = [
197221 (os .path .join (self .data_dir , PG_CONF_FILE ), 0 ),
@@ -560,14 +584,16 @@ def pg_ctl(self, params):
560584 def free_port (self ):
561585 """
562586 Reclaim port owned by this node.
587+ NOTE: does not free auto selected ports.
563588 """
564589
565590 if self ._should_free_port :
566591 release_port (self .port )
567592
568593 def cleanup (self , max_attempts = 3 ):
569594 """
570- Stop node if needed and remove its data directory.
595+ Stop node if needed and remove its data/logs directory.
596+ NOTE: take a look at TestgresConfig.node_cleanup_full.
571597
572598 Args:
573599 max_attempts: how many times should we try to stop()?
@@ -576,30 +602,15 @@ def cleanup(self, max_attempts=3):
576602 This instance of PostgresNode.
577603 """
578604
579- attempts = 0
605+ self . _try_shutdown ( max_attempts )
580606
581- # try stopping server
582- while attempts < max_attempts :
583- try :
584- self .stop ()
585- break # OK
586- except ExecUtilException :
587- pass # one more time
588- except Exception :
589- print ('cannot stop node {}' .format (self .name ))
590-
591- attempts += 1
592-
593- # remove directory tree if necessary
594- if self ._should_rm_dirs :
595-
596- # choose directory to be removed
597- if TestgresConfig .node_cleanup_full :
598- rm_dir = self .base_dir # everything
599- else :
600- rm_dir = self .data_dir # just data, save logs
607+ # choose directory to be removed
608+ if TestgresConfig .node_cleanup_full :
609+ rm_dir = self .base_dir # everything
610+ else :
611+ rm_dir = self .data_dir # just data, save logs
601612
602- shutil .rmtree (rm_dir , ignore_errors = True )
613+ shutil .rmtree (rm_dir , ignore_errors = True )
603614
604615 return self
605616
@@ -750,8 +761,8 @@ def poll_query_until(self,
750761 raise_programming_error = True ,
751762 raise_internal_error = True ):
752763 """
753- Run a query once a second until it returs 'expected'.
754- Query should return single column.
764+ Run a query once per second until it returns 'expected'.
765+ Query should return a single value (1 row, 1 column) .
755766
756767 Args:
757768 query: query to be executed.
0 commit comments