diff --git a/.travis.yml b/.travis.yml index 83a80fd5..324a68a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,10 @@ language: python python: - 2.7 + - 3.3 + - 3.4 + - 3.5 + - 3.6 install: - pip install -r tests_requirements.txt - pip install coveralls diff --git a/CHANGELOG.md b/CHANGELOG.md index 96265f78..ba3a2218 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## [0.5.0] - 2017-07-10 +### New Endpoint Coverage + +- Files (Get file from Canvas, Course, Group, or User) + +### General + +- Added support for Python 3.3, 3.4, 3.5, and 3.6 while maintaining 2.7 compatibility. + +### Bugfixes + +- Fixed an issue where non-ASCII characters in CanvasObject data would throw UnicodeEncodeError exceptions. + ## [0.4.0] - 2017-06-16 ### New Endpoint Coverage diff --git a/DEPLOY.md b/DEPLOY.md index 2126063a..e4e197be 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -1,12 +1,13 @@ -canvasapi Deploy Procedures -========================== +CanvasAPI Deploy Procedures +=========================== Pre-Flight Checklist -------------------- -- On branch `master` and up-to-date +- On branch `develop` and up-to-date - All tests pass - 100% coverage +- No linter errors - `CHANGELOG` is accurate Packaging @@ -16,20 +17,28 @@ Update version number in `__init__.py`. Run `python setup.py sdist`. This should create a file in the `dist` directory called something like `canvasapi-0.0.0.tar.gz`. + Generate Documentation ---------------------- In the `docs` directory, run `make clean html`. -**TODO:** how to publish documentation. +This will create all the documentation files in the `_build/html` directory + Deploy ------ Commit the new files and the changes to `__init__.py` and push. -Create a merge request from `master` to `stable`, and merge. +Create a merge request from `develop` to `master`, and merge. Tag the merge commit with the version number: `git tag -s v0.0.0 -m "Release version 0.0.0" abc1234` Push the tag: `git push origin v0.0.0` + +Run `twine upload dist/canvasapi-0.0.0.tar.gz` to upload to PyPI. + +Compress all files in the `docs/_build/html` directory into a `.zip` file. Upload this file to PyPI to update the [documentation](https://pythonhosted.org/canvasapi/). + +Create release on GitHub for the new tag. Use the text from the changelog for content. \ No newline at end of file diff --git a/canvasapi/__init__.py b/canvasapi/__init__.py index 8ec3a648..fd2bebb1 100644 --- a/canvasapi/__init__.py +++ b/canvasapi/__init__.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- -from canvas import Canvas +from __future__ import unicode_literals + +from canvasapi.canvas import Canvas __all__ = ["Canvas"] -__version__ = '0.4.0' +__version__ = '0.5.0' diff --git a/canvasapi/account.py b/canvasapi/account.py index 30fb7bdb..019a943c 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1,3 +1,7 @@ +from __future__ import unicode_literals + +from builtins import str + from canvasapi.canvas_object import CanvasObject from canvasapi.exceptions import RequiredFieldMissing from canvasapi.paginated_list import PaginatedList @@ -495,7 +499,7 @@ def list_groups(self, **kwargs): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group` """ - from group import Group + from canvasapi.group import Group return PaginatedList( Group, self._requester, @@ -515,7 +519,7 @@ def create_group_category(self, name, **kwargs): :type name: str :rtype: :class:`canvasapi.group.GroupCategory` """ - from group import GroupCategory + from canvasapi.group import GroupCategory response = self._requester.request( 'POST', @@ -535,7 +539,7 @@ def list_group_categories(self): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.GroupCategory` """ - from group import GroupCategory + from canvasapi.group import GroupCategory return PaginatedList( GroupCategory, @@ -605,7 +609,7 @@ def list_enrollment_terms(self, **kwargs): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.enrollment_term.EnrollmentTerm` """ - from enrollment_term import EnrollmentTerm + from canvasapi.enrollment_term import EnrollmentTerm return PaginatedList( EnrollmentTerm, @@ -626,7 +630,7 @@ def list_user_logins(self, **kwargs): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.login.Login` """ - from login import Login + from canvasapi.login import Login return PaginatedList( Login, @@ -649,7 +653,7 @@ def create_user_login(self, user, login, **kwargs): :type login: `dict` :rtype: :class:`canvasapi.login.Login` """ - from login import Login + from canvasapi.login import Login if isinstance(user, dict) and 'id' in user: kwargs['user'] = user diff --git a/canvasapi/appointment_group.py b/canvasapi/appointment_group.py index 25b14303..84ef2236 100644 --- a/canvasapi/appointment_group.py +++ b/canvasapi/appointment_group.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.exceptions import RequiredFieldMissing from canvasapi.util import combine_kwargs diff --git a/canvasapi/assignment.py b/canvasapi/assignment.py index 7456098d..79152a32 100644 --- a/canvasapi/assignment.py +++ b/canvasapi/assignment.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs diff --git a/canvasapi/authentication_provider.py b/canvasapi/authentication_provider.py index 3615d895..dec3b33c 100644 --- a/canvasapi/authentication_provider.py +++ b/canvasapi/authentication_provider.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs diff --git a/canvasapi/avatar.py b/canvasapi/avatar.py index 66a830a2..315f42b8 100644 --- a/canvasapi/avatar.py +++ b/canvasapi/avatar.py @@ -1,3 +1,7 @@ +from __future__ import unicode_literals + +from builtins import str + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/bookmark.py b/canvasapi/bookmark.py index 84b43ace..98b481fe 100644 --- a/canvasapi/bookmark.py +++ b/canvasapi/bookmark.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs diff --git a/canvasapi/calendar_event.py b/canvasapi/calendar_event.py index ba49ea97..1b811c5f 100644 --- a/canvasapi/calendar_event.py +++ b/canvasapi/calendar_event.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py index a4c06170..c7d2778f 100644 --- a/canvasapi/canvas.py +++ b/canvasapi/canvas.py @@ -1,6 +1,11 @@ +from __future__ import unicode_literals + +from builtins import object + from canvasapi.account import Account from canvasapi.course import Course from canvasapi.exceptions import RequiredFieldMissing +from canvasapi.file import File from canvasapi.folder import Folder from canvasapi.group import Group, GroupCategory from canvasapi.paginated_list import PaginatedList @@ -757,9 +762,27 @@ def list_group_participants(self, appointment_group_id, **kwargs): **combine_kwargs(**kwargs) ) + def get_file(self, file_id, **kwargs): + """ + Return the standard attachment json object for a file. + + :calls: `GET /api/v1/files/:id \ + `_ + + :param file_id: The ID of the file to retrieve. + :type file_id: int + :rtype: :class:`canvasapi.file.File` + """ + response = self.__requester.request( + 'GET', + 'files/{}'.format(file_id), + **combine_kwargs(**kwargs) + ) + return File(self.__requester, response.json()) + def get_folder(self, folder_id): """ - Returns the details for a folder + Return the details for a folder :calls: `GET /api/v1/folders/:id \ `_ diff --git a/canvasapi/canvas_object.py b/canvasapi/canvas_object.py index 06a156c0..46b56d9a 100644 --- a/canvasapi/canvas_object.py +++ b/canvasapi/canvas_object.py @@ -1,7 +1,10 @@ +from __future__ import unicode_literals from datetime import datetime import json import re +from builtins import str, object + DATE_PATTERN = re.compile('[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z') @@ -25,7 +28,7 @@ def __init__(self, requester, attributes): def __repr__(self): # pragma: no cover classname = self.__class__.__name__ - attrs = ', '.join(['%s=%s' % (attr, val) for attr, val in self.__dict__.iteritems() if attr != 'attributes']) # noqa + attrs = ', '.join(['%s=%s' % (attr, val) for attr, val in self.__dict__.items() if attr != 'attributes']) # noqa return '%s(%s)' % (classname, attrs) def to_json(self): @@ -61,14 +64,10 @@ def set_attributes(self, attributes): """ self.attributes = attributes - for attribute, value in attributes.iteritems(): + for attribute, value in attributes.items(): self.__setattr__(attribute, value) - try: - # datetime field - if DATE_PATTERN.match(str(value)): - date = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') - self.__setattr__(attribute + '_date', date) - # Non-unicode character. We can skip over this attribute. - except UnicodeEncodeError: - continue + # datetime field + if DATE_PATTERN.match(str(value)): + date = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') + self.__setattr__(attribute + '_date', date) diff --git a/canvasapi/communication_channel.py b/canvasapi/communication_channel.py index cd8984a7..2e4ccd5a 100644 --- a/canvasapi/communication_channel.py +++ b/canvasapi/communication_channel.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.notification_preference import NotificationPreference diff --git a/canvasapi/conversation.py b/canvasapi/conversation.py index 767f7405..52c433b3 100644 --- a/canvasapi/conversation.py +++ b/canvasapi/conversation.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs diff --git a/canvasapi/course.py b/canvasapi/course.py index 15e26c45..296c8d79 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.discussion_topic import DiscussionTopic from canvasapi.exceptions import RequiredFieldMissing @@ -694,7 +696,7 @@ def list_groups(self, **kwargs): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.course.Course` """ - from group import Group + from canvasapi.group import Group return PaginatedList( Group, self._requester, @@ -765,6 +767,25 @@ def get_discussion_topic(self, topic_id): return DiscussionTopic(self._requester, response_json) + def get_file(self, file_id, **kwargs): + """ + Return the standard attachment json object for a file. + + :calls: `GET /api/v1/courses/:course_id/files/:id \ + `_ + + :param file_id: The ID of the file to retrieve. + :type file_id: int + :rtype: :class:`canvasapi.file.File` + """ + from canvasapi.file import File + response = self._requester.request( + 'GET', + 'courses/{}/files/{}'.format(self.id, file_id), + **combine_kwargs(**kwargs) + ) + return File(self._requester, response.json()) + def get_full_discussion_topic(self, topic_id): """ Return a cached structure of the discussion topic. diff --git a/canvasapi/discussion_topic.py b/canvasapi/discussion_topic.py index 1a657a2f..19fdf5ad 100644 --- a/canvasapi/discussion_topic.py +++ b/canvasapi/discussion_topic.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.paginated_list import PaginatedList from canvasapi.util import combine_kwargs diff --git a/canvasapi/enrollment.py b/canvasapi/enrollment.py index fde3ace1..7cda3fae 100644 --- a/canvasapi/enrollment.py +++ b/canvasapi/enrollment.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/enrollment_term.py b/canvasapi/enrollment_term.py index a7fa09ec..77107448 100644 --- a/canvasapi/enrollment_term.py +++ b/canvasapi/enrollment_term.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs diff --git a/canvasapi/exceptions.py b/canvasapi/exceptions.py index 7fa60a41..3887c75a 100644 --- a/canvasapi/exceptions.py +++ b/canvasapi/exceptions.py @@ -1,3 +1,8 @@ +from __future__ import unicode_literals + +from builtins import str + + class CanvasException(Exception): # pragma: no cover """ Base class for all errors returned by the Canvas API. diff --git a/canvasapi/external_feed.py b/canvasapi/external_feed.py index 8769ba01..e2307d7d 100644 --- a/canvasapi/external_feed.py +++ b/canvasapi/external_feed.py @@ -1,3 +1,7 @@ +from __future__ import unicode_literals + +from builtins import str + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/external_tool.py b/canvasapi/external_tool.py index e1e2700c..4c61bc14 100644 --- a/canvasapi/external_tool.py +++ b/canvasapi/external_tool.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.exceptions import CanvasException from canvasapi.util import combine_kwargs diff --git a/canvasapi/file.py b/canvasapi/file.py index 6b82c089..0a9d7cca 100644 --- a/canvasapi/file.py +++ b/canvasapi/file.py @@ -1,3 +1,7 @@ +from __future__ import unicode_literals + +from builtins import str + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/folder.py b/canvasapi/folder.py index a66adfba..0baea1f3 100644 --- a/canvasapi/folder.py +++ b/canvasapi/folder.py @@ -1,3 +1,7 @@ +from __future__ import unicode_literals + +from builtins import str + from canvasapi.canvas_object import CanvasObject from canvasapi.paginated_list import PaginatedList from canvasapi.util import combine_kwargs diff --git a/canvasapi/group.py b/canvasapi/group.py index 22efa73d..1d62f839 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.discussion_topic import DiscussionTopic from canvasapi.folder import Folder @@ -377,6 +379,25 @@ def get_discussion_topic(self, topic_id): return DiscussionTopic(self._requester, response_json) + def get_file(self, file_id, **kwargs): + """ + Return the standard attachment json object for a file. + + :calls: `GET /api/v1/groups/:group_id/files/:id \ + `_ + + :param file_id: The ID of the file to retrieve. + :type file_id: int + :rtype: :class:`canvasapi.file.File` + """ + from canvasapi.file import File + response = self._requester.request( + 'GET', + 'groups/{}/files/{}'.format(self.id, file_id), + **combine_kwargs(**kwargs) + ) + return File(self._requester, response.json()) + def get_full_discussion_topic(self, topic_id): """ Return a cached structure of the discussion topic. diff --git a/canvasapi/login.py b/canvasapi/login.py index 25cc22f8..955b4c50 100644 --- a/canvasapi/login.py +++ b/canvasapi/login.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs diff --git a/canvasapi/module.py b/canvasapi/module.py index b1be178e..60a289ca 100644 --- a/canvasapi/module.py +++ b/canvasapi/module.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.exceptions import RequiredFieldMissing from canvasapi.paginated_list import PaginatedList diff --git a/canvasapi/notification_preference.py b/canvasapi/notification_preference.py index 22c58f14..5983e7f6 100644 --- a/canvasapi/notification_preference.py +++ b/canvasapi/notification_preference.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/page.py b/canvasapi/page.py index 594b78a5..fd6c26f3 100644 --- a/canvasapi/page.py +++ b/canvasapi/page.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs from canvasapi.paginated_list import PaginatedList diff --git a/canvasapi/page_view.py b/canvasapi/page_view.py index 216b02a5..f5848135 100644 --- a/canvasapi/page_view.py +++ b/canvasapi/page_view.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/paginated_list.py b/canvasapi/paginated_list.py index 0e27fbcc..fec05e15 100644 --- a/canvasapi/paginated_list.py +++ b/canvasapi/paginated_list.py @@ -1,5 +1,8 @@ +from __future__ import unicode_literals import re +from builtins import object + class PaginatedList(object): """ @@ -25,7 +28,7 @@ def __init__( def __getitem__(self, index): assert isinstance(index, (int, slice)) - if isinstance(index, (int, long)): + if isinstance(index, int): self.__get_up_to_index(index) return self.__elements[index] else: @@ -81,7 +84,7 @@ def _get_next_page(self): return content - class _Slice: + class _Slice(object): def __init__(self, the_list, the_slice): self.__list = the_list self.__start = the_slice.start or 0 diff --git a/canvasapi/progress.py b/canvasapi/progress.py index edacadfa..962951aa 100644 --- a/canvasapi/progress.py +++ b/canvasapi/progress.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/quiz.py b/canvasapi/quiz.py index eef668bb..04f87449 100644 --- a/canvasapi/quiz.py +++ b/canvasapi/quiz.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.util import combine_kwargs diff --git a/canvasapi/requester.py b/canvasapi/requester.py index e12666ca..48ea06ce 100644 --- a/canvasapi/requester.py +++ b/canvasapi/requester.py @@ -1,3 +1,6 @@ +from __future__ import unicode_literals + +from builtins import object import requests from canvasapi.exceptions import ( diff --git a/canvasapi/section.py b/canvasapi/section.py index 60609d0b..19dd63b7 100644 --- a/canvasapi/section.py +++ b/canvasapi/section.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject from canvasapi.exceptions import RequiredFieldMissing from canvasapi.paginated_list import PaginatedList diff --git a/canvasapi/submission.py b/canvasapi/submission.py index 27473c17..391c38be 100644 --- a/canvasapi/submission.py +++ b/canvasapi/submission.py @@ -1,3 +1,7 @@ +from __future__ import unicode_literals + +from builtins import str + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/tab.py b/canvasapi/tab.py index c35eaf5d..9c22ac40 100644 --- a/canvasapi/tab.py +++ b/canvasapi/tab.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from canvasapi.canvas_object import CanvasObject diff --git a/canvasapi/upload.py b/canvasapi/upload.py index 793136a1..f9b0ae6b 100644 --- a/canvasapi/upload.py +++ b/canvasapi/upload.py @@ -1,5 +1,8 @@ +from __future__ import unicode_literals import os +from builtins import object, str + from canvasapi.util import combine_kwargs @@ -14,14 +17,15 @@ def __init__(self, requester, url, file, **kwargs): :type requester: :class:`canvasapi.requester.Requester` :param url: The URL to upload the file to. :type url: str - :param file: The file or path of the file to upload. + :param file: A file handler or path of the file to upload. :type file: file or str """ if isinstance(file, str): if not os.path.exists(file): raise IOError('File ' + file + ' does not exist.') - - file = open(file, 'rb') + self._using_filename = True + else: + self._using_filename = False self._requester = requester self.url = url @@ -29,27 +33,50 @@ def __init__(self, requester, url, file, **kwargs): self.kwargs = kwargs def start(self): + """ + Kick off uploading process. Handles open/closing file if a path + is passed. + + :calls: request_upload_token + :returns: True if the file uploaded successfully, False \ + otherwise, and the JSON response from the API. + :rtype: tuple + """ + if self._using_filename: + with open(self.file, 'rb') as file: + return self.request_upload_token(file) + else: + return self.request_upload_token(self.file) + + def request_upload_token(self, file): """ Request an upload token. + + :param file: A file handler pointing to the file to upload. + :returns: True if the file uploaded successfully, False otherwise, \ + and the JSON response from the API. + :rtype: tuple """ - self.kwargs['name'] = os.path.basename(self.file.name) - self.kwargs['size'] = os.fstat(self.file.fileno()).st_size + self.kwargs['name'] = os.path.basename(file.name) + self.kwargs['size'] = os.fstat(file.fileno()).st_size response = self._requester.request( 'POST', self.url, **combine_kwargs(**self.kwargs) ) - return self.upload(response) - def upload(self, response): + return self.upload(response, file) + + def upload(self, response, file): """ Upload the file. :param response: The response from the upload request. :type response: dict + :param file: A file handler pointing to the file to upload. :returns: True if the file uploaded successfully, False otherwise, \ - and the JSON response from the API. + and the JSON response from the API. :rtype: tuple """ response = response.json() @@ -60,7 +87,7 @@ def upload(self, response): raise ValueError('Bad API response. No upload_params.') kwargs = response.get('upload_params') - kwargs['file'] = self.file + kwargs['file'] = file response_json = self._requester.request( 'POST', diff --git a/canvasapi/user.py b/canvasapi/user.py index 97aaa918..9576c025 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -1,3 +1,7 @@ +from __future__ import unicode_literals + +from builtins import str + from canvasapi.bookmark import Bookmark from canvasapi.calendar_event import CalendarEvent from canvasapi.canvas_object import CanvasObject @@ -295,7 +299,7 @@ def list_groups(self, **kwargs): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group` """ - from group import Group + from canvasapi.group import Group return PaginatedList( Group, @@ -423,6 +427,25 @@ def list_files(self, **kwargs): **combine_kwargs(**kwargs) ) + def get_file(self, file_id, **kwargs): + """ + Return the standard attachment json object for a file. + + :calls: `GET /api/v1/users/:group_id/files/:id \ + `_ + + :param file_id: The ID of the file to retrieve. + :type file_id: int + :rtype: :class:`canvasapi.file.File` + """ + from canvasapi.file import File + response = self._requester.request( + 'GET', + 'users/{}/files/{}'.format(self.id, file_id), + **combine_kwargs(**kwargs) + ) + return File(self._requester, response.json()) + def get_folder(self, folder_id): """ Returns the details for a user's folder diff --git a/canvasapi/util.py b/canvasapi/util.py index b1e2cd3a..f9467c9e 100644 --- a/canvasapi/util.py +++ b/canvasapi/util.py @@ -1,3 +1,8 @@ +from __future__ import unicode_literals + +from builtins import str + + def combine_kwargs(**kwargs): """ Combines a list of keyword arguments into a single dictionary. @@ -8,7 +13,7 @@ def flatten_dict(prefix, key, value): new_prefix = prefix + '[' + str(key) + ']' if isinstance(value, dict): d = {} - for k, v in value.iteritems(): + for k, v in value.items(): d.update(flatten_dict(new_prefix, k, v)) return d else: @@ -17,10 +22,10 @@ def flatten_dict(prefix, key, value): combined_kwargs = {} # Loop through all kwargs. - for kw, arg in kwargs.iteritems(): + for kw, arg in kwargs.items(): if isinstance(arg, dict): # If the argument is a dictionary, flatten it. - for key, value in arg.iteritems(): + for key, value in arg.items(): combined_kwargs.update(flatten_dict(str(kw), key, value)) else: combined_kwargs.update({str(kw): arg}) diff --git a/docs/index.rst b/docs/index.rst index 7d5117bd..3672a945 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,7 +3,7 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to canvasapi's documentation! +Welcome to CanvasAPI's documentation! ====================================== Contents: diff --git a/requirements.txt b/requirements.txt index 78a61867..1536591e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ +future==0.16.0 requests==2.10.0 six==1.10.0 diff --git a/setup.py b/setup.py index dcc996ca..69629b91 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals import re from setuptools import setup @@ -22,7 +23,7 @@ license='MIT License', packages=['canvasapi'], include_package_data=True, - install_requires=['requests'], + install_requires=['requests', 'future'], zip_safe=False, classifiers=[ 'Development Status :: 3 - Alpha', @@ -32,6 +33,11 @@ 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Software Development :: Libraries', ], ) diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json index 93ff266b..4a879bc1 100644 --- a/tests/fixtures/course.json +++ b/tests/fixtures/course.json @@ -18,16 +18,16 @@ "status_code": 200 }, "create_folder": { - "method": "POST", - "endpoint": "courses/1/folders", - "data": { - "id": 2, - "name": "Test String", - "locked": false, - "hidden": false - }, - "status_code": 200 - }, + "method": "POST", + "endpoint": "courses/1/folders", + "data": { + "id": 2, + "name": "Test String", + "locked": false, + "hidden": false + }, + "status_code": 200 + }, "create_quiz": { "method": "POST", "endpoint": "courses/1/quizzes", @@ -213,17 +213,17 @@ "status_code": 200 }, "get_folder": { - "method": "GET", - "endpoint": "courses/1/folders/1", - "data": { - "id": 1, - "files_count": 10, - "folders_count": 2, - "name": "Folder 1", - "full_name": "Folder 1" - }, - "status_code": 200 - }, + "method": "GET", + "endpoint": "courses/1/folders/1", + "data": { + "id": 1, + "files_count": 10, + "folders_count": 2, + "name": "Folder 1", + "full_name": "Folder 1" + }, + "status_code": 200 + }, "get_quiz": { "method": "GET", "endpoint": "courses/1/quizzes/1", @@ -382,7 +382,7 @@ "url": "http://example.com/myblog2.rss" } ], - "status_code": 200 + "status_code": 200 }, "list_course_files": { "method": "GET", @@ -422,26 +422,26 @@ "status_code": 200 }, "list_folders": { - "method": "GET", - "endpoint": "courses/1/folders", - "data": [ - { - "id": 2, - "files_count": 0, - "folders_count": 0, - "name": "Folder 2", - "full_name": "course_files/Folder 2" - }, - { - "id": 3, - "files_count": 0, - "folders_count": 0, - "name": "Folder 3", - "full_name": "course_files/Folder 3" - } - ], - "status_code": 200 - }, + "method": "GET", + "endpoint": "courses/1/folders", + "data": [ + { + "id": 2, + "files_count": 0, + "folders_count": 0, + "name": "Folder 2", + "full_name": "course_files/Folder 2" + }, + { + "id": 3, + "files_count": 0, + "folders_count": 0, + "name": "Folder 3", + "full_name": "course_files/Folder 3" + } + ], + "status_code": 200 + }, "list_quizzes": { "method": "GET", "endpoint": "courses/1/quizzes", @@ -926,6 +926,16 @@ }, "status_code": 200 }, + "get_file": { + "method": "GET", + "endpoint": "courses/1/files/1", + "data": { + "id": 1, + "display_name": "Course_File.docx", + "size": 2048 + }, + "status_code": 200 + }, "get_full_discussion_topic": { "method": "GET", "endpoint": "courses/1/discussion_topics/1/view", @@ -1163,20 +1173,20 @@ "endpoint": "courses/1/tabs", "data": [ { - "id": "home", - "html_url": "/courses/1", - "position": 1, - "visibility": "public", - "label": "Home", - "type": "internal" - }, - { - "id": "pages", - "html_url": "/courses/1/wiki", - "position": 2, - "visibility": "public", - "label": "Pages", - "type": "internal" + "id": "home", + "html_url": "/courses/1", + "position": 1, + "visibility": "public", + "label": "Home", + "type": "internal" + }, + { + "id": "pages", + "html_url": "/courses/1/wiki", + "position": 2, + "visibility": "public", + "label": "Pages", + "type": "internal" } ], "status_code": 200 @@ -1267,11 +1277,11 @@ "endpoint": "courses/1/tabs/pages", "data": { "id": "pages", - "html_url": "/courses/1/wiki", - "position": 3, - "visibility": "public", - "label": "Pages", - "type": "internal" + "html_url": "/courses/1/wiki", + "position": 3, + "visibility": "public", + "label": "Pages", + "type": "internal" }, "status_code": 200 }, diff --git a/tests/fixtures/file.json b/tests/fixtures/file.json index 23a0b969..96921634 100644 --- a/tests/fixtures/file.json +++ b/tests/fixtures/file.json @@ -8,5 +8,15 @@ "size": 5512 }, "status_code": 200 + }, + "get_by_id": { + "method": "GET", + "endpoint": "files/1", + "data": { + "id": 1, + "display_name": "File.docx", + "size": 6144 + }, + "status_code": 200 } } diff --git a/tests/fixtures/group.json b/tests/fixtures/group.json index f8811927..0481a4c6 100644 --- a/tests/fixtures/group.json +++ b/tests/fixtures/group.json @@ -608,6 +608,16 @@ }, "status_code": 200 }, + "get_file": { + "method": "GET", + "endpoint": "groups/1/files/1", + "data": { + "id": 1, + "display_name": "Group_File.docx", + "size": 4096 + }, + "status_code": 200 + }, "get_full_discussion_topic": { "method": "GET", "endpoint": "groups/1/discussion_topics/1/view", diff --git a/tests/fixtures/user.json b/tests/fixtures/user.json index fabf08bb..99220a54 100644 --- a/tests/fixtures/user.json +++ b/tests/fixtures/user.json @@ -226,6 +226,16 @@ "name": "John Doe" } }, + "get_file": { + "method": "GET", + "endpoint": "users/1/files/1", + "data": { + "id": 1, + "display_name": "User_File.docx", + "size": 1024 + }, + "status_code": 200 + }, "get_folder": { "method": "GET", "endpoint": "users/1/folders/1", diff --git a/tests/settings.py b/tests/settings.py index 636763d8..71d0b142 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + BASE_URL = 'http://example.com/api/v1/' API_KEY = '123' diff --git a/tests/test_account.py b/tests/test_account.py index c296f4ea..58796bbe 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -1,6 +1,8 @@ +from __future__ import unicode_literals import datetime import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -21,7 +23,6 @@ @requests_mock.Mocker() class TestAccount(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_appointment_group.py b/tests/test_appointment_group.py index e8935c1f..81bf5c82 100644 --- a/tests/test_appointment_group.py +++ b/tests/test_appointment_group.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -12,7 +14,6 @@ @requests_mock.Mocker() class TestAppointmentGroup(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_assignment.py b/tests/test_assignment.py index 915d0275..f1158d65 100644 --- a/tests/test_assignment.py +++ b/tests/test_assignment.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestAssignment(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -49,7 +50,6 @@ def test__str__(self, m): @requests_mock.Mocker() class TestAssignmentGroup(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_authentication_providers.py b/tests/test_authentication_providers.py index 6e60a4a8..368f313a 100644 --- a/tests/test_authentication_providers.py +++ b/tests/test_authentication_providers.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestAuthenticationProvider(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_bookmark.py b/tests/test_bookmark.py index db54218b..e923dfa2 100644 --- a/tests/test_bookmark.py +++ b/tests/test_bookmark.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestBookmark(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_calendar_event.py b/tests/test_calendar_event.py index 9b8d0dbd..0d10c38c 100644 --- a/tests/test_calendar_event.py +++ b/tests/test_calendar_event.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestCalendarEvent(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_canvas.py b/tests/test_canvas.py index 0be45707..cb552a81 100644 --- a/tests/test_canvas.py +++ b/tests/test_canvas.py @@ -1,6 +1,8 @@ +from __future__ import unicode_literals import unittest from datetime import datetime +from builtins import str import requests_mock from canvasapi import Canvas @@ -10,6 +12,7 @@ from canvasapi.conversation import Conversation from canvasapi.course import Course, CourseNickname from canvasapi.exceptions import RequiredFieldMissing +from canvasapi.file import File from canvasapi.group import Group, GroupCategory from canvasapi.exceptions import ResourceDoesNotExist from canvasapi.progress import Progress @@ -22,7 +25,6 @@ @requests_mock.Mocker() class TestCanvas(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -86,7 +88,7 @@ def test_get_course_with_start_date(self, m): course = self.canvas.get_course(2) self.assertTrue(hasattr(course, 'start_at')) - self.assertIsInstance(course.start_at, (str, unicode)) + self.assertIsInstance(course.start_at, str) self.assertTrue(hasattr(course, 'start_at_date')) self.assertIsInstance(course.start_at_date, datetime) @@ -444,6 +446,15 @@ def test_list_group_participants(self, m): groups_list = [group for group in groups] self.assertEqual(len(groups_list), 2) + # get_file() + def test_get_file(self, m): + register_uris({'file': ['get_by_id']}, m) + + file = self.canvas.get_file(1) + self.assertIsInstance(file, File) + self.assertEqual(file.display_name, "File.docx") + self.assertEqual(file.size, 6144) + # search_recipients() def test_search_recipients(self, m): register_uris({'user': ['search_recipients']}, m) diff --git a/tests/test_canvas_object.py b/tests/test_canvas_object.py index e86f566d..8bb48754 100644 --- a/tests/test_canvas_object.py +++ b/tests/test_canvas_object.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals import unittest from canvasapi.canvas_object import CanvasObject diff --git a/tests/test_communication_channel.py b/tests/test_communication_channel.py index ca28283f..20f54259 100644 --- a/tests/test_communication_channel.py +++ b/tests/test_communication_channel.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestCommunicationChannel(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_conversation.py b/tests/test_conversation.py index 216c0847..a579e305 100644 --- a/tests/test_conversation.py +++ b/tests/test_conversation.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestConversation(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_course.py b/tests/test_course.py index cb9f46b4..cee7eb1a 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -1,7 +1,9 @@ +from __future__ import unicode_literals import unittest import uuid import os +from builtins import str import requests_mock from canvasapi import Canvas @@ -29,7 +31,6 @@ @requests_mock.Mocker() class TestCourse(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -135,7 +136,7 @@ def test_preview_html(self, m): html_str = "

