Skip to content

Commit

Permalink
Merge pull request #332 from sentinel-hub/feat/format-be-gone
Browse files Browse the repository at this point in the history
Update .format to f-strings
  • Loading branch information
AleksMat committed Sep 20, 2021
2 parents acbda59 + 6a0bf6f commit 8306800
Show file tree
Hide file tree
Showing 31 changed files with 161 additions and 165 deletions.
2 changes: 1 addition & 1 deletion core/eolearn/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class FileFormat(Enum):
def extension(self):
""" Returns file extension of file format
"""
return '.{}'.format(self.value)
return f'.{self.value}'

@staticmethod
def split_by_extensions(filename):
Expand Down
2 changes: 1 addition & 1 deletion core/eolearn/core/core_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ def execute(self, eopatch):

for feature_type, feature_name, new_feature_name in self.feature_gen(eopatch):
if new_feature_name in eopatch[feature_type]:
raise ValueError("A feature named '{}' already exists.".format(new_feature_name))
raise ValueError(f"A feature named '{new_feature_name}' already exists.")

if self.deep:
eopatch[feature_type][new_feature_name] = copy.deepcopy(eopatch[feature_type][feature_name])
Expand Down
84 changes: 41 additions & 43 deletions core/eolearn/core/eodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ def _parse_feature_type_value(feature_type, value):
return [timestamp if isinstance(timestamp, datetime.date) else dateutil.parser.parse(timestamp)
for timestamp in value]

raise TypeError('Attribute {} requires value of type {} - '
'failed to parse given value {}'.format(feature_type, feature_type.type(), value))
raise TypeError(f'Attribute {feature_type} requires value of type {feature_type.type()} - '
f'failed to parse given value {value}')

def __getattribute__(self, key, load=True, feature_name=None):
""" Handles lazy loading and it can even provide a single feature from _FeatureDict
Expand Down Expand Up @@ -186,16 +186,19 @@ def __add__(self, other):
return self.merge(other)

def __repr__(self):
feature_repr_list = ['{}('.format(self.__class__.__name__)]
feature_repr_list = [f'{self.__class__.__name__}(']
for feature_type in FeatureType:
content = self[feature_type]

if isinstance(content, dict) and content:
content_str = '\n '.join(['{'] + ['{}: {}'.format(label, self._repr_value(value)) for label, value in
sorted(content.items())]) + '\n }'
content_str = (
'{\n '
+ '\n '.join([f'{label}: {self._repr_value(value)}' for label, value in sorted(content.items())])
+ '\n }'
)
else:
content_str = self._repr_value(content)
feature_repr_list.append('{}: {}'.format(feature_type.value, content_str))
feature_repr_list.append(f'{feature_type.value}: {content_str}')

return '\n '.join(feature_repr_list) + '\n)'

Expand All @@ -208,31 +211,28 @@ def _repr_value(value):
:rtype: str
"""
if isinstance(value, np.ndarray):
return '{}(shape={}, dtype={})'.format(EOPatch._repr_value_class(value), value.shape, value.dtype)
return f'{EOPatch._repr_value_class(value)}(shape={value.shape}, dtype={value.dtype})'

if isinstance(value, gpd.GeoDataFrame):
crs = CRS(value.crs).ogc_string() if value.crs else value.crs
return f'{EOPatch._repr_value_class(value)}(' \
f'columns={list(value)}, ' \
f'length={len(value)}, ' \
f'crs={crs})'
return f'{EOPatch._repr_value_class(value)}(columns={list(value)}, length={len(value)}, crs={crs})'

if isinstance(value, (list, tuple, dict)) and value:
repr_str = str(value)
if len(repr_str) <= MAX_DATA_REPR_LEN:
return repr_str

bracket_str = '[{}]' if isinstance(value, list) else '({})'
l_bracket, r_bracket = '[]' if isinstance(value, list) else '()'
if isinstance(value, (list, tuple)) and len(value) > 2:
repr_str = bracket_str.format('{}, ..., {}'.format(repr(value[0]), repr(value[-1])))
repr_str = f'{l_bracket}{repr(value[0])}, ..., {repr(value[-1])}{r_bracket}'

