diff --git a/.travis.yml b/.travis.yml index ff2ecd2..6d7ba27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,9 @@ language: python python: - - 3.5 - - 3.8 + - '3.8' env: - TOXENV=quality -matrix: - include: - - python: 3.5 - env: TOXENV=py35 - - python: 3.8 - env: TOXENV=py38 + - TOXENV=py38 cache: pip sudo: false branches: @@ -17,19 +11,19 @@ branches: - master - /^\d+\.\d+(\.\d+)?(-\S*)?$/ install: -- pip install coveralls -- pip install -r requirements/tox.txt + - pip install coveralls + - pip install -r requirements/tox.txt script: tox after_success: -- coveralls -- bash ./scripts/build-stats-to-datadog.sh + - coveralls + - bash ./scripts/build-stats-to-datadog.sh deploy: provider: pypi user: edx distributions: sdist bdist_wheel on: tags: true - python: 3.5 - condition: "$TOXENV = quality" + python: 3.8 + condition: $TOXENV = quality password: secure: niAkz7MPmeGxu+dgicFGm8SydYPy0PYZI8jn/Rcs+B9ASPt8oJ0sFxGLtYLl7b0Rg9UcRrHHfoLOMWYK/0n+cdgGPJEmtXHnxBgzSdliYl5kO16A3omK4s7a8794qJ+4DM/LraFc2jU3Fg1t2PJZp4BuNMVIpSch3iZ3bvWhh/s= diff --git a/analyticsclient/__init__.py b/analyticsclient/__init__.py index 9513287..435d64b 100644 --- a/analyticsclient/__init__.py +++ b/analyticsclient/__init__.py @@ -1 +1 @@ -__version__ = '0.16.1' +__version__ = '0.17.0' diff --git a/analyticsclient/base.py b/analyticsclient/base.py index 60b9451..f3794b8 100644 --- a/analyticsclient/base.py +++ b/analyticsclient/base.py @@ -1,5 +1,3 @@ - - from analyticsclient.constants import data_formats, http_methods diff --git a/analyticsclient/client.py b/analyticsclient/client.py index 7502792..05e76b1 100644 --- a/analyticsclient/client.py +++ b/analyticsclient/client.py @@ -1,5 +1,3 @@ - - import logging import requests @@ -95,10 +93,10 @@ def request(self, method, resource, data=None, timeout=None, data_format=data_fo try: return response.json() - except ValueError: + except ValueError as exception: message = 'Unable to decode JSON response' log.exception(message) - raise ClientError(message) + raise ClientError(message) from exception def has_resource(self, resource, timeout=None): """ @@ -138,7 +136,7 @@ def _request(self, method, resource, data=None, timeout=None, data_format=data_f headers['Authorization'] = 'Token ' + self.auth_token try: - uri = '{0}/{1}'.format(self.base_url, resource) + uri = f'{self.base_url}/{resource}' if method == http_methods.GET: params = self._data_to_get_params(data or {}) @@ -147,7 +145,7 @@ def _request(self, method, resource, data=None, timeout=None, data_format=data_f response = requests.post(uri, data=(data or {}), headers=headers, timeout=timeout) else: raise ValueError( - 'Invalid \'method\' argument: expected {0} or {1}, got {2}'.format( + 'Invalid \'method\' argument: expected {} or {}, got {}'.format( http_methods.GET, http_methods.POST, method, @@ -156,14 +154,14 @@ def _request(self, method, resource, data=None, timeout=None, data_format=data_f status = response.status_code if status != requests.codes.ok: - message = 'Resource "{0}" returned status code {1}'.format(resource, status) + message = f'Resource "{resource}" returned status code {status}' error_class = ClientError if status == requests.codes.bad_request: - message = 'The request to {0} was invalid.'.format(uri) + message = f'The request to {uri} was invalid.' error_class = InvalidRequestError elif status == requests.codes.not_found: - message = 'Resource {0} was not found on the API server.'.format(uri) + message = f'Resource {uri} was not found on the API server.' error_class = NotFoundError log.error(message) @@ -171,15 +169,15 @@ def _request(self, method, resource, data=None, timeout=None, data_format=data_f return response - except requests.exceptions.Timeout: - message = "Response from {0} exceeded timeout of {1}s.".format(resource, timeout) + except requests.exceptions.Timeout as exception: + message = f"Response from {resource} exceeded timeout of {timeout}s." log.exception(message) - raise TimeoutError(message) + raise TimeoutError(message) from exception - except requests.exceptions.RequestException: + except requests.exceptions.RequestException as exception: message = 'Unable to retrieve resource' log.exception(message) - raise ClientError('{0} "{1}"'.format(message, resource)) + raise ClientError(f'{message} "{resource}"') from exception @staticmethod def _data_to_get_params(data): diff --git a/analyticsclient/constants/__init__.py b/analyticsclient/constants/__init__.py index 2c6f7fd..0b404d5 100644 --- a/analyticsclient/constants/__init__.py +++ b/analyticsclient/constants/__init__.py @@ -1 +1 @@ -UNKNOWN_COUNTRY_CODE = u'UNKNOWN' +UNKNOWN_COUNTRY_CODE = 'UNKNOWN' diff --git a/analyticsclient/constants/activity_types.py b/analyticsclient/constants/activity_types.py index d64c6ae..acb254b 100644 --- a/analyticsclient/constants/activity_types.py +++ b/analyticsclient/constants/activity_types.py @@ -1,6 +1,6 @@ """Course activity types.""" -ANY = u'any' -ATTEMPTED_PROBLEM = u'attempted_problem' -PLAYED_VIDEO = u'played_video' -POSTED_FORUM = u'posted_forum' +ANY = 'any' +ATTEMPTED_PROBLEM = 'attempted_problem' +PLAYED_VIDEO = 'played_video' +POSTED_FORUM = 'posted_forum' diff --git a/analyticsclient/constants/demographics.py b/analyticsclient/constants/demographics.py index df3ca32..15e9c19 100644 --- a/analyticsclient/constants/demographics.py +++ b/analyticsclient/constants/demographics.py @@ -1,6 +1,6 @@ """Course demographics.""" -BIRTH_YEAR = u'birth_year' -EDUCATION = u'education' -GENDER = u'gender' -LOCATION = u'location' +BIRTH_YEAR = 'birth_year' +EDUCATION = 'education' +GENDER = 'gender' +LOCATION = 'location' diff --git a/analyticsclient/constants/education_levels.py b/analyticsclient/constants/education_levels.py index 7b4ea74..b004472 100644 --- a/analyticsclient/constants/education_levels.py +++ b/analyticsclient/constants/education_levels.py @@ -1,9 +1,9 @@ -NONE = u'none' -OTHER = u'other' -PRIMARY = u'primary' -JUNIOR_SECONDARY = u'junior_secondary' -SECONDARY = u'secondary' -ASSOCIATES = u'associates' -BACHELORS = u'bachelors' -MASTERS = u'masters' -DOCTORATE = u'doctorate' +NONE = 'none' +OTHER = 'other' +PRIMARY = 'primary' +JUNIOR_SECONDARY = 'junior_secondary' +SECONDARY = 'secondary' +ASSOCIATES = 'associates' +BACHELORS = 'bachelors' +MASTERS = 'masters' +DOCTORATE = 'doctorate' diff --git a/analyticsclient/constants/enrollment_modes.py b/analyticsclient/constants/enrollment_modes.py index 4c06d22..4476e81 100644 --- a/analyticsclient/constants/enrollment_modes.py +++ b/analyticsclient/constants/enrollment_modes.py @@ -1,8 +1,8 @@ -AUDIT = u'audit' -CREDIT = u'credit' -HONOR = u'honor' -PROFESSIONAL = u'professional' -VERIFIED = u'verified' -MASTERS = u'masters' +AUDIT = 'audit' +CREDIT = 'credit' +HONOR = 'honor' +PROFESSIONAL = 'professional' +VERIFIED = 'verified' +MASTERS = 'masters' ALL = [AUDIT, CREDIT, HONOR, PROFESSIONAL, VERIFIED, MASTERS] diff --git a/analyticsclient/constants/genders.py b/analyticsclient/constants/genders.py index 362e81d..6691f47 100644 --- a/analyticsclient/constants/genders.py +++ b/analyticsclient/constants/genders.py @@ -1,4 +1,4 @@ -FEMALE = u'female' -MALE = u'male' -OTHER = u'other' -UNKNOWN = u'unknown' +FEMALE = 'female' +MALE = 'male' +OTHER = 'other' +UNKNOWN = 'unknown' diff --git a/analyticsclient/constants/http_methods.py b/analyticsclient/constants/http_methods.py index 1404439..1835a18 100644 --- a/analyticsclient/constants/http_methods.py +++ b/analyticsclient/constants/http_methods.py @@ -1,9 +1,9 @@ -GET = u'GET' -HEAD = u'HEAD' -POST = u'POST' -PUT = u'PUT' -DELETE = u'DELETE' -CONNECT = u'CONNECT' -OPTIONS = u'OPTIONS' -TRACE = u'TRACE' -PATCH = u'PATCH' +GET = 'GET' +HEAD = 'HEAD' +POST = 'POST' +PUT = 'PUT' +DELETE = 'DELETE' +CONNECT = 'CONNECT' +OPTIONS = 'OPTIONS' +TRACE = 'TRACE' +PATCH = 'PATCH' diff --git a/analyticsclient/course.py b/analyticsclient/course.py index 80c08fd..cd95dd0 100644 --- a/analyticsclient/course.py +++ b/analyticsclient/course.py @@ -1,5 +1,3 @@ - - import warnings from urllib.parse import urlencode @@ -21,7 +19,7 @@ def __init__(self, client, course_id): course_id (str): String identifying the course (e.g. edX/DemoX/Demo_Course) """ - super(Course, self).__init__(client) + super().__init__(client) self.course_id = str(course_id) def enrollment(self, demographic=None, start_date=None, end_date=None, data_format=data_formats.JSON): @@ -41,9 +39,9 @@ def enrollment(self, demographic=None, start_date=None, end_date=None, data_form end_date (str): Maximum date for returned enrollment data data_format (str): Format in which data should be returned """ - path = 'courses/{0}/enrollment/'.format(self.course_id) + path = f'courses/{self.course_id}/enrollment/' if demographic: - path += '{0}/'.format(demographic) + path += f'{demographic}/' params = {} if start_date: @@ -54,7 +52,7 @@ def enrollment(self, demographic=None, start_date=None, end_date=None, data_form querystring = urlencode(params) if querystring: - path += '?{0}'.format(querystring) + path += f'?{querystring}' return self.client.get(path, data_format=data_format) @@ -79,9 +77,9 @@ def activity(self, activity_type=activity_types.ANY, start_date=None, end_date=N if end_date: params['end_date'] = end_date - path = 'courses/{0}/activity/'.format(self.course_id) + path = f'courses/{self.course_id}/activity/' querystring = urlencode(params) - path += '?{0}'.format(querystring) + path += f'?{querystring}' return self.client.get(path, data_format=data_format) @@ -95,7 +93,7 @@ def recent_activity(self, activity_type=activity_types.ANY, data_format=data_for """ warnings.warn('recent_activity has been deprecated! Use activity instead.', DeprecationWarning) - path = 'courses/{0}/recent_activity/?activity_type={1}'.format(self.course_id, activity_type) + path = f'courses/{self.course_id}/recent_activity/?activity_type={activity_type}' return self.client.get(path, data_format=data_format) def problems(self, data_format=data_formats.JSON): @@ -105,7 +103,7 @@ def problems(self, data_format=data_formats.JSON): Arguments: data_format (str): Format in which data should be returned """ - path = 'courses/{0}/problems/'.format(self.course_id) + path = f'courses/{self.course_id}/problems/' return self.client.get(path, data_format=data_format) def problems_and_tags(self, data_format=data_formats.JSON): @@ -115,7 +113,7 @@ def problems_and_tags(self, data_format=data_formats.JSON): Arguments: data_format (str): Format in which data should be returned """ - path = 'courses/{0}/problems_and_tags/'.format(self.course_id) + path = f'courses/{self.course_id}/problems_and_tags/' return self.client.get(path, data_format=data_format) def reports(self, report_name, data_format=data_formats.JSON): @@ -125,7 +123,7 @@ def reports(self, report_name, data_format=data_formats.JSON): Arguments: report_name (str): Report name, e.g. "problem_response" """ - path = 'courses/{0}/reports/{1}/'.format(self.course_id, report_name) + path = f'courses/{self.course_id}/reports/{report_name}/' return self.client.get(path, data_format=data_format) def videos(self, data_format=data_formats.JSON): @@ -135,5 +133,5 @@ def videos(self, data_format=data_formats.JSON): Arguments: data_format (str): Format in which data should be returned """ - path = 'courses/{0}/videos/'.format(self.course_id) + path = f'courses/{self.course_id}/videos/' return self.client.get(path, data_format=data_format) diff --git a/analyticsclient/course_summaries.py b/analyticsclient/course_summaries.py index f351da4..dceb6a5 100644 --- a/analyticsclient/course_summaries.py +++ b/analyticsclient/course_summaries.py @@ -1,5 +1,3 @@ - - import datetime from analyticsclient.base import PostableCourseIDsEndpoint diff --git a/analyticsclient/course_totals.py b/analyticsclient/course_totals.py index 4b23fe1..949de71 100644 --- a/analyticsclient/course_totals.py +++ b/analyticsclient/course_totals.py @@ -1,5 +1,3 @@ - - from analyticsclient.base import PostableCourseIDsEndpoint from analyticsclient.constants import data_formats diff --git a/analyticsclient/engagement_timeline.py b/analyticsclient/engagement_timeline.py index a02d8c5..e69e4e4 100644 --- a/analyticsclient/engagement_timeline.py +++ b/analyticsclient/engagement_timeline.py @@ -1,4 +1,3 @@ - from urllib.parse import urlencode from analyticsclient.base import PostableCourseIDsEndpoint @@ -19,7 +18,7 @@ def __init__(self, client, username, course_id): course_id (str): String identifying the course (e.g. edX/DemoX/Demo_Course) """ - super(EngagementTimeline, self).__init__(client) + super().__init__(client) self.username = str(username) self.course_id = str(course_id) @@ -27,5 +26,5 @@ def __init__(self, client, username, course_id): def get(self): """Get a particular learner's engagement timeline for a particular course.""" querystring = urlencode({'course_id': self.course_id}) - path = 'engagement_timelines/{username}/?{querystring}'.format(username=self.username, querystring=querystring) + path = f'engagement_timelines/{self.username}/?{querystring}' return self.client.get(path, data_format=data_formats.JSON) diff --git a/analyticsclient/module.py b/analyticsclient/module.py index 30c6f39..23345a6 100644 --- a/analyticsclient/module.py +++ b/analyticsclient/module.py @@ -1,5 +1,3 @@ - - from analyticsclient.base import BaseEndpoint from analyticsclient.constants import data_formats @@ -16,7 +14,7 @@ def __init__(self, client, course_id, module_id): course_id (str): String identifying the course module_id (str): String identifying the module """ - super(Module, self).__init__(client) + super().__init__(client) self.course_id = str(course_id) self.module_id = str(module_id) @@ -27,7 +25,7 @@ def answer_distribution(self, data_format=data_formats.JSON): Arguments: data_format (str): Format in which to return data (default is JSON) """ - path = 'problems/{0}/answer_distribution/'.format(self.module_id) + path = f'problems/{self.module_id}/answer_distribution/' return self.client.get(path, data_format=data_format) @@ -38,7 +36,7 @@ def grade_distribution(self, data_format=data_formats.JSON): Arguments: data_format (str): Format in which to return data (default is JSON) """ - path = 'problems/{0}/grade_distribution/'.format(self.module_id) + path = f'problems/{self.module_id}/grade_distribution/' return self.client.get(path, data_format=data_format) @@ -49,7 +47,7 @@ def sequential_open_distribution(self, data_format=data_formats.JSON): Arguments: data_format (str): Format in which to return data (default is JSON) """ - path = 'problems/{0}/sequential_open_distribution/'.format(self.module_id) + path = f'problems/{self.module_id}/sequential_open_distribution/' return self.client.get(path, data_format=data_format) @@ -60,6 +58,6 @@ def video_timeline(self, data_format=data_formats.JSON): Arguments: data_format (str): Format in which to return data (default is JSON) """ - path = 'videos/{0}/timeline/'.format(self.module_id) + path = f'videos/{self.module_id}/timeline/' return self.client.get(path, data_format=data_format) diff --git a/analyticsclient/programs.py b/analyticsclient/programs.py index 935e5d6..8712457 100644 --- a/analyticsclient/programs.py +++ b/analyticsclient/programs.py @@ -25,6 +25,6 @@ def programs(self, program_ids=None, fields=None, exclude=None, data_format=data path = 'programs/' querystring = urlencode(query_params) if querystring: - path += '?{0}'.format(querystring) + path += f'?{querystring}' return self.client.get(path, data_format=data_format) diff --git a/analyticsclient/status.py b/analyticsclient/status.py index 3775dde..f05d323 100644 --- a/analyticsclient/status.py +++ b/analyticsclient/status.py @@ -1,5 +1,3 @@ - - from analyticsclient.base import BaseEndpoint from analyticsclient.exceptions import ClientError diff --git a/analyticsclient/tests/__init__.py b/analyticsclient/tests/__init__.py index e6646ee..0c87fc6 100644 --- a/analyticsclient/tests/__init__.py +++ b/analyticsclient/tests/__init__.py @@ -1,5 +1,3 @@ - - from unittest import TestCase import ddt @@ -26,7 +24,7 @@ def get_api_url(self, path): Returns: Complete API URL and path """ - return "{0}/{1}".format(self.client.base_url, path) + return f"{self.client.base_url}/{path}" @ddt.ddt @@ -40,8 +38,8 @@ class APIWithIDsTestCase: def setUp(self): """Set up the test case.""" - super(APIWithIDsTestCase, self).setUp() - self.base_uri = self.get_api_url('{}/'.format(self.endpoint)) + super().setUp() + self.base_uri = self.get_api_url(f'{self.endpoint}/') self.client_class = getattr(self.client, self.endpoint)() httpretty.enable() diff --git a/analyticsclient/tests/test_client.py b/analyticsclient/tests/test_client.py index 3acc554..eeb3a90 100644 --- a/analyticsclient/tests/test_client.py +++ b/analyticsclient/tests/test_client.py @@ -1,9 +1,7 @@ - - import json +from unittest import mock import httpretty -import mock import requests.exceptions from testfixtures import log_capture @@ -15,7 +13,7 @@ class ClientTests(ClientTestCase): def setUp(self): - super(ClientTests, self).setUp() + super().setUp() httpretty.enable() self.test_endpoint = 'test' self.test_url = self.get_api_url(self.test_endpoint) @@ -85,7 +83,7 @@ def test_request_timeout(self, mock_get, lc): self.test_endpoint, timeout=timeout ) - msg = 'Response from {0} exceeded timeout of {1}s.'.format(self.test_endpoint, self.client.timeout) + msg = f'Response from {self.test_endpoint} exceeded timeout of {self.client.timeout}s.' lc.check(('analyticsclient.client', 'ERROR', msg)) lc.clear() mock_get.assert_called_once_with(url, headers=headers, timeout=self.client.timeout, params={}) @@ -100,7 +98,7 @@ def test_request_timeout(self, mock_get, lc): timeout=timeout ) mock_get.assert_called_once_with(url, headers=headers, timeout=timeout, params={}) - msg = 'Response from {0} exceeded timeout of {1}s.'.format(self.test_endpoint, timeout) + msg = f'Response from {self.test_endpoint} exceeded timeout of {timeout}s.' lc.check(('analyticsclient.client', 'ERROR', msg)) def test_request_format(self): diff --git a/analyticsclient/tests/test_course.py b/analyticsclient/tests/test_course.py index 30ec9b5..f2f3a94 100644 --- a/analyticsclient/tests/test_course.py +++ b/analyticsclient/tests/test_course.py @@ -1,5 +1,3 @@ - - import json import re @@ -13,19 +11,19 @@ class CoursesTests(ClientTestCase): def setUp(self): - super(CoursesTests, self).setUp() + super().setUp() self.course_id = 'edX/DemoX/Demo_Course' self.course = self.client.courses(self.course_id) httpretty.enable() def tearDown(self): - super(CoursesTests, self).tearDown() + super().tearDown() httpretty.disable() def assertCorrectEnrollmentUrl(self, course, demographic=None): """ Verifies that the enrollment URL is correct. """ - uri = self.get_api_url('courses/{0}/enrollment/'.format(course.course_id)) + uri = self.get_api_url(f'courses/{course.course_id}/enrollment/') if demographic: uri += '%s/' % demographic @@ -34,11 +32,11 @@ def assertCorrectEnrollmentUrl(self, course, demographic=None): date = '2014-01-01' httpretty.reset() - httpretty.register_uri(httpretty.GET, '{0}?start_date={1}'.format(uri, date), body='{}') + httpretty.register_uri(httpretty.GET, f'{uri}?start_date={date}', body='{}') course.enrollment(demographic, start_date=date) httpretty.reset() - httpretty.register_uri(httpretty.GET, '{0}?end_date={1}'.format(uri, date), body='{}') + httpretty.register_uri(httpretty.GET, f'{uri}?end_date={date}', body='{}') course.enrollment(demographic, end_date=date) httpretty.reset() @@ -48,7 +46,7 @@ def assertCorrectEnrollmentUrl(self, course, demographic=None): def assertCorrectActivityUrl(self, course, activity_type=None): """ Verifies that the activity URL is correct. """ - uri = self.get_api_url('courses/{0}/activity/'.format(course.course_id)) + uri = self.get_api_url(f'courses/{course.course_id}/activity/') if activity_type: uri += '?activity_type=%s' % activity_type @@ -57,11 +55,11 @@ def assertCorrectActivityUrl(self, course, activity_type=None): date = '2014-01-01' httpretty.reset() - httpretty.register_uri(httpretty.GET, '{0}&start_date={1}'.format(uri, date), body='{}') + httpretty.register_uri(httpretty.GET, f'{uri}&start_date={date}', body='{}') course.activity(activity_type, start_date=date) httpretty.reset() - httpretty.register_uri(httpretty.GET, '{0}&end_date={1}'.format(uri, date), body='{}') + httpretty.register_uri(httpretty.GET, f'{uri}&end_date={date}', body='{}') course.activity(activity_type, end_date=date) httpretty.reset() @@ -71,14 +69,14 @@ def assertCorrectActivityUrl(self, course, activity_type=None): @httpretty.activate def assertRecentActivityResponseData(self, course, activity_type): body = { - u'course_id': str(course.course_id), - u'interval_start': u'2014-05-24T00:00:00Z', - u'interval_end': u'2014-06-01T00:00:00Z', - u'activity_type': str(activity_type), - u'count': 200, + 'course_id': str(course.course_id), + 'interval_start': '2014-05-24T00:00:00Z', + 'interval_end': '2014-06-01T00:00:00Z', + 'activity_type': str(activity_type), + 'count': 200, } - uri = self.get_api_url('courses/{0}/recent_activity/?activity_type={1}'.format(self.course_id, activity_type)) + uri = self.get_api_url(f'courses/{self.course_id}/recent_activity/?activity_type={activity_type}') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertDictEqual(body, self.course.recent_activity(activity_type)) @@ -92,7 +90,7 @@ def test_not_found(self): """ Course calls should raise a NotFoundError when provided with an invalid course. """ course_id = 'not-a-course-id' - uri = self.get_api_url('courses/{0}/'.format(course_id)) + uri = self.get_api_url(f'courses/{course_id}/') uri = re.compile(r'^' + re.escape(uri) + r'.*$') httpretty.register_uri(httpretty.GET, uri, status=404) @@ -103,7 +101,7 @@ def test_not_found(self): def test_invalid_parameter(self): """ Course calls should raise a InvalidRequestError when parameters are invalid. """ - uri = self.get_api_url('courses/{0}/'.format(self.course_id)) + uri = self.get_api_url(f'courses/{self.course_id}/') uri = re.compile(r'^' + re.escape(uri) + r'.*$') httpretty.register_uri(httpretty.GET, uri, status=400) @@ -125,7 +123,7 @@ def test_activity(self): self.assertCorrectActivityUrl(self.course, activity_types.POSTED_FORUM) def test_enrollment_data_format(self): - uri = self.get_api_url('courses/{0}/enrollment/'.format(self.course.course_id)) + uri = self.get_api_url(f'courses/{self.course.course_id}/enrollment/') httpretty.register_uri(httpretty.GET, uri, body='{}') @@ -145,7 +143,7 @@ def test_problems(self): } ] - uri = self.get_api_url('courses/{0}/problems/'.format(self.course_id)) + uri = self.get_api_url(f'courses/{self.course_id}/problems/') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertEqual(body, self.course.problems()) @@ -158,7 +156,7 @@ def test_problems_and_tags(self): } ] - uri = self.get_api_url('courses/{0}/problems_and_tags/'.format(self.course_id)) + uri = self.get_api_url(f'courses/{self.course_id}/problems_and_tags/') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertEqual(body, self.course.problems_and_tags()) @@ -174,7 +172,7 @@ def test_reports(self): "report_name": "problem_response" } - uri = self.get_api_url('courses/{0}/reports/problem_response/'.format(self.course_id)) + uri = self.get_api_url(f'courses/{self.course_id}/reports/problem_response/') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertEqual(body, self.course.reports("problem_response")) @@ -193,6 +191,6 @@ def test_videos(self): } ] - uri = self.get_api_url('courses/{0}/videos/'.format(self.course_id)) + uri = self.get_api_url(f'courses/{self.course_id}/videos/') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertEqual(body, self.course.videos()) diff --git a/analyticsclient/tests/test_course_summaries.py b/analyticsclient/tests/test_course_summaries.py index 5e6d836..559ec27 100644 --- a/analyticsclient/tests/test_course_summaries.py +++ b/analyticsclient/tests/test_course_summaries.py @@ -1,5 +1,3 @@ - - import datetime import ddt diff --git a/analyticsclient/tests/test_course_totals.py b/analyticsclient/tests/test_course_totals.py index e4e977e..4ab68c8 100644 --- a/analyticsclient/tests/test_course_totals.py +++ b/analyticsclient/tests/test_course_totals.py @@ -1,5 +1,3 @@ - - from analyticsclient.tests import APIWithPostableIDsTestCase, ClientTestCase diff --git a/analyticsclient/tests/test_engagement_timeline.py b/analyticsclient/tests/test_engagement_timeline.py index 90f8cf8..a8e0a20 100644 --- a/analyticsclient/tests/test_engagement_timeline.py +++ b/analyticsclient/tests/test_engagement_timeline.py @@ -1,5 +1,3 @@ - - import json import httpretty @@ -11,14 +9,14 @@ class EngagementTimelineTests(ClientTestCase): def setUp(self): - super(EngagementTimelineTests, self).setUp() + super().setUp() self.username = 'edx' self.course_id = 'edX/DemoX/Demo_Course' self.engagement_timeline = self.client.engagement_timeline(self.username, self.course_id) httpretty.enable() def tearDown(self): - super(EngagementTimelineTests, self).tearDown() + super().tearDown() httpretty.disable() def test_not_found(self): diff --git a/analyticsclient/tests/test_helpers.py b/analyticsclient/tests/test_helpers.py index 0a5177f..ea32f0e 100644 --- a/analyticsclient/tests/test_helpers.py +++ b/analyticsclient/tests/test_helpers.py @@ -1,5 +1,3 @@ - - from unittest import TestCase from analyticsclient.constants import (activity_types, demographics, diff --git a/analyticsclient/tests/test_module.py b/analyticsclient/tests/test_module.py index aebb6f9..d738822 100644 --- a/analyticsclient/tests/test_module.py +++ b/analyticsclient/tests/test_module.py @@ -1,5 +1,3 @@ - - import json import httpretty @@ -9,7 +7,7 @@ class ModulesTests(ClientTestCase): def setUp(self): - super(ModulesTests, self).setUp() + super().setUp() httpretty.enable() self.course_id = 'edX/TestX/TestCourse' @@ -18,26 +16,26 @@ def setUp(self): self.module = self.client.modules(self.course_id, self.module_id) def tearDown(self): - super(ModulesTests, self).tearDown() + super().tearDown() httpretty.disable() def test_open_distribution_url(self): """ Verifies that the sequential open URL is correct. """ - uri = self.get_api_url('problems/{0}/sequential_open_distribution/'.format(self.module_id)) + uri = self.get_api_url(f'problems/{self.module_id}/sequential_open_distribution/') httpretty.register_uri(httpretty.GET, uri, body='{}') self.module.sequential_open_distribution() def test_answer_distribution_url(self): """ Verifies that the answer distribution URL is correct. """ - uri = self.get_api_url('problems/{0}/answer_distribution/'.format(self.module_id)) + uri = self.get_api_url(f'problems/{self.module_id}/answer_distribution/') httpretty.register_uri(httpretty.GET, uri, body='{}') self.module.answer_distribution() def test_grade_distribution_url(self): """ Verifies that the grade distribution URL is correct. """ - uri = self.get_api_url('problems/{0}/grade_distribution/'.format(self.module_id)) + uri = self.get_api_url(f'problems/{self.module_id}/grade_distribution/') httpretty.register_uri(httpretty.GET, uri, body='{}') self.module.grade_distribution() @@ -54,7 +52,7 @@ def test_open_distribution_response(self): } ] - uri = self.get_api_url('problems/{0}/sequential_open_distribution/'.format(self.module_id)) + uri = self.get_api_url(f'problems/{self.module_id}/sequential_open_distribution/') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertEqual(body, self.module.sequential_open_distribution()) @@ -77,7 +75,7 @@ def test_answer_distribution_response(self): } ] - uri = self.get_api_url('problems/{0}/answer_distribution/'.format(self.module_id)) + uri = self.get_api_url(f'problems/{self.module_id}/answer_distribution/') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertEqual(body, self.module.answer_distribution()) @@ -95,7 +93,7 @@ def test_grade_distribution_response(self): } ] - uri = self.get_api_url('problems/{0}/grade_distribution/'.format(self.module_id)) + uri = self.get_api_url(f'problems/{self.module_id}/grade_distribution/') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertEqual(body, self.module.grade_distribution()) @@ -111,6 +109,6 @@ def test_video_timeline_response(self): } ] - uri = self.get_api_url('videos/{0}/timeline/'.format(self.module_id)) + uri = self.get_api_url(f'videos/{self.module_id}/timeline/') httpretty.register_uri(httpretty.GET, uri, body=json.dumps(body)) self.assertEqual(body, self.module.video_timeline()) diff --git a/analyticsclient/tests/test_programs.py b/analyticsclient/tests/test_programs.py index 6c275a2..4c2e666 100644 --- a/analyticsclient/tests/test_programs.py +++ b/analyticsclient/tests/test_programs.py @@ -1,5 +1,3 @@ - - import ddt from analyticsclient.tests import APIListTestCase, ClientTestCase diff --git a/analyticsclient/tests/test_status.py b/analyticsclient/tests/test_status.py index 03d3674..e45e4ca 100644 --- a/analyticsclient/tests/test_status.py +++ b/analyticsclient/tests/test_status.py @@ -1,5 +1,3 @@ - - import json import httpretty diff --git a/requirements/base.txt b/requirements/base.txt index b1411cc..91d1f51 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -4,8 +4,13 @@ # # make upgrade # -certifi==2020.4.5.1 # via requests -chardet==3.0.4 # via requests -idna==2.9 # via requests -requests==2.23.0 # via -r requirements/base.in -urllib3==1.25.9 # via requests +certifi==2020.12.5 + # via requests +chardet==4.0.0 + # via requests +idna==2.10 + # via requests +requests==2.25.1 + # via -r requirements/base.in +urllib3==1.26.2 + # via requests diff --git a/requirements/test.in b/requirements/test.in index 90e0310..77c73a9 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -10,5 +10,4 @@ pylint pydocstyle pytest pytest-cov -mock testfixtures diff --git a/requirements/test.txt b/requirements/test.txt index 5e1547d..2e195c9 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -2,39 +2,79 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile --output-file=requirements/test.txt requirements/test.in +# make upgrade # -astroid==2.4.1 # via pylint -attrs==19.3.0 # via pytest -certifi==2020.4.5.1 # via -r requirements/base.txt, requests -chardet==3.0.4 # via -r requirements/base.txt, requests -coverage==5.1 # via -r requirements/test.in, pytest-cov -ddt==1.3.1 # via -r requirements/test.in -httpretty==0.9.7 # via -c requirements/constraints.txt, -r requirements/test.in -idna==2.9 # via -r requirements/base.txt, requests -importlib-metadata==1.6.0 # via pluggy, pytest -isort==4.3.21 # via pylint -lazy-object-proxy==1.4.3 # via astroid -mccabe==0.6.1 # via pylint -mock==3.0.5 # via -r requirements/test.in -more-itertools==8.2.0 # via pytest -packaging==20.3 # via pytest -pathlib2==2.3.5 # via pytest -pluggy==0.13.1 # via pytest -py==1.8.1 # via pytest -pycodestyle==2.5.0 # via -r requirements/test.in -pydocstyle==5.0.2 # via -r requirements/test.in -pylint==2.5.2 # via -r requirements/test.in -pyparsing==2.4.7 # via packaging -pytest-cov==2.8.1 # via -r requirements/test.in -pytest==5.4.1 # via -r requirements/test.in, pytest-cov -requests==2.23.0 # via -r requirements/base.txt -six==1.14.0 # via astroid, httpretty, mock, packaging, pathlib2 -snowballstemmer==2.0.0 # via pydocstyle -testfixtures==6.14.1 # via -r requirements/test.in -toml==0.10.0 # via pylint -typed-ast==1.4.1 # via astroid -urllib3==1.25.9 # via -r requirements/base.txt, requests -wcwidth==0.1.9 # via pytest -wrapt==1.12.1 # via astroid -zipp==1.2.0 # via importlib-metadata +astroid==2.4.2 + # via pylint +attrs==20.3.0 + # via pytest +certifi==2020.12.5 + # via + # -r requirements/base.txt + # requests +chardet==4.0.0 + # via + # -r requirements/base.txt + # requests +coverage==5.3.1 + # via + # -r requirements/test.in + # pytest-cov +ddt==1.4.1 + # via -r requirements/test.in +httpretty==0.9.7 + # via + # -c requirements/constraints.txt + # -r requirements/test.in +idna==2.10 + # via + # -r requirements/base.txt + # requests +iniconfig==1.1.1 + # via pytest +isort==5.7.0 + # via pylint +lazy-object-proxy==1.4.3 + # via astroid +mccabe==0.6.1 + # via pylint +packaging==20.8 + # via pytest +pluggy==0.13.1 + # via pytest +py==1.10.0 + # via pytest +pycodestyle==2.6.0 + # via -r requirements/test.in +pydocstyle==5.1.1 + # via -r requirements/test.in +pylint==2.6.0 + # via -r requirements/test.in +pyparsing==2.4.7 + # via packaging +pytest-cov==2.10.1 + # via -r requirements/test.in +pytest==6.2.1 + # via + # -r requirements/test.in + # pytest-cov +requests==2.25.1 + # via -r requirements/base.txt +six==1.15.0 + # via + # astroid + # httpretty +snowballstemmer==2.0.0 + # via pydocstyle +testfixtures==6.17.1 + # via -r requirements/test.in +toml==0.10.2 + # via + # pylint + # pytest +urllib3==1.26.2 + # via + # -r requirements/base.txt + # requests +wrapt==1.12.1 + # via astroid diff --git a/requirements/tox.txt b/requirements/tox.txt index 2cf001a..24127be 100644 --- a/requirements/tox.txt +++ b/requirements/tox.txt @@ -4,18 +4,33 @@ # # make upgrade # -appdirs==1.4.3 # via virtualenv -distlib==0.3.0 # via virtualenv -filelock==3.0.12 # via tox, virtualenv -importlib-metadata==1.6.0 # via importlib-resources, pluggy, tox, virtualenv -importlib-resources==1.5.0 # via virtualenv -packaging==20.3 # via tox -pluggy==0.13.1 # via tox -py==1.8.1 # via tox -pyparsing==2.4.7 # via packaging -six==1.14.0 # via packaging, tox, virtualenv -toml==0.10.0 # via tox -tox-travis==0.12 # via -r requirements/tox.in -tox==3.15.0 # via -r requirements/tox.in, tox-travis -virtualenv==20.0.20 # via tox -zipp==1.2.0 # via importlib-metadata, importlib-resources +appdirs==1.4.4 + # via virtualenv +distlib==0.3.1 + # via virtualenv +filelock==3.0.12 + # via + # tox + # virtualenv +packaging==20.8 + # via tox +pluggy==0.13.1 + # via tox +py==1.10.0 + # via tox +pyparsing==2.4.7 + # via packaging +six==1.15.0 + # via + # tox + # virtualenv +toml==0.10.2 + # via tox +tox-travis==0.12 + # via -r requirements/tox.in +tox==3.21.1 + # via + # -r requirements/tox.in + # tox-travis +virtualenv==20.3.1 + # via tox diff --git a/setup.py b/setup.py index b17227a..c272fa2 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,3 @@ - - import codecs import os import sys @@ -56,7 +54,6 @@ def is_requirement(line): 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.8', ], install_requires=load_requirements('requirements/base.in'), diff --git a/tox.ini b/tox.ini index 90a8a31..d42390b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{35,38}, quality +envlist = py38,quality [testenv] deps = -r{toxinidir}/requirements/test.txt @@ -9,3 +9,4 @@ whitelist_externals = make [testenv:quality] deps = -r{toxinidir}/requirements/test.txt commands = make quality +