mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Refs #23919 -- Removed misc Python 2/3 references.
This commit is contained in:
		| @@ -57,8 +57,8 @@ class AppConfig: | ||||
|         """Attempt to determine app's filesystem path from its module.""" | ||||
|         # See #21874 for extended discussion of the behavior of this method in | ||||
|         # various cases. | ||||
|         # Convert paths to list because Python 3's _NamespacePath does not | ||||
|         # support indexing. | ||||
|         # Convert paths to list because Python's _NamespacePath doesn't support | ||||
|         # indexing. | ||||
|         paths = list(getattr(module, '__path__', [])) | ||||
|         if len(paths) != 1: | ||||
|             filename = getattr(module, '__file__', None) | ||||
|   | ||||
| @@ -419,8 +419,7 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher): | ||||
|         # Hash the password prior to using bcrypt to prevent password | ||||
|         # truncation as described in #20138. | ||||
|         if self.digest is not None: | ||||
|             # Use binascii.hexlify() because a hex encoded bytestring is | ||||
|             # Unicode on Python 3. | ||||
|             # Use binascii.hexlify() because a hex encoded bytestring is str. | ||||
|             password = binascii.hexlify(self.digest(force_bytes(password)).digest()) | ||||
|         else: | ||||
|             password = force_bytes(password) | ||||
|   | ||||
| @@ -298,7 +298,7 @@ def password_reset_confirm(request, uidb64=None, token=None, | ||||
|     else: | ||||
|         post_reset_redirect = resolve_url(post_reset_redirect) | ||||
|     try: | ||||
|         # urlsafe_base64_decode() decodes to bytestring on Python 3 | ||||
|         # urlsafe_base64_decode() decodes to bytestring | ||||
|         uid = force_text(urlsafe_base64_decode(uidb64)) | ||||
|         user = UserModel._default_manager.get(pk=uid) | ||||
|     except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist): | ||||
| @@ -442,7 +442,7 @@ class PasswordResetConfirmView(PasswordContextMixin, FormView): | ||||
|  | ||||
|     def get_user(self, uidb64): | ||||
|         try: | ||||
|             # urlsafe_base64_decode() decodes to bytestring on Python 3 | ||||
|             # urlsafe_base64_decode() decodes to bytestring | ||||
|             uid = force_text(urlsafe_base64_decode(uidb64)) | ||||
|             user = UserModel._default_manager.get(pk=uid) | ||||
|         except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist): | ||||
|   | ||||
| @@ -324,8 +324,8 @@ class EmailMessage: | ||||
|                     try: | ||||
|                         content = content.decode('utf-8') | ||||
|                     except UnicodeDecodeError: | ||||
|                         # If mimetype suggests the file is text but it's actually | ||||
|                         # binary, read() will raise a UnicodeDecodeError on Python 3. | ||||
|                         # If mimetype suggests the file is text but it's | ||||
|                         # actually binary, read() raises a UnicodeDecodeError. | ||||
|                         mimetype = DEFAULT_ATTACHMENT_MIME_TYPE | ||||
|  | ||||
|             self.attachments.append((filename, content, mimetype)) | ||||
|   | ||||
| @@ -53,7 +53,7 @@ class Command(BaseCommand): | ||||
|             import rlcompleter | ||||
|             readline.set_completer(rlcompleter.Completer(imported_objects).complete) | ||||
|             # Enable tab completion on systems using libedit (e.g. Mac OSX). | ||||
|             # These lines are copied from Lib/site.py on Python 3.4. | ||||
|             # These lines are copied from Python's Lib/site.py. | ||||
|             readline_doc = getattr(readline, '__doc__', '') | ||||
|             if readline_doc is not None and 'libedit' in readline_doc: | ||||
|                 readline.parse_and_bind("bind ^I rl_complete") | ||||
|   | ||||
| @@ -322,8 +322,7 @@ class OracleParam: | ||||
|             param = Oracle_datetime.from_datetime(param) | ||||
|  | ||||
|         string_size = 0 | ||||
|         # Oracle doesn't recognize True and False correctly in Python 3. | ||||
|         # The conversion done below works both in 2 and 3. | ||||
|         # Oracle doesn't recognize True and False correctly. | ||||
|         if param is True: | ||||
|             param = 1 | ||||
|         elif param is False: | ||||
|   | ||||
| @@ -8,7 +8,6 @@ import math | ||||
| import re | ||||
| import types | ||||
| import uuid | ||||
| from importlib import import_module | ||||
|  | ||||
| from django.db import models | ||||
| from django.db.migrations.operations.base import Operation | ||||
| @@ -155,20 +154,15 @@ class FunctionTypeSerializer(BaseSerializer): | ||||
|             raise ValueError("Cannot serialize function: lambda") | ||||
|         if self.value.__module__ is None: | ||||
|             raise ValueError("Cannot serialize function %r: No module" % self.value) | ||||
|         # Python 3 is a lot easier, and only uses this branch if it's not local. | ||||
|         if getattr(self.value, "__qualname__", None) and getattr(self.value, "__module__", None): | ||||
|             if "<" not in self.value.__qualname__:  # Qualname can include <locals> | ||||
|                 return "%s.%s" % \ | ||||
|                     (self.value.__module__, self.value.__qualname__), {"import %s" % self.value.__module__} | ||||
|         # Fallback version | ||||
|  | ||||
|         module_name = self.value.__module__ | ||||
|         # Make sure it's actually there | ||||
|         module = import_module(module_name) | ||||
|         if not hasattr(module, self.value.__name__): | ||||
|             raise ValueError( | ||||
|                 "Could not find function %s in %s.\n" % (self.value.__name__, module_name) | ||||
|             ) | ||||
|         return "%s.%s" % (module_name, self.value.__name__), {"import %s" % module_name} | ||||
|  | ||||
|         if '<' not in self.value.__qualname__:  # Qualname can include <locals> | ||||
|             return '%s.%s' % (module_name, self.value.__qualname__), {'import %s' % self.value.__module__} | ||||
|  | ||||
|         raise ValueError( | ||||
|             'Could not find function %s in %s.\n' % (self.value.__name__, module_name) | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class FunctoolsPartialSerializer(BaseSerializer): | ||||
|   | ||||
| @@ -258,8 +258,7 @@ def default_test_processes(): | ||||
|     """ | ||||
|     # The current implementation of the parallel test runner requires | ||||
|     # multiprocessing to start subprocesses with fork(). | ||||
|     # On Python 3.4+: if multiprocessing.get_start_method() != 'fork': | ||||
|     if not hasattr(os, 'fork'): | ||||
|     if multiprocessing.get_start_method() != 'fork': | ||||
|         return 1 | ||||
|     try: | ||||
|         return int(os.environ['DJANGO_TEST_PROCESSES']) | ||||
|   | ||||
| @@ -303,7 +303,7 @@ Now you're ready to actually put the release out there. To do this: | ||||
|         $ pip install https://www.djangoproject.com/m/releases/$MAJOR_VERSION/Django-$RELEASE_VERSION.tar.gz | ||||
|         $ deactivate | ||||
|         $ mktmpenv | ||||
|         $ pip install https://www.djangoproject.com/m/releases/$MAJOR_VERSION/Django-$RELEASE_VERSION-py2.py3-none-any.whl | ||||
|         $ pip install https://www.djangoproject.com/m/releases/$MAJOR_VERSION/Django-$RELEASE_VERSION-py3-none-any.whl | ||||
|         $ deactivate | ||||
|  | ||||
|    This just tests that the tarballs are available (i.e. redirects are up) and | ||||
|   | ||||
| @@ -1,8 +0,0 @@ | ||||
| set OSGEO4W_ROOT=C:\OSGeo4W | ||||
| set PYTHON_ROOT=C:\Python27 | ||||
| set GDAL_DATA=%OSGEO4W_ROOT%\share\gdal | ||||
| set PROJ_LIB=%OSGEO4W_ROOT%\share\proj | ||||
| set PATH=%PATH%;%PYTHON_ROOT%;%OSGEO4W_ROOT%\bin | ||||
| reg ADD "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path /t REG_EXPAND_SZ /f /d "%PATH%" | ||||
| reg ADD "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v GDAL_DATA /t REG_EXPAND_SZ /f /d "%GDAL_DATA%" | ||||
| reg ADD "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PROJ_LIB /t REG_EXPAND_SZ /f /d "%PROJ_LIB%" | ||||
| @@ -463,7 +463,7 @@ executable with ``cmd.exe``, will set this up: | ||||
| .. code-block:: bat | ||||
|  | ||||
|     set OSGEO4W_ROOT=C:\OSGeo4W | ||||
|     set PYTHON_ROOT=C:\Python27 | ||||
|     set PYTHON_ROOT=C:\Python3X | ||||
|     set GDAL_DATA=%OSGEO4W_ROOT%\share\gdal | ||||
|     set PROJ_LIB=%OSGEO4W_ROOT%\share\proj | ||||
|     set PATH=%PATH%;%PYTHON_ROOT%;%OSGEO4W_ROOT%\bin | ||||
| @@ -471,15 +471,12 @@ executable with ``cmd.exe``, will set this up: | ||||
|     reg ADD "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v GDAL_DATA /t REG_EXPAND_SZ /f /d "%GDAL_DATA%" | ||||
|     reg ADD "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PROJ_LIB /t REG_EXPAND_SZ /f /d "%PROJ_LIB%" | ||||
|  | ||||
| For your convenience, these commands are available in the executable batch | ||||
| script, :download:`geodjango_setup.bat`. | ||||
|  | ||||
| .. note:: | ||||
|  | ||||
|     Administrator privileges are required to execute these commands. | ||||
|     To do this, right-click on :download:`geodjango_setup.bat` and select | ||||
|     :menuselection:`Run as administrator`. You need to log out and log back in again | ||||
|     for the settings to take effect. | ||||
|     To do this, create a ``bat`` script with the commands, right-click it, and | ||||
|     select :menuselection:`Run as administrator`. You need to log out and log | ||||
|     back in again for the settings to take effect. | ||||
|  | ||||
| .. note:: | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,3 @@ not_skip = __init__.py | ||||
|  | ||||
| [metadata] | ||||
| license-file = LICENSE | ||||
|  | ||||
| [wheel] | ||||
| universal = 1 | ||||
|   | ||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							| @@ -61,8 +61,6 @@ setup( | ||||
|         'License :: OSI Approved :: BSD License', | ||||
|         'Operating System :: OS Independent', | ||||
|         'Programming Language :: Python', | ||||
|         'Programming Language :: Python :: 2', | ||||
|         'Programming Language :: Python :: 2.7', | ||||
|         'Programming Language :: Python :: 3', | ||||
|         'Programming Language :: Python :: 3.4', | ||||
|         'Programming Language :: Python :: 3.5', | ||||
|   | ||||
| @@ -3,7 +3,7 @@ install some requirements and run the tests:: | ||||
|  | ||||
|     $ cd tests | ||||
|     $ pip install -e .. | ||||
|     $ pip install -r requirements/py3.txt  # or py2.txt | ||||
|     $ pip install -r requirements/py3.txt | ||||
|     $ ./runtests.py | ||||
|  | ||||
| For more information about the test suite, see | ||||
|   | ||||
| @@ -434,7 +434,6 @@ class TestUtilsHashPass(SimpleTestCase): | ||||
|  | ||||
|     def test_load_library_importerror(self): | ||||
|         PlainHasher = type('PlainHasher', (BasePasswordHasher,), {'algorithm': 'plain', 'library': 'plain'}) | ||||
|         # Python 3 adds quotes around module name | ||||
|         msg = "Couldn't load 'PlainHasher' algorithm library: No module named 'plain'" | ||||
|         with self.assertRaisesMessage(ValueError, msg): | ||||
|             PlainHasher()._load_library() | ||||
|   | ||||
							
								
								
									
										4
									
								
								tests/cache/tests.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								tests/cache/tests.py
									
									
									
									
										vendored
									
									
								
							| @@ -1912,10 +1912,6 @@ class CacheI18nTest(TestCase): | ||||
|         get_cache_data = FetchFromCacheMiddleware().process_request(request) | ||||
|         self.assertIsNone(get_cache_data) | ||||
|  | ||||
|         # This test passes on Python < 3.3 even without the corresponding code | ||||
|         # in UpdateCacheMiddleware, because pickling a StreamingHttpResponse | ||||
|         # fails (http://bugs.python.org/issue14288). LocMemCache silently | ||||
|         # swallows the exception and doesn't store the response in cache. | ||||
|         content = ['Check for cache with streaming content.'] | ||||
|         response = StreamingHttpResponse(content) | ||||
|         UpdateCacheMiddleware().process_response(request, response) | ||||
|   | ||||
| @@ -16,6 +16,7 @@ from django.core.management.commands.makemessages import \ | ||||
| from django.core.management.utils import find_command | ||||
| from django.test import SimpleTestCase, override_settings | ||||
| from django.test.utils import captured_stderr, captured_stdout | ||||
| from django.utils._os import symlinks_supported | ||||
| from django.utils.translation import TranslatorCommentWarning | ||||
|  | ||||
| from .utils import POFileAssertionMixin, RunInTmpDirMixin, copytree | ||||
| @@ -382,7 +383,10 @@ class BasicExtractorTests(ExtractorTests): | ||||
|             cmd.gettext_version | ||||
|  | ||||
|     def test_po_file_encoding_when_updating(self): | ||||
|         """Update of PO file doesn't corrupt it with non-UTF-8 encoding on Python3+Windows (#23271)""" | ||||
|         """ | ||||
|         Update of PO file doesn't corrupt it with non-UTF-8 encoding on Windows | ||||
|         (#23271). | ||||
|         """ | ||||
|         BR_PO_BASE = 'locale/pt_BR/LC_MESSAGES/django' | ||||
|         shutil.copyfile(BR_PO_BASE + '.pristine', BR_PO_BASE + '.po') | ||||
|         management.call_command('makemessages', locale=['pt_BR'], verbosity=0) | ||||
| @@ -472,29 +476,20 @@ class SymlinkExtractorTests(ExtractorTests): | ||||
|         self.symlinked_dir = os.path.join(self.test_dir, 'templates_symlinked') | ||||
|  | ||||
|     def test_symlink(self): | ||||
|         # On Python < 3.2 os.symlink() exists only on Unix | ||||
|         if hasattr(os, 'symlink'): | ||||
|             if os.path.exists(self.symlinked_dir): | ||||
|                 self.assertTrue(os.path.islink(self.symlinked_dir)) | ||||
|             else: | ||||
|                 # On Python >= 3.2) os.symlink() exists always but then can | ||||
|                 # fail at runtime when user hasn't the needed permissions on | ||||
|                 # Windows versions that support symbolink links (>= 6/Vista). | ||||
|                 # See Python issue 9333 (http://bugs.python.org/issue9333). | ||||
|                 # Skip the test in that case | ||||
|                 try: | ||||
|                     os.symlink(os.path.join(self.test_dir, 'templates'), self.symlinked_dir) | ||||
|                 except (OSError, NotImplementedError): | ||||
|                     self.skipTest("os.symlink() is available on this OS but can't be used by this user.") | ||||
|             os.chdir(self.test_dir) | ||||
|             management.call_command('makemessages', locale=[LOCALE], verbosity=0, symlinks=True) | ||||
|             self.assertTrue(os.path.exists(self.PO_FILE)) | ||||
|             with open(self.PO_FILE, 'r') as fp: | ||||
|                 po_contents = fp.read() | ||||
|                 self.assertMsgId('This literal should be included.', po_contents) | ||||
|             self.assertLocationCommentPresent(self.PO_FILE, None, 'templates_symlinked', 'test.html') | ||||
|         if os.path.exists(self.symlinked_dir): | ||||
|             self.assertTrue(os.path.islink(self.symlinked_dir)) | ||||
|         else: | ||||
|             self.skipTest("os.symlink() not available on this OS + Python version combination.") | ||||
|             if symlinks_supported(): | ||||
|                 os.symlink(os.path.join(self.test_dir, 'templates'), self.symlinked_dir) | ||||
|             else: | ||||
|                 self.skipTest("os.symlink() not available on this OS + Python version combination.") | ||||
|         os.chdir(self.test_dir) | ||||
|         management.call_command('makemessages', locale=[LOCALE], verbosity=0, symlinks=True) | ||||
|         self.assertTrue(os.path.exists(self.PO_FILE)) | ||||
|         with open(self.PO_FILE, 'r') as fp: | ||||
|             po_contents = fp.read() | ||||
|             self.assertMsgId('This literal should be included.', po_contents) | ||||
|         self.assertLocationCommentPresent(self.PO_FILE, None, 'templates_symlinked', 'test.html') | ||||
|  | ||||
|  | ||||
| class CopyPluralFormsExtractorTests(ExtractorTests): | ||||
|   | ||||
| @@ -192,7 +192,6 @@ class MailTests(HeadersCheckMixin, SimpleTestCase): | ||||
|             'Content', 'from@example.com', ['to@example.com'] | ||||
|         ) | ||||
|         message = email.message() | ||||
|         # Note that in Python 3, maximum line length has increased from 76 to 78 | ||||
|         self.assertEqual( | ||||
|             message['Subject'].encode(), | ||||
|             b'Long subject lines that get wrapped should contain a space continuation\n' | ||||
| @@ -1157,8 +1156,8 @@ class FakeSMTPServer(smtpd.SMTPServer, threading.Thread): | ||||
|  | ||||
|         if mailfrom != maddr: | ||||
|             # According to the spec, mailfrom does not necessarily match the | ||||
|             # From header - on Python 3 this is the case where the local part | ||||
|             # isn't encoded, so try to correct that. | ||||
|             # From header - this is the case where the local part isn't | ||||
|             # encoded, so try to correct that. | ||||
|             lp, domain = mailfrom.split('@', 1) | ||||
|             lp = Header(lp, 'utf-8').encode() | ||||
|             mailfrom = '@'.join([lp, domain]) | ||||
|   | ||||
| @@ -672,8 +672,8 @@ class MakeMigrationsTests(MigrationTestBase): | ||||
|         module = 'migrations.test_migrations_order' | ||||
|         with self.temporary_migration_module(module=module) as migration_dir: | ||||
|             if hasattr(importlib, 'invalidate_caches'): | ||||
|                 # Python 3 importlib caches os.listdir() on some platforms like | ||||
|                 # Mac OS X (#23850). | ||||
|                 # importlib caches os.listdir() on some platforms like Mac OS X | ||||
|                 # (#23850). | ||||
|                 importlib.invalidate_caches() | ||||
|             call_command('makemigrations', 'migrations', '--empty', '-n', 'a', '-v', '0') | ||||
|             self.assertTrue(os.path.exists(os.path.join(migration_dir, '0002_a.py'))) | ||||
| @@ -1202,8 +1202,8 @@ class MakeMigrationsTests(MigrationTestBase): | ||||
|             content = cmd("0001", migration_name_0001) | ||||
|             self.assertIn("dependencies=[\n]", content) | ||||
|  | ||||
|             # Python 3 importlib caches os.listdir() on some platforms like | ||||
|             # Mac OS X (#23850). | ||||
|             # importlib caches os.listdir() on some platforms like Mac OS X | ||||
|             # (#23850). | ||||
|             if hasattr(importlib, 'invalidate_caches'): | ||||
|                 importlib.invalidate_caches() | ||||
|  | ||||
|   | ||||
| @@ -40,6 +40,12 @@ class Money(decimal.Decimal): | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class TestModel1(object): | ||||
|     def upload_to(self): | ||||
|         return '/somewhere/dynamic/' | ||||
|     thing = models.FileField(upload_to=upload_to) | ||||
|  | ||||
|  | ||||
| class OperationWriterTests(SimpleTestCase): | ||||
|  | ||||
|     def test_empty_signature(self): | ||||
| @@ -472,21 +478,12 @@ class WriterTests(SimpleTestCase): | ||||
|         self.assertEqual(string, 'range') | ||||
|         self.assertEqual(imports, set()) | ||||
|  | ||||
|     def test_serialize_local_function_reference(self): | ||||
|         """ | ||||
|         Neither py2 or py3 can serialize a reference in a local scope. | ||||
|         """ | ||||
|         class TestModel2: | ||||
|             def upload_to(self): | ||||
|                 return "somewhere dynamic" | ||||
|             thing = models.FileField(upload_to=upload_to) | ||||
|         with self.assertRaises(ValueError): | ||||
|             self.serialize_round_trip(TestModel2.thing) | ||||
|     def test_serialize_unbound_method_reference(self): | ||||
|         """An unbound method used within a class body can be serialized.""" | ||||
|         self.serialize_round_trip(TestModel1.thing) | ||||
|  | ||||
|     def test_serialize_local_function_reference_message(self): | ||||
|         """ | ||||
|         Make sure user is seeing which module/function is the issue | ||||
|         """ | ||||
|     def test_serialize_local_function_reference(self): | ||||
|         """A reference in a local scope can't be serialized.""" | ||||
|         class TestModel2: | ||||
|             def upload_to(self): | ||||
|                 return "somewhere dynamic" | ||||
|   | ||||
| @@ -1,19 +1,10 @@ | ||||
| import gzip | ||||
| import io | ||||
|  | ||||
| from django.http import HttpRequest, HttpResponse, StreamingHttpResponse | ||||
| from django.test import SimpleTestCase | ||||
| from django.test.client import conditional_content_removal | ||||
|  | ||||
|  | ||||
| # based on Python 3.3's gzip.compress | ||||
| def gzip_compress(data): | ||||
|     buf = io.BytesIO() | ||||
|     with gzip.GzipFile(fileobj=buf, mode='wb', compresslevel=0) as f: | ||||
|         f.write(data) | ||||
|     return buf.getvalue() | ||||
|  | ||||
|  | ||||
| class ConditionalContentTests(SimpleTestCase): | ||||
|  | ||||
|     def test_conditional_content_removal(self): | ||||
| @@ -43,7 +34,7 @@ class ConditionalContentTests(SimpleTestCase): | ||||
|             self.assertEqual(b''.join(res), b'') | ||||
|  | ||||
|         # Issue #20472 | ||||
|         abc = gzip_compress(b'abc') | ||||
|         abc = gzip.compress(b'abc') | ||||
|         res = HttpResponse(abc, status=304) | ||||
|         res['Content-Encoding'] = 'gzip' | ||||
|         conditional_content_removal(req, res) | ||||
|   | ||||
							
								
								
									
										9
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								tox.ini
									
									
									
									
									
								
							| @@ -11,10 +11,7 @@ envlist = | ||||
|     docs | ||||
|     isort | ||||
|  | ||||
| # Add environments to use default python2 and python3 installations | ||||
| [testenv:py2] | ||||
| basepython = python2 | ||||
|  | ||||
| # Add environment to use the default python3 installation | ||||
| [testenv:py3] | ||||
| basepython = python3 | ||||
|  | ||||
| @@ -24,7 +21,6 @@ passenv = DJANGO_SETTINGS_MODULE PYTHONPATH HOME DISPLAY | ||||
| setenv = | ||||
|     PYTHONDONTWRITEBYTECODE=1 | ||||
| deps = | ||||
|     py{2,27}: -rtests/requirements/py2.txt | ||||
|     py{3,34,35,36}: -rtests/requirements/py3.txt | ||||
|     postgres: -rtests/requirements/postgres.txt | ||||
|     mysql: -rtests/requirements/mysql.txt | ||||
| @@ -41,8 +37,7 @@ changedir = {toxinidir} | ||||
| commands = flake8 . | ||||
|  | ||||
| [testenv:docs] | ||||
| # On OS X, as of pyenchant 1.6.6, the docs build only works under Python 2. | ||||
| basepython = python2 | ||||
| basepython = python3 | ||||
| usedevelop = false | ||||
| whitelist_externals = | ||||
|     make | ||||
|   | ||||
		Reference in New Issue
	
	Block a user