hello

" prev_html = self.course.preview_html(html_str) - self.assertIsInstance(prev_html, (str, unicode)) + self.assertIsInstance(prev_html, str) self.assertEqual(prev_html, "

hello

") # get_settings() @@ -159,10 +160,9 @@ def test_update_settings(self, m): def test_upload(self, m): register_uris({'course': ['upload', 'upload_final']}, m) - filename = 'testfile_%s' % uuid.uuid4().hex - file = open(filename, 'w+') - - response = self.course.upload(file) + filename = 'testfile_course_%s' % uuid.uuid4().hex + with open(filename, 'w+') as file: + response = self.course.upload(file) self.assertTrue(response[0]) self.assertIsInstance(response[1], dict) @@ -450,7 +450,16 @@ def test_get_discussion_topic(self, m): discussion = self.course.get_discussion_topic(topic_id) self.assertIsInstance(discussion, DiscussionTopic) self.assertTrue(hasattr(discussion, 'course_id')) - self.assertEquals(discussion.course_id, 1) + self.assertEqual(discussion.course_id, 1) + + # get_file() + def test_get_file(self, m): + register_uris({'course': ['get_file']}, m) + + file = self.course.get_file(1) + self.assertIsInstance(file, File) + self.assertEqual(file.display_name, 'Course_File.docx') + self.assertEqual(file.size, 2048) # get_full_discussion_topic() def test_get_full_discussion_topic(self, m): @@ -461,7 +470,7 @@ def test_get_full_discussion_topic(self, m): self.assertIsInstance(discussion, DiscussionTopic) self.assertTrue(hasattr(discussion, 'view')) self.assertTrue(hasattr(discussion, 'participants')) - self.assertEquals(discussion.course_id, 1) + self.assertEqual(discussion.course_id, 1) # get_discussion_topics() def test_get_discussion_topics(self, m): @@ -471,7 +480,7 @@ def test_get_discussion_topics(self, m): discussion_list = [discussion for discussion in response] self.assertIsInstance(discussion_list[0], DiscussionTopic) self.assertTrue(hasattr(discussion_list[0], 'course_id')) - self.assertEquals(2, len(discussion_list)) + self.assertEqual(2, len(discussion_list)) # create_discussion_topic() def test_create_discussion_topic(self, m): @@ -481,8 +490,8 @@ def test_create_discussion_topic(self, m): discussion = self.course.create_discussion_topic() self.assertIsInstance(discussion, DiscussionTopic) self.assertTrue(hasattr(discussion, 'course_id')) - self.assertEquals(title, discussion.title) - self.assertEquals(discussion.course_id, 1) + self.assertEqual(title, discussion.title) + self.assertEqual(discussion.course_id, 1) # reorder_pinned_topics() def test_reorder_pinned_topics(self, m): @@ -782,7 +791,6 @@ def test_update_tab(self, m): @requests_mock.Mocker() class TestCourseNickname(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_discussion_topic.py b/tests/test_discussion_topic.py index e07b5daa..103831c3 100644 --- a/tests/test_discussion_topic.py +++ b/tests/test_discussion_topic.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -13,7 +15,6 @@ @requests_mock.Mocker() class TestDiscussionTopic(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -49,7 +50,7 @@ def test_update(self, m): discussion = self.discussion_topic.update() self.assertIsInstance(discussion, DiscussionTopic) self.assertTrue(hasattr(discussion, 'course_id')) - self.assertEquals(discussion.course_id, 1) + self.assertEqual(discussion.course_id, 1) # update_entry() def test_update_entry(self, m): @@ -81,8 +82,8 @@ def test_list_topic_entries(self, m): entries = self.discussion_topic.list_topic_entries() entry_list = [entry for entry in entries] self.assertIsInstance(entry_list[0], DiscussionTopic) - self.assertEquals(entry_list[0].id, 1) - self.assertEquals(entry_list[0].user_id, 1) + self.assertEqual(entry_list[0].id, 1) + self.assertEqual(entry_list[0].user_id, 1) # post_reply() def test_post_reply(self, m): @@ -91,7 +92,7 @@ def test_post_reply(self, m): message = "Message 1" reply = self.discussion_topic.post_reply(1) self.assertIsInstance(reply, DiscussionTopic) - self.assertEquals(reply.message, message) + self.assertEqual(reply.message, message) self.assertTrue(hasattr(reply, 'created_at')) self.assertTrue(hasattr(reply, 'message')) @@ -102,7 +103,7 @@ def test_list_entry_replies(self, m): replies = self.discussion_topic.list_entry_replies(1) reply_list = [reply for reply in replies] self.assertIsInstance(reply_list[0], DiscussionTopic) - self.assertEquals(reply_list[0].id, 1) + self.assertEqual(reply_list[0].id, 1) # list_entries() def test_list_entries(self, m): @@ -111,7 +112,7 @@ def test_list_entries(self, m): entries = self.discussion_topic.list_entries() entry_list = [entry for entry in entries] self.assertIsInstance(entry_list[0], DiscussionTopic) - self.assertEquals(entry_list[0].id, 1) + self.assertEqual(entry_list[0].id, 1) # mark_as_read() def test_mark_as_read(self, m): @@ -231,10 +232,10 @@ def test_unsubscribe_status(self, m): # parent_id def test_parent_id_course(self, m): - self.assertEquals(self.discussion_topic.parent_id, 1) + self.assertEqual(self.discussion_topic.parent_id, 1) def test_parent_id_group(self, m): - self.assertEquals(self.discussion_topic_group.parent_id, 1) + self.assertEqual(self.discussion_topic_group.parent_id, 1) def test_parent_id_no_id(self, m): discussion = DiscussionTopic(self.canvas._Canvas__requester, {'id': 1}) @@ -243,10 +244,10 @@ def test_parent_id_no_id(self, m): # parent_type def test_parent_type_course(self, m): - self.assertEquals(self.discussion_topic.parent_type, 'course') + self.assertEqual(self.discussion_topic.parent_type, 'course') def test_parent_type_group(self, m): - self.assertEquals(self.discussion_topic_group.parent_type, 'group') + self.assertEqual(self.discussion_topic_group.parent_type, 'group') def test_parent_type_no_id(self, m): discussion = DiscussionTopic(self.canvas._Canvas__requester, {'id': 1}) diff --git a/tests/test_enrollment.py b/tests/test_enrollment.py index c9f943ea..3fb5f2b1 100644 --- a/tests/test_enrollment.py +++ b/tests/test_enrollment.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi.canvas import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestEnrollment(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_enrollment_term.py b/tests/test_enrollment_term.py index 88ce42b1..1043fe19 100644 --- a/tests/test_enrollment_term.py +++ b/tests/test_enrollment_term.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestEnrollmentTerm(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_external_feed.py b/tests/test_external_feed.py index e75cfa50..bd68fda1 100644 --- a/tests/test_external_feed.py +++ b/tests/test_external_feed.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -10,7 +12,6 @@ @requests_mock.Mocker() class TestExternalFeed(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_external_tool.py b/tests/test_external_tool.py index 534c4691..439286ea 100644 --- a/tests/test_external_tool.py +++ b/tests/test_external_tool.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -14,7 +16,6 @@ @requests_mock.Mocker() class TestExternalTool(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -93,7 +94,7 @@ def test_get_sessionless_launch_url(self, m): requires = {'external_tool': ['get_sessionless_launch_url_course']} register_uris(requires, m) - self.assertIsInstance(self.ext_tool_course.get_sessionless_launch_url(), (str, unicode)) + self.assertIsInstance(self.ext_tool_course.get_sessionless_launch_url(), str) def test_get_sessionless_launch_url_no_url(self, m): requires = { diff --git a/tests/test_file.py b/tests/test_file.py index f9b406fc..f91c6f75 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestFile(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_folder.py b/tests/test_folder.py index 6eabd965..9bdc2e00 100644 --- a/tests/test_folder.py +++ b/tests/test_folder.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -12,7 +14,6 @@ @requests_mock.Mocker() class TestFolder(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_group.py b/tests/test_group.py index 265bc3ce..eb171d57 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -1,7 +1,9 @@ +from __future__ import unicode_literals import os import unittest import uuid +from builtins import str import requests_mock from canvasapi import Canvas @@ -20,7 +22,6 @@ @requests_mock.Mocker() class TestGroup(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -139,9 +140,9 @@ def test_remove_user(self, m): def test_upload(self, m): register_uris({'group': ['upload', 'upload_final']}, m) - filename = 'testfile_%s' % uuid.uuid4().hex - file = open(filename, 'w+') - response = self.group.upload(file) + filename = 'testfile_group_%s' % uuid.uuid4().hex + with open(filename, 'w+') as file: + response = self.group.upload(file) self.assertTrue(response[0]) self.assertIsInstance(response[1], dict) self.assertIn('url', response[1]) @@ -207,8 +208,17 @@ def test_get_discussion_topic(self, m): discussion = self.group.get_discussion_topic(group_id) self.assertIsInstance(discussion, DiscussionTopic) self.assertTrue(hasattr(discussion, 'group_id')) - self.assertEquals(group_id, discussion.id) - self.assertEquals(discussion.group_id, 1) + self.assertEqual(group_id, discussion.id) + self.assertEqual(discussion.group_id, 1) + + # get_file() + def test_get_file(self, m): + register_uris({'group': ['get_file']}, m) + + file = self.group.get_file(1) + self.assertIsInstance(file, File) + self.assertEqual(file.display_name, 'Group_File.docx') + self.assertEqual(file.size, 4096) # get_full_discussion_topic def test_get_full_discussion_topic(self, m): @@ -219,7 +229,7 @@ def test_get_full_discussion_topic(self, m): self.assertIsInstance(discussion, DiscussionTopic) self.assertTrue(hasattr(discussion, 'view')) self.assertTrue(hasattr(discussion, 'participants')) - self.assertEquals(discussion.group_id, 1) + self.assertEqual(discussion.group_id, 1) # get_discussion_topics() def test_get_discussion_topics(self, m): @@ -229,7 +239,7 @@ def test_get_discussion_topics(self, m): discussion_list = [discussion for discussion in response] self.assertIsInstance(discussion_list[0], DiscussionTopic) self.assertTrue(hasattr(discussion_list[0], 'group_id')) - self.assertEquals(2, len(discussion_list)) + self.assertEqual(2, len(discussion_list)) # create_discussion_topic() def test_create_discussion_topic(self, m): @@ -239,8 +249,8 @@ def test_create_discussion_topic(self, m): discussion = self.group.create_discussion_topic() self.assertTrue(hasattr(discussion, 'group_id')) self.assertIsInstance(discussion, DiscussionTopic) - self.assertEquals(discussion.title, title) - self.assertEquals(discussion.group_id, 1) + self.assertEqual(discussion.title, title) + self.assertEqual(discussion.group_id, 1) # reorder_pinned_topics() def test_reorder_pinned_topics(self, m): @@ -335,7 +345,6 @@ def test_list_tabs(self, m): @requests_mock.Mocker() class TestGroupMembership(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -379,7 +388,6 @@ def test_remove_self(self, m): @requests_mock.Mocker() class TestGroupCategory(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) with requests_mock.Mocker() as m: diff --git a/tests/test_login.py b/tests/test_login.py index 5e012234..98b09e63 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -11,7 +13,6 @@ @requests_mock.Mocker() class TestLogin(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_module.py b/tests/test_module.py index 9e144378..fbe246bf 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -1,18 +1,19 @@ +from __future__ import unicode_literals import unittest -import settings +from builtins import str import requests_mock from canvasapi import Canvas from canvasapi.exceptions import RequiredFieldMissing from canvasapi.module import Module, ModuleItem +from tests import settings from tests.util import register_uris @requests_mock.Mocker() class TestModule(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -112,7 +113,6 @@ def test__str__(self, m): @requests_mock.Mocker() class TestModuleItem(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_notification_preference.py b/tests/test_notification_preference.py index 1a2fdcbb..92de0692 100644 --- a/tests/test_notification_preference.py +++ b/tests/test_notification_preference.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -10,7 +12,6 @@ @requests_mock.Mocker() class TestNotificationPreference(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_page.py b/tests/test_page.py index c10bff75..a979c641 100644 --- a/tests/test_page.py +++ b/tests/test_page.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi.canvas import Canvas @@ -13,7 +15,6 @@ @requests_mock.Mocker() class TestPage(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -136,7 +137,6 @@ def test_get_parent_group(self, m): @requests_mock.Mocker() class TestPageRevision(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_page_view.py b/tests/test_page_view.py index 68ea4c09..f3605e10 100644 --- a/tests/test_page_view.py +++ b/tests/test_page_view.py @@ -1,16 +1,17 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas from tests import settings -from util import register_uris +from tests.util import register_uris @requests_mock.Mocker() class TestPageView(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_paginated_list.py b/tests/test_paginated_list.py index 889424d5..25c4124c 100644 --- a/tests/test_paginated_list.py +++ b/tests/test_paginated_list.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals import unittest import requests_mock @@ -12,7 +13,6 @@ @requests_mock.Mocker() class TestPaginatedList(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) self.requester = self.canvas._Canvas__requester @@ -100,7 +100,7 @@ def test_iterator(self, m): ) list_1 = [item for item in pag_list] list_2 = [item for item in pag_list] - self.assertEqual(cmp(list_1, list_2), 0) + self.assertEqual(list_1, list_2) # get item def test_getitem_first(self, m): diff --git a/tests/test_progress.py b/tests/test_progress.py index 80ea0156..862f9ef2 100644 --- a/tests/test_progress.py +++ b/tests/test_progress.py @@ -1,17 +1,18 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock -import settings from canvasapi.canvas import Canvas from canvasapi.progress import Progress -from util import register_uris +from tests import settings +from tests.util import register_uris @requests_mock.Mocker() class TestProgress(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_quiz.py b/tests/test_quiz.py index 13c27931..83d133ff 100644 --- a/tests/test_quiz.py +++ b/tests/test_quiz.py @@ -1,17 +1,18 @@ +from __future__ import unicode_literals import unittest -import settings +from builtins import str import requests_mock from canvasapi import Canvas from canvasapi.quiz import Quiz +from tests import settings from tests.util import register_uris @requests_mock.Mocker() class TestQuiz(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_requester.py b/tests/test_requester.py index 57577c61..1193c337 100644 --- a/tests/test_requester.py +++ b/tests/test_requester.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals import unittest import requests_mock @@ -14,7 +15,6 @@ @requests_mock.Mocker() class TestRequester(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) self.requester = self.canvas._Canvas__requester diff --git a/tests/test_section.py b/tests/test_section.py index e2459c84..1378b9b7 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest -import settings + +from builtins import str import requests_mock from canvasapi import Canvas @@ -7,13 +9,13 @@ from canvasapi.exceptions import RequiredFieldMissing from canvasapi.section import Section from canvasapi.submission import Submission +from tests import settings from tests.util import register_uris @requests_mock.Mocker() class TestSection(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_submission.py b/tests/test_submission.py index 2c5bd5a2..bcd546d4 100644 --- a/tests/test_submission.py +++ b/tests/test_submission.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -10,7 +12,6 @@ @requests_mock.Mocker() class TestSubmission(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_tab.py b/tests/test_tab.py index 95683c6a..80277166 100644 --- a/tests/test_tab.py +++ b/tests/test_tab.py @@ -1,5 +1,7 @@ +from __future__ import unicode_literals import unittest +from builtins import str import requests_mock from canvasapi import Canvas @@ -10,7 +12,6 @@ @requests_mock.Mocker() class TestTab(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_uploader.py b/tests/test_uploader.py index 368f1e62..a6d992ce 100644 --- a/tests/test_uploader.py +++ b/tests/test_uploader.py @@ -1,6 +1,7 @@ +from __future__ import unicode_literals +import os import unittest import uuid -import os import requests_mock @@ -13,15 +14,16 @@ @requests_mock.Mocker() class TestUploader(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) self.requester = self.canvas._Canvas__requester - self.filename = 'testfile_%s' % uuid.uuid4().hex + self.filename = 'testfile_uploader_%s' % uuid.uuid4().hex self.file = open(self.filename, 'w+') def tearDown(self): + self.file.close() + # http://stackoverflow.com/a/10840586 # Not as stupid as it looks. try: diff --git a/tests/test_user.py b/tests/test_user.py index 68249b56..8abc9263 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,7 +1,9 @@ +from __future__ import unicode_literals +import os import unittest import uuid -import os +from builtins import str import requests_mock from canvasapi import Canvas @@ -25,7 +27,6 @@ @requests_mock.Mocker() class TestUser(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) @@ -194,10 +195,9 @@ def test_list_enrollments(self, m): def test_upload(self, m): register_uris({'user': ['upload', 'upload_final']}, m) - filename = 'testfile_%s' % uuid.uuid4().hex - file = open(filename, 'w+') - - response = self.user.upload(file) + filename = 'testfile_user_%s' % uuid.uuid4().hex + with open(filename, 'w+') as file: + response = self.user.upload(file) self.assertTrue(response[0]) self.assertIsInstance(response[1], dict) @@ -277,6 +277,15 @@ def test_user_files(self, m): self.assertEqual(len(file_list), 4) self.assertIsInstance(file_list[0], File) + # get_file() + def test_get_file(self, m): + register_uris({'user': ['get_file']}, m) + + file = self.user.get_file(1) + self.assertIsInstance(file, File) + self.assertEqual(file.display_name, 'User_File.docx') + self.assertEqual(file.size, 1024) + # get_folder() def test_get_folder(self, m): register_uris({'user': ['get_folder']}, m) @@ -360,7 +369,6 @@ def test_remove_observee(self, m): @requests_mock.Mocker() class TestUserDisplay(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/test_util.py b/tests/test_util.py index e84ea439..f61b3ef5 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,3 +1,4 @@ +from __future__ import unicode_literals import unittest import requests_mock @@ -13,7 +14,6 @@ @requests_mock.Mocker() class TestUtil(unittest.TestCase): - @classmethod def setUp(self): self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) diff --git a/tests/util.py b/tests/util.py index 2f3cb17c..5dcb1f8a 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,3 +1,4 @@ +from __future__ import print_function, unicode_literals import json import requests_mock @@ -14,10 +15,11 @@ def register_uris(requirements, requests_mocker): :param requirements: dict :param requests_mocker: requests_mock.mocker.Mocker """ - for fixture, objects in requirements.iteritems(): + for fixture, objects in requirements.items(): try: - data = json.loads(open('tests/fixtures/{}.json'.format(fixture)).read()) + with open('tests/fixtures/{}.json'.format(fixture)) as file: + data = json.loads(file.read()) except IOError: raise ValueError('Fixture {}.json contains invalid JSON.'.format(fixture)) @@ -48,4 +50,4 @@ def register_uris(requirements, requests_mocker): headers=obj.get('headers', {}) ) except Exception as e: - print e + print(e)