Skip to content

Commit 9145606

Browse files
authored
Add Pylint (#465)
Add Pylint checking. Fixed all the current errors and warnings, any future PRs will fail if they introduce any pylint error/warnings.
1 parent 5921325 commit 9145606

26 files changed

+200
-174
lines changed

.pylintrc

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
[MASTER]
2+
3+
ignore=
4+
tensorflow_serving
5+
6+
[MESSAGES CONTROL]
7+
8+
disable=
9+
C, # convention
10+
R, # refactor
11+
too-many-arguments, # We should fix the offending ones soon.
12+
too-many-lines, # Some files are too big, we should fix this too
13+
too-few-public-methods,
14+
too-many-instance-attributes,
15+
too-many-locals,
16+
len-as-condition, # Nice to have in the future
17+
bad-indentation,
18+
line-too-long, # We let Flake8 take care of this
19+
logging-format-interpolation,
20+
useless-object-inheritance, # We still support python2 so inheriting from object is ok
21+
invalid-name,
22+
import-error,
23+
logging-not-lazy,
24+
fixme,
25+
no-self-use,
26+
attribute-defined-outside-init,
27+
protected-access,
28+
invalid-all-object,
29+
arguments-differ,
30+
abstract-method,
31+
signature-differs
32+
33+
[REPORTS]
34+
# Set the output format. Available formats are text, parseable, colorized, msvs
35+
# (visual studio) and html
36+
output-format=colorized
37+
38+
# Tells whether to display a full report or only the messages
39+
# CHANGE: No report.
40+
reports=no
41+
42+
[FORMAT]
43+
# Maximum number of characters on a single line.
44+
max-line-length=100
45+
# Maximum number of lines in a module
46+
#max-module-lines=1000
47+
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 tab).
48+
indent-string=' '
49+
50+
[BASIC]
51+
52+
# Required attributes for module, separated by a comma
53+
#required-attributes=
54+
# List of builtins function names that should not be used, separated by a comma.
55+
# XXX: Should we ban map() & filter() for list comprehensions?
56+
# exit & quit are for the interactive interpreter shell only.
57+
# https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module
58+
bad-functions=
59+
apply,
60+
exit,
61+
input,
62+
quit,
63+
64+
[SIMILARITIES]
65+
# Minimum lines number of a similarity.
66+
min-similarity-lines=5
67+
# Ignore comments when computing similarities.
68+
ignore-comments=yes
69+
# Ignore docstrings when computing similarities.
70+
ignore-docstrings=yes
71+
72+
[VARIABLES]
73+
# Tells whether we should check for unused import in __init__ files.
74+
init-import=no
75+
# A regular expression matching the beginning of the name of dummy variables
76+
# (i.e. not used).
77+
dummy-variables-rgx=_|unused_
78+
79+
# List of additional names supposed to be defined in builtins. Remember that
80+
# you should avoid to define new builtins when possible.
81+
#additional-builtins=
82+
83+
[LOGGING]
84+
# Apply logging string format checks to calls on these modules.
85+
logging-modules=
86+
logging

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66
==========
77

88
* doc-fix: fix rendering error in README.rst
9+
* build: added pylint
910

1011
1.14.1
1112
======

src/sagemaker/amazon/amazon_estimator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
import json
1616
import logging
1717
import tempfile
18+
1819
from six.moves.urllib.parse import urlparse
20+
1921
from sagemaker.amazon import validation
2022
from sagemaker.amazon.hyperparameter import Hyperparameter as hp # noqa
2123
from sagemaker.amazon.common import write_numpy_to_dense_tensor
@@ -32,6 +34,8 @@ class AmazonAlgorithmEstimatorBase(EstimatorBase):
3234

3335
feature_dim = hp('feature_dim', validation.gt(0), data_type=int)
3436
mini_batch_size = hp('mini_batch_size', validation.gt(0), data_type=int)
37+
repo_name = None
38+
repo_version = None
3539

3640
def __init__(self, role, train_instance_count, train_instance_type, data_location=None, **kwargs):
3741
"""Initialize an AmazonAlgorithmEstimatorBase.
@@ -263,7 +267,7 @@ def upload_numpy_to_s3_shards(num_shards, s3, bucket, key_prefix, array, labels=
263267
[{'prefix': 's3://{}/{}'.format(bucket, key_prefix)}] + uploaded_files)
264268
s3.Object(bucket, manifest_key).put(Body=manifest_str.encode('utf-8'))
265269
return "s3://{}/{}".format(bucket, manifest_key)
266-
except Exception as ex:
270+
except Exception as ex: # pylint: disable=broad-except
267271
try:
268272
for file in uploaded_files:
269273
s3.Object(bucket, key_prefix + file).delete()

src/sagemaker/amazon/kmeans.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,9 @@ def _prepare_for_training(self, records, mini_batch_size=5000, job_name=None):
121121

122122
def hyperparameters(self):
123123
"""Return the SageMaker hyperparameters for training this KMeans Estimator"""
124-
hp = dict(force_dense='True') # KMeans requires this hp to fit on Record objects
125-
hp.update(super(KMeans, self).hyperparameters())
126-
return hp
124+
hp_dict = dict(force_dense='True') # KMeans requires this hp to fit on Record objects
125+
hp_dict.update(super(KMeans, self).hyperparameters())
126+
return hp_dict
127127

128128

129129
class KMeansPredictor(RealTimePredictor):

src/sagemaker/cli/mxnet.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ def host(args):
2424

2525

2626
class MXNetTrainCommand(TrainCommand):
27-
def __init__(self, args):
28-
super(MXNetTrainCommand, self).__init__(args)
29-
3027
def create_estimator(self):
3128
from sagemaker.mxnet.estimator import MXNet
3229
return MXNet(self.script,
@@ -39,9 +36,6 @@ def create_estimator(self):
3936

4037

4138
class MXNetHostCommand(HostCommand):
42-
def __init__(self, args):
43-
super(MXNetHostCommand, self).__init__(args)
44-
4539
def create_model(self, model_url):
4640
from sagemaker.mxnet.model import MXNetModel
4741
return MXNetModel(model_data=model_url, role=self.role_name, entry_point=self.script,

src/sagemaker/cli/tensorflow.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ def create_estimator(self):
4343

4444

4545
class TensorFlowHostCommand(HostCommand):
46-
def __init__(self, args):
47-
super(TensorFlowHostCommand, self).__init__(args)
48-
4946
def create_model(self, model_url):
5047
from sagemaker.tensorflow.model import TensorFlowModel
5148
return TensorFlowModel(model_data=model_url, role=self.role_name, entry_point=self.script,

src/sagemaker/estimator.py

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -210,21 +210,6 @@ def fit(self, inputs=None, wait=True, logs=True, job_name=None):
210210
if wait:
211211
self.latest_training_job.wait(logs=logs)
212212

213-
@classmethod
214-
def _from_training_job(cls, init_params, hyperparameters, image, sagemaker_session):
215-
"""Create an Estimator from existing training job data.
216-
217-
Args:
218-
init_params (dict): The init_params the training job was created with.
219-
hyperparameters (dict): The hyperparameters the training job was created with.
220-
image (str): Container image (if any) the training job was created with
221-
sagemaker_session (sagemaker.session.Session): A sagemaker Session to pass to the estimator.
222-
223-
Returns: An instance of the calling Estimator Class.
224-
225-
"""
226-
raise NotImplementedError()
227-
228213
@classmethod
229214
def attach(cls, training_job_name, sagemaker_session=None, model_channel_name='model'):
230215
"""Attach to an existing training job.
@@ -262,7 +247,7 @@ def attach(cls, training_job_name, sagemaker_session=None, model_channel_name='m
262247

263248
estimator = cls(sagemaker_session=sagemaker_session, **init_params)
264249
estimator.latest_training_job = _TrainingJob(sagemaker_session=sagemaker_session,
265-
training_job_name=init_params['base_job_name'])
250+
job_name=init_params['base_job_name'])
266251
estimator.latest_training_job.wait()
267252
return estimator
268253