if len(repr_str) > MAX_DATA_REPR_LEN and isinstance(value, (list, tuple)) and len(value) > 1:
repr_str = bracket_str.format('{}, ...'.format(repr(value[0])))
repr_str = f'{l_bracket}{repr(value[0])}, ...{r_bracket}'

if len(repr_str) > MAX_DATA_REPR_LEN:
repr_str = str(type(value))

return '{}, length={}'.format(repr_str, len(value))
return f'{repr_str}, length={len(value)}'

return repr(value)

Expand Down Expand Up @@ -337,8 +337,7 @@ def rename_feature(self, feature_type, feature_name, new_feature_name):
self[feature_type][new_feature_name] = self[feature_type][feature_name]
del self[feature_type][feature_name]
else:
raise ValueError("Feature {} from attribute {} does not exist!".format(
feature_name, feature_type.value))
raise ValueError(f"Feature {feature_name} from attribute {feature_type.value} does not exist!")
else:
LOGGER.debug("Feature '%s' was not renamed because new name is identical.", feature_name)

Expand All @@ -352,7 +351,7 @@ def _check_if_dict(feature_type):
"""
feature_type = FeatureType(feature_type)
if feature_type.type() is not dict:
raise TypeError('{} does not contain a dictionary of features'.format(feature_type))
raise TypeError(f'{feature_type} does not contain a dictionary of features')

def reset_feature_type(self, feature_type):
"""Resets the values of the given feature type.
Expand Down Expand Up @@ -484,8 +483,8 @@ def concatenate(eopatch1, eopatch2):
if feature_type.is_time_dependent() and not timestamps_match:
eopatch_content[feature_type.value][feature_name] = EOPatch.concatenate_data(data1, data2)
elif not deep_eq(data1, data2):
raise ValueError('Could not merge ({}, {}) feature because values differ'.format(feature_type,
feature_name))
raise ValueError(f'Could not merge ({feature_type}, {feature_name}) feature because values '
'differ')

elif feature_type is FeatureType.TIMESTAMP and timestamps_exist and not timestamps_match:
eopatch_content[feature_type.value] = eopatch1[feature_type] + eopatch2[feature_type]
Expand All @@ -495,7 +494,7 @@ def concatenate(eopatch1, eopatch2):
elif not eopatch2[feature_type]:
eopatch_content[feature_type.value] = copy.copy(eopatch1[feature_type])
else:
raise ValueError('Could not merge {} feature because values differ'.format(feature_type))
raise ValueError(f'Could not merge {feature_type} feature because values differ')

return EOPatch(**eopatch_content)

Expand Down Expand Up @@ -718,16 +717,15 @@ def __setitem__(self, feature_name, value):

def _check_feature_name(self, feature_name):
if not isinstance(feature_name, str):
error_msg = "Feature name must be a string but an object of type {} was given."
raise ValueError(error_msg.format(type(feature_name)))
raise ValueError(f'Feature name must be a string but an object of type {type(feature_name)} was given.')

for char in feature_name:
if char in self.FORBIDDEN_CHARS:
error_msg = "The name of feature ({}, {}) contains an illegal character '{}'."
raise ValueError(error_msg.format(self.feature_type, feature_name, char))
raise ValueError(f"The name of feature ({self.feature_type}, {feature_name}) contains an illegal "
f"character '{char}'.")

if feature_name == '':
raise ValueError("Feature name cannot be an empty string.")
raise ValueError('Feature name cannot be an empty string.')

def __getitem__(self, feature_name, load=True):
"""Implements lazy loading."""
Expand Down Expand Up @@ -765,27 +763,27 @@ def _parse_feature_value(self, value):

if self.ndim:
if not isinstance(value, np.ndarray):
raise ValueError('{} feature has to be a numpy array'.format(self.feature_type))
raise ValueError(f'{self.feature_type} feature has to be a numpy array')
if value.ndim != self.ndim:
raise ValueError('Numpy array of {} feature has to have {} '
'dimension{}'.format(self.feature_type, self.ndim, 's' if self.ndim > 1 else ''))
raise ValueError(f'Numpy array of {self.feature_type} feature has to have {self.ndim} '
f'dimension{"s" if self.ndim > 1 else ""}')

if self.feature_type.is_discrete():
if not issubclass(value.dtype.type, (np.integer, bool, np.bool_, np.bool8)):
msg = '{} is a discrete feature type therefore dtype of data should be a subtype of ' \
'numpy.integer or numpy.bool, found type {}. In the future an error will be raised because ' \
'of this'.format(self.feature_type, value.dtype.type)
msg = (
f'{self.feature_type} is a discrete feature type therefore dtype of data should be a subtype '
f'of numpy.integer or numpy.bool, found type {value.dtype.type}. In the future an error will '
'be raised because of this'
)
warnings.warn(msg, DeprecationWarning, stacklevel=3)

# raise ValueError('{} is a discrete feature type therefore dtype of data has to be a subtype of '
# 'numpy.integer or numpy.bool, found type {}'.format(self.feature_type,
# value.dtype.type))
# This checking is disabled for now
# raise ValueError(f'{self.feature_type} is a discrete feature type therefore dtype of data has to '
# f'be a subtype of numpy.integer or numpy.bool, found type {value.dtype.type}')
# # This checking is disabled for now
# else:
# if not issubclass(value.dtype.type, (np.floating, np.float)):
# raise ValueError('{} is a floating feature type therefore dtype of data has to be a subtype of '
# 'numpy.floating or numpy.float, found type {}'.format(self.feature_type,
# value.dtype.type))
# raise ValueError(f'{self.feature_type} is a floating feature type therefore dtype of data has to '
# f'be a subtype of numpy.floating or numpy.float, found type {value.dtype.type}')
return value

if self.is_vector:
Expand All @@ -795,12 +793,12 @@ def _parse_feature_value(self, value):
if isinstance(value, gpd.GeoDataFrame):
if self.feature_type is FeatureType.VECTOR:
if FeatureType.TIMESTAMP.value.upper() not in value:
raise ValueError("{} feature has to contain a column 'TIMESTAMP' with "
"timestamps".format(self.feature_type))
raise ValueError(f"{self.feature_type} feature has to contain a column 'TIMESTAMP' with "
"timestamps")

return value

raise ValueError('{} feature works with data of type {}, parsing data type {} is not supported'
'given'.format(self.feature_type, gpd.GeoDataFrame.__name__, type(value)))
raise ValueError(f'{self.feature_type} feature works with data of type {gpd.GeoDataFrame.__name__}, '
f'parsing data type {type(value)} is not supported')

return value
10 changes: 5 additions & 5 deletions core/eolearn/core/eoexecution.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,7 @@ def _try_add_logging(self, log_path, filter_logs_by_thread):
logger.addHandler(handler)
return logger, handler
except BaseException as exception:
warnings.warn('Failed to create logs with exception: {}'.format(repr(exception)),
category=RuntimeWarning)
warnings.warn(f'Failed to create logs with exception: {repr(exception)}', category=RuntimeWarning)

return None, None

Expand All @@ -182,7 +181,8 @@ def _try_remove_logging(cls, log_path, logger, handler, stats):
"""
if log_path:
try:
message = 'EOWorkflow execution {}'.format('failed' if cls.STATS_ERROR in stats else 'finished')
status = 'failed' if cls.STATS_ERROR in stats else 'finished'
message = f'EOWorkflow execution {status}'
logger.debug(message)
handler.close()
logger.removeHandler(handler)
Expand Down Expand Up @@ -243,13 +243,13 @@ def _get_report_folder(self):
""" Returns file path of folder where report will be saved
"""
return os.path.join(self.logs_folder,
'eoexecution-report-{}'.format(self.start_time.strftime("%Y_%m_%d-%H_%M_%S")))
f'eoexecution-report-{self.start_time.strftime("%Y_%m_%d-%H_%M_%S")}')

def _get_log_paths(self):
""" Returns a list of file paths containing logs
"""
if self.save_logs:
return [os.path.join(self.report_folder, 'eoexecution-{}.log'.format(name))
return [os.path.join(self.report_folder, f'eoexecution-{name}.log')
for name in self.execution_names]

return [None] * len(self.execution_names)
Expand Down
2 changes: 1 addition & 1 deletion core/eolearn/core/eotask.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def _execute_handling(self, *eopatches, **kwargs):

# Some special exceptions don't accept an error message as a parameter and raise a TypeError in such case.
try:
errmsg = 'During execution of task {}: {}'.format(self.__class__.__name__, exception)
errmsg = f'During execution of task {self.__class__.__name__}: {exception}'
extended_exception = type(exception)(errmsg)
except TypeError:
extended_exception = exception
Expand Down
20 changes: 10 additions & 10 deletions core/eolearn/core/eoworkflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ def create_dag(self, dependencies):
for vertex in dep.inputs:
task_uuid = vertex.private_task_config.uuid
if task_uuid not in self.uuid_dict:
raise ValueError('Task {}, which is an input of a task {}, is not part of the defined '
'workflow'.format(vertex.__class__.__name__, dep.name))
raise ValueError(f'Task {vertex.__class__.__name__}, which is an input of a task {dep.name}, is '
'not part of the defined workflow')
dag.add_edge(self.uuid_dict[task_uuid], dep)
if not dep.inputs:
dag.add_vertex(dep)
Expand Down Expand Up @@ -182,11 +182,11 @@ def parse_input_args(input_args):
input_args = input_args if input_args else {}
for task, args in input_args.items():
if not isinstance(task, EOTask):
raise ValueError('Invalid input argument {}, should be an instance of EOTask'.format(task))
raise ValueError(f'Invalid input argument {task}, should be an instance of EOTask')

if not isinstance(args, (tuple, dict)):
raise ValueError('Execution input arguments of each task should be a dictionary or a tuple, for task '
'{} got arguments of type {}'.format(task.__class__.__name__, type(args)))
f'{task.__class__.__name__} got arguments of type {type(args)}')

return input_args

Expand Down Expand Up @@ -389,16 +389,16 @@ def __attrs_post_init__(self):
""" This is executed right after init method
"""
if not isinstance(self.task, EOTask):
raise ValueError('Value {} should be an instance of {}'.format(self.task, EOTask.__name__))
raise ValueError(f'Value {self.task} should be an instance of {EOTask.__name__}')
self.task = self.task

if isinstance(self.inputs, EOTask):
self.inputs = [self.inputs]
if not isinstance(self.inputs, (list, tuple)):
raise ValueError('Value {} should be a list'.format(self.inputs))
raise ValueError(f'Value {self.inputs} should be a list')
for input_task in self.inputs:
if not isinstance(input_task, EOTask):
raise ValueError('Value {} should be an instance of {}'.format(input_task, EOTask.__name__))
raise ValueError(f'Value {input_task} should be an instance of {EOTask.__name__}')

if self.name is None:
self.name = self.task.__class__.__name__
Expand All @@ -412,7 +412,7 @@ def get_custom_name(self, number=0):
""" Provides custom task name according to given number. E.g. FooTask -> FooTask
"""
if number:
return '{}_{}'.format(self.name, number)
return f'{self.name}_{number}'
return self.name


Expand Down Expand Up @@ -474,11 +474,11 @@ def eopatch(self):
return list(self.values())[-1]

def __repr__(self):
repr_list = ['{}('.format(self.__class__.__name__)]
repr_list = [f'{self.__class__.__name__}(']

for _, dep in self._uuid_dict.items():
result_repr = repr(self._result[dep]).replace('\n', '\n ')
dependency_repr = '{}({}):\n {}'.format(Dependency.__name__, dep.name, result_repr)
dependency_repr = f'{Dependency.__name__}({dep.name}):\n {result_repr}'

repr_list.append(dependency_repr)

Expand Down
2 changes: 1 addition & 1 deletion core/eolearn/core/fs_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def load_s3_filesystem(path, strict=False, config=None):
:rtype: fs_s3fs.S3FS
"""
if not path.startswith('s3://'):
raise ValueError("AWS path has to start with s3:// but found '{}'".format(path))
raise ValueError(f"AWS path has to start with s3:// but found '{path}'")

if config is None:
config = SHConfig()
Expand Down

0 comments on commit 8306800

Please sign in to comment.