Reputation: 3941
I am using a property class to set a destination on S3
for an AWS
wrapper class. It should only set the destination if an infile is also set. (I've realized that the best solution would be to split up this particular class as it is trying to do two things well, instead of one).
Nevertheless, I'm curious what is causing this recursive stack overflow. Here is the relevant part of my class:
class S3Loads(PrettyStr):
def __init__(self,
infile=None,
s3_destination=None,
s3_bucket=config3.S3_BUCKET,
s3_credentials=config3.S3_INFO,
database_credentials=config3.REDSHIFT_POSTGRES_INFO_PROD):
"""
This class automates the upload of a file to AWS S3.
:param infile: A gzipped csv file
:param s3_destination: The path to the desired folder on S3. The file
will be named by joining the s3_destination and the infile name.
:param s3_bucket: The name of the bucket on S3
:return: None, but will write out to logging file.
"""
self.infile = infile
self.s3_destination = s3_destination
self.bucket_name = s3_bucket
self.s3_creds = s3_credentials
self.database_credentials = database_credentials
@property
def s3_destination(self):
return self.s3_destination
# TODO This is kicking off a recursive call, find out why
@s3_destination.setter
def s3_destination(self, s3_destination):
if self.infile:
if config3.SYSTEM == 'OSX':
self.s3_destination = path.join(
s3_destination,
self.infile.split('/')[-1]
)
elif config3.SYSTEM == 'Windows':
self.s3_destination = path.join(
s3_destination,
self.infile.split('\\')[-1]
)
else:
logging.warning('S3 Destination is being set to "None" due to '
'no infile being set. Please set an infile '
'and then set the S3 Destination.')
self.s3_destination = None
And here are the unit tests that are kicking this off:
def test_setting_s3_destination_without_infile_set_it_to_none(self):
s3 = aws_utils.S3Loads()
def test_func():
s3.s3_destination = 'Some destination'
self.assertIsNone(s3.s3_destination)
def test_can_set_s3_destination_with_infile_specified(self):
s3 = aws_utils.S3Loads()
s3.infile='testfile.txt'
s3.destination='testloads/test1'
self.assertEqual(s3.destination, 'testloads/test1/testfile.txt')
I'm just curious about what is causing the recursion.
Upvotes: 0
Views: 28
Reputation: 880547
Every time Python encounters self.s3_destination
, the s3_destination
function is called. Since the return value, self.s3_destination
references the property, the s3_destination
function is called recursively, ad infinitum...
@property
def s3_destination(self):
return self.s3_destination
The standard way to fix this is to use a private attribute self._s3_destination
:
class S3Loads(PrettyStr):
def __init__(self,...)
...
self._s3_destination = s3_destination
@property
def s3_destination(self):
return self._s3_destination
@s3_destination.setter
def s3_destination(self, value):
if self.infile:
if config3.SYSTEM == 'OSX':
self._s3_destination = path.join(
value,
self.infile.split('/')[-1]
)
elif config3.SYSTEM == 'Windows':
self._s3_destination = path.join(
value,
self.infile.split('\\')[-1]
)
else:
logging.warning('S3 Destination is being set to "None" due to '
'no infile being set. Please set an infile '
'and then set the S3 Destination.')
self._s3_destination = None
Upvotes: 1