Commit 81d17c51 by Ned Batchelder

Fix or remove tearDown methods that don't use super.

Update edx-lint to the version that checks if tearDown uses super.

Convert a number of tearDown methods into addCleanup.

Remove some unneeded tearDown methods: no need to call patch.stopall if
none of them were started with patch.start.
parent 40ae5d2e
...@@ -21,6 +21,7 @@ class ExportAllCourses(ModuleStoreTestCase): ...@@ -21,6 +21,7 @@ class ExportAllCourses(ModuleStoreTestCase):
super(ExportAllCourses, self).setUp() super(ExportAllCourses, self).setUp()
self.store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo) self.store = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.mongo)
self.temp_dir = mkdtemp() self.temp_dir = mkdtemp()
self.addCleanup(shutil.rmtree, self.temp_dir)
self.first_course = CourseFactory.create( self.first_course = CourseFactory.create(
org="test", course="course1", display_name="run1", default_store=ModuleStoreEnum.Type.mongo org="test", course="course1", display_name="run1", default_store=ModuleStoreEnum.Type.mongo
) )
...@@ -47,7 +48,3 @@ class ExportAllCourses(ModuleStoreTestCase): ...@@ -47,7 +48,3 @@ class ExportAllCourses(ModuleStoreTestCase):
self.assertEqual(len(courses), 2) self.assertEqual(len(courses), 2)
self.assertEqual(len(failed_export_courses), 1) self.assertEqual(len(failed_export_courses), 1)
self.assertEqual(failed_export_courses[0], unicode(second_course_id)) self.assertEqual(failed_export_courses[0], unicode(second_course_id))
def tearDown(self):
""" Common cleanup. """
shutil.rmtree(self.temp_dir)
...@@ -19,16 +19,13 @@ class ConvertExportFormat(TestCase): ...@@ -19,16 +19,13 @@ class ConvertExportFormat(TestCase):
super(ConvertExportFormat, self).setUp() super(ConvertExportFormat, self).setUp()
self.temp_dir = mkdtemp() self.temp_dir = mkdtemp()
self.addCleanup(shutil.rmtree, self.temp_dir)
self.data_dir = path(__file__).realpath().parent / 'data' self.data_dir = path(__file__).realpath().parent / 'data'
self.version0 = self.data_dir / "Version0_drafts.tar.gz" self.version0 = self.data_dir / "Version0_drafts.tar.gz"
self.version1 = self.data_dir / "Version1_drafts.tar.gz" self.version1 = self.data_dir / "Version1_drafts.tar.gz"
self.command = Command() self.command = Command()
def tearDown(self):
""" Common cleanup. """
shutil.rmtree(self.temp_dir)
def test_no_args(self): def test_no_args(self):
""" Test error condition of no arguments. """ """ Test error condition of no arguments. """
errstring = "export requires two arguments" errstring = "export requires two arguments"
......
...@@ -112,8 +112,9 @@ class TestSaveSubsToStore(ModuleStoreTestCase): ...@@ -112,8 +112,9 @@ class TestSaveSubsToStore(ModuleStoreTestCase):
self.subs_id = str(uuid4()) self.subs_id = str(uuid4())
filename = 'subs_{0}.srt.sjson'.format(self.subs_id) filename = 'subs_{0}.srt.sjson'.format(self.subs_id)
self.content_location = StaticContent.compute_location(self.course.id, filename) self.content_location = StaticContent.compute_location(self.course.id, filename)
self.addCleanup(self.clear_subs_content)
# incorrect subs # incorrect subs
self.unjsonable_subs = set([1]) # set can't be serialized self.unjsonable_subs = set([1]) # set can't be serialized
self.unjsonable_subs_id = str(uuid4()) self.unjsonable_subs_id = str(uuid4())
...@@ -150,9 +151,6 @@ class TestSaveSubsToStore(ModuleStoreTestCase): ...@@ -150,9 +151,6 @@ class TestSaveSubsToStore(ModuleStoreTestCase):
with self.assertRaises(NotFoundError): with self.assertRaises(NotFoundError):
contentstore().find(self.content_location_unjsonable) contentstore().find(self.content_location_unjsonable)
def tearDown(self):
self.clear_subs_content()
@override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE) @override_settings(CONTENTSTORE=TEST_DATA_CONTENTSTORE)
class TestDownloadYoutubeSubs(ModuleStoreTestCase): class TestDownloadYoutubeSubs(ModuleStoreTestCase):
......
...@@ -383,6 +383,8 @@ class TestCourseReIndex(CourseTestCase): ...@@ -383,6 +383,8 @@ class TestCourseReIndex(CourseTestCase):
with open(self.TEST_INDEX_FILENAME, "w+") as index_file: with open(self.TEST_INDEX_FILENAME, "w+") as index_file:
json.dump({}, index_file) json.dump({}, index_file)
self.addCleanup(os.remove, self.TEST_INDEX_FILENAME)
def test_reindex_course(self): def test_reindex_course(self):
""" """
Verify that course gets reindexed. Verify that course gets reindexed.
...@@ -666,6 +668,3 @@ class TestCourseReIndex(CourseTestCase): ...@@ -666,6 +668,3 @@ class TestCourseReIndex(CourseTestCase):
# Start manual reindex and check error in response # Start manual reindex and check error in response
with self.assertRaises(SearchIndexingError): with self.assertRaises(SearchIndexingError):
CoursewareSearchIndexer.do_course_reindex(modulestore(), self.course.id) CoursewareSearchIndexer.do_course_reindex(modulestore(), self.course.id)
def tearDown(self):
os.remove(self.TEST_INDEX_FILENAME)
...@@ -43,6 +43,7 @@ class ImportTestCase(CourseTestCase): ...@@ -43,6 +43,7 @@ class ImportTestCase(CourseTestCase):
super(ImportTestCase, self).setUp() super(ImportTestCase, self).setUp()
self.url = reverse_course_url('import_handler', self.course.id) self.url = reverse_course_url('import_handler', self.course.id)
self.content_dir = path(tempfile.mkdtemp()) self.content_dir = path(tempfile.mkdtemp())
self.addCleanup(shutil.rmtree, self.content_dir)
def touch(name): def touch(name):
""" Equivalent to shell's 'touch'""" """ Equivalent to shell's 'touch'"""
...@@ -74,9 +75,6 @@ class ImportTestCase(CourseTestCase): ...@@ -74,9 +75,6 @@ class ImportTestCase(CourseTestCase):
self.unsafe_common_dir = path(tempfile.mkdtemp(dir=self.content_dir)) self.unsafe_common_dir = path(tempfile.mkdtemp(dir=self.content_dir))
def tearDown(self):
shutil.rmtree(self.content_dir)
def test_no_coursexml(self): def test_no_coursexml(self):
""" """
Check that the response for a tar.gz import without a course.xml is Check that the response for a tar.gz import without a course.xml is
......
...@@ -227,6 +227,7 @@ class ConvertExportFormat(unittest.TestCase): ...@@ -227,6 +227,7 @@ class ConvertExportFormat(unittest.TestCase):
# Directory for expanding all the test archives # Directory for expanding all the test archives
self.temp_dir = mkdtemp() self.temp_dir = mkdtemp()
self.addCleanup(shutil.rmtree, self.temp_dir)
# Directory where new archive will be created # Directory where new archive will be created
self.result_dir = path(self.temp_dir) / uuid.uuid4().hex self.result_dir = path(self.temp_dir) / uuid.uuid4().hex
...@@ -284,10 +285,6 @@ class ConvertExportFormat(unittest.TestCase): ...@@ -284,10 +285,6 @@ class ConvertExportFormat(unittest.TestCase):
self._no_version = self._expand_archive('NoVersionNumber.tar.gz') self._no_version = self._expand_archive('NoVersionNumber.tar.gz')
return self._no_version return self._no_version
def tearDown(self):
""" Common cleanup. """
shutil.rmtree(self.temp_dir)
def _expand_archive(self, name): def _expand_archive(self, name):
""" Expand archive into a directory and return the directory. """ """ Expand archive into a directory and return the directory. """
target = path(self.temp_dir) / uuid.uuid4().hex target = path(self.temp_dir) / uuid.uuid4().hex
......
...@@ -642,6 +642,7 @@ class VideoDescriptorIndexingTestCase(unittest.TestCase): ...@@ -642,6 +642,7 @@ class VideoDescriptorIndexingTestCase(unittest.TestCase):
""" """
Overrides YOUTUBE and CONTENTSTORE settings Overrides YOUTUBE and CONTENTSTORE settings
""" """
super(VideoDescriptorIndexingTestCase, self).setUp()
self.youtube_setting = getattr(settings, "YOUTUBE", None) self.youtube_setting = getattr(settings, "YOUTUBE", None)
self.contentstore_setting = getattr(settings, "CONTENTSTORE", None) self.contentstore_setting = getattr(settings, "CONTENTSTORE", None)
settings.YOUTUBE = { settings.YOUTUBE = {
......
...@@ -11,6 +11,7 @@ class BrandingInfoConfigTest(TestCase): ...@@ -11,6 +11,7 @@ class BrandingInfoConfigTest(TestCase):
Test the BrandingInfoConfig model. Test the BrandingInfoConfig model.
""" """
def setUp(self): def setUp(self):
super(BrandingInfoConfigTest, self).setUp()
self.configuration_string = """{ self.configuration_string = """{
"CN": { "CN": {
"url": "http://www.xuetangx.com", "url": "http://www.xuetangx.com",
......
...@@ -18,7 +18,6 @@ from xmodule.modulestore.tests.factories import CourseFactory ...@@ -18,7 +18,6 @@ from xmodule.modulestore.tests.factories import CourseFactory
@patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message')) @patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message'))
class TestOptoutCourseEmails(ModuleStoreTestCase): class TestOptoutCourseEmails(ModuleStoreTestCase):
""" """
Test that optouts are referenced in sending course email. Test that optouts are referenced in sending course email.
""" """
...@@ -42,12 +41,6 @@ class TestOptoutCourseEmails(ModuleStoreTestCase): ...@@ -42,12 +41,6 @@ class TestOptoutCourseEmails(ModuleStoreTestCase):
'success': True, 'success': True,
} }
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
def navigate_to_email_view(self): def navigate_to_email_view(self):
"""Navigate to the instructor dash's email view""" """Navigate to the instructor dash's email view"""
# Pull up email view on instructor dashboard # Pull up email view on instructor dashboard
......
...@@ -84,12 +84,6 @@ class EmailSendFromDashboardTestCase(ModuleStoreTestCase): ...@@ -84,12 +84,6 @@ class EmailSendFromDashboardTestCase(ModuleStoreTestCase):
'success': True, 'success': True,
} }
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False}) @patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
@patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message')) @patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message'))
......
...@@ -58,9 +58,6 @@ class TestEmailErrors(ModuleStoreTestCase): ...@@ -58,9 +58,6 @@ class TestEmailErrors(ModuleStoreTestCase):
'success': True, 'success': True,
} }
def tearDown(self):
patch.stopall()
@patch('bulk_email.tasks.get_connection', autospec=True) @patch('bulk_email.tasks.get_connection', autospec=True)
@patch('bulk_email.tasks.send_course_email.retry') @patch('bulk_email.tasks.send_course_email.retry')
def test_data_err_retry(self, retry, get_conn): def test_data_err_retry(self, retry, get_conn):
......
...@@ -23,12 +23,6 @@ class CourseAuthorizationFormTest(ModuleStoreTestCase): ...@@ -23,12 +23,6 @@ class CourseAuthorizationFormTest(ModuleStoreTestCase):
course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ" course_title = u"ẗëṡẗ title イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ"
self.course = CourseFactory.create(display_name=course_title) self.course = CourseFactory.create(display_name=course_title)
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': True}) @patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': True})
def test_authorize_mongo_course(self): def test_authorize_mongo_course(self):
# Initially course shouldn't be authorized # Initially course shouldn't be authorized
......
...@@ -176,6 +176,7 @@ class GroupAccessTestCase(ModuleStoreTestCase): ...@@ -176,6 +176,7 @@ class GroupAccessTestCase(ModuleStoreTestCase):
side-effects in other tests. side-effects in other tests.
""" """
UserPartition.scheme_extensions = None UserPartition.scheme_extensions = None
super(GroupAccessTestCase, self).tearDown()
def check_access(self, user, block_location, is_accessible): def check_access(self, user, block_location, is_accessible):
""" """
......
...@@ -154,6 +154,7 @@ class TestVideo(BaseTestXmodule): ...@@ -154,6 +154,7 @@ class TestVideo(BaseTestXmodule):
def tearDown(self): def tearDown(self):
_clear_assets(self.item_descriptor.location) _clear_assets(self.item_descriptor.location)
super(TestVideo, self).tearDown()
class TestTranscriptAvailableTranslationsDispatch(TestVideo): class TestTranscriptAvailableTranslationsDispatch(TestVideo):
......
...@@ -45,6 +45,7 @@ class RefundTests(ModuleStoreTestCase): ...@@ -45,6 +45,7 @@ class RefundTests(ModuleStoreTestCase):
def tearDown(self): def tearDown(self):
self.course_mode.delete() self.course_mode.delete()
Order.objects.filter(user=self.student).delete() Order.objects.filter(user=self.student).delete()
super(RefundTests, self).tearDown()
def _enroll(self, purchase=True): def _enroll(self, purchase=True):
# pylint: disable=missing-docstring # pylint: disable=missing-docstring
......
...@@ -31,15 +31,12 @@ class PermissionsTestCase(TestCase): ...@@ -31,15 +31,12 @@ class PermissionsTestCase(TestCase):
self.moderator.is_staff = True self.moderator.is_staff = True
self.moderator.save() self.moderator.save()
self.student_enrollment = CourseEnrollment.enroll(self.student, self.course_id) self.student_enrollment = CourseEnrollment.enroll(self.student, self.course_id)
self.addCleanup(self.student_enrollment.delete)
self.moderator_enrollment = CourseEnrollment.enroll(self.moderator, self.course_id) self.moderator_enrollment = CourseEnrollment.enroll(self.moderator, self.course_id)
self.addCleanup(self.moderator_enrollment.delete)
def tearDown(self): # Do we need to have this in a cleanup? We shouldn't be deleting students, ever.
self.student_enrollment.delete() # self.student.delete()
self.moderator_enrollment.delete() # self.moderator.delete()
# Do we need to have this? We shouldn't be deleting students, ever
# self.student.delete()
# self.moderator.delete()
def testDefaultRoles(self): def testDefaultRoles(self):
self.assertTrue(self.student_role in self.student.roles.all()) self.assertTrue(self.student_role in self.student.roles.all())
......
...@@ -29,16 +29,13 @@ class MockCommentServiceServerTest(unittest.TestCase): ...@@ -29,16 +29,13 @@ class MockCommentServiceServerTest(unittest.TestCase):
self.expected_response = {'username': 'user100', 'external_id': '4'} self.expected_response = {'username': 'user100', 'external_id': '4'}
self.server = MockCommentServiceServer(port_num=server_port, self.server = MockCommentServiceServer(port_num=server_port,
response=self.expected_response) response=self.expected_response)
self.addCleanup(self.server.shutdown)
# Start the server in a separate daemon thread # Start the server in a separate daemon thread
server_thread = threading.Thread(target=self.server.serve_forever) server_thread = threading.Thread(target=self.server.serve_forever)
server_thread.daemon = True server_thread.daemon = True
server_thread.start() server_thread.start()
def tearDown(self):
# Stop the server, freeing up the port
self.server.shutdown()
def test_new_user_request(self): def test_new_user_request(self):
""" """
Test the mock comment service using an example Test the mock comment service using an example
......
...@@ -602,12 +602,6 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -602,12 +602,6 @@ class TestInstructorAPIEnrollment(ModuleStoreTestCase, LoginEnrollmentTestCase):
# (comment because pylint C0103(invalid-name)) # (comment because pylint C0103(invalid-name))
# self.maxDiff = None # self.maxDiff = None
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
def test_missing_params(self): def test_missing_params(self):
""" Test missing all query parameters. """ """ Test missing all query parameters. """
url = reverse('students_update_enrollment', kwargs={'course_id': self.course.id.to_deprecated_string()}) url = reverse('students_update_enrollment', kwargs={'course_id': self.course.id.to_deprecated_string()})
...@@ -2725,12 +2719,6 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -2725,12 +2719,6 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
self.tasks = [self.FakeTask(mock_factory.mock_get_task_completion_info) for _ in xrange(7)] self.tasks = [self.FakeTask(mock_factory.mock_get_task_completion_info) for _ in xrange(7)]
self.tasks[-1].make_invalid_output() self.tasks[-1].make_invalid_output()
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
@patch.object(instructor_task.api, 'get_running_instructor_tasks') @patch.object(instructor_task.api, 'get_running_instructor_tasks')
def test_list_instructor_tasks_running(self, act): def test_list_instructor_tasks_running(self, act):
""" Test list of all running tasks. """ """ Test list of all running tasks. """
...@@ -2830,12 +2818,6 @@ class TestInstructorEmailContentList(ModuleStoreTestCase, LoginEnrollmentTestCas ...@@ -2830,12 +2818,6 @@ class TestInstructorEmailContentList(ModuleStoreTestCase, LoginEnrollmentTestCas
self.emails = {} self.emails = {}
self.emails_info = {} self.emails_info = {}
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
def setup_fake_email_info(self, num_emails, with_failures=False): def setup_fake_email_info(self, num_emails, with_failures=False):
""" Initialize the specified number of fake emails """ """ Initialize the specified number of fake emails """
for email_id in range(num_emails): for email_id in range(num_emails):
...@@ -3783,10 +3765,7 @@ class TestBulkCohorting(ModuleStoreTestCase): ...@@ -3783,10 +3765,7 @@ class TestBulkCohorting(ModuleStoreTestCase):
self.staff_user = StaffFactory(course_key=self.course.id) self.staff_user = StaffFactory(course_key=self.course.id)
self.non_staff_user = UserFactory.create() self.non_staff_user = UserFactory.create()
self.tempdir = tempfile.mkdtemp() self.tempdir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.tempdir)
def tearDown(self):
if os.path.exists(self.tempdir):
shutil.rmtree(self.tempdir)
def call_add_users_to_cohorts(self, csv_data, suffix='.csv', method='POST'): def call_add_users_to_cohorts(self, csv_data, suffix='.csv', method='POST'):
""" """
......
...@@ -4,7 +4,6 @@ Unit tests for Ecommerce feature flag in new instructor dashboard. ...@@ -4,7 +4,6 @@ Unit tests for Ecommerce feature flag in new instructor dashboard.
import datetime import datetime
from mock import patch
import pytz import pytz
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
...@@ -38,12 +37,6 @@ class TestECommerceDashboardViews(ModuleStoreTestCase): ...@@ -38,12 +37,6 @@ class TestECommerceDashboardViews(ModuleStoreTestCase):
self.e_commerce_link = '<a href="" data-section="e-commerce">E-Commerce</a>' self.e_commerce_link = '<a href="" data-section="e-commerce">E-Commerce</a>'
CourseFinanceAdminRole(self.course.id).add_users(self.instructor) CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
def test_pass_e_commerce_tab_in_instructor_dashboard(self): def test_pass_e_commerce_tab_in_instructor_dashboard(self):
""" """
Test Pass E-commerce Tab is in the Instructor Dashboard Test Pass E-commerce Tab is in the Instructor Dashboard
......
...@@ -33,12 +33,6 @@ class TestNewInstructorDashboardEmailViewMongoBacked(ModuleStoreTestCase): ...@@ -33,12 +33,6 @@ class TestNewInstructorDashboardEmailViewMongoBacked(ModuleStoreTestCase):
# URL for email view # URL for email view
self.email_link = '<a href="" data-section="send_email">Email</a>' self.email_link = '<a href="" data-section="send_email">Email</a>'
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
# In order for bulk email to work, we must have both the ENABLE_INSTRUCTOR_EMAIL_FLAG # In order for bulk email to work, we must have both the ENABLE_INSTRUCTOR_EMAIL_FLAG
# set to True and for the course to be Mongo-backed. # set to True and for the course to be Mongo-backed.
# The flag is enabled and the course is Mongo-backed (should work) # The flag is enabled and the course is Mongo-backed (should work)
......
...@@ -926,6 +926,7 @@ class InvoiceHistoryTest(TestCase): ...@@ -926,6 +926,7 @@ class InvoiceHistoryTest(TestCase):
} }
def setUp(self): def setUp(self):
super(InvoiceHistoryTest, self).setUp()
invoice_data = copy.copy(self.INVOICE_INFO) invoice_data = copy.copy(self.INVOICE_INFO)
invoice_data.update(self.CONTACT_INFO) invoice_data.update(self.CONTACT_INFO)
self.invoice = Invoice.objects.create(total_amount="123.45", **invoice_data) self.invoice = Invoice.objects.create(total_amount="123.45", **invoice_data)
......
...@@ -39,7 +39,7 @@ git+https://github.com/mitocw/django-cas.git@60a5b8e5a62e63e0d5d224a87f0b489201a ...@@ -39,7 +39,7 @@ git+https://github.com/mitocw/django-cas.git@60a5b8e5a62e63e0d5d224a87f0b489201a
-e git+https://github.com/pmitros/RecommenderXBlock.git@9b07e807c89ba5761827d0387177f71aa57ef056#egg=recommender-xblock -e git+https://github.com/pmitros/RecommenderXBlock.git@9b07e807c89ba5761827d0387177f71aa57ef056#egg=recommender-xblock
-e git+https://github.com/edx/edx-milestones.git@547f2250ee49e73ce8d7ff4e78ecf1b049892510#egg=edx-milestones -e git+https://github.com/edx/edx-milestones.git@547f2250ee49e73ce8d7ff4e78ecf1b049892510#egg=edx-milestones
-e git+https://github.com/edx/edx-search.git@264bb3317f98e9cb22b932aa11b89d0651fd741c#egg=edx-search -e git+https://github.com/edx/edx-search.git@264bb3317f98e9cb22b932aa11b89d0651fd741c#egg=edx-search
git+https://github.com/edx/edx-lint.git@c592557ecee8f2f1ef14ad984e1353f41aba0b47#egg=edx_lint==0.2 git+https://github.com/edx/edx-lint.git@8bf82a32ecb8598c415413df66f5232ab8d974e9#egg=edx_lint==0.2.1
-e git+https://github.com/edx/xblock-utils.git@17e247d66fabb53f0453515b093a030ee345a1b0#egg=xblock-utils -e git+https://github.com/edx/xblock-utils.git@17e247d66fabb53f0453515b093a030ee345a1b0#egg=xblock-utils
-e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive -e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment