Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace PEP8 by pylint #257

Merged
merged 2 commits into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Loading