@@ -425,9 +410,6 @@ def _ensure_latest_training_job(self, error_message='Estimator is not associated
425410

426411

427412
class _TrainingJob(_Job):
428-
def __init__(self, sagemaker_session, training_job_name):
429-
super(_TrainingJob, self).__init__(sagemaker_session, training_job_name)
430-
431413
@classmethod
432414
def start_new(cls, estimator, inputs):
433415
"""Create a new Amazon SageMaker training job from the estimator.
@@ -627,12 +609,10 @@ class Framework(EstimatorBase):
627609
such as training/deployment images and predictor instances.
628610
"""
629611

630-
_DISTRIBUTION_SUPPORTED_FRAMEWORKS = ('mxnet',)
631-
LAUNCH_PS_ENV_NAME = 'sagemaker_parameter_server_enabled'
612+
__framework_name__ = None
632613

633614
def __init__(self, entry_point, source_dir=None, hyperparameters=None, enable_cloudwatch_metrics=False,
634-
container_log_level=logging.INFO, code_location=None, image_name=None,
635-
distributions=None, **kwargs):
615+
container_log_level=logging.INFO, code_location=None, image_name=None, **kwargs):
636616
"""Base class initializer. Subclasses which override ``__init__`` should invoke ``super()``
637617
638618
Args:
@@ -654,8 +634,6 @@ def __init__(self, entry_point, source_dir=None, hyperparameters=None, enable_cl
654634
image_name (str): An alternate image name to use instead of the official Sagemaker image
655635
for the framework. This is useful to run one of the Sagemaker supported frameworks
656636
with an image containing custom dependencies.
657-
distributions (dict): A dictionary with information on how to run distributed training
658-
(default: None).
659637
**kwargs: Additional kwargs passed to the ``EstimatorBase`` constructor.
660638
"""
661639
super(Framework, self).__init__(**kwargs)
@@ -670,22 +648,6 @@ def __init__(self, entry_point, source_dir=None, hyperparameters=None, enable_cl
670648
self.image_name = image_name
671649

672650
self._hyperparameters = hyperparameters or {}
673-
self._configure_distributions(distributions)
674-
675-
def _configure_distributions(self, distributions):
676-
if distributions is None:
677-
return
678-
679-
if self.__framework_name__ not in self._DISTRIBUTION_SUPPORTED_FRAMEWORKS:
680-
raise ValueError('This framework does not support the distributions option.')
681-
682-
if self.framework_version.split('.') < self._LOWEST_SCRIPT_MODE_VERSION:
683-
raise ValueError('The distributions option is valid for only versions {} and higher'
684-
.format('.'.join(self._LOWEST_SCRIPT_MODE_VERSION)))
685-
686-
if 'parameter_server' in distributions:
687-
enabled = distributions['parameter_server'].get('enabled', False)
688-
self._hyperparameters[self.LAUNCH_PS_ENV_NAME] = enabled
689651

690652
def _prepare_for_training(self, job_name=None):
691653
"""Set hyperparameters needed for training. This method will also validate ``source_dir``.
@@ -810,8 +772,11 @@ def train_image(self):
810772
if self.image_name:
811773
return self.image_name
812774
else:
813-
return create_image_uri(self.sagemaker_session.boto_region_name, self.__framework_name__,
814-
self.train_instance_type, self.framework_version, py_version=self.py_version)
775+
return create_image_uri(self.sagemaker_session.boto_region_name,
776+
self.__framework_name__,
777+
self.train_instance_type,
778+
self.framework_version, # pylint: disable=no-member
779+
py_version=self.py_version) # pylint: disable=no-member
815780

816781
@classmethod
817782
def attach(cls, training_job_name, sagemaker_session=None, model_channel_name='model'):
@@ -844,7 +809,11 @@ def attach(cls, training_job_name, sagemaker_session=None, model_channel_name='m
844809
Instance of the calling ``Estimator`` Class with the attached training job.
845810
"""
846811
estimator = super(Framework, cls).attach(training_job_name, sagemaker_session, model_channel_name)
847-
estimator.uploaded_code = UploadedCode(estimator.source_dir, estimator.entry_point)
812+
813+
# pylint gets confused thinking that estimator is an EstimatorBase instance, but it actually
814+
# is a Framework or any of its derived classes. We can safely ignore the no-member errors.
815+
estimator.uploaded_code = UploadedCode(
816+
estimator.source_dir, estimator.entry_point) # pylint: disable=no-member
848817
return estimator
849818

850819
@staticmethod

src/sagemaker/fw_utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919

2020
import sagemaker.utils
2121

22-
"""This module contains utility functions shared across ``Framework`` components."""
23-
2422
UploadedCode = namedtuple('UserCode', ['s3_prefix', 'script_name'])
2523
"""sagemaker.fw_utils.UserCode: An object containing the S3 prefix and script name.
2624
@@ -36,8 +34,9 @@
3634
VALID_PY_VERSIONS = ['py2', 'py3']
3735

3836

39-
def create_image_uri(region, framework, instance_type, framework_version, py_version=None, account='520713654638',
40-
optimized_families=[]):
37+
def create_image_uri(region, framework, instance_type, framework_version, py_version=None,
38+
account='520713654638', optimized_families=None):
39+
4140
"""Return the ECR URI of an image.
4241
4342
Args:
@@ -53,6 +52,7 @@ def create_image_uri(region, framework, instance_type, framework_version, py_ver
5352
Returns:
5453
str: The appropriate image URI based on the given parameters.
5554
"""
55+
optimized_families = optimized_families or []
5656

5757
if py_version and py_version not in VALID_PY_VERSIONS:
5858
raise ValueError('invalid py_version argument: {}'.format(py_version))

src/sagemaker/job.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def __init__(self, sagemaker_session, job_name):
3232
self.job_name = job_name
3333

3434
@abstractmethod
35-
def start_new(cls, estimator, inputs):
35+
def start_new(self, estimator, inputs):
3636
"""Create a new Amazon SageMaker job from the estimator.
3737
3838
Args:
@@ -111,21 +111,21 @@ def _convert_input_to_channel(channel_name, channel_s3_input):
111111
return channel_config
112112

113113
@staticmethod
114-
def _format_string_uri_input(input):
115-
if isinstance(input, str):
116-
if input.startswith('s3://'):
117-
return s3_input(input)
118-
elif input.startswith('file://'):
119-
return file_input(input)
114+
def _format_string_uri_input(uri_input):
115+
if isinstance(uri_input, str):
116+
if uri_input.startswith('s3://'):
117+
return s3_input(uri_input)
118+
elif uri_input.startswith('file://'):
119+
return file_input(uri_input)
120120
else:
121121
raise ValueError('Training input data must be a valid S3 or FILE URI: must start with "s3://" or '
122122
'"file://"')
123-
elif isinstance(input, s3_input):
124-
return input
125-
elif isinstance(input, file_input):
126-
return input
123+
elif isinstance(uri_input, s3_input):
124+
return uri_input
125+
elif isinstance(uri_input, file_input):
126+
return uri_input
127127
else:
128-
raise ValueError('Cannot format input {}. Expecting one of str, s3_input, or file_input'.format(input))
128+
raise ValueError('Cannot format input {}. Expecting one of str, s3_input, or file_input'.format(uri_input))
129129

130130
@staticmethod
131131
def _prepare_model_channel(input_config, model_uri=None, model_channel_name=None):

0 commit comments

Comments
 (0)