cleanup references of python 2 & <3.11 (#35799)

* chore: cleanup of old python references
This commit is contained in:
Irtaza Akram
2024-11-15 16:58:20 +05:00
committed by GitHub
parent 9de9f2648d
commit ec2a698604
18 changed files with 87 additions and 165 deletions

View File

@@ -1,4 +1,4 @@
name: CI
name: Verify Dunder __init__.py Files
on:
pull_request:

View File

@@ -38,28 +38,20 @@ class LinkedInAddToProfileUrlTests(TestCase):
def test_linked_in_url(self, cert_mode, expected_cert_name):
config = LinkedInAddToProfileConfigurationFactory()
# We can switch to this once edx-platform reaches Python 3.8
# expected_url = (
# 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
# 'name={platform}+{cert_name}&certUrl={cert_url}&'
# 'organizationId={company_identifier}'
# ).format(
# platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
# cert_name=expected_cert_name,
# cert_url=quote(self.CERT_URL, safe=''),
# company_identifier=config.company_identifier,
# )
expected_url = (
'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
'name={platform}+{cert_name}&certUrl={cert_url}&'
'organizationId={company_identifier}'
).format(
platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
cert_name=expected_cert_name,
cert_url=quote(self.CERT_URL, safe=''),
company_identifier=config.company_identifier,
)
actual_url = config.add_to_profile_url(self.COURSE_NAME, cert_mode, self.CERT_URL)
# We can switch to this instead of the assertIn once edx-platform reaches Python 3.8
# There was a problem with dict ordering in the add_to_profile_url function that will go away then.
# self.assertEqual(actual_url, expected_url)
assert 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME' in actual_url
assert f'&name={quote(settings.PLATFORM_NAME.encode("utf-8"))}+{expected_cert_name}' in actual_url
assert '&certUrl={cert_url}'.format(cert_url=quote(self.CERT_URL, safe='')) in actual_url
assert f'&organizationId={config.company_identifier}' in actual_url
self.assertEqual(actual_url, expected_url)
@ddt.data(
('honor', 'Honor+Code+Credential+for+Test+Course+%E2%98%83'),
@@ -72,26 +64,18 @@ class LinkedInAddToProfileUrlTests(TestCase):
def test_linked_in_url_with_cert_name_override(self, cert_mode, expected_cert_name):
config = LinkedInAddToProfileConfigurationFactory()
# We can switch to this once edx-platform reaches Python 3.8
# expected_url = (
# 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
# 'name={platform}+{cert_name}&certUrl={cert_url}&'
# 'organizationId={company_identifier}'
# ).format(
# platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
# cert_name=expected_cert_name,
# cert_url=quote(self.CERT_URL, safe=''),
# company_identifier=config.company_identifier,
# )
expected_url = (
'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
'name={platform}+{cert_name}&certUrl={cert_url}&'
'organizationId={company_identifier}'
).format(
platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
cert_name=expected_cert_name,
cert_url=quote(self.CERT_URL, safe=''),
company_identifier=config.company_identifier,
)
with with_site_configuration_context(configuration=self.SITE_CONFIGURATION):
actual_url = config.add_to_profile_url(self.COURSE_NAME, cert_mode, self.CERT_URL)
# We can switch to this instead of the assertIn once edx-platform reaches Python 3.8
# There was a problem with dict ordering in the add_to_profile_url function that will go away then.
# self.assertEqual(actual_url, expected_url)
assert 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME' in actual_url
assert f'&name={quote(settings.PLATFORM_NAME.encode("utf-8"))}+{expected_cert_name}' in actual_url
assert '&certUrl={cert_url}'.format(cert_url=quote(self.CERT_URL, safe='')) in actual_url
assert f'&organizationId={config.company_identifier}' in actual_url
self.assertEqual(actual_url, expected_url)

View File

@@ -440,28 +440,19 @@ class DashboardTest(ModuleStoreTestCase, TestVerificationBase):
assert response.status_code == 200
self.assertContains(response, 'Add Certificate to LinkedIn')
# We can switch to this and the commented out assertContains once edx-platform reaches Python 3.8
# expected_url = (
# 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
# 'name={platform}+Honor+Code+Certificate+for+Omega&certUrl={cert_url}&'
# 'organizationId={company_identifier}'
# ).format(
# platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
# cert_url=quote(cert.download_url, safe=''),
# company_identifier=linkedin_config.company_identifier,
# )
# self.assertContains(response, escape(expected_url))
# These can be removed (in favor of the above) once we are on Python 3.8. Fails in 3.5 because of dict ordering
self.assertContains(response, escape('https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME'))
self.assertContains(response, escape('&name={platform}+Honor+Code+Certificate+for+Omega'.format(
platform=quote(settings.PLATFORM_NAME.encode('utf-8'))
)))
self.assertContains(response, escape('&certUrl={cert_url}'.format(cert_url=quote(cert.download_url, safe=''))))
self.assertContains(response, escape('&organizationId={company_identifier}'.format(
expected_url = (
'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&'
'name={platform}+Honor+Code+Certificate+for+Omega&'
'certUrl={cert_url}&'
'organizationId={company_identifier}'
).format(
platform=quote(settings.PLATFORM_NAME.encode('utf-8')),
cert_url=quote(cert.download_url, safe=''),
company_identifier=linkedin_config.company_identifier
)))
)
# Single assertion for the expected LinkedIn URL
self.assertContains(response, escape(expected_url))
@skip_unless_lms
def test_dashboard_metadata_caching(self):

View File

@@ -53,13 +53,11 @@ def cache_if_anonymous(*get_parameters):
# specifically the branding index, to do authentication.
# If that page is cached the authentication doesn't
# happen, so we disable the cache when that feature is enabled.
if (
not request.user.is_authenticated
):
if not request.user.is_authenticated:
# Use the cache. The same view accessed through different domain names may
# return different things, so include the domain name in the key.
domain = str(request.META.get('HTTP_HOST')) + '.'
cache_key = domain + "cache_if_anonymous." + get_language() + '.' + request.path
domain = request.META.get('HTTP_HOST', '') + '.'
cache_key = f"{domain}cache_if_anonymous.{get_language()}.{request.path}"
# Include the values of GET parameters in the cache key.
for get_parameter in get_parameters:
@@ -67,24 +65,20 @@ def cache_if_anonymous(*get_parameters):
if parameter_value is not None:
# urlencode expects data to be of type str, and doesn't deal well with Unicode data
# since it doesn't provide a way to specify an encoding.
cache_key = cache_key + '.' + urlencode({
get_parameter: str(parameter_value).encode('utf-8')
})
cache_key += '.' + urlencode({get_parameter: str(parameter_value).encode('utf-8')})
response = cache.get(cache_key)
if response:
# A hack to ensure that the response data is a valid text type for both Python 2 and 3.
response_content = list(response._container) # lint-amnesty, pylint: disable=bad-option-value, protected-access, protected-member
response.content = b''
for item in response_content:
response.write(item)
# Ensure that response content is properly handled for caching
response.content = (
# pylint: disable=protected-access
b''.join(response._container) if hasattr(response, '_container') else response.content
)
else:
response = view_func(request, *args, **kwargs)
cache.set(cache_key, response, 60 * 3)
return response
else:
# Don't use the cache.
return view_func(request, *args, **kwargs)

View File

@@ -86,15 +86,9 @@ class TestProgramListing(ProgramsApiConfigMixin, SharedModuleStoreTestCase):
def assert_dict_contains_subset(self, superset, subset):
"""
Verify that the dict superset contains the dict subset.
Works like assertDictContainsSubset, deprecated since Python 3.2.
See: https://docs.python.org/2.7/library/unittest.html#unittest.TestCase.assertDictContainsSubset.
"""
superset_keys = set(superset.keys())
subset_keys = set(subset.keys())
intersection = {key: superset[key] for key in superset_keys & subset_keys}
assert subset == intersection
for key, value in subset.items():
assert key in superset and superset[key] == value, f"{key}: {value} not found in superset or does not match"
def test_login_required(self, mock_get_programs):
"""

View File

@@ -106,9 +106,12 @@ def meili_id_from_opaque_key(usage_key: UsageKey) -> str:
we could use PublishableEntity's primary key / UUID instead.
"""
# The slugified key _may_ not be unique so we append a hashed string to make it unique:
key_bin = str(usage_key).encode()
suffix = blake2b(key_bin, digest_size=4).hexdigest() # When we use Python 3.9+, should add usedforsecurity=False
return slugify(str(usage_key)) + "-" + suffix
key_str = str(usage_key)
key_bin = key_str.encode()
suffix = blake2b(key_bin, digest_size=4, usedforsecurity=False).hexdigest()
return f"{slugify(key_str)}-{suffix}"
def _meili_access_id_from_context_key(context_key: LearningContextKey) -> int:

View File

@@ -86,11 +86,10 @@ class ContentLibrariesRestApiTest(APITransactionTestCase):
"""
Assert that the first dict contains at least all of the same entries as
the second dict.
Like python 2's assertDictContainsSubset, but with the arguments in the
correct order.
"""
assert big_dict.items() >= subset_dict.items()
for key, value in subset_dict.items():
assert key in big_dict, f"Missing key: {key}"
assert big_dict[key] == value, f"Value for key {key} does not match: expected {value}, got {big_dict[key]}"
def assertOrderEqual(self, libraries_list, expected_order):
"""

View File

@@ -270,14 +270,13 @@ class TestCsrfCrossDomainCookieMiddleware(TestCase):
if is_set:
assert self.COOKIE_NAME in response.cookies
cookie_header = str(response.cookies[self.COOKIE_NAME])
# lint-amnesty, pylint: disable=bad-option-value, unicode-format-string
expected = 'Set-Cookie: {name}={value}; Domain={domain};'.format(
name=self.COOKIE_NAME,
value=self.COOKIE_VALUE,
domain=self.COOKIE_DOMAIN
)
assert expected in cookie_header
# added lower function because in python 3 the value of cookie_header has Secure and secure in python 2
assert 'Max-Age=31449600; Path=/; secure'.lower() in cookie_header.lower()
assert 'Max-Age=31449600; Path=/; Secure' in cookie_header
else:
assert self.COOKIE_NAME not in response.cookies

View File

@@ -42,16 +42,14 @@ class CrawlersConfig(ConfigurationModel):
# If there was no user agent detected or no crawler agents configured,
# then just return False.
if (not req_user_agent) or (not crawler_agents):
if not req_user_agent or not crawler_agents:
return False
# The crawler_agents list we pull from our model always has unicode objects, but the
# req_user_agent we get from HTTP headers ultimately comes to us via WSGI. That
# value is an ISO-8859-1 encoded byte string in Python 2.7 (and in the HTTP spec), but
# it will be a unicode str when we move to Python 3.x. This code should work under
# either version.
# Decode req_user_agent if it's bytes, so we can work with consistent string types.
if isinstance(req_user_agent, bytes):
crawler_agents = [crawler_agent.encode('iso-8859-1') for crawler_agent in crawler_agents]
req_user_agent = req_user_agent.decode('iso-8859-1')
crawler_agents = [crawler_agent.strip() for crawler_agent in crawler_agents]
# We perform prefix matching of the crawler agent here so that we don't
# have to worry about version bumps.

View File

@@ -207,7 +207,7 @@ class CacheInvalidationManager:
def zpickle(data):
"""Given any data structure, returns a zlib compressed pickled serialization."""
return zlib.compress(pickle.dumps(data, 4)) # Keep this constant as we upgrade from python 2 to 3.
return zlib.compress(pickle.dumps(data, 4))
def zunpickle(zdata):

View File

@@ -48,11 +48,9 @@ def is_score_higher_or_equal(earned1, possible1, earned2, possible2, treat_undef
def round_away_from_zero(number, digits=0):
"""
Round numbers using the 'away from zero' strategy as opposed to the
'Banker's rounding strategy.' The strategy refers to how we round when
a number is half way between two numbers. eg. 0.5, 1.5, etc. In python 2
positive numbers in this category would be rounded up and negative numbers
would be rounded down. ie. away from zero. In python 3 numbers round
towards even. So 0.5 would round to 0 but 1.5 would round to 2.
'Banker's rounding strategy.' The strategy refers to how we round when
a number is half way between two numbers. eg. 0.5, 1.5, etc. In python 3
numbers round towards even. So 0.5 would round to 0 but 1.5 would round to 2.
See here for more on floating point rounding strategies:
https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules

View File

@@ -77,17 +77,9 @@ class TimedDecoratorTests(TestCase):
messages = self.get_log_messages()
assert len(messages) == 1
# I'm not using assertDictContainsSubset because it is
# removed in python 3.2 (because the arguments were backwards)
# and it wasn't ever replaced by anything *headdesk*
assert 'duration' in messages[0]
assert 35.6 == messages[0]['duration']
assert 'started_at' in messages[0]
assert start.isoformat(' ') == messages[0]['started_at']
assert 'ended_at' in messages[0]
assert end.isoformat(' ') == messages[0]['ended_at']
assert 'duration' in messages[0] and messages[0]['duration'] == 35.6
assert 'started_at' in messages[0] and messages[0]['started_at'] == start.isoformat(' ')
assert 'ended_at' in messages[0] and messages[0]['ended_at'] == end.isoformat(' ')
@patch.object(timer, 'PAVER_TIMER_LOG', None)
def test_no_logs(self):
@@ -99,28 +91,18 @@ class TimedDecoratorTests(TestCase):
messages = self.get_log_messages(args=(1, 'foo'), kwargs=dict(bar='baz'))
assert len(messages) == 1
# I'm not using assertDictContainsSubset because it is
# removed in python 3.2 (because the arguments were backwards)
# and it wasn't ever replaced by anything *headdesk*
assert 'args' in messages[0]
assert [repr(1), repr('foo')] == messages[0]['args']
assert 'kwargs' in messages[0]
assert {'bar': repr('baz')} == messages[0]['kwargs']
assert 'args' in messages[0] and messages[0]['args'] == [repr(1), repr('foo')]
assert 'kwargs' in messages[0] and messages[0]['kwargs'] == {'bar': repr('baz')}
@patch.object(timer, 'PAVER_TIMER_LOG', '/tmp/some-log')
def test_task_name(self):
messages = self.get_log_messages()
assert len(messages) == 1
# I'm not using assertDictContainsSubset because it is
# removed in python 3.2 (because the arguments were backwards)
# and it wasn't ever replaced by anything *headdesk*
assert 'task' in messages[0]
assert 'pavelib.paver_tests.test_timer.identity' == messages[0]['task']
assert 'task' in messages[0] and messages[0]['task'] == 'pavelib.paver_tests.test_timer.identity'
@patch.object(timer, 'PAVER_TIMER_LOG', '/tmp/some-log')
def test_exceptions(self):
@timer.timed
def raises():
"""
@@ -131,11 +113,7 @@ class TimedDecoratorTests(TestCase):
messages = self.get_log_messages(task=raises, raises=Exception)
assert len(messages) == 1
# I'm not using assertDictContainsSubset because it is
# removed in python 3.2 (because the arguments were backwards)
# and it wasn't ever replaced by anything *headdesk*
assert 'exception' in messages[0]
assert 'Exception: The Message!' == messages[0]['exception']
assert 'exception' in messages[0] and messages[0]['exception'] == 'Exception: The Message!'
@patch.object(timer, 'PAVER_TIMER_LOG', '/tmp/some-log-%Y-%m-%d-%H-%M-%S.log')
def test_date_formatting(self):

View File

@@ -19,10 +19,6 @@
# Ticket: https://github.com/openedx/edx-platform/issues/35334
algoliasearch<4.0.0
# Date: 2024-03-14
# Temporary to Support the python 3.11 Upgrade
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35281
backports.zoneinfo;python_version<"3.9" # Newer versions have zoneinfo available in the standard library
# Date: 2020-02-26
# As it is not clarified what exact breaking changes will be introduced as per

View File

@@ -32,4 +32,4 @@ You'd have to update the `unit-test-shards.json` file manually to fix this.
```
pytest --collect-only --ds=cms.envs.test cms/
```
For more details on how this check collects and compares the unit tests count please take a look at [verify unit tests count](../.github/workflows/verify-gha-unit-tests-count.yml)
For more details on how this check collects and compares the unit tests count please take a look at [verify unit tests count](../.github/workflows/unit-tests.yml)

View File

@@ -29,11 +29,11 @@ To download the scripts, you can perform a partial clone of the edx-platform rep
Create Python Virtual Environment
---------------------------------
Create a Python virtual environment using Python 3.8:
Create a Python virtual environment using Python 3.11:
.. code-block:: bash
python3.8 -m venv ../venv
python3.11 -m venv ../venv
source ../venv/bin/activate
Install Pip Packages

View File

@@ -28,11 +28,11 @@ To download the scripts, you can perform a partial clone of the edx-platform rep
Create Python Virtual Environment
---------------------------------
Create a Python virtual environment using Python 3.8:
Create a Python virtual environment using Python 3.11:
.. code-block:: bash
python3.8 -m venv ../venv
python3.11 -m venv ../venv
source ../venv/bin/activate
Install Pip Packages

View File

@@ -43,12 +43,11 @@ def _fail(kind, code, message):
"""
_log(kind, message)
# Try to get a traceback, if there is one. On Python 3.4 this raises an AttributeError
# if there is no current exception, so we eat that here.
try:
_log(kind, traceback.format_exc())
except AttributeError:
pass
# Log the traceback if an exception is currently being handled
exc_type, exc_value, exc_traceback = sys.exc_info()
if exc_type is not None:
traceback_str = traceback.format_exception(exc_type, exc_value, exc_traceback)
_log(kind, ''.join(traceback_str))
sys.exit(code)
@@ -66,16 +65,12 @@ def _get_error_str_from_exception(exc):
"""
Return a string from an exception that may or may not have a .content (Slumber)
"""
exc_msg = text_type(exc)
exc_msg = str(exc)
if hasattr(exc, 'content'):
# Slumber inconveniently discards the decoded .text attribute from the Response object,
# and instead gives us the raw encoded .content attribute, so we need to decode it first.
# Python 2 needs the decode, Py3 does not have it.
try:
exc_msg += '\n' + str(exc.content).decode('utf-8')
except AttributeError:
exc_msg += '\n' + str(exc.content)
# Attempt to decode `exc.content` if it's in bytes, otherwise just convert to str
exc_content = exc.content.decode('utf-8') if isinstance(exc.content, bytes) else str(exc.content)
exc_msg += '\n' + exc_content
return exc_msg

View File

@@ -306,13 +306,6 @@ class TestUpdateHash(unittest.TestCase):
"""
d1 = {k: 1 for k in "abcdefghijklmnopqrstuvwxyz"}
d2 = {k: 1 for k in "bcdefghijklmnopqrstuvwxyza"}
# TODO: remove the next lines once we are in python3.8
# since python3.8 dict preserve the order of insertion
# and therefore d2 and d1 keys are already in different order.
for i in range(10000):
d2[i] = 1
for i in range(10000):
del d2[i]
# Check that our dicts are equal, but with different key order.
assert d1 == d2