Skip to content

Commit

Permalink
Replace PEP8 by pylint (#257)
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentame committed Mar 23, 2020
1 parent a635863 commit 1090393
Show file tree
Hide file tree
Showing 18 changed files with 296 additions and 191 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -14,5 +14,5 @@ before_install:
- pip install -r requirements_all.txt
- pip install -e .
script:
- pep8 pyicloud
- pylint pyicloud tests
- py.test
1 change: 1 addition & 0 deletions pyicloud/__init__.py
@@ -1,3 +1,4 @@
"""The pyiCloud library."""
import logging
from pyicloud.base import PyiCloudService

Expand Down
121 changes: 65 additions & 56 deletions pyicloud/base.py
@@ -1,10 +1,10 @@
"""Library base file."""
import six
import uuid
import hashlib
import inspect
import json
import logging
import requests
from requests import Session
import sys
import tempfile
import os
Expand All @@ -30,40 +30,42 @@
if six.PY3:
import http.cookiejar as cookielib
else:
import cookielib
import cookielib # pylint: disable=import-error


logger = logging.getLogger(__name__)
LOGGER = logging.getLogger(__name__)


class PyiCloudPasswordFilter(logging.Filter):
"""Password log hider."""
def __init__(self, password):
self.password = password
super(PyiCloudPasswordFilter, self).__init__(password)

def filter(self, record):
message = record.getMessage()
if self.password in message:
record.msg = message.replace(self.password, "*" * 8)
if self.name in message:
record.msg = message.replace(self.name, "*" * 8)
record.args = []

return True


class PyiCloudSession(requests.Session):
class PyiCloudSession(Session):
"""iCloud session."""
def __init__(self, service):
self.service = service
super(PyiCloudSession, self).__init__()

def request(self, *args, **kwargs):
def request(self, *args, **kwargs): # pylint: disable=arguments-differ

# Charge logging to the right service endpoint
callee = inspect.stack()[2]
module = inspect.getmodule(callee[0])
logger = logging.getLogger(module.__name__).getChild('http')
if self.service._password_filter not in logger.filters:
logger.addFilter(self.service._password_filter)
request_logger = logging.getLogger(module.__name__).getChild('http')
if self.service.password_filter not in request_logger.filters:
request_logger.addFilter(self.service.password_filter)

logger.debug("%s %s %s", args[0], args[1], kwargs.get('data', ''))
request_logger.debug("%s %s %s", args[0], args[1], kwargs.get('data', ''))

kwargs.pop('retried', None)
response = super(PyiCloudSession, self).request(*args, **kwargs)
Expand All @@ -78,7 +80,7 @@ def request(self, *args, **kwargs):
response.status_code,
retry=True
)
logger.warn(api_error)
request_logger.warn(api_error)
kwargs['retried'] = True
return self.request(*args, **kwargs)
self._raise_error(response.status_code, response.reason)
Expand All @@ -87,24 +89,24 @@ def request(self, *args, **kwargs):
return response

try:
json = response.json()
except:
logger.warning('Failed to parse response with JSON mimetype')
data = response.json()
except: # pylint: disable=bare-except
request_logger.warning('Failed to parse response with JSON mimetype')
return response

logger.debug(json)
request_logger.debug(data)

reason = json.get('errorMessage')
reason = reason or json.get('reason')
reason = reason or json.get('errorReason')
if not reason and isinstance(json.get('error'), six.string_types):
reason = json.get('error')
if not reason and json.get('error'):
reason = data.get('errorMessage')
reason = reason or data.get('reason')
reason = reason or data.get('errorReason')
if not reason and isinstance(data.get('error'), six.string_types):
reason = data.get('error')
if not reason and data.get('error'):
reason = "Unknown reason"

code = json.get('errorCode')
if not code and json.get('serverErrorCode'):
code = json.get('serverErrorCode')
code = data.get('errorCode')
if not code and data.get('serverErrorCode'):
code = data.get('serverErrorCode')

if reason:
self._raise_error(code, reason)
Expand All @@ -115,11 +117,11 @@ def _raise_error(self, code, reason):
if self.service.requires_2sa and \
reason == 'Missing X-APPLE-WEBAUTH-TOKEN cookie':
raise PyiCloud2SARequiredException(self.service.user['apple_id'])
if code == 'ZONE_NOT_FOUND' or code == 'AUTHENTICATION_FAILED':
if code in ('ZONE_NOT_FOUND', 'AUTHENTICATION_FAILED'):
reason = 'Please log into https://icloud.com/ to manually ' \
'finish setting up your iCloud service'
api_error = PyiCloudServiceNotActivatedException(reason, code)
logger.error(api_error)
LOGGER.error(api_error)

raise(api_error)
if code == 'ACCESS_DENIED':
Expand All @@ -128,7 +130,7 @@ def _raise_error(self, code, reason):
'throttle requests.'

api_error = PyiCloudAPIResponseException(reason, code)
logger.error(api_error)
LOGGER.error(api_error)
raise api_error


Expand All @@ -155,8 +157,8 @@ def __init__(
self.with_family = with_family
self.user = {'apple_id': apple_id, 'password': password}

self._password_filter = PyiCloudPasswordFilter(password)
logger.addFilter(self._password_filter)
self.password_filter = PyiCloudPasswordFilter(password)
LOGGER.addFilter(self.password_filter)

self._home_endpoint = 'https://www.icloud.com'
self._setup_endpoint = 'https://setup.icloud.com/setup/ws/1'
Expand Down Expand Up @@ -186,12 +188,12 @@ def __init__(
if os.path.exists(cookiejar_path):
try:
self.session.cookies.load()
logger.debug("Read cookies from %s", cookiejar_path)
except:
LOGGER.debug("Read cookies from %s", cookiejar_path)
except: # pylint: disable=bare-except
# Most likely a pickled cookiejar from earlier versions.
# The cookiejar will get replaced with a valid one after
# successful authentication.
logger.warning("Failed to read cookiejar %s", cookiejar_path)
LOGGER.warning("Failed to read cookiejar %s", cookiejar_path)

self.params = {
'clientBuildNumber': '17DHotfix5',
Expand All @@ -203,13 +205,16 @@ def __init__(

self.authenticate()

self._files = None
self._photos = None

def authenticate(self):
"""
Handles authentication, and persists the X-APPLE-WEB-KB cookie so that
subsequent logins will not cause additional e-mails from Apple.
"""

logger.info("Authenticating as %s", self.user['apple_id'])
LOGGER.info("Authenticating as %s", self.user['apple_id'])

data = dict(self.user)

Expand All @@ -226,22 +231,20 @@ def authenticate(self):
msg = 'Invalid email/password combination.'
raise PyiCloudFailedLoginException(msg, error)

resp = req.json()
self.params.update({'dsid': resp['dsInfo']['dsid']})
self.data = req.json()
self.params.update({'dsid': self.data['dsInfo']['dsid']})
self._webservices = self.data['webservices']

if not os.path.exists(self._cookie_directory):
os.mkdir(self._cookie_directory)
self.session.cookies.save()
logger.debug("Cookies saved to %s", self._get_cookiejar_path())

self.data = resp
self._webservices = self.data['webservices']
LOGGER.debug("Cookies saved to %s", self._get_cookiejar_path())

logger.info("Authentication completed successfully")
logger.debug(self.params)
LOGGER.info("Authentication completed successfully")
LOGGER.debug(self.params)

def _get_cookiejar_path(self):
# Get path for cookiejar file
"""Get path for cookiejar file."""
return os.path.join(
self._cookie_directory,
''.join([c for c in self.user.get('apple_id') if match(r'\w', c)])
Expand All @@ -252,7 +255,7 @@ def requires_2sa(self):
"""Returns True if two-step authentication is required."""
return self.data.get('hsaChallengeRequired', False) \
and self.data['dsInfo'].get('hsaVersion', 0) >= 1
# FIXME: Implement 2FA for hsaVersion == 2
# FIXME: Implement 2FA for hsaVersion == 2 # pylint: disable=fixme

@property
def trusted_devices(self):
Expand Down Expand Up @@ -282,7 +285,7 @@ def validate_verification_code(self, device, code):
data = json.dumps(device)

try:
request = self.session.post(
self.session.post(
'%s/validateVerificationCode' % self._setup_endpoint,
params=self.params,
data=data
Expand Down Expand Up @@ -310,7 +313,7 @@ def _get_webservice_url(self, ws_key):

@property
def devices(self):
"""Return all devices."""
"""Returns all devices."""
service_root = self._get_webservice_url('findme')
return FindMyiPhoneServiceManager(
service_root,
Expand All @@ -319,22 +322,25 @@ def devices(self):
self.with_family
)

@property
def iphone(self):
"""Returns the iPhone."""
return self.devices[0]

@property
def account(self):
"""Gets the 'Account' service."""
service_root = self._get_webservice_url('account')
return AccountService(
service_root,
self.session,
self.params
)

@property
def iphone(self):
return self.devices[0]

@property
def files(self):
if not hasattr(self, '_files'):
"""Gets the 'File' service."""
if not self._files:
service_root = self._get_webservice_url('ubiquity')
self._files = UbiquityService(
service_root,
Expand All @@ -345,7 +351,8 @@ def files(self):

@property
def photos(self):
if not hasattr(self, '_photos'):
"""Gets the 'Photo' service."""
if not self._photos:
service_root = self._get_webservice_url('ckdatabasews')
self._photos = PhotosService(
service_root,
Expand All @@ -356,16 +363,19 @@ def photos(self):

@property
def calendar(self):
"""Gets the 'Calendar' service."""
service_root = self._get_webservice_url('calendar')
return CalendarService(service_root, self.session, self.params)

@property
def contacts(self):
"""Gets the 'Contacts' service."""
service_root = self._get_webservice_url('contacts')
return ContactsService(service_root, self.session, self.params)

@property
def reminders(self):
"""Gets the 'Reminders' service."""
service_root = self._get_webservice_url('reminders')
return RemindersService(service_root, self.session, self.params)

Expand All @@ -376,8 +386,7 @@ def __str__(self):
as_unicode = self.__unicode__()
if sys.version_info[0] >= 3:
return as_unicode
else:
return as_unicode.encode('utf-8', 'ignore')
return as_unicode.encode('utf-8', 'ignore')

def __repr__(self):
return '<%s>' % str(self)
20 changes: 8 additions & 12 deletions pyicloud/cmdline.py
Expand Up @@ -9,7 +9,7 @@
import pickle
import sys

from click import confirm
from click import confirm, prompt

import pyicloud
from . import utils
Expand All @@ -25,13 +25,10 @@ def create_pickled_data(idevice, filename):
after the passed filename.
This allows the data to be used without resorting to screen / pipe
scrapping. """
data = {}
for x in idevice.content:
data[x] = idevice.content[x]
scrapping."""
location = filename
pickle_file = open(location, 'wb')
pickle.dump(data, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
pickle.dump(idevice.content, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
pickle_file.close()


Expand Down Expand Up @@ -209,7 +206,6 @@ def main(args=None):
utils.store_password_in_keyring(username, password)

if api.requires_2sa:
import click
print("Two-step authentication required.",
"Your trusted devices are:")

Expand All @@ -220,14 +216,14 @@ def main(args=None):
'deviceName',
"SMS to %s" % device.get('phoneNumber'))))

device = click.prompt('Which device would you like to use?',
device = prompt('Which device would you like to use?',
default=0)
device = devices[device]
if not api.send_verification_code(device):
print("Failed to send verification code")
sys.exit(1)

code = click.prompt('Please enter validation code')
code = prompt('Please enter validation code')
if not api.validate_verification_code(device, code):
print("Failed to verify verification code")
sys.exit(1)
Expand Down Expand Up @@ -258,7 +254,7 @@ def main(args=None):
dev.content["id"].strip().lower()
)
):
# List device(s)
# List device(s)
if command_line.locate:
dev.location()

Expand All @@ -274,8 +270,8 @@ def main(args=None):
if command_line.longlist:
print("-"*30)
print(contents["name"])
for x in contents:
print("%20s - %s" % (x, contents[x]))
for key in contents:
print("%20s - %s" % (key, contents[key]))
elif command_line.list:
print("-"*30)
print("Name - %s" % contents["name"])
Expand Down

0 comments on commit 1090393

Please sign in to comment.