From 23b20cdb8729597c7eae8235d3ff4b5da47fe2a7 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Thu, 3 Jan 2019 16:42:58 +0100 Subject: [PATCH 01/42] Do not run py27 on tox --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 4f96fee1936..ffac6f78c68 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,10 @@ [tox] minversion=2.9.0 -envlist = py{27,36},lint,docs +envlist = py36,lint,docs skipsdist = True [travis] python = - 2.7: py27 3.6: py36, codecov [testenv] From 678027c5f6c32ef156d91f13d9327905fed014dd Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Thu, 3 Jan 2019 16:43:31 +0100 Subject: [PATCH 02/42] Do not run py27 on Travis --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6743bf289bb..bce5b2f3742 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - 2.7 - 3.6 env: - ES_VERSION=1.3.9 ES_DOWNLOAD_URL=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz From f7dea72b86f09e33014c1fcd7e113eaf875cad2a Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Thu, 3 Jan 2019 16:56:33 +0100 Subject: [PATCH 03/42] pyupgrade 1.11.0 on all code pyupgrade --py3-plus --py3-only (*.py) --- readthedocs/api/base.py | 12 ++-- readthedocs/api/utils.py | 2 +- readthedocs/builds/forms.py | 4 +- readthedocs/builds/managers.py | 2 +- readthedocs/builds/migrations/0001_initial.py | 4 +- readthedocs/builds/models.py | 18 +++--- readthedocs/builds/syncers.py | 4 +- readthedocs/builds/version_slug.py | 4 +- readthedocs/builds/views.py | 8 +-- readthedocs/config/config.py | 14 ++--- readthedocs/config/tests/test_config.py | 16 ++--- readthedocs/config/tests/test_parser.py | 18 +++--- readthedocs/config/tests/test_validation.py | 22 +++---- readthedocs/config/validation.py | 8 +-- readthedocs/core/adapters.py | 6 +- readthedocs/core/forms.py | 10 +-- .../commands/import_github_language.py | 4 +- readthedocs/core/middleware.py | 8 +-- readthedocs/core/mixins.py | 6 +- readthedocs/core/permissions.py | 2 +- readthedocs/core/resolver.py | 14 ++--- readthedocs/core/settings.py | 2 +- readthedocs/core/static.py | 2 +- readthedocs/core/symlink.py | 6 +- readthedocs/core/tests/test_signals.py | 2 +- readthedocs/core/utils/__init__.py | 2 +- readthedocs/core/utils/tasks/public.py | 2 +- readthedocs/core/utils/tasks/retrieve.py | 2 +- readthedocs/core/views/__init__.py | 4 +- readthedocs/core/views/hooks.py | 8 +-- readthedocs/core/views/serve.py | 2 +- readthedocs/doc_builder/backends/mkdocs.py | 2 +- readthedocs/doc_builder/backends/sphinx.py | 24 +++---- readthedocs/doc_builder/base.py | 2 +- readthedocs/doc_builder/environments.py | 26 ++++---- readthedocs/doc_builder/exceptions.py | 2 +- .../doc_builder/python_environments.py | 8 +-- readthedocs/gold/forms.py | 8 +-- readthedocs/gold/models.py | 2 +- readthedocs/gold/views.py | 10 +-- readthedocs/integrations/admin.py | 6 +- readthedocs/integrations/models.py | 30 ++++----- readthedocs/notifications/backends.py | 2 +- readthedocs/notifications/forms.py | 4 +- readthedocs/notifications/notification.py | 6 +- readthedocs/notifications/storages.py | 4 +- readthedocs/notifications/views.py | 8 +-- .../oauth/migrations/0003_move_github.py | 4 +- readthedocs/oauth/models.py | 2 +- readthedocs/oauth/notifications.py | 4 +- readthedocs/oauth/services/base.py | 2 +- readthedocs/oauth/services/bitbucket.py | 4 +- readthedocs/payments/forms.py | 6 +- readthedocs/payments/mixins.py | 4 +- readthedocs/projects/admin.py | 6 +- readthedocs/projects/forms.py | 62 +++++++++---------- .../migrations/0007_migrate_canonical_data.py | 4 +- .../migrations/0010_migrate_domain_data.py | 4 +- .../migrations/0016_build-queue-name.py | 2 +- readthedocs/projects/models.py | 26 ++++---- readthedocs/projects/tasks.py | 10 +-- readthedocs/projects/validators.py | 8 +-- readthedocs/projects/version_handling.py | 2 +- readthedocs/projects/views/base.py | 12 ++-- readthedocs/projects/views/mixins.py | 4 +- readthedocs/projects/views/private.py | 23 +++---- readthedocs/projects/views/public.py | 10 +-- readthedocs/redirects/models.py | 2 +- readthedocs/restapi/permissions.py | 4 +- readthedocs/restapi/serializers.py | 16 ++--- readthedocs/restapi/views/footer_views.py | 4 +- readthedocs/restapi/views/integrations.py | 10 +-- readthedocs/rtd_tests/base.py | 14 ++--- readthedocs/rtd_tests/files/conf.py | 6 +- .../fixtures/sample_repo/source/conf.py | 12 ++-- readthedocs/rtd_tests/mocks/environment.py | 2 +- readthedocs/rtd_tests/mocks/mock_api.py | 6 +- readthedocs/rtd_tests/tests/test_api.py | 47 +++++++------- readthedocs/rtd_tests/tests/test_backend.py | 26 ++++---- .../rtd_tests/tests/test_build_config.py | 2 +- readthedocs/rtd_tests/tests/test_celery.py | 4 +- .../tests/test_config_integration.py | 2 +- .../rtd_tests/tests/test_doc_building.py | 56 ++++++++--------- readthedocs/rtd_tests/tests/test_extend.py | 2 +- .../rtd_tests/tests/test_integrations.py | 32 +++++----- .../rtd_tests/tests/test_notifications.py | 4 +- .../rtd_tests/tests/test_privacy_urls.py | 10 +-- readthedocs/rtd_tests/tests/test_project.py | 2 +- .../rtd_tests/tests/test_project_forms.py | 8 +-- .../rtd_tests/tests/test_project_symlinks.py | 30 ++++----- .../rtd_tests/tests/test_project_views.py | 15 +++-- readthedocs/rtd_tests/tests/test_resolver.py | 2 +- readthedocs/rtd_tests/tests/test_urls.py | 2 +- .../tests/test_version_commit_name.py | 30 ++++----- readthedocs/search/indexes.py | 4 +- readthedocs/search/parse_json.py | 6 +- readthedocs/search/tests/test_views.py | 2 +- readthedocs/search/utils.py | 4 +- readthedocs/search/views.py | 2 +- readthedocs/settings/base.py | 2 +- readthedocs/settings/dev.py | 2 +- readthedocs/vcs_support/backends/bzr.py | 4 +- readthedocs/vcs_support/backends/git.py | 10 +-- readthedocs/vcs_support/backends/hg.py | 4 +- readthedocs/vcs_support/backends/svn.py | 6 +- readthedocs/vcs_support/base.py | 6 +- readthedocs/vcs_support/utils.py | 4 +- 107 files changed, 479 insertions(+), 488 deletions(-) diff --git a/readthedocs/api/base.py b/readthedocs/api/base.py index 73b64e431cf..8e90c279b62 100644 --- a/readthedocs/api/base.py +++ b/readthedocs/api/base.py @@ -34,7 +34,7 @@ class ProjectResource(ModelResource): users = fields.ToManyField('readthedocs.api.base.UserResource', 'users') - class Meta(object): + class Meta: include_absolute_url = True allowed_methods = ['get', 'post', 'put'] queryset = Project.objects.api() @@ -48,7 +48,7 @@ class Meta(object): def get_object_list(self, request): self._meta.queryset = Project.objects.api(user=request.user) - return super(ProjectResource, self).get_object_list(request) + return super().get_object_list(request) def dehydrate(self, bundle): bundle.data['downloads'] = bundle.obj.get_downloads() @@ -98,7 +98,7 @@ class VersionResource(ModelResource): project = fields.ForeignKey(ProjectResource, 'project', full=True) - class Meta(object): + class Meta: allowed_methods = ['get', 'put', 'post'] always_return_data = True queryset = Version.objects.api() @@ -112,7 +112,7 @@ class Meta(object): def get_object_list(self, request): self._meta.queryset = Version.objects.api(user=request.user) - return super(VersionResource, self).get_object_list(request) + return super().get_object_list(request) def build_version(self, request, **kwargs): project = get_object_or_404(Project, slug=kwargs['project_slug']) @@ -145,7 +145,7 @@ class FileResource(ModelResource): project = fields.ForeignKey(ProjectResource, 'project', full=True) - class Meta(object): + class Meta: allowed_methods = ['get', 'post'] queryset = ImportedFile.objects.all() excludes = ['md5', 'slug'] @@ -190,7 +190,7 @@ class UserResource(ModelResource): """Read-only API resource for User model.""" - class Meta(object): + class Meta: allowed_methods = ['get'] queryset = User.objects.all() fields = ['username', 'id'] diff --git a/readthedocs/api/utils.py b/readthedocs/api/utils.py index 1daa2deb963..c15cb02ec02 100644 --- a/readthedocs/api/utils.py +++ b/readthedocs/api/utils.py @@ -18,7 +18,7 @@ class PostAuthentication(BasicAuthentication): """Require HTTP Basic authentication for any method other than GET.""" def is_authenticated(self, request, **kwargs): - val = super(PostAuthentication, self).is_authenticated(request, + val = super().is_authenticated(request, **kwargs) if request.method == "GET": return True diff --git a/readthedocs/builds/forms.py b/readthedocs/builds/forms.py index 74fe0dba504..6e5e94b50ee 100644 --- a/readthedocs/builds/forms.py +++ b/readthedocs/builds/forms.py @@ -17,7 +17,7 @@ class VersionForm(forms.ModelForm): - class Meta(object): + class Meta: model = Version fields = ['active', 'privacy_level', 'tags'] @@ -38,7 +38,7 @@ def _is_default_version(self): return project.default_version == self.instance.slug def save(self, commit=True): - obj = super(VersionForm, self).save(commit=commit) + obj = super().save(commit=commit) if obj.active and not obj.built and not obj.uploaded: trigger_build(project=obj.project, version=obj) return obj diff --git a/readthedocs/builds/managers.py b/readthedocs/builds/managers.py index 9ef1b2836e5..bd9316c3ef3 100644 --- a/readthedocs/builds/managers.py +++ b/readthedocs/builds/managers.py @@ -32,7 +32,7 @@ def from_queryset(cls, queryset_class, class_name=None): VersionQuerySet, VersionQuerySet._default_class # pylint: disable=protected-access ) - return super(VersionManagerBase, cls).from_queryset(queryset_class, class_name) + return super().from_queryset(queryset_class, class_name) def create_stable(self, **kwargs): defaults = { diff --git a/readthedocs/builds/migrations/0001_initial.py b/readthedocs/builds/migrations/0001_initial.py index 32e6e0eb1fd..73ff3a648fe 100644 --- a/readthedocs/builds/migrations/0001_initial.py +++ b/readthedocs/builds/migrations/0001_initial.py @@ -77,10 +77,10 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='version', - unique_together=set([('project', 'slug')]), + unique_together={('project', 'slug')}, ), migrations.AlterIndexTogether( name='build', - index_together=set([('version', 'state', 'type')]), + index_together={('version', 'state', 'type')}, ), ] diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index 93678975232..b27b7e145af 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -114,7 +114,7 @@ class Version(models.Model): objects = VersionManager.from_queryset(VersionQuerySet)() - class Meta(object): + class Meta: unique_together = [('project', 'slug')] ordering = ['-verbose_name'] permissions = ( @@ -203,7 +203,7 @@ def get_absolute_url(self): def save(self, *args, **kwargs): # pylint: disable=arguments-differ """Add permissions to the Version for all owners on save.""" from readthedocs.projects import tasks - obj = super(Version, self).save(*args, **kwargs) + obj = super().save(*args, **kwargs) for owner in self.project.users.all(): assign('view_version', owner, self) broadcast( @@ -216,7 +216,7 @@ def delete(self, *args, **kwargs): # pylint: disable=arguments-differ broadcast(type='app', task=tasks.clear_artifacts, args=[self.get_artifact_paths()]) broadcast( type='app', task=tasks.symlink_project, args=[self.project.pk]) - super(Version, self).delete(*args, **kwargs) + super().delete(*args, **kwargs) @property def identifier_friendly(self): @@ -434,7 +434,7 @@ def __init__(self, *args, **kwargs): del kwargs[key] except KeyError: pass - super(APIVersion, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, *args, **kwargs): return 0 @@ -479,13 +479,13 @@ class Build(models.Model): CONFIG_KEY = '__config' - class Meta(object): + class Meta: ordering = ['-date'] get_latest_by = 'date' index_together = [['version', 'state', 'type']] def __init__(self, *args, **kwargs): - super(Build, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._config_changed = False @property @@ -549,7 +549,7 @@ def save(self, *args, **kwargs): # noqa self._config and self._config == previous.config): previous_pk = previous._config.get(self.CONFIG_KEY, previous.pk) self._config = {self.CONFIG_KEY: previous_pk} - super(Build, self).save(*args, **kwargs) + super().save(*args, **kwargs) self._config_changed = False def __str__(self): @@ -571,7 +571,7 @@ def finished(self): return self.state == BUILD_STATE_FINISHED -class BuildCommandResultMixin(object): +class BuildCommandResultMixin: """ Mixin for common command result methods/properties. @@ -611,7 +611,7 @@ class BuildCommandResult(BuildCommandResultMixin, models.Model): start_time = models.DateTimeField(_('Start time')) end_time = models.DateTimeField(_('End time')) - class Meta(object): + class Meta: ordering = ['start_time'] get_latest_by = 'start_time' diff --git a/readthedocs/builds/syncers.py b/readthedocs/builds/syncers.py index 0ac0c6dedf6..cf5a9a90e07 100644 --- a/readthedocs/builds/syncers.py +++ b/readthedocs/builds/syncers.py @@ -22,7 +22,7 @@ log = logging.getLogger(__name__) -class BaseSyncer(object): +class BaseSyncer: """A base object for syncers and pullers""" @@ -64,7 +64,7 @@ def copy(cls, path, target, is_file=False, **kwargs): if app_servers: log.info("Remote Copy %s to %s on %s", path, target, app_servers) for server in app_servers: - mkdir_cmd = ("ssh %s@%s mkdir -p %s" % (sync_user, server, target)) + mkdir_cmd = ("ssh {}@{} mkdir -p {}".format(sync_user, server, target)) ret = os.system(mkdir_cmd) if ret != 0: log.debug("Copy error to app servers: cmd=%s", mkdir_cmd) diff --git a/readthedocs/builds/version_slug.py b/readthedocs/builds/version_slug.py index 61622369da7..7eb71f82798 100644 --- a/readthedocs/builds/version_slug.py +++ b/readthedocs/builds/version_slug.py @@ -72,7 +72,7 @@ def __init__(self, *args, **kwargs): raise ValueError("missing 'populate_from' argument") else: self._populate_from = populate_from - super(VersionSlugField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def get_queryset(self, model_cls, slug_field): # pylint: disable=protected-access @@ -180,6 +180,6 @@ def pre_save(self, model_instance, add): return value def deconstruct(self): - name, path, args, kwargs = super(VersionSlugField, self).deconstruct() + name, path, args, kwargs = super().deconstruct() kwargs['populate_from'] = self._populate_from return name, path, args, kwargs diff --git a/readthedocs/builds/views.py b/readthedocs/builds/views.py index 7cbe310fbb5..680bd271c29 100644 --- a/readthedocs/builds/views.py +++ b/readthedocs/builds/views.py @@ -33,7 +33,7 @@ log = logging.getLogger(__name__) -class BuildBase(object): +class BuildBase: model = Build def get_queryset(self): @@ -49,7 +49,7 @@ def get_queryset(self): return queryset -class BuildTriggerMixin(object): +class BuildTriggerMixin: @method_decorator(login_required) def post(self, request, project_slug): @@ -85,7 +85,7 @@ def post(self, request, project_slug): class BuildList(BuildBase, BuildTriggerMixin, ListView): def get_context_data(self, **kwargs): - context = super(BuildList, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) active_builds = self.get_queryset().exclude(state='finished' ).values('id') @@ -104,7 +104,7 @@ class BuildDetail(BuildBase, DetailView): pk_url_kwarg = 'build_pk' def get_context_data(self, **kwargs): - context = super(BuildDetail, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['project'] = self.project return context diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index 4b7c48f5649..ac6b3db99a3 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -85,7 +85,7 @@ class ConfigError(Exception): def __init__(self, message, code): self.code = code - super(ConfigError, self).__init__(message) + super().__init__(message) class ConfigOptionNotSupportedError(ConfigError): @@ -97,7 +97,7 @@ def __init__(self, configuration): template = ( 'The "{}" configuration option is not supported in this version' ) - super(ConfigOptionNotSupportedError, self).__init__( + super().__init__( template.format(self.configuration), CONFIG_NOT_SUPPORTED ) @@ -118,10 +118,10 @@ def __init__(self, key, code, error_message, source_file=None): code=code, error=error_message, ) - super(InvalidConfig, self).__init__(message, code=code) + super().__init__(message, code=code) -class BuildConfigBase(object): +class BuildConfigBase: """ Config that handles the build of one particular documentation. @@ -229,7 +229,7 @@ def validate(self): @property def python_interpreter(self): ver = self.python_full_version - return 'python{0}'.format(ver) + return 'python{}'.format(ver) @property def python_full_version(self): @@ -481,7 +481,7 @@ def validate_python(self): # Try to convert strings to an int first, to catch '2', then # a float, to catch '2.7' version = raw_python['version'] - if isinstance(version, six.string_types): + if isinstance(version, str): try: version = int(version) except ValueError: @@ -729,7 +729,7 @@ def validate_python(self): python = {} with self.catch_validation_error('python.version'): version = self.pop_config('python.version', 3) - if isinstance(version, six.string_types): + if isinstance(version, str): try: version = int(version) except ValueError: diff --git a/readthedocs/config/tests/test_config.py b/readthedocs/config/tests/test_config.py index f623881a615..29b776d0fb5 100644 --- a/readthedocs/config/tests/test_config.py +++ b/readthedocs/config/tests/test_config.py @@ -268,7 +268,7 @@ def test_python_pip_install_default(): assert build.python.install_with_pip is False -class TestValidatePythonExtraRequirements(object): +class TestValidatePythonExtraRequirements: def test_it_defaults_to_list(self): build = get_build_config({'python': {}}, get_env_config()) @@ -302,7 +302,7 @@ def test_it_uses_validate_string(self, validate_string): validate_string.assert_any_call('tests') -class TestValidateUseSystemSitePackages(object): +class TestValidateUseSystemSitePackages: def test_it_defaults_to_false(self): build = get_build_config({'python': {}}, get_env_config()) @@ -330,7 +330,7 @@ def test_it_uses_validate_bool(self, validate_bool): validate_bool.assert_any_call('to-validate') -class TestValidateSetupPyInstall(object): +class TestValidateSetupPyInstall: def test_it_defaults_to_false(self): build = get_build_config({'python': {}}, get_env_config()) @@ -358,7 +358,7 @@ def test_it_uses_validate_bool(self, validate_bool): validate_bool.assert_any_call('to-validate') -class TestValidatePythonVersion(object): +class TestValidatePythonVersion: def test_it_defaults_to_a_valid_version(self): build = get_build_config({'python': {}}, get_env_config()) @@ -458,7 +458,7 @@ def test_it_respects_default_value(self, value): assert build.python.version == value -class TestValidateFormats(object): +class TestValidateFormats: def test_it_defaults_to_empty(self): build = get_build_config({}, get_env_config()) @@ -537,7 +537,7 @@ def test_valid_build_config(): assert build.output_base -class TestValidateBase(object): +class TestValidateBase: def test_it_validates_to_abspath(self, tmpdir): apply_fs(tmpdir, {'configs': minimal_config, 'docs': {}}) @@ -586,7 +586,7 @@ def test_it_fails_if_base_does_not_exist(self, tmpdir): assert excinfo.value.code == INVALID_PATH -class TestValidateBuild(object): +class TestValidateBuild: def test_it_fails_if_build_is_invalid_option(self, tmpdir): apply_fs(tmpdir, minimal_config) @@ -842,7 +842,7 @@ def test_as_dict(tmpdir): assert build.as_dict() == expected_dict -class TestBuildConfigV2(object): +class TestBuildConfigV2: def get_build_config( self, config, env_config=None, source_file='readthedocs.yml'): diff --git a/readthedocs/config/tests/test_parser.py b/readthedocs/config/tests/test_parser.py index 5c37c3c5cb0..4cbe457bbe8 100644 --- a/readthedocs/config/tests/test_parser.py +++ b/readthedocs/config/tests/test_parser.py @@ -8,57 +8,57 @@ def test_parse_empty_config_file(): - buf = StringIO(u'') + buf = StringIO('') with raises(ParseError): parse(buf) def test_parse_invalid_yaml(): - buf = StringIO(u'- - !asdf') + buf = StringIO('- - !asdf') with raises(ParseError): parse(buf) def test_parse_bad_type(): - buf = StringIO(u'Hello') + buf = StringIO('Hello') with raises(ParseError): parse(buf) def test_parse_single_config(): - buf = StringIO(u'base: path') + buf = StringIO('base: path') config = parse(buf) assert isinstance(config, dict) assert config['base'] == 'path' def test_parse_null_value(): - buf = StringIO(u'base: null') + buf = StringIO('base: null') config = parse(buf) assert config['base'] is None def test_parse_empty_value(): - buf = StringIO(u'base:') + buf = StringIO('base:') config = parse(buf) assert config['base'] is None def test_parse_empty_string_value(): - buf = StringIO(u'base: ""') + buf = StringIO('base: ""') config = parse(buf) assert config['base'] == '' def test_parse_empty_list(): - buf = StringIO(u'base: []') + buf = StringIO('base: []') config = parse(buf) assert config['base'] == [] def test_do_not_parse_multiple_configs_in_one_file(): buf = StringIO( - u''' + ''' base: path --- base: other_path diff --git a/readthedocs/config/tests/test_validation.py b/readthedocs/config/tests/test_validation.py index 8c2519570d2..6437acfbff2 100644 --- a/readthedocs/config/tests/test_validation.py +++ b/readthedocs/config/tests/test_validation.py @@ -14,7 +14,7 @@ validate_path, validate_string) -class TestValidateBool(object): +class TestValidateBool: def test_it_accepts_true(self): assert validate_bool(True) is True @@ -33,7 +33,7 @@ def test_it_fails_on_string(self): assert excinfo.value.code == INVALID_BOOL -class TestValidateChoice(object): +class TestValidateChoice: def test_it_accepts_valid_choice(self): result = validate_choice('choice', ('choice', 'another_choice')) @@ -49,7 +49,7 @@ def test_it_rejects_invalid_choice(self): assert excinfo.value.code == INVALID_CHOICE -class TestValidateList(object): +class TestValidateList: def test_it_accepts_list_types(self): result = validate_list(['choice', 'another_choice']) @@ -74,12 +74,12 @@ def test_it_rejects_string_types(self): assert excinfo.value.code == INVALID_LIST -class TestValidateDirectory(object): +class TestValidateDirectory: def test_it_uses_validate_path(self, tmpdir): patcher = patch('readthedocs.config.validation.validate_path') with patcher as validate_path: - path = text_type(tmpdir.mkdir('a directory')) + path = str(tmpdir.mkdir('a directory')) validate_path.return_value = path validate_directory(path, str(tmpdir)) validate_path.assert_called_with(path, str(tmpdir)) @@ -91,7 +91,7 @@ def test_it_rejects_files(self, tmpdir): assert excinfo.value.code == INVALID_DIRECTORY -class TestValidateFile(object): +class TestValidateFile: def test_it_uses_validate_path(self, tmpdir): patcher = patch('readthedocs.config.validation.validate_path') @@ -110,7 +110,7 @@ def test_it_rejects_directories(self, tmpdir): assert excinfo.value.code == INVALID_FILE -class TestValidatePath(object): +class TestValidatePath: def test_it_accepts_relative_path(self, tmpdir): tmpdir.mkdir('a directory') @@ -140,15 +140,15 @@ def test_it_rejects_non_existent_path(self, tmpdir): assert excinfo.value.code == INVALID_PATH -class TestValidateString(object): +class TestValidateString: def test_it_accepts_unicode(self): - result = validate_string(u'Unicöde') - assert isinstance(result, text_type) + result = validate_string('Unicöde') + assert isinstance(result, str) def test_it_accepts_nonunicode(self): result = validate_string('Unicode') - assert isinstance(result, text_type) + assert isinstance(result, str) def test_it_rejects_float(self): with raises(ValidationError) as excinfo: diff --git a/readthedocs/config/validation.py b/readthedocs/config/validation.py index ab9164f335e..b1b42fc6775 100644 --- a/readthedocs/config/validation.py +++ b/readthedocs/config/validation.py @@ -41,12 +41,12 @@ def __init__(self, value, code, format_kwargs=None): if format_kwargs is not None: defaults.update(format_kwargs) message = self.messages[code].format(**defaults) - super(ValidationError, self).__init__(message) + super().__init__(message) def validate_list(value): """Check if ``value`` is an iterable.""" - if isinstance(value, (dict, string_types)): + if isinstance(value, (dict, (str,))): raise ValidationError(value, INVALID_LIST) if not hasattr(value, '__iter__'): raise ValidationError(value, INVALID_LIST) @@ -113,6 +113,6 @@ def validate_path(value, base_path): def validate_string(value): """Check that ``value`` is a string type.""" - if not isinstance(value, string_types): + if not isinstance(value, str): raise ValidationError(value, INVALID_STRING) - return text_type(value) + return str(value) diff --git a/readthedocs/core/adapters.py b/readthedocs/core/adapters.py index 170f8954e26..5bfbf6d5c2e 100644 --- a/readthedocs/core/adapters.py +++ b/readthedocs/core/adapters.py @@ -26,7 +26,7 @@ def format_email_subject(self, subject): def send_mail(self, template_prefix, email, context): subject = render_to_string( - '{0}_subject.txt'.format(template_prefix), context + '{}_subject.txt'.format(template_prefix), context ) subject = " ".join(subject.splitlines()).strip() subject = self.format_email_subject(subject) @@ -47,7 +47,7 @@ def send_mail(self, template_prefix, email, context): send_email( recipient=email, subject=subject, - template='{0}_message.txt'.format(template_prefix), - template_html='{0}_message.html'.format(template_prefix), + template='{}_message.txt'.format(template_prefix), + template_html='{}_message.html'.format(template_prefix), context=context ) diff --git a/readthedocs/core/forms.py b/readthedocs/core/forms.py index bc062286547..fdc93ffe28b 100644 --- a/readthedocs/core/forms.py +++ b/readthedocs/core/forms.py @@ -21,13 +21,13 @@ class UserProfileForm(forms.ModelForm): first_name = CharField(label=_('First name'), required=False, max_length=30) last_name = CharField(label=_('Last name'), required=False, max_length=30) - class Meta(object): + class Meta: model = UserProfile # Don't allow users edit someone else's user page fields = ['first_name', 'last_name', 'homepage'] def __init__(self, *args, **kwargs): - super(UserProfileForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) try: self.fields['first_name'].initial = self.instance.user.first_name self.fields['last_name'].initial = self.instance.user.last_name @@ -37,7 +37,7 @@ def __init__(self, *args, **kwargs): def save(self, commit=True): first_name = self.cleaned_data.pop('first_name', None) last_name = self.cleaned_data.pop('last_name', None) - profile = super(UserProfileForm, self).save(commit=commit) + profile = super().save(commit=commit) if commit: user = profile.user user.first_name = first_name @@ -52,7 +52,7 @@ class UserDeleteForm(forms.ModelForm): help_text=_('Please type your username to confirm.'), ) - class Meta(object): + class Meta: model = User fields = ['username'] @@ -66,7 +66,7 @@ def clean_username(self): class UserAdvertisingForm(forms.ModelForm): - class Meta(object): + class Meta: model = UserProfile fields = ['allow_ads'] diff --git a/readthedocs/core/management/commands/import_github_language.py b/readthedocs/core/management/commands/import_github_language.py index ef2945ea557..f53a8088763 100644 --- a/readthedocs/core/management/commands/import_github_language.py +++ b/readthedocs/core/management/commands/import_github_language.py @@ -53,7 +53,7 @@ def handle(self, *args, **options): print('No GitHub repo for %s' % repo_url) continue - cache_key = '%s-%s' % (user, repo) + cache_key = '{}-{}'.format(user, repo) top_lang = cache.get(cache_key, None) if not top_lang: url = 'https://api.github.com/repos/{user}/{repo}/languages'.format( @@ -73,7 +73,7 @@ def handle(self, *args, **options): print('Cached top_lang: %s' % top_lang) if top_lang in PL_DICT: slug = PL_DICT[top_lang] - print('Setting %s to %s' % (repo_url, slug)) + print('Setting {} to {}'.format(repo_url, slug)) Project.objects.filter(pk=project.pk).update(programming_language=slug) else: print('Language unknown: %s' % top_lang) diff --git a/readthedocs/core/middleware.py b/readthedocs/core/middleware.py index 262fcb064e0..4556d92ff90 100644 --- a/readthedocs/core/middleware.py +++ b/readthedocs/core/middleware.py @@ -19,7 +19,7 @@ log = logging.getLogger(__name__) -LOG_TEMPLATE = u"(Middleware) {msg} [{host}{path}]" +LOG_TEMPLATE = "(Middleware) {msg} [{host}{path}]" SUBDOMAIN_URLCONF = getattr( settings, 'SUBDOMAIN_URLCONF', @@ -112,7 +112,7 @@ def process_request(self, request): cache.set(host, slug, 60 * 60) # Cache the slug -> host mapping permanently. log.info(LOG_TEMPLATE.format( - msg='CNAME cached: %s->%s' % (slug, host), + msg='CNAME cached: {}->{}'.format(slug, host), **log_kwargs)) request.slug = slug request.urlconf = SUBDOMAIN_URLCONF @@ -240,11 +240,11 @@ def process_request(self, request): # Hack request.session otherwise the Authentication middleware complains. request.session = {} return - super(FooterNoSessionMiddleware, self).process_request(request) + super().process_request(request) def process_response(self, request, response): for url in self.IGNORE_URLS: if (request.path_info.startswith(url) and settings.SESSION_COOKIE_NAME not in request.COOKIES): return response - return super(FooterNoSessionMiddleware, self).process_response(request, response) + return super().process_response(request, response) diff --git a/readthedocs/core/mixins.py b/readthedocs/core/mixins.py index 7655db20bd4..45bf2be75ab 100644 --- a/readthedocs/core/mixins.py +++ b/readthedocs/core/mixins.py @@ -12,13 +12,13 @@ class ListViewWithForm(ListView): """List view that also exposes a create form""" def get_context_data(self, **kwargs): - context = super(ListViewWithForm, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['form'] = self.get_form(data=None, files=None) return context -class LoginRequiredMixin(object): +class LoginRequiredMixin: @method_decorator(login_required) def dispatch(self, *args, **kwargs): - return super(LoginRequiredMixin, self).dispatch(*args, **kwargs) + return super().dispatch(*args, **kwargs) diff --git a/readthedocs/core/permissions.py b/readthedocs/core/permissions.py index c8a7fe6821b..ad2427b9b25 100644 --- a/readthedocs/core/permissions.py +++ b/readthedocs/core/permissions.py @@ -5,7 +5,7 @@ from readthedocs.core.utils.extend import SettingsOverrideObject -class AdminPermissionBase(object): +class AdminPermissionBase: @classmethod def is_admin(cls, user, project): diff --git a/readthedocs/core/resolver.py b/readthedocs/core/resolver.py index 43fce68b3f6..a17e832160c 100644 --- a/readthedocs/core/resolver.py +++ b/readthedocs/core/resolver.py @@ -10,7 +10,7 @@ from readthedocs.core.utils.extend import SettingsOverrideObject -class ResolverBase(object): +class ResolverBase: """ Read the Docs URL Resolver. @@ -59,17 +59,17 @@ def base_resolve_path(self, project_slug, filename, version_slug=None, # the path should always have a subdomain or CNAME domain # pylint: disable=unused-argument if subdomain or cname or (self._use_subdomain()): - url = u'/' + url = '/' else: - url = u'/docs/{project_slug}/' + url = '/docs/{project_slug}/' if subproject_slug: - url += u'projects/{subproject_slug}/' + url += 'projects/{subproject_slug}/' if single_version: - url += u'{filename}' + url += '{filename}' else: - url += u'{language}/{version_slug}/{filename}' + url += '{language}/{version_slug}/{filename}' return url.format( project_slug=project_slug, filename=filename, @@ -212,7 +212,7 @@ def _get_project_subdomain(self, project): if self._use_subdomain(): project = self._get_canonical_project(project) subdomain_slug = project.slug.replace('_', '-') - return "%s.%s" % (subdomain_slug, public_domain) + return "{}.{}".format(subdomain_slug, public_domain) def _get_project_custom_domain(self, project): return project.domains.filter(canonical=True).first() diff --git a/readthedocs/core/settings.py b/readthedocs/core/settings.py index d66c6d02d28..b758cb57fba 100644 --- a/readthedocs/core/settings.py +++ b/readthedocs/core/settings.py @@ -6,7 +6,7 @@ import sys -class Settings(object): +class Settings: """Class-based settings wrapper.""" diff --git a/readthedocs/core/static.py b/readthedocs/core/static.py index 89cd883877e..b7ec2b7962f 100644 --- a/readthedocs/core/static.py +++ b/readthedocs/core/static.py @@ -15,4 +15,4 @@ class SelectiveFileSystemFinder(FileSystemFinder): def list(self, ignore_patterns): ignore_patterns.extend(['epub', 'pdf', 'htmlzip', 'json', 'man']) - return super(SelectiveFileSystemFinder, self).list(ignore_patterns) + return super().list(ignore_patterns) diff --git a/readthedocs/core/symlink.py b/readthedocs/core/symlink.py index ec110cf6a15..dcaad60bc3e 100644 --- a/readthedocs/core/symlink.py +++ b/readthedocs/core/symlink.py @@ -77,7 +77,7 @@ log = logging.getLogger(__name__) -class Symlink(object): +class Symlink: """Base class for symlinking of projects.""" @@ -177,7 +177,7 @@ def symlink_cnames(self, domain=None): def remove_symlink_cname(self, domain): """Remove CNAME symlink.""" - log_msg = "Removing symlink for CNAME {0}".format(domain.domain) + log_msg = "Removing symlink for CNAME {}".format(domain.domain) log.info(constants.LOG_TEMPLATE.format(project=self.project.slug, version='', msg=log_msg)) symlink = os.path.join(self.CNAME_ROOT, domain.domain) @@ -205,7 +205,7 @@ def symlink_subprojects(self): from_to[rel.alias] = rel.child.slug subprojects.add(rel.alias) for from_slug, to_slug in list(from_to.items()): - log_msg = "Symlinking subproject: {0} -> {1}".format(from_slug, to_slug) + log_msg = "Symlinking subproject: {} -> {}".format(from_slug, to_slug) log.info(constants.LOG_TEMPLATE.format(project=self.project.slug, version='', msg=log_msg)) symlink = os.path.join(self.subproject_root, from_slug) diff --git a/readthedocs/core/tests/test_signals.py b/readthedocs/core/tests/test_signals.py index c38705a1f8c..6c906c9e87d 100644 --- a/readthedocs/core/tests/test_signals.py +++ b/readthedocs/core/tests/test_signals.py @@ -8,7 +8,7 @@ @pytest.mark.django_db -class TestProjectOrganizationSignal(object): +class TestProjectOrganizationSignal: @pytest.mark.parametrize('model_class', [Project, RemoteOrganization]) def test_project_organization_get_deleted_upon_user_delete(self, model_class): diff --git a/readthedocs/core/utils/__init__.py b/readthedocs/core/utils/__init__.py index 1927c98b74f..0b8134751a0 100644 --- a/readthedocs/core/utils/__init__.py +++ b/readthedocs/core/utils/__init__.py @@ -221,7 +221,7 @@ def slugify(value, *args, **kwargs): return value -slugify = allow_lazy(slugify, six.text_type, SafeText) +slugify = allow_lazy(slugify, str, SafeText) def safe_makedirs(directory_name): diff --git a/readthedocs/core/utils/tasks/public.py b/readthedocs/core/utils/tasks/public.py index 9fb2948ef71..1e51fc65b29 100644 --- a/readthedocs/core/utils/tasks/public.py +++ b/readthedocs/core/utils/tasks/public.py @@ -119,7 +119,7 @@ class TaskNoPermission(Exception): def __init__(self, task_id, *args, **kwargs): message = 'No permission to access task with id {id}'.format( id=task_id) - super(TaskNoPermission, self).__init__(message, *args, **kwargs) + super().__init__(message, *args, **kwargs) def get_public_task_data(request, task_id): diff --git a/readthedocs/core/utils/tasks/retrieve.py b/readthedocs/core/utils/tasks/retrieve.py index c96b7823706..0aa3e7570f0 100644 --- a/readthedocs/core/utils/tasks/retrieve.py +++ b/readthedocs/core/utils/tasks/retrieve.py @@ -16,7 +16,7 @@ class TaskNotFound(Exception): def __init__(self, task_id, *args, **kwargs): message = 'No public task found with id {id}'.format(id=task_id) - super(TaskNotFound, self).__init__(message, *args, **kwargs) + super().__init__(message, *args, **kwargs) def get_task_data(task_id): diff --git a/readthedocs/core/views/__init__.py b/readthedocs/core/views/__init__.py index 6f25e9bd92b..85aec548c42 100644 --- a/readthedocs/core/views/__init__.py +++ b/readthedocs/core/views/__init__.py @@ -35,7 +35,7 @@ class HomepageView(TemplateView): def get_context_data(self, **kwargs): """Add latest builds and featured projects.""" - context = super(HomepageView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['featured_list'] = Project.objects.filter(featured=True) context['projects_count'] = Project.objects.count() return context @@ -45,7 +45,7 @@ class SupportView(TemplateView): template_name = 'support.html' def get_context_data(self, **kwargs): - context = super(SupportView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) support_email = getattr(settings, 'SUPPORT_EMAIL', None) if not support_email: support_email = 'support@{domain}'.format( diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index 6b00b2d77ef..3156e3cd4fb 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -156,14 +156,14 @@ def _build_url(url, projects, branches): for project_slug, built in list(all_built.items()): if built: - msg = '(URL Build) Build Started: %s [%s]' % ( + msg = '(URL Build) Build Started: {} [{}]'.format( url, ' '.join(built)) log_info(project_slug, msg=msg) ret += msg for project_slug, not_building in list(all_not_building.items()): if not_building: - msg = '(URL Build) Not Building: %s [%s]' % ( + msg = '(URL Build) Not Building: {} [{}]'.format( url, ' '.join(not_building)) log_info(project_slug, msg=msg) ret += msg @@ -300,7 +300,7 @@ def bitbucket_build(request): repository = data['repository'] if not repository['absolute_url']: return HttpResponse('Invalid request', status=400) - search_url = 'bitbucket.org{0}'.format( + search_url = 'bitbucket.org{}'.format( repository['absolute_url'].rstrip('/') ) elif version == 2: @@ -309,7 +309,7 @@ def bitbucket_build(request): for change in changes] if not data['repository']['full_name']: return HttpResponse('Invalid request', status=400) - search_url = 'bitbucket.org/{0}'.format( + search_url = 'bitbucket.org/{}'.format( data['repository']['full_name'] ) except (TypeError, ValueError, KeyError): diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 36510440ae3..d663dc79180 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -116,7 +116,7 @@ def redirect_page_with_filename(request, project, subproject, filename): # pyli def _serve_401(request, project): res = render(request, '401.html') res.status_code = 401 - log.debug('Unauthorized access to {0} documentation'.format(project.slug)) + log.debug('Unauthorized access to {} documentation'.format(project.slug)) return res diff --git a/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py index 6119673b8fa..eeb06379c13 100644 --- a/readthedocs/doc_builder/backends/mkdocs.py +++ b/readthedocs/doc_builder/backends/mkdocs.py @@ -44,7 +44,7 @@ class BaseMkdocs(BaseBuilder): DEFAULT_THEME_NAME = 'mkdocs' def __init__(self, *args, **kwargs): - super(BaseMkdocs, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.old_artifact_path = os.path.join( self.version.project.checkout_path(self.version.slug), self.build_dir) diff --git a/readthedocs/doc_builder/backends/sphinx.py b/readthedocs/doc_builder/backends/sphinx.py index 1d280b8a318..4f2f7b33829 100644 --- a/readthedocs/doc_builder/backends/sphinx.py +++ b/readthedocs/doc_builder/backends/sphinx.py @@ -40,7 +40,7 @@ class BaseSphinx(BaseBuilder): """The parent for most sphinx builders.""" def __init__(self, *args, **kwargs): - super(BaseSphinx, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.config_file = self.config.sphinx.configuration try: if not self.config_file: @@ -172,13 +172,9 @@ def append_conf(self, **__): outfile = codecs.open(self.config_file, encoding='utf-8', mode='a') except (ProjectConfigurationError, IOError): trace = sys.exc_info()[2] - six.reraise( - ProjectConfigurationError, - ProjectConfigurationError( + raise ProjectConfigurationError( ProjectConfigurationError.NOT_FOUND - ), - trace - ) + ).with_traceback(trace) # Append config to project conf file tmpl = template_loader.get_template('doc_builder/conf.py.tmpl') @@ -234,11 +230,11 @@ class HtmlBuilder(BaseSphinx): sphinx_build_dir = '_build/html' def __init__(self, *args, **kwargs): - super(HtmlBuilder, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.sphinx_builder = 'readthedocs' def move(self, **__): - super(HtmlBuilder, self).move() + super().move() # Copy JSON artifacts to its own directory # to keep compatibility with the older builder. json_path = os.path.abspath( @@ -265,7 +261,7 @@ class HtmlDirBuilder(HtmlBuilder): type = 'sphinx_htmldir' def __init__(self, *args, **kwargs): - super(HtmlDirBuilder, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.sphinx_builder = 'readthedocsdirhtml' @@ -273,7 +269,7 @@ class SingleHtmlBuilder(HtmlBuilder): type = 'sphinx_singlehtml' def __init__(self, *args, **kwargs): - super(SingleHtmlBuilder, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.sphinx_builder = 'readthedocssinglehtml' @@ -338,7 +334,7 @@ class LatexBuildCommand(BuildCommand): """Ignore LaTeX exit code if there was file output.""" def run(self): - super(LatexBuildCommand, self).run() + super().run() # Force LaTeX exit code to be a little more optimistic. If LaTeX # reports an output file, let's just assume we're fine. if PDF_RE.search(self.output): @@ -350,7 +346,7 @@ class DockerLatexBuildCommand(DockerBuildCommand): """Ignore LaTeX exit code if there was file output.""" def run(self): - super(DockerLatexBuildCommand, self).run() + super().run() # Force LaTeX exit code to be a little more optimistic. If LaTeX # reports an output file, let's just assume we're fine. if PDF_RE.search(self.output): @@ -395,7 +391,7 @@ def build(self): ['pdflatex', '-interaction=nonstopmode', tex_file] for tex_file in tex_files] # yapf: disable makeindex_cmds = [ - ['makeindex', '-s', 'python.ist', '{0}.idx'.format( + ['makeindex', '-s', 'python.ist', '{}.idx'.format( os.path.splitext(os.path.relpath(tex_file, latex_cwd))[0])] for tex_file in tex_files] # yapf: disable diff --git a/readthedocs/doc_builder/base.py b/readthedocs/doc_builder/base.py index 83aac0da617..c382fefc393 100644 --- a/readthedocs/doc_builder/base.py +++ b/readthedocs/doc_builder/base.py @@ -26,7 +26,7 @@ def decorator(*args, **kw): return decorator -class BaseBuilder(object): +class BaseBuilder: """ The Base for all Builders. Defines the API for subclasses. diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py index b8980ec6971..10ba06f5c60 100644 --- a/readthedocs/doc_builder/environments.py +++ b/readthedocs/doc_builder/environments.py @@ -123,7 +123,7 @@ def __init__(self, command, cwd=None, shell=False, environment=None, def __str__(self): # TODO do we want to expose the full command here? - output = u'' + output = '' if self.output is not None: output = self.output.encode('utf-8') return '\n'.join([self.get_command(), output]) @@ -178,7 +178,7 @@ def run(self): if self.input_data is not None: cmd_input = self.input_data - if isinstance(cmd_input, six.string_types): + if isinstance(cmd_input, str): cmd_input_bytes = cmd_input.encode('utf-8') else: cmd_input_bytes = cmd_input @@ -336,7 +336,7 @@ def get_wrapped_command(self): r"\[\\\]\^\`\{\|\}\~])") prefix = '' if self.bin_path: - prefix += 'PATH={0}:$PATH '.format(self.bin_path) + prefix += 'PATH={}:$PATH '.format(self.bin_path) return ("/bin/sh -c 'cd {cwd} && {prefix}{cmd}'" .format( cwd=self.cwd, @@ -345,7 +345,7 @@ def get_wrapped_command(self): for part in self.command])))) -class BaseEnvironment(object): +class BaseEnvironment: """ Base environment class. @@ -418,10 +418,10 @@ def run_command_class( self.commands.append(build_cmd) if build_cmd.failed: - msg = u'Command {cmd} failed'.format(cmd=build_cmd.get_command()) + msg = 'Command {cmd} failed'.format(cmd=build_cmd.get_command()) if build_cmd.output: - msg += u':\n{out}'.format(out=build_cmd.output) + msg += ':\n{out}'.format(out=build_cmd.output) if warn_only: log.warning(LOG_TEMPLATE.format( @@ -486,7 +486,7 @@ class BuildEnvironment(BaseEnvironment): def __init__(self, project=None, version=None, build=None, config=None, record=True, environment=None, update_on_success=True): - super(BuildEnvironment, self).__init__(project, environment) + super().__init__(project, environment) self.version = version self.build = build self.config = config @@ -562,13 +562,13 @@ def run(self, *cmd, **kwargs): kwargs.update({ 'build_env': self, }) - return super(BuildEnvironment, self).run(*cmd, **kwargs) + return super().run(*cmd, **kwargs) def run_command_class(self, *cmd, **kwargs): # pylint: disable=arguments-differ kwargs.update({ 'build_env': self, }) - return super(BuildEnvironment, self).run_command_class(*cmd, **kwargs) + return super().run_command_class(*cmd, **kwargs) @property def successful(self): @@ -662,7 +662,7 @@ def update_build(self, state=None): # Attempt to stop unicode errors on build reporting for key, val in list(self.build.items()): - if isinstance(val, six.binary_type): + if isinstance(val, bytes): self.build[key] = val.decode('utf-8', 'ignore') # We are selective about when we update the build object here @@ -716,7 +716,7 @@ class DockerBuildEnvironment(BuildEnvironment): def __init__(self, *args, **kwargs): self.docker_socket = kwargs.pop('docker_socket', DOCKER_SOCKET) - super(DockerBuildEnvironment, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.client = None self.container = None self.container_name = slugify( @@ -760,7 +760,7 @@ def __enter__(self): project=self.project.slug, version=self.version.slug, msg=( - 'Removing stale container {0}' + 'Removing stale container {}' .format(self.container_id) ), ) @@ -824,7 +824,7 @@ def __exit__(self, exc_type, exc_value, tb): if not all([exc_type, exc_value, tb]): exc_type, exc_value, tb = sys.exc_info() - return super(DockerBuildEnvironment, self).__exit__(exc_type, exc_value, tb) + return super().__exit__(exc_type, exc_value, tb) def get_client(self): """Create Docker client connection.""" diff --git a/readthedocs/doc_builder/exceptions.py b/readthedocs/doc_builder/exceptions.py index ce2ce3d844b..80582595b00 100644 --- a/readthedocs/doc_builder/exceptions.py +++ b/readthedocs/doc_builder/exceptions.py @@ -13,7 +13,7 @@ class BuildEnvironmentException(Exception): def __init__(self, message=None, **kwargs): self.status_code = kwargs.pop('status_code', None) or self.status_code or 1 message = message or self.get_default_message() - super(BuildEnvironmentException, self).__init__(message, **kwargs) + super().__init__(message, **kwargs) def get_default_message(self): return self.message diff --git a/readthedocs/doc_builder/python_environments.py b/readthedocs/doc_builder/python_environments.py index fa9bd9f8bf3..7d85a275f83 100644 --- a/readthedocs/doc_builder/python_environments.py +++ b/readthedocs/doc_builder/python_environments.py @@ -29,7 +29,7 @@ log = logging.getLogger(__name__) -class PythonEnvironment(object): +class PythonEnvironment: """An isolated environment into which Python packages can be installed.""" @@ -73,7 +73,7 @@ def install_package(self): getattr(settings, 'USE_PIP_INSTALL', False)): extra_req_param = '' if self.config.python.extra_requirements: - extra_req_param = '[{0}]'.format( + extra_req_param = '[{}]'.format( ','.join(self.config.python.extra_requirements) ) self.build_env.run( @@ -83,7 +83,7 @@ def install_package(self): '--ignore-installed', '--cache-dir', self.project.pip_cache_path, - '.{0}'.format(extra_req_param), + '.{}'.format(extra_req_param), cwd=self.checkout_path, bin_path=self.venv_bin(), ) @@ -195,7 +195,7 @@ def save_environment_json(self): with open(self.environment_json_path(), 'w') as fpath: # Compatibility for Py2 and Py3. ``io.TextIOWrapper`` expects # unicode but ``json.dumps`` returns str in Py2. - fpath.write(six.text_type(json.dumps(data))) + fpath.write(str(json.dumps(data))) class Virtualenv(PythonEnvironment): diff --git a/readthedocs/gold/forms.py b/readthedocs/gold/forms.py index 949ab9c4c61..fead5e26b11 100644 --- a/readthedocs/gold/forms.py +++ b/readthedocs/gold/forms.py @@ -24,7 +24,7 @@ class GoldSubscriptionForm(StripeResourceMixin, StripeModelForm): :py:class:`StripeResourceMixin` for common operations against the Stripe API. """ - class Meta(object): + class Meta: model = GoldUser fields = ['last_4_card_digits', 'level'] @@ -44,7 +44,7 @@ class Meta(object): def clean(self): self.instance.user = self.customer - return super(GoldSubscriptionForm, self).clean() + return super().clean() def validate_stripe(self): subscription = self.get_subscription() @@ -97,7 +97,7 @@ class GoldProjectForm(forms.Form): def __init__(self, active_user, *args, **kwargs): self.user = kwargs.pop('user', None) self.projects = kwargs.pop('projects', None) - super(GoldProjectForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields['project'].choices = self.generate_choices(active_user) def generate_choices(self, active_user): @@ -114,7 +114,7 @@ def clean_project(self): return project_slug def clean(self): - cleaned_data = super(GoldProjectForm, self).clean() + cleaned_data = super().clean() if self.projects.count() < self.user.num_supported_projects: return cleaned_data diff --git a/readthedocs/gold/models.py b/readthedocs/gold/models.py index b87e0f72345..278dc60227e 100644 --- a/readthedocs/gold/models.py +++ b/readthedocs/gold/models.py @@ -62,7 +62,7 @@ class GoldUser(models.Model): business_vat_id = models.CharField(max_length=128, null=True, blank=True) def __str__(self): - return 'Gold Level %s for %s' % (self.level, self.user) + return 'Gold Level {} for {}'.format(self.level, self.user) @property def num_supported_projects(self): diff --git a/readthedocs/gold/views.py b/readthedocs/gold/views.py index a0122d8286c..7e2677e2a25 100644 --- a/readthedocs/gold/views.py +++ b/readthedocs/gold/views.py @@ -43,16 +43,16 @@ def get_object(self): def get_form(self, data=None, files=None, **kwargs): """Pass in copy of POST data to avoid read only QueryDicts.""" kwargs['customer'] = self.request.user - return super(GoldSubscriptionMixin, self).get_form(data, files, **kwargs) + return super().get_form(data, files, **kwargs) def get_success_url(self, **__): return reverse_lazy('gold_detail') def get_template_names(self): - return ('gold/subscription{0}.html'.format(self.template_name_suffix)) + return ('gold/subscription{}.html'.format(self.template_name_suffix)) def get_context_data(self, **kwargs): - context = super(GoldSubscriptionMixin, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) domains = Domain.objects.filter(project__users=self.request.user) context['domains'] = domains return context @@ -70,7 +70,7 @@ def get(self, request, *args, **kwargs): If there is a gold subscription instance, then we show the normal detail page, otherwise show the registration form """ - resp = super(DetailGoldSubscription, self).get(request, *args, **kwargs) + resp = super().get(request, *args, **kwargs) if self.object is None: return HttpResponseRedirect(reverse('gold_subscription')) return resp @@ -94,7 +94,7 @@ class DeleteGoldSubscription(GoldSubscriptionMixin, DeleteView): def post(self, request, *args, **kwargs): """Add success message to delete post.""" - resp = super(DeleteGoldSubscription, self).post(request, *args, **kwargs) + resp = super().post(request, *args, **kwargs) success_message = self.get_success_message({}) if success_message: messages.success(self.request, success_message) diff --git a/readthedocs/integrations/admin.py b/readthedocs/integrations/admin.py index cbeabea02e6..5fabe23be91 100644 --- a/readthedocs/integrations/admin.py +++ b/readthedocs/integrations/admin.py @@ -18,7 +18,7 @@ def inner(_, obj): if include_styles: formatter = HtmlFormatter(style='colorful') styles = '' - return mark_safe('
{1}
{2}'.format( + return mark_safe('
{}
{}'.format( 'float: left;', obj.formatted_json(field), styles, @@ -96,11 +96,11 @@ def exchanges(self, obj): JSONField doesn't do well with fieldsets for whatever reason. This is just to link to the exchanges. """ - url = urls.reverse('admin:{0}_{1}_changelist'.format( + url = urls.reverse('admin:{}_{}_changelist'.format( HttpExchange._meta.app_label, # pylint: disable=protected-access HttpExchange._meta.model_name, # pylint: disable=protected-access )) - return mark_safe('{3} HTTP transactions'.format( + return mark_safe('{} HTTP transactions'.format( url, 'integrations', obj.pk, diff --git a/readthedocs/integrations/models.py b/readthedocs/integrations/models.py index 7514699cef6..032c16c28a4 100644 --- a/readthedocs/integrations/models.py +++ b/readthedocs/integrations/models.py @@ -73,11 +73,11 @@ def from_exchange(self, req, resp, related_object, payload=None): # headers. HTTP headers are prefixed with `HTTP_`, which we remove, # and because the keys are all uppercase, we'll normalize them to # title case-y hyphen separated values. - request_headers = dict( - (key[5:].title().replace('_', '-'), str(val)) + request_headers = { + key[5:].title().replace('_', '-'): str(val) for (key, val) in list(req.META.items()) - if key.startswith('HTTP_'), - ) # yapf: disable + if key.startswith('HTTP_') + } # yapf: disable request_headers['Content-Type'] = req.content_type # Remove unwanted headers @@ -146,7 +146,7 @@ class HttpExchange(models.Model): objects = HttpExchangeManager() - class Meta(object): + class Meta: ordering = ['-date'] def __str__(self): @@ -191,11 +191,11 @@ class IntegrationQuerySet(models.QuerySet): def _get_subclass(self, integration_type): # Build a mapping of integration_type -> class dynamically - class_map = dict( - (cls.integration_type_id, cls) + class_map = { + cls.integration_type_id: cls for cls in self.model.__subclasses__() - if hasattr(cls, 'integration_type_id'), - ) # yapf: disable + if hasattr(cls, 'integration_type_id') + } # yapf: disable return class_map.get(integration_type) def _get_subclass_replacement(self, original): @@ -215,7 +215,7 @@ def _get_subclass_replacement(self, original): return new def get(self, *args, **kwargs): - original = super(IntegrationQuerySet, self).get(*args, **kwargs) + original = super().get(*args, **kwargs) return self._get_subclass_replacement(original) def subclass(self, instance): @@ -285,7 +285,7 @@ class GitHubWebhook(Integration): integration_type_id = Integration.GITHUB_WEBHOOK has_sync = True - class Meta(object): + class Meta: proxy = True @property @@ -301,7 +301,7 @@ class BitbucketWebhook(Integration): integration_type_id = Integration.BITBUCKET_WEBHOOK has_sync = True - class Meta(object): + class Meta: proxy = True @property @@ -317,7 +317,7 @@ class GitLabWebhook(Integration): integration_type_id = Integration.GITLAB_WEBHOOK has_sync = True - class Meta(object): + class Meta: proxy = True @property @@ -333,7 +333,7 @@ class GenericAPIWebhook(Integration): integration_type_id = Integration.API_WEBHOOK has_sync = False - class Meta(object): + class Meta: proxy = True def save(self, *args, **kwargs): # pylint: disable=arguments-differ @@ -346,7 +346,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ if token is None: token = default_token() self.provider_data = {'token': token} - super(GenericAPIWebhook, self).save(*args, **kwargs) + super().save(*args, **kwargs) @property def token(self): diff --git a/readthedocs/notifications/backends.py b/readthedocs/notifications/backends.py index 28529f1f1a6..e2552c1bdaa 100644 --- a/readthedocs/notifications/backends.py +++ b/readthedocs/notifications/backends.py @@ -39,7 +39,7 @@ def send_notification(request, notification): backend.send(notification) -class Backend(object): +class Backend: def __init__(self, request): self.request = request diff --git a/readthedocs/notifications/forms.py b/readthedocs/notifications/forms.py index b65c1c15e76..58924308515 100644 --- a/readthedocs/notifications/forms.py +++ b/readthedocs/notifications/forms.py @@ -29,12 +29,12 @@ class SendNotificationForm(forms.Form): def __init__(self, *args, **kwargs): self.notification_classes = kwargs.pop('notification_classes', []) - super(SendNotificationForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields['source'].choices = [(cls.name, cls.name) for cls in self.notification_classes] def clean_source(self): """Get the source class from the class name.""" source = self.cleaned_data['source'] - classes = dict((cls.name, cls) for cls in self.notification_classes) + classes = {cls.name: cls for cls in self.notification_classes} return classes.get(source, None) diff --git a/readthedocs/notifications/notification.py b/readthedocs/notifications/notification.py index d2300e1f4f0..8329bb91b26 100644 --- a/readthedocs/notifications/notification.py +++ b/readthedocs/notifications/notification.py @@ -17,7 +17,7 @@ log = logging.getLogger(__name__) -class Notification(object): +class Notification: """ An unsent notification linked to an object. @@ -134,10 +134,10 @@ def __init__( self.success = success self.reason = reason self.extra_context = extra_context or {} - super(SiteNotification, self).__init__(context_object, request, user) + super().__init__(context_object, request, user) def get_context_data(self): - context = super(SiteNotification, self).get_context_data() + context = super().get_context_data() context.update(self.extra_context) return context diff --git a/readthedocs/notifications/storages.py b/readthedocs/notifications/storages.py index 1755db952e0..9a98ef7632f 100644 --- a/readthedocs/notifications/storages.py +++ b/readthedocs/notifications/storages.py @@ -49,7 +49,7 @@ class FallbackUniqueStorage(FallbackStorage): def _get(self, *args, **kwargs): # The database backend for persistent messages doesn't support setting # messages with ``mark_safe``, therefore, we need to do it broadly here. - messages, all_ret = (super(FallbackUniqueStorage, self) + messages, all_ret = (super() ._get(self, *args, **kwargs)) safe_messages = [] @@ -75,7 +75,7 @@ def add(self, level, message, extra_tags='', *args, **kwargs): # noqa read=False)) if persist_messages.exists(): return - super(FallbackUniqueStorage, self).add(level, message, extra_tags, + super().add(level, message, extra_tags, *args, **kwargs) diff --git a/readthedocs/notifications/views.py b/readthedocs/notifications/views.py index 0ba04fae0c9..2cbdb0959d4 100644 --- a/readthedocs/notifications/views.py +++ b/readthedocs/notifications/views.py @@ -35,7 +35,7 @@ def get_form_kwargs(self): The admin posts to this view initially, so detect the send button on form post variables. Drop additional fields if we see the send button. """ - kwargs = super(SendNotificationView, self).get_form_kwargs() + kwargs = super().get_form_kwargs() kwargs['notification_classes'] = self.notification_classes if 'send' not in self.request.POST: kwargs.pop('data', None) @@ -44,7 +44,7 @@ def get_form_kwargs(self): def get_initial(self): """Add selected ids to initial form data.""" - initial = super(SendNotificationView, self).get_initial() + initial = super().get_initial() initial['_selected_action'] = self.request.POST.getlist( admin.ACTION_CHECKBOX_NAME) return initial @@ -63,7 +63,7 @@ def form_valid(self, form): if count == 0: self.message_user("No recipients to send to", level=messages.ERROR) else: - self.message_user("Queued {0} messages".format(count)) + self.message_user("Queued {} messages".format(count)) return HttpResponseRedirect(self.request.get_full_path()) def get_object_recipients(self, obj): @@ -89,7 +89,7 @@ def get_queryset(self): def get_context_data(self, **kwargs): """Return queryset in context.""" - context = super(SendNotificationView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) recipients = [] for obj in self.get_queryset().all(): recipients.extend(self.get_object_recipients(obj)) diff --git a/readthedocs/oauth/migrations/0003_move_github.py b/readthedocs/oauth/migrations/0003_move_github.py index 40fd4c562e0..7ff1dbc8e10 100644 --- a/readthedocs/oauth/migrations/0003_move_github.py +++ b/readthedocs/oauth/migrations/0003_move_github.py @@ -143,9 +143,9 @@ def forwards_move_repos(apps, schema_editor): new_repo.private = data.get('is_private', False) new_repo.json = json.dumps(data) - clone_urls = dict((location['name'], location['href']) + clone_urls = {location['name']: location['href'] for location - in data.get('links', {}).get('clone', {})) + in data.get('links', {}).get('clone', {})} if new_repo.private: new_repo.clone_url = clone_urls.get('ssh', project.git_url) else: diff --git a/readthedocs/oauth/models.py b/readthedocs/oauth/models.py index b93f71b9faa..54a02191fe7 100644 --- a/readthedocs/oauth/models.py +++ b/readthedocs/oauth/models.py @@ -141,7 +141,7 @@ class RemoteRepository(models.Model): objects = RemoteRepositoryQuerySet.as_manager() - class Meta(object): + class Meta: ordering = ['organization__name', 'name'] verbose_name_plural = 'remote repositories' diff --git a/readthedocs/oauth/notifications.py b/readthedocs/oauth/notifications.py index fc9aefc0c3b..41b9c78ce6e 100644 --- a/readthedocs/oauth/notifications.py +++ b/readthedocs/oauth/notifications.py @@ -22,7 +22,7 @@ class AttachWebhookNotification(SiteNotification): } def get_context_data(self): - context = super(AttachWebhookNotification, self).get_context_data() + context = super().get_context_data() project = self.extra_context.get('project') context.update({ 'url_connect_account': reverse( @@ -44,7 +44,7 @@ class InvalidProjectWebhookNotification(SiteNotification): "See the project integrations for more information.") # noqa def get_context_data(self): - context = super(InvalidProjectWebhookNotification, self).get_context_data() + context = super().get_context_data() context.update({ 'url_integrations': reverse( 'projects_integrations', diff --git a/readthedocs/oauth/services/base.py b/readthedocs/oauth/services/base.py index 93064779ef9..87086a03149 100644 --- a/readthedocs/oauth/services/base.py +++ b/readthedocs/oauth/services/base.py @@ -19,7 +19,7 @@ log = logging.getLogger(__name__) -class Service(object): +class Service: """ Service mapping for local accounts. diff --git a/readthedocs/oauth/services/bitbucket.py b/readthedocs/oauth/services/bitbucket.py index 0ce2f55a21c..2586de5697b 100644 --- a/readthedocs/oauth/services/bitbucket.py +++ b/readthedocs/oauth/services/bitbucket.py @@ -119,8 +119,8 @@ def create_repository(self, fields, privacy=None, organization=None): repo.private = fields['is_private'] # Default to HTTPS, use SSH for private repositories - clone_urls = dict((u['name'], u['href']) - for u in fields['links']['clone']) + clone_urls = {u['name']: u['href'] + for u in fields['links']['clone']} repo.clone_url = self.https_url_pattern.sub( 'https://bitbucket.org/', clone_urls.get('https') diff --git a/readthedocs/payments/forms.py b/readthedocs/payments/forms.py index eae6e88dcb6..89b2c1dc782 100644 --- a/readthedocs/payments/forms.py +++ b/readthedocs/payments/forms.py @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) -class StripeResourceMixin(object): +class StripeResourceMixin: """Stripe actions for resources, available as a Form mixin class.""" @@ -117,7 +117,7 @@ class StripeModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.customer = kwargs.pop('customer', None) - super(StripeModelForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def validate_stripe(self): """ @@ -147,7 +147,7 @@ def clean(self): raise any issues as validation errors. This is required because part of Stripe's validation happens on the API call to establish a subscription. """ - cleaned_data = super(StripeModelForm, self).clean() + cleaned_data = super().clean() # Form isn't valid, no need to try to associate a card now if not self.is_valid(): diff --git a/readthedocs/payments/mixins.py b/readthedocs/payments/mixins.py index 0219da08098..d90fc169925 100644 --- a/readthedocs/payments/mixins.py +++ b/readthedocs/payments/mixins.py @@ -5,12 +5,12 @@ from django.conf import settings -class StripeMixin(object): +class StripeMixin: """Adds Stripe publishable key to the context data.""" def get_context_data(self, **kwargs): - context = super(StripeMixin, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['stripe_publishable'] = settings.STRIPE_PUBLISHABLE return context diff --git a/readthedocs/projects/admin.py b/readthedocs/projects/admin.py index d55954fdbcf..48109f7eafb 100644 --- a/readthedocs/projects/admin.py +++ b/readthedocs/projects/admin.py @@ -156,12 +156,12 @@ def ban_owner(self, request, queryset): total += count else: messages.add_message(request, messages.ERROR, - 'Project has multiple owners: {0}'.format(project)) + 'Project has multiple owners: {}'.format(project)) if total == 0: messages.add_message(request, messages.ERROR, 'No users banned') else: messages.add_message(request, messages.INFO, - 'Banned {0} user(s)'.format(total)) + 'Banned {} user(s)'.format(total)) ban_owner.short_description = 'Ban project owner' @@ -178,7 +178,7 @@ def delete_selected_and_artifacts(self, request, queryset): return delete_selected(self, request, queryset) def get_actions(self, request): - actions = super(ProjectAdmin, self).get_actions(request) + actions = super().get_actions(request) actions['delete_selected'] = ( self.__class__.delete_selected_and_artifacts, 'delete_selected', diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index 5f7f407f1ab..c447ab40c0b 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -52,17 +52,17 @@ class ProjectForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) - super(ProjectForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, commit=True): - project = super(ProjectForm, self).save(commit) + project = super().save(commit) if commit: if self.user and not project.users.filter(pk=self.user.pk).exists(): project.users.add(self.user) return project -class ProjectTriggerBuildMixin(object): +class ProjectTriggerBuildMixin: """ Mixin to trigger build on form save. @@ -73,7 +73,7 @@ class ProjectTriggerBuildMixin(object): def save(self, commit=True): """Trigger build on commit save.""" - project = super(ProjectTriggerBuildMixin, self).save(commit) + project = super().save(commit) if commit: trigger_build(project=project) return project @@ -90,7 +90,7 @@ class ProjectBasicsForm(ProjectForm): """Form for basic project fields.""" - class Meta(object): + class Meta: model = Project fields = ('name', 'repo', 'repo_type') @@ -101,7 +101,7 @@ class Meta(object): def __init__(self, *args, **kwargs): show_advanced = kwargs.pop('show_advanced', False) - super(ProjectBasicsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if show_advanced: self.fields['advanced'] = forms.BooleanField( required=False, @@ -112,7 +112,7 @@ def __init__(self, *args, **kwargs): def save(self, commit=True): """Add remote repository relationship to the project instance.""" - instance = super(ProjectBasicsForm, self).save(commit) + instance = super().save(commit) remote_repo = self.cleaned_data.get('remote_repository', None) if remote_repo: if commit: @@ -165,7 +165,7 @@ class ProjectExtraForm(ProjectForm): """Additional project information form.""" - class Meta(object): + class Meta: model = Project fields = ( 'description', @@ -203,7 +203,7 @@ class ProjectAdvancedForm(ProjectTriggerBuildMixin, ProjectForm): 'environment.'), ) - class Meta(object): + class Meta: model = Project fields = ( # Standard build edits @@ -227,7 +227,7 @@ class Meta(object): ) def __init__(self, *args, **kwargs): - super(ProjectAdvancedForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) default_choice = (None, '-' * 9) all_versions = self.instance.versions.values_list( @@ -255,7 +255,7 @@ def clean_conf_py_file(self): class UpdateProjectForm(ProjectTriggerBuildMixin, ProjectBasicsForm, ProjectExtraForm): - class Meta(object): + class Meta: model = Project fields = ( # Basics @@ -308,14 +308,14 @@ class ProjectRelationshipBaseForm(forms.ModelForm): parent = forms.CharField(widget=forms.HiddenInput(), required=False) - class Meta(object): + class Meta: model = ProjectRelationship fields = '__all__' def __init__(self, *args, **kwargs): self.project = kwargs.pop('project') self.user = kwargs.pop('user') - super(ProjectRelationshipBaseForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Don't display the update form with an editable child, as it will be # filtered out from the queryset anyways. if hasattr(self, 'instance') and self.instance.pk is not None: @@ -362,11 +362,11 @@ class DualCheckboxWidget(forms.CheckboxInput): """Checkbox with link to the version's built documentation.""" def __init__(self, version, attrs=None, check_test=bool): - super(DualCheckboxWidget, self).__init__(attrs, check_test) + super().__init__(attrs, check_test) self.version = version def render(self, name, value, attrs=None, renderer=None): - checkbox = super(DualCheckboxWidget, self).render(name, value, attrs, renderer) + checkbox = super().render(name, value, attrs, renderer) icon = self.render_icon() return mark_safe('{}{}'.format(checkbox, icon)) @@ -459,7 +459,7 @@ class BaseUploadHTMLForm(forms.Form): def __init__(self, *args, **kwargs): self.request = kwargs.pop('request', None) - super(BaseUploadHTMLForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean(self): version_slug = self.cleaned_data['version'] @@ -499,7 +499,7 @@ class UserForm(forms.Form): def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(UserForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean_user(self): name = self.cleaned_data['user'] @@ -525,7 +525,7 @@ class EmailHookForm(forms.Form): def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(EmailHookForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean_email(self): self.email = EmailHook.objects.get_or_create( @@ -543,7 +543,7 @@ class WebHookForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(WebHookForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, commit=True): self.webhook = WebHook.objects.get_or_create( @@ -565,7 +565,7 @@ class TranslationBaseForm(forms.Form): def __init__(self, *args, **kwargs): self.parent = kwargs.pop('parent', None) self.user = kwargs.pop('user') - super(TranslationBaseForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields['project'].choices = self.get_choices() def get_choices(self): @@ -646,13 +646,13 @@ class RedirectForm(forms.ModelForm): """Form for project redirects.""" - class Meta(object): + class Meta: model = Redirect fields = ['redirect_type', 'from_url', 'to_url'] def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(RedirectForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, **_): # pylint: disable=arguments-differ # TODO this should respect the unused argument `commit`. It's not clear @@ -673,13 +673,13 @@ class DomainBaseForm(forms.ModelForm): project = forms.CharField(widget=forms.HiddenInput(), required=False) - class Meta(object): + class Meta: model = Domain exclude = ['machine', 'cname', 'count'] # pylint: disable=modelform-uses-exclude def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(DomainBaseForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean_project(self): return self.project @@ -717,13 +717,13 @@ class IntegrationForm(forms.ModelForm): project = forms.CharField(widget=forms.HiddenInput(), required=False) - class Meta(object): + class Meta: model = Integration exclude = ['provider_data', 'exchanges'] # pylint: disable=modelform-uses-exclude def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(IntegrationForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Alter the integration type choices to only provider webhooks self.fields['integration_type'].choices = Integration.WEBHOOK_INTEGRATIONS # yapf: disable # noqa @@ -732,20 +732,20 @@ def clean_project(self): def save(self, commit=True): self.instance = Integration.objects.subclass(self.instance) - return super(IntegrationForm, self).save(commit) + return super().save(commit) class ProjectAdvertisingForm(forms.ModelForm): """Project promotion opt-out form.""" - class Meta(object): + class Meta: model = Project fields = ['allow_promos'] def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(ProjectAdvertisingForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) class FeatureForm(forms.ModelForm): @@ -760,10 +760,10 @@ class FeatureForm(forms.ModelForm): feature_id = forms.ChoiceField() - class Meta(object): + class Meta: model = Feature fields = ['projects', 'feature_id', 'default_true'] def __init__(self, *args, **kwargs): - super(FeatureForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields['feature_id'].choices = Feature.FEATURES diff --git a/readthedocs/projects/migrations/0007_migrate_canonical_data.py b/readthedocs/projects/migrations/0007_migrate_canonical_data.py index 743d6a145cb..8c842a6a0b9 100644 --- a/readthedocs/projects/migrations/0007_migrate_canonical_data.py +++ b/readthedocs/projects/migrations/0007_migrate_canonical_data.py @@ -16,10 +16,10 @@ def migrate_canonical(apps, schema_editor): url=project.canonical_url, canonical=True, ) - print(u"Added {url} to {project}".format(url=project.canonical_url, project=project.name)) + print("Added {url} to {project}".format(url=project.canonical_url, project=project.name)) except Exception as e: print(e) - print(u"Failed adding {url} to {project}".format( + print("Failed adding {url} to {project}".format( url=project.canonical_url, project=project.name )) diff --git a/readthedocs/projects/migrations/0010_migrate_domain_data.py b/readthedocs/projects/migrations/0010_migrate_domain_data.py index ef60b2a00d1..636f200bfe7 100644 --- a/readthedocs/projects/migrations/0010_migrate_domain_data.py +++ b/readthedocs/projects/migrations/0010_migrate_domain_data.py @@ -24,10 +24,10 @@ def migrate_url(apps, schema_editor): try: domain.domain = domain_string domain.save() - print(u"Added {domain} from {url}".format(url=domain.url, domain=domain_string)) + print("Added {domain} from {url}".format(url=domain.url, domain=domain_string)) except Exception as e: print(e) - print(u"Failed {domain} from {url}".format(url=domain.url, domain=domain_string)) + print("Failed {domain} from {url}".format(url=domain.url, domain=domain_string)) dms = Domain.objects.filter(domain=domain_string).order_by('-count') if dms.count() > 1: diff --git a/readthedocs/projects/migrations/0016_build-queue-name.py b/readthedocs/projects/migrations/0016_build-queue-name.py index 46833ada78c..d4e2201b230 100644 --- a/readthedocs/projects/migrations/0016_build-queue-name.py +++ b/readthedocs/projects/migrations/0016_build-queue-name.py @@ -11,7 +11,7 @@ def update_build_queue(apps, schema): for project in Project.objects.all(): if project.build_queue is not None: if not project.build_queue.startswith('build-'): - project.build_queue = 'build-{0}'.format(project.build_queue) + project.build_queue = 'build-{}'.format(project.build_queue) project.save() diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index d02ecfc76c0..c87295b864d 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -56,12 +56,12 @@ class ProjectRelationship(models.Model): objects = ChildRelatedProjectQuerySet.as_manager() def __str__(self): - return '%s -> %s' % (self.parent, self.child) + return '{} -> {}'.format(self.parent, self.child) def save(self, *args, **kwargs): # pylint: disable=arguments-differ if not self.alias: self.alias = self.child.slug - super(ProjectRelationship, self).save(*args, **kwargs) + super().save(*args, **kwargs) # HACK def get_absolute_url(self): @@ -251,7 +251,7 @@ class Project(models.Model): objects = ProjectQuerySet.as_manager() all_objects = models.Manager() - class Meta(object): + class Meta: ordering = ('slug',) permissions = ( # Translators: Permission around whether a user can view the @@ -275,7 +275,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ # documentation type to Sphinx for rST and Mkdocs for markdown. # It now just forces Sphinx, due to markdown support. self.documentation_type = 'sphinx' - super(Project, self).save(*args, **kwargs) + super().save(*args, **kwargs) for owner in self.users.all(): assign('view_project', owner, self) try: @@ -379,7 +379,7 @@ def get_production_media_path(self, type_, version_slug, include_file=True): settings.PRODUCTION_MEDIA_ARTIFACTS, type_, self.slug, version_slug) if include_file: path = os.path.join( - path, '%s.%s' % (self.slug, type_.replace('htmlzip', 'zip'))) + path, '{}.{}'.format(self.slug, type_.replace('htmlzip', 'zip'))) return path def get_production_media_url(self, type_, version_slug, full_path=True): @@ -393,7 +393,7 @@ def get_production_media_url(self, type_, version_slug, full_path=True): except NoReverseMatch: return '' if full_path: - path = '//%s%s' % (settings.PRODUCTION_DOMAIN, path) + path = '//{}{}'.format(settings.PRODUCTION_DOMAIN, path) return path def subdomain(self): @@ -856,7 +856,7 @@ def __init__(self, *args, **kwargs): del kwargs[key] except KeyError: pass - super(APIProject, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Overwrite the database property with the value from the API self.ad_free = ad_free @@ -903,7 +903,7 @@ def get_absolute_url(self): return resolve(project=self.project, version_slug=self.version.slug, filename=self.path) def __str__(self): - return '%s: %s' % (self.name, self.project) + return '{}: {}'.format(self.name, self.project) class Notification(models.Model): @@ -911,7 +911,7 @@ class Notification(models.Model): related_name='%(class)s_notifications') objects = RelatedProjectQuerySet.as_manager() - class Meta(object): + class Meta: abstract = True @@ -962,7 +962,7 @@ class Domain(models.Model): objects = RelatedProjectQuerySet.as_manager() - class Meta(object): + class Meta: ordering = ('-canonical', '-machine', 'domain') def __str__(self): @@ -975,7 +975,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ self.domain = parsed.netloc else: self.domain = parsed.path - super(Domain, self).save(*args, **kwargs) + super().save(*args, **kwargs) broadcast(type='app', task=tasks.symlink_domain, args=[self.project.pk, self.pk],) @@ -983,7 +983,7 @@ def delete(self, *args, **kwargs): # pylint: disable=arguments-differ from readthedocs.projects import tasks broadcast(type='app', task=tasks.symlink_domain, args=[self.project.pk, self.pk, True],) - super(Domain, self).delete(*args, **kwargs) + super().delete(*args, **kwargs) @python_2_unicode_compatible @@ -1053,7 +1053,7 @@ def add_features(sender, **kwargs): objects = FeatureQuerySet.as_manager() def __str__(self): - return '{0} feature'.format( + return '{} feature'.format( self.get_feature_display(), ) diff --git a/readthedocs/projects/tasks.py b/readthedocs/projects/tasks.py index bcc81913753..10b6660deca 100644 --- a/readthedocs/projects/tasks.py +++ b/readthedocs/projects/tasks.py @@ -85,7 +85,7 @@ log = logging.getLogger(__name__) -class SyncRepositoryMixin(object): +class SyncRepositoryMixin: """Mixin that handles the VCS sync/update.""" @@ -1217,11 +1217,11 @@ def email_notification(version, build, email): 'pk': build.pk, 'error': build.error, }, - 'build_url': 'https://{0}{1}'.format( + 'build_url': 'https://{}{}'.format( getattr(settings, 'PRODUCTION_DOMAIN', 'readthedocs.org'), build.get_absolute_url(), ), - 'unsub_url': 'https://{0}{1}'.format( + 'unsub_url': 'https://{}{}'.format( getattr(settings, 'PRODUCTION_DOMAIN', 'readthedocs.org'), reverse('projects_notifications', args=[version.project.slug]), ), @@ -1320,7 +1320,7 @@ def update_static_metadata(project_pk, path=None): LOG_TEMPLATE.format( project=project.slug, version='', - msg='Cannot write to metadata.json: {0}'.format(e), + msg='Cannot write to metadata.json: {}'.format(e), ) ) @@ -1396,7 +1396,7 @@ def finish_inactive_builds(): build.error = _( 'This build was terminated due to inactivity. If you ' 'continue to encounter this error, file a support ' - 'request with and reference this build id ({0}).'.format(build.pk), + 'request with and reference this build id ({}).'.format(build.pk), ) build.save() builds_finished += 1 diff --git a/readthedocs/projects/validators.py b/readthedocs/projects/validators.py index 01d350a43aa..f2b7b468746 100644 --- a/readthedocs/projects/validators.py +++ b/readthedocs/projects/validators.py @@ -28,13 +28,13 @@ class DomainNameValidator(RegexValidator): def __init__(self, accept_idna=True, **kwargs): message = kwargs.get('message') self.accept_idna = accept_idna - super(DomainNameValidator, self).__init__(**kwargs) + super().__init__(**kwargs) if not self.accept_idna and message is None: self.message = _('Enter a valid domain name value') def __call__(self, value): try: - super(DomainNameValidator, self).__call__(value) + super().__call__(value) except ValidationError as exc: if not self.accept_idna: raise @@ -44,14 +44,14 @@ def __call__(self, value): idnavalue = value.encode('idna') except UnicodeError: raise exc - super(DomainNameValidator, self).__call__(idnavalue) + super().__call__(idnavalue) validate_domain_name = DomainNameValidator() @deconstructible -class RepositoryURLValidator(object): +class RepositoryURLValidator: disallow_relative_url = True diff --git a/readthedocs/projects/version_handling.py b/readthedocs/projects/version_handling.py index 2a7cffa5bdd..d3ef3d3a108 100644 --- a/readthedocs/projects/version_handling.py +++ b/readthedocs/projects/version_handling.py @@ -25,7 +25,7 @@ def parse_version_failsafe(version_string): :rtype: packaging.version.Version """ - if not isinstance(version_string, six.text_type): + if not isinstance(version_string, str): uni_version = version_string.decode('utf-8') else: uni_version = version_string diff --git a/readthedocs/projects/views/base.py b/readthedocs/projects/views/base.py index 2bdb17fdfb2..b6e8e387dd1 100644 --- a/readthedocs/projects/views/base.py +++ b/readthedocs/projects/views/base.py @@ -21,13 +21,13 @@ USER_MATURITY_DAYS = getattr(settings, 'USER_MATURITY_DAYS', 7) -class ProjectOnboardMixin(object): +class ProjectOnboardMixin: """Add project onboard context data to project object views.""" def get_context_data(self, **kwargs): """Add onboard context data.""" - context = super(ProjectOnboardMixin, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) # If more than 1 project, don't show onboarding at all. This could # change in the future, to onboard each user maybe? if Project.objects.for_admin_user(self.request.user).count() > 1: @@ -51,7 +51,7 @@ def get_context_data(self, **kwargs): # Mixins -class ProjectAdminMixin(object): +class ProjectAdminMixin: """ Mixin class that provides project sublevel objects. @@ -78,7 +78,7 @@ def get_project(self): def get_context_data(self, **kwargs): """Add project to context data.""" - context = super(ProjectAdminMixin, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['project'] = self.get_project() return context @@ -88,7 +88,7 @@ def get_form(self, data=None, files=None, **kwargs): return self.form_class(data, files, **kwargs) -class ProjectSpamMixin(object): +class ProjectSpamMixin: """Protects POST views from spammers.""" @@ -100,7 +100,7 @@ def post(self, request, *args, **kwargs): ) return HttpResponseRedirect(self.get_failure_url()) try: - return super(ProjectSpamMixin, self).post(request, *args, **kwargs) + return super().post(request, *args, **kwargs) except ProjectSpamError: date_maturity = timezone.now() - timedelta(days=USER_MATURITY_DAYS) if request.user.date_joined > date_maturity: diff --git a/readthedocs/projects/views/mixins.py b/readthedocs/projects/views/mixins.py index 50e03beb475..1692b0e461e 100644 --- a/readthedocs/projects/views/mixins.py +++ b/readthedocs/projects/views/mixins.py @@ -7,7 +7,7 @@ from readthedocs.projects.models import Project -class ProjectRelationMixin(object): +class ProjectRelationMixin: """ Mixin class for constructing model views for project dashboard. @@ -41,6 +41,6 @@ def get_queryset(self): ) def get_context_data(self, **kwargs): - context = super(ProjectRelationMixin, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context[self.project_context_object_name] = self.get_project() return context diff --git a/readthedocs/projects/views/private.py b/readthedocs/projects/views/private.py index 330c22b5b7d..22b355f94a2 100644 --- a/readthedocs/projects/views/private.py +++ b/readthedocs/projects/views/private.py @@ -84,7 +84,7 @@ def get_queryset(self): return Project.objects.dashboard(self.request.user) def get_context_data(self, **kwargs): - context = super(ProjectDashboard, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) return context @@ -250,7 +250,7 @@ def get_form_kwargs(self, step=None): def get_template_names(self): """Return template names based on step name.""" - return 'projects/import_{0}.html'.format(self.steps.current) + return 'projects/import_{}.html'.format(self.steps.current) def done(self, form_list, **kwargs): """ @@ -351,7 +351,7 @@ def get(self, request, *args, **kwargs): def get_form_data(self): """Get form data to post to import form.""" return { - 'name': '{0}-demo'.format(self.request.user.username), + 'name': '{}-demo'.format(self.request.user.username), 'repo_type': 'git', 'repo': 'https://github.com/readthedocs/template.git', } @@ -405,7 +405,7 @@ def get(self, request, *args, **kwargs): ) )), # yapf: disable ) - return super(ImportView, self).get(request, *args, **kwargs) + return super().get(request, *args, **kwargs) def post(self, request, *args, **kwargs): initial_data = {} @@ -419,7 +419,7 @@ def post(self, request, *args, **kwargs): return self.wizard_class.as_view(initial_dict=initial_data)(request) def get_context_data(self, **kwargs): - context = super(ImportView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['view_csrf_token'] = get_token(self.request) context['has_connected_accounts'] = SocialAccount.objects.filter( user=self.request.user, @@ -440,10 +440,7 @@ def get_queryset(self): def get_form(self, data=None, files=None, **kwargs): kwargs['user'] = self.request.user - return super( - ProjectRelationshipMixin, - self, - ).get_form(data, files, **kwargs) + return super().get_form(data, files, **kwargs) def form_valid(self, form): broadcast( @@ -451,7 +448,7 @@ def form_valid(self, form): task=tasks.symlink_subproject, args=[self.get_project().pk], ) - return super(ProjectRelationshipMixin, self).form_valid(form) + return super().form_valid(form) def get_success_url(self): return reverse('projects_subprojects', args=[self.get_project().slug]) @@ -460,7 +457,7 @@ def get_success_url(self): class ProjectRelationshipList(ProjectRelationshipMixin, ListView): def get_context_data(self, **kwargs): - ctx = super(ProjectRelationshipList, self).get_context_data(**kwargs) + ctx = super().get_context_data(**kwargs) ctx['superproject'] = self.project.superprojects.first() return ctx @@ -775,7 +772,7 @@ def get_success_url(self): def get_template_names(self): if self.template_name: return self.template_name - return 'projects/integration{0}.html'.format(self.template_name_suffix) + return 'projects/integration{}.html'.format(self.template_name_suffix) class IntegrationList(IntegrationMixin, ListView): @@ -810,7 +807,7 @@ def get_template_names(self): integration_type = self.get_integration().integration_type suffix = self.SUFFIX_MAP.get(integration_type, integration_type) return ( - 'projects/integration_{0}{1}.html' + 'projects/integration_{}{}.html' .format(suffix, self.template_name_suffix) ) diff --git a/readthedocs/projects/views/public.py b/readthedocs/projects/views/public.py index 90f5ef978b1..028571a293d 100644 --- a/readthedocs/projects/views/public.py +++ b/readthedocs/projects/views/public.py @@ -62,7 +62,7 @@ def get_queryset(self): return queryset def get_context_data(self, **kwargs): - context = super(ProjectIndex, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['person'] = self.user context['tag'] = self.tag return context @@ -82,7 +82,7 @@ def get_queryset(self): return Project.objects.protected(self.request.user) def get_context_data(self, **kwargs): - context = super(ProjectDetailView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) project = self.get_object() context['versions'] = Version.objects.public( @@ -94,7 +94,7 @@ def get_context_data(self, **kwargs): version_slug = project.get_default_version() - context['badge_url'] = '%s://%s%s?version=%s' % ( + context['badge_url'] = '{}://{}{}?version={}'.format( protocol, settings.PRODUCTION_DOMAIN, reverse('project_badge', args=[project.slug]), @@ -192,7 +192,7 @@ def project_download_media(request, project_slug, type_, version_slug): if privacy_level == 'public' or settings.DEBUG: path = os.path.join( settings.MEDIA_URL, type_, project_slug, version_slug, - '%s.%s' % (project_slug, type_.replace('htmlzip', 'zip'))) + '{}.{}'.format(project_slug, type_.replace('htmlzip', 'zip'))) return HttpResponseRedirect(path) # Get relative media path @@ -207,7 +207,7 @@ def project_download_media(request, project_slug, type_, version_slug): response['Content-Encoding'] = encoding response['X-Accel-Redirect'] = path # Include version in filename; this fixes a long-standing bug - filename = '%s-%s.%s' % ( + filename = '{}-{}.{}'.format( project_slug, version_slug, path.split('.')[-1]) response['Content-Disposition'] = 'filename=%s' % filename return response diff --git a/readthedocs/redirects/models.py b/readthedocs/redirects/models.py index cbd080ca28c..f8eddb16cc4 100644 --- a/readthedocs/redirects/models.py +++ b/readthedocs/redirects/models.py @@ -76,7 +76,7 @@ class Redirect(models.Model): objects = RedirectManager() - class Meta(object): + class Meta: verbose_name = _('redirect') verbose_name_plural = _('redirects') ordering = ('-update_dt',) diff --git a/readthedocs/restapi/permissions.py b/readthedocs/restapi/permissions.py index 615872d307e..a9d7aa8d92f 100644 --- a/readthedocs/restapi/permissions.py +++ b/readthedocs/restapi/permissions.py @@ -51,11 +51,11 @@ class APIPermission(permissions.IsAuthenticatedOrReadOnly): """ def has_permission(self, request, view): - has_perm = super(APIPermission, self).has_permission(request, view) + has_perm = super().has_permission(request, view) return has_perm or (request.user and request.user.is_staff) def has_object_permission(self, request, view, obj): - has_perm = super(APIPermission, self).has_object_permission( + has_perm = super().has_object_permission( request, view, obj) return has_perm or (request.user and request.user.is_staff) diff --git a/readthedocs/restapi/serializers.py b/readthedocs/restapi/serializers.py index 1d473a9eada..ba6a88ddc90 100644 --- a/readthedocs/restapi/serializers.py +++ b/readthedocs/restapi/serializers.py @@ -15,7 +15,7 @@ class ProjectSerializer(serializers.ModelSerializer): canonical_url = serializers.ReadOnlyField(source='get_docs_url') - class Meta(object): + class Meta: model = Project fields = ( 'id', @@ -70,7 +70,7 @@ class VersionSerializer(serializers.ModelSerializer): project = ProjectSerializer() downloads = serializers.DictField(source='get_downloads', read_only=True) - class Meta(object): + class Meta: model = Version fields = ( 'id', @@ -93,7 +93,7 @@ class BuildCommandSerializer(serializers.ModelSerializer): run_time = serializers.ReadOnlyField() - class Meta(object): + class Meta: model = BuildCommandResult exclude = ('') @@ -111,7 +111,7 @@ class BuildSerializer(serializers.ModelSerializer): # https://github.com/dmkoch/django-jsonfield/issues/188#issuecomment-300439829 config = serializers.JSONField(required=False) - class Meta(object): + class Meta: model = Build # `_config` should be excluded to avoid conflicts with `config` exclude = ('builder', '_config') @@ -136,7 +136,7 @@ class SearchIndexSerializer(serializers.Serializer): class DomainSerializer(serializers.ModelSerializer): project = ProjectSerializer() - class Meta(object): + class Meta: model = Domain fields = ( 'id', @@ -150,7 +150,7 @@ class Meta(object): class RemoteOrganizationSerializer(serializers.ModelSerializer): - class Meta(object): + class Meta: model = RemoteOrganization exclude = ('json', 'email', 'users') @@ -162,7 +162,7 @@ class RemoteRepositorySerializer(serializers.ModelSerializer): organization = RemoteOrganizationSerializer() matches = serializers.SerializerMethodField() - class Meta(object): + class Meta: model = RemoteRepository exclude = ('json', 'users') @@ -184,7 +184,7 @@ class SocialAccountSerializer(serializers.ModelSerializer): avatar_url = serializers.URLField(source='get_avatar_url') provider = ProviderSerializer(source='get_provider') - class Meta(object): + class Meta: model = SocialAccount exclude = ('extra_data',) diff --git a/readthedocs/restapi/views/footer_views.py b/readthedocs/restapi/views/footer_views.py index f09ceaff527..ad5ec7028b6 100644 --- a/readthedocs/restapi/views/footer_views.py +++ b/readthedocs/restapi/views/footer_views.py @@ -37,8 +37,8 @@ def get_version_compare_data(project, base_version=None): highest_version_obj, highest_version_comparable = highest_version( versions_qs) ret_val = { - 'project': six.text_type(highest_version_obj), - 'version': six.text_type(highest_version_comparable), + 'project': str(highest_version_obj), + 'version': str(highest_version_comparable), 'is_highest': True, } if highest_version_obj: diff --git a/readthedocs/restapi/views/integrations.py b/readthedocs/restapi/views/integrations.py index 9a27e2ef9af..4950518408b 100644 --- a/readthedocs/restapi/views/integrations.py +++ b/readthedocs/restapi/views/integrations.py @@ -43,7 +43,7 @@ BITBUCKET_PUSH = 'repo:push' -class WebhookMixin(object): +class WebhookMixin: """Base class for Webhook mixins.""" @@ -75,7 +75,7 @@ def get_project(self, **kwargs): def finalize_response(self, req, *args, **kwargs): """If the project was set on POST, store an HTTP exchange.""" - resp = super(WebhookMixin, self).finalize_response(req, *args, **kwargs) + resp = super().finalize_response(req, *args, **kwargs) if hasattr(self, 'project') and self.project: HttpExchange.objects.from_exchange( req, @@ -180,7 +180,7 @@ def get_data(self): return json.loads(self.request.data['payload']) except (ValueError, KeyError): pass - return super(GitHubWebhookView, self).get_data() + return super().get_data() def handle_webhook(self): # Get event and trigger other webhook events @@ -350,7 +350,7 @@ class IsAuthenticatedOrHasToken(permissions.IsAuthenticated): """ def has_permission(self, request, view): - has_perm = (super(IsAuthenticatedOrHasToken, self) + has_perm = (super() .has_permission(request, view)) return has_perm or 'token' in request.data @@ -404,7 +404,7 @@ def handle_webhook(self): 'branches', [self.project.get_default_branch()] ) - if isinstance(branches, six.string_types): + if isinstance(branches, str): branches = [branches] return self.get_response_push(self.project, branches) except TypeError: diff --git a/readthedocs/rtd_tests/base.py b/readthedocs/rtd_tests/base.py index f11ec5a49b4..c6813f22f8f 100644 --- a/readthedocs/rtd_tests/base.py +++ b/readthedocs/rtd_tests/base.py @@ -42,7 +42,7 @@ class MockBuildTestCase(TestCase): pass -class RequestFactoryTestMixin(object): +class RequestFactoryTestMixin: """ Adds helper methods for testing with :py:class:`RequestFactory` @@ -107,14 +107,14 @@ def post_step(self, step, **kwargs): if not self.url: raise Exception('Missing wizard URL') try: - data = dict( - ('{0}-{1}'.format(step, k), v) + data = { + '{}-{}'.format(step, k): v for (k, v) in list(self.step_data[step].items()) - ) + } except KeyError: pass # Update with prefixed step data - data['{0}-current_step'.format(self.wizard_class_slug)] = step + data['{}-current_step'.format(self.wizard_class_slug)] = step view = self.wizard_class.as_view() req = self.request(self.url, method='post', data=data, **kwargs) resp = view(req) @@ -146,7 +146,7 @@ def assertWizardResponse(self, response, step=None): # noqa response.render() self.assertContains( response, - u'name="{0}-current_step"'.format(self.wizard_class_slug) + 'name="{}-current_step"'.format(self.wizard_class_slug) ) # We use camelCase on purpose here to conform with unittest's naming @@ -170,4 +170,4 @@ def assertWizardFailure(self, response, field, match=None): # noqa self.assertIn(field, response.context_data['wizard']['form'].errors) if match is not None: error = response.context_data['wizard']['form'].errors[field] - self.assertRegex(six.text_type(error), match) # noqa + self.assertRegex(str(error), match) # noqa diff --git a/readthedocs/rtd_tests/files/conf.py b/readthedocs/rtd_tests/files/conf.py index 4007dcfab19..11f872849dd 100644 --- a/readthedocs/rtd_tests/files/conf.py +++ b/readthedocs/rtd_tests/files/conf.py @@ -13,7 +13,7 @@ '.md': CommonMarkParser, } master_doc = 'index' -project = u'Pip' +project = 'Pip' copyright = str(datetime.now().year) version = '0.8.1' release = '0.8.1' @@ -23,6 +23,6 @@ html_theme = 'sphinx_rtd_theme' file_insertion_enabled = False latex_documents = [ - ('index', 'pip.tex', u'Pip Documentation', - u'', 'manual'), + ('index', 'pip.tex', 'Pip Documentation', + '', 'manual'), ] diff --git a/readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py b/readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py index c6c9fcb64db..68b327ca8af 100644 --- a/readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py +++ b/readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py @@ -41,8 +41,8 @@ master_doc = 'index' # General information about the project. -project = u'sample' -copyright = u'2011, Dan' +project = 'sample' +copyright = '2011, Dan' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -180,8 +180,8 @@ # Grouping the document tree into LaTeX files. List of tuples (source start # file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'sample.tex', u'sample Documentation', - u'Dan', 'manual'), + ('index', 'sample.tex', 'sample Documentation', + 'Dan', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -213,6 +213,6 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'sample', u'sample Documentation', - [u'Dan'], 1) + ('index', 'sample', 'sample Documentation', + ['Dan'], 1) ] diff --git a/readthedocs/rtd_tests/mocks/environment.py b/readthedocs/rtd_tests/mocks/environment.py index 4b963b769ba..3b2a28f49bd 100644 --- a/readthedocs/rtd_tests/mocks/environment.py +++ b/readthedocs/rtd_tests/mocks/environment.py @@ -4,7 +4,7 @@ import mock -class EnvironmentMockGroup(object): +class EnvironmentMockGroup: """Mock out necessary environment pieces""" diff --git a/readthedocs/rtd_tests/mocks/mock_api.py b/readthedocs/rtd_tests/mocks/mock_api.py index 84c40d7c4d1..68d1e544357 100644 --- a/readthedocs/rtd_tests/mocks/mock_api.py +++ b/readthedocs/rtd_tests/mocks/mock_api.py @@ -8,7 +8,7 @@ # Mock tastypi API. -class ProjectData(object): +class ProjectData: def get(self): return dict() @@ -18,7 +18,7 @@ def put(self, x=None): def mock_version(repo): """Construct and return a class implementing the Version interface.""" - class MockVersion(object): + class MockVersion: def __init__(self, x=None): pass @@ -71,7 +71,7 @@ def get(self, **kwargs): return MockVersion -class MockApi(object): +class MockApi: def __init__(self, repo): self.version = mock_version(repo) diff --git a/readthedocs/rtd_tests/tests/test_api.py b/readthedocs/rtd_tests/tests/test_api.py index 707a4da79c3..af63a2378e7 100644 --- a/readthedocs/rtd_tests/tests/test_api.py +++ b/readthedocs/rtd_tests/tests/test_api.py @@ -359,7 +359,7 @@ def test_update_build_without_permission(self): client.force_authenticate(user=api_user) build = get(Build, project_id=1, version_id=1, state='cloning') resp = client.put( - '/api/v2/build/{0}/'.format(build.pk), + '/api/v2/build/{}/'.format(build.pk), { 'project': 1, 'version': 1, @@ -381,11 +381,11 @@ def test_make_build_protected_fields(self): api_user = get(User, staff=False, password='test') client.force_authenticate(user=api_user) - resp = client.get('/api/v2/build/{0}/'.format(build.pk), format='json') + resp = client.get('/api/v2/build/{}/'.format(build.pk), format='json') self.assertEqual(resp.status_code, 200) client.force_authenticate(user=User.objects.get(username='super')) - resp = client.get('/api/v2/build/{0}/'.format(build.pk), format='json') + resp = client.get('/api/v2/build/{}/'.format(build.pk), format='json') self.assertEqual(resp.status_code, 200) self.assertIn('builder', resp.data) @@ -443,7 +443,7 @@ def test_get_raw_log_success(self): api_user = get(User, user='test', password='test') client.force_authenticate(user=api_user) - resp = client.get('/api/v2/build/{0}.txt'.format(build.pk)) + resp = client.get('/api/v2/build/{}.txt'.format(build.pk)) self.assertEqual(resp.status_code, 200) self.assertIn('Read the Docs build information', resp.content.decode()) @@ -487,7 +487,7 @@ def test_get_raw_log_building(self): api_user = get(User, user='test', password='test') client.force_authenticate(user=api_user) - resp = client.get('/api/v2/build/{0}.txt'.format(build.pk)) + resp = client.get('/api/v2/build/{}.txt'.format(build.pk)) self.assertEqual(resp.status_code, 200) self.assertIn('Read the Docs build information', resp.content.decode()) @@ -530,7 +530,7 @@ def test_get_raw_log_failure(self): api_user = get(User, user='test', password='test') client.force_authenticate(user=api_user) - resp = client.get('/api/v2/build/{0}.txt'.format(build.pk)) + resp = client.get('/api/v2/build/{}.txt'.format(build.pk)) self.assertEqual(resp.status_code, 200) self.assertIn('Read the Docs build information', resp.content.decode()) @@ -556,7 +556,7 @@ def test_get_invalid_raw_log(self): api_user = get(User, user='test', password='test') client.force_authenticate(user=api_user) - resp = client.get('/api/v2/build/{0}.txt'.format(404)) + resp = client.get('/api/v2/build/{}.txt'.format(404)) self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) def test_build_filter_by_commit(self): @@ -661,8 +661,7 @@ def test_project_features(self): resp = client.get('/api/v2/project/%s/' % (project.pk)) self.assertEqual(resp.status_code, 200) self.assertIn('features', resp.data) - six.assertCountEqual( - self, + self.assertCountEqual( resp.data['features'], [feature1.feature_id, feature2.feature_id], ) @@ -868,7 +867,7 @@ def test_webhook_skipped_project(self, trigger_build): self.project.save() response = client.post( - '/api/v2/webhook/github/{0}/'.format( + '/api/v2/webhook/github/{}/'.format( self.project.slug, ), self.github_payload, @@ -883,7 +882,7 @@ def test_github_webhook_for_branches(self, trigger_build): client = APIClient() client.post( - '/api/v2/webhook/github/{0}/'.format(self.project.slug), + '/api/v2/webhook/github/{}/'.format(self.project.slug), {'ref': 'master'}, format='json', ) @@ -891,7 +890,7 @@ def test_github_webhook_for_branches(self, trigger_build): [mock.call(force=True, version=self.version, project=self.project)]) client.post( - '/api/v2/webhook/github/{0}/'.format(self.project.slug), + '/api/v2/webhook/github/{}/'.format(self.project.slug), {'ref': 'non-existent'}, format='json', ) @@ -899,7 +898,7 @@ def test_github_webhook_for_branches(self, trigger_build): [mock.call(force=True, version=mock.ANY, project=self.project)]) client.post( - '/api/v2/webhook/github/{0}/'.format(self.project.slug), + '/api/v2/webhook/github/{}/'.format(self.project.slug), {'ref': 'refs/heads/master'}, format='json', ) @@ -911,7 +910,7 @@ def test_github_webhook_for_tags(self, trigger_build): client = APIClient() client.post( - '/api/v2/webhook/github/{0}/'.format(self.project.slug), + '/api/v2/webhook/github/{}/'.format(self.project.slug), {'ref': 'v1.0'}, format='json', ) @@ -919,7 +918,7 @@ def test_github_webhook_for_tags(self, trigger_build): [mock.call(force=True, version=self.version_tag, project=self.project)]) client.post( - '/api/v2/webhook/github/{0}/'.format(self.project.slug), + '/api/v2/webhook/github/{}/'.format(self.project.slug), {'ref': 'refs/heads/non-existent'}, format='json', ) @@ -927,7 +926,7 @@ def test_github_webhook_for_tags(self, trigger_build): [mock.call(force=True, version=mock.ANY, project=self.project)]) client.post( - '/api/v2/webhook/github/{0}/'.format(self.project.slug), + '/api/v2/webhook/github/{}/'.format(self.project.slug), {'ref': 'refs/tags/v1.0'}, format='json', ) @@ -986,7 +985,7 @@ def test_github_invalid_webhook(self, trigger_build): """GitHub webhook unhandled event.""" client = APIClient() resp = client.post( - '/api/v2/webhook/github/{0}/'.format(self.project.slug), + '/api/v2/webhook/github/{}/'.format(self.project.slug), {'foo': 'bar'}, format='json', HTTP_X_GITHUB_EVENT='pull_request', @@ -1146,7 +1145,7 @@ def test_gitlab_invalid_webhook(self, trigger_build): """GitLab webhook unhandled event.""" client = APIClient() resp = client.post( - '/api/v2/webhook/gitlab/{0}/'.format(self.project.slug), + '/api/v2/webhook/gitlab/{}/'.format(self.project.slug), {'object_kind': 'pull_request'}, format='json', ) @@ -1236,7 +1235,7 @@ def test_bitbucket_invalid_webhook(self, trigger_build): """Bitbucket webhook unhandled event.""" client = APIClient() resp = client.post( - '/api/v2/webhook/bitbucket/{0}/'.format(self.project.slug), + '/api/v2/webhook/bitbucket/{}/'.format(self.project.slug), {'foo': 'bar'}, format='json', HTTP_X_EVENT_KEY='pull_request') self.assertEqual(resp.status_code, 200) self.assertEqual(resp.data['detail'], 'Unhandled webhook event') @@ -1244,7 +1243,7 @@ def test_bitbucket_invalid_webhook(self, trigger_build): def test_generic_api_fails_without_auth(self, trigger_build): client = APIClient() resp = client.post( - '/api/v2/webhook/generic/{0}/'.format(self.project.slug), + '/api/v2/webhook/generic/{}/'.format(self.project.slug), {}, format='json', ) @@ -1262,7 +1261,7 @@ def test_generic_api_respects_token_auth(self, trigger_build): ) self.assertIsNotNone(integration.token) resp = client.post( - '/api/v2/webhook/{0}/{1}/'.format( + '/api/v2/webhook/{}/{}/'.format( self.project.slug, integration.pk, ), @@ -1273,7 +1272,7 @@ def test_generic_api_respects_token_auth(self, trigger_build): self.assertTrue(resp.data['build_triggered']) # Test nonexistent branch resp = client.post( - '/api/v2/webhook/{0}/{1}/'.format( + '/api/v2/webhook/{}/{}/'.format( self.project.slug, integration.pk, ), @@ -1289,7 +1288,7 @@ def test_generic_api_respects_basic_auth(self, trigger_build): self.project.users.add(user) client.force_authenticate(user=user) resp = client.post( - '/api/v2/webhook/generic/{0}/'.format(self.project.slug), + '/api/v2/webhook/generic/{}/'.format(self.project.slug), {}, format='json', ) @@ -1304,7 +1303,7 @@ def test_generic_api_falls_back_to_token_auth(self, trigger_build): project=self.project, integration_type=Integration.API_WEBHOOK) self.assertIsNotNone(integration.token) resp = client.post( - '/api/v2/webhook/{0}/{1}/'.format( + '/api/v2/webhook/{}/{}/'.format( self.project.slug, integration.pk, ), diff --git a/readthedocs/rtd_tests/tests/test_backend.py b/readthedocs/rtd_tests/tests/test_backend.py index ca0de802c9e..34510778fea 100644 --- a/readthedocs/rtd_tests/tests/test_backend.py +++ b/readthedocs/rtd_tests/tests/test_backend.py @@ -34,7 +34,7 @@ class TestGitBackend(RTDTestCase): def setUp(self): git_repo = make_test_git() - super(TestGitBackend, self).setUp() + super().setUp() self.eric = User(username='eric') self.eric.set_password('test') self.eric.save() @@ -128,7 +128,7 @@ def test_git_tags(self): # so we need to hack the repo path repo.working_dir = repo_path self.assertEqual( - set(['v01', 'v02', 'release-ünîø∂é']), + {'v01', 'v02', 'release-ünîø∂é'}, {vcs.verbose_name for vcs in repo.tags}, ) @@ -207,28 +207,28 @@ def test_fetch_clean_tags_and_branches(self, checkout_path): # We still have all branches and tags in the local repo self.assertEqual( - set(['v01', 'v02']), - set(vcs.verbose_name for vcs in repo.tags) + {'v01', 'v02'}, + {vcs.verbose_name for vcs in repo.tags} ) self.assertEqual( - set([ + { 'invalidsubmodule', 'master', 'submodule', 'newbranch', - ]), - set(vcs.verbose_name for vcs in repo.branches) + }, + {vcs.verbose_name for vcs in repo.branches} ) repo.update() # We don't have the eliminated branches and tags in the local repo self.assertEqual( - set(['v01']), - set(vcs.verbose_name for vcs in repo.tags) + {'v01'}, + {vcs.verbose_name for vcs in repo.tags} ) self.assertEqual( - set([ + { 'invalidsubmodule', 'master', 'submodule' - ]), - set(vcs.verbose_name for vcs in repo.branches) + }, + {vcs.verbose_name for vcs in repo.branches} ) @@ -236,7 +236,7 @@ class TestHgBackend(RTDTestCase): def setUp(self): hg_repo = make_test_hg() - super(TestHgBackend, self).setUp() + super().setUp() self.eric = User(username='eric') self.eric.set_password('test') self.eric.save() diff --git a/readthedocs/rtd_tests/tests/test_build_config.py b/readthedocs/rtd_tests/tests/test_build_config.py index b0c2186d419..9085d6f7926 100644 --- a/readthedocs/rtd_tests/tests/test_build_config.py +++ b/readthedocs/rtd_tests/tests/test_build_config.py @@ -28,7 +28,7 @@ class PathValidator(Validator): configuration_file = '.' def _is_valid(self, value): - if isinstance(value, six.string_types): + if isinstance(value, str): file_ = path.join( path.dirname(self.configuration_file), value diff --git a/readthedocs/rtd_tests/tests/test_celery.py b/readthedocs/rtd_tests/tests/test_celery.py index 11a9e31e46f..e718e220b36 100644 --- a/readthedocs/rtd_tests/tests/test_celery.py +++ b/readthedocs/rtd_tests/tests/test_celery.py @@ -30,7 +30,7 @@ class TestCeleryBuilding(RTDTestCase): def setUp(self): repo = make_test_git() self.repo = repo - super(TestCeleryBuilding, self).setUp() + super().setUp() self.eric = User(username='eric') self.eric.set_password('test') self.eric.save() @@ -44,7 +44,7 @@ def setUp(self): def tearDown(self): shutil.rmtree(self.repo) - super(TestCeleryBuilding, self).tearDown() + super().tearDown() def test_remove_dir(self): directory = mkdtemp() diff --git a/readthedocs/rtd_tests/tests/test_config_integration.py b/readthedocs/rtd_tests/tests/test_config_integration.py index 2b2c6ceb489..8474c4bea11 100644 --- a/readthedocs/rtd_tests/tests/test_config_integration.py +++ b/readthedocs/rtd_tests/tests/test_config_integration.py @@ -288,7 +288,7 @@ def test_requirements_file_from_yml(self, checkout_path): @pytest.mark.django_db @mock.patch('readthedocs.projects.models.Project.checkout_path') -class TestLoadConfigV2(object): +class TestLoadConfigV2: @pytest.fixture(autouse=True) def create_project(self): diff --git a/readthedocs/rtd_tests/tests/test_doc_building.py b/readthedocs/rtd_tests/tests/test_doc_building.py index 6a954de74d6..1b8f11c3880 100644 --- a/readthedocs/rtd_tests/tests/test_doc_building.py +++ b/readthedocs/rtd_tests/tests/test_doc_building.py @@ -45,7 +45,7 @@ DUMMY_BUILD_ID = 123 -SAMPLE_UNICODE = u'HérÉ îß sömê ünïçó∂é' +SAMPLE_UNICODE = 'HérÉ îß sömê ünïçó∂é' SAMPLE_UTF8_BYTES = SAMPLE_UNICODE.encode('utf-8') @@ -83,7 +83,7 @@ def test_normal_execution(self): self.assertTrue(build_env.done) self.assertTrue(build_env.successful) self.assertEqual(len(build_env.commands), 1) - self.assertEqual(build_env.commands[0].output, u'This is okay') + self.assertEqual(build_env.commands[0].output, 'This is okay') # api() is not called anymore, we use api_v2 instead self.assertFalse(self.mocks.api()(DUMMY_BUILD_ID).put.called) @@ -103,7 +103,7 @@ def test_normal_execution(self): 'version': self.version.pk, 'success': True, 'project': self.project.pk, - 'setup_error': u'', + 'setup_error': '', 'length': mock.ANY, 'error': '', 'setup': '', @@ -169,7 +169,7 @@ def test_record_command_as_success(self): self.assertTrue(build_env.done) self.assertTrue(build_env.successful) self.assertEqual(len(build_env.commands), 1) - self.assertEqual(build_env.commands[0].output, u'This is okay') + self.assertEqual(build_env.commands[0].output, 'This is okay') # api() is not called anymore, we use api_v2 instead self.assertFalse(self.mocks.api()(DUMMY_BUILD_ID).put.called) @@ -189,7 +189,7 @@ def test_record_command_as_success(self): 'version': self.version.pk, 'success': True, 'project': self.project.pk, - 'setup_error': u'', + 'setup_error': '', 'length': mock.ANY, 'error': '', 'setup': '', @@ -254,7 +254,7 @@ def test_failing_execution(self): self.assertTrue(build_env.done) self.assertTrue(build_env.failed) self.assertEqual(len(build_env.commands), 1) - self.assertEqual(build_env.commands[0].output, u'This is not okay') + self.assertEqual(build_env.commands[0].output, 'This is not okay') # api() is not called anymore, we use api_v2 instead self.assertFalse(self.mocks.api()(DUMMY_BUILD_ID).put.called) @@ -274,7 +274,7 @@ def test_failing_execution(self): 'version': self.version.pk, 'success': False, 'project': self.project.pk, - 'setup_error': u'', + 'setup_error': '', 'length': mock.ANY, 'error': '', 'setup': '', @@ -537,13 +537,13 @@ def _inner(): 'version': self.version.pk, 'success': False, 'project': self.project.pk, - 'setup_error': u'', + 'setup_error': '', 'exit_code': 1, 'length': mock.ANY, 'error': 'Build environment creation failed', - 'setup': u'', - 'output': u'', - 'state': u'finished', + 'setup': '', + 'output': '', + 'state': 'finished', 'builder': mock.ANY, }) @@ -588,13 +588,13 @@ def test_api_failure_on_docker_memory_limit(self): 'version': self.version.pk, 'success': False, 'project': self.project.pk, - 'setup_error': u'', + 'setup_error': '', 'exit_code': -1, 'length': mock.ANY, 'error': '', - 'setup': u'', - 'output': u'', - 'state': u'finished', + 'setup': '', + 'output': '', + 'state': 'finished', 'builder': mock.ANY, }) @@ -694,7 +694,7 @@ def test_command_execution(self): container='build-123-project-6-pip', cmd="/bin/sh -c 'cd /tmp && echo\\ test'", stderr=True, stdout=True) self.assertEqual(build_env.commands[0].exit_code, 1) - self.assertEqual(build_env.commands[0].output, u'This is the return') + self.assertEqual(build_env.commands[0].output, 'This is the return') self.assertEqual(build_env.commands[0].error, None) self.assertTrue(build_env.failed) @@ -789,7 +789,7 @@ def test_record_command_as_success(self): container='build-123-project-6-pip', cmd="/bin/sh -c 'cd /tmp && echo\\ test'", stderr=True, stdout=True) self.assertEqual(build_env.commands[0].exit_code, 0) - self.assertEqual(build_env.commands[0].output, u'This is the return') + self.assertEqual(build_env.commands[0].output, 'This is the return') self.assertEqual(build_env.commands[0].error, None) self.assertFalse(build_env.failed) @@ -867,12 +867,12 @@ def test_command_execution_cleanup_exception(self): 'error': '', 'success': True, 'project': self.project.pk, - 'setup_error': u'', + 'setup_error': '', 'exit_code': 0, 'length': 0, - 'setup': u'', - 'output': u'', - 'state': u'finished', + 'setup': '', + 'output': '', + 'state': 'finished', 'builder': mock.ANY, }) @@ -970,13 +970,13 @@ def test_container_timeout(self): 'version': self.version.pk, 'success': False, 'project': self.project.pk, - 'setup_error': u'', + 'setup_error': '', 'exit_code': 1, 'length': 0, 'error': 'Build exited due to time out', - 'setup': u'', - 'output': u'', - 'state': u'finished', + 'setup': '', + 'output': '', + 'state': 'finished', 'builder': mock.ANY, }) @@ -1068,7 +1068,7 @@ def test_unicode_output(self, mock_subprocess): cmd.run() self.assertEqual( cmd.output, - u'H\xe9r\xc9 \xee\xdf s\xf6m\xea \xfcn\xef\xe7\xf3\u2202\xe9') + 'H\xe9r\xc9 \xee\xdf s\xf6m\xea \xfcn\xef\xe7\xf3\u2202\xe9') class TestDockerBuildCommand(TestCase): @@ -1117,7 +1117,7 @@ def test_unicode_output(self): cmd.run() self.assertEqual( cmd.output, - u'H\xe9r\xc9 \xee\xdf s\xf6m\xea \xfcn\xef\xe7\xf3\u2202\xe9') + 'H\xe9r\xc9 \xee\xdf s\xf6m\xea \xfcn\xef\xe7\xf3\u2202\xe9') self.assertEqual(self.mocks.docker_client.exec_start.call_count, 1) self.assertEqual(self.mocks.docker_client.exec_create.call_count, 1) self.assertEqual(self.mocks.docker_client.exec_inspect.call_count, 1) @@ -1402,7 +1402,7 @@ def test_install_user_requirements_conda(self, checkout_path): self.build_env_mock.run.assert_not_called() -class AutoWipeEnvironmentBase(object): +class AutoWipeEnvironmentBase: fixtures = ['test_data'] build_env_class = None diff --git a/readthedocs/rtd_tests/tests/test_extend.py b/readthedocs/rtd_tests/tests/test_extend.py index 205fa64e7bc..d3c66552b6a 100644 --- a/readthedocs/rtd_tests/tests/test_extend.py +++ b/readthedocs/rtd_tests/tests/test_extend.py @@ -7,7 +7,7 @@ # Top level to ensure module name is correct -class FooBase(object): +class FooBase: def bar(self): return 1 diff --git a/readthedocs/rtd_tests/tests/test_integrations.py b/readthedocs/rtd_tests/tests/test_integrations.py index 51006b7e757..17f9b291253 100644 --- a/readthedocs/rtd_tests/tests/test_integrations.py +++ b/readthedocs/rtd_tests/tests/test_integrations.py @@ -30,7 +30,7 @@ def test_exchange_json_request_body(self): integration_type=Integration.GITHUB_WEBHOOK, provider_data='') resp = client.post( - '/api/v2/webhook/github/{0}/'.format(project.slug), + '/api/v2/webhook/github/{}/'.format(project.slug), {'ref': 'exchange_json'}, format='json' ) @@ -41,8 +41,8 @@ def test_exchange_json_request_body(self): ) self.assertEqual( exchange.request_headers, - {u'Content-Type': u'application/json; charset=None', - u'Cookie': u''} + {'Content-Type': 'application/json; charset=None', + 'Cookie': ''} ) self.assertEqual( exchange.response_body, @@ -51,8 +51,8 @@ def test_exchange_json_request_body(self): ) self.assertEqual( exchange.response_headers, - {u'Allow': u'POST, OPTIONS', - u'Content-Type': u'text/html; charset=utf-8'} + {'Allow': 'POST, OPTIONS', + 'Content-Type': 'text/html; charset=utf-8'} ) def test_exchange_form_request_body(self): @@ -63,7 +63,7 @@ def test_exchange_form_request_body(self): integration_type=Integration.GITHUB_WEBHOOK, provider_data='') resp = client.post( - '/api/v2/webhook/github/{0}/'.format(project.slug), + '/api/v2/webhook/github/{}/'.format(project.slug), 'payload=%7B%22ref%22%3A+%22exchange_form%22%7D', content_type='application/x-www-form-urlencoded', ) @@ -74,8 +74,8 @@ def test_exchange_form_request_body(self): ) self.assertEqual( exchange.request_headers, - {u'Content-Type': u'application/x-www-form-urlencoded', - u'Cookie': u''} + {'Content-Type': 'application/x-www-form-urlencoded', + 'Cookie': ''} ) self.assertEqual( exchange.response_body, @@ -84,8 +84,8 @@ def test_exchange_form_request_body(self): ) self.assertEqual( exchange.response_headers, - {u'Allow': u'POST, OPTIONS', - u'Content-Type': u'text/html; charset=utf-8'} + {'Allow': 'POST, OPTIONS', + 'Content-Type': 'text/html; charset=utf-8'} ) def test_extraneous_exchanges_deleted_in_correct_order(self): @@ -103,13 +103,13 @@ def test_extraneous_exchanges_deleted_in_correct_order(self): for _ in range(10): resp = client.post( - '/api/v2/webhook/github/{0}/'.format(project.slug), + '/api/v2/webhook/github/{}/'.format(project.slug), {'ref': 'deleted'}, format='json' ) for _ in range(10): resp = client.post( - '/api/v2/webhook/github/{0}/'.format(project.slug), + '/api/v2/webhook/github/{}/'.format(project.slug), {'ref': 'preserved'}, format='json' ) @@ -134,7 +134,7 @@ def test_request_headers_are_removed(self): integration_type=Integration.GITHUB_WEBHOOK, provider_data='') resp = client.post( - '/api/v2/webhook/github/{0}/'.format(project.slug), + '/api/v2/webhook/github/{}/'.format(project.slug), {'ref': 'exchange_json'}, format='json', HTTP_X_FORWARDED_FOR='1.2.3.4', @@ -144,9 +144,9 @@ def test_request_headers_are_removed(self): exchange = HttpExchange.objects.get(integrations=integration) self.assertEqual( exchange.request_headers, - {u'Content-Type': u'application/json; charset=None', - u'Cookie': u'', - u'X-Foo': u'bar'} + {'Content-Type': 'application/json; charset=None', + 'Cookie': '', + 'X-Foo': 'bar'} ) diff --git a/readthedocs/rtd_tests/tests/test_notifications.py b/readthedocs/rtd_tests/tests/test_notifications.py index 2213b0d8c7c..adae07e49fe 100644 --- a/readthedocs/rtd_tests/tests/test_notifications.py +++ b/readthedocs/rtd_tests/tests/test_notifications.py @@ -43,7 +43,7 @@ class TestNotification(Notification): self.assertEqual(notify.get_template_names('site'), ['builds/notifications/foo_site.html']) self.assertEqual(notify.get_subject(), - 'This is {0}'.format(build.id)) + 'This is {}'.format(build.id)) self.assertEqual(notify.get_context_data(), {'foo': build, 'production_uri': 'https://readthedocs.org', @@ -82,7 +82,7 @@ class TestNotification(Notification): request=mock.ANY, template='core/email/common.txt', context={'content': 'Test'}, - subject=u'This is {}'.format(build.id), + subject='This is {}'.format(build.id), template_html='core/email/common.html', recipient=user.email, ) diff --git a/readthedocs/rtd_tests/tests/test_privacy_urls.py b/readthedocs/rtd_tests/tests/test_privacy_urls.py index 277c566e22e..f793f8c51ab 100644 --- a/readthedocs/rtd_tests/tests/test_privacy_urls.py +++ b/readthedocs/rtd_tests/tests/test_privacy_urls.py @@ -19,7 +19,7 @@ from readthedocs.rtd_tests.utils import create_user -class URLAccessMixin(object): +class URLAccessMixin: default_kwargs = {} response_data = {} @@ -93,10 +93,10 @@ def setUp(self): for not_obj in self.context_data: if isinstance(obj, list) or isinstance(obj, set) or isinstance(obj, tuple): self.assertNotIn(not_obj, obj) - print("%s not in %s" % (not_obj, obj)) + print("{} not in {}".format(not_obj, obj)) else: self.assertNotEqual(not_obj, obj) - print("%s is not %s" % (not_obj, obj)) + print("{} is not {}".format(not_obj, obj)) def _test_url(self, urlpatterns): deconstructed_urls = extract_views_from_urlpatterns(urlpatterns) @@ -134,7 +134,7 @@ def setUp(self): class ProjectMixin(URLAccessMixin): def setUp(self): - super(ProjectMixin, self).setUp() + super().setUp() self.build = get(Build, project=self.pip) self.tag = get(Tag, slug='coolness') self.subproject = get(Project, slug='sub', language='ja', @@ -307,7 +307,7 @@ def is_admin(self): class APIMixin(URLAccessMixin): def setUp(self): - super(APIMixin, self).setUp() + super().setUp() self.build = get(Build, project=self.pip) self.build_command_result = get(BuildCommandResult, project=self.pip) self.domain = get(Domain, url='http://docs.foobar.com', project=self.pip) diff --git a/readthedocs/rtd_tests/tests/test_project.py b/readthedocs/rtd_tests/tests/test_project.py index 679b761a25a..bbbd43a11a3 100644 --- a/readthedocs/rtd_tests/tests/test_project.py +++ b/readthedocs/rtd_tests/tests/test_project.py @@ -22,7 +22,7 @@ from readthedocs.rtd_tests.mocks.paths import fake_paths_by_regex -class ProjectMixin(object): +class ProjectMixin: fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_project_forms.py b/readthedocs/rtd_tests/tests/test_project_forms.py index 6d358123b65..e7fefb5bbbf 100644 --- a/readthedocs/rtd_tests/tests/test_project_forms.py +++ b/readthedocs/rtd_tests/tests/test_project_forms.py @@ -242,10 +242,10 @@ def test_list_only_active_versions_on_default_version(self): # This version is created automatically by the project on save self.assertTrue(self.project.versions.filter(slug=LATEST).exists()) self.assertEqual( - set( + { slug for slug, _ in form.fields['default_version'].widget.choices - ), + }, {'latest', 'public-1', 'public-2', 'private', 'protected'}, ) @@ -254,10 +254,10 @@ def test_list_all_versions_on_default_branch(self): # This version is created automatically by the project on save self.assertTrue(self.project.versions.filter(slug=LATEST).exists()) self.assertEqual( - set( + { identifier for identifier, _ in form.fields['default_branch'].widget.choices - ), + }, { None, 'master', 'public-1', 'public-2', 'public-3', 'public/4', 'protected', 'private' diff --git a/readthedocs/rtd_tests/tests/test_project_symlinks.py b/readthedocs/rtd_tests/tests/test_project_symlinks.py index 307e4e4516f..e35ca38743d 100644 --- a/readthedocs/rtd_tests/tests/test_project_symlinks.py +++ b/readthedocs/rtd_tests/tests/test_project_symlinks.py @@ -97,7 +97,7 @@ def setUp(self): new_callable=mock.PropertyMock ), } - self.patches = dict((key, mock.start()) for (key, mock) in list(self.mocks.items())) + self.patches = {key: mock.start() for (key, mock) in list(self.mocks.items())} self.patches['PublicSymlinkBase.CNAME_ROOT'].return_value = os.path.join( settings.SITE_ROOT, 'public_cname_root' ) @@ -127,10 +127,10 @@ def assertFilesystem(self, filesystem): self.assertEqual(filesystem, get_filesystem(settings.SITE_ROOT)) -class BaseSymlinkCnames(object): +class BaseSymlinkCnames: def setUp(self): - super(BaseSymlinkCnames, self).setUp() + super().setUp() self.project = get(Project, slug='kong', privacy_level=self.privacy, main_language_project=None) self.project.versions.update(privacy_level=self.privacy) @@ -303,10 +303,10 @@ class TestPrivateSymlinkCnames(BaseSymlinkCnames, TempSiteRootTestCase): symlink_class = PrivateSymlink -class BaseSubprojects(object): +class BaseSubprojects: def setUp(self): - super(BaseSubprojects, self).setUp() + super().setUp() self.project = get(Project, slug='kong', privacy_level=self.privacy, main_language_project=None) self.project.versions.update(privacy_level=self.privacy) @@ -514,10 +514,10 @@ class TestPrivateSubprojects(BaseSubprojects, TempSiteRootTestCase): symlink_class = PrivateSymlink -class BaseSymlinkTranslations(object): +class BaseSymlinkTranslations: def setUp(self): - super(BaseSymlinkTranslations, self).setUp() + super().setUp() self.project = get(Project, slug='kong', privacy_level=self.privacy, main_language_project=None) self.project.versions.update(privacy_level=self.privacy) @@ -766,10 +766,10 @@ class TestPrivateSymlinkTranslations(BaseSymlinkTranslations, TempSiteRootTestCa symlink_class = PrivateSymlink -class BaseSymlinkSingleVersion(object): +class BaseSymlinkSingleVersion: def setUp(self): - super(BaseSymlinkSingleVersion, self).setUp() + super().setUp() self.project = get(Project, slug='kong', privacy_level=self.privacy, main_language_project=None) self.project.versions.update(privacy_level=self.privacy) @@ -841,10 +841,10 @@ class TestPublicSymlinkSingleVersion(BaseSymlinkSingleVersion, TempSiteRootTestC symlink_class = PrivateSymlink -class BaseSymlinkVersions(object): +class BaseSymlinkVersions: def setUp(self): - super(BaseSymlinkVersions, self).setUp() + super().setUp() self.project = get(Project, slug='kong', privacy_level=self.privacy, main_language_project=None) self.project.versions.update(privacy_level=self.privacy) @@ -970,11 +970,11 @@ class TestPrivateSymlinkVersions(BaseSymlinkVersions, TempSiteRootTestCase): class TestPublicSymlinkUnicode(TempSiteRootTestCase): def setUp(self): - super(TestPublicSymlinkUnicode, self).setUp() - self.project = get(Project, slug='kong', name=u'foo-∫', + super().setUp() + self.project = get(Project, slug='kong', name='foo-∫', main_language_project=None) self.project.save() - self.stable = get(Version, slug='foo-a', verbose_name=u'foo-∂', + self.stable = get(Version, slug='foo-a', verbose_name='foo-∂', active=True, project=self.project) self.symlink = PublicSymlink(self.project) @@ -1035,7 +1035,7 @@ def test_symlink_broadcast_calls_on_project_save(self): class TestPublicPrivateSymlink(TempSiteRootTestCase): def setUp(self): - super(TestPublicPrivateSymlink, self).setUp() + super().setUp() from django.contrib.auth.models import User self.user = get(User) diff --git a/readthedocs/rtd_tests/tests/test_project_views.py b/readthedocs/rtd_tests/tests/test_project_views.py index c6e4a93f884..124fd018c00 100644 --- a/readthedocs/rtd_tests/tests/test_project_views.py +++ b/readthedocs/rtd_tests/tests/test_project_views.py @@ -32,7 +32,7 @@ class TestProfileMiddleware(RequestFactoryTestMixin, TestCase): url = '/dashboard/import/manual/' def setUp(self): - super(TestProfileMiddleware, self).setUp() + super().setUp() data = { 'basics': { 'name': 'foobar', @@ -47,9 +47,9 @@ def setUp(self): } self.data = {} for key in data: - self.data.update({('{0}-{1}'.format(key, k), v) + self.data.update({('{}-{}'.format(key, k), v) for (k, v) in list(data[key].items())}) - self.data['{0}-current_step'.format(self.wizard_class_slug)] = 'extra' + self.data['{}-current_step'.format(self.wizard_class_slug)] = 'extra' def test_profile_middleware_no_profile(self): """User without profile and isn't banned""" @@ -101,7 +101,7 @@ def tearDown(self): def request(self, *args, **kwargs): kwargs['user'] = self.user - return super(TestBasicsForm, self).request(*args, **kwargs) + return super().request(*args, **kwargs) def test_form_pass(self): """Only submit the basics""" @@ -146,7 +146,7 @@ def test_form_missing(self): class TestAdvancedForm(TestBasicsForm): def setUp(self): - super(TestAdvancedForm, self).setUp() + super().setUp() self.step_data['basics']['advanced'] = True self.step_data['extra'] = { 'description': 'Describe foobar', @@ -169,10 +169,9 @@ def test_form_pass(self): data = self.step_data['basics'] del data['advanced'] del self.step_data['extra']['tags'] - six.assertCountEqual( - self, + self.assertCountEqual( [tag.name for tag in proj.tags.all()], - [u'bar', u'baz', u'foo']) + ['bar', 'baz', 'foo']) data.update(self.step_data['extra']) for (key, val) in list(data.items()): self.assertEqual(getattr(proj, key), val) diff --git a/readthedocs/rtd_tests/tests/test_resolver.py b/readthedocs/rtd_tests/tests/test_resolver.py index 1ef55d564ca..841da05c1b6 100644 --- a/readthedocs/rtd_tests/tests/test_resolver.py +++ b/readthedocs/rtd_tests/tests/test_resolver.py @@ -627,7 +627,7 @@ def test_resolver_domain_https(self): self.assertEqual(url, 'http://pip.readthedocs.io/en/latest/') -class ResolverAltSetUp(object): +class ResolverAltSetUp: def setUp(self): with mock.patch('readthedocs.projects.models.broadcast'): diff --git a/readthedocs/rtd_tests/tests/test_urls.py b/readthedocs/rtd_tests/tests/test_urls.py index d70e0be8b71..cb4204e2870 100644 --- a/readthedocs/rtd_tests/tests/test_urls.py +++ b/readthedocs/rtd_tests/tests/test_urls.py @@ -48,7 +48,7 @@ class TestVersionURLs(TestCase): def test_version_url_with_caps(self): url = reverse( 'project_download_media', - kwargs={'type_': 'pdf', 'version_slug': u'1.4.X', 'project_slug': u'django'} + kwargs={'type_': 'pdf', 'version_slug': '1.4.X', 'project_slug': 'django'} ) self.assertTrue(url) diff --git a/readthedocs/rtd_tests/tests/test_version_commit_name.py b/readthedocs/rtd_tests/tests/test_version_commit_name.py index e9ffd833ee7..943843c22f8 100644 --- a/readthedocs/rtd_tests/tests/test_version_commit_name.py +++ b/readthedocs/rtd_tests/tests/test_version_commit_name.py @@ -20,44 +20,44 @@ def test_branch_name_unicode_non_ascii(self): self.assertEqual(version.identifier_friendly, unicode_name) def test_branch_name_made_friendly_when_sha(self): - commit_hash = u'3d92b728b7d7b842259ac2020c2fa389f13aff0d' + commit_hash = '3d92b728b7d7b842259ac2020c2fa389f13aff0d' version = new(Version, identifier=commit_hash, slug=STABLE, verbose_name=STABLE, type=TAG) # we shorten commit hashes to keep things readable self.assertEqual(version.identifier_friendly, '3d92b728') def test_branch_name(self): - version = new(Version, identifier=u'release-2.5.x', - slug=u'release-2.5.x', verbose_name=u'release-2.5.x', + version = new(Version, identifier='release-2.5.x', + slug='release-2.5.x', verbose_name='release-2.5.x', type=BRANCH) self.assertEqual(version.commit_name, 'release-2.5.x') def test_tag_name(self): - version = new(Version, identifier=u'10f1b29a2bd2', slug=u'release-2.5.0', - verbose_name=u'release-2.5.0', type=TAG) - self.assertEqual(version.commit_name, u'release-2.5.0') + version = new(Version, identifier='10f1b29a2bd2', slug='release-2.5.0', + verbose_name='release-2.5.0', type=TAG) + self.assertEqual(version.commit_name, 'release-2.5.0') def test_branch_with_name_stable(self): - version = new(Version, identifier=u'origin/stable', slug=STABLE, - verbose_name=u'stable', type=BRANCH) - self.assertEqual(version.commit_name, u'stable') + version = new(Version, identifier='origin/stable', slug=STABLE, + verbose_name='stable', type=BRANCH) + self.assertEqual(version.commit_name, 'stable') def test_stable_version_tag(self): version = new(Version, - identifier=u'3d92b728b7d7b842259ac2020c2fa389f13aff0d', + identifier='3d92b728b7d7b842259ac2020c2fa389f13aff0d', slug=STABLE, verbose_name=STABLE, type=TAG) self.assertEqual(version.commit_name, - u'3d92b728b7d7b842259ac2020c2fa389f13aff0d') + '3d92b728b7d7b842259ac2020c2fa389f13aff0d') def test_hg_latest_branch(self): hg_project = get(Project, repo_type=REPO_TYPE_HG) - version = new(Version, identifier=u'default', slug=LATEST, + version = new(Version, identifier='default', slug=LATEST, verbose_name=LATEST, type=BRANCH, project=hg_project) - self.assertEqual(version.commit_name, u'default') + self.assertEqual(version.commit_name, 'default') def test_git_latest_branch(self): git_project = get(Project, repo_type=REPO_TYPE_GIT) version = new(Version, project=git_project, - identifier=u'origin/master', slug=LATEST, + identifier='origin/master', slug=LATEST, verbose_name=LATEST, type=BRANCH) - self.assertEqual(version.commit_name, u'master') + self.assertEqual(version.commit_name, 'master') diff --git a/readthedocs/search/indexes.py b/readthedocs/search/indexes.py index 48e4baecc5e..189f441d84e 100644 --- a/readthedocs/search/indexes.py +++ b/readthedocs/search/indexes.py @@ -25,7 +25,7 @@ from django.conf import settings -class Index(object): +class Index: """Base class to define some common methods across indexes.""" @@ -92,7 +92,7 @@ def get_analysis(self): } def timestamped_index(self): - return '{0}-{1}'.format( + return '{}-{}'.format( self._index, timezone.now().strftime('%Y%m%d%H%M%S')) def create_index(self, index=None): diff --git a/readthedocs/search/parse_json.py b/readthedocs/search/parse_json.py index 9b19a7e7cb3..baa4abe5eca 100644 --- a/readthedocs/search/parse_json.py +++ b/readthedocs/search/parse_json.py @@ -57,7 +57,7 @@ def generate_sections_from_pyquery(body): h1_section = body('.section > h1') if h1_section: div = h1_section.parent() - h1_title = h1_section.text().replace(u'¶', '').strip() + h1_title = h1_section.text().replace('¶', '').strip() h1_id = div.attr('id') h1_content = "" next_p = body('h1').next() @@ -79,7 +79,7 @@ def generate_sections_from_pyquery(body): for num in range(len(section_list)): div = section_list.eq(num).parent() header = section_list.eq(num) - title = header.text().replace(u'¶', '').strip() + title = header.text().replace('¶', '').strip() section_id = div.attr('id') content = div.html() yield { @@ -108,7 +108,7 @@ def process_file(filename): return None if 'body' in data and data['body']: body = PyQuery(data['body']) - body_content = body.text().replace(u'¶', '') + body_content = body.text().replace('¶', '') sections.extend(generate_sections_from_pyquery(body)) else: log.info('Unable to index content for: %s', filename) diff --git a/readthedocs/search/tests/test_views.py b/readthedocs/search/tests/test_views.py index 07444a731fb..d5ac18bafc9 100644 --- a/readthedocs/search/tests/test_views.py +++ b/readthedocs/search/tests/test_views.py @@ -14,7 +14,7 @@ @pytest.mark.django_db @pytest.mark.search -class TestElasticSearch(object): +class TestElasticSearch: url = reverse_lazy('search') diff --git a/readthedocs/search/utils.py b/readthedocs/search/utils.py index a742a341912..ec429e4ace9 100644 --- a/readthedocs/search/utils.py +++ b/readthedocs/search/utils.py @@ -207,7 +207,7 @@ def parse_sphinx_sections(content): h1_section = body('.section > h1') if h1_section: div = h1_section.parent() - h1_title = h1_section.text().replace(u'¶', '').strip() + h1_title = h1_section.text().replace('¶', '').strip() h1_id = div.attr('id') h1_content = "" next_p = next(body('h1')) # pylint: disable=stop-iteration-return @@ -229,7 +229,7 @@ def parse_sphinx_sections(content): for num in range(len(section_list)): div = section_list.eq(num).parent() header = section_list.eq(num) - title = header.text().replace(u'¶', '').strip() + title = header.text().replace('¶', '').strip() section_id = div.attr('id') content = div.html() yield { diff --git a/readthedocs/search/views.py b/readthedocs/search/views.py index bac1969e80e..e1725f87fdc 100644 --- a/readthedocs/search/views.py +++ b/readthedocs/search/views.py @@ -14,7 +14,7 @@ from readthedocs.search import lib as search_lib log = logging.getLogger(__name__) -LOG_TEMPLATE = u'(Elastic Search) [{user}:{type}] [{project}:{version}:{language}] {msg}' +LOG_TEMPLATE = '(Elastic Search) [{user}:{type}] [{project}:{version}:{language}] {msg}' UserInput = collections.namedtuple( 'UserInput', diff --git a/readthedocs/settings/base.py b/readthedocs/settings/base.py index a6a3e867978..205dbdd9989 100644 --- a/readthedocs/settings/base.py +++ b/readthedocs/settings/base.py @@ -43,7 +43,7 @@ class CommunityBaseSettings(Settings): PUBLIC_DOMAIN = None PUBLIC_DOMAIN_USES_HTTPS = False USE_SUBDOMAIN = False - PUBLIC_API_URL = 'https://{0}'.format(PRODUCTION_DOMAIN) + PUBLIC_API_URL = 'https://{}'.format(PRODUCTION_DOMAIN) # Email DEFAULT_FROM_EMAIL = 'no-reply@readthedocs.org' diff --git a/readthedocs/settings/dev.py b/readthedocs/settings/dev.py index 7fa4dafe959..8fd9860a0bd 100644 --- a/readthedocs/settings/dev.py +++ b/readthedocs/settings/dev.py @@ -50,7 +50,7 @@ def DATABASES(self): # noqa @property def LOGGING(self): # noqa - avoid pep8 N802 - logging = super(CommunityDevSettings, self).LOGGING + logging = super().LOGGING logging['formatters']['default']['format'] = '[%(asctime)s] ' + self.LOG_FORMAT # Allow Sphinx and other tools to create loggers logging['disable_existing_loggers'] = False diff --git a/readthedocs/vcs_support/backends/bzr.py b/readthedocs/vcs_support/backends/bzr.py index 5ea817c1156..9a309a8077a 100644 --- a/readthedocs/vcs_support/backends/bzr.py +++ b/readthedocs/vcs_support/backends/bzr.py @@ -26,7 +26,7 @@ class Backend(BaseVCS): fallback_branch = '' def update(self): - super(Backend, self).update() + super().update() retcode = self.run('bzr', 'status', record=False)[0] if retcode == 0: return self.up() @@ -90,7 +90,7 @@ def commit(self): return stdout.strip() def checkout(self, identifier=None): - super(Backend, self).checkout() + super().checkout() if not identifier: return self.up() return self.run('bzr', 'switch', identifier) diff --git a/readthedocs/vcs_support/backends/git.py b/readthedocs/vcs_support/backends/git.py index f9608799570..80fafb89e9d 100644 --- a/readthedocs/vcs_support/backends/git.py +++ b/readthedocs/vcs_support/backends/git.py @@ -36,7 +36,7 @@ class Backend(BaseVCS): repo_depth = 50 def __init__(self, *args, **kwargs): - super(Backend, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.token = kwargs.get('token', None) self.repo_url = self._get_clone_url() @@ -46,7 +46,7 @@ def _get_clone_url(self): hacked_url = re.sub('.git$', '', hacked_url) clone_url = 'https://%s' % hacked_url if self.token: - clone_url = 'https://%s@%s' % (self.token, hacked_url) + clone_url = 'https://{}@{}'.format(self.token, hacked_url) return clone_url # Don't edit URL because all hosts aren't the same # else: @@ -58,7 +58,7 @@ def set_remote_url(self, url): def update(self): """Clone or update the repository.""" - super(Backend, self).update() + super().update() if self.repo_exists(): self.set_remote_url(self.repo_url) return self.fetch() @@ -228,7 +228,7 @@ def commit(self): def checkout(self, identifier=None): """Checkout to identifier or latest.""" - super(Backend, self).checkout() + super().checkout() # Find proper identifier if not identifier: identifier = self.default_branch or self.fallback_branch @@ -291,7 +291,7 @@ def ref_exists(self, ref): @property def env(self): - env = super(Backend, self).env + env = super().env env['GIT_DIR'] = os.path.join(self.working_dir, '.git') # Don't prompt for username, this requires Git 2.3+ env['GIT_TERMINAL_PROMPT'] = '0' diff --git a/readthedocs/vcs_support/backends/hg.py b/readthedocs/vcs_support/backends/hg.py index 4a66624f1ae..e65d4c650f0 100644 --- a/readthedocs/vcs_support/backends/hg.py +++ b/readthedocs/vcs_support/backends/hg.py @@ -20,7 +20,7 @@ class Backend(BaseVCS): fallback_branch = 'default' def update(self): - super(Backend, self).update() + super().update() retcode = self.run('hg', 'status', record=False)[0] if retcode == 0: return self.pull() @@ -107,7 +107,7 @@ def commit(self): return stdout.strip() def checkout(self, identifier=None): - super(Backend, self).checkout() + super().checkout() if not identifier: identifier = 'tip' return self.run('hg', 'update', '--clean', identifier) diff --git a/readthedocs/vcs_support/backends/svn.py b/readthedocs/vcs_support/backends/svn.py index 61e29324281..2363116c6fd 100644 --- a/readthedocs/vcs_support/backends/svn.py +++ b/readthedocs/vcs_support/backends/svn.py @@ -25,7 +25,7 @@ class Backend(BaseVCS): fallback_branch = '/trunk/' def __init__(self, *args, **kwargs): - super(Backend, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if self.repo_url[-1] != '/': self.base_url = self.repo_url self.repo_url += '/' @@ -36,7 +36,7 @@ def __init__(self, *args, **kwargs): self.base_url = self.repo_url def update(self): - super(Backend, self).update() + super().update() # For some reason `svn status` gives me retcode 0 in non-svn # directories that's why I use `svn info` here. retcode, _, _ = self.run('svn', 'info', record=False) @@ -102,7 +102,7 @@ def commit(self): return stdout.strip() def checkout(self, identifier=None): - super(Backend, self).checkout() + super().checkout() return self.co(identifier) def get_url(self, base_url, identifier): diff --git a/readthedocs/vcs_support/base.py b/readthedocs/vcs_support/base.py index 0e632e3b858..f430fd0d0a5 100644 --- a/readthedocs/vcs_support/base.py +++ b/readthedocs/vcs_support/base.py @@ -12,7 +12,7 @@ log = logging.getLogger(__name__) -class VCSVersion(object): +class VCSVersion: """ Represents a Version (tag or branch) in a VCS. @@ -29,11 +29,11 @@ def __init__(self, repository, identifier, verbose_name): self.verbose_name = verbose_name def __repr__(self): - return ' Date: Thu, 3 Jan 2019 17:04:44 +0100 Subject: [PATCH 04/42] Use unicode string for Sphinx config file This file is generated by our code and can be used by Python2 when building the documentation, so we need to keep the `u` before the definition of the string. --- readthedocs/rtd_tests/files/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs/rtd_tests/files/conf.py b/readthedocs/rtd_tests/files/conf.py index 11f872849dd..b448eab9ab7 100644 --- a/readthedocs/rtd_tests/files/conf.py +++ b/readthedocs/rtd_tests/files/conf.py @@ -13,7 +13,7 @@ '.md': CommonMarkParser, } master_doc = 'index' -project = 'Pip' +project = u'Pip' copyright = str(datetime.now().year) version = '0.8.1' release = '0.8.1' From 1b589392703721642ecb4ebcb0b23663a2c1ef4e Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Thu, 3 Jan 2019 17:26:23 +0100 Subject: [PATCH 05/42] Remove unused imports to avoid lint issues --- readthedocs/config/config.py | 2 -- readthedocs/config/validation.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index ac6b3db99a3..900fe637e2e 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -9,8 +9,6 @@ import re from contextlib import contextmanager -import six - from readthedocs.projects.constants import DOCUMENTATION_CHOICES from .find import find_one diff --git a/readthedocs/config/validation.py b/readthedocs/config/validation.py index b1b42fc6775..baf7808d61e 100644 --- a/readthedocs/config/validation.py +++ b/readthedocs/config/validation.py @@ -3,8 +3,6 @@ import os -from six import string_types, text_type - INVALID_BOOL = 'invalid-bool' INVALID_CHOICE = 'invalid-choice' INVALID_LIST = 'invalid-list' From 271884a6649b3d0f8e1ee31b6ef22684f8dc1a0f Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Thu, 3 Jan 2019 17:30:05 +0100 Subject: [PATCH 06/42] Run tests with python3.6 on Travis --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bce5b2f3742..e5083ec7d50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,8 @@ env: - ES_VERSION=1.3.9 ES_DOWNLOAD_URL=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz matrix: include: + - python: 3.6 + env: TOXENV=py36 - python: 3.6 env: TOXENV=docs - python: 3.6 @@ -44,6 +46,6 @@ notifications: branches: only: - - master + - master - rel # Community release branch - relcorp # Corporate release branch From 849abb3cc28dc369fe5db4a5eb6365e15cb97cdd Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Thu, 3 Jan 2019 17:30:20 +0100 Subject: [PATCH 07/42] Do not include search tests when running migrations env --- scripts/travis/run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/travis/run_tests.sh b/scripts/travis/run_tests.sh index 99c56387033..c5da9d551f7 100755 --- a/scripts/travis/run_tests.sh +++ b/scripts/travis/run_tests.sh @@ -1,4 +1,4 @@ -if ! [[ "$TOXENV" =~ ^(docs|lint|eslint) ]]; +if ! [[ "$TOXENV" =~ ^(docs|lint|eslint|migrations) ]]; then args="'--including-search'" fi From c78187b62fb00113405ec4f433caf7122dbad49c Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 20:53:00 +0100 Subject: [PATCH 08/42] isort all the files $ pre-commit run python-import-sorter --files `find readthedocs -name '*.py'` It was ran with this configuration: https://github.com/rtfd/common/pull/28 --- readthedocs/analytics/apps.py | 1 - readthedocs/analytics/tasks.py | 2 -- readthedocs/analytics/tests.py | 4 --- readthedocs/analytics/utils.py | 6 ++--- readthedocs/analytics/vendor/ipaddress.py | 4 +-- readthedocs/api/base.py | 5 +--- readthedocs/api/client.py | 6 ++--- readthedocs/api/utils.py | 4 +-- readthedocs/builds/admin.py | 4 +-- readthedocs/builds/constants.py | 4 +-- readthedocs/builds/forms.py | 8 ------ readthedocs/builds/managers.py | 19 ++++++++----- readthedocs/builds/models.py | 11 ++------ readthedocs/builds/querysets.py | 2 -- readthedocs/builds/signals.py | 1 - readthedocs/builds/syncers.py | 5 +--- readthedocs/builds/urls.py | 4 +-- readthedocs/builds/utils.py | 8 +++--- readthedocs/builds/version_slug.py | 3 --- readthedocs/builds/views.py | 8 ------ readthedocs/config/config.py | 3 +-- readthedocs/config/find.py | 2 -- readthedocs/config/models.py | 2 -- readthedocs/config/parser.py | 3 +-- readthedocs/config/tests/test_config.py | 2 -- readthedocs/config/tests/test_find.py | 6 ++--- readthedocs/config/tests/test_parser.py | 3 --- readthedocs/config/tests/test_utils.py | 3 --- readthedocs/config/tests/test_validation.py | 24 +++++++++++------ readthedocs/config/tests/utils.py | 2 -- readthedocs/config/validation.py | 3 +-- readthedocs/constants.py | 1 - readthedocs/core/adapters.py | 2 +- readthedocs/core/admin.py | 5 ++-- readthedocs/core/apps.py | 1 - readthedocs/core/backends.py | 3 +-- readthedocs/core/context_processors.py | 1 - readthedocs/core/fields.py | 1 - readthedocs/core/forms.py | 5 +--- .../core/management/commands/archive.py | 7 +++-- .../core/management/commands/clean_builds.py | 4 +-- .../core/management/commands/import_github.py | 3 +-- .../commands/import_github_language.py | 9 +++---- .../commands/provision_elasticsearch.py | 9 +++++-- readthedocs/core/management/commands/pull.py | 1 - .../commands/reindex_elasticsearch.py | 5 ++-- .../core/management/commands/set_metadata.py | 4 +-- .../core/management/commands/symlink.py | 3 +-- .../core/management/commands/update_api.py | 1 - .../core/management/commands/update_repos.py | 4 +-- .../management/commands/update_versions.py | 1 - readthedocs/core/middleware.py | 6 ++--- readthedocs/core/mixins.py | 4 +-- readthedocs/core/models.py | 8 +++--- readthedocs/core/permissions.py | 2 -- readthedocs/core/resolver.py | 4 +-- readthedocs/core/settings.py | 2 -- readthedocs/core/signals.py | 10 +++---- readthedocs/core/static.py | 2 -- readthedocs/core/symlink.py | 9 +------ readthedocs/core/tasks.py | 3 +-- readthedocs/core/templatetags/core_tags.py | 3 --- readthedocs/core/templatetags/privacy_tags.py | 2 -- readthedocs/core/tests/test_signals.py | 4 +-- readthedocs/core/urls/single_version.py | 5 ++-- readthedocs/core/urls/subdomain.py | 15 +++++------ readthedocs/core/utils/extend.py | 2 -- readthedocs/core/utils/tasks/public.py | 8 +----- readthedocs/core/utils/tasks/retrieve.py | 8 +----- readthedocs/core/views/hooks.py | 8 +----- readthedocs/core/views/serve.py | 9 +++---- readthedocs/doc_builder/backends/mkdocs.py | 4 +-- readthedocs/doc_builder/backends/sphinx.py | 9 +++---- readthedocs/doc_builder/base.py | 5 +--- readthedocs/doc_builder/config.py | 3 --- readthedocs/doc_builder/constants.py | 4 +-- readthedocs/doc_builder/environments.py | 9 ------- readthedocs/doc_builder/exceptions.py | 2 -- readthedocs/doc_builder/loader.py | 2 +- .../doc_builder/python_environments.py | 10 +------ readthedocs/doc_builder/signals.py | 3 +-- readthedocs/gold/admin.py | 2 +- readthedocs/gold/apps.py | 1 - readthedocs/gold/forms.py | 4 --- readthedocs/gold/models.py | 8 +----- readthedocs/gold/signals.py | 1 - readthedocs/gold/tests/test_forms.py | 8 +++--- readthedocs/gold/tests/test_signals.py | 6 ++--- readthedocs/gold/urls.py | 1 - readthedocs/gold/views.py | 9 +------ readthedocs/integrations/admin.py | 5 ++-- readthedocs/integrations/models.py | 8 ------ readthedocs/notifications/apps.py | 1 - readthedocs/notifications/backends.py | 4 --- readthedocs/notifications/constants.py | 1 - readthedocs/notifications/forms.py | 1 - readthedocs/notifications/notification.py | 9 +++---- readthedocs/notifications/storages.py | 9 ++++--- readthedocs/notifications/urls.py | 4 +-- readthedocs/notifications/views.py | 3 +-- readthedocs/oauth/admin.py | 3 +-- readthedocs/oauth/models.py | 6 +---- readthedocs/oauth/notifications.py | 2 -- readthedocs/oauth/querysets.py | 2 -- readthedocs/oauth/services/base.py | 5 +--- readthedocs/oauth/services/bitbucket.py | 9 +++---- readthedocs/oauth/services/github.py | 9 +++---- readthedocs/oauth/services/gitlab.py | 3 +-- readthedocs/oauth/tasks.py | 8 +----- readthedocs/oauth/utils.py | 10 ++++--- readthedocs/payments/forms.py | 8 +++--- readthedocs/payments/mixins.py | 2 -- readthedocs/payments/utils.py | 2 +- readthedocs/profiles/urls/private.py | 7 ----- readthedocs/profiles/urls/public.py | 1 - readthedocs/profiles/views.py | 9 +------ readthedocs/projects/admin.py | 7 ----- readthedocs/projects/backends/views.py | 1 - readthedocs/projects/constants.py | 4 +-- readthedocs/projects/exceptions.py | 2 -- readthedocs/projects/feeds.py | 1 - readthedocs/projects/forms.py | 8 ------ .../commands/import_project_from_live.py | 8 +++--- readthedocs/projects/models.py | 19 +++++++------ readthedocs/projects/notifications.py | 1 - readthedocs/projects/querysets.py | 2 -- readthedocs/projects/signals.py | 1 - readthedocs/projects/tasks.py | 13 +++------ .../projects/templatetags/projects_tags.py | 1 - readthedocs/projects/urls/private.py | 24 ++++++++++++----- readthedocs/projects/urls/public.py | 6 ++--- readthedocs/projects/utils.py | 9 +------ readthedocs/projects/validators.py | 4 +-- readthedocs/projects/version_handling.py | 9 +++---- readthedocs/projects/views/base.py | 7 ++--- readthedocs/projects/views/mixins.py | 2 -- readthedocs/projects/views/private.py | 8 +----- readthedocs/projects/views/public.py | 6 ++--- readthedocs/redirects/admin.py | 3 +-- readthedocs/redirects/managers.py | 1 - readthedocs/redirects/models.py | 8 +++--- readthedocs/redirects/utils.py | 4 +-- readthedocs/restapi/client.py | 8 +----- readthedocs/restapi/permissions.py | 2 -- readthedocs/restapi/serializers.py | 6 +---- readthedocs/restapi/signals.py | 3 +-- readthedocs/restapi/urls.py | 8 +----- readthedocs/restapi/utils.py | 8 +----- readthedocs/restapi/views/core_views.py | 7 ++--- readthedocs/restapi/views/footer_views.py | 8 +++--- readthedocs/restapi/views/integrations.py | 9 +------ readthedocs/restapi/views/model_views.py | 27 ++++++++++++------- readthedocs/restapi/views/search_views.py | 3 +-- readthedocs/restapi/views/task_views.py | 8 +----- readthedocs/rtd_tests/base.py | 9 +++---- readthedocs/rtd_tests/files/conf.py | 2 -- readthedocs/rtd_tests/mocks/environment.py | 3 --- readthedocs/rtd_tests/mocks/mock_api.py | 5 ++-- readthedocs/rtd_tests/mocks/paths.py | 3 +-- .../tests/projects/test_admin_actions.py | 5 ++-- readthedocs/rtd_tests/tests/test_api.py | 10 +------ .../rtd_tests/tests/test_api_permissions.py | 5 ++-- .../tests/test_api_version_compare.py | 2 -- readthedocs/rtd_tests/tests/test_backend.py | 9 ------- .../rtd_tests/tests/test_backend_svn.py | 13 +++------ .../rtd_tests/tests/test_build_config.py | 6 ++--- .../rtd_tests/tests/test_build_forms.py | 3 --- .../tests/test_build_notifications.py | 8 ++---- readthedocs/rtd_tests/tests/test_builds.py | 11 ++------ readthedocs/rtd_tests/tests/test_celery.py | 19 +++++++------ .../tests/test_config_integration.py | 4 --- readthedocs/rtd_tests/tests/test_core_tags.py | 5 +--- .../rtd_tests/tests/test_core_utils.py | 9 +++---- .../rtd_tests/tests/test_doc_builder.py | 4 --- .../rtd_tests/tests/test_doc_building.py | 9 ------- .../rtd_tests/tests/test_doc_serving.py | 13 ++++----- readthedocs/rtd_tests/tests/test_domains.py | 5 +--- readthedocs/rtd_tests/tests/test_extend.py | 9 +++---- readthedocs/rtd_tests/tests/test_footer.py | 8 ------ readthedocs/rtd_tests/tests/test_gold.py | 10 +++---- .../rtd_tests/tests/test_imported_file.py | 4 +-- .../rtd_tests/tests/test_integrations.py | 13 ++++----- .../rtd_tests/tests/test_middleware.py | 14 +++------- .../rtd_tests/tests/test_notifications.py | 14 +++++----- readthedocs/rtd_tests/tests/test_oauth.py | 9 +++---- .../rtd_tests/tests/test_post_commit_hooks.py | 5 +--- readthedocs/rtd_tests/tests/test_privacy.py | 13 +++++---- .../rtd_tests/tests/test_privacy_urls.py | 10 +++---- .../rtd_tests/tests/test_profile_views.py | 5 +--- readthedocs/rtd_tests/tests/test_project.py | 12 ++++----- .../rtd_tests/tests/test_project_forms.py | 8 ------ .../rtd_tests/tests/test_project_querysets.py | 11 ++++---- .../rtd_tests/tests/test_project_symlinks.py | 15 ++++++----- .../rtd_tests/tests/test_project_views.py | 26 +++++++++--------- readthedocs/rtd_tests/tests/test_redirects.py | 10 +++---- .../rtd_tests/tests/test_repo_parsing.py | 4 --- readthedocs/rtd_tests/tests/test_resolver.py | 9 +++---- .../rtd_tests/tests/test_restapi_client.py | 4 --- .../tests/test_search_json_parsing.py | 1 - .../rtd_tests/tests/test_single_version.py | 5 +--- .../rtd_tests/tests/test_subprojects.py | 5 +--- .../rtd_tests/tests/test_sync_versions.py | 10 +------ readthedocs/rtd_tests/tests/test_urls.py | 5 +--- .../tests/test_version_commit_name.py | 13 +++------ .../rtd_tests/tests/test_version_config.py | 3 --- .../rtd_tests/tests/test_version_slug.py | 6 ++--- readthedocs/rtd_tests/tests/test_views.py | 9 +------ readthedocs/rtd_tests/utils.py | 7 ----- readthedocs/search/indexes.py | 7 +---- readthedocs/search/lib.py | 14 +++++----- readthedocs/search/parse_json.py | 6 ++--- readthedocs/search/signals.py | 2 +- readthedocs/search/tests/conftest.py | 11 +++++--- readthedocs/search/tests/test_views.py | 1 - readthedocs/search/tests/utils.py | 1 - readthedocs/search/utils.py | 11 +++----- readthedocs/search/views.py | 4 +-- readthedocs/urls.py | 20 +++++++------- readthedocs/vcs_support/backends/bzr.py | 10 ------- readthedocs/vcs_support/backends/git.py | 9 +------ readthedocs/vcs_support/backends/hg.py | 7 ----- readthedocs/vcs_support/backends/svn.py | 10 ------- readthedocs/vcs_support/base.py | 4 --- readthedocs/vcs_support/tests.py | 4 --- readthedocs/vcs_support/utils.py | 3 --- readthedocs/worker.py | 2 -- 226 files changed, 415 insertions(+), 974 deletions(-) diff --git a/readthedocs/analytics/apps.py b/readthedocs/analytics/apps.py index afdea7f5dec..219a787dce7 100644 --- a/readthedocs/analytics/apps.py +++ b/readthedocs/analytics/apps.py @@ -1,6 +1,5 @@ """Django app config for the analytics app.""" -from __future__ import absolute_import from django.apps import AppConfig diff --git a/readthedocs/analytics/tasks.py b/readthedocs/analytics/tasks.py index 6c1ec2cfce1..b79102fc6fb 100644 --- a/readthedocs/analytics/tasks.py +++ b/readthedocs/analytics/tasks.py @@ -1,7 +1,5 @@ """Tasks for Read the Docs' analytics""" -from __future__ import absolute_import - from django.conf import settings import readthedocs diff --git a/readthedocs/analytics/tests.py b/readthedocs/analytics/tests.py index 37b26957033..93435551684 100644 --- a/readthedocs/analytics/tests.py +++ b/readthedocs/analytics/tests.py @@ -1,10 +1,7 @@ -from __future__ import absolute_import, unicode_literals - from django.test import TestCase from .utils import anonymize_ip_address, anonymize_user_agent - class UtilsTests(TestCase): def test_anonymize_ip(self): self.assertEqual(anonymize_ip_address('127.0.0.1'), '127.0.0.0') @@ -29,4 +26,3 @@ def test_anonymize_ua(self): anonymize_user_agent('Some rare user agent'), 'Rare user agent', ) - diff --git a/readthedocs/analytics/utils.py b/readthedocs/analytics/utils.py index 44eef551125..52c44d5cd35 100644 --- a/readthedocs/analytics/utils.py +++ b/readthedocs/analytics/utils.py @@ -1,15 +1,15 @@ """Utilities related to analytics""" -from __future__ import absolute_import, unicode_literals import hashlib import logging +import requests from django.conf import settings -from django.utils.encoding import force_text, force_bytes from django.utils.crypto import get_random_string -import requests +from django.utils.encoding import force_bytes, force_text from user_agents import parse + try: # Python 3.3+ only import ipaddress diff --git a/readthedocs/analytics/vendor/ipaddress.py b/readthedocs/analytics/vendor/ipaddress.py index b81d477bf96..8014d2ef777 100644 --- a/readthedocs/analytics/vendor/ipaddress.py +++ b/readthedocs/analytics/vendor/ipaddress.py @@ -9,12 +9,10 @@ """ -from __future__ import unicode_literals - - import itertools import struct + __version__ = '1.0.22' # Compatibility functions diff --git a/readthedocs/api/base.py b/readthedocs/api/base.py index 8e90c279b62..75e984d422e 100644 --- a/readthedocs/api/base.py +++ b/readthedocs/api/base.py @@ -1,10 +1,6 @@ # -*- coding: utf-8 -*- """API resources.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging -from builtins import object import redis from django.conf.urls import url @@ -25,6 +21,7 @@ from .utils import PostAuthentication + log = logging.getLogger(__name__) diff --git a/readthedocs/api/client.py b/readthedocs/api/client.py index dd742198b15..a3020163449 100644 --- a/readthedocs/api/client.py +++ b/readthedocs/api/client.py @@ -1,16 +1,14 @@ # -*- coding: utf-8 -*- """Slumber API client.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging -from django.conf import settings import requests +from django.conf import settings from requests_toolbelt.adapters import host_header_ssl from slumber import API + log = logging.getLogger(__name__) PRODUCTION_DOMAIN = getattr(settings, 'PRODUCTION_DOMAIN', 'readthedocs.org') diff --git a/readthedocs/api/utils.py b/readthedocs/api/utils.py index c15cb02ec02..e2b5e9c331a 100644 --- a/readthedocs/api/utils.py +++ b/readthedocs/api/utils.py @@ -1,13 +1,11 @@ """Utility classes for api module""" -from __future__ import absolute_import import logging from django.utils.translation import ugettext - from tastypie.authentication import BasicAuthentication from tastypie.authorization import Authorization -from tastypie.resources import ModelResource from tastypie.exceptions import NotFound +from tastypie.resources import ModelResource log = logging.getLogger(__name__) diff --git a/readthedocs/builds/admin.py b/readthedocs/builds/admin.py index 66c046f9c3e..59fe235aef3 100644 --- a/readthedocs/builds/admin.py +++ b/readthedocs/builds/admin.py @@ -1,10 +1,10 @@ """Django admin interface for `~builds.models.Build` and related models.""" -from __future__ import absolute_import from django.contrib import admin -from readthedocs.builds.models import Build, Version, BuildCommandResult from guardian.admin import GuardedModelAdmin +from readthedocs.builds.models import Build, BuildCommandResult, Version + class BuildCommandResultInline(admin.TabularInline): model = BuildCommandResult diff --git a/readthedocs/builds/constants.py b/readthedocs/builds/constants.py index 99d816814ba..660fb0b0f4b 100644 --- a/readthedocs/builds/constants.py +++ b/readthedocs/builds/constants.py @@ -1,8 +1,8 @@ """Constants for the builds app.""" -from __future__ import absolute_import -from django.utils.translation import ugettext_lazy as _ from django.conf import settings +from django.utils.translation import ugettext_lazy as _ + BUILD_STATE_TRIGGERED = 'triggered' BUILD_STATE_CLONING = 'cloning' diff --git a/readthedocs/builds/forms.py b/readthedocs/builds/forms.py index 6e5e94b50ee..ae655bfc90e 100644 --- a/readthedocs/builds/forms.py +++ b/readthedocs/builds/forms.py @@ -1,13 +1,5 @@ """Django forms for the builds app.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - -from builtins import object from django import forms from django.utils.translation import ugettext_lazy as _ diff --git a/readthedocs/builds/managers.py b/readthedocs/builds/managers.py index bd9316c3ef3..bd886605886 100644 --- a/readthedocs/builds/managers.py +++ b/readthedocs/builds/managers.py @@ -1,14 +1,21 @@ """Build and Version class model Managers""" -from __future__ import absolute_import - from django.db import models -from .constants import (BRANCH, TAG, LATEST, LATEST_VERBOSE_NAME, STABLE, - STABLE_VERBOSE_NAME) +from readthedocs.core.utils.extend import ( + SettingsOverrideObject, + get_override_class, +) + +from .constants import ( + BRANCH, + LATEST, + LATEST_VERBOSE_NAME, + STABLE, + STABLE_VERBOSE_NAME, + TAG, +) from .querysets import VersionQuerySet -from readthedocs.core.utils.extend import (SettingsOverrideObject, - get_override_class) __all__ = ['VersionManager'] diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index b27b7e145af..c11fa13d0a0 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -1,21 +1,14 @@ # -*- coding: utf-8 -*- """Models for the builds app.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging import os.path import re from shutil import rmtree -from builtins import object from django.conf import settings from django.db import models +from django.urls import reverse from django.utils import timezone from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext @@ -23,7 +16,6 @@ from guardian.shortcuts import assign from jsonfield import JSONField from taggit.managers import TaggableManager -from django.urls import reverse from readthedocs.core.utils import broadcast from readthedocs.projects.constants import ( @@ -55,6 +47,7 @@ ) from .version_slug import VersionSlugField + DEFAULT_VERSION_PRIVACY_LEVEL = getattr( settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public') diff --git a/readthedocs/builds/querysets.py b/readthedocs/builds/querysets.py index 34408b9d982..a2a34938836 100644 --- a/readthedocs/builds/querysets.py +++ b/readthedocs/builds/querysets.py @@ -1,7 +1,5 @@ """Build and Version QuerySet classes""" -from __future__ import absolute_import - from django.db import models from guardian.shortcuts import get_objects_for_user diff --git a/readthedocs/builds/signals.py b/readthedocs/builds/signals.py index c80f7d3e42d..c9bb49cc7cc 100644 --- a/readthedocs/builds/signals.py +++ b/readthedocs/builds/signals.py @@ -1,6 +1,5 @@ """Build signals""" -from __future__ import absolute_import import django.dispatch diff --git a/readthedocs/builds/syncers.py b/readthedocs/builds/syncers.py index cf5a9a90e07..5f02f966bf7 100644 --- a/readthedocs/builds/syncers.py +++ b/readthedocs/builds/syncers.py @@ -5,18 +5,15 @@ local machine. """ -from __future__ import absolute_import - import getpass import logging import os import shutil -from builtins import object from django.conf import settings -from readthedocs.core.utils.extend import SettingsOverrideObject from readthedocs.core.utils import safe_makedirs +from readthedocs.core.utils.extend import SettingsOverrideObject log = logging.getLogger(__name__) diff --git a/readthedocs/builds/urls.py b/readthedocs/builds/urls.py index a148362a51b..07f787d0b4b 100644 --- a/readthedocs/builds/urls.py +++ b/readthedocs/builds/urls.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- """URL configuration for builds app.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - from django.conf.urls import url from .views import builds_redirect_detail, builds_redirect_list + urlpatterns = [ url( r'^(?P[-\w]+)/(?P\d+)/$', diff --git a/readthedocs/builds/utils.py b/readthedocs/builds/utils.py index 7fcc245dbb8..d2d49270cb2 100644 --- a/readthedocs/builds/utils.py +++ b/readthedocs/builds/utils.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- """Utilities for the builds app.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - from readthedocs.projects.constants import ( - BITBUCKET_REGEXS, GITHUB_REGEXS, GITLAB_REGEXS) + BITBUCKET_REGEXS, + GITHUB_REGEXS, + GITLAB_REGEXS, +) def get_github_username_repo(url): diff --git a/readthedocs/builds/version_slug.py b/readthedocs/builds/version_slug.py index 7eb71f82798..18ad3d19f99 100644 --- a/readthedocs/builds/version_slug.py +++ b/readthedocs/builds/version_slug.py @@ -17,8 +17,6 @@ another number would be confusing. """ -from __future__ import absolute_import - import math import re import string @@ -26,7 +24,6 @@ from django.db import models from django.utils.encoding import force_text -from builtins import range def get_fields_with_model(cls): diff --git a/readthedocs/builds/views.py b/readthedocs/builds/views.py index 680bd271c29..37a46ae3ece 100644 --- a/readthedocs/builds/views.py +++ b/readthedocs/builds/views.py @@ -2,16 +2,8 @@ """Views for builds app.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging -from builtins import object from django.contrib import messages from django.contrib.auth.decorators import login_required from django.http import ( diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index 900fe637e2e..57e01439e0a 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -3,8 +3,6 @@ # pylint: disable=too-many-lines """Build configuration for rtd.""" -from __future__ import division, print_function, unicode_literals - import os import re from contextlib import contextmanager @@ -26,6 +24,7 @@ validate_string, ) + __all__ = ( 'ALL', 'load', diff --git a/readthedocs/config/find.py b/readthedocs/config/find.py index cb3e5e0c56d..ed49c765c1d 100644 --- a/readthedocs/config/find.py +++ b/readthedocs/config/find.py @@ -1,7 +1,5 @@ """Helper functions to search files.""" -from __future__ import division, print_function, unicode_literals - import os import re diff --git a/readthedocs/config/models.py b/readthedocs/config/models.py index bf12ddfa6d4..ce0604f18b7 100644 --- a/readthedocs/config/models.py +++ b/readthedocs/config/models.py @@ -1,7 +1,5 @@ """Models for the response of the configuration object.""" -from __future__ import division, print_function, unicode_literals - from collections import namedtuple diff --git a/readthedocs/config/parser.py b/readthedocs/config/parser.py index 655b1601bf7..85e31782b00 100644 --- a/readthedocs/config/parser.py +++ b/readthedocs/config/parser.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- """YAML parser for the RTD configuration file.""" -from __future__ import division, print_function, unicode_literals - import yaml + __all__ = ('parse', 'ParseError') diff --git a/readthedocs/config/tests/test_config.py b/readthedocs/config/tests/test_config.py index 29b776d0fb5..e3e68110e99 100644 --- a/readthedocs/config/tests/test_config.py +++ b/readthedocs/config/tests/test_config.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - import os import re import textwrap diff --git a/readthedocs/config/tests/test_find.py b/readthedocs/config/tests/test_find.py index 7ba651059e3..dd4715f20d9 100644 --- a/readthedocs/config/tests/test_find.py +++ b/readthedocs/config/tests/test_find.py @@ -1,13 +1,11 @@ -from __future__ import division, print_function, unicode_literals - import os + import pytest -import six + from readthedocs.config.find import find_one from .utils import apply_fs - def test_find_no_files(tmpdir): with tmpdir.as_cwd(): path = find_one(os.getcwd(), r'readthedocs.yml') diff --git a/readthedocs/config/tests/test_parser.py b/readthedocs/config/tests/test_parser.py index 4cbe457bbe8..37476af9162 100644 --- a/readthedocs/config/tests/test_parser.py +++ b/readthedocs/config/tests/test_parser.py @@ -1,12 +1,9 @@ -from __future__ import division, print_function, unicode_literals - from io import StringIO from pytest import raises from readthedocs.config.parser import ParseError, parse - def test_parse_empty_config_file(): buf = StringIO('') with raises(ParseError): diff --git a/readthedocs/config/tests/test_utils.py b/readthedocs/config/tests/test_utils.py index 3d89cb0d0c1..2332b30716b 100644 --- a/readthedocs/config/tests/test_utils.py +++ b/readthedocs/config/tests/test_utils.py @@ -1,8 +1,5 @@ -from __future__ import division, print_function, unicode_literals - from .utils import apply_fs - def test_apply_fs_with_empty_contents(tmpdir): # Doesn't do anything if second parameter is empty. apply_fs(tmpdir, {}) diff --git a/readthedocs/config/tests/test_validation.py b/readthedocs/config/tests/test_validation.py index 6437acfbff2..fd7d9e781fb 100644 --- a/readthedocs/config/tests/test_validation.py +++ b/readthedocs/config/tests/test_validation.py @@ -1,18 +1,26 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - import os from mock import patch from pytest import raises -from six import text_type from readthedocs.config.validation import ( - INVALID_BOOL, INVALID_CHOICE, INVALID_DIRECTORY, INVALID_FILE, INVALID_LIST, - INVALID_PATH, INVALID_STRING, ValidationError, validate_bool, - validate_choice, validate_directory, validate_file, validate_list, - validate_path, validate_string) - + INVALID_BOOL, + INVALID_CHOICE, + INVALID_DIRECTORY, + INVALID_FILE, + INVALID_LIST, + INVALID_PATH, + INVALID_STRING, + ValidationError, + validate_bool, + validate_choice, + validate_directory, + validate_file, + validate_list, + validate_path, + validate_string, +) class TestValidateBool: def test_it_accepts_true(self): diff --git a/readthedocs/config/tests/utils.py b/readthedocs/config/tests/utils.py index b1b312420bb..1e390fa8a75 100644 --- a/readthedocs/config/tests/utils.py +++ b/readthedocs/config/tests/utils.py @@ -1,5 +1,3 @@ -from __future__ import division, print_function, unicode_literals - def apply_fs(tmpdir, contents): """ diff --git a/readthedocs/config/validation.py b/readthedocs/config/validation.py index baf7808d61e..a69b8ed7769 100644 --- a/readthedocs/config/validation.py +++ b/readthedocs/config/validation.py @@ -1,8 +1,7 @@ """Validations for the RTD configuration file.""" -from __future__ import division, print_function, unicode_literals - import os + INVALID_BOOL = 'invalid-bool' INVALID_CHOICE = 'invalid-choice' INVALID_LIST = 'invalid-list' diff --git a/readthedocs/constants.py b/readthedocs/constants.py index b9796e8f998..35cca627432 100644 --- a/readthedocs/constants.py +++ b/readthedocs/constants.py @@ -1,6 +1,5 @@ """Common constants""" -from __future__ import absolute_import from readthedocs.builds.version_slug import VERSION_SLUG_REGEX from readthedocs.projects.constants import LANGUAGES_REGEX, PROJECT_SLUG_REGEX diff --git a/readthedocs/core/adapters.py b/readthedocs/core/adapters.py index 5bfbf6d5c2e..803952a792d 100644 --- a/readthedocs/core/adapters.py +++ b/readthedocs/core/adapters.py @@ -1,6 +1,5 @@ """Allauth overrides""" -from __future__ import absolute_import import json import logging @@ -9,6 +8,7 @@ from readthedocs.core.utils import send_email + try: from django.utils.encoding import force_text except ImportError: diff --git a/readthedocs/core/admin.py b/readthedocs/core/admin.py index b30f5460484..5e8042f1202 100644 --- a/readthedocs/core/admin.py +++ b/readthedocs/core/admin.py @@ -1,13 +1,12 @@ """Django admin interface for core models.""" -from __future__ import absolute_import from datetime import timedelta from django.contrib import admin -from django.contrib.auth.models import User from django.contrib.auth.admin import UserAdmin -from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import User from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ from readthedocs.core.models import UserProfile from readthedocs.projects.models import Project diff --git a/readthedocs/core/apps.py b/readthedocs/core/apps.py index 4a9875ffb13..d422684ba03 100644 --- a/readthedocs/core/apps.py +++ b/readthedocs/core/apps.py @@ -1,6 +1,5 @@ """App configurations for core app.""" -from __future__ import absolute_import from django.apps import AppConfig diff --git a/readthedocs/core/backends.py b/readthedocs/core/backends.py index 6a8b8ec9007..64258fe91eb 100644 --- a/readthedocs/core/backends.py +++ b/readthedocs/core/backends.py @@ -1,10 +1,9 @@ """Email backends for core app.""" -from __future__ import absolute_import import smtplib -from django.core.mail.utils import DNS_NAME from django.core.mail.backends.smtp import EmailBackend +from django.core.mail.utils import DNS_NAME class SSLEmailBackend(EmailBackend): diff --git a/readthedocs/core/context_processors.py b/readthedocs/core/context_processors.py index 6ac71df2ac7..dc136c51171 100644 --- a/readthedocs/core/context_processors.py +++ b/readthedocs/core/context_processors.py @@ -1,6 +1,5 @@ """Template context processors for core app.""" -from __future__ import absolute_import from django.conf import settings diff --git a/readthedocs/core/fields.py b/readthedocs/core/fields.py index 87f06090870..822ad5ca151 100644 --- a/readthedocs/core/fields.py +++ b/readthedocs/core/fields.py @@ -1,6 +1,5 @@ """Shared model fields and defaults""" -from __future__ import absolute_import import binascii import os diff --git a/readthedocs/core/forms.py b/readthedocs/core/forms.py index fdc93ffe28b..66c09e84b93 100644 --- a/readthedocs/core/forms.py +++ b/readthedocs/core/forms.py @@ -1,11 +1,7 @@ # -*- coding: utf-8 -*- """Forms for core app.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging -from builtins import object from django import forms from django.contrib.auth.models import User @@ -14,6 +10,7 @@ from .models import UserProfile + log = logging.getLogger(__name__) diff --git a/readthedocs/core/management/commands/archive.py b/readthedocs/core/management/commands/archive.py index 33a35bc56c3..3402d1823b4 100644 --- a/readthedocs/core/management/commands/archive.py +++ b/readthedocs/core/management/commands/archive.py @@ -1,15 +1,14 @@ """Rebuild documentation for all projects""" -from __future__ import absolute_import -from __future__ import print_function -from glob import glob -import os import logging +import os +from glob import glob from django.conf import settings from django.core.management.base import BaseCommand from django.template import loader as template_loader + log = logging.getLogger(__name__) diff --git a/readthedocs/core/management/commands/clean_builds.py b/readthedocs/core/management/commands/clean_builds.py index e47a651cf8a..1a48748a963 100644 --- a/readthedocs/core/management/commands/clean_builds.py +++ b/readthedocs/core/management/commands/clean_builds.py @@ -1,8 +1,7 @@ """Clean up stable build paths per project version""" -from __future__ import absolute_import -from datetime import timedelta import logging +from datetime import timedelta from optparse import make_option from django.core.management.base import BaseCommand @@ -11,6 +10,7 @@ from readthedocs.builds.models import Build, Version + log = logging.getLogger(__name__) diff --git a/readthedocs/core/management/commands/import_github.py b/readthedocs/core/management/commands/import_github.py index 3bb34b1b4ad..2e2e49176b7 100644 --- a/readthedocs/core/management/commands/import_github.py +++ b/readthedocs/core/management/commands/import_github.py @@ -1,8 +1,7 @@ """Resync GitHub project for user""" -from __future__ import absolute_import -from django.core.management.base import BaseCommand from django.contrib.auth.models import User +from django.core.management.base import BaseCommand from readthedocs.oauth.services import GitHubService diff --git a/readthedocs/core/management/commands/import_github_language.py b/readthedocs/core/management/commands/import_github_language.py index f53a8088763..1e42685a57e 100644 --- a/readthedocs/core/management/commands/import_github_language.py +++ b/readthedocs/core/management/commands/import_github_language.py @@ -8,16 +8,15 @@ which should contain a proper GitHub Oauth Token for rate limiting. """ -from __future__ import absolute_import -from __future__ import print_function import os -import requests -from django.core.management.base import BaseCommand +import requests from django.core.cache import cache +from django.core.management.base import BaseCommand -from readthedocs.projects.models import Project from readthedocs.projects.constants import GITHUB_REGEXS, PROGRAMMING_LANGUAGES +from readthedocs.projects.models import Project + PL_DICT = {} diff --git a/readthedocs/core/management/commands/provision_elasticsearch.py b/readthedocs/core/management/commands/provision_elasticsearch.py index 9f29fa37a9e..690a45803d9 100644 --- a/readthedocs/core/management/commands/provision_elasticsearch.py +++ b/readthedocs/core/management/commands/provision_elasticsearch.py @@ -1,11 +1,16 @@ """Provision Elastic Search""" -from __future__ import absolute_import import logging from django.core.management.base import BaseCommand -from readthedocs.search.indexes import Index, PageIndex, ProjectIndex, SectionIndex +from readthedocs.search.indexes import ( + Index, + PageIndex, + ProjectIndex, + SectionIndex, +) + log = logging.getLogger(__name__) diff --git a/readthedocs/core/management/commands/pull.py b/readthedocs/core/management/commands/pull.py index 3540a35c077..f0ebe4206bd 100644 --- a/readthedocs/core/management/commands/pull.py +++ b/readthedocs/core/management/commands/pull.py @@ -1,6 +1,5 @@ """Trigger build for project slug""" -from __future__ import absolute_import import logging from django.core.management.base import BaseCommand diff --git a/readthedocs/core/management/commands/reindex_elasticsearch.py b/readthedocs/core/management/commands/reindex_elasticsearch.py index 7a5f25a065a..9d2ab83495a 100644 --- a/readthedocs/core/management/commands/reindex_elasticsearch.py +++ b/readthedocs/core/management/commands/reindex_elasticsearch.py @@ -1,17 +1,16 @@ """Reindex Elastic Search indexes""" -from __future__ import absolute_import import logging from optparse import make_option -from django.core.management.base import BaseCommand -from django.core.management.base import CommandError from django.conf import settings +from django.core.management.base import BaseCommand, CommandError from readthedocs.builds.constants import LATEST from readthedocs.builds.models import Version from readthedocs.projects.tasks import update_search + log = logging.getLogger(__name__) diff --git a/readthedocs/core/management/commands/set_metadata.py b/readthedocs/core/management/commands/set_metadata.py index dbefcddbdd0..e8d17a9dbf1 100644 --- a/readthedocs/core/management/commands/set_metadata.py +++ b/readthedocs/core/management/commands/set_metadata.py @@ -1,13 +1,13 @@ """Generate metadata for all projects""" -from __future__ import absolute_import import logging from django.core.management.base import BaseCommand +from readthedocs.core.utils import broadcast from readthedocs.projects import tasks from readthedocs.projects.models import Project -from readthedocs.core.utils import broadcast + log = logging.getLogger(__name__) diff --git a/readthedocs/core/management/commands/symlink.py b/readthedocs/core/management/commands/symlink.py index e5c039c622b..3fda03a41db 100644 --- a/readthedocs/core/management/commands/symlink.py +++ b/readthedocs/core/management/commands/symlink.py @@ -1,14 +1,13 @@ """Update symlinks for projects""" -from __future__ import absolute_import import logging from django.core.management.base import BaseCommand from readthedocs.projects import tasks - from readthedocs.projects.models import Project + log = logging.getLogger(__name__) diff --git a/readthedocs/core/management/commands/update_api.py b/readthedocs/core/management/commands/update_api.py index e95b23b1899..3a9b44b1c9e 100644 --- a/readthedocs/core/management/commands/update_api.py +++ b/readthedocs/core/management/commands/update_api.py @@ -6,7 +6,6 @@ ./manage.py update_api """ -from __future__ import absolute_import import logging from django.core.management.base import BaseCommand diff --git a/readthedocs/core/management/commands/update_repos.py b/readthedocs/core/management/commands/update_repos.py index b5233d2d6df..17d0951909e 100644 --- a/readthedocs/core/management/commands/update_repos.py +++ b/readthedocs/core/management/commands/update_repos.py @@ -6,9 +6,6 @@ Invoked via ``./manage.py update_repos``. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging from django.core.management.base import BaseCommand @@ -18,6 +15,7 @@ from readthedocs.projects import tasks from readthedocs.projects.models import Project + log = logging.getLogger(__name__) diff --git a/readthedocs/core/management/commands/update_versions.py b/readthedocs/core/management/commands/update_versions.py index 8961d6706cf..9cc0c297130 100644 --- a/readthedocs/core/management/commands/update_versions.py +++ b/readthedocs/core/management/commands/update_versions.py @@ -1,6 +1,5 @@ """Rebuild documentation for all projects""" -from __future__ import absolute_import from django.core.management.base import BaseCommand from readthedocs.builds.models import Version diff --git a/readthedocs/core/middleware.py b/readthedocs/core/middleware.py index 4556d92ff90..967a4e84aa9 100644 --- a/readthedocs/core/middleware.py +++ b/readthedocs/core/middleware.py @@ -1,22 +1,20 @@ """Middleware for core app.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging from django.conf import settings from django.contrib.sessions.middleware import SessionMiddleware from django.core.cache import cache from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist -from django.urls.base import get_urlconf, set_urlconf from django.http import Http404, HttpResponseBadRequest +from django.urls.base import get_urlconf, set_urlconf from django.utils.deprecation import MiddlewareMixin from django.utils.translation import ugettext_lazy as _ from readthedocs.core.utils import cname_to_slug from readthedocs.projects.models import Domain, Project + log = logging.getLogger(__name__) LOG_TEMPLATE = "(Middleware) {msg} [{host}{path}]" diff --git a/readthedocs/core/mixins.py b/readthedocs/core/mixins.py index 45bf2be75ab..2460daacd13 100644 --- a/readthedocs/core/mixins.py +++ b/readthedocs/core/mixins.py @@ -1,10 +1,8 @@ """Common mixin classes for views""" -from __future__ import absolute_import -from builtins import object -from vanilla import ListView from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator +from vanilla import ListView class ListViewWithForm(ListView): diff --git a/readthedocs/core/models.py b/readthedocs/core/models.py index 8b72a79199f..6f4f019ae44 100644 --- a/readthedocs/core/models.py +++ b/readthedocs/core/models.py @@ -1,16 +1,14 @@ # -*- coding: utf-8 -*- """Models for the core app.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging from annoying.fields import AutoOneToOneField from django.db import models +from django.urls import reverse from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext -from django.urls import reverse +from django.utils.translation import ugettext_lazy as _ + STANDARD_EMAIL = 'anonymous@readthedocs.org' diff --git a/readthedocs/core/permissions.py b/readthedocs/core/permissions.py index ad2427b9b25..6af5f2a58b8 100644 --- a/readthedocs/core/permissions.py +++ b/readthedocs/core/permissions.py @@ -1,7 +1,5 @@ """Objects for User permission checks""" -from __future__ import absolute_import - from readthedocs.core.utils.extend import SettingsOverrideObject diff --git a/readthedocs/core/resolver.py b/readthedocs/core/resolver.py index a17e832160c..5e0b27492aa 100644 --- a/readthedocs/core/resolver.py +++ b/readthedocs/core/resolver.py @@ -1,13 +1,11 @@ """URL resolver for documentation.""" -from __future__ import absolute_import -from builtins import object import re from django.conf import settings -from readthedocs.projects.constants import PRIVATE, PUBLIC from readthedocs.core.utils.extend import SettingsOverrideObject +from readthedocs.projects.constants import PRIVATE, PUBLIC class ResolverBase: diff --git a/readthedocs/core/settings.py b/readthedocs/core/settings.py index b758cb57fba..5967893cc0e 100644 --- a/readthedocs/core/settings.py +++ b/readthedocs/core/settings.py @@ -1,7 +1,5 @@ """Class based settings for complex settings inheritance.""" -from __future__ import absolute_import -from builtins import object import inspect import sys diff --git a/readthedocs/core/signals.py b/readthedocs/core/signals.py index d8b90b57d82..875cde381d9 100644 --- a/readthedocs/core/signals.py +++ b/readthedocs/core/signals.py @@ -2,21 +2,19 @@ """Signal handling for core app.""" -from __future__ import absolute_import - import logging from corsheaders import signals from django.conf import settings +from django.db.models import Count, Q from django.db.models.signals import pre_delete -from django.dispatch import Signal -from django.db.models import Q, Count -from django.dispatch import receiver +from django.dispatch import Signal, receiver from future.backports.urllib.parse import urlparse from rest_framework.permissions import SAFE_METHODS from readthedocs.oauth.models import RemoteOrganization -from readthedocs.projects.models import Project, Domain +from readthedocs.projects.models import Domain, Project + log = logging.getLogger(__name__) diff --git a/readthedocs/core/static.py b/readthedocs/core/static.py index b7ec2b7962f..71d433b259c 100644 --- a/readthedocs/core/static.py +++ b/readthedocs/core/static.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - from django.contrib.staticfiles.finders import FileSystemFinder diff --git a/readthedocs/core/symlink.py b/readthedocs/core/symlink.py index dcaad60bc3e..bde0d2d454f 100644 --- a/readthedocs/core/symlink.py +++ b/readthedocs/core/symlink.py @@ -52,19 +52,11 @@ fabric -> rtd-builds/fabric/en/latest/ # single version """ -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging import os import shutil from collections import OrderedDict -from builtins import object from django.conf import settings from readthedocs.builds.models import Version @@ -74,6 +66,7 @@ from readthedocs.projects import constants from readthedocs.projects.models import Domain + log = logging.getLogger(__name__) diff --git a/readthedocs/core/tasks.py b/readthedocs/core/tasks.py index 8ed81f1bb1e..446a19b7ea5 100644 --- a/readthedocs/core/tasks.py +++ b/readthedocs/core/tasks.py @@ -1,12 +1,11 @@ """Basic tasks.""" -from __future__ import absolute_import import logging from django.conf import settings from django.core.mail import EmailMultiAlternatives -from django.template.loader import get_template from django.template import TemplateDoesNotExist +from django.template.loader import get_template from django.utils import timezone from messages_extends.models import Message as PersistentMessage diff --git a/readthedocs/core/templatetags/core_tags.py b/readthedocs/core/templatetags/core_tags.py index e91c1fb667e..d05831aade9 100644 --- a/readthedocs/core/templatetags/core_tags.py +++ b/readthedocs/core/templatetags/core_tags.py @@ -1,10 +1,7 @@ """Template tags for core app.""" -from __future__ import absolute_import - import hashlib -from builtins import str # pylint: disable=redefined-builtin from django import template from django.conf import settings from django.utils.encoding import force_bytes, force_text diff --git a/readthedocs/core/templatetags/privacy_tags.py b/readthedocs/core/templatetags/privacy_tags.py index d18778778f6..814018dc9df 100644 --- a/readthedocs/core/templatetags/privacy_tags.py +++ b/readthedocs/core/templatetags/privacy_tags.py @@ -1,7 +1,5 @@ """Template tags to query projects by privacy.""" -from __future__ import absolute_import - from django import template from readthedocs.core.permissions import AdminPermission diff --git a/readthedocs/core/tests/test_signals.py b/readthedocs/core/tests/test_signals.py index 6c906c9e87d..f96bb30d566 100644 --- a/readthedocs/core/tests/test_signals.py +++ b/readthedocs/core/tests/test_signals.py @@ -1,12 +1,10 @@ -import pytest import django_dynamic_fixture - +import pytest from django.contrib.auth.models import User from readthedocs.oauth.models import RemoteOrganization from readthedocs.projects.models import Project - @pytest.mark.django_db class TestProjectOrganizationSignal: diff --git a/readthedocs/core/urls/single_version.py b/readthedocs/core/urls/single_version.py index afc84bda83f..b232ef9ae96 100644 --- a/readthedocs/core/urls/single_version.py +++ b/readthedocs/core/urls/single_version.py @@ -1,16 +1,15 @@ """URL configuration for a single version.""" -from __future__ import absolute_import - from functools import reduce from operator import add -from django.conf.urls import url from django.conf import settings +from django.conf.urls import url from django.conf.urls.static import static from readthedocs.constants import pattern_opts from readthedocs.core.views import serve + handler500 = 'readthedocs.core.views.server_error_500' handler404 = 'readthedocs.core.views.server_error_404' diff --git a/readthedocs/core/urls/subdomain.py b/readthedocs/core/urls/subdomain.py index 826c6443660..7b5b752fc88 100644 --- a/readthedocs/core/urls/subdomain.py +++ b/readthedocs/core/urls/subdomain.py @@ -1,22 +1,19 @@ """URL configurations for subdomains.""" -from __future__ import absolute_import - from functools import reduce from operator import add -from django.conf.urls import url from django.conf import settings +from django.conf.urls import url from django.conf.urls.static import static +from readthedocs.constants import pattern_opts +from readthedocs.core.views import server_error_404, server_error_500 from readthedocs.core.views.serve import ( redirect_page_with_filename, - redirect_project_slug, serve_docs + redirect_project_slug, + serve_docs, ) -from readthedocs.core.views import ( - server_error_500, - server_error_404, -) -from readthedocs.constants import pattern_opts + handler500 = server_error_500 handler404 = server_error_404 diff --git a/readthedocs/core/utils/extend.py b/readthedocs/core/utils/extend.py index 567c0c23a4d..c01c76eee86 100644 --- a/readthedocs/core/utils/extend.py +++ b/readthedocs/core/utils/extend.py @@ -1,11 +1,9 @@ """Patterns for extending Read the Docs.""" -from __future__ import absolute_import import inspect from django.conf import settings from django.utils.module_loading import import_string -import six def get_override_class(proxy_class, default_class=None): diff --git a/readthedocs/core/utils/tasks/public.py b/readthedocs/core/utils/tasks/public.py index 1e51fc65b29..2c1ccaa9e0d 100644 --- a/readthedocs/core/utils/tasks/public.py +++ b/readthedocs/core/utils/tasks/public.py @@ -1,17 +1,11 @@ """Celery tasks with publicly viewable status""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from celery import Task, states from django.conf import settings from .retrieve import TaskNotFound, get_task_data + __all__ = ( 'PublicTask', 'TaskNoPermission', 'get_public_task_data' ) diff --git a/readthedocs/core/utils/tasks/retrieve.py b/readthedocs/core/utils/tasks/retrieve.py index 0aa3e7570f0..4c592116995 100644 --- a/readthedocs/core/utils/tasks/retrieve.py +++ b/readthedocs/core/utils/tasks/retrieve.py @@ -1,15 +1,9 @@ """Utilities for retrieving task data.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from celery import states from celery.result import AsyncResult + __all__ = ('TaskNotFound', 'get_task_data') diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index 3156e3cd4fb..a92640e127b 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -1,12 +1,5 @@ """Views pertaining to builds.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import json import logging import re @@ -21,6 +14,7 @@ from readthedocs.projects.models import Feature, Project from readthedocs.projects.tasks import sync_repository_task + log = logging.getLogger(__name__) diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index d663dc79180..7f8bb3651aa 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -25,18 +25,14 @@ SERVE_DOCS (['private']) - The list of ['private', 'public'] docs to serve. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging import mimetypes import os from functools import wraps from django.conf import settings -from django.http import HttpResponse, HttpResponseRedirect, Http404 -from django.shortcuts import get_object_or_404 -from django.shortcuts import render +from django.http import Http404, HttpResponse, HttpResponseRedirect +from django.shortcuts import get_object_or_404, render from django.views.static import serve from readthedocs.builds.models import Version @@ -46,6 +42,7 @@ from readthedocs.projects import constants from readthedocs.projects.models import Project, ProjectRelationship + log = logging.getLogger(__name__) diff --git a/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py index eeb06379c13..65b35f5f28f 100644 --- a/readthedocs/doc_builder/backends/mkdocs.py +++ b/readthedocs/doc_builder/backends/mkdocs.py @@ -3,9 +3,6 @@ .. _MkDocs: http://www.mkdocs.org/ """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import json import logging import os @@ -18,6 +15,7 @@ from readthedocs.doc_builder.exceptions import MkDocsYAMLParseError from readthedocs.projects.models import Feature + log = logging.getLogger(__name__) diff --git a/readthedocs/doc_builder/backends/sphinx.py b/readthedocs/doc_builder/backends/sphinx.py index 4f2f7b33829..8402cfd7e1c 100644 --- a/readthedocs/doc_builder/backends/sphinx.py +++ b/readthedocs/doc_builder/backends/sphinx.py @@ -4,27 +4,23 @@ .. _Sphinx: http://www.sphinx-doc.org/ """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import codecs -import shutil import logging import os +import shutil import sys import zipfile from glob import glob -import six from django.conf import settings from django.template import loader as template_loader from django.template.loader import render_to_string from readthedocs.builds import utils as version_utils from readthedocs.projects.exceptions import ProjectConfigurationError +from readthedocs.projects.models import Feature from readthedocs.projects.utils import safe_write from readthedocs.restapi.client import api -from readthedocs.projects.models import Feature from ..base import BaseBuilder, restoring_chdir from ..constants import PDF_RE @@ -32,6 +28,7 @@ from ..exceptions import BuildEnvironmentError from ..signals import finalize_sphinx_context_data + log = logging.getLogger(__name__) diff --git a/readthedocs/doc_builder/base.py b/readthedocs/doc_builder/base.py index c382fefc393..8810ab5f782 100644 --- a/readthedocs/doc_builder/base.py +++ b/readthedocs/doc_builder/base.py @@ -1,15 +1,12 @@ # -*- coding: utf-8 -*- """Base classes for Builders.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging import os import shutil -from builtins import object from functools import wraps + log = logging.getLogger(__name__) diff --git a/readthedocs/doc_builder/config.py b/readthedocs/doc_builder/config.py index 00a0095bcc6..9eabeaa0ce0 100644 --- a/readthedocs/doc_builder/config.py +++ b/readthedocs/doc_builder/config.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- """An API to load config from a readthedocs.yml file.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - from os import path from readthedocs.config import BuildConfigV1, ConfigError, InvalidConfig diff --git a/readthedocs/doc_builder/constants.py b/readthedocs/doc_builder/constants.py index 1cd1a5b1348..f2f15010bba 100644 --- a/readthedocs/doc_builder/constants.py +++ b/readthedocs/doc_builder/constants.py @@ -1,15 +1,13 @@ # -*- coding: utf-8 -*- """Doc build constants.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging import os import re from django.conf import settings + log = logging.getLogger(__name__) MKDOCS_TEMPLATE_DIR = os.path.join( diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py index 10ba06f5c60..cbdff522d92 100644 --- a/readthedocs/doc_builder/environments.py +++ b/readthedocs/doc_builder/environments.py @@ -2,13 +2,6 @@ """Documentation Builder Environments.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging import os import re @@ -18,8 +11,6 @@ import traceback from datetime import datetime -import six -from builtins import object, str from django.conf import settings from django.utils.translation import ugettext_lazy as _ from docker import APIClient diff --git a/readthedocs/doc_builder/exceptions.py b/readthedocs/doc_builder/exceptions.py index 80582595b00..5898dfce015 100644 --- a/readthedocs/doc_builder/exceptions.py +++ b/readthedocs/doc_builder/exceptions.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- """Exceptions raised when building documentation.""" -from __future__ import division, print_function, unicode_literals - from django.utils.translation import ugettext_noop diff --git a/readthedocs/doc_builder/loader.py b/readthedocs/doc_builder/loader.py index 0edcaace778..8d733b3d8a7 100644 --- a/readthedocs/doc_builder/loader.py +++ b/readthedocs/doc_builder/loader.py @@ -1,9 +1,9 @@ """Lookup tables for builders and backends.""" -from __future__ import absolute_import from importlib import import_module from django.conf import settings + # Managers mkdocs = import_module( getattr(settings, 'MKDOCS_BACKEND', diff --git a/readthedocs/doc_builder/python_environments.py b/readthedocs/doc_builder/python_environments.py index 7d85a275f83..92ec9a2f5f0 100644 --- a/readthedocs/doc_builder/python_environments.py +++ b/readthedocs/doc_builder/python_environments.py @@ -1,13 +1,6 @@ # -*- coding: utf-8 -*- """An abstraction over virtualenv and Conda environments.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import copy import itertools import json @@ -15,8 +8,6 @@ import os import shutil -import six -from builtins import object, open from django.conf import settings from readthedocs.doc_builder.config import load_yaml_config @@ -26,6 +17,7 @@ from readthedocs.projects.constants import LOG_TEMPLATE from readthedocs.projects.models import Feature + log = logging.getLogger(__name__) diff --git a/readthedocs/doc_builder/signals.py b/readthedocs/doc_builder/signals.py index 419531a5630..08991fabb03 100644 --- a/readthedocs/doc_builder/signals.py +++ b/readthedocs/doc_builder/signals.py @@ -1,9 +1,8 @@ """Signals for adding custom context data""" -from __future__ import absolute_import - import django.dispatch + finalize_sphinx_context_data = django.dispatch.Signal( providing_args=['buildenv', 'context', 'response_data'] ) diff --git a/readthedocs/gold/admin.py b/readthedocs/gold/admin.py index ecd512b75a8..a1dc3e80e45 100644 --- a/readthedocs/gold/admin.py +++ b/readthedocs/gold/admin.py @@ -1,7 +1,7 @@ """Django admin configuration for the Gold Membership app.""" -from __future__ import absolute_import from django.contrib import admin + from .models import GoldUser diff --git a/readthedocs/gold/apps.py b/readthedocs/gold/apps.py index eda30768d46..380a21f0947 100644 --- a/readthedocs/gold/apps.py +++ b/readthedocs/gold/apps.py @@ -1,6 +1,5 @@ """Django app configuration for the Gold Membership app.""" -from __future__ import absolute_import from django.apps import AppConfig diff --git a/readthedocs/gold/forms.py b/readthedocs/gold/forms.py index fead5e26b11..31c4a18aa1d 100644 --- a/readthedocs/gold/forms.py +++ b/readthedocs/gold/forms.py @@ -1,10 +1,6 @@ """Gold subscription forms.""" -from __future__ import absolute_import - -from builtins import object from django import forms - from django.utils.translation import ugettext_lazy as _ from readthedocs.payments.forms import StripeModelForm, StripeResourceMixin diff --git a/readthedocs/gold/models.py b/readthedocs/gold/models.py index 278dc60227e..c73ec79e39d 100644 --- a/readthedocs/gold/models.py +++ b/readthedocs/gold/models.py @@ -1,13 +1,6 @@ # -*- coding: utf-8 -*- """Django models for recurring donations aka Gold Membership.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import math from django.db import models @@ -16,6 +9,7 @@ from readthedocs.projects.models import Project + #: The membership options that are currently available LEVEL_CHOICES = ( ('v1-org-5', '$5/mo'), diff --git a/readthedocs/gold/signals.py b/readthedocs/gold/signals.py index a3cef14ca9e..4c089ebb0d5 100644 --- a/readthedocs/gold/signals.py +++ b/readthedocs/gold/signals.py @@ -1,6 +1,5 @@ """Gold model signals""" -from __future__ import absolute_import from django.db.models.signals import pre_delete from django.dispatch import receiver diff --git a/readthedocs/gold/tests/test_forms.py b/readthedocs/gold/tests/test_forms.py index acf1c82fe1e..5ffaf918384 100644 --- a/readthedocs/gold/tests/test_forms.py +++ b/readthedocs/gold/tests/test_forms.py @@ -1,14 +1,12 @@ -from __future__ import absolute_import -import mock import django_dynamic_fixture as fixture -from django.test import TestCase +import mock from django.contrib.auth.models import User +from django.test import TestCase from readthedocs.projects.models import Project -from ..models import GoldUser from ..forms import GoldSubscriptionForm - +from ..models import GoldUser class GoldSubscriptionFormTests(TestCase): diff --git a/readthedocs/gold/tests/test_signals.py b/readthedocs/gold/tests/test_signals.py index 62618581c06..947f042e035 100644 --- a/readthedocs/gold/tests/test_signals.py +++ b/readthedocs/gold/tests/test_signals.py @@ -1,16 +1,14 @@ -from __future__ import absolute_import -import mock import django_dynamic_fixture as fixture -from django.test import TestCase +import mock from django.contrib.auth.models import User from django.db.models.signals import pre_delete +from django.test import TestCase from readthedocs.projects.models import Project from ..models import GoldUser from ..signals import delete_customer - class GoldSignalTests(TestCase): def setUp(self): diff --git a/readthedocs/gold/urls.py b/readthedocs/gold/urls.py index dd8b6e354a4..2a1e1acafe3 100644 --- a/readthedocs/gold/urls.py +++ b/readthedocs/gold/urls.py @@ -1,6 +1,5 @@ """Gold subscription URLs""" -from __future__ import absolute_import from django.conf.urls import url from readthedocs.gold import views diff --git a/readthedocs/gold/views.py b/readthedocs/gold/views.py index 7e2677e2a25..2867d49dbf9 100644 --- a/readthedocs/gold/views.py +++ b/readthedocs/gold/views.py @@ -1,20 +1,13 @@ # -*- coding: utf-8 -*- """Gold subscription views.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals -) - from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.messages.views import SuccessMessageMixin -from django.urls import reverse, reverse_lazy from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render +from django.urls import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _ from vanilla import DeleteView, DetailView, UpdateView diff --git a/readthedocs/integrations/admin.py b/readthedocs/integrations/admin.py index 5fabe23be91..24f0ff95192 100644 --- a/readthedocs/integrations/admin.py +++ b/readthedocs/integrations/admin.py @@ -1,12 +1,11 @@ """Integration admin models.""" -from __future__ import absolute_import -from django.contrib import admin from django import urls +from django.contrib import admin from django.utils.safestring import mark_safe from pygments.formatters import HtmlFormatter -from .models import Integration, HttpExchange +from .models import HttpExchange, Integration def pretty_json_field(field, description, include_styles=False): diff --git a/readthedocs/integrations/models.py b/readthedocs/integrations/models.py index 032c16c28a4..8dbbbf80e93 100644 --- a/readthedocs/integrations/models.py +++ b/readthedocs/integrations/models.py @@ -2,18 +2,10 @@ """Integration models for external services.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import json import re import uuid -from builtins import object, str from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, diff --git a/readthedocs/notifications/apps.py b/readthedocs/notifications/apps.py index 38ed93cda31..3be3e557bf9 100644 --- a/readthedocs/notifications/apps.py +++ b/readthedocs/notifications/apps.py @@ -1,5 +1,4 @@ """Django app configuration for the notifications app.""" -from __future__ import absolute_import from django.apps import AppConfig diff --git a/readthedocs/notifications/backends.py b/readthedocs/notifications/backends.py index e2552c1bdaa..e2a3ed8dfbf 100644 --- a/readthedocs/notifications/backends.py +++ b/readthedocs/notifications/backends.py @@ -7,10 +7,6 @@ displayed on the site. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - -from builtins import object from django.conf import settings from django.http import HttpRequest from django.utils.module_loading import import_string diff --git a/readthedocs/notifications/constants.py b/readthedocs/notifications/constants.py index d15efa98448..4ee285d17a4 100644 --- a/readthedocs/notifications/constants.py +++ b/readthedocs/notifications/constants.py @@ -1,6 +1,5 @@ """Notification constants""" -from __future__ import absolute_import from messages_extends import constants as message_constants diff --git a/readthedocs/notifications/forms.py b/readthedocs/notifications/forms.py index 58924308515..f0026d5b378 100644 --- a/readthedocs/notifications/forms.py +++ b/readthedocs/notifications/forms.py @@ -1,5 +1,4 @@ """HTML forms for sending notifications.""" -from __future__ import absolute_import from django import forms from django.utils.translation import ugettext_lazy as _ diff --git a/readthedocs/notifications/notification.py b/readthedocs/notifications/notification.py index 8329bb91b26..e363bc16a79 100644 --- a/readthedocs/notifications/notification.py +++ b/readthedocs/notifications/notification.py @@ -1,17 +1,16 @@ # -*- coding: utf-8 -*- """Support for templating of notifications.""" -from __future__ import absolute_import -from builtins import object import logging + from django.conf import settings -from django.template import Template, Context -from django.template.loader import render_to_string from django.db import models from django.http import HttpRequest +from django.template import Context, Template +from django.template.loader import render_to_string -from .backends import send_notification from . import constants +from .backends import send_notification log = logging.getLogger(__name__) diff --git a/readthedocs/notifications/storages.py b/readthedocs/notifications/storages.py index 9a98ef7632f..ccd9f8e9a13 100644 --- a/readthedocs/notifications/storages.py +++ b/readthedocs/notifications/storages.py @@ -1,20 +1,21 @@ # -*- coding: utf-8 -*- """Customised storage for notifications.""" -from __future__ import absolute_import from django.contrib.messages.storage.base import Message from django.db.models import Q from django.utils.safestring import mark_safe -from messages_extends.storages import FallbackStorage, PersistentStorage -from messages_extends.models import Message as PersistentMessage from messages_extends.constants import PERSISTENT_MESSAGE_LEVELS +from messages_extends.models import Message as PersistentMessage +from messages_extends.storages import FallbackStorage, PersistentStorage + +from .constants import NON_PERSISTENT_MESSAGE_LEVELS + try: from django.utils import timezone except ImportError: from datetime import datetime as timezone -from .constants import NON_PERSISTENT_MESSAGE_LEVELS class FallbackUniqueStorage(FallbackStorage): diff --git a/readthedocs/notifications/urls.py b/readthedocs/notifications/urls.py index 349c1f293aa..d44522bc9f1 100644 --- a/readthedocs/notifications/urls.py +++ b/readthedocs/notifications/urls.py @@ -1,9 +1,7 @@ """Renames for messages_extends URLs""" -from __future__ import absolute_import from django.conf.urls import url - -from messages_extends.views import message_mark_read, message_mark_all_read +from messages_extends.views import message_mark_all_read, message_mark_read urlpatterns = [ diff --git a/readthedocs/notifications/views.py b/readthedocs/notifications/views.py index 2cbdb0959d4..3853e8cdbdd 100644 --- a/readthedocs/notifications/views.py +++ b/readthedocs/notifications/views.py @@ -1,8 +1,7 @@ """Django views for the notifications app.""" -from __future__ import absolute_import -from django.views.generic import FormView from django.contrib import admin, messages from django.http import HttpResponseRedirect +from django.views.generic import FormView from .forms import SendNotificationForm diff --git a/readthedocs/oauth/admin.py b/readthedocs/oauth/admin.py index b961e6472c5..777e48272bc 100644 --- a/readthedocs/oauth/admin.py +++ b/readthedocs/oauth/admin.py @@ -1,9 +1,8 @@ """Admin configuration for the OAuth app.""" -from __future__ import absolute_import from django.contrib import admin -from .models import RemoteRepository, RemoteOrganization +from .models import RemoteOrganization, RemoteRepository class RemoteRepositoryAdmin(admin.ModelAdmin): diff --git a/readthedocs/oauth/models.py b/readthedocs/oauth/models.py index 54a02191fe7..7d6aeec15de 100644 --- a/readthedocs/oauth/models.py +++ b/readthedocs/oauth/models.py @@ -1,19 +1,15 @@ # -*- coding: utf-8 -*- """OAuth service models.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import json -from builtins import object from allauth.socialaccount.models import SocialAccount from django.conf import settings from django.contrib.auth.models import User -from django.urls import reverse from django.core.validators import URLValidator from django.db import models from django.db.models import Q +from django.urls import reverse from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ diff --git a/readthedocs/oauth/notifications.py b/readthedocs/oauth/notifications.py index 41b9c78ce6e..915a2003c7e 100644 --- a/readthedocs/oauth/notifications.py +++ b/readthedocs/oauth/notifications.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from messages_extends.constants import ERROR_PERSISTENT diff --git a/readthedocs/oauth/querysets.py b/readthedocs/oauth/querysets.py index d01703eb1f0..c23f6cbeea1 100644 --- a/readthedocs/oauth/querysets.py +++ b/readthedocs/oauth/querysets.py @@ -1,7 +1,5 @@ """Managers for OAuth models""" -from __future__ import absolute_import - from django.db import models from readthedocs.core.utils.extend import SettingsOverrideObject diff --git a/readthedocs/oauth/services/base.py b/readthedocs/oauth/services/base.py index 87086a03149..77f2a7bf1a6 100644 --- a/readthedocs/oauth/services/base.py +++ b/readthedocs/oauth/services/base.py @@ -1,21 +1,18 @@ # -*- coding: utf-8 -*- """OAuth utility functions.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging from datetime import datetime from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.providers import registry -from builtins import object from django.conf import settings from django.utils import timezone from oauthlib.oauth2.rfc6749.errors import InvalidClientIdError from requests.exceptions import RequestException from requests_oauthlib import OAuth2Session + log = logging.getLogger(__name__) diff --git a/readthedocs/oauth/services/bitbucket.py b/readthedocs/oauth/services/bitbucket.py index 2586de5697b..70fb90b0b8a 100644 --- a/readthedocs/oauth/services/bitbucket.py +++ b/readthedocs/oauth/services/bitbucket.py @@ -1,16 +1,15 @@ """OAuth utility functions.""" -from __future__ import absolute_import -from builtins import str -import logging import json +import logging import re +from allauth.socialaccount.providers.bitbucket_oauth2.views import ( + BitbucketOAuth2Adapter, +) from django.conf import settings from django.urls import reverse from requests.exceptions import RequestException -from allauth.socialaccount.providers.bitbucket_oauth2.views import ( - BitbucketOAuth2Adapter) from readthedocs.builds import utils as build_utils from readthedocs.integrations.models import Integration diff --git a/readthedocs/oauth/services/github.py b/readthedocs/oauth/services/github.py index f9a03c4ff76..2e844aaaf31 100644 --- a/readthedocs/oauth/services/github.py +++ b/readthedocs/oauth/services/github.py @@ -1,16 +1,14 @@ """OAuth utility functions.""" -from __future__ import absolute_import -from builtins import str -import logging import json +import logging import re +from allauth.socialaccount.models import SocialToken +from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter from django.conf import settings from django.urls import reverse from requests.exceptions import RequestException -from allauth.socialaccount.models import SocialToken -from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter from readthedocs.builds import utils as build_utils from readthedocs.integrations.models import Integration @@ -19,6 +17,7 @@ from ..models import RemoteOrganization, RemoteRepository from .base import Service + log = logging.getLogger(__name__) diff --git a/readthedocs/oauth/services/gitlab.py b/readthedocs/oauth/services/gitlab.py index b9562617adf..ada8a6f1d5f 100644 --- a/readthedocs/oauth/services/gitlab.py +++ b/readthedocs/oauth/services/gitlab.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- """OAuth utility functions.""" -from __future__ import division, print_function, unicode_literals - import json import logging import re @@ -19,6 +17,7 @@ from ..models import RemoteOrganization, RemoteRepository from .base import Service + try: from urlparse import urljoin, urlparse except ImportError: diff --git a/readthedocs/oauth/tasks.py b/readthedocs/oauth/tasks.py index 45b49ceac09..eee0efe4845 100644 --- a/readthedocs/oauth/tasks.py +++ b/readthedocs/oauth/tasks.py @@ -1,13 +1,6 @@ # -*- coding: utf-8 -*- """Tasks for OAuth services.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging from allauth.socialaccount.providers import registry as allauth_registry @@ -23,6 +16,7 @@ from .services import registry + log = logging.getLogger(__name__) diff --git a/readthedocs/oauth/utils.py b/readthedocs/oauth/utils.py index 64e7dc7ed07..d797baa057f 100644 --- a/readthedocs/oauth/utils.py +++ b/readthedocs/oauth/utils.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- """Support code for OAuth, including webhook support.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging from django.contrib import messages @@ -11,9 +8,14 @@ from readthedocs.integrations.models import Integration from readthedocs.oauth.services import ( - BitbucketService, GitHubService, GitLabService, registry) + BitbucketService, + GitHubService, + GitLabService, + registry, +) from readthedocs.projects.models import Project + log = logging.getLogger(__name__) SERVICE_MAP = { diff --git a/readthedocs/payments/forms.py b/readthedocs/payments/forms.py index 89b2c1dc782..c4501d68224 100644 --- a/readthedocs/payments/forms.py +++ b/readthedocs/payments/forms.py @@ -1,17 +1,15 @@ """Payment forms.""" -from __future__ import absolute_import -from builtins import str -from builtins import object import logging -from stripe import Customer, Charge -from stripe.error import InvalidRequestError from django import forms from django.utils.translation import ugettext_lazy as _ +from stripe import Charge, Customer +from stripe.error import InvalidRequestError from .utils import stripe + log = logging.getLogger(__name__) diff --git a/readthedocs/payments/mixins.py b/readthedocs/payments/mixins.py index d90fc169925..3741147866c 100644 --- a/readthedocs/payments/mixins.py +++ b/readthedocs/payments/mixins.py @@ -1,7 +1,5 @@ """Payment view mixin classes.""" -from __future__ import absolute_import -from builtins import object from django.conf import settings diff --git a/readthedocs/payments/utils.py b/readthedocs/payments/utils.py index a65b5b0a8f6..36dc1f76941 100644 --- a/readthedocs/payments/utils.py +++ b/readthedocs/payments/utils.py @@ -5,10 +5,10 @@ :py:class:`readthedocs.payments.forms.StripeResourceMixin`. """ -from __future__ import absolute_import import stripe from django.conf import settings + stripe.api_key = getattr(settings, 'STRIPE_SECRET', None) diff --git a/readthedocs/profiles/urls/private.py b/readthedocs/profiles/urls/private.py index 85acbf2d9fa..21a6e604fc3 100644 --- a/readthedocs/profiles/urls/private.py +++ b/readthedocs/profiles/urls/private.py @@ -1,12 +1,5 @@ """URL patterns for views to modify user profiles.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from django.conf.urls import url from readthedocs.core.forms import UserProfileForm diff --git a/readthedocs/profiles/urls/public.py b/readthedocs/profiles/urls/public.py index 2a9c458e6fd..0cade801350 100644 --- a/readthedocs/profiles/urls/public.py +++ b/readthedocs/profiles/urls/public.py @@ -1,6 +1,5 @@ """URL patterns to view user profiles.""" -from __future__ import absolute_import from django.conf.urls import url from readthedocs.profiles import views diff --git a/readthedocs/profiles/views.py b/readthedocs/profiles/views.py index d288f4767bf..0396795e9ca 100644 --- a/readthedocs/profiles/views.py +++ b/readthedocs/profiles/views.py @@ -1,20 +1,13 @@ # -*- coding: utf-8 -*- """Views for creating, editing and viewing site-specific user profiles.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from django.contrib import messages from django.contrib.auth import logout from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User -from django.urls import reverse from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render +from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from readthedocs.core.forms import UserAdvertisingForm, UserDeleteForm diff --git a/readthedocs/projects/admin.py b/readthedocs/projects/admin.py index 48109f7eafb..5beebc58d50 100644 --- a/readthedocs/projects/admin.py +++ b/readthedocs/projects/admin.py @@ -1,13 +1,6 @@ # -*- coding: utf-8 -*- """Django administration interface for `projects.models`""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from django.contrib import admin, messages from django.contrib.admin.actions import delete_selected from django.utils.translation import ugettext_lazy as _ diff --git a/readthedocs/projects/backends/views.py b/readthedocs/projects/backends/views.py index 2b3d0fa41c5..428ca3f82ca 100644 --- a/readthedocs/projects/backends/views.py +++ b/readthedocs/projects/backends/views.py @@ -5,7 +5,6 @@ settings override of the view class. """ -from __future__ import absolute_import from readthedocs.core.utils.extend import SettingsOverrideObject from readthedocs.projects.views import private diff --git a/readthedocs/projects/constants.py b/readthedocs/projects/constants.py index 7e6d130e93e..f357942d611 100644 --- a/readthedocs/projects/constants.py +++ b/readthedocs/projects/constants.py @@ -6,13 +6,11 @@ theme names and repository types. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import re from django.utils.translation import ugettext_lazy as _ + DOCUMENTATION_CHOICES = ( ('auto', _('Automatically Choose')), ('sphinx', _('Sphinx Html')), diff --git a/readthedocs/projects/exceptions.py b/readthedocs/projects/exceptions.py index 41daacdeaaf..07ccd562459 100644 --- a/readthedocs/projects/exceptions.py +++ b/readthedocs/projects/exceptions.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- """Project exceptions.""" -from __future__ import division, print_function, unicode_literals - from django.conf import settings from django.utils.translation import ugettext_noop as _ diff --git a/readthedocs/projects/feeds.py b/readthedocs/projects/feeds.py index b3739f4b005..de3cc008b60 100644 --- a/readthedocs/projects/feeds.py +++ b/readthedocs/projects/feeds.py @@ -1,6 +1,5 @@ """Project RSS feeds""" -from __future__ import absolute_import from django.contrib.syndication.views import Feed from readthedocs.projects.models import Project diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index c447ab40c0b..a929213885e 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -1,16 +1,8 @@ # -*- coding: utf-8 -*- """Project forms.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from random import choice -from builtins import object from django import forms from django.conf import settings from django.contrib.auth.models import User diff --git a/readthedocs/projects/management/commands/import_project_from_live.py b/readthedocs/projects/management/commands/import_project_from_live.py index 95c85778349..042caf709fc 100644 --- a/readthedocs/projects/management/commands/import_project_from_live.py +++ b/readthedocs/projects/management/commands/import_project_from_live.py @@ -1,12 +1,12 @@ """Import project command""" -from __future__ import absolute_import -from django.core.management import call_command -from django.core.management.base import BaseCommand import json -import slumber +import slumber from django.contrib.auth.models import User +from django.core.management import call_command +from django.core.management.base import BaseCommand + from ...models import Project diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index c87295b864d..24bda8f18e9 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -1,18 +1,14 @@ # -*- coding: utf-8 -*- """Project models.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import fnmatch import logging import os -from builtins import object # pylint: disable=redefined-builtin from django.conf import settings from django.contrib.auth.models import User -from django.urls import NoReverseMatch, reverse from django.db import models +from django.urls import NoReverseMatch, reverse from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from django_extensions.db.models import TimeStampedModel @@ -26,15 +22,22 @@ from readthedocs.projects import constants from readthedocs.projects.exceptions import ProjectConfigurationError from readthedocs.projects.querysets import ( - ChildRelatedProjectQuerySet, FeatureQuerySet, ProjectQuerySet, - RelatedProjectQuerySet) + ChildRelatedProjectQuerySet, + FeatureQuerySet, + ProjectQuerySet, + RelatedProjectQuerySet, +) from readthedocs.projects.templatetags.projects_tags import sort_version_aware -from readthedocs.projects.validators import validate_domain_name, validate_repository_url +from readthedocs.projects.validators import ( + validate_domain_name, + validate_repository_url, +) from readthedocs.projects.version_handling import determine_stable_version from readthedocs.restapi.client import api from readthedocs.vcs_support.backends import backend_cls from readthedocs.vcs_support.utils import Lock, NonBlockingLock + log = logging.getLogger(__name__) diff --git a/readthedocs/projects/notifications.py b/readthedocs/projects/notifications.py index eafbecf66c3..fc46cb0fdca 100644 --- a/readthedocs/projects/notifications.py +++ b/readthedocs/projects/notifications.py @@ -1,6 +1,5 @@ """Project notifications""" -from __future__ import absolute_import from readthedocs.notifications import Notification from readthedocs.notifications.constants import REQUIREMENT diff --git a/readthedocs/projects/querysets.py b/readthedocs/projects/querysets.py index 3884eb26db6..4bde40fa39f 100644 --- a/readthedocs/projects/querysets.py +++ b/readthedocs/projects/querysets.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- """Project model QuerySet classes.""" -from __future__ import absolute_import - from django.db import models from django.db.models import Q from guardian.shortcuts import get_objects_for_user diff --git a/readthedocs/projects/signals.py b/readthedocs/projects/signals.py index 6ef49f9e67c..61251c3298f 100644 --- a/readthedocs/projects/signals.py +++ b/readthedocs/projects/signals.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Project signals""" -from __future__ import absolute_import import django.dispatch diff --git a/readthedocs/projects/tasks.py b/readthedocs/projects/tasks.py index 10b6660deca..06ec7981ea2 100644 --- a/readthedocs/projects/tasks.py +++ b/readthedocs/projects/tasks.py @@ -6,13 +6,6 @@ rebuilding documentation. """ -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import datetime import hashlib import json @@ -23,11 +16,10 @@ from collections import Counter, defaultdict import requests -from builtins import str from celery.exceptions import SoftTimeLimitExceeded from django.conf import settings -from django.urls import reverse from django.db.models import Q +from django.urls import reverse from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from slumber.exceptions import HttpClientError @@ -56,11 +48,11 @@ ) from readthedocs.doc_builder.exceptions import ( BuildEnvironmentError, + BuildEnvironmentWarning, BuildTimeoutError, ProjectBuildsSkippedError, VersionLockedError, YAMLParseError, - BuildEnvironmentWarning, ) from readthedocs.doc_builder.loader import get_builder_class from readthedocs.doc_builder.python_environments import Conda, Virtualenv @@ -82,6 +74,7 @@ files_changed, ) + log = logging.getLogger(__name__) diff --git a/readthedocs/projects/templatetags/projects_tags.py b/readthedocs/projects/templatetags/projects_tags.py index 699b1a24900..34c97508523 100644 --- a/readthedocs/projects/templatetags/projects_tags.py +++ b/readthedocs/projects/templatetags/projects_tags.py @@ -1,6 +1,5 @@ """Project template tags and filters""" -from __future__ import absolute_import from django import template from readthedocs.projects.version_handling import comparable_version diff --git a/readthedocs/projects/urls/private.py b/readthedocs/projects/urls/private.py index 1cdc294416d..a5ca6388f47 100644 --- a/readthedocs/projects/urls/private.py +++ b/readthedocs/projects/urls/private.py @@ -1,17 +1,27 @@ """Project URLs for authenticated users""" -from __future__ import absolute_import from django.conf.urls import url from readthedocs.constants import pattern_opts +from readthedocs.projects.backends.views import ImportDemoView, ImportWizardView from readthedocs.projects.views import private from readthedocs.projects.views.private import ( - ProjectDashboard, ImportView, - ProjectUpdate, ProjectAdvancedUpdate, - DomainList, DomainCreate, DomainDelete, DomainUpdate, - IntegrationList, IntegrationCreate, IntegrationDetail, IntegrationDelete, - IntegrationExchangeDetail, IntegrationWebhookSync, ProjectAdvertisingUpdate) -from readthedocs.projects.backends.views import ImportWizardView, ImportDemoView + DomainCreate, + DomainDelete, + DomainList, + DomainUpdate, + ImportView, + IntegrationCreate, + IntegrationDelete, + IntegrationDetail, + IntegrationExchangeDetail, + IntegrationList, + IntegrationWebhookSync, + ProjectAdvancedUpdate, + ProjectAdvertisingUpdate, + ProjectDashboard, + ProjectUpdate, +) urlpatterns = [ diff --git a/readthedocs/projects/urls/public.py b/readthedocs/projects/urls/public.py index b46b8105aa4..be0d0fc179e 100644 --- a/readthedocs/projects/urls/public.py +++ b/readthedocs/projects/urls/public.py @@ -1,13 +1,11 @@ """Project URLS for public users""" -from __future__ import absolute_import from django.conf.urls import url -from readthedocs.projects.views import public -from readthedocs.projects.views.public import ProjectIndex, ProjectDetailView - from readthedocs.builds import views as build_views from readthedocs.constants import pattern_opts +from readthedocs.projects.views import public +from readthedocs.projects.views.public import ProjectDetailView, ProjectIndex urlpatterns = [ diff --git a/readthedocs/projects/utils.py b/readthedocs/projects/utils.py index 840dd17482a..cdacd26c276 100644 --- a/readthedocs/projects/utils.py +++ b/readthedocs/projects/utils.py @@ -1,19 +1,12 @@ # -*- coding: utf-8 -*- """Utility functions used by projects.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging import os -from builtins import open from django.conf import settings + log = logging.getLogger(__name__) diff --git a/readthedocs/projects/validators.py b/readthedocs/projects/validators.py index f2b7b468746..a362692c43d 100644 --- a/readthedocs/projects/validators.py +++ b/readthedocs/projects/validators.py @@ -1,14 +1,12 @@ """Validators for projects app.""" -# From https://github.com/django/django/pull/3477/files -from __future__ import absolute_import import re from django.conf import settings from django.core.exceptions import ValidationError +from django.core.validators import RegexValidator from django.utils.deconstruct import deconstructible from django.utils.translation import ugettext_lazy as _ -from django.core.validators import RegexValidator from future.backports.urllib.parse import urlparse diff --git a/readthedocs/projects/version_handling.py b/readthedocs/projects/version_handling.py index d3ef3d3a108..d1ef1751a18 100644 --- a/readthedocs/projects/version_handling.py +++ b/readthedocs/projects/version_handling.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- """Project version handling.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import unicodedata -import six from packaging.version import InvalidVersion, Version from readthedocs.builds.constants import ( - LATEST_VERBOSE_NAME, STABLE_VERBOSE_NAME, TAG) + LATEST_VERBOSE_NAME, + STABLE_VERBOSE_NAME, + TAG, +) def parse_version_failsafe(version_string): diff --git a/readthedocs/projects/views/base.py b/readthedocs/projects/views/base.py index b6e8e387dd1..fa85dd440f3 100644 --- a/readthedocs/projects/views/base.py +++ b/readthedocs/projects/views/base.py @@ -1,21 +1,18 @@ # -*- coding: utf-8 -*- """Mix-in classes for project views.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging -from builtins import object from datetime import timedelta from django.conf import settings -from django.urls import reverse from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 +from django.urls import reverse from django.utils import timezone from ..exceptions import ProjectSpamError from ..models import Project + log = logging.getLogger(__name__) USER_MATURITY_DAYS = getattr(settings, 'USER_MATURITY_DAYS', 7) diff --git a/readthedocs/projects/views/mixins.py b/readthedocs/projects/views/mixins.py index 1692b0e461e..ed04d0377fb 100644 --- a/readthedocs/projects/views/mixins.py +++ b/readthedocs/projects/views/mixins.py @@ -1,7 +1,5 @@ """Mixin classes for project views.""" -from __future__ import absolute_import -from builtins import object from django.shortcuts import get_object_or_404 from readthedocs.projects.models import Project diff --git a/readthedocs/projects/views/private.py b/readthedocs/projects/views/private.py index 22b355f94a2..7a3e8b7523a 100644 --- a/readthedocs/projects/views/private.py +++ b/readthedocs/projects/views/private.py @@ -2,13 +2,6 @@ """Project views for authenticated users.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging from allauth.socialaccount.models import SocialAccount @@ -66,6 +59,7 @@ from readthedocs.projects.signals import project_import from readthedocs.projects.views.base import ProjectAdminMixin, ProjectSpamMixin + log = logging.getLogger(__name__) diff --git a/readthedocs/projects/views/public.py b/readthedocs/projects/views/public.py index 028571a293d..87a76947d80 100644 --- a/readthedocs/projects/views/public.py +++ b/readthedocs/projects/views/public.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- """Public project views.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import json import logging import mimetypes @@ -16,9 +13,9 @@ from django.contrib import messages from django.contrib.auth.models import User from django.core.cache import cache -from django.urls import reverse from django.http import Http404, HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render +from django.urls import reverse from django.views.decorators.cache import never_cache from django.views.generic import DetailView, ListView from taggit.models import Tag @@ -32,6 +29,7 @@ from .base import ProjectOnboardMixin + log = logging.getLogger(__name__) search_log = logging.getLogger(__name__ + '.search') mimetypes.add_type('application/epub+zip', '.epub') diff --git a/readthedocs/redirects/admin.py b/readthedocs/redirects/admin.py index 6bd2d73470d..2091ab87acc 100644 --- a/readthedocs/redirects/admin.py +++ b/readthedocs/redirects/admin.py @@ -1,8 +1,7 @@ """Django admin configuration for the redirects app.""" -from __future__ import absolute_import - from django.contrib import admin + from .models import Redirect diff --git a/readthedocs/redirects/managers.py b/readthedocs/redirects/managers.py index 9c0f1bf47fa..af18ebae64f 100644 --- a/readthedocs/redirects/managers.py +++ b/readthedocs/redirects/managers.py @@ -1,6 +1,5 @@ """Manager and queryset for the redirects app.""" -from __future__ import absolute_import from django.db.models import Manager from django.db.models.query import QuerySet diff --git a/readthedocs/redirects/models.py b/readthedocs/redirects/models.py index f8eddb16cc4..a4e9b72f731 100644 --- a/readthedocs/redirects/models.py +++ b/readthedocs/redirects/models.py @@ -1,16 +1,16 @@ """Django models for the redirects app.""" -from __future__ import absolute_import -from builtins import object +import logging +import re + from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext from django.utils.translation import ugettext_lazy as _ -import logging -import re from readthedocs.core.resolver import resolve_path from readthedocs.projects.models import Project + from .managers import RedirectManager diff --git a/readthedocs/redirects/utils.py b/readthedocs/redirects/utils.py index 1edc628626a..0817d99b1bd 100644 --- a/readthedocs/redirects/utils.py +++ b/readthedocs/redirects/utils.py @@ -7,11 +7,11 @@ These are not used directly as views; they are instead included into 404 handlers, so that redirects only take effect if no other view matches. """ -from __future__ import absolute_import -from django.http import HttpResponseRedirect import logging import re +from django.http import HttpResponseRedirect + from readthedocs.constants import LANGUAGES_REGEX from readthedocs.projects.models import Project diff --git a/readthedocs/restapi/client.py b/readthedocs/restapi/client.py index 83f5b861d83..a2f9943c6da 100644 --- a/readthedocs/restapi/client.py +++ b/readthedocs/restapi/client.py @@ -2,13 +2,6 @@ """Simple client to access our API with Slumber credentials.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging import requests @@ -18,6 +11,7 @@ from rest_framework.renderers import JSONRenderer from slumber import API, serialize + log = logging.getLogger(__name__) PRODUCTION_DOMAIN = getattr(settings, 'PRODUCTION_DOMAIN', 'readthedocs.org') diff --git a/readthedocs/restapi/permissions.py b/readthedocs/restapi/permissions.py index a9d7aa8d92f..4645cca618d 100644 --- a/readthedocs/restapi/permissions.py +++ b/readthedocs/restapi/permissions.py @@ -1,7 +1,5 @@ """Defines access permissions for the API.""" -from __future__ import absolute_import - from rest_framework import permissions from readthedocs.core.permissions import AdminPermission diff --git a/readthedocs/restapi/serializers.py b/readthedocs/restapi/serializers.py index ba6a88ddc90..4529bcdafbd 100644 --- a/readthedocs/restapi/serializers.py +++ b/readthedocs/restapi/serializers.py @@ -1,15 +1,11 @@ """Defines serializers for each of our models.""" -from __future__ import absolute_import - -from builtins import object - from allauth.socialaccount.models import SocialAccount from rest_framework import serializers from readthedocs.builds.models import Build, BuildCommandResult, Version from readthedocs.oauth.models import RemoteOrganization, RemoteRepository -from readthedocs.projects.models import Project, Domain +from readthedocs.projects.models import Domain, Project class ProjectSerializer(serializers.ModelSerializer): diff --git a/readthedocs/restapi/signals.py b/readthedocs/restapi/signals.py index 6b6d0b3955f..6968cb5342e 100644 --- a/readthedocs/restapi/signals.py +++ b/readthedocs/restapi/signals.py @@ -1,9 +1,8 @@ """We define custom Django signals to trigger when a footer is rendered.""" -from __future__ import absolute_import - import django.dispatch + footer_response = django.dispatch.Signal( providing_args=["request", "context", "response_data"] ) diff --git a/readthedocs/restapi/urls.py b/readthedocs/restapi/urls.py index c8cdf6cd21e..50767355fee 100644 --- a/readthedocs/restapi/urls.py +++ b/readthedocs/restapi/urls.py @@ -2,13 +2,6 @@ """Define routes between URL paths and views/endpoints.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from django.conf import settings from django.conf.urls import include, url from rest_framework import routers @@ -35,6 +28,7 @@ VersionViewSet, ) + router = routers.DefaultRouter() router.register(r'build', BuildViewSet, basename='build') router.register(r'command', BuildCommandViewSet, basename='buildcommandresult') diff --git a/readthedocs/restapi/utils.py b/readthedocs/restapi/utils.py index 8637cd1779b..19a325be54c 100644 --- a/readthedocs/restapi/utils.py +++ b/readthedocs/restapi/utils.py @@ -1,13 +1,6 @@ # -*- coding: utf-8 -*- """Utility functions that are used by both views and celery tasks.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import hashlib import logging @@ -25,6 +18,7 @@ from readthedocs.builds.models import Version from readthedocs.search.indexes import PageIndex, ProjectIndex, SectionIndex + log = logging.getLogger(__name__) diff --git a/readthedocs/restapi/views/core_views.py b/readthedocs/restapi/views/core_views.py index 08fc9e7d764..e6f2879b2eb 100644 --- a/readthedocs/restapi/views/core_views.py +++ b/readthedocs/restapi/views/core_views.py @@ -1,17 +1,14 @@ """Utility endpoints relating to canonical urls, embedded content, etc.""" -from __future__ import absolute_import - +from django.shortcuts import get_object_or_404 from rest_framework import decorators, permissions, status from rest_framework.renderers import JSONRenderer from rest_framework.response import Response -from django.shortcuts import get_object_or_404 - from readthedocs.builds.constants import LATEST from readthedocs.builds.models import Version -from readthedocs.projects.models import Project from readthedocs.core.templatetags.core_tags import make_document_url +from readthedocs.projects.models import Project @decorators.api_view(['GET']) diff --git a/readthedocs/restapi/views/footer_views.py b/readthedocs/restapi/views/footer_views.py index ad5ec7028b6..2f0bcae0e61 100644 --- a/readthedocs/restapi/views/footer_views.py +++ b/readthedocs/restapi/views/footer_views.py @@ -1,10 +1,6 @@ # -*- coding: utf-8 -*- """Endpoint to generate footer HTML.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - -import six from django.conf import settings from django.shortcuts import get_object_or_404 from django.template import loader as template_loader @@ -17,7 +13,9 @@ from readthedocs.builds.models import Version from readthedocs.projects.models import Project from readthedocs.projects.version_handling import ( - highest_version, parse_version_failsafe) + highest_version, + parse_version_failsafe, +) from readthedocs.restapi.signals import footer_response diff --git a/readthedocs/restapi/views/integrations.py b/readthedocs/restapi/views/integrations.py index 4950518408b..b8320c3525c 100644 --- a/readthedocs/restapi/views/integrations.py +++ b/readthedocs/restapi/views/integrations.py @@ -1,18 +1,10 @@ # -*- coding: utf-8 -*- """Endpoints integrating with Github, Bitbucket, and other webhooks.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import json import logging import re -import six from django.shortcuts import get_object_or_404 from rest_framework import permissions, status from rest_framework.exceptions import NotFound, ParseError @@ -30,6 +22,7 @@ from readthedocs.integrations.utils import normalize_request_payload from readthedocs.projects.models import Project + log = logging.getLogger(__name__) GITHUB_EVENT_HEADER = 'HTTP_X_GITHUB_EVENT' diff --git a/readthedocs/restapi/views/model_views.py b/readthedocs/restapi/views/model_views.py index 3e925031472..4e021d5e281 100644 --- a/readthedocs/restapi/views/model_views.py +++ b/readthedocs/restapi/views/model_views.py @@ -1,13 +1,9 @@ # -*- coding: utf-8 -*- """Endpoints for listing Projects, Versions, Builds, etc.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging from allauth.socialaccount.models import SocialAccount -from builtins import str from django.shortcuts import get_object_or_404 from django.template.loader import render_to_string from rest_framework import decorators, permissions, status, viewsets @@ -25,12 +21,25 @@ from .. import utils as api_utils from ..permissions import ( - APIPermission, APIRestrictedPermission, IsOwner, RelatedProjectIsOwner) + APIPermission, + APIRestrictedPermission, + IsOwner, + RelatedProjectIsOwner, +) from ..serializers import ( - BuildAdminSerializer, BuildCommandSerializer, BuildSerializer, - DomainSerializer, ProjectAdminSerializer, ProjectSerializer, - RemoteOrganizationSerializer, RemoteRepositorySerializer, - SocialAccountSerializer, VersionAdminSerializer, VersionSerializer) + BuildAdminSerializer, + BuildCommandSerializer, + BuildSerializer, + DomainSerializer, + ProjectAdminSerializer, + ProjectSerializer, + RemoteOrganizationSerializer, + RemoteRepositorySerializer, + SocialAccountSerializer, + VersionAdminSerializer, + VersionSerializer, +) + log = logging.getLogger(__name__) diff --git a/readthedocs/restapi/views/search_views.py b/readthedocs/restapi/views/search_views.py index abe36174097..771a3069b59 100644 --- a/readthedocs/restapi/views/search_views.py +++ b/readthedocs/restapi/views/search_views.py @@ -1,6 +1,5 @@ """Endpoints related to searching through projects, sections, etc.""" -from __future__ import absolute_import import logging from rest_framework import decorators, permissions, status @@ -10,8 +9,8 @@ from readthedocs.builds.constants import LATEST from readthedocs.builds.models import Version from readthedocs.projects.models import Project, ProjectRelationship -from readthedocs.search.lib import search_file, search_project, search_section from readthedocs.restapi import utils +from readthedocs.search.lib import search_file, search_project, search_section log = logging.getLogger(__name__) diff --git a/readthedocs/restapi/views/task_views.py b/readthedocs/restapi/views/task_views.py index 475fb17bda6..0213aeb754a 100644 --- a/readthedocs/restapi/views/task_views.py +++ b/readthedocs/restapi/views/task_views.py @@ -1,12 +1,5 @@ """Endpoints relating to task/job status, etc.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging from django.urls import reverse @@ -18,6 +11,7 @@ from readthedocs.core.utils.tasks import TaskNoPermission, get_public_task_data from readthedocs.oauth import tasks + log = logging.getLogger(__name__) diff --git a/readthedocs/rtd_tests/base.py b/readthedocs/rtd_tests/base.py index c6813f22f8f..b5ab431150a 100644 --- a/readthedocs/rtd_tests/base.py +++ b/readthedocs/rtd_tests/base.py @@ -1,19 +1,16 @@ """Base classes and mixins for unit tests.""" -from __future__ import absolute_import -from builtins import object +import logging import os import shutil -import logging import tempfile from collections import OrderedDict -from mock import patch from django.conf import settings -from django.test import TestCase, RequestFactory from django.contrib.auth.models import AnonymousUser from django.contrib.messages.storage.fallback import FallbackStorage from django.contrib.sessions.middleware import SessionMiddleware -import six +from django.test import RequestFactory, TestCase +from mock import patch log = logging.getLogger(__name__) diff --git a/readthedocs/rtd_tests/files/conf.py b/readthedocs/rtd_tests/files/conf.py index b448eab9ab7..7bdfdbe06dc 100644 --- a/readthedocs/rtd_tests/files/conf.py +++ b/readthedocs/rtd_tests/files/conf.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - from datetime import datetime from recommonmark.parser import CommonMarkParser diff --git a/readthedocs/rtd_tests/mocks/environment.py b/readthedocs/rtd_tests/mocks/environment.py index 3b2a28f49bd..a5aa1b8fe21 100644 --- a/readthedocs/rtd_tests/mocks/environment.py +++ b/readthedocs/rtd_tests/mocks/environment.py @@ -1,9 +1,6 @@ # pylint: disable=missing-docstring -from __future__ import absolute_import -from builtins import object import mock - class EnvironmentMockGroup: """Mock out necessary environment pieces""" diff --git a/readthedocs/rtd_tests/mocks/mock_api.py b/readthedocs/rtd_tests/mocks/mock_api.py index 68d1e544357..3dd54d6635a 100644 --- a/readthedocs/rtd_tests/mocks/mock_api.py +++ b/readthedocs/rtd_tests/mocks/mock_api.py @@ -1,8 +1,7 @@ """Mock versions of many API-related classes.""" -from __future__ import absolute_import -from builtins import object -from contextlib import contextmanager import json +from contextlib import contextmanager + import mock # Mock tastypi API. diff --git a/readthedocs/rtd_tests/mocks/paths.py b/readthedocs/rtd_tests/mocks/paths.py index 34fa7e5953f..ee8b29b7a26 100644 --- a/readthedocs/rtd_tests/mocks/paths.py +++ b/readthedocs/rtd_tests/mocks/paths.py @@ -1,9 +1,8 @@ """Context managers to patch os.path.exists calls.""" -from __future__ import absolute_import import os import re -import mock +import mock def fake_paths(check): """ diff --git a/readthedocs/rtd_tests/tests/projects/test_admin_actions.py b/readthedocs/rtd_tests/tests/projects/test_admin_actions.py index 54111314cee..acdc99df38f 100644 --- a/readthedocs/rtd_tests/tests/projects/test_admin_actions.py +++ b/readthedocs/rtd_tests/tests/projects/test_admin_actions.py @@ -1,14 +1,13 @@ -import mock import django_dynamic_fixture as fixture +import mock +from django import urls from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME from django.contrib.auth.models import User -from django import urls from django.test import TestCase from readthedocs.core.models import UserProfile from readthedocs.projects.models import Project - class ProjectAdminActionsTest(TestCase): @classmethod diff --git a/readthedocs/rtd_tests/tests/test_api.py b/readthedocs/rtd_tests/tests/test_api.py index af63a2378e7..44f2a037418 100644 --- a/readthedocs/rtd_tests/tests/test_api.py +++ b/readthedocs/rtd_tests/tests/test_api.py @@ -1,22 +1,14 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import base64 import datetime import json import mock from allauth.socialaccount.models import SocialAccount -from builtins import str from django.contrib.auth.models import User -from django.urls import reverse from django.http import QueryDict from django.test import TestCase +from django.urls import reverse from django.utils import six from django_dynamic_fixture import get from rest_framework import status diff --git a/readthedocs/rtd_tests/tests/test_api_permissions.py b/readthedocs/rtd_tests/tests/test_api_permissions.py index d3666927add..40d348c6ccf 100644 --- a/readthedocs/rtd_tests/tests/test_api_permissions.py +++ b/readthedocs/rtd_tests/tests/test_api_permissions.py @@ -1,10 +1,9 @@ -from __future__ import absolute_import from functools import partial -from mock import Mock from unittest import TestCase -from readthedocs.restapi.permissions import APIRestrictedPermission +from mock import Mock +from readthedocs.restapi.permissions import APIRestrictedPermission class APIRestrictedPermissionTests(TestCase): def get_request(self, method, is_admin): diff --git a/readthedocs/rtd_tests/tests/test_api_version_compare.py b/readthedocs/rtd_tests/tests/test_api_version_compare.py index 0daec18a086..df4df452cff 100644 --- a/readthedocs/rtd_tests/tests/test_api_version_compare.py +++ b/readthedocs/rtd_tests/tests/test_api_version_compare.py @@ -1,11 +1,9 @@ -from __future__ import absolute_import from django.test import TestCase from readthedocs.builds.constants import LATEST from readthedocs.projects.models import Project from readthedocs.restapi.views.footer_views import get_version_compare_data - class VersionCompareTests(TestCase): fixtures = ['eric.json', 'test_data.json'] diff --git a/readthedocs/rtd_tests/tests/test_backend.py b/readthedocs/rtd_tests/tests/test_backend.py index 34510778fea..76eb07d371c 100644 --- a/readthedocs/rtd_tests/tests/test_backend.py +++ b/readthedocs/rtd_tests/tests/test_backend.py @@ -1,19 +1,11 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import os from os.path import exists from tempfile import mkdtemp import django_dynamic_fixture as fixture import pytest -import six from django.contrib.auth.models import User from mock import Mock, patch @@ -30,7 +22,6 @@ make_test_hg, ) - class TestGitBackend(RTDTestCase): def setUp(self): git_repo = make_test_git() diff --git a/readthedocs/rtd_tests/tests/test_backend_svn.py b/readthedocs/rtd_tests/tests/test_backend_svn.py index 8de9ea776a0..e3cf9e2e217 100644 --- a/readthedocs/rtd_tests/tests/test_backend_svn.py +++ b/readthedocs/rtd_tests/tests/test_backend_svn.py @@ -1,19 +1,12 @@ # -*- coding: utf-8 -*- """Tests For SVN""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - -from mock import patch from django_dynamic_fixture import get +from mock import patch -from readthedocs.rtd_tests.base import RTDTestCase -from readthedocs.projects.models import Project from readthedocs.builds.models import Version +from readthedocs.projects.models import Project +from readthedocs.rtd_tests.base import RTDTestCase from readthedocs.vcs_support.backends.svn import Backend as SvnBackend class TestSvnBackend(RTDTestCase): diff --git a/readthedocs/rtd_tests/tests/test_build_config.py b/readthedocs/rtd_tests/tests/test_build_config.py index 9085d6f7926..90a28ce25f6 100644 --- a/readthedocs/rtd_tests/tests/test_build_config.py +++ b/readthedocs/rtd_tests/tests/test_build_config.py @@ -1,13 +1,11 @@ -from __future__ import division, print_function, unicode_literals - from os import path import pytest -import six import yamale -from readthedocs.config.tests import utils from yamale.validators import DefaultValidators, Validator +from readthedocs.config.tests import utils + V2_SCHEMA = path.join( path.dirname(__file__), '../fixtures/spec/v2/schema.yml' diff --git a/readthedocs/rtd_tests/tests/test_build_forms.py b/readthedocs/rtd_tests/tests/test_build_forms.py index b0ba6890d09..d69ca289481 100644 --- a/readthedocs/rtd_tests/tests/test_build_forms.py +++ b/readthedocs/rtd_tests/tests/test_build_forms.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - from django.test import TestCase from django_dynamic_fixture import get @@ -10,7 +8,6 @@ from readthedocs.projects.constants import PRIVATE from readthedocs.projects.models import Project - class TestVersionForm(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_build_notifications.py b/readthedocs/rtd_tests/tests/test_build_notifications.py index 9460589463a..b276d1d935e 100644 --- a/readthedocs/rtd_tests/tests/test_build_notifications.py +++ b/readthedocs/rtd_tests/tests/test_build_notifications.py @@ -1,19 +1,15 @@ # -*- coding: utf-8 -*- """Notifications sent after build is completed.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import django_dynamic_fixture as fixture from django.core import mail from django.test import TestCase from mock import patch from readthedocs.builds.models import Build, Version -from readthedocs.projects.models import Project, EmailHook, WebHook -from readthedocs.projects.tasks import send_notifications from readthedocs.projects.forms import WebHookForm - +from readthedocs.projects.models import EmailHook, Project, WebHook +from readthedocs.projects.tasks import send_notifications class BuildNotificationsTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_builds.py b/readthedocs/rtd_tests/tests/test_builds.py index 53e222203fe..6f8808bc1ad 100644 --- a/readthedocs/rtd_tests/tests/test_builds.py +++ b/readthedocs/rtd_tests/tests/test_builds.py @@ -1,12 +1,6 @@ -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) +import os import mock -import os from django.test import TestCase from django_dynamic_fixture import fixture, get @@ -14,13 +8,12 @@ from readthedocs.doc_builder.config import load_yaml_config from readthedocs.doc_builder.environments import LocalBuildEnvironment from readthedocs.doc_builder.python_environments import Virtualenv -from readthedocs.projects.models import Project, EnvironmentVariable +from readthedocs.projects.models import EnvironmentVariable, Project from readthedocs.projects.tasks import UpdateDocsTaskStep from readthedocs.rtd_tests.tests.test_config_integration import create_load from ..mocks.environment import EnvironmentMockGroup - class BuildEnvironmentTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_celery.py b/readthedocs/rtd_tests/tests/test_celery.py index e718e220b36..9191250e8be 100644 --- a/readthedocs/rtd_tests/tests/test_celery.py +++ b/readthedocs/rtd_tests/tests/test_celery.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - import os import shutil from os.path import exists @@ -8,20 +6,21 @@ from django.contrib.auth.models import User from django_dynamic_fixture import get -from mock import patch, MagicMock +from mock import MagicMock, patch from readthedocs.builds.constants import LATEST -from readthedocs.projects.exceptions import RepositoryError from readthedocs.builds.models import Build -from readthedocs.projects.models import Project from readthedocs.projects import tasks - -from readthedocs.rtd_tests.utils import ( - create_git_branch, create_git_tag, delete_git_branch) -from readthedocs.rtd_tests.utils import make_test_git +from readthedocs.projects.exceptions import RepositoryError +from readthedocs.projects.models import Project from readthedocs.rtd_tests.base import RTDTestCase from readthedocs.rtd_tests.mocks.mock_api import mock_api - +from readthedocs.rtd_tests.utils import ( + create_git_branch, + create_git_tag, + delete_git_branch, + make_test_git, +) class TestCeleryBuilding(RTDTestCase): diff --git a/readthedocs/rtd_tests/tests/test_config_integration.py b/readthedocs/rtd_tests/tests/test_config_integration.py index 8474c4bea11..e0b93243c6a 100644 --- a/readthedocs/rtd_tests/tests/test_config_integration.py +++ b/readthedocs/rtd_tests/tests/test_config_integration.py @@ -1,7 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import tempfile from os import path @@ -22,7 +19,6 @@ from readthedocs.projects.models import Feature, Project from readthedocs.rtd_tests.utils import create_git_submodule, make_git_repo - def create_load(config=None): """ Mock out the function of the build load function. diff --git a/readthedocs/rtd_tests/tests/test_core_tags.py b/readthedocs/rtd_tests/tests/test_core_tags.py index 851fd40d1b6..20dd966ad4a 100644 --- a/readthedocs/rtd_tests/tests/test_core_tags.py +++ b/readthedocs/rtd_tests/tests/test_core_tags.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import import mock import pytest - from django.conf import settings from django.test import TestCase from django.test.utils import override_settings -from readthedocs.projects.models import Project from readthedocs.builds.constants import LATEST from readthedocs.core.templatetags import core_tags - +from readthedocs.projects.models import Project @override_settings(USE_SUBDOMAIN=False, PRODUCTION_DOMAIN='readthedocs.org') class CoreTagsTests(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_core_utils.py b/readthedocs/rtd_tests/tests/test_core_utils.py index 85e7d735507..e480f6398ad 100644 --- a/readthedocs/rtd_tests/tests/test_core_utils.py +++ b/readthedocs/rtd_tests/tests/test_core_utils.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- """Test core util functions""" -from __future__ import absolute_import import mock - -from django_dynamic_fixture import get from django.test import TestCase +from django_dynamic_fixture import get -from readthedocs.projects.models import Project from readthedocs.builds.models import Version -from readthedocs.core.utils import trigger_build, slugify - +from readthedocs.core.utils import slugify, trigger_build +from readthedocs.projects.models import Project class CoreUtilTests(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_doc_builder.py b/readthedocs/rtd_tests/tests/test_doc_builder.py index a384f5d00b7..383237d26cb 100644 --- a/readthedocs/rtd_tests/tests/test_doc_builder.py +++ b/readthedocs/rtd_tests/tests/test_doc_builder.py @@ -1,7 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import os import tempfile from collections import namedtuple @@ -22,7 +19,6 @@ from readthedocs.projects.exceptions import ProjectConfigurationError from readthedocs.projects.models import Feature, Project - class SphinxBuilderTest(TestCase): fixtures = ['test_data'] diff --git a/readthedocs/rtd_tests/tests/test_doc_building.py b/readthedocs/rtd_tests/tests/test_doc_building.py index 1b8f11c3880..1a1dbb579a1 100644 --- a/readthedocs/rtd_tests/tests/test_doc_building.py +++ b/readthedocs/rtd_tests/tests/test_doc_building.py @@ -5,13 +5,6 @@ * raw subprocess calls like .communicate expects bytes * the Command wrappers encapsulate the bytes and expose unicode """ -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import json import os import re @@ -20,7 +13,6 @@ import mock import pytest -from builtins import str from django.test import TestCase from django_dynamic_fixture import get from docker.errors import APIError as DockerAPIError @@ -43,7 +35,6 @@ from readthedocs.rtd_tests.mocks.paths import fake_paths_lookup from readthedocs.rtd_tests.tests.test_config_integration import create_load - DUMMY_BUILD_ID = 123 SAMPLE_UNICODE = 'HérÉ îß sömê ünïçó∂é' SAMPLE_UTF8_BYTES = SAMPLE_UNICODE.encode('utf-8') diff --git a/readthedocs/rtd_tests/tests/test_doc_serving.py b/readthedocs/rtd_tests/tests/test_doc_serving.py index 1236472010a..8d8e458197d 100644 --- a/readthedocs/rtd_tests/tests/test_doc_serving.py +++ b/readthedocs/rtd_tests/tests/test_doc_serving.py @@ -1,18 +1,15 @@ -from __future__ import absolute_import -import mock import django_dynamic_fixture as fixture - +import mock +from django.conf import settings from django.contrib.auth.models import User +from django.http import Http404 from django.test import TestCase from django.test.utils import override_settings -from django.http import Http404 -from django.conf import settings -from readthedocs.rtd_tests.base import RequestFactoryTestMixin +from readthedocs.core.views.serve import _serve_symlink_docs from readthedocs.projects import constants from readthedocs.projects.models import Project -from readthedocs.core.views.serve import _serve_symlink_docs - +from readthedocs.rtd_tests.base import RequestFactoryTestMixin @override_settings( USE_SUBDOMAIN=False, PUBLIC_DOMAIN='public.readthedocs.org', DEBUG=False diff --git a/readthedocs/rtd_tests/tests/test_domains.py b/readthedocs/rtd_tests/tests/test_domains.py index 5e487ce2c94..963b07a91f1 100644 --- a/readthedocs/rtd_tests/tests/test_domains.py +++ b/readthedocs/rtd_tests/tests/test_domains.py @@ -1,19 +1,16 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import import json from django.core.cache import cache from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings - from django_dynamic_fixture import get from readthedocs.core.middleware import SubdomainMiddleware -from readthedocs.projects.models import Project, Domain from readthedocs.projects.forms import DomainForm - +from readthedocs.projects.models import Domain, Project class MiddlewareTests(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_extend.py b/readthedocs/rtd_tests/tests/test_extend.py index d3c66552b6a..21017e3088d 100644 --- a/readthedocs/rtd_tests/tests/test_extend.py +++ b/readthedocs/rtd_tests/tests/test_extend.py @@ -1,10 +1,9 @@ -from __future__ import absolute_import -from builtins import object from django.test import TestCase, override_settings -from readthedocs.core.utils.extend import (SettingsOverrideObject, - get_override_class) - +from readthedocs.core.utils.extend import ( + SettingsOverrideObject, + get_override_class, +) # Top level to ensure module name is correct class FooBase: diff --git a/readthedocs/rtd_tests/tests/test_footer.py b/readthedocs/rtd_tests/tests/test_footer.py index ec5ca4d754a..692e85cd121 100644 --- a/readthedocs/rtd_tests/tests/test_footer.py +++ b/readthedocs/rtd_tests/tests/test_footer.py @@ -1,11 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import mock from django.test import TestCase from rest_framework.test import APIRequestFactory, APITestCase @@ -20,7 +13,6 @@ ) from readthedocs.rtd_tests.mocks.paths import fake_paths_by_regex - class Testmaker(APITestCase): fixtures = ['test_data'] url = '/api/v2/footer_html/?project=pip&version=latest&page=index&docroot=/' diff --git a/readthedocs/rtd_tests/tests/test_gold.py b/readthedocs/rtd_tests/tests/test_gold.py index 63f50f8d696..9475f4fd110 100644 --- a/readthedocs/rtd_tests/tests/test_gold.py +++ b/readthedocs/rtd_tests/tests/test_gold.py @@ -1,15 +1,11 @@ -from __future__ import absolute_import - -from django.urls import reverse from django.test import TestCase +from django.urls import reverse +from django_dynamic_fixture import fixture, get -from django_dynamic_fixture import get, fixture - -from readthedocs.gold.models import GoldUser, LEVEL_CHOICES +from readthedocs.gold.models import LEVEL_CHOICES, GoldUser from readthedocs.projects.models import Project from readthedocs.rtd_tests.utils import create_user - class GoldViewTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_imported_file.py b/readthedocs/rtd_tests/tests/test_imported_file.py index 81f5e2a684b..d005fc9a182 100644 --- a/readthedocs/rtd_tests/tests/test_imported_file.py +++ b/readthedocs/rtd_tests/tests/test_imported_file.py @@ -1,9 +1,9 @@ -from __future__ import absolute_import import os + from django.test import TestCase +from readthedocs.projects.models import ImportedFile, Project from readthedocs.projects.tasks import _manage_imported_files -from readthedocs.projects.models import Project, ImportedFile base_dir = os.path.dirname(os.path.dirname(__file__)) diff --git a/readthedocs/rtd_tests/tests/test_integrations.py b/readthedocs/rtd_tests/tests/test_integrations.py index 17f9b291253..360fdb0f1ed 100644 --- a/readthedocs/rtd_tests/tests/test_integrations.py +++ b/readthedocs/rtd_tests/tests/test_integrations.py @@ -1,19 +1,16 @@ -from __future__ import absolute_import - -from builtins import range import django_dynamic_fixture as fixture -from django.test import TestCase, RequestFactory from django.contrib.contenttypes.models import ContentType -from rest_framework.test import APIClient -from rest_framework.test import APIRequestFactory +from django.test import RequestFactory, TestCase from rest_framework.response import Response +from rest_framework.test import APIClient, APIRequestFactory from readthedocs.integrations.models import ( - HttpExchange, Integration, GitHubWebhook + GitHubWebhook, + HttpExchange, + Integration, ) from readthedocs.projects.models import Project - class HttpExchangeTests(TestCase): """Test HttpExchange model by using existing views diff --git a/readthedocs/rtd_tests/tests/test_middleware.py b/readthedocs/rtd_tests/tests/test_middleware.py index 0bfe9b56304..f7e5d607624 100644 --- a/readthedocs/rtd_tests/tests/test_middleware.py +++ b/readthedocs/rtd_tests/tests/test_middleware.py @@ -1,26 +1,20 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import - -from django.http import Http404 +from corsheaders.middleware import CorsMiddleware from django.conf import settings from django.core.cache import cache -from django.urls.base import get_urlconf, set_urlconf +from django.http import Http404 from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings - +from django.urls.base import get_urlconf, set_urlconf from django_dynamic_fixture import get - -from corsheaders.middleware import CorsMiddleware from mock import patch from readthedocs.core.middleware import SubdomainMiddleware -from readthedocs.projects.models import Project, ProjectRelationship, Domain - +from readthedocs.projects.models import Domain, Project, ProjectRelationship from readthedocs.rtd_tests.utils import create_user - @override_settings(USE_SUBDOMAIN=True) class MiddlewareTests(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_notifications.py b/readthedocs/rtd_tests/tests/test_notifications.py index adae07e49fe..ddc8c9ada56 100644 --- a/readthedocs/rtd_tests/tests/test_notifications.py +++ b/readthedocs/rtd_tests/tests/test_notifications.py @@ -1,19 +1,21 @@ # -*- coding: utf-8 -*- """Notification tests""" -from __future__ import absolute_import -import mock import django_dynamic_fixture as fixture +import mock +from django.contrib.auth.models import AnonymousUser, User from django.test import TestCase from django.test.utils import override_settings -from django.contrib.auth.models import User, AnonymousUser from messages_extends.models import Message as PersistentMessage +from readthedocs.builds.models import Build from readthedocs.notifications import Notification, SiteNotification from readthedocs.notifications.backends import EmailBackend, SiteBackend -from readthedocs.notifications.constants import ERROR, INFO_NON_PERSISTENT, WARNING_NON_PERSISTENT -from readthedocs.builds.models import Build - +from readthedocs.notifications.constants import ( + ERROR, + INFO_NON_PERSISTENT, + WARNING_NON_PERSISTENT, +) @override_settings( NOTIFICATION_BACKENDS=[ diff --git a/readthedocs/rtd_tests/tests/test_oauth.py b/readthedocs/rtd_tests/tests/test_oauth.py index fedbaf0d47d..45c2983bb15 100644 --- a/readthedocs/rtd_tests/tests/test_oauth.py +++ b/readthedocs/rtd_tests/tests/test_oauth.py @@ -1,7 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import mock from django.conf import settings from django.contrib.auth.models import User @@ -10,11 +7,13 @@ from readthedocs.oauth.models import RemoteOrganization, RemoteRepository from readthedocs.oauth.services import ( - BitbucketService, GitHubService, GitLabService) + BitbucketService, + GitHubService, + GitLabService, +) from readthedocs.projects import constants from readthedocs.projects.models import Project - class GitHubOAuthTests(TestCase): fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py index 86297dbe4b4..97eceae19a8 100644 --- a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py +++ b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import json import logging @@ -9,8 +7,7 @@ from future.backports.urllib.parse import urlencode from readthedocs.builds.models import Version -from readthedocs.projects.models import Project, Feature - +from readthedocs.projects.models import Feature, Project log = logging.getLogger(__name__) diff --git a/readthedocs/rtd_tests/tests/test_privacy.py b/readthedocs/rtd_tests/tests/test_privacy.py index 8a7359c9055..7844ece2e5e 100644 --- a/readthedocs/rtd_tests/tests/test_privacy.py +++ b/readthedocs/rtd_tests/tests/test_privacy.py @@ -1,17 +1,16 @@ -from __future__ import absolute_import -import logging import json -import mock +import logging +import mock +from django.contrib.auth.models import User from django.test import TestCase from django.test.utils import override_settings -from django.contrib.auth.models import User from readthedocs.builds.constants import LATEST -from readthedocs.builds.models import Version, Build -from readthedocs.projects.models import Project -from readthedocs.projects.forms import UpdateProjectForm +from readthedocs.builds.models import Build, Version from readthedocs.projects import tasks +from readthedocs.projects.forms import UpdateProjectForm +from readthedocs.projects.models import Project log = logging.getLogger(__name__) diff --git a/readthedocs/rtd_tests/tests/test_privacy_urls.py b/readthedocs/rtd_tests/tests/test_privacy_urls.py index f793f8c51ab..ba71d66bf70 100644 --- a/readthedocs/rtd_tests/tests/test_privacy_urls.py +++ b/readthedocs/rtd_tests/tests/test_privacy_urls.py @@ -1,24 +1,20 @@ -from __future__ import absolute_import -from __future__ import print_function import re +import mock from allauth.socialaccount.models import SocialAccount -from builtins import object from django.contrib.admindocs.views import extract_views_from_urlpatterns from django.test import TestCase from django.urls import reverse from django_dynamic_fixture import get -import mock from taggit.models import Tag from readthedocs.builds.models import Build, BuildCommandResult from readthedocs.core.utils.tasks import TaskNoPermission from readthedocs.integrations.models import HttpExchange, Integration -from readthedocs.projects.models import Project, Domain -from readthedocs.oauth.models import RemoteRepository, RemoteOrganization +from readthedocs.oauth.models import RemoteOrganization, RemoteRepository +from readthedocs.projects.models import Domain, Project from readthedocs.rtd_tests.utils import create_user - class URLAccessMixin: default_kwargs = {} diff --git a/readthedocs/rtd_tests/tests/test_profile_views.py b/readthedocs/rtd_tests/tests/test_profile_views.py index 9e3a75a449e..239f093beae 100644 --- a/readthedocs/rtd_tests/tests/test_profile_views.py +++ b/readthedocs/rtd_tests/tests/test_profile_views.py @@ -1,11 +1,8 @@ -from __future__ import division, print_function, unicode_literals - from django.contrib.auth.models import User -from django.urls import reverse from django.test import TestCase +from django.urls import reverse from django_dynamic_fixture import get - class ProfileViewsTest(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_project.py b/readthedocs/rtd_tests/tests/test_project.py index bbbd43a11a3..b6bee71d091 100644 --- a/readthedocs/rtd_tests/tests/test_project.py +++ b/readthedocs/rtd_tests/tests/test_project.py @@ -1,27 +1,27 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import datetime import json from django.contrib.auth.models import User from django.forms.models import model_to_dict from django.test import TestCase -from django_dynamic_fixture import get from django.utils import timezone +from django_dynamic_fixture import get from mock import patch from rest_framework.reverse import reverse from readthedocs.builds.constants import ( - BUILD_STATE_CLONING, BUILD_STATE_FINISHED, BUILD_STATE_TRIGGERED, LATEST) + BUILD_STATE_CLONING, + BUILD_STATE_FINISHED, + BUILD_STATE_TRIGGERED, + LATEST, +) from readthedocs.builds.models import Build from readthedocs.projects.exceptions import ProjectConfigurationError from readthedocs.projects.models import Project from readthedocs.projects.tasks import finish_inactive_builds from readthedocs.rtd_tests.mocks.paths import fake_paths_by_regex - class ProjectMixin: fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_project_forms.py b/readthedocs/rtd_tests/tests/test_project_forms.py index e7fefb5bbbf..bc423b8d4d6 100644 --- a/readthedocs/rtd_tests/tests/test_project_forms.py +++ b/readthedocs/rtd_tests/tests/test_project_forms.py @@ -1,12 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import mock from django.contrib.auth.models import User from django.test import TestCase @@ -33,7 +26,6 @@ ) from readthedocs.projects.models import Project - class TestProjectForms(TestCase): @mock.patch.object(ClassifierValidator, '__call__') diff --git a/readthedocs/rtd_tests/tests/test_project_querysets.py b/readthedocs/rtd_tests/tests/test_project_querysets.py index 682e54cd9ea..a9193e47ead 100644 --- a/readthedocs/rtd_tests/tests/test_project_querysets.py +++ b/readthedocs/rtd_tests/tests/test_project_querysets.py @@ -1,14 +1,13 @@ -from __future__ import absolute_import - from datetime import timedelta import django_dynamic_fixture as fixture from django.test import TestCase -from readthedocs.projects.models import Project, ProjectRelationship, Feature -from readthedocs.projects.querysets import (ParentRelatedProjectQuerySet, - ChildRelatedProjectQuerySet) - +from readthedocs.projects.models import Feature, Project, ProjectRelationship +from readthedocs.projects.querysets import ( + ChildRelatedProjectQuerySet, + ParentRelatedProjectQuerySet, +) class ProjectQuerySetTests(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_project_symlinks.py b/readthedocs/rtd_tests/tests/test_project_symlinks.py index e35ca38743d..bd9cb76ef73 100644 --- a/readthedocs/rtd_tests/tests/test_project_symlinks.py +++ b/readthedocs/rtd_tests/tests/test_project_symlinks.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import -from builtins import object import os import shutil import tempfile import mock from django.conf import settings -from django.urls import reverse from django.test import TestCase, override_settings +from django.urls import reverse from django_dynamic_fixture import get from readthedocs.builds.models import Version -from readthedocs.projects.models import Project, Domain -from readthedocs.projects.tasks import broadcast_remove_orphan_symlinks, remove_orphan_symlinks, symlink_project -from readthedocs.core.symlink import PublicSymlink, PrivateSymlink - +from readthedocs.core.symlink import PrivateSymlink, PublicSymlink +from readthedocs.projects.models import Domain, Project +from readthedocs.projects.tasks import ( + broadcast_remove_orphan_symlinks, + remove_orphan_symlinks, + symlink_project, +) def get_filesystem(path, top_level_path=None): """Recurse into path, return dictionary mapping of path and files diff --git a/readthedocs/rtd_tests/tests/test_project_views.py b/readthedocs/rtd_tests/tests/test_project_views.py index 124fd018c00..ecf3c8bd6ed 100644 --- a/readthedocs/rtd_tests/tests/test_project_views.py +++ b/readthedocs/rtd_tests/tests/test_project_views.py @@ -1,29 +1,27 @@ -from __future__ import absolute_import from datetime import timedelta - -from mock import patch -from django.test import TestCase from django.contrib.auth.models import User from django.contrib.messages import constants as message_const -from django.urls import reverse from django.http.response import HttpResponseRedirect -from django.views.generic.base import ContextMixin +from django.test import TestCase +from django.urls import reverse from django.utils import timezone +from django.views.generic.base import ContextMixin from django_dynamic_fixture import get, new - -import six +from mock import patch from readthedocs.builds.models import Build, Version -from readthedocs.rtd_tests.base import (WizardTestCase, MockBuildTestCase, - RequestFactoryTestMixin) from readthedocs.oauth.models import RemoteRepository +from readthedocs.projects import tasks from readthedocs.projects.exceptions import ProjectSpamError -from readthedocs.projects.models import Project, Domain -from readthedocs.projects.views.private import ImportWizardView +from readthedocs.projects.models import Domain, Project from readthedocs.projects.views.mixins import ProjectRelationMixin -from readthedocs.projects import tasks - +from readthedocs.projects.views.private import ImportWizardView +from readthedocs.rtd_tests.base import ( + MockBuildTestCase, + RequestFactoryTestMixin, + WizardTestCase, +) @patch('readthedocs.projects.views.private.trigger_build', lambda x: None) class TestProfileMiddleware(RequestFactoryTestMixin, TestCase): diff --git a/readthedocs/rtd_tests/tests/test_redirects.py b/readthedocs/rtd_tests/tests/test_redirects.py index 335c8feba86..d53d55125b8 100644 --- a/readthedocs/rtd_tests/tests/test_redirects.py +++ b/readthedocs/rtd_tests/tests/test_redirects.py @@ -1,10 +1,9 @@ -from __future__ import absolute_import +import logging + from django.http import Http404 from django.test import TestCase from django.test.utils import override_settings - -from django_dynamic_fixture import get -from django_dynamic_fixture import fixture +from django_dynamic_fixture import fixture, get from mock import patch from readthedocs.builds.constants import LATEST @@ -12,9 +11,6 @@ from readthedocs.projects.models import Project from readthedocs.redirects.models import Redirect -import logging - - @override_settings(PUBLIC_DOMAIN='readthedocs.org', USE_SUBDOMAIN=False, APPEND_SLASH=False) class RedirectTests(TestCase): fixtures = ["eric", "test_data"] diff --git a/readthedocs/rtd_tests/tests/test_repo_parsing.py b/readthedocs/rtd_tests/tests/test_repo_parsing.py index f946db61e53..d77c51ecb2d 100644 --- a/readthedocs/rtd_tests/tests/test_repo_parsing.py +++ b/readthedocs/rtd_tests/tests/test_repo_parsing.py @@ -1,12 +1,8 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - from django.test import TestCase from readthedocs.projects.models import Project - class TestRepoParsing(TestCase): fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_resolver.py b/readthedocs/rtd_tests/tests/test_resolver.py index 841da05c1b6..929ccc8dbee 100644 --- a/readthedocs/rtd_tests/tests/test_resolver.py +++ b/readthedocs/rtd_tests/tests/test_resolver.py @@ -1,19 +1,18 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import django_dynamic_fixture as fixture import mock from django.test import TestCase, override_settings from readthedocs.core.resolver import ( - Resolver, resolve, resolve_domain, resolve_path + Resolver, + resolve, + resolve_domain, + resolve_path, ) from readthedocs.projects.constants import PRIVATE from readthedocs.projects.models import Domain, Project, ProjectRelationship from readthedocs.rtd_tests.utils import create_user - @override_settings(PUBLIC_DOMAIN='readthedocs.org') class ResolverBase(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_restapi_client.py b/readthedocs/rtd_tests/tests/test_restapi_client.py index 88906fdc10f..1aeca51ee54 100644 --- a/readthedocs/rtd_tests/tests/test_restapi_client.py +++ b/readthedocs/rtd_tests/tests/test_restapi_client.py @@ -1,12 +1,8 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, unicode_literals) - from django.test import TestCase from readthedocs.restapi.client import DrfJsonSerializer - class TestDrfJsonSerializer(TestCase): data = { 'proper': 'json' diff --git a/readthedocs/rtd_tests/tests/test_search_json_parsing.py b/readthedocs/rtd_tests/tests/test_search_json_parsing.py index fb91d31b276..b51ea834f1e 100644 --- a/readthedocs/rtd_tests/tests/test_search_json_parsing.py +++ b/readthedocs/rtd_tests/tests/test_search_json_parsing.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import import os from django.test import TestCase diff --git a/readthedocs/rtd_tests/tests/test_single_version.py b/readthedocs/rtd_tests/tests/test_single_version.py index faee5f585f7..de1bc9300cd 100644 --- a/readthedocs/rtd_tests/tests/test_single_version.py +++ b/readthedocs/rtd_tests/tests/test_single_version.py @@ -1,12 +1,9 @@ -from __future__ import absolute_import +import django_dynamic_fixture as fixture from django.test import TestCase from django.test.utils import override_settings -import django_dynamic_fixture as fixture - from readthedocs.projects.models import Project - @override_settings( USE_SUBDOMAIN=True, PUBLIC_DOMAIN='public.readthedocs.org', SERVE_PUBLIC_DOCS=True ) diff --git a/readthedocs/rtd_tests/tests/test_subprojects.py b/readthedocs/rtd_tests/tests/test_subprojects.py index 081bd89a0bb..542b2d8b02d 100644 --- a/readthedocs/rtd_tests/tests/test_subprojects.py +++ b/readthedocs/rtd_tests/tests/test_subprojects.py @@ -1,7 +1,5 @@ -from __future__ import absolute_import - -import mock import django_dynamic_fixture as fixture +import mock from django.contrib.auth.models import User from django.test import TestCase from django.test.utils import override_settings @@ -10,7 +8,6 @@ from readthedocs.projects.models import Project, ProjectRelationship from readthedocs.rtd_tests.utils import create_user - class SubprojectFormTests(TestCase): def test_empty_child(self): diff --git a/readthedocs/rtd_tests/tests/test_sync_versions.py b/readthedocs/rtd_tests/tests/test_sync_versions.py index 533ad424729..731cc0675bc 100644 --- a/readthedocs/rtd_tests/tests/test_sync_versions.py +++ b/readthedocs/rtd_tests/tests/test_sync_versions.py @@ -1,23 +1,15 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals -) - import json +import pytest from django.test import TestCase from django.urls import reverse -import pytest from readthedocs.builds.constants import BRANCH, STABLE, TAG from readthedocs.builds.models import Version from readthedocs.projects.models import Project - class TestSyncVersions(TestCase): fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_urls.py b/readthedocs/rtd_tests/tests/test_urls.py index cb4204e2870..55a4a840065 100644 --- a/readthedocs/rtd_tests/tests/test_urls.py +++ b/readthedocs/rtd_tests/tests/test_urls.py @@ -1,8 +1,5 @@ -from __future__ import absolute_import - -from django.urls import reverse, NoReverseMatch from django.test import TestCase - +from django.urls import NoReverseMatch, reverse class WipeUrlTests(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_version_commit_name.py b/readthedocs/rtd_tests/tests/test_version_commit_name.py index 943843c22f8..23875955f00 100644 --- a/readthedocs/rtd_tests/tests/test_version_commit_name.py +++ b/readthedocs/rtd_tests/tests/test_version_commit_name.py @@ -1,18 +1,11 @@ -from __future__ import absolute_import from django.test import TestCase -from django_dynamic_fixture import get -from django_dynamic_fixture import new +from django_dynamic_fixture import get, new -from readthedocs.builds.constants import BRANCH -from readthedocs.builds.constants import LATEST -from readthedocs.builds.constants import STABLE -from readthedocs.builds.constants import TAG +from readthedocs.builds.constants import BRANCH, LATEST, STABLE, TAG from readthedocs.builds.models import Version -from readthedocs.projects.constants import REPO_TYPE_GIT -from readthedocs.projects.constants import REPO_TYPE_HG +from readthedocs.projects.constants import REPO_TYPE_GIT, REPO_TYPE_HG from readthedocs.projects.models import Project - class VersionCommitNameTests(TestCase): def test_branch_name_unicode_non_ascii(self): unicode_name = b'abc_\xd1\x84_\xe2\x99\x98'.decode('utf-8') diff --git a/readthedocs/rtd_tests/tests/test_version_config.py b/readthedocs/rtd_tests/tests/test_version_config.py index 82286ade4bf..4fd8d085454 100644 --- a/readthedocs/rtd_tests/tests/test_version_config.py +++ b/readthedocs/rtd_tests/tests/test_version_config.py @@ -1,12 +1,9 @@ -from __future__ import division, print_function, unicode_literals - from django.test import TestCase from django_dynamic_fixture import get from readthedocs.builds.models import Build, Version from readthedocs.projects.models import Project - class VersionConfigTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_version_slug.py b/readthedocs/rtd_tests/tests/test_version_slug.py index 31cf2ca72fc..44d8500f251 100644 --- a/readthedocs/rtd_tests/tests/test_version_slug.py +++ b/readthedocs/rtd_tests/tests/test_version_slug.py @@ -1,13 +1,11 @@ -from __future__ import absolute_import import re + from django.test import TestCase from readthedocs.builds.models import Version -from readthedocs.builds.version_slug import VersionSlugField -from readthedocs.builds.version_slug import VERSION_SLUG_REGEX +from readthedocs.builds.version_slug import VERSION_SLUG_REGEX, VersionSlugField from readthedocs.projects.models import Project - class VersionSlugPatternTests(TestCase): pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX)) diff --git a/readthedocs/rtd_tests/tests/test_views.py b/readthedocs/rtd_tests/tests/test_views.py index e68989e1483..dd64a438a8e 100644 --- a/readthedocs/rtd_tests/tests/test_views.py +++ b/readthedocs/rtd_tests/tests/test_views.py @@ -1,15 +1,8 @@ # -*- coding: utf-8 -*- -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import mock from django.contrib.auth.models import User -from django.urls import reverse from django.test import TestCase +from django.urls import reverse from django.utils.six.moves.urllib.parse import urlsplit from django_dynamic_fixture import get, new diff --git a/readthedocs/rtd_tests/utils.py b/readthedocs/rtd_tests/utils.py index 1908ef0ccd5..a5768a19d7d 100644 --- a/readthedocs/rtd_tests/utils.py +++ b/readthedocs/rtd_tests/utils.py @@ -1,13 +1,6 @@ # -*- coding: utf-8 -*- """Utility functions for use in tests.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging import subprocess import textwrap diff --git a/readthedocs/search/indexes.py b/readthedocs/search/indexes.py index 189f441d84e..65fc1464330 100644 --- a/readthedocs/search/indexes.py +++ b/readthedocs/search/indexes.py @@ -14,16 +14,11 @@ TODO: Handle page removal case in Page. """ -from __future__ import absolute_import -from builtins import object - +from django.conf import settings from django.utils import timezone - from elasticsearch import Elasticsearch, exceptions from elasticsearch.helpers import bulk_index -from django.conf import settings - class Index: diff --git a/readthedocs/search/lib.py b/readthedocs/search/lib.py index 8500a829b03..b5ebf8754ca 100644 --- a/readthedocs/search/lib.py +++ b/readthedocs/search/lib.py @@ -1,17 +1,17 @@ """Utilities related to searching Elastic.""" -from __future__ import absolute_import -from __future__ import print_function from pprint import pprint from django.conf import settings -from .indexes import PageIndex, ProjectIndex, SectionIndex - from readthedocs.builds.constants import LATEST from readthedocs.projects.models import Project -from readthedocs.search.signals import (before_project_search, - before_file_search, - before_section_search) +from readthedocs.search.signals import ( + before_file_search, + before_project_search, + before_section_search, +) + +from .indexes import PageIndex, ProjectIndex, SectionIndex def search_project(request, query, language=None): diff --git a/readthedocs/search/parse_json.py b/readthedocs/search/parse_json.py index baa4abe5eca..4ad53beeff6 100644 --- a/readthedocs/search/parse_json.py +++ b/readthedocs/search/parse_json.py @@ -1,17 +1,15 @@ # -*- coding: utf-8 -*- """Functions related to converting content into dict/JSON structures.""" -from __future__ import absolute_import - -import logging import codecs import fnmatch import json +import logging import os -from builtins import next, range # pylint: disable=redefined-builtin from pyquery import PyQuery + log = logging.getLogger(__name__) diff --git a/readthedocs/search/signals.py b/readthedocs/search/signals.py index 6abdf64cce9..7f599c35678 100644 --- a/readthedocs/search/signals.py +++ b/readthedocs/search/signals.py @@ -1,7 +1,7 @@ """We define custom Django signals to trigger before executing searches.""" -from __future__ import absolute_import import django.dispatch + before_project_search = django.dispatch.Signal(providing_args=["body"]) before_file_search = django.dispatch.Signal(providing_args=["body"]) before_section_search = django.dispatch.Signal(providing_args=["body"]) diff --git a/readthedocs/search/tests/conftest.py b/readthedocs/search/tests/conftest.py index 59961f3a7e2..ab46332f7a2 100644 --- a/readthedocs/search/tests/conftest.py +++ b/readthedocs/search/tests/conftest.py @@ -6,9 +6,14 @@ from django_dynamic_fixture import G from readthedocs.projects.models import Project -from readthedocs.search.indexes import Index, ProjectIndex, PageIndex, SectionIndex -from .dummy_data import DUMMY_PAGE_JSON, ALL_PROJECTS - +from readthedocs.search.indexes import ( + Index, + PageIndex, + ProjectIndex, + SectionIndex, +) + +from .dummy_data import ALL_PROJECTS, DUMMY_PAGE_JSON @pytest.fixture(autouse=True) def mock_elastic_index(mocker): diff --git a/readthedocs/search/tests/test_views.py b/readthedocs/search/tests/test_views.py index d5ac18bafc9..37072e94948 100644 --- a/readthedocs/search/tests/test_views.py +++ b/readthedocs/search/tests/test_views.py @@ -11,7 +11,6 @@ from readthedocs.projects.models import Project from readthedocs.search.tests.utils import get_search_query_from_project_file - @pytest.mark.django_db @pytest.mark.search class TestElasticSearch: diff --git a/readthedocs/search/tests/utils.py b/readthedocs/search/tests/utils.py index a48ea83dd74..90ce5f70566 100644 --- a/readthedocs/search/tests/utils.py +++ b/readthedocs/search/tests/utils.py @@ -1,6 +1,5 @@ from readthedocs.search.tests.dummy_data import DUMMY_PAGE_JSON - def get_search_query_from_project_file(project_slug, page_num=0, data_type='title'): """Return search query from the project's page file. Query is generated from the value of `data_type` diff --git a/readthedocs/search/utils.py b/readthedocs/search/utils.py index ec429e4ace9..8e795a569bd 100644 --- a/readthedocs/search/utils.py +++ b/readthedocs/search/utils.py @@ -1,16 +1,13 @@ # -*- coding: utf-8 -*- """Utilities related to reading and generating indexable search content.""" -from __future__ import absolute_import - -import os -import fnmatch -import re import codecs -import logging +import fnmatch import json +import logging +import os +import re -from builtins import next, range from pyquery import PyQuery diff --git a/readthedocs/search/views.py b/readthedocs/search/views.py index e1725f87fdc..200dbadd7f8 100644 --- a/readthedocs/search/views.py +++ b/readthedocs/search/views.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- """Search views.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import collections import logging from pprint import pprint @@ -13,6 +10,7 @@ from readthedocs.builds.constants import LATEST from readthedocs.search import lib as search_lib + log = logging.getLogger(__name__) LOG_TEMPLATE = '(Elastic Search) [{user}:{type}] [{project}:{version}:{language}] {msg}' diff --git a/readthedocs/urls.py b/readthedocs/urls.py index e441bc94459..ba6e67ef338 100644 --- a/readthedocs/urls.py +++ b/readthedocs/urls.py @@ -1,26 +1,28 @@ # pylint: disable=missing-docstring -from __future__ import absolute_import - import os from functools import reduce from operator import add -from django.conf.urls import url, include -from django.contrib import admin from django.conf import settings +from django.conf.urls import include, url from django.conf.urls.static import static -from django.views.generic.base import TemplateView, RedirectView +from django.contrib import admin +from django.views.generic.base import RedirectView, TemplateView from tastypie.api import Api -from readthedocs.api.base import (ProjectResource, UserResource, - VersionResource, FileResource) -from readthedocs.core.urls import docs_urls, core_urls, deprecated_urls +from readthedocs.api.base import ( + FileResource, + ProjectResource, + UserResource, + VersionResource, +) +from readthedocs.core.urls import core_urls, deprecated_urls, docs_urls from readthedocs.core.views import ( HomepageView, SupportView, + do_not_track, server_error_404, server_error_500, - do_not_track, ) from readthedocs.search import views as search_views diff --git a/readthedocs/vcs_support/backends/bzr.py b/readthedocs/vcs_support/backends/bzr.py index 9a309a8077a..9c97870408c 100644 --- a/readthedocs/vcs_support/backends/bzr.py +++ b/readthedocs/vcs_support/backends/bzr.py @@ -1,19 +1,9 @@ # -*- coding: utf-8 -*- """Bazaar-related utilities.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import csv import re -from builtins import str # pylint: disable=redefined-builtin -from six import StringIO - from readthedocs.projects.exceptions import RepositoryError from readthedocs.vcs_support.base import BaseVCS, VCSVersion diff --git a/readthedocs/vcs_support/backends/git.py b/readthedocs/vcs_support/backends/git.py index 80fafb89e9d..5d1b4eaa8dd 100644 --- a/readthedocs/vcs_support/backends/git.py +++ b/readthedocs/vcs_support/backends/git.py @@ -1,19 +1,11 @@ # -*- coding: utf-8 -*- """Git-related utilities.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging import os import re import git -from builtins import str from django.core.exceptions import ValidationError from git.exc import BadName, InvalidGitRepositoryError @@ -22,6 +14,7 @@ from readthedocs.projects.validators import validate_submodule_url from readthedocs.vcs_support.base import BaseVCS, VCSVersion + log = logging.getLogger(__name__) diff --git a/readthedocs/vcs_support/backends/hg.py b/readthedocs/vcs_support/backends/hg.py index e65d4c650f0..7bb252322d8 100644 --- a/readthedocs/vcs_support/backends/hg.py +++ b/readthedocs/vcs_support/backends/hg.py @@ -1,12 +1,5 @@ # -*- coding: utf-8 -*- """Mercurial-related utilities.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from readthedocs.projects.exceptions import RepositoryError from readthedocs.vcs_support.base import BaseVCS, VCSVersion diff --git a/readthedocs/vcs_support/backends/svn.py b/readthedocs/vcs_support/backends/svn.py index 2363116c6fd..b4ad6130302 100644 --- a/readthedocs/vcs_support/backends/svn.py +++ b/readthedocs/vcs_support/backends/svn.py @@ -1,18 +1,8 @@ # -*- coding: utf-8 -*- """Subversion-related utilities.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import csv -from builtins import str -from six import StringIO # noqa - from readthedocs.projects.exceptions import RepositoryError from readthedocs.vcs_support.base import BaseVCS, VCSVersion diff --git a/readthedocs/vcs_support/base.py b/readthedocs/vcs_support/base.py index f430fd0d0a5..9530cebde8c 100644 --- a/readthedocs/vcs_support/base.py +++ b/readthedocs/vcs_support/base.py @@ -1,13 +1,9 @@ # -*- coding: utf-8 -*- """Base classes for VCS backends.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging import os import shutil -from builtins import object log = logging.getLogger(__name__) diff --git a/readthedocs/vcs_support/tests.py b/readthedocs/vcs_support/tests.py index 649f633c5f7..df9702ce7c8 100644 --- a/readthedocs/vcs_support/tests.py +++ b/readthedocs/vcs_support/tests.py @@ -1,10 +1,8 @@ -from __future__ import absolute_import import os import shutil import unittest import mock - from django.conf import settings from readthedocs.vcs_support import utils @@ -64,5 +62,3 @@ def test_forceacquire(self): pass except utils.LockTimeout: raise AssertionError('Should have thrown LockTimeout') - - diff --git a/readthedocs/vcs_support/utils.py b/readthedocs/vcs_support/utils.py index eb5d22bb52f..56714763808 100644 --- a/readthedocs/vcs_support/utils.py +++ b/readthedocs/vcs_support/utils.py @@ -1,12 +1,9 @@ """Locking utilities.""" -from __future__ import absolute_import - import errno import logging import os import stat import time -from builtins import object log = logging.getLogger(__name__) diff --git a/readthedocs/worker.py b/readthedocs/worker.py index 21023e9cdff..47dfdb48282 100644 --- a/readthedocs/worker.py +++ b/readthedocs/worker.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- """Celery worker application instantiation""" -from __future__ import absolute_import, unicode_literals - import os from celery import Celery From c56b2893c916134b42d73e3eea94a15c6889e345 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:03:29 +0100 Subject: [PATCH 09/42] Remove six and future from requirements --- requirements/pip.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/requirements/pip.txt b/requirements/pip.txt index 3490128e231..6d4ac7d2d4f 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -16,8 +16,6 @@ mkdocs==1.0.4 Markdown==3.0.1 django==1.11.16 -six==1.11.0 -future==0.17.1 django-tastypie==0.14.2 django-guardian==1.4.9 django-extensions==2.1.4 From 979f216507903b4ff4d30cf81e35fe445ed6aa68 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:07:00 +0100 Subject: [PATCH 10/42] Manually remove usage of `future` module --- readthedocs/__init__.py | 2 +- readthedocs/core/signals.py | 2 +- readthedocs/core/templatetags/core_tags.py | 2 +- readthedocs/projects/forms.py | 2 +- readthedocs/projects/migrations/0010_migrate_domain_data.py | 3 +-- readthedocs/projects/models.py | 2 +- readthedocs/projects/validators.py | 2 +- readthedocs/rtd_tests/tests/test_post_commit_hooks.py | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/readthedocs/__init__.py b/readthedocs/__init__.py index bf6f944d7e6..1c8afb1a02b 100644 --- a/readthedocs/__init__.py +++ b/readthedocs/__init__.py @@ -3,7 +3,7 @@ import os.path -from future.moves.configparser import RawConfigParser +from configparser import RawConfigParser def get_version(setupcfg_path): diff --git a/readthedocs/core/signals.py b/readthedocs/core/signals.py index 875cde381d9..f8fa5f5adec 100644 --- a/readthedocs/core/signals.py +++ b/readthedocs/core/signals.py @@ -3,13 +3,13 @@ """Signal handling for core app.""" import logging +from urllib.parse import urlparse from corsheaders import signals from django.conf import settings from django.db.models import Count, Q from django.db.models.signals import pre_delete from django.dispatch import Signal, receiver -from future.backports.urllib.parse import urlparse from rest_framework.permissions import SAFE_METHODS from readthedocs.oauth.models import RemoteOrganization diff --git a/readthedocs/core/templatetags/core_tags.py b/readthedocs/core/templatetags/core_tags.py index d05831aade9..e1b9e6f7903 100644 --- a/readthedocs/core/templatetags/core_tags.py +++ b/readthedocs/core/templatetags/core_tags.py @@ -1,12 +1,12 @@ """Template tags for core app.""" import hashlib +from urllib.parse import urlparse from django import template from django.conf import settings from django.utils.encoding import force_bytes, force_text from django.utils.safestring import mark_safe -from future.backports.urllib.parse import urlencode from readthedocs import __version__ from readthedocs.core.resolver import resolve diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index a929213885e..05f3afb7eac 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -2,6 +2,7 @@ """Project forms.""" from random import choice +from urllib.parse import urlparse from django import forms from django.conf import settings @@ -9,7 +10,6 @@ from django.template.loader import render_to_string from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ -from future.backports.urllib.parse import urlparse from guardian.shortcuts import assign from textclassifier.validators import ClassifierValidator diff --git a/readthedocs/projects/migrations/0010_migrate_domain_data.py b/readthedocs/projects/migrations/0010_migrate_domain_data.py index 636f200bfe7..2d18756d6d0 100644 --- a/readthedocs/projects/migrations/0010_migrate_domain_data.py +++ b/readthedocs/projects/migrations/0010_migrate_domain_data.py @@ -2,8 +2,7 @@ from __future__ import (absolute_import, print_function, unicode_literals) from django.db import models, migrations -from future.backports.urllib.parse import urlparse - +from urllib.parse import urlparse import readthedocs.projects.validators diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index 24bda8f18e9..a54f15d87b0 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -4,6 +4,7 @@ import fnmatch import logging import os +from urllib.parse import urlparse from django.conf import settings from django.contrib.auth.models import User @@ -12,7 +13,6 @@ from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from django_extensions.db.models import TimeStampedModel -from future.backports.urllib.parse import urlparse # noqa from guardian.shortcuts import assign from taggit.managers import TaggableManager diff --git a/readthedocs/projects/validators.py b/readthedocs/projects/validators.py index a362692c43d..d0f78d0ba02 100644 --- a/readthedocs/projects/validators.py +++ b/readthedocs/projects/validators.py @@ -1,13 +1,13 @@ """Validators for projects app.""" import re +from urllib.parse import urlparse from django.conf import settings from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from django.utils.deconstruct import deconstructible from django.utils.translation import ugettext_lazy as _ -from future.backports.urllib.parse import urlparse domain_regex = ( diff --git a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py index 97eceae19a8..6b4a6db070b 100644 --- a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py +++ b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py @@ -4,7 +4,7 @@ import mock from django.test import TestCase from django_dynamic_fixture import get -from future.backports.urllib.parse import urlencode +from urllib.parse import urlparse from readthedocs.builds.models import Version from readthedocs.projects.models import Feature, Project From f124be55434d87832ba5d50a59f6d158d3e547d2 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:10:21 +0100 Subject: [PATCH 11/42] Remove skipif --- readthedocs/config/tests/test_find.py | 17 ----------------- readthedocs/rtd_tests/tests/test_backend.py | 2 -- 2 files changed, 19 deletions(-) diff --git a/readthedocs/config/tests/test_find.py b/readthedocs/config/tests/test_find.py index dd4715f20d9..048a6c9b90c 100644 --- a/readthedocs/config/tests/test_find.py +++ b/readthedocs/config/tests/test_find.py @@ -1,7 +1,3 @@ -import os - -import pytest - from readthedocs.config.find import find_one from .utils import apply_fs @@ -17,16 +13,3 @@ def test_find_at_root(tmpdir): base = str(tmpdir) path = find_one(base, r'readthedocs\.yml') assert path == os.path.abspath(os.path.join(base, 'readthedocs.yml')) - - -@pytest.mark.skipif(not six.PY2, reason='Only for python2') -def test_find_unicode_path(tmpdir): - base_path = os.path.abspath( - os.path.join(os.path.dirname(__file__), 'fixtures/bad_encode_project') - ) - path = find_one(base_path, r'readthedocs\.yml') - assert path == '' - unicode_base_path = base_path.decode('utf-8') - assert isinstance(unicode_base_path, unicode) - path = find_one(unicode_base_path, r'readthedocs\.yml') - assert path == '' diff --git a/readthedocs/rtd_tests/tests/test_backend.py b/readthedocs/rtd_tests/tests/test_backend.py index 76eb07d371c..c47e8d4676a 100644 --- a/readthedocs/rtd_tests/tests/test_backend.py +++ b/readthedocs/rtd_tests/tests/test_backend.py @@ -5,7 +5,6 @@ from tempfile import mkdtemp import django_dynamic_fixture as fixture -import pytest from django.contrib.auth.models import User from mock import Mock, patch @@ -72,7 +71,6 @@ def test_git_branches(self, checkout_path): {branch.verbose_name for branch in repo.branches}, ) - @pytest.mark.skipif(six.PY2, reason='Only for python3') @patch('readthedocs.projects.models.Project.checkout_path') def test_git_branches_unicode(self, checkout_path): repo_path = self.project.repo From 05f5309d996c4f47baf69c1598e5f20aa585a029 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:11:05 +0100 Subject: [PATCH 12/42] Remove usage of `from django.utils import six` --- readthedocs/core/utils/__init__.py | 1 - readthedocs/rtd_tests/tests/test_api.py | 1 - 2 files changed, 2 deletions(-) diff --git a/readthedocs/core/utils/__init__.py b/readthedocs/core/utils/__init__.py index 0b8134751a0..8632b1557d0 100644 --- a/readthedocs/core/utils/__init__.py +++ b/readthedocs/core/utils/__init__.py @@ -11,7 +11,6 @@ import re from django.conf import settings -from django.utils import six from django.utils.functional import allow_lazy from django.utils.safestring import SafeText, mark_safe from django.utils.text import slugify as slugify_base diff --git a/readthedocs/rtd_tests/tests/test_api.py b/readthedocs/rtd_tests/tests/test_api.py index 44f2a037418..01693da1838 100644 --- a/readthedocs/rtd_tests/tests/test_api.py +++ b/readthedocs/rtd_tests/tests/test_api.py @@ -9,7 +9,6 @@ from django.http import QueryDict from django.test import TestCase from django.urls import reverse -from django.utils import six from django_dynamic_fixture import get from rest_framework import status from rest_framework.test import APIClient From 685e547aa34f9809b68cbc720f9c68ba767cac5d Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:12:29 +0100 Subject: [PATCH 13/42] Use ipaddress from standard library --- readthedocs/analytics/utils.py | 7 +- readthedocs/analytics/vendor/__init__.py | 0 readthedocs/analytics/vendor/ipaddress.py | 2418 --------------------- 3 files changed, 1 insertion(+), 2424 deletions(-) delete mode 100644 readthedocs/analytics/vendor/__init__.py delete mode 100644 readthedocs/analytics/vendor/ipaddress.py diff --git a/readthedocs/analytics/utils.py b/readthedocs/analytics/utils.py index 52c44d5cd35..5c74cfd1eae 100644 --- a/readthedocs/analytics/utils.py +++ b/readthedocs/analytics/utils.py @@ -1,6 +1,7 @@ """Utilities related to analytics""" import hashlib +import ipaddress import logging import requests @@ -10,12 +11,6 @@ from user_agents import parse -try: - # Python 3.3+ only - import ipaddress -except ImportError: - from .vendor import ipaddress - log = logging.getLogger(__name__) # noqa diff --git a/readthedocs/analytics/vendor/__init__.py b/readthedocs/analytics/vendor/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/readthedocs/analytics/vendor/ipaddress.py b/readthedocs/analytics/vendor/ipaddress.py deleted file mode 100644 index 8014d2ef777..00000000000 --- a/readthedocs/analytics/vendor/ipaddress.py +++ /dev/null @@ -1,2418 +0,0 @@ -# flake8: noqa -# Copyright 2007 Google Inc. -# Licensed to PSF under a Contributor Agreement. - -"""A fast, lightweight IPv4/IPv6 manipulation library in Python. - -This library is used to create/poke/manipulate IPv4 and IPv6 addresses -and networks. - -""" - -import itertools -import struct - - -__version__ = '1.0.22' - -# Compatibility functions -_compat_int_types = (int,) -try: - _compat_int_types = (int, long) -except NameError: - pass -try: - _compat_str = unicode -except NameError: - _compat_str = str - assert bytes != str -if b'\0'[0] == 0: # Python 3 semantics - def _compat_bytes_to_byte_vals(byt): - return byt -else: - def _compat_bytes_to_byte_vals(byt): - return [struct.unpack(b'!B', b)[0] for b in byt] -try: - _compat_int_from_byte_vals = int.from_bytes -except AttributeError: - def _compat_int_from_byte_vals(bytvals, endianess): - assert endianess == 'big' - res = 0 - for bv in bytvals: - assert isinstance(bv, _compat_int_types) - res = (res << 8) + bv - return res - - -def _compat_to_bytes(intval, length, endianess): - assert isinstance(intval, _compat_int_types) - assert endianess == 'big' - if length == 4: - if intval < 0 or intval >= 2 ** 32: - raise struct.error("integer out of range for 'I' format code") - return struct.pack(b'!I', intval) - elif length == 16: - if intval < 0 or intval >= 2 ** 128: - raise struct.error("integer out of range for 'QQ' format code") - return struct.pack(b'!QQ', intval >> 64, intval & 0xffffffffffffffff) - else: - raise NotImplementedError() - - -if hasattr(int, 'bit_length'): - # Not int.bit_length , since that won't work in 2.7 where long exists - def _compat_bit_length(i): - return i.bit_length() -else: - def _compat_bit_length(i): - for res in itertools.count(): - if i >> res == 0: - return res - - -def _compat_range(start, end, step=1): - assert step > 0 - i = start - while i < end: - yield i - i += step - - -class _TotalOrderingMixin(object): - __slots__ = () - - # Helper that derives the other comparison operations from - # __lt__ and __eq__ - # We avoid functools.total_ordering because it doesn't handle - # NotImplemented correctly yet (http://bugs.python.org/issue10042) - def __eq__(self, other): - raise NotImplementedError - - def __ne__(self, other): - equal = self.__eq__(other) - if equal is NotImplemented: - return NotImplemented - return not equal - - def __lt__(self, other): - raise NotImplementedError - - def __le__(self, other): - less = self.__lt__(other) - if less is NotImplemented or not less: - return self.__eq__(other) - return less - - def __gt__(self, other): - less = self.__lt__(other) - if less is NotImplemented: - return NotImplemented - equal = self.__eq__(other) - if equal is NotImplemented: - return NotImplemented - return not (less or equal) - - def __ge__(self, other): - less = self.__lt__(other) - if less is NotImplemented: - return NotImplemented - return not less - - -IPV4LENGTH = 32 -IPV6LENGTH = 128 - - -class AddressValueError(ValueError): - """A Value Error related to the address.""" - - -class NetmaskValueError(ValueError): - """A Value Error related to the netmask.""" - - -def ip_address(address): - """Take an IP string/int and return an object of the correct type. - - Args: - address: A string or integer, the IP address. Either IPv4 or - IPv6 addresses may be supplied; integers less than 2**32 will - be considered to be IPv4 by default. - - Returns: - An IPv4Address or IPv6Address object. - - Raises: - ValueError: if the *address* passed isn't either a v4 or a v6 - address - - """ - try: - return IPv4Address(address) - except (AddressValueError, NetmaskValueError): - pass - - try: - return IPv6Address(address) - except (AddressValueError, NetmaskValueError): - pass - - if isinstance(address, bytes): - raise AddressValueError( - '%r does not appear to be an IPv4 or IPv6 address. ' - 'Did you pass in a bytes (str in Python 2) instead of' - ' a unicode object?' % address) - - raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % - address) - - -def ip_network(address, strict=True): - """Take an IP string/int and return an object of the correct type. - - Args: - address: A string or integer, the IP network. Either IPv4 or - IPv6 networks may be supplied; integers less than 2**32 will - be considered to be IPv4 by default. - - Returns: - An IPv4Network or IPv6Network object. - - Raises: - ValueError: if the string passed isn't either a v4 or a v6 - address. Or if the network has host bits set. - - """ - try: - return IPv4Network(address, strict) - except (AddressValueError, NetmaskValueError): - pass - - try: - return IPv6Network(address, strict) - except (AddressValueError, NetmaskValueError): - pass - - if isinstance(address, bytes): - raise AddressValueError( - '%r does not appear to be an IPv4 or IPv6 network. ' - 'Did you pass in a bytes (str in Python 2) instead of' - ' a unicode object?' % address) - - raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % - address) - - -def ip_interface(address): - """Take an IP string/int and return an object of the correct type. - - Args: - address: A string or integer, the IP address. Either IPv4 or - IPv6 addresses may be supplied; integers less than 2**32 will - be considered to be IPv4 by default. - - Returns: - An IPv4Interface or IPv6Interface object. - - Raises: - ValueError: if the string passed isn't either a v4 or a v6 - address. - - Notes: - The IPv?Interface classes describe an Address on a particular - Network, so they're basically a combination of both the Address - and Network classes. - - """ - try: - return IPv4Interface(address) - except (AddressValueError, NetmaskValueError): - pass - - try: - return IPv6Interface(address) - except (AddressValueError, NetmaskValueError): - pass - - raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' % - address) - - -def v4_int_to_packed(address): - """Represent an address as 4 packed bytes in network (big-endian) order. - - Args: - address: An integer representation of an IPv4 IP address. - - Returns: - The integer address packed as 4 bytes in network (big-endian) order. - - Raises: - ValueError: If the integer is negative or too large to be an - IPv4 IP address. - - """ - try: - return _compat_to_bytes(address, 4, 'big') - except (struct.error, OverflowError): - raise ValueError("Address negative or too large for IPv4") - - -def v6_int_to_packed(address): - """Represent an address as 16 packed bytes in network (big-endian) order. - - Args: - address: An integer representation of an IPv6 IP address. - - Returns: - The integer address packed as 16 bytes in network (big-endian) order. - - """ - try: - return _compat_to_bytes(address, 16, 'big') - except (struct.error, OverflowError): - raise ValueError("Address negative or too large for IPv6") - - -def _split_optional_netmask(address): - """Helper to split the netmask and raise AddressValueError if needed""" - addr = _compat_str(address).split('/') - if len(addr) > 2: - raise AddressValueError("Only one '/' permitted in %r" % address) - return addr - - -def _find_address_range(addresses): - """Find a sequence of sorted deduplicated IPv#Address. - - Args: - addresses: a list of IPv#Address objects. - - Yields: - A tuple containing the first and last IP addresses in the sequence. - - """ - it = iter(addresses) - first = last = next(it) - for ip in it: - if ip._ip != last._ip + 1: - yield first, last - first = ip - last = ip - yield first, last - - -def _count_righthand_zero_bits(number, bits): - """Count the number of zero bits on the right hand side. - - Args: - number: an integer. - bits: maximum number of bits to count. - - Returns: - The number of zero bits on the right hand side of the number. - - """ - if number == 0: - return bits - return min(bits, _compat_bit_length(~number & (number - 1))) - - -def summarize_address_range(first, last): - """Summarize a network range given the first and last IP addresses. - - Example: - >>> list(summarize_address_range(IPv4Address('192.0.2.0'), - ... IPv4Address('192.0.2.130'))) - ... #doctest: +NORMALIZE_WHITESPACE - [IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'), - IPv4Network('192.0.2.130/32')] - - Args: - first: the first IPv4Address or IPv6Address in the range. - last: the last IPv4Address or IPv6Address in the range. - - Returns: - An iterator of the summarized IPv(4|6) network objects. - - Raise: - TypeError: - If the first and last objects are not IP addresses. - If the first and last objects are not the same version. - ValueError: - If the last object is not greater than the first. - If the version of the first address is not 4 or 6. - - """ - if (not (isinstance(first, _BaseAddress) and - isinstance(last, _BaseAddress))): - raise TypeError('first and last must be IP addresses, not networks') - if first.version != last.version: - raise TypeError("%s and %s are not of the same version" % ( - first, last)) - if first > last: - raise ValueError('last IP address must be greater than first') - - if first.version == 4: - ip = IPv4Network - elif first.version == 6: - ip = IPv6Network - else: - raise ValueError('unknown IP version') - - ip_bits = first._max_prefixlen - first_int = first._ip - last_int = last._ip - while first_int <= last_int: - nbits = min(_count_righthand_zero_bits(first_int, ip_bits), - _compat_bit_length(last_int - first_int + 1) - 1) - net = ip((first_int, ip_bits - nbits)) - yield net - first_int += 1 << nbits - if first_int - 1 == ip._ALL_ONES: - break - - -def _collapse_addresses_internal(addresses): - """Loops through the addresses, collapsing concurrent netblocks. - - Example: - - ip1 = IPv4Network('192.0.2.0/26') - ip2 = IPv4Network('192.0.2.64/26') - ip3 = IPv4Network('192.0.2.128/26') - ip4 = IPv4Network('192.0.2.192/26') - - _collapse_addresses_internal([ip1, ip2, ip3, ip4]) -> - [IPv4Network('192.0.2.0/24')] - - This shouldn't be called directly; it is called via - collapse_addresses([]). - - Args: - addresses: A list of IPv4Network's or IPv6Network's - - Returns: - A list of IPv4Network's or IPv6Network's depending on what we were - passed. - - """ - # First merge - to_merge = list(addresses) - subnets = {} - while to_merge: - net = to_merge.pop() - supernet = net.supernet() - existing = subnets.get(supernet) - if existing is None: - subnets[supernet] = net - elif existing != net: - # Merge consecutive subnets - del subnets[supernet] - to_merge.append(supernet) - # Then iterate over resulting networks, skipping subsumed subnets - last = None - for net in sorted(subnets.values()): - if last is not None: - # Since they are sorted, - # last.network_address <= net.network_address is a given. - if last.broadcast_address >= net.broadcast_address: - continue - yield net - last = net - - -def collapse_addresses(addresses): - """Collapse a list of IP objects. - - Example: - collapse_addresses([IPv4Network('192.0.2.0/25'), - IPv4Network('192.0.2.128/25')]) -> - [IPv4Network('192.0.2.0/24')] - - Args: - addresses: An iterator of IPv4Network or IPv6Network objects. - - Returns: - An iterator of the collapsed IPv(4|6)Network objects. - - Raises: - TypeError: If passed a list of mixed version objects. - - """ - addrs = [] - ips = [] - nets = [] - - # split IP addresses and networks - for ip in addresses: - if isinstance(ip, _BaseAddress): - if ips and ips[-1]._version != ip._version: - raise TypeError("%s and %s are not of the same version" % ( - ip, ips[-1])) - ips.append(ip) - elif ip._prefixlen == ip._max_prefixlen: - if ips and ips[-1]._version != ip._version: - raise TypeError("%s and %s are not of the same version" % ( - ip, ips[-1])) - try: - ips.append(ip.ip) - except AttributeError: - ips.append(ip.network_address) - else: - if nets and nets[-1]._version != ip._version: - raise TypeError("%s and %s are not of the same version" % ( - ip, nets[-1])) - nets.append(ip) - - # sort and dedup - ips = sorted(set(ips)) - - # find consecutive address ranges in the sorted sequence and summarize them - if ips: - for first, last in _find_address_range(ips): - addrs.extend(summarize_address_range(first, last)) - - return _collapse_addresses_internal(addrs + nets) - - -def get_mixed_type_key(obj): - """Return a key suitable for sorting between networks and addresses. - - Address and Network objects are not sortable by default; they're - fundamentally different so the expression - - IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24') - - doesn't make any sense. There are some times however, where you may wish - to have ipaddress sort these for you anyway. If you need to do this, you - can use this function as the key= argument to sorted(). - - Args: - obj: either a Network or Address object. - Returns: - appropriate key. - - """ - if isinstance(obj, _BaseNetwork): - return obj._get_networks_key() - elif isinstance(obj, _BaseAddress): - return obj._get_address_key() - return NotImplemented - - -class _IPAddressBase(_TotalOrderingMixin): - - """The mother class.""" - - __slots__ = () - - @property - def exploded(self): - """Return the longhand version of the IP address as a string.""" - return self._explode_shorthand_ip_string() - - @property - def compressed(self): - """Return the shorthand version of the IP address as a string.""" - return _compat_str(self) - - @property - def reverse_pointer(self): - """The name of the reverse DNS pointer for the IP address, e.g.: - >>> ipaddress.ip_address("127.0.0.1").reverse_pointer - '1.0.0.127.in-addr.arpa' - >>> ipaddress.ip_address("2001:db8::1").reverse_pointer - '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa' - - """ - return self._reverse_pointer() - - @property - def version(self): - msg = '%200s has no version specified' % (type(self),) - raise NotImplementedError(msg) - - def _check_int_address(self, address): - if address < 0: - msg = "%d (< 0) is not permitted as an IPv%d address" - raise AddressValueError(msg % (address, self._version)) - if address > self._ALL_ONES: - msg = "%d (>= 2**%d) is not permitted as an IPv%d address" - raise AddressValueError(msg % (address, self._max_prefixlen, - self._version)) - - def _check_packed_address(self, address, expected_len): - address_len = len(address) - if address_len != expected_len: - msg = ( - '%r (len %d != %d) is not permitted as an IPv%d address. ' - 'Did you pass in a bytes (str in Python 2) instead of' - ' a unicode object?') - raise AddressValueError(msg % (address, address_len, - expected_len, self._version)) - - @classmethod - def _ip_int_from_prefix(cls, prefixlen): - """Turn the prefix length into a bitwise netmask - - Args: - prefixlen: An integer, the prefix length. - - Returns: - An integer. - - """ - return cls._ALL_ONES ^ (cls._ALL_ONES >> prefixlen) - - @classmethod - def _prefix_from_ip_int(cls, ip_int): - """Return prefix length from the bitwise netmask. - - Args: - ip_int: An integer, the netmask in expanded bitwise format - - Returns: - An integer, the prefix length. - - Raises: - ValueError: If the input intermingles zeroes & ones - """ - trailing_zeroes = _count_righthand_zero_bits(ip_int, - cls._max_prefixlen) - prefixlen = cls._max_prefixlen - trailing_zeroes - leading_ones = ip_int >> trailing_zeroes - all_ones = (1 << prefixlen) - 1 - if leading_ones != all_ones: - byteslen = cls._max_prefixlen // 8 - details = _compat_to_bytes(ip_int, byteslen, 'big') - msg = 'Netmask pattern %r mixes zeroes & ones' - raise ValueError(msg % details) - return prefixlen - - @classmethod - def _report_invalid_netmask(cls, netmask_str): - msg = '%r is not a valid netmask' % netmask_str - raise NetmaskValueError(msg) - - @classmethod - def _prefix_from_prefix_string(cls, prefixlen_str): - """Return prefix length from a numeric string - - Args: - prefixlen_str: The string to be converted - - Returns: - An integer, the prefix length. - - Raises: - NetmaskValueError: If the input is not a valid netmask - """ - # int allows a leading +/- as well as surrounding whitespace, - # so we ensure that isn't the case - if not _BaseV4._DECIMAL_DIGITS.issuperset(prefixlen_str): - cls._report_invalid_netmask(prefixlen_str) - try: - prefixlen = int(prefixlen_str) - except ValueError: - cls._report_invalid_netmask(prefixlen_str) - if not (0 <= prefixlen <= cls._max_prefixlen): - cls._report_invalid_netmask(prefixlen_str) - return prefixlen - - @classmethod - def _prefix_from_ip_string(cls, ip_str): - """Turn a netmask/hostmask string into a prefix length - - Args: - ip_str: The netmask/hostmask to be converted - - Returns: - An integer, the prefix length. - - Raises: - NetmaskValueError: If the input is not a valid netmask/hostmask - """ - # Parse the netmask/hostmask like an IP address. - try: - ip_int = cls._ip_int_from_string(ip_str) - except AddressValueError: - cls._report_invalid_netmask(ip_str) - - # Try matching a netmask (this would be /1*0*/ as a bitwise regexp). - # Note that the two ambiguous cases (all-ones and all-zeroes) are - # treated as netmasks. - try: - return cls._prefix_from_ip_int(ip_int) - except ValueError: - pass - - # Invert the bits, and try matching a /0+1+/ hostmask instead. - ip_int ^= cls._ALL_ONES - try: - return cls._prefix_from_ip_int(ip_int) - except ValueError: - cls._report_invalid_netmask(ip_str) - - def __reduce__(self): - return self.__class__, (_compat_str(self),) - - -class _BaseAddress(_IPAddressBase): - - """A generic IP object. - - This IP class contains the version independent methods which are - used by single IP addresses. - """ - - __slots__ = () - - def __int__(self): - return self._ip - - def __eq__(self, other): - try: - return (self._ip == other._ip and - self._version == other._version) - except AttributeError: - return NotImplemented - - def __lt__(self, other): - if not isinstance(other, _IPAddressBase): - return NotImplemented - if not isinstance(other, _BaseAddress): - raise TypeError('%s and %s are not of the same type' % ( - self, other)) - if self._version != other._version: - raise TypeError('%s and %s are not of the same version' % ( - self, other)) - if self._ip != other._ip: - return self._ip < other._ip - return False - - # Shorthand for Integer addition and subtraction. This is not - # meant to ever support addition/subtraction of addresses. - def __add__(self, other): - if not isinstance(other, _compat_int_types): - return NotImplemented - return self.__class__(int(self) + other) - - def __sub__(self, other): - if not isinstance(other, _compat_int_types): - return NotImplemented - return self.__class__(int(self) - other) - - def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) - - def __str__(self): - return _compat_str(self._string_from_ip_int(self._ip)) - - def __hash__(self): - return hash(hex(int(self._ip))) - - def _get_address_key(self): - return (self._version, self) - - def __reduce__(self): - return self.__class__, (self._ip,) - - -class _BaseNetwork(_IPAddressBase): - - """A generic IP network object. - - This IP class contains the version independent methods which are - used by networks. - - """ - def __init__(self, address): - self._cache = {} - - def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, _compat_str(self)) - - def __str__(self): - return '%s/%d' % (self.network_address, self.prefixlen) - - def hosts(self): - """Generate Iterator over usable hosts in a network. - - This is like __iter__ except it doesn't return the network - or broadcast addresses. - - """ - network = int(self.network_address) - broadcast = int(self.broadcast_address) - for x in _compat_range(network + 1, broadcast): - yield self._address_class(x) - - def __iter__(self): - network = int(self.network_address) - broadcast = int(self.broadcast_address) - for x in _compat_range(network, broadcast + 1): - yield self._address_class(x) - - def __getitem__(self, n): - network = int(self.network_address) - broadcast = int(self.broadcast_address) - if n >= 0: - if network + n > broadcast: - raise IndexError('address out of range') - return self._address_class(network + n) - else: - n += 1 - if broadcast + n < network: - raise IndexError('address out of range') - return self._address_class(broadcast + n) - - def __lt__(self, other): - if not isinstance(other, _IPAddressBase): - return NotImplemented - if not isinstance(other, _BaseNetwork): - raise TypeError('%s and %s are not of the same type' % ( - self, other)) - if self._version != other._version: - raise TypeError('%s and %s are not of the same version' % ( - self, other)) - if self.network_address != other.network_address: - return self.network_address < other.network_address - if self.netmask != other.netmask: - return self.netmask < other.netmask - return False - - def __eq__(self, other): - try: - return (self._version == other._version and - self.network_address == other.network_address and - int(self.netmask) == int(other.netmask)) - except AttributeError: - return NotImplemented - - def __hash__(self): - return hash(int(self.network_address) ^ int(self.netmask)) - - def __contains__(self, other): - # always false if one is v4 and the other is v6. - if self._version != other._version: - return False - # dealing with another network. - if isinstance(other, _BaseNetwork): - return False - # dealing with another address - else: - # address - return (int(self.network_address) <= int(other._ip) <= - int(self.broadcast_address)) - - def overlaps(self, other): - """Tell if self is partly contained in other.""" - return self.network_address in other or ( - self.broadcast_address in other or ( - other.network_address in self or ( - other.broadcast_address in self))) - - @property - def broadcast_address(self): - x = self._cache.get('broadcast_address') - if x is None: - x = self._address_class(int(self.network_address) | - int(self.hostmask)) - self._cache['broadcast_address'] = x - return x - - @property - def hostmask(self): - x = self._cache.get('hostmask') - if x is None: - x = self._address_class(int(self.netmask) ^ self._ALL_ONES) - self._cache['hostmask'] = x - return x - - @property - def with_prefixlen(self): - return '%s/%d' % (self.network_address, self._prefixlen) - - @property - def with_netmask(self): - return '%s/%s' % (self.network_address, self.netmask) - - @property - def with_hostmask(self): - return '%s/%s' % (self.network_address, self.hostmask) - - @property - def num_addresses(self): - """Number of hosts in the current subnet.""" - return int(self.broadcast_address) - int(self.network_address) + 1 - - @property - def _address_class(self): - # Returning bare address objects (rather than interfaces) allows for - # more consistent behaviour across the network address, broadcast - # address and individual host addresses. - msg = '%200s has no associated address class' % (type(self),) - raise NotImplementedError(msg) - - @property - def prefixlen(self): - return self._prefixlen - - def address_exclude(self, other): - """Remove an address from a larger block. - - For example: - - addr1 = ip_network('192.0.2.0/28') - addr2 = ip_network('192.0.2.1/32') - list(addr1.address_exclude(addr2)) = - [IPv4Network('192.0.2.0/32'), IPv4Network('192.0.2.2/31'), - IPv4Network('192.0.2.4/30'), IPv4Network('192.0.2.8/29')] - - or IPv6: - - addr1 = ip_network('2001:db8::1/32') - addr2 = ip_network('2001:db8::1/128') - list(addr1.address_exclude(addr2)) = - [ip_network('2001:db8::1/128'), - ip_network('2001:db8::2/127'), - ip_network('2001:db8::4/126'), - ip_network('2001:db8::8/125'), - ... - ip_network('2001:db8:8000::/33')] - - Args: - other: An IPv4Network or IPv6Network object of the same type. - - Returns: - An iterator of the IPv(4|6)Network objects which is self - minus other. - - Raises: - TypeError: If self and other are of differing address - versions, or if other is not a network object. - ValueError: If other is not completely contained by self. - - """ - if not self._version == other._version: - raise TypeError("%s and %s are not of the same version" % ( - self, other)) - - if not isinstance(other, _BaseNetwork): - raise TypeError("%s is not a network object" % other) - - if not other.subnet_of(self): - raise ValueError('%s not contained in %s' % (other, self)) - if other == self: - return - - # Make sure we're comparing the network of other. - other = other.__class__('%s/%s' % (other.network_address, - other.prefixlen)) - - s1, s2 = self.subnets() - while s1 != other and s2 != other: - if other.subnet_of(s1): - yield s2 - s1, s2 = s1.subnets() - elif other.subnet_of(s2): - yield s1 - s1, s2 = s2.subnets() - else: - # If we got here, there's a bug somewhere. - raise AssertionError('Error performing exclusion: ' - 's1: %s s2: %s other: %s' % - (s1, s2, other)) - if s1 == other: - yield s2 - elif s2 == other: - yield s1 - else: - # If we got here, there's a bug somewhere. - raise AssertionError('Error performing exclusion: ' - 's1: %s s2: %s other: %s' % - (s1, s2, other)) - - def compare_networks(self, other): - """Compare two IP objects. - - This is only concerned about the comparison of the integer - representation of the network addresses. This means that the - host bits aren't considered at all in this method. If you want - to compare host bits, you can easily enough do a - 'HostA._ip < HostB._ip' - - Args: - other: An IP object. - - Returns: - If the IP versions of self and other are the same, returns: - - -1 if self < other: - eg: IPv4Network('192.0.2.0/25') < IPv4Network('192.0.2.128/25') - IPv6Network('2001:db8::1000/124') < - IPv6Network('2001:db8::2000/124') - 0 if self == other - eg: IPv4Network('192.0.2.0/24') == IPv4Network('192.0.2.0/24') - IPv6Network('2001:db8::1000/124') == - IPv6Network('2001:db8::1000/124') - 1 if self > other - eg: IPv4Network('192.0.2.128/25') > IPv4Network('192.0.2.0/25') - IPv6Network('2001:db8::2000/124') > - IPv6Network('2001:db8::1000/124') - - Raises: - TypeError if the IP versions are different. - - """ - # does this need to raise a ValueError? - if self._version != other._version: - raise TypeError('%s and %s are not of the same type' % ( - self, other)) - # self._version == other._version below here: - if self.network_address < other.network_address: - return -1 - if self.network_address > other.network_address: - return 1 - # self.network_address == other.network_address below here: - if self.netmask < other.netmask: - return -1 - if self.netmask > other.netmask: - return 1 - return 0 - - def _get_networks_key(self): - """Network-only key function. - - Returns an object that identifies this address' network and - netmask. This function is a suitable "key" argument for sorted() - and list.sort(). - - """ - return (self._version, self.network_address, self.netmask) - - def subnets(self, prefixlen_diff=1, new_prefix=None): - """The subnets which join to make the current subnet. - - In the case that self contains only one IP - (self._prefixlen == 32 for IPv4 or self._prefixlen == 128 - for IPv6), yield an iterator with just ourself. - - Args: - prefixlen_diff: An integer, the amount the prefix length - should be increased by. This should not be set if - new_prefix is also set. - new_prefix: The desired new prefix length. This must be a - larger number (smaller prefix) than the existing prefix. - This should not be set if prefixlen_diff is also set. - - Returns: - An iterator of IPv(4|6) objects. - - Raises: - ValueError: The prefixlen_diff is too small or too large. - OR - prefixlen_diff and new_prefix are both set or new_prefix - is a smaller number than the current prefix (smaller - number means a larger network) - - """ - if self._prefixlen == self._max_prefixlen: - yield self - return - - if new_prefix is not None: - if new_prefix < self._prefixlen: - raise ValueError('new prefix must be longer') - if prefixlen_diff != 1: - raise ValueError('cannot set prefixlen_diff and new_prefix') - prefixlen_diff = new_prefix - self._prefixlen - - if prefixlen_diff < 0: - raise ValueError('prefix length diff must be > 0') - new_prefixlen = self._prefixlen + prefixlen_diff - - if new_prefixlen > self._max_prefixlen: - raise ValueError( - 'prefix length diff %d is invalid for netblock %s' % ( - new_prefixlen, self)) - - start = int(self.network_address) - end = int(self.broadcast_address) + 1 - step = (int(self.hostmask) + 1) >> prefixlen_diff - for new_addr in _compat_range(start, end, step): - current = self.__class__((new_addr, new_prefixlen)) - yield current - - def supernet(self, prefixlen_diff=1, new_prefix=None): - """The supernet containing the current network. - - Args: - prefixlen_diff: An integer, the amount the prefix length of - the network should be decreased by. For example, given a - /24 network and a prefixlen_diff of 3, a supernet with a - /21 netmask is returned. - - Returns: - An IPv4 network object. - - Raises: - ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have - a negative prefix length. - OR - If prefixlen_diff and new_prefix are both set or new_prefix is a - larger number than the current prefix (larger number means a - smaller network) - - """ - if self._prefixlen == 0: - return self - - if new_prefix is not None: - if new_prefix > self._prefixlen: - raise ValueError('new prefix must be shorter') - if prefixlen_diff != 1: - raise ValueError('cannot set prefixlen_diff and new_prefix') - prefixlen_diff = self._prefixlen - new_prefix - - new_prefixlen = self.prefixlen - prefixlen_diff - if new_prefixlen < 0: - raise ValueError( - 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % - (self.prefixlen, prefixlen_diff)) - return self.__class__(( - int(self.network_address) & (int(self.netmask) << prefixlen_diff), - new_prefixlen)) - - @property - def is_multicast(self): - """Test if the address is reserved for multicast use. - - Returns: - A boolean, True if the address is a multicast address. - See RFC 2373 2.7 for details. - - """ - return (self.network_address.is_multicast and - self.broadcast_address.is_multicast) - - @staticmethod - def _is_subnet_of(a, b): - try: - # Always false if one is v4 and the other is v6. - if a._version != b._version: - raise TypeError("%s and %s are not of the same version" (a, b)) - return (b.network_address <= a.network_address and - b.broadcast_address >= a.broadcast_address) - except AttributeError: - raise TypeError("Unable to test subnet containment " - "between %s and %s" % (a, b)) - - def subnet_of(self, other): - """Return True if this network is a subnet of other.""" - return self._is_subnet_of(self, other) - - def supernet_of(self, other): - """Return True if this network is a supernet of other.""" - return self._is_subnet_of(other, self) - - @property - def is_reserved(self): - """Test if the address is otherwise IETF reserved. - - Returns: - A boolean, True if the address is within one of the - reserved IPv6 Network ranges. - - """ - return (self.network_address.is_reserved and - self.broadcast_address.is_reserved) - - @property - def is_link_local(self): - """Test if the address is reserved for link-local. - - Returns: - A boolean, True if the address is reserved per RFC 4291. - - """ - return (self.network_address.is_link_local and - self.broadcast_address.is_link_local) - - @property - def is_private(self): - """Test if this address is allocated for private networks. - - Returns: - A boolean, True if the address is reserved per - iana-ipv4-special-registry or iana-ipv6-special-registry. - - """ - return (self.network_address.is_private and - self.broadcast_address.is_private) - - @property - def is_global(self): - """Test if this address is allocated for public networks. - - Returns: - A boolean, True if the address is not reserved per - iana-ipv4-special-registry or iana-ipv6-special-registry. - - """ - return not self.is_private - - @property - def is_unspecified(self): - """Test if the address is unspecified. - - Returns: - A boolean, True if this is the unspecified address as defined in - RFC 2373 2.5.2. - - """ - return (self.network_address.is_unspecified and - self.broadcast_address.is_unspecified) - - @property - def is_loopback(self): - """Test if the address is a loopback address. - - Returns: - A boolean, True if the address is a loopback address as defined in - RFC 2373 2.5.3. - - """ - return (self.network_address.is_loopback and - self.broadcast_address.is_loopback) - - -class _BaseV4(object): - - """Base IPv4 object. - - The following methods are used by IPv4 objects in both single IP - addresses and networks. - - """ - - __slots__ = () - _version = 4 - # Equivalent to 255.255.255.255 or 32 bits of 1's. - _ALL_ONES = (2 ** IPV4LENGTH) - 1 - _DECIMAL_DIGITS = frozenset('0123456789') - - # the valid octets for host and netmasks. only useful for IPv4. - _valid_mask_octets = frozenset([255, 254, 252, 248, 240, 224, 192, 128, 0]) - - _max_prefixlen = IPV4LENGTH - # There are only a handful of valid v4 netmasks, so we cache them all - # when constructed (see _make_netmask()). - _netmask_cache = {} - - def _explode_shorthand_ip_string(self): - return _compat_str(self) - - @classmethod - def _make_netmask(cls, arg): - """Make a (netmask, prefix_len) tuple from the given argument. - - Argument can be: - - an integer (the prefix length) - - a string representing the prefix length (e.g. "24") - - a string representing the prefix netmask (e.g. "255.255.255.0") - """ - if arg not in cls._netmask_cache: - if isinstance(arg, _compat_int_types): - prefixlen = arg - else: - try: - # Check for a netmask in prefix length form - prefixlen = cls._prefix_from_prefix_string(arg) - except NetmaskValueError: - # Check for a netmask or hostmask in dotted-quad form. - # This may raise NetmaskValueError. - prefixlen = cls._prefix_from_ip_string(arg) - netmask = IPv4Address(cls._ip_int_from_prefix(prefixlen)) - cls._netmask_cache[arg] = netmask, prefixlen - return cls._netmask_cache[arg] - - @classmethod - def _ip_int_from_string(cls, ip_str): - """Turn the given IP string into an integer for comparison. - - Args: - ip_str: A string, the IP ip_str. - - Returns: - The IP ip_str as an integer. - - Raises: - AddressValueError: if ip_str isn't a valid IPv4 Address. - - """ - if not ip_str: - raise AddressValueError('Address cannot be empty') - - octets = ip_str.split('.') - if len(octets) != 4: - raise AddressValueError("Expected 4 octets in %r" % ip_str) - - try: - return _compat_int_from_byte_vals( - map(cls._parse_octet, octets), 'big') - except ValueError as exc: - raise AddressValueError("%s in %r" % (exc, ip_str)) - - @classmethod - def _parse_octet(cls, octet_str): - """Convert a decimal octet into an integer. - - Args: - octet_str: A string, the number to parse. - - Returns: - The octet as an integer. - - Raises: - ValueError: if the octet isn't strictly a decimal from [0..255]. - - """ - if not octet_str: - raise ValueError("Empty octet not permitted") - # Whitelist the characters, since int() allows a lot of bizarre stuff. - if not cls._DECIMAL_DIGITS.issuperset(octet_str): - msg = "Only decimal digits permitted in %r" - raise ValueError(msg % octet_str) - # We do the length check second, since the invalid character error - # is likely to be more informative for the user - if len(octet_str) > 3: - msg = "At most 3 characters permitted in %r" - raise ValueError(msg % octet_str) - # Convert to integer (we know digits are legal) - octet_int = int(octet_str, 10) - # Any octets that look like they *might* be written in octal, - # and which don't look exactly the same in both octal and - # decimal are rejected as ambiguous - if octet_int > 7 and octet_str[0] == '0': - msg = "Ambiguous (octal/decimal) value in %r not permitted" - raise ValueError(msg % octet_str) - if octet_int > 255: - raise ValueError("Octet %d (> 255) not permitted" % octet_int) - return octet_int - - @classmethod - def _string_from_ip_int(cls, ip_int): - """Turns a 32-bit integer into dotted decimal notation. - - Args: - ip_int: An integer, the IP address. - - Returns: - The IP address as a string in dotted decimal notation. - - """ - return '.'.join(_compat_str(struct.unpack(b'!B', b)[0] - if isinstance(b, bytes) - else b) - for b in _compat_to_bytes(ip_int, 4, 'big')) - - def _is_hostmask(self, ip_str): - """Test if the IP string is a hostmask (rather than a netmask). - - Args: - ip_str: A string, the potential hostmask. - - Returns: - A boolean, True if the IP string is a hostmask. - - """ - bits = ip_str.split('.') - try: - parts = [x for x in map(int, bits) if x in self._valid_mask_octets] - except ValueError: - return False - if len(parts) != len(bits): - return False - if parts[0] < parts[-1]: - return True - return False - - def _reverse_pointer(self): - """Return the reverse DNS pointer name for the IPv4 address. - - This implements the method described in RFC1035 3.5. - - """ - reverse_octets = _compat_str(self).split('.')[::-1] - return '.'.join(reverse_octets) + '.in-addr.arpa' - - @property - def max_prefixlen(self): - return self._max_prefixlen - - @property - def version(self): - return self._version - - -class IPv4Address(_BaseV4, _BaseAddress): - - """Represent and manipulate single IPv4 Addresses.""" - - __slots__ = ('_ip', '__weakref__') - - def __init__(self, address): - - """ - Args: - address: A string or integer representing the IP - - Additionally, an integer can be passed, so - IPv4Address('192.0.2.1') == IPv4Address(3221225985). - or, more generally - IPv4Address(int(IPv4Address('192.0.2.1'))) == - IPv4Address('192.0.2.1') - - Raises: - AddressValueError: If ipaddress isn't a valid IPv4 address. - - """ - # Efficient constructor from integer. - if isinstance(address, _compat_int_types): - self._check_int_address(address) - self._ip = address - return - - # Constructing from a packed address - if isinstance(address, bytes): - self._check_packed_address(address, 4) - bvs = _compat_bytes_to_byte_vals(address) - self._ip = _compat_int_from_byte_vals(bvs, 'big') - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP string. - addr_str = _compat_str(address) - if '/' in addr_str: - raise AddressValueError("Unexpected '/' in %r" % address) - self._ip = self._ip_int_from_string(addr_str) - - @property - def packed(self): - """The binary representation of this address.""" - return v4_int_to_packed(self._ip) - - @property - def is_reserved(self): - """Test if the address is otherwise IETF reserved. - - Returns: - A boolean, True if the address is within the - reserved IPv4 Network range. - - """ - return self in self._constants._reserved_network - - @property - def is_private(self): - """Test if this address is allocated for private networks. - - Returns: - A boolean, True if the address is reserved per - iana-ipv4-special-registry. - - """ - return any(self in net for net in self._constants._private_networks) - - @property - def is_global(self): - return ( - self not in self._constants._public_network and - not self.is_private) - - @property - def is_multicast(self): - """Test if the address is reserved for multicast use. - - Returns: - A boolean, True if the address is multicast. - See RFC 3171 for details. - - """ - return self in self._constants._multicast_network - - @property - def is_unspecified(self): - """Test if the address is unspecified. - - Returns: - A boolean, True if this is the unspecified address as defined in - RFC 5735 3. - - """ - return self == self._constants._unspecified_address - - @property - def is_loopback(self): - """Test if the address is a loopback address. - - Returns: - A boolean, True if the address is a loopback per RFC 3330. - - """ - return self in self._constants._loopback_network - - @property - def is_link_local(self): - """Test if the address is reserved for link-local. - - Returns: - A boolean, True if the address is link-local per RFC 3927. - - """ - return self in self._constants._linklocal_network - - -class IPv4Interface(IPv4Address): - - def __init__(self, address): - if isinstance(address, (bytes, _compat_int_types)): - IPv4Address.__init__(self, address) - self.network = IPv4Network(self._ip) - self._prefixlen = self._max_prefixlen - return - - if isinstance(address, tuple): - IPv4Address.__init__(self, address[0]) - if len(address) > 1: - self._prefixlen = int(address[1]) - else: - self._prefixlen = self._max_prefixlen - - self.network = IPv4Network(address, strict=False) - self.netmask = self.network.netmask - self.hostmask = self.network.hostmask - return - - addr = _split_optional_netmask(address) - IPv4Address.__init__(self, addr[0]) - - self.network = IPv4Network(address, strict=False) - self._prefixlen = self.network._prefixlen - - self.netmask = self.network.netmask - self.hostmask = self.network.hostmask - - def __str__(self): - return '%s/%d' % (self._string_from_ip_int(self._ip), - self.network.prefixlen) - - def __eq__(self, other): - address_equal = IPv4Address.__eq__(self, other) - if not address_equal or address_equal is NotImplemented: - return address_equal - try: - return self.network == other.network - except AttributeError: - # An interface with an associated network is NOT the - # same as an unassociated address. That's why the hash - # takes the extra info into account. - return False - - def __lt__(self, other): - address_less = IPv4Address.__lt__(self, other) - if address_less is NotImplemented: - return NotImplemented - try: - return (self.network < other.network or - self.network == other.network and address_less) - except AttributeError: - # We *do* allow addresses and interfaces to be sorted. The - # unassociated address is considered less than all interfaces. - return False - - def __hash__(self): - return self._ip ^ self._prefixlen ^ int(self.network.network_address) - - __reduce__ = _IPAddressBase.__reduce__ - - @property - def ip(self): - return IPv4Address(self._ip) - - @property - def with_prefixlen(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self._prefixlen) - - @property - def with_netmask(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self.netmask) - - @property - def with_hostmask(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self.hostmask) - - -class IPv4Network(_BaseV4, _BaseNetwork): - - """This class represents and manipulates 32-bit IPv4 network + addresses.. - - Attributes: [examples for IPv4Network('192.0.2.0/27')] - .network_address: IPv4Address('192.0.2.0') - .hostmask: IPv4Address('0.0.0.31') - .broadcast_address: IPv4Address('192.0.2.32') - .netmask: IPv4Address('255.255.255.224') - .prefixlen: 27 - - """ - # Class to use when creating address objects - _address_class = IPv4Address - - def __init__(self, address, strict=True): - - """Instantiate a new IPv4 network object. - - Args: - address: A string or integer representing the IP [& network]. - '192.0.2.0/24' - '192.0.2.0/255.255.255.0' - '192.0.0.2/0.0.0.255' - are all functionally the same in IPv4. Similarly, - '192.0.2.1' - '192.0.2.1/255.255.255.255' - '192.0.2.1/32' - are also functionally equivalent. That is to say, failing to - provide a subnetmask will create an object with a mask of /32. - - If the mask (portion after the / in the argument) is given in - dotted quad form, it is treated as a netmask if it starts with a - non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it - starts with a zero field (e.g. 0.255.255.255 == /8), with the - single exception of an all-zero mask which is treated as a - netmask == /0. If no mask is given, a default of /32 is used. - - Additionally, an integer can be passed, so - IPv4Network('192.0.2.1') == IPv4Network(3221225985) - or, more generally - IPv4Interface(int(IPv4Interface('192.0.2.1'))) == - IPv4Interface('192.0.2.1') - - Raises: - AddressValueError: If ipaddress isn't a valid IPv4 address. - NetmaskValueError: If the netmask isn't valid for - an IPv4 address. - ValueError: If strict is True and a network address is not - supplied. - - """ - _BaseNetwork.__init__(self, address) - - # Constructing from a packed address or integer - if isinstance(address, (_compat_int_types, bytes)): - self.network_address = IPv4Address(address) - self.netmask, self._prefixlen = self._make_netmask( - self._max_prefixlen) - # fixme: address/network test here. - return - - if isinstance(address, tuple): - if len(address) > 1: - arg = address[1] - else: - # We weren't given an address[1] - arg = self._max_prefixlen - self.network_address = IPv4Address(address[0]) - self.netmask, self._prefixlen = self._make_netmask(arg) - packed = int(self.network_address) - if packed & int(self.netmask) != packed: - if strict: - raise ValueError('%s has host bits set' % self) - else: - self.network_address = IPv4Address(packed & - int(self.netmask)) - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP prefix string. - addr = _split_optional_netmask(address) - self.network_address = IPv4Address(self._ip_int_from_string(addr[0])) - - if len(addr) == 2: - arg = addr[1] - else: - arg = self._max_prefixlen - self.netmask, self._prefixlen = self._make_netmask(arg) - - if strict: - if (IPv4Address(int(self.network_address) & int(self.netmask)) != - self.network_address): - raise ValueError('%s has host bits set' % self) - self.network_address = IPv4Address(int(self.network_address) & - int(self.netmask)) - - if self._prefixlen == (self._max_prefixlen - 1): - self.hosts = self.__iter__ - - @property - def is_global(self): - """Test if this address is allocated for public networks. - - Returns: - A boolean, True if the address is not reserved per - iana-ipv4-special-registry. - - """ - return (not (self.network_address in IPv4Network('100.64.0.0/10') and - self.broadcast_address in IPv4Network('100.64.0.0/10')) and - not self.is_private) - - -class _IPv4Constants(object): - - _linklocal_network = IPv4Network('169.254.0.0/16') - - _loopback_network = IPv4Network('127.0.0.0/8') - - _multicast_network = IPv4Network('224.0.0.0/4') - - _public_network = IPv4Network('100.64.0.0/10') - - _private_networks = [ - IPv4Network('0.0.0.0/8'), - IPv4Network('10.0.0.0/8'), - IPv4Network('127.0.0.0/8'), - IPv4Network('169.254.0.0/16'), - IPv4Network('172.16.0.0/12'), - IPv4Network('192.0.0.0/29'), - IPv4Network('192.0.0.170/31'), - IPv4Network('192.0.2.0/24'), - IPv4Network('192.168.0.0/16'), - IPv4Network('198.18.0.0/15'), - IPv4Network('198.51.100.0/24'), - IPv4Network('203.0.113.0/24'), - IPv4Network('240.0.0.0/4'), - IPv4Network('255.255.255.255/32'), - ] - - _reserved_network = IPv4Network('240.0.0.0/4') - - _unspecified_address = IPv4Address('0.0.0.0') - - -IPv4Address._constants = _IPv4Constants - - -class _BaseV6(object): - - """Base IPv6 object. - - The following methods are used by IPv6 objects in both single IP - addresses and networks. - - """ - - __slots__ = () - _version = 6 - _ALL_ONES = (2 ** IPV6LENGTH) - 1 - _HEXTET_COUNT = 8 - _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') - _max_prefixlen = IPV6LENGTH - - # There are only a bunch of valid v6 netmasks, so we cache them all - # when constructed (see _make_netmask()). - _netmask_cache = {} - - @classmethod - def _make_netmask(cls, arg): - """Make a (netmask, prefix_len) tuple from the given argument. - - Argument can be: - - an integer (the prefix length) - - a string representing the prefix length (e.g. "24") - - a string representing the prefix netmask (e.g. "255.255.255.0") - """ - if arg not in cls._netmask_cache: - if isinstance(arg, _compat_int_types): - prefixlen = arg - else: - prefixlen = cls._prefix_from_prefix_string(arg) - netmask = IPv6Address(cls._ip_int_from_prefix(prefixlen)) - cls._netmask_cache[arg] = netmask, prefixlen - return cls._netmask_cache[arg] - - @classmethod - def _ip_int_from_string(cls, ip_str): - """Turn an IPv6 ip_str into an integer. - - Args: - ip_str: A string, the IPv6 ip_str. - - Returns: - An int, the IPv6 address - - Raises: - AddressValueError: if ip_str isn't a valid IPv6 Address. - - """ - if not ip_str: - raise AddressValueError('Address cannot be empty') - - parts = ip_str.split(':') - - # An IPv6 address needs at least 2 colons (3 parts). - _min_parts = 3 - if len(parts) < _min_parts: - msg = "At least %d parts expected in %r" % (_min_parts, ip_str) - raise AddressValueError(msg) - - # If the address has an IPv4-style suffix, convert it to hexadecimal. - if '.' in parts[-1]: - try: - ipv4_int = IPv4Address(parts.pop())._ip - except AddressValueError as exc: - raise AddressValueError("%s in %r" % (exc, ip_str)) - parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF)) - parts.append('%x' % (ipv4_int & 0xFFFF)) - - # An IPv6 address can't have more than 8 colons (9 parts). - # The extra colon comes from using the "::" notation for a single - # leading or trailing zero part. - _max_parts = cls._HEXTET_COUNT + 1 - if len(parts) > _max_parts: - msg = "At most %d colons permitted in %r" % ( - _max_parts - 1, ip_str) - raise AddressValueError(msg) - - # Disregarding the endpoints, find '::' with nothing in between. - # This indicates that a run of zeroes has been skipped. - skip_index = None - for i in _compat_range(1, len(parts) - 1): - if not parts[i]: - if skip_index is not None: - # Can't have more than one '::' - msg = "At most one '::' permitted in %r" % ip_str - raise AddressValueError(msg) - skip_index = i - - # parts_hi is the number of parts to copy from above/before the '::' - # parts_lo is the number of parts to copy from below/after the '::' - if skip_index is not None: - # If we found a '::', then check if it also covers the endpoints. - parts_hi = skip_index - parts_lo = len(parts) - skip_index - 1 - if not parts[0]: - parts_hi -= 1 - if parts_hi: - msg = "Leading ':' only permitted as part of '::' in %r" - raise AddressValueError(msg % ip_str) # ^: requires ^:: - if not parts[-1]: - parts_lo -= 1 - if parts_lo: - msg = "Trailing ':' only permitted as part of '::' in %r" - raise AddressValueError(msg % ip_str) # :$ requires ::$ - parts_skipped = cls._HEXTET_COUNT - (parts_hi + parts_lo) - if parts_skipped < 1: - msg = "Expected at most %d other parts with '::' in %r" - raise AddressValueError(msg % (cls._HEXTET_COUNT - 1, ip_str)) - else: - # Otherwise, allocate the entire address to parts_hi. The - # endpoints could still be empty, but _parse_hextet() will check - # for that. - if len(parts) != cls._HEXTET_COUNT: - msg = "Exactly %d parts expected without '::' in %r" - raise AddressValueError(msg % (cls._HEXTET_COUNT, ip_str)) - if not parts[0]: - msg = "Leading ':' only permitted as part of '::' in %r" - raise AddressValueError(msg % ip_str) # ^: requires ^:: - if not parts[-1]: - msg = "Trailing ':' only permitted as part of '::' in %r" - raise AddressValueError(msg % ip_str) # :$ requires ::$ - parts_hi = len(parts) - parts_lo = 0 - parts_skipped = 0 - - try: - # Now, parse the hextets into a 128-bit integer. - ip_int = 0 - for i in range(parts_hi): - ip_int <<= 16 - ip_int |= cls._parse_hextet(parts[i]) - ip_int <<= 16 * parts_skipped - for i in range(-parts_lo, 0): - ip_int <<= 16 - ip_int |= cls._parse_hextet(parts[i]) - return ip_int - except ValueError as exc: - raise AddressValueError("%s in %r" % (exc, ip_str)) - - @classmethod - def _parse_hextet(cls, hextet_str): - """Convert an IPv6 hextet string into an integer. - - Args: - hextet_str: A string, the number to parse. - - Returns: - The hextet as an integer. - - Raises: - ValueError: if the input isn't strictly a hex number from - [0..FFFF]. - - """ - # Whitelist the characters, since int() allows a lot of bizarre stuff. - if not cls._HEX_DIGITS.issuperset(hextet_str): - raise ValueError("Only hex digits permitted in %r" % hextet_str) - # We do the length check second, since the invalid character error - # is likely to be more informative for the user - if len(hextet_str) > 4: - msg = "At most 4 characters permitted in %r" - raise ValueError(msg % hextet_str) - # Length check means we can skip checking the integer value - return int(hextet_str, 16) - - @classmethod - def _compress_hextets(cls, hextets): - """Compresses a list of hextets. - - Compresses a list of strings, replacing the longest continuous - sequence of "0" in the list with "" and adding empty strings at - the beginning or at the end of the string such that subsequently - calling ":".join(hextets) will produce the compressed version of - the IPv6 address. - - Args: - hextets: A list of strings, the hextets to compress. - - Returns: - A list of strings. - - """ - best_doublecolon_start = -1 - best_doublecolon_len = 0 - doublecolon_start = -1 - doublecolon_len = 0 - for index, hextet in enumerate(hextets): - if hextet == '0': - doublecolon_len += 1 - if doublecolon_start == -1: - # Start of a sequence of zeros. - doublecolon_start = index - if doublecolon_len > best_doublecolon_len: - # This is the longest sequence of zeros so far. - best_doublecolon_len = doublecolon_len - best_doublecolon_start = doublecolon_start - else: - doublecolon_len = 0 - doublecolon_start = -1 - - if best_doublecolon_len > 1: - best_doublecolon_end = (best_doublecolon_start + - best_doublecolon_len) - # For zeros at the end of the address. - if best_doublecolon_end == len(hextets): - hextets += [''] - hextets[best_doublecolon_start:best_doublecolon_end] = [''] - # For zeros at the beginning of the address. - if best_doublecolon_start == 0: - hextets = [''] + hextets - - return hextets - - @classmethod - def _string_from_ip_int(cls, ip_int=None): - """Turns a 128-bit integer into hexadecimal notation. - - Args: - ip_int: An integer, the IP address. - - Returns: - A string, the hexadecimal representation of the address. - - Raises: - ValueError: The address is bigger than 128 bits of all ones. - - """ - if ip_int is None: - ip_int = int(cls._ip) - - if ip_int > cls._ALL_ONES: - raise ValueError('IPv6 address is too large') - - hex_str = '%032x' % ip_int - hextets = ['%x' % int(hex_str[x:x + 4], 16) for x in range(0, 32, 4)] - - hextets = cls._compress_hextets(hextets) - return ':'.join(hextets) - - def _explode_shorthand_ip_string(self): - """Expand a shortened IPv6 address. - - Args: - ip_str: A string, the IPv6 address. - - Returns: - A string, the expanded IPv6 address. - - """ - if isinstance(self, IPv6Network): - ip_str = _compat_str(self.network_address) - elif isinstance(self, IPv6Interface): - ip_str = _compat_str(self.ip) - else: - ip_str = _compat_str(self) - - ip_int = self._ip_int_from_string(ip_str) - hex_str = '%032x' % ip_int - parts = [hex_str[x:x + 4] for x in range(0, 32, 4)] - if isinstance(self, (_BaseNetwork, IPv6Interface)): - return '%s/%d' % (':'.join(parts), self._prefixlen) - return ':'.join(parts) - - def _reverse_pointer(self): - """Return the reverse DNS pointer name for the IPv6 address. - - This implements the method described in RFC3596 2.5. - - """ - reverse_chars = self.exploded[::-1].replace(':', '') - return '.'.join(reverse_chars) + '.ip6.arpa' - - @property - def max_prefixlen(self): - return self._max_prefixlen - - @property - def version(self): - return self._version - - -class IPv6Address(_BaseV6, _BaseAddress): - - """Represent and manipulate single IPv6 Addresses.""" - - __slots__ = ('_ip', '__weakref__') - - def __init__(self, address): - """Instantiate a new IPv6 address object. - - Args: - address: A string or integer representing the IP - - Additionally, an integer can be passed, so - IPv6Address('2001:db8::') == - IPv6Address(42540766411282592856903984951653826560) - or, more generally - IPv6Address(int(IPv6Address('2001:db8::'))) == - IPv6Address('2001:db8::') - - Raises: - AddressValueError: If address isn't a valid IPv6 address. - - """ - # Efficient constructor from integer. - if isinstance(address, _compat_int_types): - self._check_int_address(address) - self._ip = address - return - - # Constructing from a packed address - if isinstance(address, bytes): - self._check_packed_address(address, 16) - bvs = _compat_bytes_to_byte_vals(address) - self._ip = _compat_int_from_byte_vals(bvs, 'big') - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP string. - addr_str = _compat_str(address) - if '/' in addr_str: - raise AddressValueError("Unexpected '/' in %r" % address) - self._ip = self._ip_int_from_string(addr_str) - - @property - def packed(self): - """The binary representation of this address.""" - return v6_int_to_packed(self._ip) - - @property - def is_multicast(self): - """Test if the address is reserved for multicast use. - - Returns: - A boolean, True if the address is a multicast address. - See RFC 2373 2.7 for details. - - """ - return self in self._constants._multicast_network - - @property - def is_reserved(self): - """Test if the address is otherwise IETF reserved. - - Returns: - A boolean, True if the address is within one of the - reserved IPv6 Network ranges. - - """ - return any(self in x for x in self._constants._reserved_networks) - - @property - def is_link_local(self): - """Test if the address is reserved for link-local. - - Returns: - A boolean, True if the address is reserved per RFC 4291. - - """ - return self in self._constants._linklocal_network - - @property - def is_site_local(self): - """Test if the address is reserved for site-local. - - Note that the site-local address space has been deprecated by RFC 3879. - Use is_private to test if this address is in the space of unique local - addresses as defined by RFC 4193. - - Returns: - A boolean, True if the address is reserved per RFC 3513 2.5.6. - - """ - return self in self._constants._sitelocal_network - - @property - def is_private(self): - """Test if this address is allocated for private networks. - - Returns: - A boolean, True if the address is reserved per - iana-ipv6-special-registry. - - """ - return any(self in net for net in self._constants._private_networks) - - @property - def is_global(self): - """Test if this address is allocated for public networks. - - Returns: - A boolean, true if the address is not reserved per - iana-ipv6-special-registry. - - """ - return not self.is_private - - @property - def is_unspecified(self): - """Test if the address is unspecified. - - Returns: - A boolean, True if this is the unspecified address as defined in - RFC 2373 2.5.2. - - """ - return self._ip == 0 - - @property - def is_loopback(self): - """Test if the address is a loopback address. - - Returns: - A boolean, True if the address is a loopback address as defined in - RFC 2373 2.5.3. - - """ - return self._ip == 1 - - @property - def ipv4_mapped(self): - """Return the IPv4 mapped address. - - Returns: - If the IPv6 address is a v4 mapped address, return the - IPv4 mapped address. Return None otherwise. - - """ - if (self._ip >> 32) != 0xFFFF: - return None - return IPv4Address(self._ip & 0xFFFFFFFF) - - @property - def teredo(self): - """Tuple of embedded teredo IPs. - - Returns: - Tuple of the (server, client) IPs or None if the address - doesn't appear to be a teredo address (doesn't start with - 2001::/32) - - """ - if (self._ip >> 96) != 0x20010000: - return None - return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF), - IPv4Address(~self._ip & 0xFFFFFFFF)) - - @property - def sixtofour(self): - """Return the IPv4 6to4 embedded address. - - Returns: - The IPv4 6to4-embedded address if present or None if the - address doesn't appear to contain a 6to4 embedded address. - - """ - if (self._ip >> 112) != 0x2002: - return None - return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) - - -class IPv6Interface(IPv6Address): - - def __init__(self, address): - if isinstance(address, (bytes, _compat_int_types)): - IPv6Address.__init__(self, address) - self.network = IPv6Network(self._ip) - self._prefixlen = self._max_prefixlen - return - if isinstance(address, tuple): - IPv6Address.__init__(self, address[0]) - if len(address) > 1: - self._prefixlen = int(address[1]) - else: - self._prefixlen = self._max_prefixlen - self.network = IPv6Network(address, strict=False) - self.netmask = self.network.netmask - self.hostmask = self.network.hostmask - return - - addr = _split_optional_netmask(address) - IPv6Address.__init__(self, addr[0]) - self.network = IPv6Network(address, strict=False) - self.netmask = self.network.netmask - self._prefixlen = self.network._prefixlen - self.hostmask = self.network.hostmask - - def __str__(self): - return '%s/%d' % (self._string_from_ip_int(self._ip), - self.network.prefixlen) - - def __eq__(self, other): - address_equal = IPv6Address.__eq__(self, other) - if not address_equal or address_equal is NotImplemented: - return address_equal - try: - return self.network == other.network - except AttributeError: - # An interface with an associated network is NOT the - # same as an unassociated address. That's why the hash - # takes the extra info into account. - return False - - def __lt__(self, other): - address_less = IPv6Address.__lt__(self, other) - if address_less is NotImplemented: - return NotImplemented - try: - return (self.network < other.network or - self.network == other.network and address_less) - except AttributeError: - # We *do* allow addresses and interfaces to be sorted. The - # unassociated address is considered less than all interfaces. - return False - - def __hash__(self): - return self._ip ^ self._prefixlen ^ int(self.network.network_address) - - __reduce__ = _IPAddressBase.__reduce__ - - @property - def ip(self): - return IPv6Address(self._ip) - - @property - def with_prefixlen(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self._prefixlen) - - @property - def with_netmask(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self.netmask) - - @property - def with_hostmask(self): - return '%s/%s' % (self._string_from_ip_int(self._ip), - self.hostmask) - - @property - def is_unspecified(self): - return self._ip == 0 and self.network.is_unspecified - - @property - def is_loopback(self): - return self._ip == 1 and self.network.is_loopback - - -class IPv6Network(_BaseV6, _BaseNetwork): - - """This class represents and manipulates 128-bit IPv6 networks. - - Attributes: [examples for IPv6('2001:db8::1000/124')] - .network_address: IPv6Address('2001:db8::1000') - .hostmask: IPv6Address('::f') - .broadcast_address: IPv6Address('2001:db8::100f') - .netmask: IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0') - .prefixlen: 124 - - """ - - # Class to use when creating address objects - _address_class = IPv6Address - - def __init__(self, address, strict=True): - """Instantiate a new IPv6 Network object. - - Args: - address: A string or integer representing the IPv6 network or the - IP and prefix/netmask. - '2001:db8::/128' - '2001:db8:0000:0000:0000:0000:0000:0000/128' - '2001:db8::' - are all functionally the same in IPv6. That is to say, - failing to provide a subnetmask will create an object with - a mask of /128. - - Additionally, an integer can be passed, so - IPv6Network('2001:db8::') == - IPv6Network(42540766411282592856903984951653826560) - or, more generally - IPv6Network(int(IPv6Network('2001:db8::'))) == - IPv6Network('2001:db8::') - - strict: A boolean. If true, ensure that we have been passed - A true network address, eg, 2001:db8::1000/124 and not an - IP address on a network, eg, 2001:db8::1/124. - - Raises: - AddressValueError: If address isn't a valid IPv6 address. - NetmaskValueError: If the netmask isn't valid for - an IPv6 address. - ValueError: If strict was True and a network address was not - supplied. - - """ - _BaseNetwork.__init__(self, address) - - # Efficient constructor from integer or packed address - if isinstance(address, (bytes, _compat_int_types)): - self.network_address = IPv6Address(address) - self.netmask, self._prefixlen = self._make_netmask( - self._max_prefixlen) - return - - if isinstance(address, tuple): - if len(address) > 1: - arg = address[1] - else: - arg = self._max_prefixlen - self.netmask, self._prefixlen = self._make_netmask(arg) - self.network_address = IPv6Address(address[0]) - packed = int(self.network_address) - if packed & int(self.netmask) != packed: - if strict: - raise ValueError('%s has host bits set' % self) - else: - self.network_address = IPv6Address(packed & - int(self.netmask)) - return - - # Assume input argument to be string or any object representation - # which converts into a formatted IP prefix string. - addr = _split_optional_netmask(address) - - self.network_address = IPv6Address(self._ip_int_from_string(addr[0])) - - if len(addr) == 2: - arg = addr[1] - else: - arg = self._max_prefixlen - self.netmask, self._prefixlen = self._make_netmask(arg) - - if strict: - if (IPv6Address(int(self.network_address) & int(self.netmask)) != - self.network_address): - raise ValueError('%s has host bits set' % self) - self.network_address = IPv6Address(int(self.network_address) & - int(self.netmask)) - - if self._prefixlen == (self._max_prefixlen - 1): - self.hosts = self.__iter__ - - def hosts(self): - """Generate Iterator over usable hosts in a network. - - This is like __iter__ except it doesn't return the - Subnet-Router anycast address. - - """ - network = int(self.network_address) - broadcast = int(self.broadcast_address) - for x in _compat_range(network + 1, broadcast + 1): - yield self._address_class(x) - - @property - def is_site_local(self): - """Test if the address is reserved for site-local. - - Note that the site-local address space has been deprecated by RFC 3879. - Use is_private to test if this address is in the space of unique local - addresses as defined by RFC 4193. - - Returns: - A boolean, True if the address is reserved per RFC 3513 2.5.6. - - """ - return (self.network_address.is_site_local and - self.broadcast_address.is_site_local) - - -class _IPv6Constants(object): - - _linklocal_network = IPv6Network('fe80::/10') - - _multicast_network = IPv6Network('ff00::/8') - - _private_networks = [ - IPv6Network('::1/128'), - IPv6Network('::/128'), - IPv6Network('::ffff:0:0/96'), - IPv6Network('100::/64'), - IPv6Network('2001::/23'), - IPv6Network('2001:2::/48'), - IPv6Network('2001:db8::/32'), - IPv6Network('2001:10::/28'), - IPv6Network('fc00::/7'), - IPv6Network('fe80::/10'), - ] - - _reserved_networks = [ - IPv6Network('::/8'), IPv6Network('100::/8'), - IPv6Network('200::/7'), IPv6Network('400::/6'), - IPv6Network('800::/5'), IPv6Network('1000::/4'), - IPv6Network('4000::/3'), IPv6Network('6000::/3'), - IPv6Network('8000::/3'), IPv6Network('A000::/3'), - IPv6Network('C000::/3'), IPv6Network('E000::/4'), - IPv6Network('F000::/5'), IPv6Network('F800::/6'), - IPv6Network('FE00::/9'), - ] - - _sitelocal_network = IPv6Network('fec0::/10') - - -IPv6Address._constants = _IPv6Constants From 796d09b4103c6ecbf8f4b10f97fbdd84e8c46552 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:17:39 +0100 Subject: [PATCH 14/42] Remove six leafovers --- readthedocs/core/utils/extend.py | 2 +- readthedocs/rtd_tests/tests/test_views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/readthedocs/core/utils/extend.py b/readthedocs/core/utils/extend.py index c01c76eee86..ca8e3f40b2e 100644 --- a/readthedocs/core/utils/extend.py +++ b/readthedocs/core/utils/extend.py @@ -39,7 +39,7 @@ def __getattr__(cls, attr): # noqa: pep8 false positive return getattr(proxy_class, attr) -class SettingsOverrideObject(six.with_metaclass(SettingsOverrideMeta, object)): +class SettingsOverrideObject(metaclass=SettingsOverrideMeta): """ Base class for creating class that can be overridden. diff --git a/readthedocs/rtd_tests/tests/test_views.py b/readthedocs/rtd_tests/tests/test_views.py index dd64a438a8e..56b669979f4 100644 --- a/readthedocs/rtd_tests/tests/test_views.py +++ b/readthedocs/rtd_tests/tests/test_views.py @@ -3,8 +3,8 @@ from django.contrib.auth.models import User from django.test import TestCase from django.urls import reverse -from django.utils.six.moves.urllib.parse import urlsplit from django_dynamic_fixture import get, new +from urllib.parse import urlsplit from readthedocs.builds.constants import LATEST from readthedocs.builds.models import Build From c2a662cad6dfd3e57078cbdf59e025eb01250a82 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:20:11 +0100 Subject: [PATCH 15/42] Bug fix on types checking --- readthedocs/config/validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs/config/validation.py b/readthedocs/config/validation.py index a69b8ed7769..c366b05073a 100644 --- a/readthedocs/config/validation.py +++ b/readthedocs/config/validation.py @@ -43,7 +43,7 @@ def __init__(self, value, code, format_kwargs=None): def validate_list(value): """Check if ``value`` is an iterable.""" - if isinstance(value, (dict, (str,))): + if isinstance(value, (dict, str)): raise ValidationError(value, INVALID_LIST) if not hasattr(value, '__iter__'): raise ValidationError(value, INVALID_LIST) From 686c8201d7bac90afadecb4860e17971be9ba3fa Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:28:59 +0100 Subject: [PATCH 16/42] Import missing modules removed by mistake --- readthedocs/config/tests/test_find.py | 2 ++ readthedocs/rtd_tests/tests/test_post_commit_hooks.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/readthedocs/config/tests/test_find.py b/readthedocs/config/tests/test_find.py index 048a6c9b90c..4ebcb783cde 100644 --- a/readthedocs/config/tests/test_find.py +++ b/readthedocs/config/tests/test_find.py @@ -1,3 +1,5 @@ +import os + from readthedocs.config.find import find_one from .utils import apply_fs diff --git a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py index 6b4a6db070b..f9b310a6207 100644 --- a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py +++ b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py @@ -4,7 +4,7 @@ import mock from django.test import TestCase from django_dynamic_fixture import get -from urllib.parse import urlparse +from urllib.parse import urlparse, urlencode from readthedocs.builds.models import Version from readthedocs.projects.models import Feature, Project From 25eb8c480cdd24e340ba538acf252f1292cd6cd4 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:29:11 +0100 Subject: [PATCH 17/42] Keep __future__ in conf.py because we build with Py2 also --- readthedocs/rtd_tests/files/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/readthedocs/rtd_tests/files/conf.py b/readthedocs/rtd_tests/files/conf.py index 7bdfdbe06dc..331f306020d 100644 --- a/readthedocs/rtd_tests/files/conf.py +++ b/readthedocs/rtd_tests/files/conf.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, unicode_literals from datetime import datetime from recommonmark.parser import CommonMarkParser From 06b07cc6ffd3aa64ec84442e0821f4a9425b5ac4 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:31:00 +0100 Subject: [PATCH 18/42] Make our generated conf.py compatible with Py2 and Py3 --- readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl | 7 ++++--- readthedocs/rtd_tests/files/conf.py | 3 ++- readthedocs/templates/sphinx/conf.py.conf | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl b/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl index e36cdd16af5..0ec2b9c331b 100644 --- a/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl +++ b/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl @@ -13,6 +13,7 @@ # https://github.com/rtfd/readthedocs.org/blob/master/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl # +from __future__ import absolute_import, division, print_function, unicode_literals import importlib import sys @@ -81,9 +82,9 @@ context = { ("{{ slug }}", "{{ url }}"),{% endfor %} ], 'slug': '{{ project.slug }}', - 'name': u'{{ project.name }}', - 'rtd_language': u'{{ project.language }}', - 'programming_language': u'{{ project.programming_language }}', + 'name': '{{ project.name }}', + 'rtd_language': '{{ project.language }}', + 'programming_language': '{{ project.programming_language }}', 'canonical_url': '{{ project.get_canonical_url }}', 'analytics_code': '{{ project.analytics_code }}', 'single_version': {{ project.single_version }}, diff --git a/readthedocs/rtd_tests/files/conf.py b/readthedocs/rtd_tests/files/conf.py index 331f306020d..11f872849dd 100644 --- a/readthedocs/rtd_tests/files/conf.py +++ b/readthedocs/rtd_tests/files/conf.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import division, print_function, unicode_literals + from datetime import datetime from recommonmark.parser import CommonMarkParser @@ -12,7 +13,7 @@ '.md': CommonMarkParser, } master_doc = 'index' -project = u'Pip' +project = 'Pip' copyright = str(datetime.now().year) version = '0.8.1' release = '0.8.1' diff --git a/readthedocs/templates/sphinx/conf.py.conf b/readthedocs/templates/sphinx/conf.py.conf index 43e7b58b290..520670cca16 100644 --- a/readthedocs/templates/sphinx/conf.py.conf +++ b/readthedocs/templates/sphinx/conf.py.conf @@ -13,7 +13,7 @@ source_parsers = { '.md': CommonMarkParser, } master_doc = 'index' -project = u'{{ project.name }}' +project = '{{ project.name }}' copyright = str(datetime.now().year) version = '{{ version.verbose_name }}' release = '{{ version.verbose_name }}' @@ -23,6 +23,6 @@ htmlhelp_basename = '{{ project.slug }}' html_theme = 'sphinx_rtd_theme' file_insertion_enabled = False latex_documents = [ - ('index', '{{ project.slug }}.tex', u'{{ project.name }} Documentation', - u'{{ project.copyright }}', 'manual'), + ('index', '{{ project.slug }}.tex', '{{ project.name }} Documentation', + '{{ project.copyright }}', 'manual'), ] From e066c0d983775bb9be87fa0c10c68e27c2265ef1 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:43:13 +0100 Subject: [PATCH 19/42] Import missing modules --- readthedocs/core/templatetags/core_tags.py | 2 +- readthedocs/vcs_support/backends/bzr.py | 1 + readthedocs/vcs_support/backends/svn.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/readthedocs/core/templatetags/core_tags.py b/readthedocs/core/templatetags/core_tags.py index e1b9e6f7903..304ca3aff3a 100644 --- a/readthedocs/core/templatetags/core_tags.py +++ b/readthedocs/core/templatetags/core_tags.py @@ -1,7 +1,7 @@ """Template tags for core app.""" import hashlib -from urllib.parse import urlparse +from urllib.parse import urlparse, urlencode from django import template from django.conf import settings diff --git a/readthedocs/vcs_support/backends/bzr.py b/readthedocs/vcs_support/backends/bzr.py index 9c97870408c..ebfb5574f08 100644 --- a/readthedocs/vcs_support/backends/bzr.py +++ b/readthedocs/vcs_support/backends/bzr.py @@ -3,6 +3,7 @@ import csv import re +from io import StringIO from readthedocs.projects.exceptions import RepositoryError from readthedocs.vcs_support.base import BaseVCS, VCSVersion diff --git a/readthedocs/vcs_support/backends/svn.py b/readthedocs/vcs_support/backends/svn.py index b4ad6130302..d591dc880cc 100644 --- a/readthedocs/vcs_support/backends/svn.py +++ b/readthedocs/vcs_support/backends/svn.py @@ -2,6 +2,7 @@ """Subversion-related utilities.""" import csv +from io import StringIO from readthedocs.projects.exceptions import RepositoryError from readthedocs.vcs_support.base import BaseVCS, VCSVersion From d606c25d10b7b8979e4b0bc3da6d5963ed93968e Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:45:29 +0100 Subject: [PATCH 20/42] Upgrade common submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index a6cb6bbafb3..890625b9bfc 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a6cb6bbafb3cf93bfad7bab98c6636021719cc48 +Subproject commit 890625b9bfca1981a3f68a9fb7d168bba714421d From 3219f7acbdde68e889c828920d0b181bae93277e Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 21:51:36 +0100 Subject: [PATCH 21/42] Run pre-commit on all the files $ pre-commit run --files `find readthedocs -name '*'` --- readthedocs/__init__.py | 1 + readthedocs/analytics/__init__.py | 5 +- readthedocs/analytics/apps.py | 3 +- readthedocs/analytics/tasks.py | 36 +- readthedocs/analytics/utils.py | 21 +- readthedocs/api/base.py | 60 +- readthedocs/api/utils.py | 19 +- readthedocs/builds/admin.py | 14 +- readthedocs/builds/constants.py | 1 + readthedocs/builds/forms.py | 5 +- readthedocs/builds/managers.py | 5 +- readthedocs/builds/models.py | 145 +- readthedocs/builds/querysets.py | 14 +- readthedocs/builds/signals.py | 3 +- readthedocs/builds/syncers.py | 53 +- readthedocs/builds/urls.py | 1 + readthedocs/builds/utils.py | 1 + readthedocs/builds/version_slug.py | 14 +- readthedocs/builds/views.py | 19 +- readthedocs/config/__init__.py | 1 + readthedocs/config/config.py | 82 +- readthedocs/config/find.py | 1 + readthedocs/config/models.py | 1 + readthedocs/config/parser.py | 3 +- readthedocs/config/validation.py | 13 +- readthedocs/constants.py | 3 +- readthedocs/core/__init__.py | 1 + readthedocs/core/adapters.py | 18 +- readthedocs/core/admin.py | 11 +- readthedocs/core/apps.py | 1 + readthedocs/core/backends.py | 8 +- readthedocs/core/context_processors.py | 9 +- readthedocs/core/fields.py | 5 +- readthedocs/core/fixtures/flag_types.json | 28 +- readthedocs/core/forms.py | 2 + .../core/management/commands/archive.py | 10 +- .../core/management/commands/clean_builds.py | 20 +- .../core/management/commands/import_github.py | 8 +- .../commands/import_github_language.py | 17 +- .../commands/provision_elasticsearch.py | 11 +- readthedocs/core/management/commands/pull.py | 3 +- .../commands/reindex_elasticsearch.py | 22 +- .../core/management/commands/set_metadata.py | 9 +- .../core/management/commands/symlink.py | 6 +- .../core/management/commands/update_api.py | 3 +- .../management/commands/update_versions.py | 5 +- readthedocs/core/middleware.py | 83 +- readthedocs/core/mixins.py | 5 +- readthedocs/core/models.py | 13 +- readthedocs/core/permissions.py | 3 +- readthedocs/core/resolver.py | 67 +- readthedocs/core/signals.py | 10 +- .../static/core/font/fontawesome-webfont.svg | 1418 ++++++++--------- readthedocs/core/symlink.py | 110 +- readthedocs/core/tasks.py | 24 +- readthedocs/core/templatetags/core_tags.py | 31 +- readthedocs/core/templatetags/privacy_tags.py | 5 +- readthedocs/core/urls/__init__.py | 81 +- readthedocs/core/urls/single_version.py | 35 +- readthedocs/core/urls/subdomain.py | 35 +- readthedocs/core/utils/__init__.py | 1 - readthedocs/core/utils/extend.py | 6 +- readthedocs/core/utils/tasks/__init__.py | 3 +- .../core/utils/tasks/permission_checks.py | 3 +- readthedocs/core/utils/tasks/public.py | 18 +- readthedocs/core/utils/tasks/retrieve.py | 4 +- readthedocs/core/views/__init__.py | 22 +- readthedocs/core/views/hooks.py | 78 +- readthedocs/core/views/serve.py | 32 +- readthedocs/doc_builder/backends/mkdocs.py | 46 +- readthedocs/doc_builder/backends/sphinx.py | 86 +- readthedocs/doc_builder/base.py | 15 +- readthedocs/doc_builder/config.py | 5 +- readthedocs/doc_builder/constants.py | 5 +- readthedocs/doc_builder/environments.py | 136 +- readthedocs/doc_builder/exceptions.py | 5 +- readthedocs/doc_builder/loader.py | 17 +- .../doc_builder/python_environments.py | 61 +- readthedocs/doc_builder/signals.py | 5 +- .../templates/doc_builder/data.js.tmpl | 2 +- readthedocs/gold/__init__.py | 1 + readthedocs/gold/admin.py | 1 + readthedocs/gold/apps.py | 1 + readthedocs/gold/forms.py | 20 +- readthedocs/gold/signals.py | 5 +- readthedocs/gold/templates/gold/projects.html | 1 - .../templates/gold/subscription_form.html | 2 +- readthedocs/gold/urls.py | 25 +- readthedocs/gold/views.py | 21 +- readthedocs/integrations/admin.py | 37 +- readthedocs/integrations/models.py | 3 +- readthedocs/integrations/utils.py | 1 + readthedocs/notifications/__init__.py | 4 +- readthedocs/notifications/apps.py | 1 + readthedocs/notifications/backends.py | 1 + readthedocs/notifications/constants.py | 4 +- readthedocs/notifications/forms.py | 5 +- readthedocs/notifications/notification.py | 20 +- readthedocs/notifications/storages.py | 36 +- readthedocs/notifications/urls.py | 17 +- readthedocs/notifications/views.py | 37 +- readthedocs/oauth/__init__.py | 1 + readthedocs/oauth/admin.py | 1 + readthedocs/oauth/apps.py | 3 +- readthedocs/oauth/models.py | 45 +- readthedocs/oauth/notifications.py | 11 +- readthedocs/oauth/querysets.py | 5 +- readthedocs/oauth/services/__init__.py | 7 +- readthedocs/oauth/services/base.py | 4 +- readthedocs/oauth/services/bitbucket.py | 76 +- readthedocs/oauth/services/github.py | 64 +- readthedocs/oauth/services/gitlab.py | 18 +- readthedocs/oauth/tasks.py | 1 + readthedocs/oauth/utils.py | 5 +- readthedocs/payments/forms.py | 89 +- readthedocs/payments/mixins.py | 1 + readthedocs/payments/utils.py | 1 + readthedocs/profiles/urls/private.py | 6 +- readthedocs/profiles/urls/public.py | 7 +- readthedocs/profiles/views.py | 24 +- readthedocs/projects/__init__.py | 1 + readthedocs/projects/admin.py | 49 +- readthedocs/projects/apps.py | 3 +- readthedocs/projects/backends/views.py | 3 +- readthedocs/projects/constants.py | 10 +- readthedocs/projects/exceptions.py | 17 +- readthedocs/projects/feeds.py | 19 +- readthedocs/projects/fixtures/test_auth.json | 2 +- readthedocs/projects/forms.py | 141 +- .../commands/import_project_from_live.py | 16 +- readthedocs/projects/models.py | 578 +++++-- readthedocs/projects/notifications.py | 3 +- readthedocs/projects/querysets.py | 14 +- readthedocs/projects/signals.py | 15 +- readthedocs/projects/tasks.py | 232 +-- .../projects/templatetags/projects_tags.py | 10 +- readthedocs/projects/urls/private.py | 271 +++- readthedocs/projects/urls/public.py | 94 +- readthedocs/projects/utils.py | 1 + readthedocs/projects/validators.py | 3 +- readthedocs/projects/version_handling.py | 4 +- readthedocs/projects/views/base.py | 4 +- readthedocs/projects/views/mixins.py | 3 +- readthedocs/projects/views/public.py | 99 +- readthedocs/redirects/admin.py | 1 + readthedocs/redirects/managers.py | 7 +- readthedocs/redirects/models.py | 103 +- readthedocs/redirects/utils.py | 12 +- readthedocs/restapi/client.py | 2 +- readthedocs/restapi/permissions.py | 6 +- readthedocs/restapi/serializers.py | 25 +- readthedocs/restapi/signals.py | 3 +- readthedocs/restapi/urls.py | 20 +- readthedocs/restapi/utils.py | 53 +- readthedocs/restapi/views/core_views.py | 15 +- readthedocs/restapi/views/footer_views.py | 18 +- readthedocs/restapi/views/integrations.py | 40 +- readthedocs/restapi/views/model_views.py | 61 +- readthedocs/restapi/views/search_views.py | 59 +- readthedocs/restapi/views/task_views.py | 22 +- readthedocs/search/indexes.py | 91 +- readthedocs/search/lib.py | 260 +-- readthedocs/search/parse_json.py | 25 +- readthedocs/search/signals.py | 7 +- readthedocs/search/utils.py | 45 +- readthedocs/search/views.py | 17 +- .../email/email_confirmation_message.html | 1 - readthedocs/templates/core/badge_markup.html | 2 +- .../templates/core/build_list_detailed.html | 1 - readthedocs/templates/core/project_list.html | 1 - .../templates/core/project_list_detailed.html | 2 +- readthedocs/templates/core/widesearchbar.html | 2 +- readthedocs/templates/dnt-policy.txt | 50 +- .../templates/flagging/flag_confirm.html | 2 +- .../templates/flagging/flag_success.html | 2 +- .../projects/domain_confirm_delete.html | 2 - .../templates/projects/domain_form.html | 1 - .../templates/projects/domain_list.html | 3 +- .../templates/projects/project_analytics.html | 4 +- .../projects/project_version_list.html | 2 +- .../templates/search/elastic_search.html | 8 +- readthedocs/templates/style_catalog.html | 12 +- readthedocs/urls.py | 40 +- readthedocs/vcs_support/backends/__init__.py | 1 + readthedocs/vcs_support/backends/bzr.py | 1 + readthedocs/vcs_support/backends/git.py | 13 +- readthedocs/vcs_support/backends/hg.py | 7 +- readthedocs/vcs_support/backends/svn.py | 30 +- readthedocs/vcs_support/base.py | 5 +- readthedocs/vcs_support/utils.py | 54 +- readthedocs/worker.py | 3 +- readthedocs/wsgi.py | 5 +- 192 files changed, 4005 insertions(+), 2536 deletions(-) diff --git a/readthedocs/__init__.py b/readthedocs/__init__.py index 1c8afb1a02b..8f8c9ee7c80 100644 --- a/readthedocs/__init__.py +++ b/readthedocs/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Read the Docs.""" import os.path diff --git a/readthedocs/analytics/__init__.py b/readthedocs/analytics/__init__.py index f2531d76edb..b8f7ef2906d 100644 --- a/readthedocs/analytics/__init__.py +++ b/readthedocs/analytics/__init__.py @@ -1,3 +1,4 @@ -"""App init""" +# -*- coding: utf-8 -*- +"""App init.""" -default_app_config = 'readthedocs.analytics.apps.AnalyticsAppConfig' # noqa +default_app_config = 'readthedocs.analytics.apps.AnalyticsAppConfig' # noqa diff --git a/readthedocs/analytics/apps.py b/readthedocs/analytics/apps.py index 219a787dce7..3bfa8a3559d 100644 --- a/readthedocs/analytics/apps.py +++ b/readthedocs/analytics/apps.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Django app config for the analytics app.""" from django.apps import AppConfig @@ -5,7 +6,7 @@ class AnalyticsAppConfig(AppConfig): - """Analytics app init code""" + """Analytics app init code.""" name = 'readthedocs.analytics' verbose_name = 'Analytics' diff --git a/readthedocs/analytics/tasks.py b/readthedocs/analytics/tasks.py index b79102fc6fb..02b0a7524ef 100644 --- a/readthedocs/analytics/tasks.py +++ b/readthedocs/analytics/tasks.py @@ -1,4 +1,5 @@ -"""Tasks for Read the Docs' analytics""" +# -*- coding: utf-8 -*- +"""Tasks for Read the Docs' analytics.""" from django.conf import settings @@ -9,24 +10,24 @@ DEFAULT_PARAMETERS = { - 'v': '1', # analytics version (always 1) - 'aip': '1', # anonymize IP + 'v': '1', # analytics version (always 1) + 'aip': '1', # anonymize IP 'tid': settings.GLOBAL_ANALYTICS_CODE, # User data - 'uip': None, # User IP address - 'ua': None, # User agent + 'uip': None, # User IP address + 'ua': None, # User agent # Application info 'an': 'Read the Docs', - 'av': readthedocs.__version__, # App version + 'av': readthedocs.__version__, # App version } @app.task(queue='web') def analytics_pageview(url, title=None, **kwargs): """ - Send a pageview to Google Analytics + Send a pageview to Google Analytics. :see: https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters :param url: the URL of the pageview @@ -35,8 +36,8 @@ def analytics_pageview(url, title=None, **kwargs): """ data = { 't': 'pageview', - 'dl': url, # URL of the pageview (required) - 'dt': title, # Title of the page + 'dl': url, # URL of the pageview (required) + 'dt': title, # Title of the page } data.update(DEFAULT_PARAMETERS) data.update(kwargs) @@ -44,9 +45,12 @@ def analytics_pageview(url, title=None, **kwargs): @app.task(queue='web') -def analytics_event(event_category, event_action, event_label=None, event_value=None, **kwargs): +def analytics_event( + event_category, event_action, event_label=None, event_value=None, + **kwargs +): """ - Send an analytics event to Google Analytics + Send an analytics event to Google Analytics. :see: https://developers.google.com/analytics/devguides/collection/protocol/v1/devguide#event :param event_category: the category of the event @@ -56,11 +60,11 @@ def analytics_event(event_category, event_action, event_label=None, event_value= :param kwargs: extra event parameters to send to GA """ data = { - 't': 'event', # GA event - don't change - 'ec': event_category, # Event category (required) - 'ea': event_action, # Event action (required) - 'el': event_label, # Event label - 'ev': event_value, # Event value (numeric) + 't': 'event', # GA event - don't change + 'ec': event_category, # Event category (required) + 'ea': event_action, # Event action (required) + 'el': event_label, # Event label + 'ev': event_value, # Event value (numeric) } data.update(DEFAULT_PARAMETERS) data.update(kwargs) diff --git a/readthedocs/analytics/utils.py b/readthedocs/analytics/utils.py index 5c74cfd1eae..8c06e6001fd 100644 --- a/readthedocs/analytics/utils.py +++ b/readthedocs/analytics/utils.py @@ -1,4 +1,5 @@ -"""Utilities related to analytics""" +# -*- coding: utf-8 -*- +"""Utilities related to analytics.""" import hashlib import ipaddress @@ -11,11 +12,11 @@ from user_agents import parse -log = logging.getLogger(__name__) # noqa +log = logging.getLogger(__name__) # noqa def get_client_ip(request): - """Gets the real IP based on a request object""" + """Gets the real IP based on a request object.""" ip_address = request.META.get('REMOTE_ADDR') # Get the original IP address (eg. "X-Forwarded-For: client, proxy1, proxy2") @@ -27,7 +28,7 @@ def get_client_ip(request): def anonymize_ip_address(ip_address): - """Anonymizes an IP address by zeroing the last 2 bytes""" + """Anonymizes an IP address by zeroing the last 2 bytes.""" # Used to anonymize an IP by zero-ing out the last 2 bytes ip_mask = int('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000', 16) @@ -41,7 +42,7 @@ def anonymize_ip_address(ip_address): def anonymize_user_agent(user_agent): - """Anonymizes rare user agents""" + """Anonymizes rare user agents.""" # If the browser family is not recognized, this is a rare user agent parsed_ua = parse(user_agent) if parsed_ua.browser.family == 'Other' or parsed_ua.os.family == 'Other': @@ -51,7 +52,7 @@ def anonymize_user_agent(user_agent): def send_to_analytics(data): - """Sends data to Google Analytics""" + """Sends data to Google Analytics.""" if data.get('uip') and data.get('ua'): data['cid'] = generate_client_id(data['uip'], data['ua']) @@ -69,7 +70,7 @@ def send_to_analytics(data): resp = requests.post( 'https://www.google-analytics.com/collect', data=data, - timeout=3, # seconds + timeout=3, # seconds ) except requests.Timeout: log.warning('Timeout sending to Google Analytics') @@ -80,10 +81,10 @@ def send_to_analytics(data): def generate_client_id(ip_address, user_agent): """ - Create an advertising ID + Create an advertising ID. - This simplifies things but essentially if a user has the same IP and same UA, - this will treat them as the same user for analytics purposes + This simplifies things but essentially if a user has the same IP and same + UA, this will treat them as the same user for analytics purposes """ salt = b'advertising-client-id' diff --git a/readthedocs/api/base.py b/readthedocs/api/base.py index 75e984d422e..a70fb024599 100644 --- a/readthedocs/api/base.py +++ b/readthedocs/api/base.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """API resources.""" import logging @@ -69,7 +70,9 @@ def post_list(self, request, **kwargs): # Force this in an ugly way, at least should do "reverse" deserialized['users'] = ['/api/v1/user/%s/' % request.user.id] bundle = self.build_bundle( - data=dict_strip_unicode_keys(deserialized), request=request) + data=dict_strip_unicode_keys(deserialized), + request=request, + ) self.is_valid(bundle) updated_bundle = self.obj_create(bundle, request=request) return HttpCreated(location=self.get_resource_uri(updated_bundle)) @@ -78,14 +81,20 @@ def prepend_urls(self): return [ url( r'^(?P%s)/schema/$' % self._meta.resource_name, - self.wrap_view('get_schema'), name='api_get_schema'), + self.wrap_view('get_schema'), + name='api_get_schema', + ), url( r'^(?P%s)/search%s$' % (self._meta.resource_name, trailing_slash()), - self.wrap_view('get_search'), name='api_get_search'), - url((r'^(?P%s)/(?P[a-z-_]+)/$') % - self._meta.resource_name, self.wrap_view('dispatch_detail'), - name='api_dispatch_detail'), + self.wrap_view('get_search'), + name='api_get_search', + ), + url( + (r'^(?P%s)/(?P[a-z-_]+)/$') % self._meta.resource_name, + self.wrap_view('dispatch_detail'), + name='api_dispatch_detail', + ), ] @@ -122,17 +131,23 @@ def prepend_urls(self): return [ url( r'^(?P%s)/schema/$' % self._meta.resource_name, - self.wrap_view('get_schema'), name='api_get_schema'), + self.wrap_view('get_schema'), + name='api_get_schema', + ), url( r'^(?P%s)/(?P[a-z-_]+[a-z0-9-_]+)/$' # noqa % self._meta.resource_name, self.wrap_view('dispatch_list'), - name='api_version_list'), - url(( - r'^(?P%s)/(?P[a-z-_]+[a-z0-9-_]+)/(?P' - r'[a-z0-9-_.]+)/build/$') % - self._meta.resource_name, self.wrap_view('build_version'), - name='api_version_build_slug'), + name='api_version_list', + ), + url( + ( + r'^(?P%s)/(?P[a-z-_]+[a-z0-9-_]+)/(?P' + r'[a-z0-9-_.]+)/build/$' + ) % self._meta.resource_name, + self.wrap_view('build_version'), + name='api_version_build_slug', + ), ] @@ -154,11 +169,15 @@ def prepend_urls(self): return [ url( r'^(?P%s)/schema/$' % self._meta.resource_name, - self.wrap_view('get_schema'), name='api_get_schema'), + self.wrap_view('get_schema'), + name='api_get_schema', + ), url( r'^(?P%s)/anchor%s$' % (self._meta.resource_name, trailing_slash()), - self.wrap_view('get_anchor'), name='api_get_anchor'), + self.wrap_view('get_anchor'), + name='api_get_anchor', + ), ] def get_anchor(self, request, **__): @@ -199,9 +218,12 @@ def prepend_urls(self): return [ url( r'^(?P%s)/schema/$' % self._meta.resource_name, - self.wrap_view('get_schema'), name='api_get_schema'), + self.wrap_view('get_schema'), + name='api_get_schema', + ), url( - r'^(?P%s)/(?P[a-z-_]+)/$' % - self._meta.resource_name, self.wrap_view('dispatch_detail'), - name='api_dispatch_detail'), + r'^(?P%s)/(?P[a-z-_]+)/$' % self._meta.resource_name, + self.wrap_view('dispatch_detail'), + name='api_dispatch_detail', + ), ] diff --git a/readthedocs/api/utils.py b/readthedocs/api/utils.py index e2b5e9c331a..1f112a608e9 100644 --- a/readthedocs/api/utils.py +++ b/readthedocs/api/utils.py @@ -1,4 +1,5 @@ -"""Utility classes for api module""" +# -*- coding: utf-8 -*- +"""Utility classes for api module.""" import logging from django.utils.translation import ugettext @@ -16,14 +17,14 @@ class PostAuthentication(BasicAuthentication): """Require HTTP Basic authentication for any method other than GET.""" def is_authenticated(self, request, **kwargs): - val = super().is_authenticated(request, - **kwargs) - if request.method == "GET": + val = super().is_authenticated(request, **kwargs) + if request.method == 'GET': return True return val class EnhancedModelResource(ModelResource): + def obj_get_list(self, request=None, *_, **kwargs): # noqa """ A ORM-specific implementation of ``obj_get_list``. @@ -42,12 +43,16 @@ def obj_get_list(self, request=None, *_, **kwargs): # noqa try: return self.get_object_list(request).filter(**applicable_filters) except ValueError as e: - raise NotFound(ugettext("Invalid resource lookup data provided " - "(mismatched type).: %(error)s") - % {'error': e}) + raise NotFound( + ugettext( + 'Invalid resource lookup data provided ' + '(mismatched type).: %(error)s', + ) % {'error': e} + ) class OwnerAuthorization(Authorization): + def apply_limits(self, request, object_list): if request and hasattr(request, 'user') and request.method != 'GET': if request.user.is_authenticated: diff --git a/readthedocs/builds/admin.py b/readthedocs/builds/admin.py index 59fe235aef3..36b64339d18 100644 --- a/readthedocs/builds/admin.py +++ b/readthedocs/builds/admin.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Django admin interface for `~builds.models.Build` and related models.""" from django.contrib import admin @@ -12,8 +13,13 @@ class BuildCommandResultInline(admin.TabularInline): class BuildAdmin(admin.ModelAdmin): - fields = ('project', 'version', 'type', 'state', 'error', 'success', 'length', 'cold_storage') - list_display = ('id', 'project', 'version_name', 'success', 'type', 'state', 'date') + fields = ( + 'project', 'version', 'type', 'state', 'error', 'success', 'length', + 'cold_storage' + ) + list_display = ( + 'id', 'project', 'version_name', 'success', 'type', 'state', 'date' + ) list_filter = ('type', 'state', 'success') list_select_related = ('project', 'version') raw_id_fields = ('project', 'version') @@ -26,7 +32,9 @@ def version_name(self, obj): class VersionAdmin(GuardedModelAdmin): search_fields = ('slug', 'project__name') - list_display = ('slug', 'type', 'project', 'privacy_level', 'active', 'built') + list_display = ( + 'slug', 'type', 'project', 'privacy_level', 'active', 'built' + ) list_filter = ('type', 'privacy_level', 'active', 'built') raw_id_fields = ('project',) diff --git a/readthedocs/builds/constants.py b/readthedocs/builds/constants.py index 660fb0b0f4b..1175874cf19 100644 --- a/readthedocs/builds/constants.py +++ b/readthedocs/builds/constants.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Constants for the builds app.""" from django.conf import settings diff --git a/readthedocs/builds/forms.py b/readthedocs/builds/forms.py index ae655bfc90e..13f72a5b952 100644 --- a/readthedocs/builds/forms.py +++ b/readthedocs/builds/forms.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Django forms for the builds app.""" from django import forms @@ -18,10 +19,10 @@ def clean_active(self): if self._is_default_version() and not active: msg = _( '{version} is the default version of the project, ' - 'it should be active.' + 'it should be active.', ) raise forms.ValidationError( - msg.format(version=self.instance.verbose_name) + msg.format(version=self.instance.verbose_name), ) return active diff --git a/readthedocs/builds/managers.py b/readthedocs/builds/managers.py index bd886605886..d05308cc4b5 100644 --- a/readthedocs/builds/managers.py +++ b/readthedocs/builds/managers.py @@ -1,4 +1,5 @@ -"""Build and Version class model Managers""" +# -*- coding: utf-8 -*- +"""Build and Version class model Managers.""" from django.db import models @@ -37,7 +38,7 @@ def from_queryset(cls, queryset_class, class_name=None): # no direct members. queryset_class = get_override_class( VersionQuerySet, - VersionQuerySet._default_class # pylint: disable=protected-access + VersionQuerySet._default_class, # pylint: disable=protected-access ) return super().from_queryset(queryset_class, class_name) diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index c11fa13d0a0..b6a5b088125 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Models for the builds app.""" import logging @@ -49,7 +50,10 @@ DEFAULT_VERSION_PRIVACY_LEVEL = getattr( - settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public') + settings, + 'DEFAULT_VERSION_PRIVACY_LEVEL', + 'public', +) log = logging.getLogger(__name__) @@ -89,7 +93,10 @@ class Version(models.Model): #: filesystem to determine how the paths for this version are called. It #: must not be used for any other identifying purposes. slug = VersionSlugField( - _('Slug'), max_length=255, populate_from='verbose_name') + _('Slug'), + max_length=255, + populate_from='verbose_name', + ) supported = models.BooleanField(_('Supported'), default=True) active = models.BooleanField(_('Active'), default=False) @@ -113,7 +120,8 @@ class Meta: permissions = ( # Translators: Permission around whether a user can view the # version - ('view_version', _('View Version')),) + ('view_version', _('View Version')), + ) def __str__(self): return ugettext( @@ -121,7 +129,8 @@ def __str__(self): version=self.verbose_name, project=self.project, pk=self.pk, - )) + ), + ) @property def config(self): @@ -132,9 +141,8 @@ def config(self): :rtype: dict """ last_build = ( - self.builds.filter(state='finished', success=True) - .order_by('-date') - .first() + self.builds.filter(state='finished', + success=True).order_by('-date').first() ) return last_build.config @@ -177,7 +185,9 @@ def commit_name(self): # If we came that far it's not a special version nor a branch or tag. # Therefore just return the identifier to make a safe guess. - log.debug('TODO: Raise an exception here. Testing what cases it happens') + log.debug( + 'TODO: Raise an exception here. Testing what cases it happens' + ) return self.identifier def get_absolute_url(self): @@ -191,7 +201,9 @@ def get_absolute_url(self): ) private = self.privacy_level == PRIVATE return self.project.get_docs_url( - version_slug=self.slug, private=private) + version_slug=self.slug, + private=private, + ) def save(self, *args, **kwargs): # pylint: disable=arguments-differ """Add permissions to the Version for all owners on save.""" @@ -200,15 +212,24 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ for owner in self.project.users.all(): assign('view_version', owner, self) broadcast( - type='app', task=tasks.symlink_project, args=[self.project.pk]) + type='app', + task=tasks.symlink_project, + args=[self.project.pk], + ) return obj def delete(self, *args, **kwargs): # pylint: disable=arguments-differ from readthedocs.projects import tasks log.info('Removing files for version %s', self.slug) - broadcast(type='app', task=tasks.clear_artifacts, args=[self.get_artifact_paths()]) broadcast( - type='app', task=tasks.symlink_project, args=[self.project.pk]) + type='app', task=tasks.clear_artifacts, + args=[self.get_artifact_paths()] + ) + broadcast( + type='app', + task=tasks.symlink_project, + args=[self.project.pk], + ) super().delete(*args, **kwargs) @property @@ -238,19 +259,27 @@ def get_downloads(self, pretty=False): data['PDF'] = project.get_production_media_url('pdf', self.slug) if project.has_htmlzip(self.slug): data['HTML'] = project.get_production_media_url( - 'htmlzip', self.slug) + 'htmlzip', + self.slug, + ) if project.has_epub(self.slug): data['Epub'] = project.get_production_media_url( - 'epub', self.slug) + 'epub', + self.slug, + ) else: if project.has_pdf(self.slug): data['pdf'] = project.get_production_media_url('pdf', self.slug) if project.has_htmlzip(self.slug): data['htmlzip'] = project.get_production_media_url( - 'htmlzip', self.slug) + 'htmlzip', + self.slug, + ) if project.has_epub(self.slug): data['epub'] = project.get_production_media_url( - 'epub', self.slug) + 'epub', + self.slug, + ) return data def get_conf_py_path(self): @@ -278,7 +307,8 @@ def get_artifact_paths(self): paths.append( self.project.get_production_media_path( type_=type_, - version_slug=self.slug), + version_slug=self.slug, + ), ) paths.append(self.project.rtd_build_path(version=self.slug)) @@ -300,7 +330,12 @@ def clean_build_path(self): log.exception('Build path cleanup failed') def get_github_url( - self, docroot, filename, source_suffix='.rst', action='view'): + self, + docroot, + filename, + source_suffix='.rst', + action='view', + ): """ Return a GitHub URL for a given filename. @@ -342,7 +377,12 @@ def get_github_url( ) def get_gitlab_url( - self, docroot, filename, source_suffix='.rst', action='view'): + self, + docroot, + filename, + source_suffix='.rst', + action='view', + ): repo_url = self.project.repo if 'gitlab' not in repo_url: return '' @@ -439,13 +479,28 @@ class Build(models.Model): """Build data.""" project = models.ForeignKey( - Project, verbose_name=_('Project'), related_name='builds') + Project, + verbose_name=_('Project'), + related_name='builds', + ) version = models.ForeignKey( - Version, verbose_name=_('Version'), null=True, related_name='builds') + Version, + verbose_name=_('Version'), + null=True, + related_name='builds', + ) type = models.CharField( - _('Type'), max_length=55, choices=BUILD_TYPES, default='html') + _('Type'), + max_length=55, + choices=BUILD_TYPES, + default='html', + ) state = models.CharField( - _('State'), max_length=55, choices=BUILD_STATE, default='finished') + _('State'), + max_length=55, + choices=BUILD_STATE, + default='finished', + ) date = models.DateTimeField(_('Date'), auto_now_add=True) success = models.BooleanField(_('Success'), default=True) @@ -455,16 +510,26 @@ class Build(models.Model): error = models.TextField(_('Error'), default='', blank=True) exit_code = models.IntegerField(_('Exit code'), null=True, blank=True) commit = models.CharField( - _('Commit'), max_length=255, null=True, blank=True) + _('Commit'), + max_length=255, + null=True, + blank=True, + ) _config = JSONField(_('Configuration used in the build'), default=dict) length = models.IntegerField(_('Build Length'), null=True, blank=True) builder = models.CharField( - _('Builder'), max_length=255, null=True, blank=True) + _('Builder'), + max_length=255, + null=True, + blank=True, + ) cold_storage = models.NullBooleanField( - _('Cold Storage'), help_text='Build steps stored outside the database.') + _('Cold Storage'), + help_text='Build steps stored outside the database.', + ) # Manager @@ -491,14 +556,11 @@ def previous(self): date = self.date or timezone.now() if self.project is not None and self.version is not None: return ( - Build.objects - .filter( + Build.objects.filter( project=self.project, version=self.version, date__lt=date, - ) - .order_by('-date') - .first() + ).order_by('-date').first() ) return None @@ -508,9 +570,9 @@ def config(self): Get the config used for this build. Since we are saving the config into the JSON field only when it differs - from the previous one, this helper returns the correct JSON used in - this Build object (it could be stored in this object or one of the - previous ones). + from the previous one, this helper returns the correct JSON used in this + Build object (it could be stored in this object or one of the previous + ones). """ if self.CONFIG_KEY in self._config: return Build.objects.get(pk=self._config[self.CONFIG_KEY])._config @@ -538,8 +600,8 @@ def save(self, *args, **kwargs): # noqa """ if self.pk is None or self._config_changed: previous = self.previous - if (previous is not None and - self._config and self._config == previous.config): + if (previous is not None and self._config and + self._config == previous.config): previous_pk = previous._config.get(self.CONFIG_KEY, previous.pk) self._config = {self.CONFIG_KEY: previous_pk} super().save(*args, **kwargs) @@ -553,7 +615,8 @@ def __str__(self): self.project.users.all().values_list('username', flat=True), ), pk=self.pk, - )) + ), + ) def get_absolute_url(self): return reverse('builds_detail', args=[self.project.slug, self.pk]) @@ -594,7 +657,10 @@ class BuildCommandResult(BuildCommandResultMixin, models.Model): """Build command for a ``Build``.""" build = models.ForeignKey( - Build, verbose_name=_('Build'), related_name='commands') + Build, + verbose_name=_('Build'), + related_name='commands', + ) command = models.TextField(_('Command')) description = models.TextField(_('Description'), blank=True) @@ -613,7 +679,8 @@ class Meta: def __str__(self): return ( ugettext('Build command {pk} for build {build}') - .format(pk=self.pk, build=self.build)) + .format(pk=self.pk, build=self.build) + ) @property def run_time(self): diff --git a/readthedocs/builds/querysets.py b/readthedocs/builds/querysets.py index a2a34938836..5c3778257e5 100644 --- a/readthedocs/builds/querysets.py +++ b/readthedocs/builds/querysets.py @@ -1,4 +1,5 @@ -"""Build and Version QuerySet classes""" +# -*- coding: utf-8 -*- +"""Build and Version QuerySet classes.""" from django.db import models from guardian.shortcuts import get_objects_for_user @@ -35,7 +36,9 @@ def public(self, user=None, project=None, only_active=True): return queryset def protected(self, user=None, project=None, only_active=True): - queryset = self.filter(privacy_level__in=[constants.PUBLIC, constants.PROTECTED]) + queryset = self.filter( + privacy_level__in=[constants.PUBLIC, constants.PROTECTED] + ) if user: queryset = self._add_user_repos(queryset, user) if project: @@ -58,10 +61,10 @@ def api(self, user=None): return self.public(user, only_active=False) def for_project(self, project): - """Return all versions for a project, including translations""" + """Return all versions for a project, including translations.""" return self.filter( models.Q(project=project) | - models.Q(project__main_language_project=project) + models.Q(project__main_language_project=project), ) @@ -117,8 +120,7 @@ def _add_user_repos(self, queryset, user=None): if user.is_authenticated: user_queryset = get_objects_for_user(user, 'builds.view_version') pks = user_queryset.values_list('pk', flat=True) - queryset = self.filter( - build__version__pk__in=pks) | queryset + queryset = self.filter(build__version__pk__in=pks,) | queryset return queryset.distinct() def public(self, user=None, project=None): diff --git a/readthedocs/builds/signals.py b/readthedocs/builds/signals.py index c9bb49cc7cc..fb16ca75dd6 100644 --- a/readthedocs/builds/signals.py +++ b/readthedocs/builds/signals.py @@ -1,4 +1,5 @@ -"""Build signals""" +# -*- coding: utf-8 -*- +"""Build signals.""" import django.dispatch diff --git a/readthedocs/builds/syncers.py b/readthedocs/builds/syncers.py index 5f02f966bf7..185e0d30934 100644 --- a/readthedocs/builds/syncers.py +++ b/readthedocs/builds/syncers.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Classes to copy files between build and web servers. @@ -21,7 +22,7 @@ class BaseSyncer: - """A base object for syncers and pullers""" + """A base object for syncers and pullers.""" @classmethod def copy(cls, path, target, is_file=False, **kwargs): @@ -33,7 +34,7 @@ class LocalSyncer(BaseSyncer): @classmethod def copy(cls, path, target, is_file=False, **kwargs): """A copy command that works with files or directories.""" - log.info("Local Copy %s to %s", path, target) + log.info('Local Copy %s to %s', path, target) if is_file: if path == target: # Don't copy the same file over itself @@ -59,28 +60,31 @@ def copy(cls, path, target, is_file=False, **kwargs): sync_user = getattr(settings, 'SYNC_USER', getpass.getuser()) app_servers = getattr(settings, 'MULTIPLE_APP_SERVERS', []) if app_servers: - log.info("Remote Copy %s to %s on %s", path, target, app_servers) + log.info('Remote Copy %s to %s on %s', path, target, app_servers) for server in app_servers: - mkdir_cmd = ("ssh {}@{} mkdir -p {}".format(sync_user, server, target)) + mkdir_cmd = ( + 'ssh {}@{} mkdir -p {}'.format(sync_user, server, target) + ) ret = os.system(mkdir_cmd) if ret != 0: - log.debug("Copy error to app servers: cmd=%s", mkdir_cmd) + log.debug('Copy error to app servers: cmd=%s', mkdir_cmd) if is_file: - slash = "" + slash = '' else: - slash = "/" + slash = '/' # Add a slash when copying directories sync_cmd = ( - "rsync -e 'ssh -T' -av --delete {path}{slash} {user}@{server}:{target}" - .format( + "rsync -e 'ssh -T' -av --delete {path}{slash} {user}@{server}:{target}".format( path=path, slash=slash, user=sync_user, server=server, - target=target)) + target=target, + ) + ) ret = os.system(sync_cmd) if ret != 0: - log.debug("Copy error to app servers: cmd=%s", sync_cmd) + log.debug('Copy error to app servers: cmd=%s', sync_cmd) class DoubleRemotePuller(BaseSyncer): @@ -95,29 +99,32 @@ def copy(cls, path, target, host, is_file=False, **kwargs): # pylint: disable=a sync_user = getattr(settings, 'SYNC_USER', getpass.getuser()) app_servers = getattr(settings, 'MULTIPLE_APP_SERVERS', []) if not is_file: - path += "/" - log.info("Remote Copy %s to %s", path, target) + path += '/' + log.info('Remote Copy %s to %s', path, target) for server in app_servers: if not is_file: - mkdir_cmd = "ssh {user}@{server} mkdir -p {target}".format( - user=sync_user, server=server, target=target + mkdir_cmd = 'ssh {user}@{server} mkdir -p {target}'.format( + user=sync_user, + server=server, + target=target, ) ret = os.system(mkdir_cmd) if ret != 0: - log.debug("MkDir error to app servers: cmd=%s", mkdir_cmd) + log.debug('MkDir error to app servers: cmd=%s', mkdir_cmd) # Add a slash when copying directories sync_cmd = ( "ssh {user}@{server} 'rsync -av " - "--delete --exclude projects {user}@{host}:{path} {target}'" - .format( + "--delete --exclude projects {user}@{host}:{path} {target}'".format( host=host, path=path, user=sync_user, server=server, - target=target)) + target=target, + ) + ) ret = os.system(sync_cmd) if ret != 0: - log.debug("Copy error to app servers: cmd=%s", sync_cmd) + log.debug('Copy error to app servers: cmd=%s', sync_cmd) class RemotePuller(BaseSyncer): @@ -131,8 +138,8 @@ def copy(cls, path, target, host, is_file=False, **kwargs): # pylint: disable=a """ sync_user = getattr(settings, 'SYNC_USER', getpass.getuser()) if not is_file: - path += "/" - log.info("Remote Pull %s to %s", path, target) + path += '/' + log.info('Remote Pull %s to %s', path, target) if not is_file and not os.path.exists(target): safe_makedirs(target) # Add a slash when copying directories @@ -145,7 +152,7 @@ def copy(cls, path, target, host, is_file=False, **kwargs): # pylint: disable=a ret = os.system(sync_cmd) if ret != 0: log.debug( - "Copy error to app servers. Command: [%s] Return: [%s]", + 'Copy error to app servers. Command: [%s] Return: [%s]', sync_cmd, ret, ) diff --git a/readthedocs/builds/urls.py b/readthedocs/builds/urls.py index 07f787d0b4b..9c421662047 100644 --- a/readthedocs/builds/urls.py +++ b/readthedocs/builds/urls.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """URL configuration for builds app.""" from django.conf.urls import url diff --git a/readthedocs/builds/utils.py b/readthedocs/builds/utils.py index d2d49270cb2..e0025dc77ae 100644 --- a/readthedocs/builds/utils.py +++ b/readthedocs/builds/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Utilities for the builds app.""" from readthedocs.projects.constants import ( diff --git a/readthedocs/builds/version_slug.py b/readthedocs/builds/version_slug.py index 18ad3d19f99..d3e8bbe14b7 100644 --- a/readthedocs/builds/version_slug.py +++ b/readthedocs/builds/version_slug.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Contains logic for handling version slugs. @@ -34,12 +35,10 @@ def get_fields_with_model(cls): prescrived in the Django docs. https://docs.djangoproject.com/en/1.11/ref/models/meta/#migrating-from-the-old-api """ - return [ - (f, f.model if f.model != cls else None) - for f in cls._meta.get_fields() - if not f.is_relation or f.one_to_one or - (f.many_to_one and f.related_model) - ] + return [(f, f.model if f.model != cls else None) + for f in cls._meta.get_fields() + if not f.is_relation or f.one_to_one or + (f.many_to_one and f.related_model)] # Regex breakdown: @@ -165,7 +164,8 @@ def create_slug(self, model_instance): count += 1 assert self.test_pattern.match(slug), ( - 'Invalid generated slug: {slug}'.format(slug=slug)) + 'Invalid generated slug: {slug}'.format(slug=slug) + ) return slug def pre_save(self, model_instance, add): diff --git a/readthedocs/builds/views.py b/readthedocs/builds/views.py index 37a46ae3ece..69ed90dadd8 100644 --- a/readthedocs/builds/views.py +++ b/readthedocs/builds/views.py @@ -35,7 +35,8 @@ def get_queryset(self): slug=self.project_slug, ) queryset = Build.objects.public( - user=self.request.user, project=self.project + user=self.request.user, + project=self.project, ) return queryset @@ -57,7 +58,9 @@ def post(self, request, project_slug): slug=version_slug, ) - update_docs_task, build = trigger_build(project=project, version=version) + update_docs_task, build = trigger_build( + project=project, version=version + ) if (update_docs_task, build) == (None, None): # Build was skipped messages.add_message( @@ -79,13 +82,15 @@ class BuildList(BuildBase, BuildTriggerMixin, ListView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - active_builds = self.get_queryset().exclude(state='finished' - ).values('id') + active_builds = self.get_queryset().exclude( + state='finished', + ).values('id') context['project'] = self.project context['active_builds'] = active_builds context['versions'] = Version.objects.public( - user=self.request.user, project=self.project + user=self.request.user, + project=self.project, ) context['build_qs'] = self.get_queryset() @@ -106,11 +111,11 @@ def get_context_data(self, **kwargs): def builds_redirect_list(request, project_slug): # pylint: disable=unused-argument return HttpResponsePermanentRedirect( - reverse('builds_project_list', args=[project_slug]) + reverse('builds_project_list', args=[project_slug]), ) def builds_redirect_detail(request, project_slug, pk): # pylint: disable=unused-argument return HttpResponsePermanentRedirect( - reverse('builds_detail', args=[project_slug, pk]) + reverse('builds_detail', args=[project_slug, pk]), ) diff --git a/readthedocs/config/__init__.py b/readthedocs/config/__init__.py index 314f6c394cc..fd6326a50e7 100644 --- a/readthedocs/config/__init__.py +++ b/readthedocs/config/__init__.py @@ -1,2 +1,3 @@ +# -*- coding: utf-8 -*- from .config import * # noqa from .parser import * # noqa diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index 57e01439e0a..a397544e99b 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -96,7 +96,7 @@ def __init__(self, configuration): ) super().__init__( template.format(self.configuration), - CONFIG_NOT_SUPPORTED + CONFIG_NOT_SUPPORTED, ) @@ -137,9 +137,15 @@ class BuildConfigBase: """ PUBLIC_ATTRIBUTES = [ - 'version', 'formats', 'python', - 'conda', 'build', 'doctype', - 'sphinx', 'mkdocs', 'submodules', + 'version', + 'formats', + 'python', + 'conda', + 'build', + 'doctype', + 'sphinx', + 'mkdocs', + 'submodules', ] version = None @@ -235,9 +241,7 @@ def python_full_version(self): # Get the highest version of the major series version if user only # gave us a version of '2', or '3' ver = max( - v - for v in self.get_valid_python_versions() - if v < ver + 1 + v for v in self.get_valid_python_versions() if v < ver + 1 ) return ver @@ -324,11 +328,12 @@ def validate(self): def validate_output_base(self): """Validates that ``output_base`` exists and set its absolute path.""" assert 'output_base' in self.env_config, ( - '"output_base" required in "env_config"') + '"output_base" required in "env_config"' + ) output_base = os.path.abspath( os.path.join( self.env_config.get('output_base', self.base_path), - ) + ), ) return output_base @@ -345,8 +350,10 @@ def validate_name(self): 'name', self.NAME_INVALID_MESSAGE.format( name=name, - name_re=name_re), - code=NAME_INVALID) + name_re=name_re, + ), + code=NAME_INVALID, + ) return name @@ -395,18 +402,16 @@ def validate_build(self): # Prepend proper image name to user's image name build['image'] = '{}:{}'.format( DOCKER_DEFAULT_IMAGE, - build['image'] + build['image'], ) # Update docker default settings from image name if build['image'] in DOCKER_IMAGE_SETTINGS: - self.env_config.update( - DOCKER_IMAGE_SETTINGS[build['image']] - ) + self.env_config.update(DOCKER_IMAGE_SETTINGS[build['image']],) # Update docker settings from user config if 'DOCKER_IMAGE_SETTINGS' in self.env_config and \ build['image'] in self.env_config['DOCKER_IMAGE_SETTINGS']: self.env_config.update( - self.env_config['DOCKER_IMAGE_SETTINGS'][build['image']] + self.env_config['DOCKER_IMAGE_SETTINGS'][build['image']], ) # Allow to override specific project @@ -434,20 +439,22 @@ def validate_python(self): self.error( 'python', self.PYTHON_INVALID_MESSAGE, - code=PYTHON_INVALID) + code=PYTHON_INVALID, + ) # Validate use_system_site_packages. if 'use_system_site_packages' in raw_python: - with self.catch_validation_error( - 'python.use_system_site_packages'): + with self.catch_validation_error('python.use_system_site_packages',): python['use_system_site_packages'] = validate_bool( - raw_python['use_system_site_packages']) + raw_python['use_system_site_packages'], + ) # Validate pip_install. if 'pip_install' in raw_python: with self.catch_validation_error('python.pip_install'): python['install_with_pip'] = validate_bool( - raw_python['pip_install']) + raw_python['pip_install'], + ) # Validate extra_requirements. if 'extra_requirements' in raw_python: @@ -456,22 +463,23 @@ def validate_python(self): self.error( 'python.extra_requirements', self.PYTHON_EXTRA_REQUIREMENTS_INVALID_MESSAGE, - code=PYTHON_INVALID) + code=PYTHON_INVALID, + ) if not python['install_with_pip']: python['extra_requirements'] = [] else: for extra_name in raw_extra_requirements: - with self.catch_validation_error( - 'python.extra_requirements'): + with self.catch_validation_error('python.extra_requirements',): python['extra_requirements'].append( - validate_string(extra_name) + validate_string(extra_name), ) # Validate setup_py_install. if 'setup_py_install' in raw_python: with self.catch_validation_error('python.setup_py_install'): python['install_with_setup'] = validate_bool( - raw_python['setup_py_install']) + raw_python['setup_py_install'], + ) if 'version' in raw_python: with self.catch_validation_error('python.version'): @@ -505,7 +513,8 @@ def validate_conda(self): if 'file' in raw_conda: with self.catch_validation_error('conda.file'): conda_environment = validate_file( - raw_conda['file'], self.base_path + raw_conda['file'], + self.base_path, ) conda['environment'] = conda_environment @@ -758,7 +767,8 @@ def validate_python(self): with self.catch_validation_error('python.extra_requirements'): extra_requirements = self.pop_config( - 'python.extra_requirements', [] + 'python.extra_requirements', + [], ) extra_requirements = validate_list(extra_requirements) if extra_requirements and not python['install_with_pip']: @@ -876,7 +886,8 @@ def validate_sphinx(self): if not configuration: configuration = None configuration = self.pop_config( - 'sphinx.configuration', configuration + 'sphinx.configuration', + configuration, ) if configuration is not None: configuration = validate_file(configuration, self.base_path) @@ -892,9 +903,8 @@ def validate_final_doc_type(self): """ Validates that the doctype is the same as the admin panel. - This a temporal validation, as the configuration file - should support per version doctype, but we need to - adapt the rtd code for that. + This a temporal validation, as the configuration file should support per + version doctype, but we need to adapt the rtd code for that. """ dashboard_doctype = self.defaults.get('doctype', 'sphinx') if self.doctype != dashboard_doctype: @@ -904,7 +914,7 @@ def validate_final_doc_type(self): if dashboard_doctype == 'mkdocs' or not self.sphinx: error_msg += ' but there is no "{}" key specified.'.format( - 'mkdocs' if dashboard_doctype == 'mkdocs' else 'sphinx' + 'mkdocs' if dashboard_doctype == 'mkdocs' else 'sphinx', ) else: error_msg += ' but your "sphinx.builder" key does not match.' @@ -966,8 +976,8 @@ def validate_keys(self): """ Checks that we don't have extra keys (invalid ones). - This should be called after all the validations are done - and all keys are popped from `self.raw_config`. + This should be called after all the validations are done and all keys + are popped from `self.raw_config`. """ msg = ( 'Invalid configuration option: {}. ' @@ -1059,7 +1069,7 @@ def load(path, env_config): if not filename: raise ConfigError( 'No configuration file found', - code=CONFIG_REQUIRED + code=CONFIG_REQUIRED, ) with open(filename, 'r') as configuration_file: try: diff --git a/readthedocs/config/find.py b/readthedocs/config/find.py index ed49c765c1d..6fc3cb6a7a9 100644 --- a/readthedocs/config/find.py +++ b/readthedocs/config/find.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Helper functions to search files.""" import os diff --git a/readthedocs/config/models.py b/readthedocs/config/models.py index ce0604f18b7..1153fe3f981 100644 --- a/readthedocs/config/models.py +++ b/readthedocs/config/models.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Models for the response of the configuration object.""" from collections import namedtuple diff --git a/readthedocs/config/parser.py b/readthedocs/config/parser.py index 85e31782b00..376774d6144 100644 --- a/readthedocs/config/parser.py +++ b/readthedocs/config/parser.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """YAML parser for the RTD configuration file.""" import yaml @@ -11,8 +12,6 @@ class ParseError(Exception): """Parser related errors.""" - pass - def parse(stream): """ diff --git a/readthedocs/config/validation.py b/readthedocs/config/validation.py index c366b05073a..b1840b880aa 100644 --- a/readthedocs/config/validation.py +++ b/readthedocs/config/validation.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Validations for the RTD configuration file.""" import os @@ -26,7 +27,7 @@ class ValidationError(Exception): INVALID_PATH: 'path {value} does not exist', INVALID_STRING: 'expected string', INVALID_LIST: 'expected list', - VALUE_NOT_FOUND: '{value} not found' + VALUE_NOT_FOUND: '{value} not found', } def __init__(self, value, code, format_kwargs=None): @@ -60,9 +61,13 @@ def validate_choice(value, choices): """Check that ``value`` is in ``choices``.""" choices = validate_list(choices) if value not in choices: - raise ValidationError(value, INVALID_CHOICE, { - 'choices': ', '.join(map(str, choices)) - }) + raise ValidationError( + value, + INVALID_CHOICE, + { + 'choices': ', '.join(map(str, choices)), + }, + ) return value diff --git a/readthedocs/constants.py b/readthedocs/constants.py index 35cca627432..485b937398d 100644 --- a/readthedocs/constants.py +++ b/readthedocs/constants.py @@ -1,4 +1,5 @@ -"""Common constants""" +# -*- coding: utf-8 -*- +"""Common constants.""" from readthedocs.builds.version_slug import VERSION_SLUG_REGEX from readthedocs.projects.constants import LANGUAGES_REGEX, PROJECT_SLUG_REGEX diff --git a/readthedocs/core/__init__.py b/readthedocs/core/__init__.py index ed1c53debf0..c6205d82c60 100644 --- a/readthedocs/core/__init__.py +++ b/readthedocs/core/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """App initialization.""" default_app_config = 'readthedocs.core.apps.CoreAppConfig' diff --git a/readthedocs/core/adapters.py b/readthedocs/core/adapters.py index 803952a792d..aa2fad046dc 100644 --- a/readthedocs/core/adapters.py +++ b/readthedocs/core/adapters.py @@ -1,4 +1,5 @@ -"""Allauth overrides""" +# -*- coding: utf-8 -*- +"""Allauth overrides.""" import json import logging @@ -19,16 +20,17 @@ class AccountAdapter(DefaultAccountAdapter): - """Customize Allauth emails to match our current patterns""" + """Customize Allauth emails to match our current patterns.""" def format_email_subject(self, subject): return force_text(subject) def send_mail(self, template_prefix, email, context): subject = render_to_string( - '{}_subject.txt'.format(template_prefix), context + '{}_subject.txt'.format(template_prefix), + context, ) - subject = " ".join(subject.splitlines()).strip() + subject = ' '.join(subject.splitlines()).strip() subject = self.format_email_subject(subject) # Allauth sends some additional data in the context, remove it if the @@ -41,13 +43,15 @@ def send_mail(self, template_prefix, email, context): removed_keys.append(key) del context[key] if removed_keys: - log.debug('Removed context we were unable to serialize: %s', - removed_keys) + log.debug( + 'Removed context we were unable to serialize: %s', + removed_keys, + ) send_email( recipient=email, subject=subject, template='{}_message.txt'.format(template_prefix), template_html='{}_message.html'.format(template_prefix), - context=context + context=context, ) diff --git a/readthedocs/core/admin.py b/readthedocs/core/admin.py index 5e8042f1202..da3ebfa15b9 100644 --- a/readthedocs/core/admin.py +++ b/readthedocs/core/admin.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Django admin interface for core models.""" from datetime import timedelta @@ -58,8 +59,14 @@ class UserAdminExtra(UserAdmin): """Admin configuration for User.""" - list_display = ('username', 'email', 'first_name', - 'last_name', 'is_staff', 'is_banned') + list_display = ( + 'username', + 'email', + 'first_name', + 'last_name', + 'is_staff', + 'is_banned', + ) list_filter = (UserProjectFilter,) + UserAdmin.list_filter actions = ['ban_user'] inlines = [UserProjectInline] diff --git a/readthedocs/core/apps.py b/readthedocs/core/apps.py index d422684ba03..0944efc1646 100644 --- a/readthedocs/core/apps.py +++ b/readthedocs/core/apps.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """App configurations for core app.""" from django.apps import AppConfig diff --git a/readthedocs/core/backends.py b/readthedocs/core/backends.py index 64258fe91eb..381be54dc86 100644 --- a/readthedocs/core/backends.py +++ b/readthedocs/core/backends.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Email backends for core app.""" import smtplib @@ -12,8 +13,11 @@ def open(self): if self.connection: return False try: - self.connection = smtplib.SMTP_SSL(self.host, self.port, - local_hostname=DNS_NAME.get_fqdn()) + self.connection = smtplib.SMTP_SSL( + self.host, + self.port, + local_hostname=DNS_NAME.get_fqdn(), + ) if self.username and self.password: self.connection.login(self.username, self.password) return True diff --git a/readthedocs/core/context_processors.py b/readthedocs/core/context_processors.py index dc136c51171..815afbdaa6a 100644 --- a/readthedocs/core/context_processors.py +++ b/readthedocs/core/context_processors.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Template context processors for core app.""" from django.conf import settings @@ -10,10 +11,14 @@ def readthedocs_processor(request): 'PRODUCTION_DOMAIN': getattr(settings, 'PRODUCTION_DOMAIN', None), 'USE_SUBDOMAINS': getattr(settings, 'USE_SUBDOMAINS', None), 'GLOBAL_ANALYTICS_CODE': getattr(settings, 'GLOBAL_ANALYTICS_CODE'), - 'DASHBOARD_ANALYTICS_CODE': getattr(settings, 'DASHBOARD_ANALYTICS_CODE'), + 'DASHBOARD_ANALYTICS_CODE': getattr( + settings, 'DASHBOARD_ANALYTICS_CODE' + ), 'SITE_ROOT': getattr(settings, 'SITE_ROOT', '') + '/', 'TEMPLATE_ROOT': getattr(settings, 'TEMPLATE_ROOT', '') + '/', - 'DO_NOT_TRACK_ENABLED': getattr(settings, 'DO_NOT_TRACK_ENABLED', False), + 'DO_NOT_TRACK_ENABLED': getattr( + settings, 'DO_NOT_TRACK_ENABLED', False + ), 'USE_PROMOS': getattr(settings, 'USE_PROMOS', False), } return exports diff --git a/readthedocs/core/fields.py b/readthedocs/core/fields.py index 822ad5ca151..1b6d9f99420 100644 --- a/readthedocs/core/fields.py +++ b/readthedocs/core/fields.py @@ -1,9 +1,10 @@ -"""Shared model fields and defaults""" +# -*- coding: utf-8 -*- +"""Shared model fields and defaults.""" import binascii import os def default_token(): - """Generate default value for token field""" + """Generate default value for token field.""" return binascii.hexlify(os.urandom(20)).decode() diff --git a/readthedocs/core/fixtures/flag_types.json b/readthedocs/core/fixtures/flag_types.json index afe8ef1cd79..52f13740581 100644 --- a/readthedocs/core/fixtures/flag_types.json +++ b/readthedocs/core/fixtures/flag_types.json @@ -1,28 +1,28 @@ [ { - "pk": 1, - "model": "flagging.flagtype", + "pk": 1, + "model": "flagging.flagtype", "fields": { - "description": "This item is inappropriate to the purpose of the site", - "slug": "inappropriate", + "description": "This item is inappropriate to the purpose of the site", + "slug": "inappropriate", "title": "Inappropriate" } - }, + }, { - "pk": 2, - "model": "flagging.flagtype", + "pk": 2, + "model": "flagging.flagtype", "fields": { - "description": "This item is spam", - "slug": "spam", + "description": "This item is spam", + "slug": "spam", "title": "Spam" } - }, + }, { - "pk": 3, - "model": "flagging.flagtype", + "pk": 3, + "model": "flagging.flagtype", "fields": { - "description": "These docs are a duplicate of other, official docs, on the site", - "slug": "duplicate", + "description": "These docs are a duplicate of other, official docs, on the site", + "slug": "duplicate", "title": "Duplicate" } } diff --git a/readthedocs/core/forms.py b/readthedocs/core/forms.py index 66c09e84b93..34ebfbd0d2e 100644 --- a/readthedocs/core/forms.py +++ b/readthedocs/core/forms.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Forms for core app.""" import logging @@ -63,6 +64,7 @@ def clean_username(self): class UserAdvertisingForm(forms.ModelForm): + class Meta: model = UserProfile fields = ['allow_ads'] diff --git a/readthedocs/core/management/commands/archive.py b/readthedocs/core/management/commands/archive.py index 3402d1823b4..e90d8de00a0 100644 --- a/readthedocs/core/management/commands/archive.py +++ b/readthedocs/core/management/commands/archive.py @@ -1,4 +1,5 @@ -"""Rebuild documentation for all projects""" +# -*- coding: utf-8 -*- +"""Rebuild documentation for all projects.""" import logging import os @@ -20,10 +21,10 @@ def handle(self, *args, **options): doc_index = {} os.chdir(settings.DOCROOT) - for directory in glob("*"): + for directory in glob('*'): doc_index[directory] = [] path = os.path.join(directory, 'rtd-builds') - for version in glob(os.path.join(path, "*")): + for version in glob(os.path.join(path, '*')): v = version.replace(path + '/', '') doc_index[directory].append(v) @@ -31,5 +32,6 @@ def handle(self, *args, **options): 'doc_index': doc_index, 'MEDIA_URL': settings.MEDIA_URL, } - html = template_loader.get_template('archive/index.html').render(context) + html = template_loader.get_template('archive/index.html' + ).render(context) print(html) diff --git a/readthedocs/core/management/commands/clean_builds.py b/readthedocs/core/management/commands/clean_builds.py index 1a48748a963..64d52ec84a0 100644 --- a/readthedocs/core/management/commands/clean_builds.py +++ b/readthedocs/core/management/commands/clean_builds.py @@ -1,8 +1,8 @@ -"""Clean up stable build paths per project version""" +# -*- coding: utf-8 -*- +"""Clean up stable build paths per project version.""" import logging from datetime import timedelta -from optparse import make_option from django.core.management.base import BaseCommand from django.db.models import Max @@ -24,24 +24,24 @@ def add_arguments(self, parser): dest='days', type='int', default=365, - help='Find builds older than DAYS days, default: 365' + help='Find builds older than DAYS days, default: 365', ) parser.add_argument( '--dryrun', action='store_true', dest='dryrun', - help='Perform dry run on build cleanup' + help='Perform dry run on build cleanup', ) def handle(self, *args, **options): - """Find stale builds and remove build paths""" + """Find stale builds and remove build paths.""" max_date = timezone.now() - timedelta(days=options['days']) - queryset = (Build.objects - .values('project', 'version') - .annotate(max_date=Max('date')) - .filter(max_date__lt=max_date) - .order_by('-max_date')) + queryset = ( + Build.objects.values('project', 'version').annotate( + max_date=Max('date') + ).filter(max_date__lt=max_date).order_by('-max_date') + ) for build in queryset: try: # Get version from build version id, perform sanity check on diff --git a/readthedocs/core/management/commands/import_github.py b/readthedocs/core/management/commands/import_github.py index 2e2e49176b7..85530968f4b 100644 --- a/readthedocs/core/management/commands/import_github.py +++ b/readthedocs/core/management/commands/import_github.py @@ -1,4 +1,5 @@ -"""Resync GitHub project for user""" +# -*- coding: utf-8 -*- +"""Resync GitHub project for user.""" from django.contrib.auth.models import User from django.core.management.base import BaseCommand @@ -13,7 +14,6 @@ class Command(BaseCommand): def handle(self, *args, **options): if args: for slug in args: - for service in GitHubService.for_user( - User.objects.get(username=slug) - ): + for service in GitHubService.for_user(User.objects.get(username=slug + ),): service.sync() diff --git a/readthedocs/core/management/commands/import_github_language.py b/readthedocs/core/management/commands/import_github_language.py index 1e42685a57e..e3679a52614 100644 --- a/readthedocs/core/management/commands/import_github_language.py +++ b/readthedocs/core/management/commands/import_github_language.py @@ -1,5 +1,6 @@ +# -*- coding: utf-8 -*- """ -Import a project's programming language from GitHub +Import a project's programming language from GitHub. This builds a basic management command that will set a projects language to the most used one in GitHub. @@ -35,11 +36,8 @@ def handle(self, *args, **options): print('Invalid GitHub token, exiting') return - for project in Project.objects.filter( - programming_language__in=['none', '', 'words'] - ).filter( - repo__contains='github' - ): + for project in Project.objects.filter(programming_language__in=[ + 'none', '', 'words'],).filter(repo__contains='github',): user = repo = '' repo_url = project.repo for regex in GITHUB_REGEXS: @@ -65,7 +63,9 @@ def handle(self, *args, **options): languages = resp.json() if not languages: continue - sorted_langs = sorted(list(languages.items()), key=lambda x: x[1], reverse=True) + sorted_langs = sorted( + list(languages.items()), key=lambda x: x[1], reverse=True + ) print('Sorted langs: %s ' % sorted_langs) top_lang = sorted_langs[0][0] else: @@ -73,7 +73,8 @@ def handle(self, *args, **options): if top_lang in PL_DICT: slug = PL_DICT[top_lang] print('Setting {} to {}'.format(repo_url, slug)) - Project.objects.filter(pk=project.pk).update(programming_language=slug) + Project.objects.filter(pk=project.pk + ).update(programming_language=slug) else: print('Language unknown: %s' % top_lang) cache.set(cache_key, top_lang, 60 * 600) diff --git a/readthedocs/core/management/commands/provision_elasticsearch.py b/readthedocs/core/management/commands/provision_elasticsearch.py index 690a45803d9..adab81d4958 100644 --- a/readthedocs/core/management/commands/provision_elasticsearch.py +++ b/readthedocs/core/management/commands/provision_elasticsearch.py @@ -1,4 +1,5 @@ -"""Provision Elastic Search""" +# -*- coding: utf-8 -*- +"""Provision Elastic Search.""" import logging @@ -20,19 +21,19 @@ class Command(BaseCommand): help = __doc__ def handle(self, *args, **options): - """Provision new ES instance""" + """Provision new ES instance.""" index = Index() index_name = index.timestamped_index() - log.info("Creating indexes..") + log.info('Creating indexes..') index.create_index(index_name) index.update_aliases(index_name) - log.info("Updating mappings..") + log.info('Updating mappings..') proj = ProjectIndex() proj.put_mapping() page = PageIndex() page.put_mapping() sec = SectionIndex() sec.put_mapping() - log.info("Done!") + log.info('Done!') diff --git a/readthedocs/core/management/commands/pull.py b/readthedocs/core/management/commands/pull.py index f0ebe4206bd..3c965f8b1af 100644 --- a/readthedocs/core/management/commands/pull.py +++ b/readthedocs/core/management/commands/pull.py @@ -1,4 +1,5 @@ -"""Trigger build for project slug""" +# -*- coding: utf-8 -*- +"""Trigger build for project slug.""" import logging diff --git a/readthedocs/core/management/commands/reindex_elasticsearch.py b/readthedocs/core/management/commands/reindex_elasticsearch.py index 9d2ab83495a..0d552e86e3e 100644 --- a/readthedocs/core/management/commands/reindex_elasticsearch.py +++ b/readthedocs/core/management/commands/reindex_elasticsearch.py @@ -1,7 +1,7 @@ -"""Reindex Elastic Search indexes""" +# -*- coding: utf-8 -*- +"""Reindex Elastic Search indexes.""" import logging -from optparse import make_option from django.conf import settings from django.core.management.base import BaseCommand, CommandError @@ -23,11 +23,11 @@ def add_arguments(self, parser): '-p', dest='project', default='', - help='Project to index' + help='Project to index', ) def handle(self, *args, **options): - """Build/index all versions or a single project's version""" + """Build/index all versions or a single project's version.""" project = options['project'] queryset = Version.objects.all() @@ -36,13 +36,14 @@ def handle(self, *args, **options): queryset = queryset.filter(project__slug=project) if not queryset.exists(): raise CommandError( - 'No project with slug: {slug}'.format(slug=project)) - log.info("Building all versions for %s", project) + 'No project with slug: {slug}'.format(slug=project), + ) + log.info('Building all versions for %s', project) elif getattr(settings, 'INDEX_ONLY_LATEST', True): queryset = queryset.filter(slug=LATEST) for version in queryset: - log.info("Reindexing %s", version) + log.info('Reindexing %s', version) try: commit = version.project.vcs_repo(version.slug).commit except: # noqa @@ -51,7 +52,10 @@ def handle(self, *args, **options): commit = None try: - update_search(version.pk, commit, - delete_non_commit_files=False) + update_search( + version.pk, + commit, + delete_non_commit_files=False, + ) except Exception as e: log.exception('Reindex failed for %s, %s', version, e) diff --git a/readthedocs/core/management/commands/set_metadata.py b/readthedocs/core/management/commands/set_metadata.py index e8d17a9dbf1..e5782e404ef 100644 --- a/readthedocs/core/management/commands/set_metadata.py +++ b/readthedocs/core/management/commands/set_metadata.py @@ -1,4 +1,5 @@ -"""Generate metadata for all projects""" +# -*- coding: utf-8 -*- +"""Generate metadata for all projects.""" import logging @@ -19,8 +20,10 @@ class Command(BaseCommand): def handle(self, *args, **options): queryset = Project.objects.all() for p in queryset: - log.info("Generating metadata for %s", p) + log.info('Generating metadata for %s', p) try: - broadcast(type='app', task=tasks.update_static_metadata, args=[p.pk]) + broadcast( + type='app', task=tasks.update_static_metadata, args=[p.pk] + ) except Exception: log.exception('Build failed for %s', p) diff --git a/readthedocs/core/management/commands/symlink.py b/readthedocs/core/management/commands/symlink.py index 3fda03a41db..a7743a74057 100644 --- a/readthedocs/core/management/commands/symlink.py +++ b/readthedocs/core/management/commands/symlink.py @@ -1,4 +1,5 @@ -"""Update symlinks for projects""" +# -*- coding: utf-8 -*- +"""Update symlinks for projects.""" import logging @@ -23,7 +24,8 @@ def handle(self, *args, **options): if 'all' in projects: pks = Project.objects.values_list('pk', flat=True) else: - pks = Project.objects.filter(slug__in=projects).values_list('pk', flat=True) + pks = Project.objects.filter(slug__in=projects + ).values_list('pk', flat=True) for proj in pks: try: tasks.symlink_project(project_pk=proj) diff --git a/readthedocs/core/management/commands/update_api.py b/readthedocs/core/management/commands/update_api.py index 3a9b44b1c9e..5b87bd4a571 100644 --- a/readthedocs/core/management/commands/update_api.py +++ b/readthedocs/core/management/commands/update_api.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Build documentation using the API and not hitting a database. @@ -31,6 +32,6 @@ def handle(self, *args, **options): for slug in options['projects']: project_data = api.project(slug).get() p = APIProject(**project_data) - log.info("Building %s", p) + log.info('Building %s', p) # pylint: disable=no-value-for-parameter tasks.update_docs_task(p.pk, docker=docker) diff --git a/readthedocs/core/management/commands/update_versions.py b/readthedocs/core/management/commands/update_versions.py index 9cc0c297130..bf066c7757b 100644 --- a/readthedocs/core/management/commands/update_versions.py +++ b/readthedocs/core/management/commands/update_versions.py @@ -1,4 +1,5 @@ -"""Rebuild documentation for all projects""" +# -*- coding: utf-8 -*- +"""Rebuild documentation for all projects.""" from django.core.management.base import BaseCommand @@ -16,5 +17,5 @@ def handle(self, *args, **options): update_docs_task( version.project_id, record=False, - version_pk=version.pk + version_pk=version.pk, ) diff --git a/readthedocs/core/middleware.py b/readthedocs/core/middleware.py index 967a4e84aa9..1a874e12011 100644 --- a/readthedocs/core/middleware.py +++ b/readthedocs/core/middleware.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Middleware for core app.""" import logging @@ -7,7 +8,7 @@ from django.core.cache import cache from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.http import Http404, HttpResponseBadRequest -from django.urls.base import get_urlconf, set_urlconf +from django.urls.base import set_urlconf from django.utils.deprecation import MiddlewareMixin from django.utils.translation import ugettext_lazy as _ @@ -17,16 +18,16 @@ log = logging.getLogger(__name__) -LOG_TEMPLATE = "(Middleware) {msg} [{host}{path}]" +LOG_TEMPLATE = '(Middleware) {msg} [{host}{path}]' SUBDOMAIN_URLCONF = getattr( settings, 'SUBDOMAIN_URLCONF', - 'readthedocs.core.urls.subdomain' + 'readthedocs.core.urls.subdomain', ) SINGLE_VERSION_URLCONF = getattr( settings, 'SINGLE_VERSION_URLCONF', - 'readthedocs.core.urls.single_version' + 'readthedocs.core.urls.single_version', ) @@ -52,7 +53,7 @@ def process_request(self, request): production_domain = getattr( settings, 'PRODUCTION_DOMAIN', - 'readthedocs.org' + 'readthedocs.org', ) if public_domain is None: @@ -65,10 +66,8 @@ def process_request(self, request): if len(domain_parts) == len(public_domain.split('.')) + 1: subdomain = domain_parts[0] is_www = subdomain.lower() == 'www' - if not is_www and ( - # Support ports during local dev - public_domain in host or public_domain in full_host - ): + if not is_www and ( # Support ports during local dev + public_domain in host or public_domain in full_host): if not Project.objects.filter(slug=subdomain).exists(): raise Http404(_('Project not found')) request.subdomain = True @@ -77,10 +76,8 @@ def process_request(self, request): return None # Serve CNAMEs - if (public_domain not in host and - production_domain not in host and - 'localhost' not in host and - 'testserver' not in host): + if (public_domain not in host and production_domain not in host and + 'localhost' not in host and 'testserver' not in host): request.cname = True domains = Domain.objects.filter(domain=host) if domains.count(): @@ -89,18 +86,24 @@ def process_request(self, request): request.slug = domain.project.slug request.urlconf = SUBDOMAIN_URLCONF request.domain_object = True - log.debug(LOG_TEMPLATE.format( - msg='Domain Object Detected: %s' % domain.domain, - **log_kwargs)) + log.debug( + LOG_TEMPLATE.format( + msg='Domain Object Detected: %s' % domain.domain, + **log_kwargs + ) + ) break if (not hasattr(request, 'domain_object') and 'HTTP_X_RTD_SLUG' in request.META): request.slug = request.META['HTTP_X_RTD_SLUG'].lower() request.urlconf = SUBDOMAIN_URLCONF request.rtdheader = True - log.debug(LOG_TEMPLATE.format( - msg='X-RTD-Slug header detected: %s' % request.slug, - **log_kwargs)) + log.debug( + LOG_TEMPLATE.format( + msg='X-RTD-Slug header detected: %s' % request.slug, + **log_kwargs + ) + ) # Try header first, then DNS elif not hasattr(request, 'domain_object'): try: @@ -109,26 +112,39 @@ def process_request(self, request): slug = cname_to_slug(host) cache.set(host, slug, 60 * 60) # Cache the slug -> host mapping permanently. - log.info(LOG_TEMPLATE.format( - msg='CNAME cached: {}->{}'.format(slug, host), - **log_kwargs)) + log.info( + LOG_TEMPLATE.format( + msg='CNAME cached: {}->{}'.format(slug, host), + **log_kwargs + ) + ) request.slug = slug request.urlconf = SUBDOMAIN_URLCONF - log.warning(LOG_TEMPLATE.format( - msg='CNAME detected: %s' % request.slug, - **log_kwargs)) + log.warning( + LOG_TEMPLATE.format( + msg='CNAME detected: %s' % request.slug, + **log_kwargs + ) + ) except: # noqa # Some crazy person is CNAMEing to us. 404. - log.warning(LOG_TEMPLATE.format(msg='CNAME 404', **log_kwargs)) + log.warning( + LOG_TEMPLATE.format(msg='CNAME 404', **log_kwargs) + ) raise Http404(_('Invalid hostname')) # Google was finding crazy www.blah.readthedocs.org domains. # Block these explicitly after trying CNAME logic. if len(domain_parts) > 3 and not settings.DEBUG: # Stop www.fooo.readthedocs.org if domain_parts[0] == 'www': - log.debug(LOG_TEMPLATE.format(msg='404ing long domain', **log_kwargs)) + log.debug( + LOG_TEMPLATE.format(msg='404ing long domain', **log_kwargs) + ) return HttpResponseBadRequest(_('Invalid hostname')) - log.debug(LOG_TEMPLATE.format(msg='Allowing long domain name', **log_kwargs)) + log.debug( + LOG_TEMPLATE + .format(msg='Allowing long domain name', **log_kwargs) + ) # raise Http404(_('Invalid hostname')) # Normal request. return None @@ -186,8 +202,9 @@ def process_request(self, request): host = request.get_host() path = request.get_full_path() log_kwargs = dict(host=host, path=path) - log.debug(LOG_TEMPLATE.format( - msg='Handling single_version request', **log_kwargs) + log.debug( + LOG_TEMPLATE. + format(msg='Handling single_version request', **log_kwargs), ) return None @@ -217,7 +234,7 @@ def process_request(self, request): else: # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. The # client's IP will be the first one. - real_ip = real_ip.split(",")[0].strip() + real_ip = real_ip.split(',')[0].strip() request.META['REMOTE_ADDR'] = real_ip @@ -229,7 +246,9 @@ class FooterNoSessionMiddleware(SessionMiddleware): This will reduce the size of our session table drastically. """ - IGNORE_URLS = ['/api/v2/footer_html', '/sustainability/view', '/sustainability/click'] + IGNORE_URLS = [ + '/api/v2/footer_html', '/sustainability/view', '/sustainability/click' + ] def process_request(self, request): for url in self.IGNORE_URLS: diff --git a/readthedocs/core/mixins.py b/readthedocs/core/mixins.py index 2460daacd13..a2cf841aeda 100644 --- a/readthedocs/core/mixins.py +++ b/readthedocs/core/mixins.py @@ -1,4 +1,5 @@ -"""Common mixin classes for views""" +# -*- coding: utf-8 -*- +"""Common mixin classes for views.""" from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator @@ -7,7 +8,7 @@ class ListViewWithForm(ListView): - """List view that also exposes a create form""" + """List view that also exposes a create form.""" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) diff --git a/readthedocs/core/models.py b/readthedocs/core/models.py index 6f4f019ae44..63ac39d102d 100644 --- a/readthedocs/core/models.py +++ b/readthedocs/core/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Models for the core app.""" import logging @@ -21,7 +22,10 @@ class UserProfile(models.Model): """Additional information about a User.""" user = AutoOneToOneField( - 'auth.User', verbose_name=_('User'), related_name='profile') + 'auth.User', + verbose_name=_('User'), + related_name='profile', + ) whitelisted = models.BooleanField(_('Whitelisted'), default=False) banned = models.BooleanField(_('Banned'), default=False) homepage = models.CharField(_('Homepage'), max_length=100, blank=True) @@ -39,10 +43,13 @@ class UserProfile(models.Model): def __str__(self): return ( ugettext("%(username)s's profile") % - {'username': self.user.username}) + {'username': self.user.username} + ) def get_absolute_url(self): - return reverse('profiles_profile_detail', kwargs={'username': self.user.username}) + return reverse( + 'profiles_profile_detail', kwargs={'username': self.user.username} + ) def get_contribution_details(self): """ diff --git a/readthedocs/core/permissions.py b/readthedocs/core/permissions.py index 6af5f2a58b8..28d01bd7a90 100644 --- a/readthedocs/core/permissions.py +++ b/readthedocs/core/permissions.py @@ -1,4 +1,5 @@ -"""Objects for User permission checks""" +# -*- coding: utf-8 -*- +"""Objects for User permission checks.""" from readthedocs.core.utils.extend import SettingsOverrideObject diff --git a/readthedocs/core/resolver.py b/readthedocs/core/resolver.py index 5e0b27492aa..10b5f42d562 100644 --- a/readthedocs/core/resolver.py +++ b/readthedocs/core/resolver.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """URL resolver for documentation.""" import re @@ -49,9 +50,18 @@ class ResolverBase: /docs//projects// """ - def base_resolve_path(self, project_slug, filename, version_slug=None, - language=None, private=False, single_version=None, - subproject_slug=None, subdomain=None, cname=None): + def base_resolve_path( + self, + project_slug, + filename, + version_slug=None, + language=None, + private=False, + single_version=None, + subproject_slug=None, + subdomain=None, + cname=None, + ): """Resolve a with nothing smart, just filling in the blanks.""" # Only support `/docs/project' URLs outside our normal environment. Normally # the path should always have a subdomain or CNAME domain @@ -70,14 +80,25 @@ def base_resolve_path(self, project_slug, filename, version_slug=None, url += '{language}/{version_slug}/{filename}' return url.format( - project_slug=project_slug, filename=filename, - version_slug=version_slug, language=language, - single_version=single_version, subproject_slug=subproject_slug, + project_slug=project_slug, + filename=filename, + version_slug=version_slug, + language=language, + single_version=single_version, + subproject_slug=subproject_slug, ) - def resolve_path(self, project, filename='', version_slug=None, - language=None, single_version=None, subdomain=None, - cname=None, private=None): + def resolve_path( + self, + project, + filename='', + version_slug=None, + language=None, + single_version=None, + subdomain=None, + cname=None, + private=None, + ): """Resolve a URL with a subset of fields defined.""" cname = cname or project.domains.filter(canonical=True).first() version_slug = version_slug or project.get_default_version() @@ -136,8 +157,10 @@ def resolve_domain(self, project, private=None): return getattr(settings, 'PRODUCTION_DOMAIN') - def resolve(self, project, require_https=False, filename='', private=None, - **kwargs): + def resolve( + self, project, require_https=False, filename='', private=None, + **kwargs + ): if private is None: version_slug = kwargs.get('version_slug') if version_slug is None: @@ -171,8 +194,8 @@ def resolve(self, project, require_https=False, filename='', private=None, return '{protocol}://{domain}{path}'.format( protocol=protocol, domain=domain, - path=self.resolve_path(project, filename=filename, private=private, - **kwargs), + path=self. + resolve_path(project, filename=filename, private=private, **kwargs), ) def _get_canonical_project(self, project, projects=None): @@ -210,7 +233,7 @@ def _get_project_subdomain(self, project): if self._use_subdomain(): project = self._get_canonical_project(project) subdomain_slug = project.slug.replace('_', '-') - return "{}.{}".format(subdomain_slug, public_domain) + return '{}.{}'.format(subdomain_slug, public_domain) def _get_project_custom_domain(self, project): return project.domains.filter(canonical=True).first() @@ -221,7 +244,9 @@ def _get_private(self, project, version_slug): version = project.versions.get(slug=version_slug) private = version.privacy_level == PRIVATE except Version.DoesNotExist: - private = getattr(settings, 'DEFAULT_PRIVACY_LEVEL', PUBLIC) == PRIVATE + private = getattr( + settings, 'DEFAULT_PRIVACY_LEVEL', PUBLIC + ) == PRIVATE return private def _fix_filename(self, project, filename): @@ -239,17 +264,17 @@ def _fix_filename(self, project, filename): if filename: if filename.endswith('/') or filename.endswith('.html'): path = filename - elif project.documentation_type == "sphinx_singlehtml": - path = "index.html#document-" + filename - elif project.documentation_type in ["sphinx_htmldir", "mkdocs"]: - path = filename + "/" + elif project.documentation_type == 'sphinx_singlehtml': + path = 'index.html#document-' + filename + elif project.documentation_type in ['sphinx_htmldir', 'mkdocs']: + path = filename + '/' elif '#' in filename: # do nothing if the filename contains URL fragments path = filename else: - path = filename + ".html" + path = filename + '.html' else: - path = "" + path = '' return path def _use_custom_domain(self, custom_domain): diff --git a/readthedocs/core/signals.py b/readthedocs/core/signals.py index f8fa5f5adec..a4f029c6b3e 100644 --- a/readthedocs/core/signals.py +++ b/readthedocs/core/signals.py @@ -90,15 +90,17 @@ def delete_projects_and_organizations(sender, instance, *args, **kwargs): # https://github.com/rtfd/readthedocs.org/pull/4577 # https://docs.djangoproject.com/en/2.1/topics/db/aggregation/#order-of-annotate-and-filter-clauses # noqa projects = ( - Project.objects.annotate(num_users=Count('users')) - .filter(users=instance.id).exclude(num_users__gt=1) + Project.objects.annotate(num_users=Count('users') + ).filter(users=instance.id + ).exclude(num_users__gt=1) ) # Here we count the users list from the organization that the user belong # Then exclude the organizations where there are more than one user oauth_organizations = ( - RemoteOrganization.objects.annotate(num_users=Count('users')) - .filter(users=instance.id).exclude(num_users__gt=1) + RemoteOrganization.objects.annotate(num_users=Count('users') + ).filter(users=instance.id + ).exclude(num_users__gt=1) ) projects.delete() diff --git a/readthedocs/core/static/core/font/fontawesome-webfont.svg b/readthedocs/core/static/core/font/fontawesome-webfont.svg index 855c845e538..52c0773359b 100644 --- a/readthedocs/core/static/core/font/fontawesome-webfont.svg +++ b/readthedocs/core/static/core/font/fontawesome-webfont.svg @@ -8,7 +8,7 @@ Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/readthedocs/core/symlink.py b/readthedocs/core/symlink.py index bde0d2d454f..b43da937ee4 100644 --- a/readthedocs/core/symlink.py +++ b/readthedocs/core/symlink.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ A class that manages the symlinks for nginx to serve public files. @@ -77,10 +78,12 @@ class Symlink: def __init__(self, project): self.project = project self.project_root = os.path.join( - self.WEB_ROOT, project.slug + self.WEB_ROOT, + project.slug, ) self.subproject_root = os.path.join( - self.project_root, 'projects' + self.project_root, + 'projects', ) self.environment = LocalEnvironment(project) self.sanity_check() @@ -92,9 +95,13 @@ def sanity_check(self): This will leave it in the proper state for the single_project setting. """ if os.path.islink(self.project_root) and not self.project.single_version: - log.info(constants.LOG_TEMPLATE.format( - project=self.project.slug, version='', - msg="Removing single version symlink")) + log.info( + constants.LOG_TEMPLATE.format( + project=self.project.slug, + version='', + msg='Removing single version symlink', + ) + ) safe_unlink(self.project_root) safe_makedirs(self.project_root) elif (self.project.single_version and @@ -147,13 +154,15 @@ def symlink_cnames(self, domain=None): domains = Domain.objects.filter(project=self.project) for dom in domains: log_msg = 'Symlinking CNAME: {} -> {}'.format( - dom.domain, self.project.slug + dom.domain, + self.project.slug, ) log.info( constants.LOG_TEMPLATE.format( project=self.project.slug, - version='', msg=log_msg - ) + version='', + msg=log_msg, + ), ) # CNAME to doc root @@ -162,17 +171,26 @@ def symlink_cnames(self, domain=None): # Project symlink project_cname_symlink = os.path.join( - self.PROJECT_CNAME_ROOT, dom.domain + self.PROJECT_CNAME_ROOT, + dom.domain, ) self.environment.run( - 'ln', '-nsf', self.project.doc_path, project_cname_symlink + 'ln', + '-nsf', + self.project.doc_path, + project_cname_symlink, ) def remove_symlink_cname(self, domain): """Remove CNAME symlink.""" - log_msg = "Removing symlink for CNAME {}".format(domain.domain) - log.info(constants.LOG_TEMPLATE.format(project=self.project.slug, - version='', msg=log_msg)) + log_msg = 'Removing symlink for CNAME {}'.format(domain.domain) + log.info( + constants.LOG_TEMPLATE.format( + project=self.project.slug, + version='', + msg=log_msg, + ) + ) symlink = os.path.join(self.CNAME_ROOT, domain.domain) safe_unlink(symlink) @@ -180,8 +198,7 @@ def symlink_subprojects(self): """ Symlink project subprojects. - Link from $WEB_ROOT/projects/ -> - $WEB_ROOT/ + Link from $WEB_ROOT/projects/ -> $WEB_ROOT/ """ subprojects = set() rels = self.get_subprojects() @@ -198,12 +215,20 @@ def symlink_subprojects(self): from_to[rel.alias] = rel.child.slug subprojects.add(rel.alias) for from_slug, to_slug in list(from_to.items()): - log_msg = "Symlinking subproject: {} -> {}".format(from_slug, to_slug) - log.info(constants.LOG_TEMPLATE.format(project=self.project.slug, - version='', msg=log_msg)) + log_msg = 'Symlinking subproject: {} -> {}'.format( + from_slug, to_slug + ) + log.info( + constants.LOG_TEMPLATE.format( + project=self.project.slug, + version='', + msg=log_msg, + ) + ) symlink = os.path.join(self.subproject_root, from_slug) docs_dir = os.path.join( - self.WEB_ROOT, to_slug + self.WEB_ROOT, + to_slug, ) symlink_dir = os.sep.join(symlink.split(os.path.sep)[:-1]) if not os.path.lexists(symlink_dir): @@ -215,7 +240,8 @@ def symlink_subprojects(self): if result.exit_code > 0: log.error( 'Could not symlink path: status=%d error=%s', - result.exit_code, result.error + result.exit_code, + result.error, ) # Remove old symlinks @@ -229,7 +255,7 @@ def symlink_translations(self): Symlink project translations. Link from $WEB_ROOT/// -> - $WEB_ROOT/// + $WEB_ROOT/// """ translations = {} @@ -249,8 +275,9 @@ def symlink_translations(self): log.info( constants.LOG_TEMPLATE.format( project=self.project.slug, - version='', msg=log_msg - ) + version='', + msg=log_msg, + ), ) symlink = os.path.join(self.project_root, language) docs_dir = os.path.join(self.WEB_ROOT, slug, language) @@ -271,7 +298,7 @@ def symlink_single_version(self): Symlink project single version. Link from $WEB_ROOT/ -> - HOME/user_builds//rtd-builds/latest/ + HOME/user_builds//rtd-builds/latest/ """ version = self.get_default_version() @@ -288,7 +315,7 @@ def symlink_single_version(self): settings.DOCROOT, self.project.slug, 'rtd-builds', - version.slug + version.slug, ) self.environment.run('ln', '-nsf', docs_dir, symlink) @@ -297,11 +324,13 @@ def symlink_versions(self): Symlink project's versions. Link from $WEB_ROOT//// -> - HOME/user_builds//rtd-builds/ + HOME/user_builds//rtd-builds/ """ versions = set() version_dir = os.path.join( - self.WEB_ROOT, self.project.slug, self.project.language + self.WEB_ROOT, + self.project.slug, + self.project.language, ) # Include active public versions, # as well as public versions that are built but not active, for archived versions @@ -315,15 +344,15 @@ def symlink_versions(self): constants.LOG_TEMPLATE.format( project=self.project.slug, version='', - msg=log_msg - ) + msg=log_msg, + ), ) symlink = os.path.join(version_dir, version.slug) docs_dir = os.path.join( settings.DOCROOT, self.project.slug, 'rtd-builds', - version.slug + version.slug, ) self.environment.run('ln', '-nsf', docs_dir, symlink) versions.add(version.slug) @@ -346,11 +375,16 @@ def get_default_version(self): class PublicSymlinkBase(Symlink): CNAME_ROOT = os.path.join(settings.SITE_ROOT, 'public_cname_root') WEB_ROOT = os.path.join(settings.SITE_ROOT, 'public_web_root') - PROJECT_CNAME_ROOT = os.path.join(settings.SITE_ROOT, 'public_cname_project') + PROJECT_CNAME_ROOT = os.path.join( + settings.SITE_ROOT, 'public_cname_project' + ) def get_version_queryset(self): - return (self.project.versions.protected(only_active=False).filter(built=True) | - self.project.versions.protected(only_active=True)) + return ( + self.project.versions.protected(only_active=False + ).filter(built=True) | + self.project.versions.protected(only_active=True) + ) def get_subprojects(self): return self.project.subprojects.protected() @@ -362,11 +396,15 @@ def get_translations(self): class PrivateSymlinkBase(Symlink): CNAME_ROOT = os.path.join(settings.SITE_ROOT, 'private_cname_root') WEB_ROOT = os.path.join(settings.SITE_ROOT, 'private_web_root') - PROJECT_CNAME_ROOT = os.path.join(settings.SITE_ROOT, 'private_cname_project') + PROJECT_CNAME_ROOT = os.path.join( + settings.SITE_ROOT, 'private_cname_project' + ) def get_version_queryset(self): - return (self.project.versions.private(only_active=False).filter(built=True) | - self.project.versions.private(only_active=True)) + return ( + self.project.versions.private(only_active=False).filter(built=True) | + self.project.versions.private(only_active=True) + ) def get_subprojects(self): return self.project.subprojects.private() diff --git a/readthedocs/core/tasks.py b/readthedocs/core/tasks.py index 446a19b7ea5..d053614c5ee 100644 --- a/readthedocs/core/tasks.py +++ b/readthedocs/core/tasks.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Basic tasks.""" import logging @@ -18,8 +19,10 @@ @app.task(queue='web', time_limit=EMAIL_TIME_LIMIT) -def send_email_task(recipient, subject, template, template_html, - context=None, from_email=None, **kwargs): +def send_email_task( + recipient, subject, template, template_html, context=None, + from_email=None, **kwargs +): """ Send multipart email. @@ -43,14 +46,15 @@ def send_email_task(recipient, subject, template, template_html, """ msg = EmailMultiAlternatives( subject, - get_template(template).render(context), - from_email or settings.DEFAULT_FROM_EMAIL, - [recipient], - **kwargs + get_template(template).render(context), from_email or + settings.DEFAULT_FROM_EMAIL, + [recipient], **kwargs ) try: - msg.attach_alternative(get_template(template_html).render(context), - 'text/html') + msg.attach_alternative( + get_template(template_html).render(context), + 'text/html', + ) except TemplateDoesNotExist: pass msg.send() @@ -61,5 +65,7 @@ def send_email_task(recipient, subject, template, template_html, def clear_persistent_messages(): # Delete all expired message_extend's messages log.info("Deleting all expired message_extend's messages") - expired_messages = PersistentMessage.objects.filter(expires__lt=timezone.now()) + expired_messages = PersistentMessage.objects.filter( + expires__lt=timezone.now() + ) expired_messages.delete() diff --git a/readthedocs/core/templatetags/core_tags.py b/readthedocs/core/templatetags/core_tags.py index 304ca3aff3a..b4862e5257f 100644 --- a/readthedocs/core/templatetags/core_tags.py +++ b/readthedocs/core/templatetags/core_tags.py @@ -1,7 +1,8 @@ +# -*- coding: utf-8 -*- """Template tags for core app.""" import hashlib -from urllib.parse import urlparse, urlencode +from urllib.parse import urlencode from django import template from django.conf import settings @@ -19,23 +20,25 @@ @register.filter def gravatar(email, size=48): """ - Hacked from djangosnippets.org, but basically given an email address + Hacked from djangosnippets.org, but basically given an email address. render an img tag with the hashed up bits needed for leetness omgwtfstillreading """ - url = "http://www.gravatar.com/avatar.php?%s" % urlencode({ + url = 'http://www.gravatar.com/avatar.php?%s' % urlencode({ 'gravatar_id': hashlib.md5(email).hexdigest(), - 'size': str(size) + 'size': str(size), }) - return ('gravatar' % (url, size, size)) + return ( + 'gravatar' % (url, size, size) + ) -@register.simple_tag(name="doc_url") +@register.simple_tag(name='doc_url') def make_document_url(project, version=None, page=''): if not project: - return "" + return '' return resolve(project=project, version_slug=version, filename=page) @@ -48,7 +51,7 @@ def restructuredtext(value, short=False): if settings.DEBUG: raise template.TemplateSyntaxError( "Error in 'restructuredtext' filter: " - "The Python docutils library isn't installed." + "The Python docutils library isn't installed.", ) return force_text(value) else: @@ -56,20 +59,22 @@ def restructuredtext(value, short=False): 'raw_enabled': False, 'file_insertion_enabled': False, } - docutils_settings.update(getattr(settings, 'RESTRUCTUREDTEXT_FILTER_SETTINGS', {})) + docutils_settings.update( + getattr(settings, 'RESTRUCTUREDTEXT_FILTER_SETTINGS', {}) + ) try: parts = publish_parts( source=force_bytes(value), - writer_name="html4css1", + writer_name='html4css1', settings_overrides=docutils_settings, ) except ApplicationError: return force_text(value) - out = force_text(parts["fragment"]) + out = force_text(parts['fragment']) try: if short: - out = out.split("\n")[0] + out = out.split('\n')[0] except IndexError: pass return mark_safe(out) diff --git a/readthedocs/core/templatetags/privacy_tags.py b/readthedocs/core/templatetags/privacy_tags.py index 814018dc9df..fbbe9804140 100644 --- a/readthedocs/core/templatetags/privacy_tags.py +++ b/readthedocs/core/templatetags/privacy_tags.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Template tags to query projects by privacy.""" from django import template @@ -16,6 +17,8 @@ def is_admin(user, project): @register.simple_tag(takes_context=True) def get_public_projects(context, user): - projects = Project.objects.for_user_and_viewer(user=user, viewer=context['request'].user) + projects = Project.objects.for_user_and_viewer( + user=user, viewer=context['request'].user + ) context['public_projects'] = projects return '' diff --git a/readthedocs/core/urls/__init__.py b/readthedocs/core/urls/__init__.py index 48b2e9a614a..4c0951e9cf1 100644 --- a/readthedocs/core/urls/__init__.py +++ b/readthedocs/core/urls/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """URL configuration for core app.""" from __future__ import absolute_import @@ -8,53 +9,77 @@ from readthedocs.core.views import hooks, serve from readthedocs.projects.feeds import LatestProjectsFeed, NewProjectsFeed - docs_urls = [ - url((r'^docs/(?P{project_slug})/page/' - r'(?P{filename_slug})$'.format(**pattern_opts)), + url( + ( + r'^docs/(?P{project_slug})/page/' + r'(?P{filename_slug})$'.format(**pattern_opts) + ), serve.redirect_page_with_filename, - name='docs_detail'), - - url((r'^docs/(?P{project_slug})/' - r'(?:|projects/(?P{project_slug})/)$'.format(**pattern_opts)), + name='docs_detail', + ), + url( + ( + r'^docs/(?P{project_slug})/' + r'(?:|projects/(?P{project_slug})/)$'.format( + **pattern_opts + ) + ), serve.redirect_project_slug, - name='docs_detail'), - - url((r'^docs/(?P{project_slug})/' - r'(?:|projects/(?P{project_slug})/)' - r'(?P{lang_slug})/' - r'(?P{version_slug})/' - r'(?P{filename_slug})'.format(**pattern_opts)), + name='docs_detail', + ), + url( + ( + r'^docs/(?P{project_slug})/' + r'(?:|projects/(?P{project_slug})/)' + r'(?P{lang_slug})/' + r'(?P{version_slug})/' + r'(?P{filename_slug})'.format(**pattern_opts) + ), serve.serve_docs, - name='docs_detail'), + name='docs_detail', + ), ] - core_urls = [ # Hooks url(r'^github', hooks.github_build, name='github_build'), url(r'^gitlab', hooks.gitlab_build, name='gitlab_build'), url(r'^bitbucket', hooks.bitbucket_build, name='bitbucket_build'), - url((r'^build/' - r'(?P{project_slug})'.format(**pattern_opts)), + url( + ( + r'^build/' + r'(?P{project_slug})'.format(**pattern_opts) + ), hooks.generic_build, - name='generic_build'), + name='generic_build', + ), # Random other stuff - url(r'^random/(?P{project_slug})'.format(**pattern_opts), + url( + r'^random/(?P{project_slug})'.format(**pattern_opts), views.random_page, - name='random_page'), + name='random_page', + ), url(r'^random/$', views.random_page, name='random_page'), - url((r'^wipe/(?P{project_slug})/' - r'(?P{version_slug})/$'.format(**pattern_opts)), + url( + ( + r'^wipe/(?P{project_slug})/' + r'(?P{version_slug})/$'.format(**pattern_opts) + ), views.wipe_version, - name='wipe_version'), + name='wipe_version', + ), ] deprecated_urls = [ - url(r'^feeds/new/$', + url( + r'^feeds/new/$', NewProjectsFeed(), - name="new_feed"), - url(r'^feeds/latest/$', + name='new_feed', + ), + url( + r'^feeds/latest/$', LatestProjectsFeed(), - name="latest_feed"), + name='latest_feed', + ), ] diff --git a/readthedocs/core/urls/single_version.py b/readthedocs/core/urls/single_version.py index b232ef9ae96..f8d33a761c7 100644 --- a/readthedocs/core/urls/single_version.py +++ b/readthedocs/core/urls/single_version.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """URL configuration for a single version.""" from functools import reduce from operator import add @@ -14,33 +15,43 @@ handler404 = 'readthedocs.core.views.server_error_404' single_version_urls = [ - url(r'^(?:|projects/(?P{project_slug})/)' + url( + r'^(?:|projects/(?P{project_slug})/)' r'page/(?P.*)$'.format(**pattern_opts), serve.redirect_page_with_filename, - name='docs_detail'), - - url((r'^(?:|projects/(?P{project_slug})/)' - r'(?P{filename_slug})$'.format(**pattern_opts)), + name='docs_detail', + ), + url( + ( + r'^(?:|projects/(?P{project_slug})/)' + r'(?P{filename_slug})$'.format(**pattern_opts) + ), serve.serve_docs, - name='docs_detail'), + name='docs_detail', + ), ] groups = [single_version_urls] # Needed to serve media locally if getattr(settings, 'DEBUG', False): - groups.insert(0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)) + groups.insert( + 0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + ) # Allow `/docs/` URL's when not using subdomains or during local dev if not getattr(settings, 'USE_SUBDOMAIN', False) or settings.DEBUG: docs_url = [ - url((r'^docs/(?P[-\w]+)/' - r'(?:|projects/(?P{project_slug})/)' - r'(?P{filename_slug})$'.format(**pattern_opts)), + url( + ( + r'^docs/(?P[-\w]+)/' + r'(?:|projects/(?P{project_slug})/)' + r'(?P{filename_slug})$'.format(**pattern_opts) + ), serve.serve_docs, - name='docs_detail') + name='docs_detail', + ), ] groups.insert(1, docs_url) - urlpatterns = reduce(add, groups) diff --git a/readthedocs/core/urls/subdomain.py b/readthedocs/core/urls/subdomain.py index 7b5b752fc88..cddc49a98a5 100644 --- a/readthedocs/core/urls/subdomain.py +++ b/readthedocs/core/urls/subdomain.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """URL configurations for subdomains.""" from functools import reduce from operator import add @@ -19,27 +20,37 @@ handler404 = server_error_404 subdomain_urls = [ - url(r'^(?:|projects/(?P{project_slug})/)' + url( + r'^(?:|projects/(?P{project_slug})/)' r'page/(?P.*)$'.format(**pattern_opts), redirect_page_with_filename, - name='docs_detail'), - - url((r'^(?:|projects/(?P{project_slug})/)$').format(**pattern_opts), + name='docs_detail', + ), + url( + (r'^(?:|projects/(?P{project_slug})/)$').format( + **pattern_opts + ), redirect_project_slug, - name='redirect_project_slug'), - - url((r'^(?:|projects/(?P{project_slug})/)' - r'(?P{lang_slug})/' - r'(?P{version_slug})/' - r'(?P{filename_slug})$'.format(**pattern_opts)), + name='redirect_project_slug', + ), + url( + ( + r'^(?:|projects/(?P{project_slug})/)' + r'(?P{lang_slug})/' + r'(?P{version_slug})/' + r'(?P{filename_slug})$'.format(**pattern_opts) + ), serve_docs, - name='docs_detail'), + name='docs_detail', + ), ] groups = [subdomain_urls] # Needed to serve media locally if getattr(settings, 'DEBUG', False): - groups.insert(0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)) + groups.insert( + 0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + ) urlpatterns = reduce(add, groups) diff --git a/readthedocs/core/utils/__init__.py b/readthedocs/core/utils/__init__.py index 8632b1557d0..2123ce5a326 100644 --- a/readthedocs/core/utils/__init__.py +++ b/readthedocs/core/utils/__init__.py @@ -19,7 +19,6 @@ from readthedocs.builds.constants import LATEST, BUILD_STATE_TRIGGERED from readthedocs.doc_builder.constants import DOCKER_LIMITS - log = logging.getLogger(__name__) SYNC_USER = getattr(settings, 'SYNC_USER', getpass.getuser()) diff --git a/readthedocs/core/utils/extend.py b/readthedocs/core/utils/extend.py index ca8e3f40b2e..96319e691a8 100644 --- a/readthedocs/core/utils/extend.py +++ b/readthedocs/core/utils/extend.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Patterns for extending Read the Docs.""" import inspect @@ -19,7 +20,7 @@ def get_override_class(proxy_class, default_class=None): default_class = getattr(proxy_class, '_default_class') class_id = '.'.join([ inspect.getmodule(proxy_class).__name__, - proxy_class.__name__ + proxy_class.__name__, ]) class_path = getattr(settings, 'CLASS_OVERRIDES', {}).get(class_id) # pylint: disable=protected-access @@ -32,7 +33,8 @@ def get_override_class(proxy_class, default_class=None): class SettingsOverrideMeta(type): - """Meta class for passing along classmethod class to the underlying class.""" # noqa + """Meta class for passing along classmethod class to the underlying + class.""" # noqa def __getattr__(cls, attr): # noqa: pep8 false positive proxy_class = get_override_class(cls, getattr(cls, '_default_class')) diff --git a/readthedocs/core/utils/tasks/__init__.py b/readthedocs/core/utils/tasks/__init__.py index 344215036f9..492708ad57f 100644 --- a/readthedocs/core/utils/tasks/__init__.py +++ b/readthedocs/core/utils/tasks/__init__.py @@ -1,4 +1,5 @@ -"""Common task exports""" +# -*- coding: utf-8 -*- +"""Common task exports.""" from .permission_checks import user_id_matches # noqa for unused import from .public import PublicTask # noqa diff --git a/readthedocs/core/utils/tasks/permission_checks.py b/readthedocs/core/utils/tasks/permission_checks.py index 1643e866015..4a32e6a1a34 100644 --- a/readthedocs/core/utils/tasks/permission_checks.py +++ b/readthedocs/core/utils/tasks/permission_checks.py @@ -1,4 +1,5 @@ -"""Permission checks for tasks""" +# -*- coding: utf-8 -*- +"""Permission checks for tasks.""" __all__ = ('user_id_matches',) diff --git a/readthedocs/core/utils/tasks/public.py b/readthedocs/core/utils/tasks/public.py index 2c1ccaa9e0d..2f0bb011430 100644 --- a/readthedocs/core/utils/tasks/public.py +++ b/readthedocs/core/utils/tasks/public.py @@ -1,4 +1,5 @@ -"""Celery tasks with publicly viewable status""" +# -*- coding: utf-8 -*- +"""Celery tasks with publicly viewable status.""" from celery import Task, states from django.conf import settings @@ -7,10 +8,11 @@ __all__ = ( - 'PublicTask', 'TaskNoPermission', 'get_public_task_data' + 'PublicTask', + 'TaskNoPermission', + 'get_public_task_data', ) - STATUS_UPDATES_ENABLED = not getattr(settings, 'CELERY_ALWAYS_EAGER', False) @@ -45,7 +47,7 @@ def update_progress_data(self): def set_permission_context(self, context): """ - Set data that can be used by ``check_permission`` to authorize a + Set data that can be used by ``check_permission`` to authorize a. request for the this task. By default it will be the ``kwargs`` passed into the task. @@ -103,22 +105,26 @@ def permission_check(check): def my_public_task(user_id): pass """ + def decorator(func): func.check_permission = check return func + return decorator class TaskNoPermission(Exception): + def __init__(self, task_id, *args, **kwargs): message = 'No permission to access task with id {id}'.format( - id=task_id) + id=task_id, + ) super().__init__(message, *args, **kwargs) def get_public_task_data(request, task_id): """ - Return task details as tuple + Return task details as tuple. Will raise `TaskNoPermission` if `request` has no permission to access info of the task with id `task_id`. This is also the case of no task with the diff --git a/readthedocs/core/utils/tasks/retrieve.py b/readthedocs/core/utils/tasks/retrieve.py index 4c592116995..a2b008d32dd 100644 --- a/readthedocs/core/utils/tasks/retrieve.py +++ b/readthedocs/core/utils/tasks/retrieve.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Utilities for retrieving task data.""" from celery import states @@ -8,6 +9,7 @@ class TaskNotFound(Exception): + def __init__(self, task_id, *args, **kwargs): message = 'No public task found with id {id}'.format(id=task_id) super().__init__(message, *args, **kwargs) @@ -15,7 +17,7 @@ def __init__(self, task_id, *args, **kwargs): def get_task_data(task_id): """ - Will raise `TaskNotFound` if the task is in state ``PENDING`` or the task + Will raise `TaskNotFound` if the task is in state ``PENDING`` or the task. meta data has no ``'task_name'`` key set. """ diff --git a/readthedocs/core/views/__init__.py b/readthedocs/core/views/__init__.py index 85aec548c42..597c2ac6263 100644 --- a/readthedocs/core/views/__init__.py +++ b/readthedocs/core/views/__init__.py @@ -133,13 +133,15 @@ def do_not_track(request): dnt_header = request.META.get('HTTP_DNT') # https://w3c.github.io/dnt/drafts/tracking-dnt.html#status-representation - return JsonResponse({ # pylint: disable=redundant-content-type-for-json-response - 'policy': 'https://docs.readthedocs.io/en/latest/privacy-policy.html', - 'same-party': [ - 'readthedocs.org', - 'readthedocs.com', - 'readthedocs.io', # .org Documentation Sites - 'readthedocs-hosted.com', # .com Documentation Sites - ], - 'tracking': 'N' if dnt_header == '1' else 'T', - }, content_type='application/tracking-status+json') + return JsonResponse( + { # pylint: disable=redundant-content-type-for-json-response + 'policy': 'https://docs.readthedocs.io/en/latest/privacy-policy.html', + 'same-party': [ + 'readthedocs.org', + 'readthedocs.com', + 'readthedocs.io', # .org Documentation Sites + 'readthedocs-hosted.com', # .com Documentation Sites + ], + 'tracking': 'N' if dnt_header == '1' else 'T', + }, content_type='application/tracking-status+json', + ) diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index a92640e127b..16213e24bb6 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Views pertaining to builds.""" import json @@ -41,13 +42,14 @@ def _build_version(project, slug, already_built=()): version = project.versions.filter(active=True, slug=slug).first() if version and slug not in already_built: log.info( - "(Version build) Building %s:%s", - project.slug, version.slug, + '(Version build) Building %s:%s', + project.slug, + version.slug, ) trigger_build(project=project, version=version, force=True) return slug - log.info("(Version build) Not Building %s", slug) + log.info('(Version build) Not Building %s', slug) return None @@ -64,8 +66,11 @@ def build_branches(project, branch_list): for branch in branch_list: versions = project.versions_from_branch_name(branch) for version in versions: - log.info("(Branch Build) Processing %s:%s", - project.slug, version.slug) + log.info( + '(Branch Build) Processing %s:%s', + project.slug, + version.slug, + ) ret = _build_version(project, version.slug, already_built=to_build) if ret: to_build.add(ret) @@ -89,9 +94,7 @@ def sync_versions(project): try: version_identifier = project.get_default_branch() version = ( - project.versions - .filter(identifier=version_identifier) - .first() + project.versions.filter(identifier=version_identifier).first() ) if not version: log.info('Unable to sync from %s version', version_identifier) @@ -114,10 +117,13 @@ def get_project_from_url(url): def log_info(project, msg): - log.info(constants.LOG_TEMPLATE - .format(project=project, - version='', - msg=msg)) + log.info( + constants.LOG_TEMPLATE.format( + project=project, + version='', + msg=msg, + ) + ) def _build_url(url, projects, branches): @@ -127,7 +133,7 @@ def _build_url(url, projects, branches): Check each of the ``branches`` to see if they are active and should be built. """ - ret = "" + ret = '' all_built = {} all_not_building = {} @@ -151,14 +157,18 @@ def _build_url(url, projects, branches): for project_slug, built in list(all_built.items()): if built: msg = '(URL Build) Build Started: {} [{}]'.format( - url, ' '.join(built)) + url, + ' '.join(built), + ) log_info(project_slug, msg=msg) ret += msg for project_slug, not_building in list(all_not_building.items()): if not_building: msg = '(URL Build) Not Building: {} [{}]'.format( - url, ' '.join(not_building)) + url, + ' '.join(not_building), + ) log_info(project_slug, msg=msg) ret += msg @@ -192,7 +202,8 @@ def github_build(request): # noqa: D205 else: data = json.loads(request.body) http_url = data['repository']['url'] - http_search_url = http_url.replace('http://', '').replace('https://', '') + http_search_url = http_url.replace('http://', + '').replace('https://', '') ssh_url = data['repository']['ssh_url'] ssh_search_url = ssh_url.replace('git@', '').replace('.git', '') branches = [data['ref'].replace('refs/heads/', '')] @@ -205,14 +216,14 @@ def github_build(request): # noqa: D205 log.info( 'GitHub webhook search: url=%s branches=%s', http_search_url, - branches + branches, ) ssh_projects = get_project_from_url(ssh_search_url) if ssh_projects: log.info( 'GitHub webhook search: url=%s branches=%s', ssh_search_url, - branches + branches, ) projects = repo_projects | ssh_projects return _build_url(http_search_url, projects, branches) @@ -287,24 +298,26 @@ def bitbucket_build(request): else: data = json.loads(request.body) - version = 2 if request.META.get('HTTP_USER_AGENT') == 'Bitbucket-Webhooks/2.0' else 1 + version = 2 if request.META.get( + 'HTTP_USER_AGENT' + ) == 'Bitbucket-Webhooks/2.0' else 1 if version == 1: - branches = [commit.get('branch', '') - for commit in data['commits']] + branches = [ + commit.get('branch', '') for commit in data['commits'] + ] repository = data['repository'] if not repository['absolute_url']: return HttpResponse('Invalid request', status=400) search_url = 'bitbucket.org{}'.format( - repository['absolute_url'].rstrip('/') + repository['absolute_url'].rstrip('/'), ) elif version == 2: changes = data['push']['changes'] - branches = [change['new']['name'] - for change in changes] + branches = [change['new']['name'] for change in changes] if not data['repository']['full_name']: return HttpResponse('Invalid request', status=400) search_url = 'bitbucket.org/{}'.format( - data['repository']['full_name'] + data['repository']['full_name'], ) except (TypeError, ValueError, KeyError): log.exception('Invalid Bitbucket webhook payload') @@ -352,10 +365,12 @@ def generic_build(request, project_id_or_slug=None): project = Project.objects.get(slug=project_id_or_slug) except (Project.DoesNotExist, ValueError): log.exception( - "(Incoming Generic Build) Repo not found: %s", - project_id_or_slug) + '(Incoming Generic Build) Repo not found: %s', + project_id_or_slug, + ) return HttpResponseNotFound( - 'Repo not found: %s' % project_id_or_slug) + 'Repo not found: %s' % project_id_or_slug, + ) # This endpoint doesn't require authorization, we shouldn't allow builds to # be triggered from this any longer. Deprecation plan is to selectively # allow access to this endpoint for now. @@ -364,8 +379,11 @@ def generic_build(request, project_id_or_slug=None): if request.method == 'POST': slug = request.POST.get('version_slug', project.default_version) log.info( - "(Incoming Generic Build) %s [%s]", project.slug, slug) + '(Incoming Generic Build) %s [%s]', + project.slug, + slug, + ) _build_version(project, slug) else: - return HttpResponse("You must POST to this resource.") + return HttpResponse('You must POST to this resource.') return redirect('builds_project_list', project.slug) diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 7f8bb3651aa..9f87d82abf7 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Doc serving from Python. @@ -54,8 +55,11 @@ def map_subproject_slug(view_func): .. warning:: Does not take into account any kind of privacy settings. """ + @wraps(view_func) - def inner_view(request, subproject=None, subproject_slug=None, *args, **kwargs): # noqa + def inner_view( + request, subproject=None, subproject_slug=None, *args, **kwargs + ): # noqa if subproject is None and subproject_slug: # Try to fetch by subproject alias first, otherwise we might end up # redirected to an unrelated project. @@ -81,8 +85,11 @@ def map_project_slug(view_func): .. warning:: Does not take into account any kind of privacy settings. """ + @wraps(view_func) - def inner_view(request, project=None, project_slug=None, *args, **kwargs): # noqa + def inner_view( + request, project=None, project_slug=None, *args, **kwargs + ): # noqa if project is None: if not project_slug: project_slug = request.slug @@ -107,7 +114,8 @@ def redirect_project_slug(request, project, subproject): # pylint: disable=unus def redirect_page_with_filename(request, project, subproject, filename): # pylint: disable=unused-argument # noqa """Redirect /page/file.html to /en/latest/file.html.""" return HttpResponseRedirect( - resolve(subproject or project, filename=filename)) + resolve(subproject or project, filename=filename), + ) def _serve_401(request, project): @@ -125,7 +133,8 @@ def _serve_file(request, filename, basepath): # Serve from Nginx content_type, encoding = mimetypes.guess_type( - os.path.join(basepath, filename)) + os.path.join(basepath, filename), + ) content_type = content_type or 'application/octet-stream' response = HttpResponse(content_type=content_type) if encoding: @@ -144,9 +153,15 @@ def _serve_file(request, filename, basepath): @map_project_slug @map_subproject_slug def serve_docs( - request, project, subproject, lang_slug=None, version_slug=None, - filename=''): - """Exists to map existing proj, lang, version, filename views to the file format.""" + request, + project, + subproject, + lang_slug=None, + version_slug=None, + filename='', +): + """Exists to map existing proj, lang, version, filename views to the file + format.""" if not version_slug: version_slug = project.get_default_version() try: @@ -211,4 +226,5 @@ def _serve_symlink_docs(request, project, privacy_level, filename=''): files_tried.append(os.path.join(basepath, filename)) raise Http404( - 'File not found. Tried these files: %s' % ','.join(files_tried)) + 'File not found. Tried these files: %s' % ','.join(files_tried), + ) diff --git a/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py index 65b35f5f28f..805eb0ce5d3 100644 --- a/readthedocs/doc_builder/backends/mkdocs.py +++ b/readthedocs/doc_builder/backends/mkdocs.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ MkDocs_ backend for building docs. @@ -45,7 +46,8 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.old_artifact_path = os.path.join( self.version.project.checkout_path(self.version.slug), - self.build_dir) + self.build_dir, + ) self.root_path = self.version.project.checkout_path(self.version.slug) self.yaml_file = self.get_yaml_config() @@ -65,14 +67,13 @@ def __init__(self, *args, **kwargs): else: self.DEFAULT_THEME_NAME = 'mkdocs' - def get_yaml_config(self): """Find the ``mkdocs.yml`` file in the project root.""" mkdoc_path = self.config.mkdocs.configuration if not mkdoc_path: mkdoc_path = os.path.join( self.project.checkout_path(self.version.slug), - 'mkdocs.yml' + 'mkdocs.yml', ) if not os.path.exists(mkdoc_path): return None @@ -85,9 +86,7 @@ def load_yaml_config(self): Raise BuildEnvironmentError if failed due to syntax errors. """ try: - return yaml.safe_load( - open(self.yaml_file, 'r') - ) + return yaml.safe_load(open(self.yaml_file, 'r'),) except IOError: return { 'site_name': self.version.project.name, @@ -96,10 +95,12 @@ def load_yaml_config(self): note = '' if hasattr(exc, 'problem_mark'): mark = exc.problem_mark - note = ' (line %d, column %d)' % (mark.line + 1, mark.column + 1) + note = ' (line %d, column %d)' % ( + mark.line + 1, mark.column + 1 + ) raise MkDocsYAMLParseError( 'Your mkdocs.yml could not be loaded, ' - 'possibly due to a syntax error{note}'.format(note=note) + 'possibly due to a syntax error{note}'.format(note=note), ) def append_conf(self, **__): @@ -131,13 +132,13 @@ def append_conf(self, **__): # of the mkdocs configuration file. docs_path = os.path.join( os.path.dirname(self.yaml_file), - docs_dir + docs_dir, ) # RTD javascript writing rtd_data = self.generate_rtd_data( docs_dir=os.path.relpath(docs_path, self.root_path), - mkdocs_config=user_config + mkdocs_config=user_config, ) with open(os.path.join(docs_path, 'readthedocs-data.js'), 'w') as f: f.write(rtd_data) @@ -156,7 +157,7 @@ def append_conf(self, **__): # Write the modified mkdocs configuration yaml.safe_dump( user_config, - open(self.yaml_file, 'w') + open(self.yaml_file, 'w'), ) # Write the mkdocs.yml to the build logs @@ -183,13 +184,17 @@ def generate_rtd_data(self, docs_dir, mkdocs_config): 'programming_language': self.version.project.programming_language, 'page': None, 'theme': self.get_theme_name(mkdocs_config), - 'builder': "mkdocs", + 'builder': 'mkdocs', 'docroot': docs_dir, - 'source_suffix': ".md", - 'api_host': getattr(settings, 'PUBLIC_API_URL', 'https://readthedocs.org'), + 'source_suffix': '.md', + 'api_host': getattr( + settings, 'PUBLIC_API_URL', 'https://readthedocs.org' + ), 'ad_free': not self.project.show_advertising, 'commit': self.version.project.vcs_repo(self.version.slug).commit, - 'global_analytics_code': getattr(settings, 'GLOBAL_ANALYTICS_CODE', 'UA-17997319-1'), + 'global_analytics_code': getattr( + settings, 'GLOBAL_ANALYTICS_CODE', 'UA-17997319-1' + ), 'user_analytics_code': analytics_code, } data_json = json.dumps(readthedocs_data, indent=4) @@ -210,21 +215,22 @@ def build(self): self.python_env.venv_bin(filename='mkdocs'), self.builder, '--clean', - '--site-dir', self.build_dir, - '--config-file', self.yaml_file, + '--site-dir', + self.build_dir, + '--config-file', + self.yaml_file, ] if self.config.mkdocs.fail_on_warning: build_command.append('--strict') cmd_ret = self.run( - *build_command, - cwd=checkout_path, + *build_command, cwd=checkout_path, bin_path=self.python_env.venv_bin() ) return cmd_ret.successful def get_theme_name(self, mkdocs_config): """ - Get the theme configuration in the mkdocs_config + Get the theme configuration in the mkdocs_config. In v0.17.0, the theme configuration switched from two separate configs (both optional) to a nested directive. diff --git a/readthedocs/doc_builder/backends/sphinx.py b/readthedocs/doc_builder/backends/sphinx.py index 8402cfd7e1c..b656433e527 100644 --- a/readthedocs/doc_builder/backends/sphinx.py +++ b/readthedocs/doc_builder/backends/sphinx.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Sphinx_ backend for building docs. @@ -44,7 +45,7 @@ def __init__(self, *args, **kwargs): self.config_file = self.project.conf_file(self.version.slug) self.old_artifact_path = os.path.join( os.path.dirname(self.config_file), - self.sphinx_build_dir + self.sphinx_build_dir, ) except ProjectConfigurationError: docs_dir = self.docs_dir() @@ -57,11 +58,13 @@ def _write_config(self, master_doc='index'): """Create ``conf.py`` if it doesn't exist.""" docs_dir = self.docs_dir() conf_template = render_to_string( - 'sphinx/conf.py.conf', { + 'sphinx/conf.py.conf', + { 'project': self.project, 'version': self.version, 'master_doc': master_doc, - }) + }, + ) conf_file = os.path.join(docs_dir, 'conf.py') safe_write(conf_file, conf_template) @@ -73,25 +76,28 @@ def get_config_params(self): os.path.dirname( os.path.relpath( self.config_file, - self.project.checkout_path(self.version.slug) - ) + self.project.checkout_path(self.version.slug), + ), ), '', ) remote_version = self.version.commit_name github_user, github_repo = version_utils.get_github_username_repo( - url=self.project.repo) + url=self.project.repo, + ) github_version_is_editable = (self.version.type == 'branch') display_github = github_user is not None bitbucket_user, bitbucket_repo = version_utils.get_bitbucket_username_repo( # noqa - url=self.project.repo) + url=self.project.repo, + ) bitbucket_version_is_editable = (self.version.type == 'branch') display_bitbucket = bitbucket_user is not None gitlab_user, gitlab_repo = version_utils.get_gitlab_username_repo( - url=self.project.repo) + url=self.project.repo, + ) gitlab_version_is_editable = (self.version.type == 'branch') display_gitlab = gitlab_user is not None @@ -143,7 +149,7 @@ def get_config_params(self): # Features 'dont_overwrite_sphinx_context': self.project.has_feature( - Feature.DONT_OVERWRITE_SPHINX_CONTEXT + Feature.DONT_OVERWRITE_SPHINX_CONTEXT, ), } @@ -156,22 +162,22 @@ def get_config_params(self): return data def append_conf(self, **__): - """Find or create a ``conf.py`` with a rendered ``doc_builder/conf.py.tmpl`` appended""" + """Find or create a ``conf.py`` with a rendered + ``doc_builder/conf.py.tmpl`` appended.""" if self.config_file is None: master_doc = self.create_index(extension='rst') self._write_config(master_doc=master_doc) try: self.config_file = ( - self.config_file or - self.project.conf_file(self.version.slug) + self.config_file or self.project.conf_file(self.version.slug) ) outfile = codecs.open(self.config_file, encoding='utf-8', mode='a') except (ProjectConfigurationError, IOError): trace = sys.exc_info()[2] raise ProjectConfigurationError( - ProjectConfigurationError.NOT_FOUND - ).with_traceback(trace) + ProjectConfigurationError.NOT_FOUND, + ).with_traceback(trace) # Append config to project conf file tmpl = template_loader.get_template('doc_builder/conf.py.tmpl') @@ -215,8 +221,7 @@ def build(self): self.sphinx_build_dir, ]) cmd_ret = self.run( - *build_command, - cwd=os.path.dirname(self.config_file), + *build_command, cwd=os.path.dirname(self.config_file), bin_path=self.python_env.venv_bin() ) return cmd_ret.successful @@ -235,10 +240,11 @@ def move(self, **__): # Copy JSON artifacts to its own directory # to keep compatibility with the older builder. json_path = os.path.abspath( - os.path.join(self.old_artifact_path, '..', 'json') + os.path.join(self.old_artifact_path, '..', 'json'), ) json_path_target = self.project.artifact_path( - version=self.version.slug, type_='sphinx_search' + version=self.version.slug, + type_='sphinx_search', ) if os.path.exists(json_path): if os.path.exists(json_path_target): @@ -246,12 +252,10 @@ def move(self, **__): log.info('Copying json on the local filesystem') shutil.copytree( json_path, - json_path_target + json_path_target, ) else: - log.warning( - 'Not moving json because the build dir is unknown.' - ) + log.warning('Not moving json because the build dir is unknown.',) class HtmlDirBuilder(HtmlBuilder): @@ -297,7 +301,8 @@ def move(self, **__): filename=to_write, arcname=os.path.join( '{}-{}'.format(self.project.slug, self.version.slug), - to_write), + to_write, + ), ) archive.close() @@ -386,11 +391,16 @@ def build(self): # Run LaTeX -> PDF conversions pdflatex_cmds = [ ['pdflatex', '-interaction=nonstopmode', tex_file] - for tex_file in tex_files] # yapf: disable + for tex_file in tex_files + ] # yapf: disable makeindex_cmds = [ - ['makeindex', '-s', 'python.ist', '{}.idx'.format( - os.path.splitext(os.path.relpath(tex_file, latex_cwd))[0])] - for tex_file in tex_files] # yapf: disable + [ + 'makeindex', '-s', 'python.ist', '{}.idx'.format( + os.path.splitext(os.path.relpath(tex_file, latex_cwd))[0], + ), + ] + for tex_file in tex_files + ] # yapf: disable if self.build_env.command_class == DockerBuildCommand: latex_class = DockerLatexBuildCommand @@ -399,15 +409,27 @@ def build(self): pdf_commands = [] for cmd in pdflatex_cmds: cmd_ret = self.build_env.run_command_class( - cls=latex_class, cmd=cmd, cwd=latex_cwd, warn_only=True) + cls=latex_class, + cmd=cmd, + cwd=latex_cwd, + warn_only=True, + ) pdf_commands.append(cmd_ret) for cmd in makeindex_cmds: cmd_ret = self.build_env.run_command_class( - cls=latex_class, cmd=cmd, cwd=latex_cwd, warn_only=True) + cls=latex_class, + cmd=cmd, + cwd=latex_cwd, + warn_only=True, + ) pdf_commands.append(cmd_ret) for cmd in pdflatex_cmds: cmd_ret = self.build_env.run_command_class( - cls=latex_class, cmd=cmd, cwd=latex_cwd, warn_only=True) + cls=latex_class, + cmd=cmd, + cwd=latex_cwd, + warn_only=True, + ) pdf_match = PDF_RE.search(cmd_ret.output) if pdf_match: self.pdf_file_name = pdf_match.group(1).strip() @@ -441,7 +463,9 @@ def move(self, **__): from_file = None if from_file: to_file = os.path.join( - self.target, '{}.pdf'.format(self.project.slug)) + self.target, + '{}.pdf'.format(self.project.slug), + ) self.run( 'mv', '-f', diff --git a/readthedocs/doc_builder/base.py b/readthedocs/doc_builder/base.py index 8810ab5f782..6b143ca8db9 100644 --- a/readthedocs/doc_builder/base.py +++ b/readthedocs/doc_builder/base.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Base classes for Builders.""" import logging @@ -46,7 +47,9 @@ def __init__(self, build_env, python_env, force=False): self.config = python_env.config if python_env else None self._force = force self.target = self.project.artifact_path( - version=self.version.slug, type_=self.type) + version=self.version.slug, + type_=self.type, + ) def force(self, **__): """An optional step to force a build even when nothing has changed.""" @@ -67,7 +70,7 @@ def move(self, **__): shutil.copytree( self.old_artifact_path, self.target, - ignore=shutil.ignore_patterns(*self.ignore_patterns) + ignore=shutil.ignore_patterns(*self.ignore_patterns), ) else: log.warning('Not moving docs, because the build dir is unknown.') @@ -96,10 +99,14 @@ def create_index(self, extension='md', **__): docs_dir = self.docs_dir() index_filename = os.path.join( - docs_dir, 'index.{ext}'.format(ext=extension)) + docs_dir, + 'index.{ext}'.format(ext=extension), + ) if not os.path.exists(index_filename): readme_filename = os.path.join( - docs_dir, 'README.{ext}'.format(ext=extension)) + docs_dir, + 'README.{ext}'.format(ext=extension), + ) if os.path.exists(readme_filename): return 'README' diff --git a/readthedocs/doc_builder/config.py b/readthedocs/doc_builder/config.py index 9eabeaa0ce0..092939d9629 100644 --- a/readthedocs/doc_builder/config.py +++ b/readthedocs/doc_builder/config.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """An API to load config from a readthedocs.yml file.""" from os import path @@ -30,7 +31,7 @@ def load_yaml_config(version): try: sphinx_configuration = path.join( version.get_conf_py_path(), - 'conf.py' + 'conf.py', ) except ProjectConfigurationError: sphinx_configuration = None @@ -51,7 +52,7 @@ def load_yaml_config(version): 'sphinx_configuration': sphinx_configuration, 'build_image': project.container_image, 'doctype': project.documentation_type, - } + }, } img_settings = DOCKER_IMAGE_SETTINGS.get(img_name, None) if img_settings: diff --git a/readthedocs/doc_builder/constants.py b/readthedocs/doc_builder/constants.py index f2f15010bba..1abacebba0a 100644 --- a/readthedocs/doc_builder/constants.py +++ b/readthedocs/doc_builder/constants.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Doc build constants.""" import logging @@ -31,7 +32,9 @@ old_config = getattr(settings, 'DOCKER_BUILD_IMAGES', None) if old_config: - log.warning('Old config detected, DOCKER_BUILD_IMAGES->DOCKER_IMAGE_SETTINGS') + log.warning( + 'Old config detected, DOCKER_BUILD_IMAGES->DOCKER_IMAGE_SETTINGS' + ) DOCKER_IMAGE_SETTINGS.update(old_config) DOCKER_LIMITS = {'memory': '200m', 'time': 600} diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py index cbdff522d92..9276e187a7d 100644 --- a/readthedocs/doc_builder/environments.py +++ b/readthedocs/doc_builder/environments.py @@ -84,9 +84,19 @@ class BuildCommand(BuildCommandResultMixin): :param description: a more grokable description of the command being run """ - def __init__(self, command, cwd=None, shell=False, environment=None, - combine_output=True, input_data=None, build_env=None, - bin_path=None, description=None, record_as_success=False): + def __init__( + self, + command, + cwd=None, + shell=False, + environment=None, + combine_output=True, + input_data=None, + build_env=None, + bin_path=None, + description=None, + record_as_success=False, + ): self.command = command self.shell = shell if cwd is None: @@ -301,11 +311,16 @@ def run(self): # nicer. Sometimes the kernel kills the command and Docker doesn't # not use the specific exit code, so we check if the word `Killed` # is in the last 15 lines of the command's output - killed_in_output = 'Killed' in '\n'.join(self.output.splitlines()[-15:]) - if self.exit_code == DOCKER_OOM_EXIT_CODE or (self.exit_code == 1 and killed_in_output): - self.output += str(_( - '\n\nCommand killed due to excessive memory consumption\n' - )) + killed_in_output = 'Killed' in '\n'.join( + self.output.splitlines()[-15:] + ) + if self.exit_code == DOCKER_OOM_EXIT_CODE or (self.exit_code == 1 and + killed_in_output): + self.output += str( + _( + '\n\nCommand killed due to excessive memory consumption\n', + ) + ) except DockerAPIError: self.exit_code = -1 if self.output is None or not self.output: @@ -323,17 +338,25 @@ def get_wrapped_command(self): install requests<0.8``. This escapes a good majority of those characters. """ - bash_escape_re = re.compile(r"([\t\ \!\"\#\$\&\'\(\)\*\:\;\<\>\?\@" - r"\[\\\]\^\`\{\|\}\~])") + bash_escape_re = re.compile( + r"([\t\ \!\"\#\$\&\'\(\)\*\:\;\<\>\?\@" + r'\[\\\]\^\`\{\|\}\~])', + ) prefix = '' if self.bin_path: prefix += 'PATH={}:$PATH '.format(self.bin_path) - return ("/bin/sh -c 'cd {cwd} && {prefix}{cmd}'" - .format( - cwd=self.cwd, - prefix=prefix, - cmd=(' '.join([bash_escape_re.sub(r'\\\1', part) - for part in self.command])))) + return ( + "/bin/sh -c 'cd {cwd} && {prefix}{cmd}'".format( + cwd=self.cwd, + prefix=prefix, + cmd=( + ' '.join([ + bash_escape_re.sub(r'\\\1', part) + for part in self.command + ]) + ), + ) + ) class BaseEnvironment: @@ -359,7 +382,8 @@ def run(self, *cmd, **kwargs): def run_command_class( self, cls, cmd, record=None, warn_only=False, - record_as_success=False, **kwargs): + record_as_success=False, **kwargs + ): """ Run command from this environment. @@ -415,11 +439,13 @@ def run_command_class( msg += ':\n{out}'.format(out=build_cmd.output) if warn_only: - log.warning(LOG_TEMPLATE.format( - project=self.project.slug, - version='latest', - msg=msg, - )) + log.warning( + LOG_TEMPLATE.format( + project=self.project.slug, + version='latest', + msg=msg, + ) + ) else: raise BuildEnvironmentWarning(msg) return build_cmd @@ -475,8 +501,16 @@ class BuildEnvironment(BaseEnvironment): MkDocsYAMLParseError, ) - def __init__(self, project=None, version=None, build=None, config=None, - record=True, environment=None, update_on_success=True): + def __init__( + self, + project=None, + version=None, + build=None, + config=None, + record=True, + environment=None, + update_on_success=True, + ): super().__init__(project, environment) self.version = version self.build = build @@ -563,23 +597,30 @@ def run_command_class(self, *cmd, **kwargs): # pylint: disable=arguments-differ @property def successful(self): - """Is build completed, without top level failures or failing commands.""" # noqa - return (self.done and self.failure is None and - all(cmd.successful for cmd in self.commands)) + """Is build completed, without top level failures or failing + commands.""" # noqa + return ( + self.done and self.failure is None and + all(cmd.successful for cmd in self.commands) + ) @property def failed(self): """Is build completed, but has top level failure or failing commands.""" - return (self.done and ( - self.failure is not None or - any(cmd.failed for cmd in self.commands) - )) + return ( + self.done and ( + self.failure is not None or + any(cmd.failed for cmd in self.commands) + ) + ) @property def done(self): """Is build in finished state.""" - return (self.build is not None and - self.build['state'] == BUILD_STATE_FINISHED) + return ( + self.build is not None and + self.build['state'] == BUILD_STATE_FINISHED + ) def update_build(self, state=None): """ @@ -627,10 +668,10 @@ def update_build(self, state=None): # BuildEnvironmentError if not isinstance( self.failure, - ( - BuildEnvironmentException, - BuildEnvironmentWarning, - ), + ( + BuildEnvironmentException, + BuildEnvironmentWarning, + ), ): log.error( 'Build failed with unhandled exception: %s', @@ -668,7 +709,7 @@ def update_build(self, state=None): if update_build: try: api_v2.build(self.build['id']).put(self.build) - except HttpClientError as e: + except HttpClientError: log.exception( 'Unable to update build: id=%d', self.build['id'], @@ -751,10 +792,11 @@ def __enter__(self): project=self.project.slug, version=self.version.slug, msg=( - 'Removing stale container {}' - .format(self.container_id) + 'Removing stale container {}'.format( + self.container_id + ) ), - ) + ), ) client = self.get_client() client.remove_container(self.container_id) @@ -826,7 +868,7 @@ def get_client(self): version=DOCKER_VERSION, ) return self.client - except DockerException as e: + except DockerException: log.exception( LOG_TEMPLATE.format( project=self.project.slug, @@ -924,10 +966,10 @@ def update_build_from_container_state(self): ) elif state.get('Error'): self.failure = BuildEnvironmentError(( - _('Build exited due to unknown error: {0}') - .format(state.get('Error')) - ), - ) + _('Build exited due to unknown error: {0}').format( + state.get('Error') + ) + ),) def create_container(self): """Create docker container.""" @@ -952,7 +994,7 @@ def create_container(self): environment=self.environment, ) client.start(container=self.container_id) - except ConnectionError as e: + except ConnectionError: log.exception( LOG_TEMPLATE.format( project=self.project.slug, diff --git a/readthedocs/doc_builder/exceptions.py b/readthedocs/doc_builder/exceptions.py index 5898dfce015..ad6a458c874 100644 --- a/readthedocs/doc_builder/exceptions.py +++ b/readthedocs/doc_builder/exceptions.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Exceptions raised when building documentation.""" from django.utils.translation import ugettext_noop @@ -9,7 +10,9 @@ class BuildEnvironmentException(Exception): status_code = None def __init__(self, message=None, **kwargs): - self.status_code = kwargs.pop('status_code', None) or self.status_code or 1 + self.status_code = kwargs.pop( + 'status_code', None + ) or self.status_code or 1 message = message or self.get_default_message() super().__init__(message, **kwargs) diff --git a/readthedocs/doc_builder/loader.py b/readthedocs/doc_builder/loader.py index 8d733b3d8a7..b878009d206 100644 --- a/readthedocs/doc_builder/loader.py +++ b/readthedocs/doc_builder/loader.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Lookup tables for builders and backends.""" from importlib import import_module @@ -6,11 +7,19 @@ # Managers mkdocs = import_module( - getattr(settings, 'MKDOCS_BACKEND', - 'readthedocs.doc_builder.backends.mkdocs')) + getattr( + settings, + 'MKDOCS_BACKEND', + 'readthedocs.doc_builder.backends.mkdocs', + ), +) sphinx = import_module( - getattr(settings, 'SPHINX_BACKEND', - 'readthedocs.doc_builder.backends.sphinx')) + getattr( + settings, + 'SPHINX_BACKEND', + 'readthedocs.doc_builder.backends.sphinx', + ), +) BUILDER_BY_NAME = { # Possible HTML Builders diff --git a/readthedocs/doc_builder/python_environments.py b/readthedocs/doc_builder/python_environments.py index 92ec9a2f5f0..76b85188077 100644 --- a/readthedocs/doc_builder/python_environments.py +++ b/readthedocs/doc_builder/python_environments.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """An abstraction over virtualenv and Conda environments.""" import copy @@ -40,24 +41,29 @@ def delete_existing_build_dir(self): # Handle deleting old build dir build_dir = os.path.join( self.venv_path(), - 'build') + 'build', + ) if os.path.exists(build_dir): - log.info(LOG_TEMPLATE.format( - project=self.project.slug, - version=self.version.slug, - msg='Removing existing build directory', - )) + log.info( + LOG_TEMPLATE.format( + project=self.project.slug, + version=self.version.slug, + msg='Removing existing build directory', + ) + ) shutil.rmtree(build_dir) def delete_existing_venv_dir(self): venv_dir = self.venv_path() # Handle deleting old venv dir if os.path.exists(venv_dir): - log.info(LOG_TEMPLATE.format( - project=self.project.slug, - version=self.version.slug, - msg='Removing existing venv directory', - )) + log.info( + LOG_TEMPLATE.format( + project=self.project.slug, + version=self.version.slug, + msg='Removing existing venv directory', + ) + ) shutil.rmtree(venv_dir) def install_package(self): @@ -66,7 +72,7 @@ def install_package(self): extra_req_param = '' if self.config.python.extra_requirements: extra_req_param = '[{}]'.format( - ','.join(self.config.python.extra_requirements) + ','.join(self.config.python.extra_requirements), ) self.build_env.run( 'python', @@ -134,7 +140,9 @@ def is_obsolete(self): with open(self.environment_json_path(), 'r') as fpath: environment_conf = json.load(fpath) except (IOError, TypeError, KeyError, ValueError): - log.warning('Unable to read/parse readthedocs-environment.json file') + log.warning( + 'Unable to read/parse readthedocs-environment.json file' + ) # We remove the JSON file here to avoid cycling over time with a # corrupted file. os.remove(self.environment_json_path()) @@ -168,7 +176,8 @@ def is_obsolete(self): ]) def save_environment_json(self): - """Save on disk Python and build image versions used to create the venv.""" + """Save on disk Python and build image versions used to create the + venv.""" data = { 'python': { 'version': self.config.python_full_version, @@ -233,9 +242,7 @@ def install_core_requirements(self): # so it is used when installing the other requirements. cmd = pip_install_cmd + ['pip'] self.build_env.run( - *cmd, - bin_path=self.venv_bin(), - cwd=self.checkout_path + *cmd, bin_path=self.venv_bin(), cwd=self.checkout_path ) requirements = [ @@ -267,7 +274,7 @@ def install_core_requirements(self): negative='sphinx<1.8', ), 'sphinx-rtd-theme<0.5', - 'readthedocs-sphinx-ext<0.6' + 'readthedocs-sphinx-ext<0.6', ]) cmd = copy.copy(pip_install_cmd) @@ -288,8 +295,10 @@ def install_user_requirements(self): requirements_file_path = self.config.python.requirements if not requirements_file_path and requirements_file_path != '': builder_class = get_builder_class(self.config.doctype) - docs_dir = (builder_class(build_env=self.build_env, python_env=self) - .docs_dir()) + docs_dir = ( + builder_class(build_env=self.build_env, + python_env=self).docs_dir() + ) paths = [docs_dir, ''] req_files = ['pip_requirements.txt', 'requirements.txt'] for path, req_file in itertools.product(paths, req_files): @@ -337,11 +346,13 @@ def setup_base(self): if os.path.exists(version_path): # Re-create conda directory each time to keep fresh state - log.info(LOG_TEMPLATE.format( - project=self.project.slug, - version=self.version.slug, - msg='Removing existing conda directory', - )) + log.info( + LOG_TEMPLATE.format( + project=self.project.slug, + version=self.version.slug, + msg='Removing existing conda directory', + ) + ) shutil.rmtree(version_path) self.build_env.run( 'conda', diff --git a/readthedocs/doc_builder/signals.py b/readthedocs/doc_builder/signals.py index 08991fabb03..9911ba3ab5c 100644 --- a/readthedocs/doc_builder/signals.py +++ b/readthedocs/doc_builder/signals.py @@ -1,8 +1,9 @@ -"""Signals for adding custom context data""" +# -*- coding: utf-8 -*- +"""Signals for adding custom context data.""" import django.dispatch finalize_sphinx_context_data = django.dispatch.Signal( - providing_args=['buildenv', 'context', 'response_data'] + providing_args=['buildenv', 'context', 'response_data'], ) diff --git a/readthedocs/doc_builder/templates/doc_builder/data.js.tmpl b/readthedocs/doc_builder/templates/doc_builder/data.js.tmpl index 4dda93914e8..29ab61b0e65 100644 --- a/readthedocs/doc_builder/templates/doc_builder/data.js.tmpl +++ b/readthedocs/doc_builder/templates/doc_builder/data.js.tmpl @@ -6,7 +6,7 @@ var doc_slug = "{{ slug }}"; var page_name = "{{ pagename }}"; var html_theme = "{{ html_theme }}"; -// mkdocs_page_input_path is only defined on the RTD mkdocs theme but it isn't +// mkdocs_page_input_path is only defined on the RTD mkdocs theme but it isn't // available on all pages (e.g. missing in search result) if (typeof mkdocs_page_input_path !== "undefined") { READTHEDOCS_DATA["page"] = mkdocs_page_input_path.substr( diff --git a/readthedocs/gold/__init__.py b/readthedocs/gold/__init__.py index b26c8ed7c84..b7fcb9ea10e 100644 --- a/readthedocs/gold/__init__.py +++ b/readthedocs/gold/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ A Django app for Gold Membership. diff --git a/readthedocs/gold/admin.py b/readthedocs/gold/admin.py index a1dc3e80e45..020d2be9493 100644 --- a/readthedocs/gold/admin.py +++ b/readthedocs/gold/admin.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Django admin configuration for the Gold Membership app.""" from django.contrib import admin diff --git a/readthedocs/gold/apps.py b/readthedocs/gold/apps.py index 380a21f0947..5891264c8ef 100644 --- a/readthedocs/gold/apps.py +++ b/readthedocs/gold/apps.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Django app configuration for the Gold Membership app.""" from django.apps import AppConfig diff --git a/readthedocs/gold/forms.py b/readthedocs/gold/forms.py index 31c4a18aa1d..4840745d8b8 100644 --- a/readthedocs/gold/forms.py +++ b/readthedocs/gold/forms.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Gold subscription forms.""" from django import forms @@ -28,9 +29,11 @@ class Meta: required=True, min_length=4, max_length=4, - widget=forms.HiddenInput(attrs={ - 'data-bind': 'valueInit: last_4_card_digits, value: last_4_card_digits', - }) + widget=forms.HiddenInput( + attrs={ + 'data-bind': 'valueInit: last_4_card_digits, value: last_4_card_digits', + } + ), ) level = forms.ChoiceField( @@ -50,7 +53,8 @@ def validate_stripe(self): def get_customer_kwargs(self): data = { - 'description': self.customer.get_full_name() or self.customer.username, + 'description': self.customer.get_full_name() or + self.customer.username, 'email': self.customer.email, 'id': self.instance.stripe_id or None, } @@ -78,7 +82,7 @@ def get_subscription(self): # Add a new subscription subscription = customer.subscriptions.create( plan=self.cleaned_data['level'], - source=self.cleaned_data['stripe_token'] + source=self.cleaned_data['stripe_token'], ) return subscription @@ -87,7 +91,7 @@ def get_subscription(self): class GoldProjectForm(forms.Form): project = forms.ChoiceField( required=True, - help_text='Select a project.' + help_text='Select a project.', ) def __init__(self, active_user, *args, **kwargs): @@ -114,4 +118,6 @@ def clean(self): if self.projects.count() < self.user.num_supported_projects: return cleaned_data - self.add_error(None, 'You already have the max number of supported projects.') + self.add_error( + None, 'You already have the max number of supported projects.' + ) diff --git a/readthedocs/gold/signals.py b/readthedocs/gold/signals.py index 4c089ebb0d5..155230f3854 100644 --- a/readthedocs/gold/signals.py +++ b/readthedocs/gold/signals.py @@ -1,4 +1,5 @@ -"""Gold model signals""" +# -*- coding: utf-8 -*- +"""Gold model signals.""" from django.db.models.signals import pre_delete from django.dispatch import receiver @@ -10,6 +11,6 @@ @receiver(pre_delete, sender=GoldUser) def delete_customer(sender, instance, **__): - """On Gold subscription deletion, remove the customer from Stripe""" + """On Gold subscription deletion, remove the customer from Stripe.""" if sender == GoldUser and instance.stripe_id is not None: utils.delete_customer(instance.stripe_id) diff --git a/readthedocs/gold/templates/gold/projects.html b/readthedocs/gold/templates/gold/projects.html index 682b7bf3c9f..e832b53ee67 100644 --- a/readthedocs/gold/templates/gold/projects.html +++ b/readthedocs/gold/templates/gold/projects.html @@ -48,4 +48,3 @@

{% trans "Add a project" %}

{% endblock %} - diff --git a/readthedocs/gold/templates/gold/subscription_form.html b/readthedocs/gold/templates/gold/subscription_form.html index 9dc1ae592dc..241d7ddd22a 100644 --- a/readthedocs/gold/templates/gold/subscription_form.html +++ b/readthedocs/gold/templates/gold/subscription_form.html @@ -79,7 +79,7 @@

Domains

You are currently using {{ domains.count }} domains: - +

+ {% else %}
    diff --git a/readthedocs/templates/core/widesearchbar.html b/readthedocs/templates/core/widesearchbar.html index c378d92f586..d310e94b5e9 100644 --- a/readthedocs/templates/core/widesearchbar.html +++ b/readthedocs/templates/core/widesearchbar.html @@ -18,4 +18,4 @@

    {% trans "Search all the docs" %}

    - \ No newline at end of file + diff --git a/readthedocs/templates/dnt-policy.txt b/readthedocs/templates/dnt-policy.txt index ad946d1f80c..458a36d033d 100644 --- a/readthedocs/templates/dnt-policy.txt +++ b/readthedocs/templates/dnt-policy.txt @@ -1,4 +1,4 @@ -Do Not Track Compliance Policy +Do Not Track Compliance Policy Version 1.0 @@ -32,23 +32,23 @@ When this domain receives Web requests from a user who enables DNT by actively choosing an opt-out setting in their browser or by installing software that is primarily designed to protect privacy ("DNT User"), we will take the following measures with respect to those users' data, subject to the Exceptions, also -listed below: +listed below: -1. END USER IDENTIFIERS: +1. END USER IDENTIFIERS: a. If a DNT User has logged in to our service, all user identifiers, such as - unique or nearly unique cookies, "supercookies" and fingerprints are - discarded as soon as the HTTP(S) response is issued. + unique or nearly unique cookies, "supercookies" and fingerprints are + discarded as soon as the HTTP(S) response is issued. Data structures which associate user identifiers with accounts may be employed to recognize logged in users per Exception 4 below, but may not be associated with records of the user's activities unless otherwise excepted. - b. If a DNT User is not logged in to our service, we will take steps to ensure - that no user identifiers are transmitted to us at all. + b. If a DNT User is not logged in to our service, we will take steps to ensure + that no user identifiers are transmitted to us at all. -2. LOG RETENTION: +2. LOG RETENTION: a. Logs with DNT Users' identifiers removed (but including IP addresses and User Agent strings) may be retained for a period of 10 days or less, @@ -58,13 +58,13 @@ listed below: and performance problems; and that security and data aggregation systems have time to operate. - b. These logs will not be used for any other purposes. + b. These logs will not be used for any other purposes. -3. OTHER DOMAINS: +3. OTHER DOMAINS: a. If this domain transfers identifiable user data about DNT Users to contractors, affiliates or other parties, or embeds from or posts data to - other domains, we will either: + other domains, we will either: b. ensure that the operators of those domains abide by this policy overall by posting it at /.well-known/dnt-policy.txt via HTTPS on the domains in @@ -75,7 +75,7 @@ listed below: ensure that the recipient's policies and practices require the recipient to respect the policy for our DNT Users' data. - OR + OR obtain a contractual commitment from the recipient to respect this policy for our DNT Users' data. @@ -88,14 +88,14 @@ listed below: c. "Identifiable" means any records which are not Anonymized or otherwise covered by the Exceptions below. -4. PERIODIC REASSERTION OF COMPLIANCE: +4. PERIODIC REASSERTION OF COMPLIANCE: At least once every 12 months, we will take reasonable steps commensurate with the size of our organization and the nature of our service to confirm our ongoing compliance with this document, and we will publicly reassert our compliance. -5. USER NOTIFICATION: +5. USER NOTIFICATION: a. If we are required by law to retain or disclose user identifiers, we will attempt to provide the users with notice (unless we are prohibited or it @@ -105,7 +105,7 @@ listed below: b. We will attempt to provide this notice by email, if the users have given us an email address, and by postal mail if the users have provided a - postal address. + postal address. c. If the users do not challenge the disclosure request, we may be legally required to turn over their information. @@ -120,17 +120,17 @@ EXCEPTIONS Data from DNT Users collected by this domain may be logged or retained only in the following specific situations: -1. CONSENT / "OPT BACK IN" +1. CONSENT / "OPT BACK IN" a. DNT Users are opting out from tracking across the Web. It is possible that for some feature or functionality, we will need to ask a DNT User to - "opt back in" to be tracked by us across the entire Web. + "opt back in" to be tracked by us across the entire Web. b. If we do that, we will take reasonable steps to verify that the users who select this option have genuinely intended to opt back in to tracking. One way to do this is by performing scientifically reasonable user studies with a representative sample of our users, but smaller - organizations can satisfy this requirement by other means. + organizations can satisfy this requirement by other means. c. Where we believe that we have opt back in consent, our server will send a tracking value status header "Tk: C" as described in section 6.2 @@ -138,7 +138,7 @@ the following specific situations: http://www.w3.org/TR/tracking-dnt/#tracking-status-value -2. TRANSACTIONS +2. TRANSACTIONS If a DNT User actively and knowingly enters a transaction with our services (for instance, clicking on a clearly-labeled advertisement, @@ -151,19 +151,19 @@ the following specific situations: item will be shipped. By their nature, some transactions will require data to be retained indefinitely. -3. TECHNICAL AND SECURITY LOGGING: +3. TECHNICAL AND SECURITY LOGGING: a. If, during the processing of the initial request (for unique identifiers) or during the subsequent 10 days (for IP addresses and User Agent strings), we obtain specific information that causes our employees or systems to believe that a request is, or is likely to be, part of a security attack, - spam submission, or fraudulent transaction, then logs of those requests - are not subject to this policy. + spam submission, or fraudulent transaction, then logs of those requests + are not subject to this policy. b. If we encounter technical problems with our site, then, in rare circumstances, we may retain logs for longer than 10 days, if that is necessary to diagnose and fix those problems, but this practice will not be - routinized and we will strive to delete such logs as soon as possible. + routinized and we will strive to delete such logs as soon as possible. 4. AGGREGATION: @@ -179,13 +179,13 @@ the following specific situations: that the dataset, plus any additional information that is in our possession or likely to be available to us, does not allow the reconstruction of reading habits, online or offline activity of groups of - fewer than 5000 individuals or devices. + fewer than 5000 individuals or devices. c. If we generate anonymized datasets under this exception we will publicly document our anonymization methods in sufficient detail to allow outside experts to evaluate the effectiveness of those methods. -5. ERRORS: +5. ERRORS: From time to time, there may be errors by which user data is temporarily logged or retained in violation of this policy. If such errors are diff --git a/readthedocs/templates/flagging/flag_confirm.html b/readthedocs/templates/flagging/flag_confirm.html index ff584e855da..788b0c32d81 100644 --- a/readthedocs/templates/flagging/flag_confirm.html +++ b/readthedocs/templates/flagging/flag_confirm.html @@ -16,7 +16,7 @@

    {% trans "Confirm flagging" %}

    {% load flagging %}

    {% trans "What you are saying:" %}

    {{ flag_type.description }}

    - +
    {% csrf_token %}

    {% blocktrans %}Are you sure you want to flag "{{ object }}" as "{{ flag_type.title }}"?{% endblocktrans %}

    diff --git a/readthedocs/templates/flagging/flag_success.html b/readthedocs/templates/flagging/flag_success.html index d1cdff28e81..e8ab7fd04be 100644 --- a/readthedocs/templates/flagging/flag_success.html +++ b/readthedocs/templates/flagging/flag_success.html @@ -14,6 +14,6 @@

    {% trans "Flagging successful" %}

    {% block content %}

    {% blocktrans %}You have successfully flagged "{{ object }}" as "{{ flag_type.title }}"{% endblocktrans %}

    - +

    {% trans "Thank you for contributing to the quality of this site!" %}

    {% endblock %} diff --git a/readthedocs/templates/projects/domain_confirm_delete.html b/readthedocs/templates/projects/domain_confirm_delete.html index 8781f45a85b..c9aeef2ac87 100644 --- a/readthedocs/templates/projects/domain_confirm_delete.html +++ b/readthedocs/templates/projects/domain_confirm_delete.html @@ -19,5 +19,3 @@
    {% endblock %} - - diff --git a/readthedocs/templates/projects/domain_form.html b/readthedocs/templates/projects/domain_form.html index 307e089a39a..ed4688751a9 100644 --- a/readthedocs/templates/projects/domain_form.html +++ b/readthedocs/templates/projects/domain_form.html @@ -41,4 +41,3 @@

    {% endblock %} - diff --git a/readthedocs/templates/projects/domain_list.html b/readthedocs/templates/projects/domain_list.html index eab3abb587f..be4290e41ff 100644 --- a/readthedocs/templates/projects/domain_list.html +++ b/readthedocs/templates/projects/domain_list.html @@ -33,7 +33,7 @@

    {% trans "Existing Domains" %}

{% endif %} - +

{% trans "Add new Domain" %}

{% csrf_token %} {{ form.as_p }} @@ -42,4 +42,3 @@

{% trans "Add new Domain" %}

{% endblock %} - diff --git a/readthedocs/templates/projects/project_analytics.html b/readthedocs/templates/projects/project_analytics.html index 5ca9ab7d86c..bf682e90ec5 100644 --- a/readthedocs/templates/projects/project_analytics.html +++ b/readthedocs/templates/projects/project_analytics.html @@ -64,7 +64,7 @@

{% trans "Pages" %}

    {% for page, count in page_list %}
  • - {{ page }} + {{ page }} {{ count }} ({{ analytics.scaled_page|key:page }}%)
  • @@ -77,7 +77,7 @@

    {% trans "Versions" %}

      {% for version, count in version_list %}
    • - {{ version }} + {{ version }} {{ count }} ({{ analytics.scaled_version|key:version }}%)
    • diff --git a/readthedocs/templates/projects/project_version_list.html b/readthedocs/templates/projects/project_version_list.html index a83273fe5e0..f85f3be0de7 100644 --- a/readthedocs/templates/projects/project_version_list.html +++ b/readthedocs/templates/projects/project_version_list.html @@ -113,7 +113,7 @@

      {% trans "Inactive Versions" %}

      {% endblock inactive-versions %} - + {% endfor %}
    diff --git a/readthedocs/templates/search/elastic_search.html b/readthedocs/templates/search/elastic_search.html index b14ad50b20f..387bf85aa33 100644 --- a/readthedocs/templates/search/elastic_search.html +++ b/readthedocs/templates/search/elastic_search.html @@ -68,7 +68,7 @@
    {% trans 'Version' %}
    {{ name }} {% else %} {{ name }} - {% endif %} + {% endif %} ({{ count }}) @@ -86,7 +86,7 @@
    {% trans 'Taxonomy' %}
    {{ name }} {% else %} {{ name }} - {% endif %} + {% endif %} ({{ count }}) @@ -96,7 +96,7 @@
    {% trans 'Taxonomy' %}
    {% endif %} {% endif %} - + {% block sponsor %}
    Search is sponsored by Elastic, and hosted on Elastic Cloud. @@ -144,7 +144,7 @@

    {% blocktrans with query=query|default:"" %}Results for {{ query }}{% endblo {% for result in results.hits.hits %}
  • - {% if result.fields.name %} + {% if result.fields.name %} {# Project #} {{ result.fields.name }} diff --git a/readthedocs/templates/style_catalog.html b/readthedocs/templates/style_catalog.html index 68ab9b3e845..78e2123be3c 100644 --- a/readthedocs/templates/style_catalog.html +++ b/readthedocs/templates/style_catalog.html @@ -8,7 +8,7 @@

    Header 1.

    Header 2.

    Header 3.

    Header 4.

    -
    Header 5.
    +
    Header 5.

    Paragraph. Aside.

    Paragraph with link.

    Paragraph with highlighted text.

    @@ -41,7 +41,7 @@
    Header 5.
  • Ordered list item.
  • -
    +
    @@ -130,18 +130,18 @@

  • Module list item with menu.
  • Module list item with menu and right content. Right-aligned-ish content.
  • - +
  • diff --git a/readthedocs/urls.py b/readthedocs/urls.py index ba6e67ef338..de18fcd3f77 100644 --- a/readthedocs/urls.py +++ b/readthedocs/urls.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # pylint: disable=missing-docstring import os from functools import reduce @@ -42,8 +43,11 @@ url(r'^$', HomepageView.as_view(), name='homepage'), url(r'^support/', SupportView.as_view(), name='support'), url(r'^security/', TemplateView.as_view(template_name='security.html')), - url(r'^\.well-known/security.txt$', - TemplateView.as_view(template_name='security.txt', content_type='text/plain')), + url( + r'^\.well-known/security.txt$', + TemplateView + .as_view(template_name='security.txt', content_type='text/plain'), + ), ] rtd_urls = [ @@ -69,7 +73,10 @@ api_urls = [ url(r'^api/', include(v1_api.urls)), url(r'^api/v2/', include('readthedocs.restapi.urls')), - url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), + url( + r'^api-auth/', + include('rest_framework.urls', namespace='rest_framework') + ), ] i18n_urls = [ @@ -84,8 +91,11 @@ url(r'^\.well-known/dnt/$', do_not_track), # https://github.com/EFForg/dnt-guide#12-how-to-assert-dnt-compliance - url(r'^\.well-known/dnt-policy.txt$', - TemplateView.as_view(template_name='dnt-policy.txt', content_type='text/plain')), + url( + r'^\.well-known/dnt-policy.txt$', + TemplateView + .as_view(template_name='dnt-policy.txt', content_type='text/plain'), + ), ] debug_urls = [] @@ -95,16 +105,24 @@ document_root=os.path.join(settings.MEDIA_ROOT, build_format), ) debug_urls += [ - url('style-catalog/$', - TemplateView.as_view(template_name='style_catalog.html')), + url( + 'style-catalog/$', + TemplateView.as_view(template_name='style_catalog.html'), + ), # This must come last after the build output files - url(r'^media/(?P.+)$', - RedirectView.as_view(url=settings.STATIC_URL + '%(remainder)s'), name='media-redirect'), + url( + r'^media/(?P.+)$', + RedirectView.as_view(url=settings.STATIC_URL + '%(remainder)s'), + name='media-redirect', + ), ] # Export URLs -groups = [basic_urls, rtd_urls, project_urls, api_urls, core_urls, i18n_urls, deprecated_urls] +groups = [ + basic_urls, rtd_urls, project_urls, api_urls, core_urls, i18n_urls, + deprecated_urls +] if settings.DO_NOT_TRACK_ENABLED: # Include Do Not Track URLs if DNT is supported @@ -119,7 +137,7 @@ if 'readthedocsext.embed' in settings.INSTALLED_APPS: api_urls.insert( 0, - url(r'^api/v1/embed/', include('readthedocsext.embed.urls')) + url(r'^api/v1/embed/', include('readthedocsext.embed.urls')), ) if not getattr(settings, 'USE_SUBDOMAIN', False) or settings.DEBUG: diff --git a/readthedocs/vcs_support/backends/__init__.py b/readthedocs/vcs_support/backends/__init__.py index ff88fa587ec..37dde39b655 100644 --- a/readthedocs/vcs_support/backends/__init__.py +++ b/readthedocs/vcs_support/backends/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Listing of all the VCS backends.""" from __future__ import absolute_import from . import bzr, hg, git, svn diff --git a/readthedocs/vcs_support/backends/bzr.py b/readthedocs/vcs_support/backends/bzr.py index ebfb5574f08..742a8abf352 100644 --- a/readthedocs/vcs_support/backends/bzr.py +++ b/readthedocs/vcs_support/backends/bzr.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Bazaar-related utilities.""" import csv diff --git a/readthedocs/vcs_support/backends/git.py b/readthedocs/vcs_support/backends/git.py index 5d1b4eaa8dd..cf72f4da255 100644 --- a/readthedocs/vcs_support/backends/git.py +++ b/readthedocs/vcs_support/backends/git.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Git-related utilities.""" import logging @@ -70,8 +71,7 @@ def are_submodules_available(self, config): # TODO remove this after users migrate to a config file from readthedocs.projects.models import Feature submodules_in_config = ( - config.submodules.exclude != ALL or - config.submodules.include + config.submodules.exclude != ALL or config.submodules.include ) if (self.project.has_feature(Feature.SKIP_SUBMODULES) or not submodules_in_config): @@ -103,10 +103,7 @@ def validate_submodules(self, config): Returns the list of invalid submodules. """ repo = git.Repo(self.working_dir) - submodules = { - sub.path: sub - for sub in repo.submodules - } + submodules = {sub.path: sub for sub in repo.submodules} for sub_path in config.submodules.exclude: path = sub_path.rstrip('/') @@ -186,7 +183,7 @@ def tags(self): for tag in repo.tags: try: versions.append(VCSVersion(self, str(tag.commit), str(tag))) - except ValueError as e: + except ValueError: # ValueError: Cannot resolve commit as tag TAGNAME points to a # blob object - use the `.object` property instead to access it # This is not a real tag for us, so we skip it @@ -244,7 +241,7 @@ def update_submodules(self, config): self.checkout_submodules(submodules, config) else: raise RepositoryError( - RepositoryError.INVALID_SUBMODULES.format(submodules) + RepositoryError.INVALID_SUBMODULES.format(submodules), ) def checkout_submodules(self, submodules, config): diff --git a/readthedocs/vcs_support/backends/hg.py b/readthedocs/vcs_support/backends/hg.py index 7bb252322d8..a21e090ee59 100644 --- a/readthedocs/vcs_support/backends/hg.py +++ b/readthedocs/vcs_support/backends/hg.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Mercurial-related utilities.""" from readthedocs.projects.exceptions import RepositoryError from readthedocs.vcs_support.base import BaseVCS, VCSVersion @@ -38,7 +39,11 @@ def clone(self): @property def branches(self): retcode, stdout = self.run( - 'hg', 'branches', '--quiet', record_as_success=True)[:2] + 'hg', + 'branches', + '--quiet', + record_as_success=True, + )[:2] # error (or no tags found) if retcode != 0: return [] diff --git a/readthedocs/vcs_support/backends/svn.py b/readthedocs/vcs_support/backends/svn.py index d591dc880cc..b1a945aec2c 100644 --- a/readthedocs/vcs_support/backends/svn.py +++ b/readthedocs/vcs_support/backends/svn.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Subversion-related utilities.""" import csv @@ -40,8 +41,13 @@ def up(self): if retcode != 0: raise RepositoryError retcode, out, err = self.run( - 'svn', 'up', '--accept', 'theirs-full', - '--trust-server-cert', '--non-interactive') + 'svn', + 'up', + '--accept', + 'theirs-full', + '--trust-server-cert', + '--non-interactive', + ) if retcode != 0: raise RepositoryError return retcode, out, err @@ -59,8 +65,12 @@ def co(self, identifier=None): @property def tags(self): - retcode, stdout = self.run('svn', 'list', '%s/tags/' - % self.base_url, record_as_success=True)[:2] + retcode, stdout = self.run( + 'svn', + 'list', + '%s/tags/' % self.base_url, + record_as_success=True, + )[:2] # error (or no tags found) if retcode != 0: return [] @@ -70,12 +80,12 @@ def parse_tags(self, data): """ Parses output of svn list, eg: - release-1.1/ - release-1.2/ - release-1.3/ - release-1.4/ - release-1.4.1/ - release-1.5/ + release-1.1/ + release-1.2/ + release-1.3/ + release-1.4/ + release-1.4.1/ + release-1.5/ """ # parse the lines into a list of tuples (commit-hash, tag ref name) # StringIO below is expecting Unicode data, so ensure that it gets it. diff --git a/readthedocs/vcs_support/base.py b/readthedocs/vcs_support/base.py index 9530cebde8c..653629110fc 100644 --- a/readthedocs/vcs_support/base.py +++ b/readthedocs/vcs_support/base.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Base classes for VCS backends.""" import logging import os @@ -26,7 +27,9 @@ def __init__(self, repository, identifier, verbose_name): def __repr__(self): return ' self.timeout: - log.info("Lock (%s): Force unlock, old lockfile", - self.name) + log.info( + 'Lock (%s): Force unlock, old lockfile', + self.name, + ) os.remove(self.fpath) break - log.info("Lock (%s): Locked, waiting..", self.name) + log.info('Lock (%s): Locked, waiting..', self.name) time.sleep(self.polling_interval) timesince = time.time() - start if timesince > self.timeout: - log.info("Lock (%s): Force unlock, timeout reached", - self.name) + log.info( + 'Lock (%s): Force unlock, timeout reached', + self.name, + ) os.remove(self.fpath) break - log.info("%s still locked after %.2f seconds; retry for %.2f" - " seconds", self.name, timesince, self.timeout) + log.info( + '%s still locked after %.2f seconds; retry for %.2f' + ' seconds', + self.name, + timesince, + self.timeout, + ) open(self.fpath, 'w').close() - log.info("Lock (%s): Lock acquired", self.name) + log.info('Lock (%s): Lock acquired', self.name) def __exit__(self, exc, value, tb): try: - log.info("Lock (%s): Releasing", self.name) + log.info('Lock (%s): Releasing', self.name) os.remove(self.fpath) except OSError as e: # We want to ignore "No such file or directory" and log any other # type of error. if e.errno != errno.ENOENT: log.exception( - "Lock (%s): Failed to release, ignoring...", + 'Lock (%s): Failed to release, ignoring...', self.name, ) @@ -81,7 +93,9 @@ class NonBlockingLock: """ def __init__(self, project, version, max_lock_age=None): - self.fpath = os.path.join(project.doc_path, '%s__rtdlock' % version.slug) + self.fpath = os.path.join( + project.doc_path, '%s__rtdlock' % version.slug + ) self.max_lock_age = max_lock_age self.name = project.slug @@ -90,21 +104,23 @@ def __enter__(self): if path_exists and self.max_lock_age is not None: lock_age = time.time() - os.stat(self.fpath)[stat.ST_MTIME] if lock_age > self.max_lock_age: - log.info("Lock (%s): Force unlock, old lockfile", - self.name) + log.info( + 'Lock (%s): Force unlock, old lockfile', + self.name, + ) os.remove(self.fpath) else: raise LockTimeout( - "Lock ({}): Lock still active".format(self.name)) + 'Lock ({}): Lock still active'.format(self.name), + ) elif path_exists: - raise LockTimeout( - "Lock ({}): Lock still active".format(self.name)) + raise LockTimeout('Lock ({}): Lock still active'.format(self.name),) open(self.fpath, 'w').close() return self def __exit__(self, exc_type, exc_val, exc_tb): try: - log.info("Lock (%s): Releasing", self.name) + log.info('Lock (%s): Releasing', self.name) os.remove(self.fpath) except (IOError, OSError) as e: # We want to ignore "No such file or directory" and log any other diff --git a/readthedocs/worker.py b/readthedocs/worker.py index 47dfdb48282..d038e0062bc 100644 --- a/readthedocs/worker.py +++ b/readthedocs/worker.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- -"""Celery worker application instantiation""" + +"""Celery worker application instantiation.""" import os diff --git a/readthedocs/wsgi.py b/readthedocs/wsgi.py index e1cd6a8ec80..9d35da5f3a9 100644 --- a/readthedocs/wsgi.py +++ b/readthedocs/wsgi.py @@ -1,9 +1,10 @@ -"""WSGI application helper""" +# -*- coding: utf-8 -*- +"""WSGI application helper.""" from __future__ import absolute_import import os -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "readthedocs.settings.dev") +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'readthedocs.settings.dev') # This application object is used by any WSGI server configured to use this # file. This includes Django's development server, if the WSGI_APPLICATION From 5b88c0739b66b1a8cc7135e1a89cc4a915753408 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Tue, 8 Jan 2019 22:02:58 +0100 Subject: [PATCH 22/42] Add docstring to config.__init__ file --- readthedocs/config/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/readthedocs/config/__init__.py b/readthedocs/config/__init__.py index fd6326a50e7..d0c70ef6bd2 100644 --- a/readthedocs/config/__init__.py +++ b/readthedocs/config/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- +"""Logic to parse and validate ``readthedocs.yaml`` file.""" from .config import * # noqa from .parser import * # noqa From 0c535feb2081e3f6a5e1c930de2b4850137f14f8 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Wed, 9 Jan 2019 12:51:48 +0100 Subject: [PATCH 23/42] More linting issues fixed --- readthedocs/core/utils/extend.py | 8 ++++++-- readthedocs/core/utils/tasks/public.py | 1 + readthedocs/core/views/__init__.py | 4 ++-- readthedocs/core/views/serve.py | 11 +++++------ readthedocs/doc_builder/backends/sphinx.py | 7 +++++-- readthedocs/doc_builder/environments.py | 5 ++--- .../doc_builder/python_environments.py | 19 ++++++++++++++----- readthedocs/notifications/views.py | 12 +++++------- readthedocs/oauth/notifications.py | 8 ++++---- readthedocs/projects/models.py | 3 ++- readthedocs/projects/querysets.py | 7 ++++--- 11 files changed, 50 insertions(+), 35 deletions(-) diff --git a/readthedocs/core/utils/extend.py b/readthedocs/core/utils/extend.py index 96319e691a8..a74b1175835 100644 --- a/readthedocs/core/utils/extend.py +++ b/readthedocs/core/utils/extend.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Patterns for extending Read the Docs.""" import inspect @@ -33,8 +34,11 @@ def get_override_class(proxy_class, default_class=None): class SettingsOverrideMeta(type): - """Meta class for passing along classmethod class to the underlying - class.""" # noqa + """ + Meta class to manage our Setting configurations. + + Meta class for passing along classmethod class to the underlying class. + """ def __getattr__(cls, attr): # noqa: pep8 false positive proxy_class = get_override_class(cls, getattr(cls, '_default_class')) diff --git a/readthedocs/core/utils/tasks/public.py b/readthedocs/core/utils/tasks/public.py index 2f0bb011430..8b8f2421ac7 100644 --- a/readthedocs/core/utils/tasks/public.py +++ b/readthedocs/core/utils/tasks/public.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Celery tasks with publicly viewable status.""" from celery import Task, states diff --git a/readthedocs/core/views/__init__.py b/readthedocs/core/views/__init__.py index 597c2ac6263..084b7ca7dbd 100644 --- a/readthedocs/core/views/__init__.py +++ b/readthedocs/core/views/__init__.py @@ -133,8 +133,8 @@ def do_not_track(request): dnt_header = request.META.get('HTTP_DNT') # https://w3c.github.io/dnt/drafts/tracking-dnt.html#status-representation - return JsonResponse( - { # pylint: disable=redundant-content-type-for-json-response + return JsonResponse( # pylint: disable=redundant-content-type-for-json-response + { 'policy': 'https://docs.readthedocs.io/en/latest/privacy-policy.html', 'same-party': [ 'readthedocs.org', diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 9f87d82abf7..d996879f8ad 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -57,9 +57,9 @@ def map_subproject_slug(view_func): """ @wraps(view_func) - def inner_view( + def inner_view( # noqa request, subproject=None, subproject_slug=None, *args, **kwargs - ): # noqa + ): if subproject is None and subproject_slug: # Try to fetch by subproject alias first, otherwise we might end up # redirected to an unrelated project. @@ -87,9 +87,9 @@ def map_project_slug(view_func): """ @wraps(view_func) - def inner_view( + def inner_view( # noqa request, project=None, project_slug=None, *args, **kwargs - ): # noqa + ): if project is None: if not project_slug: project_slug = request.slug @@ -160,8 +160,7 @@ def serve_docs( version_slug=None, filename='', ): - """Exists to map existing proj, lang, version, filename views to the file - format.""" + """Map existing proj, lang, version, filename views to the file format.""" if not version_slug: version_slug = project.get_default_version() try: diff --git a/readthedocs/doc_builder/backends/sphinx.py b/readthedocs/doc_builder/backends/sphinx.py index b656433e527..c5a664835fa 100644 --- a/readthedocs/doc_builder/backends/sphinx.py +++ b/readthedocs/doc_builder/backends/sphinx.py @@ -162,8 +162,11 @@ def get_config_params(self): return data def append_conf(self, **__): - """Find or create a ``conf.py`` with a rendered - ``doc_builder/conf.py.tmpl`` appended.""" + """ + Find or create a ``conf.py`` and appends default content. + + The default content is rendered from ``doc_builder/conf.py.tmpl``. + """ if self.config_file is None: master_doc = self.create_index(extension='rst') self._write_config(master_doc=master_doc) diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py index 9276e187a7d..497a316a1e3 100644 --- a/readthedocs/doc_builder/environments.py +++ b/readthedocs/doc_builder/environments.py @@ -597,8 +597,7 @@ def run_command_class(self, *cmd, **kwargs): # pylint: disable=arguments-differ @property def successful(self): - """Is build completed, without top level failures or failing - commands.""" # noqa + """Build completed, without top level failures or failing commands.""" return ( self.done and self.failure is None and all(cmd.successful for cmd in self.commands) @@ -667,7 +666,7 @@ def update_build(self, state=None): # Surface a generic error if the class is not a # BuildEnvironmentError if not isinstance( - self.failure, + self.failure, ( BuildEnvironmentException, BuildEnvironmentWarning, diff --git a/readthedocs/doc_builder/python_environments.py b/readthedocs/doc_builder/python_environments.py index 76b85188077..0f0c6e4e936 100644 --- a/readthedocs/doc_builder/python_environments.py +++ b/readthedocs/doc_builder/python_environments.py @@ -141,7 +141,7 @@ def is_obsolete(self): environment_conf = json.load(fpath) except (IOError, TypeError, KeyError, ValueError): log.warning( - 'Unable to read/parse readthedocs-environment.json file' + 'Unable to read/parse readthedocs-environment.json file', ) # We remove the JSON file here to avoid cycling over time with a # corrupted file. @@ -176,8 +176,15 @@ def is_obsolete(self): ]) def save_environment_json(self): - """Save on disk Python and build image versions used to create the - venv.""" + """ + Save on builders disk data about the environment used to build docs. + + The data is saved as a ``.json`` file with this information on it: + + - python.version + - build.image + - build.hash + """ data = { 'python': { 'version': self.config.python_full_version, @@ -296,8 +303,10 @@ def install_user_requirements(self): if not requirements_file_path and requirements_file_path != '': builder_class = get_builder_class(self.config.doctype) docs_dir = ( - builder_class(build_env=self.build_env, - python_env=self).docs_dir() + builder_class( + build_env=self.build_env, + python_env=self, + ).docs_dir() ) paths = [docs_dir, ''] req_files = ['pip_requirements.txt', 'requirements.txt'] diff --git a/readthedocs/notifications/views.py b/readthedocs/notifications/views.py index 7252e8c3e55..33e6da58258 100644 --- a/readthedocs/notifications/views.py +++ b/readthedocs/notifications/views.py @@ -14,13 +14,11 @@ class SendNotificationView(FormView): Accepts the following additional parameters: - queryset - The queryset to use to determine the users to send emails to - - :cvar action_name: Name of the action to pass to the form template, - determines the action to pass back to the admin view - :cvar notification_classes: List of :py:class:`Notification` classes to - display in the form + :param queryset: Queryset to use to determine the users to send emails to + :param action_name: Name of the action to pass to the form template, + determines the action to pass back to the admin view + :param notification_classes: List of :py:class:`Notification` classes to + display in the form """ form_class = SendNotificationForm diff --git a/readthedocs/oauth/notifications.py b/readthedocs/oauth/notifications.py index afc9e43c8c4..062dfe7412b 100644 --- a/readthedocs/oauth/notifications.py +++ b/readthedocs/oauth/notifications.py @@ -16,11 +16,11 @@ class AttachWebhookNotification(SiteNotification): success_message = _('Webhook successfully added.') failure_message = { NO_PERMISSIONS: _( - 'Could not add webhook for {{ project.name }}. Make sure you have the correct {{ provider.name }} permissions.' - ), # noqa + 'Could not add webhook for {{ project.name }}. Make sure you have the correct {{ provider.name }} permissions.' # noqa + ), NO_ACCOUNTS: _( - 'Could not add webhook for {{ project.name }}. Please connect your {{ provider.name }} account.' - ), # noqa + 'Could not add webhook for {{ project.name }}. Please connect your {{ provider.name }} account.' # noqa + ), } def get_context_data(self): diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index cc34197bf1a..d552c2b28c8 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -1045,7 +1045,8 @@ def __init__(self, *args, **kwargs): 'absolute_url', 'downloads', 'main_language_project', - 'related_projects',]: + 'related_projects', + ]: try: del kwargs[key] except KeyError: diff --git a/readthedocs/projects/querysets.py b/readthedocs/projects/querysets.py index 396003dad13..cddcf30efd0 100644 --- a/readthedocs/projects/querysets.py +++ b/readthedocs/projects/querysets.py @@ -92,10 +92,11 @@ class ProjectQuerySet(SettingsOverrideObject): class RelatedProjectQuerySetBase(models.QuerySet): """ - A manager for things that relate to Project and need to get their perms from - the project. + Useful for objects that relate to Project and its permissions. - This shouldn't be used as a subclass. + Objects get the permissions from the project itself. + + ..note:: This shouldn't be used as a subclass. """ use_for_related_fields = True From f1820596f0c3c175d5c5bc147b80652ed788d5f6 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Wed, 9 Jan 2019 12:53:25 +0100 Subject: [PATCH 24/42] pre-commit run on some missing files (manually applyed) --- readthedocs/doc_builder/environments.py | 10 +++--- .../doc_builder/python_environments.py | 6 ++-- readthedocs/notifications/views.py | 4 ++- readthedocs/oauth/notifications.py | 4 +-- readthedocs/projects/models.py | 31 ++++++++++++------- readthedocs/projects/querysets.py | 5 +-- 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py index 497a316a1e3..ed158933ba9 100644 --- a/readthedocs/doc_builder/environments.py +++ b/readthedocs/doc_builder/environments.py @@ -312,14 +312,14 @@ def run(self): # not use the specific exit code, so we check if the word `Killed` # is in the last 15 lines of the command's output killed_in_output = 'Killed' in '\n'.join( - self.output.splitlines()[-15:] + self.output.splitlines()[-15:], ) if self.exit_code == DOCKER_OOM_EXIT_CODE or (self.exit_code == 1 and killed_in_output): self.output += str( _( '\n\nCommand killed due to excessive memory consumption\n', - ) + ), ) except DockerAPIError: self.exit_code = -1 @@ -444,7 +444,7 @@ def run_command_class( project=self.project.slug, version='latest', msg=msg, - ) + ), ) else: raise BuildEnvironmentWarning(msg) @@ -792,7 +792,7 @@ def __enter__(self): version=self.version.slug, msg=( 'Removing stale container {}'.format( - self.container_id + self.container_id, ) ), ), @@ -966,7 +966,7 @@ def update_build_from_container_state(self): elif state.get('Error'): self.failure = BuildEnvironmentError(( _('Build exited due to unknown error: {0}').format( - state.get('Error') + state.get('Error'), ) ),) diff --git a/readthedocs/doc_builder/python_environments.py b/readthedocs/doc_builder/python_environments.py index 0f0c6e4e936..88739586194 100644 --- a/readthedocs/doc_builder/python_environments.py +++ b/readthedocs/doc_builder/python_environments.py @@ -49,7 +49,7 @@ def delete_existing_build_dir(self): project=self.project.slug, version=self.version.slug, msg='Removing existing build directory', - ) + ), ) shutil.rmtree(build_dir) @@ -62,7 +62,7 @@ def delete_existing_venv_dir(self): project=self.project.slug, version=self.version.slug, msg='Removing existing venv directory', - ) + ), ) shutil.rmtree(venv_dir) @@ -360,7 +360,7 @@ def setup_base(self): project=self.project.slug, version=self.version.slug, msg='Removing existing conda directory', - ) + ), ) shutil.rmtree(version_path) self.build_env.run( diff --git a/readthedocs/notifications/views.py b/readthedocs/notifications/views.py index 33e6da58258..0ee0495ddb8 100644 --- a/readthedocs/notifications/views.py +++ b/readthedocs/notifications/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django views for the notifications app.""" from django.contrib import admin, messages from django.http import HttpResponseRedirect @@ -106,7 +107,8 @@ def message_user( fail_silently=False, ): """ - Implementation of + Implementation of. + :py:meth:`django.contrib.admin.options.ModelAdmin.message_user` Send message through messages framework diff --git a/readthedocs/oauth/notifications.py b/readthedocs/oauth/notifications.py index 062dfe7412b..c5c5165aa7f 100644 --- a/readthedocs/oauth/notifications.py +++ b/readthedocs/oauth/notifications.py @@ -16,10 +16,10 @@ class AttachWebhookNotification(SiteNotification): success_message = _('Webhook successfully added.') failure_message = { NO_PERMISSIONS: _( - 'Could not add webhook for {{ project.name }}. Make sure you have the correct {{ provider.name }} permissions.' # noqa + 'Could not add webhook for {{ project.name }}. Make sure you have the correct {{ provider.name }} permissions.', # noqa ), NO_ACCOUNTS: _( - 'Could not add webhook for {{ project.name }}. Please connect your {{ provider.name }} account.' # noqa + 'Could not add webhook for {{ project.name }}. Please connect your {{ provider.name }} account.', # noqa ), } diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index d552c2b28c8..12cf77ea9ab 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -62,7 +62,11 @@ class ProjectRelationship(models.Model): related_name='superprojects', ) alias = models.SlugField( - _('Alias'), max_length=255, null=True, blank=True, db_index=False + _('Alias'), + max_length=255, + null=True, + blank=True, + db_index=False, ) objects = ChildRelatedProjectQuerySet.as_manager() @@ -476,8 +480,10 @@ def get_docs_url(self, version_slug=None, lang_slug=None, private=None): Always use http for now, to avoid content warnings. """ return resolve( - project=self, version_slug=version_slug, language=lang_slug, - private=private + project=self, + version_slug=version_slug, + language=lang_slug, + private=private, ) def get_builds_url(self): @@ -730,7 +736,7 @@ def has_pdf(self, version_slug=LATEST): self.get_production_media_path( type_='pdf', version_slug=version_slug, - ) + ), ) def has_epub(self, version_slug=LATEST): @@ -740,7 +746,7 @@ def has_epub(self, version_slug=LATEST): self.get_production_media_path( type_='epub', version_slug=version_slug, - ) + ), ) def has_htmlzip(self, version_slug=LATEST): @@ -748,7 +754,7 @@ def has_htmlzip(self, version_slug=LATEST): self.get_production_media_path( type_='htmlzip', version_slug=version_slug, - ) + ), ) @property @@ -841,8 +847,7 @@ def get_latest_build(self, finished=True): def api_versions(self): from readthedocs.builds.models import APIVersion ret = [] - for version_data in api.project(self.pk - ).active_versions.get()['versions']: + for version_data in api.project(self.pk).active_versions.get()['versions']: version = APIVersion(**version_data) ret.append(version) return sort_version_aware(ret) @@ -1103,8 +1108,9 @@ class ImportedFile(models.Model): def get_absolute_url(self): return resolve( - project=self.project, version_slug=self.version.slug, - filename=self.path + project=self.project, + version_slug=self.version.slug, + filename=self.path, ) def __str__(self): @@ -1188,7 +1194,8 @@ class Meta: def __str__(self): return '{domain} pointed at {project}'.format( - domain=self.domain, project=self.project.name + domain=self.domain, + project=self.project.name, ) def save(self, *args, **kwargs): # pylint: disable=arguments-differ @@ -1264,7 +1271,7 @@ def add_features(sender, **kwargs): ), ( MKDOCS_THEME_RTD, - _('Use Read the Docs theme for MkDocs as default theme') + _('Use Read the Docs theme for MkDocs as default theme'), ), ( DONT_SHALLOW_CLONE, diff --git a/readthedocs/projects/querysets.py b/readthedocs/projects/querysets.py index cddcf30efd0..dcbf1ffda34 100644 --- a/readthedocs/projects/querysets.py +++ b/readthedocs/projects/querysets.py @@ -45,7 +45,7 @@ def public(self, user=None): def protected(self, user=None): queryset = self.filter( - privacy_level__in=[constants.PUBLIC, constants.PROTECTED] + privacy_level__in=[constants.PUBLIC, constants.PROTECTED], ) if user: return self._add_user_repos(queryset, user) @@ -126,7 +126,8 @@ def public(self, user=None, project=None): def protected(self, user=None, project=None): kwargs = { '%s__privacy_level__in' % self.project_field: [ - constants.PUBLIC, constants.PROTECTED + constants.PUBLIC, + constants.PROTECTED, ], } queryset = self.filter(**kwargs) From 47e5e7e753f7d6650242a9fb9e94363a39d1f506 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Wed, 9 Jan 2019 12:53:53 +0100 Subject: [PATCH 25/42] Update common pre-commit configs --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 890625b9bfc..bb9f74e7db5 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 890625b9bfca1981a3f68a9fb7d168bba714421d +Subproject commit bb9f74e7db578fb575bb3a1f48a4ee6c3bc2a491 From efa731ff4f50a5e2102291518384b094920707e8 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Wed, 9 Jan 2019 13:08:20 +0100 Subject: [PATCH 26/42] Upgdate common submodule --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index bb9f74e7db5..8712295c9d5 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit bb9f74e7db578fb575bb3a1f48a4ee6c3bc2a491 +Subproject commit 8712295c9d5acf4570350f22849cb705f522ea60 From 632e18c1212981a69aea243f5d9349498501f999 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Wed, 9 Jan 2019 13:19:00 +0100 Subject: [PATCH 27/42] pre-commit all the files again --- readthedocs/analytics/__init__.py | 1 + readthedocs/analytics/apps.py | 1 + readthedocs/analytics/tasks.py | 1 + readthedocs/analytics/utils.py | 1 + readthedocs/api/utils.py | 1 + readthedocs/builds/admin.py | 1 + readthedocs/builds/constants.py | 1 + readthedocs/builds/forms.py | 1 + readthedocs/builds/managers.py | 1 + readthedocs/builds/querysets.py | 1 + readthedocs/builds/signals.py | 1 + readthedocs/builds/syncers.py | 1 + readthedocs/builds/version_slug.py | 1 + readthedocs/config/__init__.py | 1 + readthedocs/config/find.py | 1 + readthedocs/config/models.py | 1 + readthedocs/config/validation.py | 1 + readthedocs/constants.py | 1 + readthedocs/core/__init__.py | 1 + readthedocs/core/adapters.py | 1 + readthedocs/core/admin.py | 1 + readthedocs/core/apps.py | 1 + readthedocs/core/backends.py | 1 + readthedocs/core/context_processors.py | 1 + readthedocs/core/fields.py | 1 + readthedocs/core/management/commands/archive.py | 1 + .../core/management/commands/clean_builds.py | 1 + .../core/management/commands/import_github.py | 1 + .../commands/import_github_language.py | 1 + .../commands/provision_elasticsearch.py | 1 + readthedocs/core/management/commands/pull.py | 1 + .../management/commands/reindex_elasticsearch.py | 1 + .../core/management/commands/set_metadata.py | 1 + readthedocs/core/management/commands/symlink.py | 1 + .../core/management/commands/update_api.py | 1 + .../core/management/commands/update_versions.py | 1 + readthedocs/core/middleware.py | 1 + readthedocs/core/mixins.py | 1 + readthedocs/core/permissions.py | 1 + readthedocs/core/resolver.py | 1 + readthedocs/core/symlink.py | 6 ++++-- readthedocs/core/tasks.py | 1 + readthedocs/core/templatetags/core_tags.py | 1 + readthedocs/core/templatetags/privacy_tags.py | 1 + readthedocs/core/urls/__init__.py | 1 + readthedocs/core/urls/single_version.py | 1 + readthedocs/core/urls/subdomain.py | 1 + readthedocs/core/utils/tasks/__init__.py | 1 + .../core/utils/tasks/permission_checks.py | 1 + readthedocs/core/utils/tasks/retrieve.py | 1 + readthedocs/core/views/hooks.py | 1 + readthedocs/doc_builder/backends/mkdocs.py | 1 + readthedocs/doc_builder/environments.py | 2 ++ readthedocs/doc_builder/loader.py | 1 + readthedocs/doc_builder/signals.py | 1 + readthedocs/gold/__init__.py | 1 + readthedocs/gold/admin.py | 1 + readthedocs/gold/apps.py | 1 + readthedocs/gold/forms.py | 1 + readthedocs/gold/signals.py | 1 + readthedocs/gold/urls.py | 1 + readthedocs/integrations/admin.py | 1 + readthedocs/integrations/utils.py | 1 + readthedocs/notifications/__init__.py | 1 + readthedocs/notifications/apps.py | 1 + readthedocs/notifications/constants.py | 1 + readthedocs/notifications/forms.py | 1 + readthedocs/notifications/urls.py | 1 + readthedocs/oauth/admin.py | 1 + readthedocs/oauth/apps.py | 1 + readthedocs/oauth/querysets.py | 1 + readthedocs/oauth/services/bitbucket.py | 1 + readthedocs/oauth/services/github.py | 1 + readthedocs/payments/forms.py | 1 + readthedocs/payments/mixins.py | 1 + readthedocs/payments/utils.py | 1 + readthedocs/profiles/urls/private.py | 1 + readthedocs/profiles/urls/public.py | 1 + readthedocs/projects/apps.py | 1 + readthedocs/projects/backends/views.py | 1 + readthedocs/projects/feeds.py | 1 + .../commands/import_project_from_live.py | 1 + readthedocs/projects/models.py | 16 +++++++++------- readthedocs/projects/notifications.py | 1 + .../projects/templatetags/projects_tags.py | 1 + readthedocs/projects/urls/private.py | 1 + readthedocs/projects/urls/public.py | 1 + readthedocs/projects/validators.py | 1 + readthedocs/projects/views/mixins.py | 1 + readthedocs/redirects/admin.py | 1 + readthedocs/redirects/managers.py | 1 + readthedocs/redirects/models.py | 1 + readthedocs/redirects/utils.py | 1 + readthedocs/restapi/permissions.py | 1 + readthedocs/restapi/serializers.py | 1 + readthedocs/restapi/signals.py | 1 + readthedocs/restapi/views/core_views.py | 1 + readthedocs/restapi/views/search_views.py | 1 + readthedocs/restapi/views/task_views.py | 1 + readthedocs/search/indexes.py | 1 + readthedocs/search/lib.py | 1 + readthedocs/search/signals.py | 1 + readthedocs/vcs_support/backends/__init__.py | 1 + readthedocs/vcs_support/utils.py | 1 + readthedocs/wsgi.py | 1 + 105 files changed, 117 insertions(+), 9 deletions(-) diff --git a/readthedocs/analytics/__init__.py b/readthedocs/analytics/__init__.py index b8f7ef2906d..b25cef94087 100644 --- a/readthedocs/analytics/__init__.py +++ b/readthedocs/analytics/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """App init.""" default_app_config = 'readthedocs.analytics.apps.AnalyticsAppConfig' # noqa diff --git a/readthedocs/analytics/apps.py b/readthedocs/analytics/apps.py index 3bfa8a3559d..c2abb221a18 100644 --- a/readthedocs/analytics/apps.py +++ b/readthedocs/analytics/apps.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django app config for the analytics app.""" from django.apps import AppConfig diff --git a/readthedocs/analytics/tasks.py b/readthedocs/analytics/tasks.py index 02b0a7524ef..4e2cc957ee8 100644 --- a/readthedocs/analytics/tasks.py +++ b/readthedocs/analytics/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Tasks for Read the Docs' analytics.""" from django.conf import settings diff --git a/readthedocs/analytics/utils.py b/readthedocs/analytics/utils.py index 8c06e6001fd..c358423e499 100644 --- a/readthedocs/analytics/utils.py +++ b/readthedocs/analytics/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Utilities related to analytics.""" import hashlib diff --git a/readthedocs/api/utils.py b/readthedocs/api/utils.py index 1f112a608e9..ab820d21d0b 100644 --- a/readthedocs/api/utils.py +++ b/readthedocs/api/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Utility classes for api module.""" import logging diff --git a/readthedocs/builds/admin.py b/readthedocs/builds/admin.py index 36b64339d18..b4bc390b980 100644 --- a/readthedocs/builds/admin.py +++ b/readthedocs/builds/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django admin interface for `~builds.models.Build` and related models.""" from django.contrib import admin diff --git a/readthedocs/builds/constants.py b/readthedocs/builds/constants.py index 1175874cf19..94858300c78 100644 --- a/readthedocs/builds/constants.py +++ b/readthedocs/builds/constants.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Constants for the builds app.""" from django.conf import settings diff --git a/readthedocs/builds/forms.py b/readthedocs/builds/forms.py index 13f72a5b952..406da7417ab 100644 --- a/readthedocs/builds/forms.py +++ b/readthedocs/builds/forms.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django forms for the builds app.""" from django import forms diff --git a/readthedocs/builds/managers.py b/readthedocs/builds/managers.py index d05308cc4b5..be9c7050ea5 100644 --- a/readthedocs/builds/managers.py +++ b/readthedocs/builds/managers.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Build and Version class model Managers.""" from django.db import models diff --git a/readthedocs/builds/querysets.py b/readthedocs/builds/querysets.py index 5c3778257e5..2039aa0ec6e 100644 --- a/readthedocs/builds/querysets.py +++ b/readthedocs/builds/querysets.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Build and Version QuerySet classes.""" from django.db import models diff --git a/readthedocs/builds/signals.py b/readthedocs/builds/signals.py index fb16ca75dd6..df87930f9b7 100644 --- a/readthedocs/builds/signals.py +++ b/readthedocs/builds/signals.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Build signals.""" import django.dispatch diff --git a/readthedocs/builds/syncers.py b/readthedocs/builds/syncers.py index 185e0d30934..6b7b1d337bf 100644 --- a/readthedocs/builds/syncers.py +++ b/readthedocs/builds/syncers.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Classes to copy files between build and web servers. diff --git a/readthedocs/builds/version_slug.py b/readthedocs/builds/version_slug.py index d3e8bbe14b7..b7f88cd585f 100644 --- a/readthedocs/builds/version_slug.py +++ b/readthedocs/builds/version_slug.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Contains logic for handling version slugs. diff --git a/readthedocs/config/__init__.py b/readthedocs/config/__init__.py index d0c70ef6bd2..23006b4c46e 100644 --- a/readthedocs/config/__init__.py +++ b/readthedocs/config/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Logic to parse and validate ``readthedocs.yaml`` file.""" from .config import * # noqa from .parser import * # noqa diff --git a/readthedocs/config/find.py b/readthedocs/config/find.py index 6fc3cb6a7a9..2fe5292c45e 100644 --- a/readthedocs/config/find.py +++ b/readthedocs/config/find.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Helper functions to search files.""" import os diff --git a/readthedocs/config/models.py b/readthedocs/config/models.py index 1153fe3f981..bc6f381e805 100644 --- a/readthedocs/config/models.py +++ b/readthedocs/config/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Models for the response of the configuration object.""" from collections import namedtuple diff --git a/readthedocs/config/validation.py b/readthedocs/config/validation.py index b1840b880aa..5d7651dffd8 100644 --- a/readthedocs/config/validation.py +++ b/readthedocs/config/validation.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Validations for the RTD configuration file.""" import os diff --git a/readthedocs/constants.py b/readthedocs/constants.py index 485b937398d..579b937715a 100644 --- a/readthedocs/constants.py +++ b/readthedocs/constants.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Common constants.""" from readthedocs.builds.version_slug import VERSION_SLUG_REGEX diff --git a/readthedocs/core/__init__.py b/readthedocs/core/__init__.py index c6205d82c60..21fae505765 100644 --- a/readthedocs/core/__init__.py +++ b/readthedocs/core/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """App initialization.""" default_app_config = 'readthedocs.core.apps.CoreAppConfig' diff --git a/readthedocs/core/adapters.py b/readthedocs/core/adapters.py index aa2fad046dc..c91ad9c3619 100644 --- a/readthedocs/core/adapters.py +++ b/readthedocs/core/adapters.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Allauth overrides.""" import json diff --git a/readthedocs/core/admin.py b/readthedocs/core/admin.py index da3ebfa15b9..d0f2051d47c 100644 --- a/readthedocs/core/admin.py +++ b/readthedocs/core/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django admin interface for core models.""" from datetime import timedelta diff --git a/readthedocs/core/apps.py b/readthedocs/core/apps.py index 0944efc1646..ac5a39d63fd 100644 --- a/readthedocs/core/apps.py +++ b/readthedocs/core/apps.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """App configurations for core app.""" from django.apps import AppConfig diff --git a/readthedocs/core/backends.py b/readthedocs/core/backends.py index 381be54dc86..b7f9bf788a7 100644 --- a/readthedocs/core/backends.py +++ b/readthedocs/core/backends.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Email backends for core app.""" import smtplib diff --git a/readthedocs/core/context_processors.py b/readthedocs/core/context_processors.py index 815afbdaa6a..536c1fe46e5 100644 --- a/readthedocs/core/context_processors.py +++ b/readthedocs/core/context_processors.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Template context processors for core app.""" from django.conf import settings diff --git a/readthedocs/core/fields.py b/readthedocs/core/fields.py index 1b6d9f99420..5801d30146b 100644 --- a/readthedocs/core/fields.py +++ b/readthedocs/core/fields.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Shared model fields and defaults.""" import binascii diff --git a/readthedocs/core/management/commands/archive.py b/readthedocs/core/management/commands/archive.py index e90d8de00a0..14e87f7df7d 100644 --- a/readthedocs/core/management/commands/archive.py +++ b/readthedocs/core/management/commands/archive.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Rebuild documentation for all projects.""" import logging diff --git a/readthedocs/core/management/commands/clean_builds.py b/readthedocs/core/management/commands/clean_builds.py index 64d52ec84a0..e6f03282490 100644 --- a/readthedocs/core/management/commands/clean_builds.py +++ b/readthedocs/core/management/commands/clean_builds.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Clean up stable build paths per project version.""" import logging diff --git a/readthedocs/core/management/commands/import_github.py b/readthedocs/core/management/commands/import_github.py index 85530968f4b..6c8cb5b5c96 100644 --- a/readthedocs/core/management/commands/import_github.py +++ b/readthedocs/core/management/commands/import_github.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Resync GitHub project for user.""" from django.contrib.auth.models import User diff --git a/readthedocs/core/management/commands/import_github_language.py b/readthedocs/core/management/commands/import_github_language.py index e3679a52614..f0da49cdd07 100644 --- a/readthedocs/core/management/commands/import_github_language.py +++ b/readthedocs/core/management/commands/import_github_language.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Import a project's programming language from GitHub. diff --git a/readthedocs/core/management/commands/provision_elasticsearch.py b/readthedocs/core/management/commands/provision_elasticsearch.py index adab81d4958..b1efe643c61 100644 --- a/readthedocs/core/management/commands/provision_elasticsearch.py +++ b/readthedocs/core/management/commands/provision_elasticsearch.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Provision Elastic Search.""" import logging diff --git a/readthedocs/core/management/commands/pull.py b/readthedocs/core/management/commands/pull.py index 3c965f8b1af..ae62f9da10c 100644 --- a/readthedocs/core/management/commands/pull.py +++ b/readthedocs/core/management/commands/pull.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Trigger build for project slug.""" import logging diff --git a/readthedocs/core/management/commands/reindex_elasticsearch.py b/readthedocs/core/management/commands/reindex_elasticsearch.py index 0d552e86e3e..bcc199a26c1 100644 --- a/readthedocs/core/management/commands/reindex_elasticsearch.py +++ b/readthedocs/core/management/commands/reindex_elasticsearch.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Reindex Elastic Search indexes.""" import logging diff --git a/readthedocs/core/management/commands/set_metadata.py b/readthedocs/core/management/commands/set_metadata.py index e5782e404ef..33736b7a765 100644 --- a/readthedocs/core/management/commands/set_metadata.py +++ b/readthedocs/core/management/commands/set_metadata.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Generate metadata for all projects.""" import logging diff --git a/readthedocs/core/management/commands/symlink.py b/readthedocs/core/management/commands/symlink.py index a7743a74057..41719354c7c 100644 --- a/readthedocs/core/management/commands/symlink.py +++ b/readthedocs/core/management/commands/symlink.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Update symlinks for projects.""" import logging diff --git a/readthedocs/core/management/commands/update_api.py b/readthedocs/core/management/commands/update_api.py index 5b87bd4a571..68b695c4d52 100644 --- a/readthedocs/core/management/commands/update_api.py +++ b/readthedocs/core/management/commands/update_api.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Build documentation using the API and not hitting a database. diff --git a/readthedocs/core/management/commands/update_versions.py b/readthedocs/core/management/commands/update_versions.py index bf066c7757b..aacb47635a1 100644 --- a/readthedocs/core/management/commands/update_versions.py +++ b/readthedocs/core/management/commands/update_versions.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Rebuild documentation for all projects.""" from django.core.management.base import BaseCommand diff --git a/readthedocs/core/middleware.py b/readthedocs/core/middleware.py index 1a874e12011..c40dbfd0f41 100644 --- a/readthedocs/core/middleware.py +++ b/readthedocs/core/middleware.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Middleware for core app.""" import logging diff --git a/readthedocs/core/mixins.py b/readthedocs/core/mixins.py index a2cf841aeda..4d6b1160c1c 100644 --- a/readthedocs/core/mixins.py +++ b/readthedocs/core/mixins.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Common mixin classes for views.""" from django.contrib.auth.decorators import login_required diff --git a/readthedocs/core/permissions.py b/readthedocs/core/permissions.py index 28d01bd7a90..1c397eae11c 100644 --- a/readthedocs/core/permissions.py +++ b/readthedocs/core/permissions.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Objects for User permission checks.""" from readthedocs.core.utils.extend import SettingsOverrideObject diff --git a/readthedocs/core/resolver.py b/readthedocs/core/resolver.py index 10b5f42d562..389fedc9843 100644 --- a/readthedocs/core/resolver.py +++ b/readthedocs/core/resolver.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """URL resolver for documentation.""" import re diff --git a/readthedocs/core/symlink.py b/readthedocs/core/symlink.py index b43da937ee4..3de4c2b3742 100644 --- a/readthedocs/core/symlink.py +++ b/readthedocs/core/symlink.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ A class that manages the symlinks for nginx to serve public files. @@ -297,8 +298,9 @@ def symlink_single_version(self): """ Symlink project single version. - Link from $WEB_ROOT/ -> - HOME/user_builds//rtd-builds/latest/ + Link from: + + $WEB_ROOT/ -> HOME/user_builds//rtd-builds/latest/ """ version = self.get_default_version() diff --git a/readthedocs/core/tasks.py b/readthedocs/core/tasks.py index d053614c5ee..e4f3e329c10 100644 --- a/readthedocs/core/tasks.py +++ b/readthedocs/core/tasks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Basic tasks.""" import logging diff --git a/readthedocs/core/templatetags/core_tags.py b/readthedocs/core/templatetags/core_tags.py index b4862e5257f..3df7c57697b 100644 --- a/readthedocs/core/templatetags/core_tags.py +++ b/readthedocs/core/templatetags/core_tags.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Template tags for core app.""" import hashlib diff --git a/readthedocs/core/templatetags/privacy_tags.py b/readthedocs/core/templatetags/privacy_tags.py index fbbe9804140..e81a6f3a3b9 100644 --- a/readthedocs/core/templatetags/privacy_tags.py +++ b/readthedocs/core/templatetags/privacy_tags.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Template tags to query projects by privacy.""" from django import template diff --git a/readthedocs/core/urls/__init__.py b/readthedocs/core/urls/__init__.py index 4c0951e9cf1..60e7fd32325 100644 --- a/readthedocs/core/urls/__init__.py +++ b/readthedocs/core/urls/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """URL configuration for core app.""" from __future__ import absolute_import diff --git a/readthedocs/core/urls/single_version.py b/readthedocs/core/urls/single_version.py index f8d33a761c7..dedc458e998 100644 --- a/readthedocs/core/urls/single_version.py +++ b/readthedocs/core/urls/single_version.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """URL configuration for a single version.""" from functools import reduce from operator import add diff --git a/readthedocs/core/urls/subdomain.py b/readthedocs/core/urls/subdomain.py index cddc49a98a5..b639bc4735f 100644 --- a/readthedocs/core/urls/subdomain.py +++ b/readthedocs/core/urls/subdomain.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """URL configurations for subdomains.""" from functools import reduce from operator import add diff --git a/readthedocs/core/utils/tasks/__init__.py b/readthedocs/core/utils/tasks/__init__.py index 492708ad57f..7aede3ac1dc 100644 --- a/readthedocs/core/utils/tasks/__init__.py +++ b/readthedocs/core/utils/tasks/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Common task exports.""" from .permission_checks import user_id_matches # noqa for unused import diff --git a/readthedocs/core/utils/tasks/permission_checks.py b/readthedocs/core/utils/tasks/permission_checks.py index 4a32e6a1a34..84f545830e1 100644 --- a/readthedocs/core/utils/tasks/permission_checks.py +++ b/readthedocs/core/utils/tasks/permission_checks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Permission checks for tasks.""" __all__ = ('user_id_matches',) diff --git a/readthedocs/core/utils/tasks/retrieve.py b/readthedocs/core/utils/tasks/retrieve.py index a2b008d32dd..9281ad8a1af 100644 --- a/readthedocs/core/utils/tasks/retrieve.py +++ b/readthedocs/core/utils/tasks/retrieve.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Utilities for retrieving task data.""" from celery import states diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index 16213e24bb6..b97583c64b7 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Views pertaining to builds.""" import json diff --git a/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py index 805eb0ce5d3..5f96a25a651 100644 --- a/readthedocs/doc_builder/backends/mkdocs.py +++ b/readthedocs/doc_builder/backends/mkdocs.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ MkDocs_ backend for building docs. diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py index ed158933ba9..f1517cf7c9c 100644 --- a/readthedocs/doc_builder/environments.py +++ b/readthedocs/doc_builder/environments.py @@ -665,6 +665,7 @@ def update_build(self, state=None): if self.failure is not None: # Surface a generic error if the class is not a # BuildEnvironmentError + # yapf: disable if not isinstance( self.failure, ( @@ -672,6 +673,7 @@ def update_build(self, state=None): BuildEnvironmentWarning, ), ): + # yapf: enable log.error( 'Build failed with unhandled exception: %s', str(self.failure), diff --git a/readthedocs/doc_builder/loader.py b/readthedocs/doc_builder/loader.py index b878009d206..016cc6bba9a 100644 --- a/readthedocs/doc_builder/loader.py +++ b/readthedocs/doc_builder/loader.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Lookup tables for builders and backends.""" from importlib import import_module diff --git a/readthedocs/doc_builder/signals.py b/readthedocs/doc_builder/signals.py index 9911ba3ab5c..5821ddeccf2 100644 --- a/readthedocs/doc_builder/signals.py +++ b/readthedocs/doc_builder/signals.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Signals for adding custom context data.""" import django.dispatch diff --git a/readthedocs/gold/__init__.py b/readthedocs/gold/__init__.py index b7fcb9ea10e..2ef7166adf3 100644 --- a/readthedocs/gold/__init__.py +++ b/readthedocs/gold/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ A Django app for Gold Membership. diff --git a/readthedocs/gold/admin.py b/readthedocs/gold/admin.py index 020d2be9493..2da5d033444 100644 --- a/readthedocs/gold/admin.py +++ b/readthedocs/gold/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django admin configuration for the Gold Membership app.""" from django.contrib import admin diff --git a/readthedocs/gold/apps.py b/readthedocs/gold/apps.py index 5891264c8ef..c54b9d5424e 100644 --- a/readthedocs/gold/apps.py +++ b/readthedocs/gold/apps.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django app configuration for the Gold Membership app.""" from django.apps import AppConfig diff --git a/readthedocs/gold/forms.py b/readthedocs/gold/forms.py index 4840745d8b8..730f2929636 100644 --- a/readthedocs/gold/forms.py +++ b/readthedocs/gold/forms.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Gold subscription forms.""" from django import forms diff --git a/readthedocs/gold/signals.py b/readthedocs/gold/signals.py index 155230f3854..a5b377290be 100644 --- a/readthedocs/gold/signals.py +++ b/readthedocs/gold/signals.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Gold model signals.""" from django.db.models.signals import pre_delete diff --git a/readthedocs/gold/urls.py b/readthedocs/gold/urls.py index c57ccab39b8..2a6b2047b0c 100644 --- a/readthedocs/gold/urls.py +++ b/readthedocs/gold/urls.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Gold subscription URLs.""" from django.conf.urls import url diff --git a/readthedocs/integrations/admin.py b/readthedocs/integrations/admin.py index 06001c024f4..9b328647343 100644 --- a/readthedocs/integrations/admin.py +++ b/readthedocs/integrations/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Integration admin models.""" from django import urls diff --git a/readthedocs/integrations/utils.py b/readthedocs/integrations/utils.py index e4c78bf4750..0939a36a855 100644 --- a/readthedocs/integrations/utils.py +++ b/readthedocs/integrations/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Integration utility functions.""" diff --git a/readthedocs/notifications/__init__.py b/readthedocs/notifications/__init__.py index 74496a1c90b..2802d286a1e 100644 --- a/readthedocs/notifications/__init__.py +++ b/readthedocs/notifications/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Extensions to Django messages to support notifications to users. diff --git a/readthedocs/notifications/apps.py b/readthedocs/notifications/apps.py index 9876fdb521e..60543374e05 100644 --- a/readthedocs/notifications/apps.py +++ b/readthedocs/notifications/apps.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django app configuration for the notifications app.""" from django.apps import AppConfig diff --git a/readthedocs/notifications/constants.py b/readthedocs/notifications/constants.py index 2716e6dfbea..640170aff40 100644 --- a/readthedocs/notifications/constants.py +++ b/readthedocs/notifications/constants.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Notification constants.""" from messages_extends import constants as message_constants diff --git a/readthedocs/notifications/forms.py b/readthedocs/notifications/forms.py index 4a6d480398d..83b68b2cff7 100644 --- a/readthedocs/notifications/forms.py +++ b/readthedocs/notifications/forms.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """HTML forms for sending notifications.""" from django import forms from django.utils.translation import ugettext_lazy as _ diff --git a/readthedocs/notifications/urls.py b/readthedocs/notifications/urls.py index 1b03e5b3843..91803e3efef 100644 --- a/readthedocs/notifications/urls.py +++ b/readthedocs/notifications/urls.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Renames for messages_extends URLs.""" from django.conf.urls import url diff --git a/readthedocs/oauth/admin.py b/readthedocs/oauth/admin.py index dd221e9b2da..eeee459e6ec 100644 --- a/readthedocs/oauth/admin.py +++ b/readthedocs/oauth/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Admin configuration for the OAuth app.""" from django.contrib import admin diff --git a/readthedocs/oauth/apps.py b/readthedocs/oauth/apps.py index c064f8fc8ee..1486873907b 100644 --- a/readthedocs/oauth/apps.py +++ b/readthedocs/oauth/apps.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """OAuth app config.""" from django.apps import AppConfig diff --git a/readthedocs/oauth/querysets.py b/readthedocs/oauth/querysets.py index f410e96663a..e7f20dc184e 100644 --- a/readthedocs/oauth/querysets.py +++ b/readthedocs/oauth/querysets.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Managers for OAuth models.""" from django.db import models diff --git a/readthedocs/oauth/services/bitbucket.py b/readthedocs/oauth/services/bitbucket.py index b980df55d4b..e267e5afe73 100644 --- a/readthedocs/oauth/services/bitbucket.py +++ b/readthedocs/oauth/services/bitbucket.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """OAuth utility functions.""" import json diff --git a/readthedocs/oauth/services/github.py b/readthedocs/oauth/services/github.py index 19bd3c3768c..a3f22b0e144 100644 --- a/readthedocs/oauth/services/github.py +++ b/readthedocs/oauth/services/github.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """OAuth utility functions.""" import json diff --git a/readthedocs/payments/forms.py b/readthedocs/payments/forms.py index e64813e6a40..d313c9e552f 100644 --- a/readthedocs/payments/forms.py +++ b/readthedocs/payments/forms.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Payment forms.""" import logging diff --git a/readthedocs/payments/mixins.py b/readthedocs/payments/mixins.py index 2ba34ec47e5..4bce56d8216 100644 --- a/readthedocs/payments/mixins.py +++ b/readthedocs/payments/mixins.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Payment view mixin classes.""" from django.conf import settings diff --git a/readthedocs/payments/utils.py b/readthedocs/payments/utils.py index e85159ae944..ba1b045a2f8 100644 --- a/readthedocs/payments/utils.py +++ b/readthedocs/payments/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Payment utility functions. diff --git a/readthedocs/profiles/urls/private.py b/readthedocs/profiles/urls/private.py index 1fe6d288643..ca6c13428f0 100644 --- a/readthedocs/profiles/urls/private.py +++ b/readthedocs/profiles/urls/private.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """URL patterns for views to modify user profiles.""" from django.conf.urls import url diff --git a/readthedocs/profiles/urls/public.py b/readthedocs/profiles/urls/public.py index 7b028431265..d2cec291bae 100644 --- a/readthedocs/profiles/urls/public.py +++ b/readthedocs/profiles/urls/public.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """URL patterns to view user profiles.""" from django.conf.urls import url diff --git a/readthedocs/projects/apps.py b/readthedocs/projects/apps.py index 818c5b5e3d0..76b3fae1b69 100644 --- a/readthedocs/projects/apps.py +++ b/readthedocs/projects/apps.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Project app config.""" from django.apps import AppConfig diff --git a/readthedocs/projects/backends/views.py b/readthedocs/projects/backends/views.py index 326a7c9637f..ebae2d8b173 100644 --- a/readthedocs/projects/backends/views.py +++ b/readthedocs/projects/backends/views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Project views loaded by configuration settings. diff --git a/readthedocs/projects/feeds.py b/readthedocs/projects/feeds.py index 4a578c6a922..35ebdd6ac32 100644 --- a/readthedocs/projects/feeds.py +++ b/readthedocs/projects/feeds.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Project RSS feeds.""" from django.contrib.syndication.views import Feed diff --git a/readthedocs/projects/management/commands/import_project_from_live.py b/readthedocs/projects/management/commands/import_project_from_live.py index 4f823cffd3d..2b006f84ecb 100644 --- a/readthedocs/projects/management/commands/import_project_from_live.py +++ b/readthedocs/projects/management/commands/import_project_from_live.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Import project command.""" import json diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index 12cf77ea9ab..1a901204d15 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -847,7 +847,7 @@ def get_latest_build(self, finished=True): def api_versions(self): from readthedocs.builds.models import APIVersion ret = [] - for version_data in api.project(self.pk).active_versions.get()['versions']: + for version_data in api.project(self.pk).active_versions.get()['versions']: # yapf: disable version = APIVersion(**version_data) ret.append(version) return sort_version_aware(ret) @@ -1044,14 +1044,16 @@ def __init__(self, *args, **kwargs): ad_free = (not kwargs.pop('show_advertising', True)) # These fields only exist on the API return, not on the model, so we'll # remove them to avoid throwing exceptions due to unexpected fields + # yapf: disable for key in [ - 'users', - 'resource_uri', - 'absolute_url', - 'downloads', - 'main_language_project', - 'related_projects', + 'users', + 'resource_uri', + 'absolute_url', + 'downloads', + 'main_language_project', + 'related_projects', ]: + # yapf: enable try: del kwargs[key] except KeyError: diff --git a/readthedocs/projects/notifications.py b/readthedocs/projects/notifications.py index 9d108ab6927..2dc1084090e 100644 --- a/readthedocs/projects/notifications.py +++ b/readthedocs/projects/notifications.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Project notifications.""" from readthedocs.notifications import Notification diff --git a/readthedocs/projects/templatetags/projects_tags.py b/readthedocs/projects/templatetags/projects_tags.py index a655487a05d..6a6d6d16a2e 100644 --- a/readthedocs/projects/templatetags/projects_tags.py +++ b/readthedocs/projects/templatetags/projects_tags.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Project template tags and filters.""" from django import template diff --git a/readthedocs/projects/urls/private.py b/readthedocs/projects/urls/private.py index 82c29500cab..62449ca1f98 100644 --- a/readthedocs/projects/urls/private.py +++ b/readthedocs/projects/urls/private.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Project URLs for authenticated users.""" from django.conf.urls import url diff --git a/readthedocs/projects/urls/public.py b/readthedocs/projects/urls/public.py index 7b17b476db9..f353714ac19 100644 --- a/readthedocs/projects/urls/public.py +++ b/readthedocs/projects/urls/public.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Project URLS for public users.""" from django.conf.urls import url diff --git a/readthedocs/projects/validators.py b/readthedocs/projects/validators.py index 8fe3bf7f530..dc64b3fc086 100644 --- a/readthedocs/projects/validators.py +++ b/readthedocs/projects/validators.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Validators for projects app.""" import re diff --git a/readthedocs/projects/views/mixins.py b/readthedocs/projects/views/mixins.py index 5ce119e4ea4..670caa21f83 100644 --- a/readthedocs/projects/views/mixins.py +++ b/readthedocs/projects/views/mixins.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Mixin classes for project views.""" from django.shortcuts import get_object_or_404 diff --git a/readthedocs/redirects/admin.py b/readthedocs/redirects/admin.py index 8b97822657f..4ce0239d48f 100644 --- a/readthedocs/redirects/admin.py +++ b/readthedocs/redirects/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django admin configuration for the redirects app.""" from django.contrib import admin diff --git a/readthedocs/redirects/managers.py b/readthedocs/redirects/managers.py index c0f48f83e4a..37e10890cc9 100644 --- a/readthedocs/redirects/managers.py +++ b/readthedocs/redirects/managers.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Manager and queryset for the redirects app.""" from django.db.models import Manager diff --git a/readthedocs/redirects/models.py b/readthedocs/redirects/models.py index 478b746304b..7945ecb5a82 100644 --- a/readthedocs/redirects/models.py +++ b/readthedocs/redirects/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django models for the redirects app.""" import logging diff --git a/readthedocs/redirects/utils.py b/readthedocs/redirects/utils.py index e6446e136e5..ce1d7514083 100644 --- a/readthedocs/redirects/utils.py +++ b/readthedocs/redirects/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Redirection view support. diff --git a/readthedocs/restapi/permissions.py b/readthedocs/restapi/permissions.py index e08087824e7..93d4695a7cc 100644 --- a/readthedocs/restapi/permissions.py +++ b/readthedocs/restapi/permissions.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Defines access permissions for the API.""" from rest_framework import permissions diff --git a/readthedocs/restapi/serializers.py b/readthedocs/restapi/serializers.py index bded753b453..17778f77732 100644 --- a/readthedocs/restapi/serializers.py +++ b/readthedocs/restapi/serializers.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Defines serializers for each of our models.""" from allauth.socialaccount.models import SocialAccount diff --git a/readthedocs/restapi/signals.py b/readthedocs/restapi/signals.py index e43a701346e..65509fc551d 100644 --- a/readthedocs/restapi/signals.py +++ b/readthedocs/restapi/signals.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """We define custom Django signals to trigger when a footer is rendered.""" import django.dispatch diff --git a/readthedocs/restapi/views/core_views.py b/readthedocs/restapi/views/core_views.py index 9ce63018ebe..31d1d5f7508 100644 --- a/readthedocs/restapi/views/core_views.py +++ b/readthedocs/restapi/views/core_views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Utility endpoints relating to canonical urls, embedded content, etc.""" from django.shortcuts import get_object_or_404 diff --git a/readthedocs/restapi/views/search_views.py b/readthedocs/restapi/views/search_views.py index b799da215a0..da0dab870cb 100644 --- a/readthedocs/restapi/views/search_views.py +++ b/readthedocs/restapi/views/search_views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Endpoints related to searching through projects, sections, etc.""" import logging diff --git a/readthedocs/restapi/views/task_views.py b/readthedocs/restapi/views/task_views.py index a61e798a022..8bf9d3843e4 100644 --- a/readthedocs/restapi/views/task_views.py +++ b/readthedocs/restapi/views/task_views.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Endpoints relating to task/job status, etc.""" import logging diff --git a/readthedocs/search/indexes.py b/readthedocs/search/indexes.py index e6f3d81dad2..a9fe07b0267 100644 --- a/readthedocs/search/indexes.py +++ b/readthedocs/search/indexes.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Search indexing classes to index into Elasticsearch. diff --git a/readthedocs/search/lib.py b/readthedocs/search/lib.py index 9167e941861..a1f0cc2095f 100644 --- a/readthedocs/search/lib.py +++ b/readthedocs/search/lib.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Utilities related to searching Elastic.""" from pprint import pprint diff --git a/readthedocs/search/signals.py b/readthedocs/search/signals.py index 60b4a4644ab..17cc3649155 100644 --- a/readthedocs/search/signals.py +++ b/readthedocs/search/signals.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """We define custom Django signals to trigger before executing searches.""" import django.dispatch diff --git a/readthedocs/vcs_support/backends/__init__.py b/readthedocs/vcs_support/backends/__init__.py index 37dde39b655..0a022a545d1 100644 --- a/readthedocs/vcs_support/backends/__init__.py +++ b/readthedocs/vcs_support/backends/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Listing of all the VCS backends.""" from __future__ import absolute_import from . import bzr, hg, git, svn diff --git a/readthedocs/vcs_support/utils.py b/readthedocs/vcs_support/utils.py index 99001668824..65f9e3839d3 100644 --- a/readthedocs/vcs_support/utils.py +++ b/readthedocs/vcs_support/utils.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Locking utilities.""" import errno import logging diff --git a/readthedocs/wsgi.py b/readthedocs/wsgi.py index 9d35da5f3a9..997fb628506 100644 --- a/readthedocs/wsgi.py +++ b/readthedocs/wsgi.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """WSGI application helper.""" from __future__ import absolute_import From 0f7b8d7f5e254a15cb04e5dc47a211d08a402646 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:09:07 +0100 Subject: [PATCH 28/42] pyupgrade over new code with conflicts --- readthedocs/builds/models.py | 18 ++--- readthedocs/config/config.py | 14 ++-- readthedocs/config/tests/test_config.py | 14 ++-- readthedocs/core/views/hooks.py | 8 +-- readthedocs/core/views/serve.py | 2 +- readthedocs/projects/admin.py | 6 +- readthedocs/projects/forms.py | 66 +++++++++---------- readthedocs/projects/models.py | 28 ++++---- readthedocs/projects/views/private.py | 2 +- readthedocs/rtd_tests/tests/test_celery.py | 4 +- .../rtd_tests/tests/test_notifications.py | 4 +- .../rtd_tests/tests/test_privacy_urls.py | 10 +-- 12 files changed, 88 insertions(+), 88 deletions(-) diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index 70ac5ed19da..d4b58bb593b 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -114,7 +114,7 @@ class Version(models.Model): objects = VersionManager.from_queryset(VersionQuerySet)() - class Meta(object): + class Meta: unique_together = [('project', 'slug')] ordering = ['-verbose_name'] permissions = ( @@ -203,7 +203,7 @@ def get_absolute_url(self): def save(self, *args, **kwargs): # pylint: disable=arguments-differ """Add permissions to the Version for all owners on save.""" from readthedocs.projects import tasks - obj = super(Version, self).save(*args, **kwargs) + obj = super().save(*args, **kwargs) for owner in self.project.users.all(): assign('view_version', owner, self) broadcast( @@ -219,7 +219,7 @@ def delete(self, *args, **kwargs): # pylint: disable=arguments-differ args=[self.get_artifact_paths()], ) project_pk = self.project.pk - super(Version, self).delete(*args, **kwargs) + super().delete(*args, **kwargs) broadcast( type='app', task=tasks.symlink_project, @@ -442,7 +442,7 @@ def __init__(self, *args, **kwargs): del kwargs[key] except KeyError: pass - super(APIVersion, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, *args, **kwargs): return 0 @@ -487,13 +487,13 @@ class Build(models.Model): CONFIG_KEY = '__config' - class Meta(object): + class Meta: ordering = ['-date'] get_latest_by = 'date' index_together = [['version', 'state', 'type']] def __init__(self, *args, **kwargs): - super(Build, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._config_changed = False @property @@ -557,7 +557,7 @@ def save(self, *args, **kwargs): # noqa self._config and self._config == previous.config): previous_pk = previous._config.get(self.CONFIG_KEY, previous.pk) self._config = {self.CONFIG_KEY: previous_pk} - super(Build, self).save(*args, **kwargs) + super().save(*args, **kwargs) self._config_changed = False def __str__(self): @@ -579,7 +579,7 @@ def finished(self): return self.state == BUILD_STATE_FINISHED -class BuildCommandResultMixin(object): +class BuildCommandResultMixin: """ Mixin for common command result methods/properties. @@ -619,7 +619,7 @@ class BuildCommandResult(BuildCommandResultMixin, models.Model): start_time = models.DateTimeField(_('Start time')) end_time = models.DateTimeField(_('End time')) - class Meta(object): + class Meta: ordering = ['start_time'] get_latest_by = 'start_time' diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index 223bd7b0a0b..5f7cd382f49 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -79,7 +79,7 @@ class ConfigError(Exception): def __init__(self, message, code): self.code = code - super(ConfigError, self).__init__(message) + super().__init__(message) class ConfigOptionNotSupportedError(ConfigError): @@ -91,7 +91,7 @@ def __init__(self, configuration): template = ( 'The "{}" configuration option is not supported in this version' ) - super(ConfigOptionNotSupportedError, self).__init__( + super().__init__( template.format(self.configuration), CONFIG_NOT_SUPPORTED ) @@ -112,10 +112,10 @@ def __init__(self, key, code, error_message, source_file=None): code=code, error=error_message, ) - super(InvalidConfig, self).__init__(message, code=code) + super().__init__(message, code=code) -class BuildConfigBase(object): +class BuildConfigBase: """ Config that handles the build of one particular documentation. @@ -223,7 +223,7 @@ def validate(self): @property def python_interpreter(self): ver = self.python_full_version - return 'python{0}'.format(ver) + return 'python{}'.format(ver) @property def python_full_version(self): @@ -417,7 +417,7 @@ def validate_python(self): # Try to convert strings to an int first, to catch '2', then # a float, to catch '2.7' version = raw_python['version'] - if isinstance(version, six.string_types): + if isinstance(version, str): try: version = int(version) except ValueError: @@ -650,7 +650,7 @@ def validate_python(self): python = {} with self.catch_validation_error('python.version'): version = self.pop_config('python.version', 3) - if isinstance(version, six.string_types): + if isinstance(version, str): try: version = int(version) except ValueError: diff --git a/readthedocs/config/tests/test_config.py b/readthedocs/config/tests/test_config.py index 593453d8cc4..0d223a671da 100644 --- a/readthedocs/config/tests/test_config.py +++ b/readthedocs/config/tests/test_config.py @@ -211,7 +211,7 @@ def test_python_pip_install_default(): assert build.python.install_with_pip is False -class TestValidatePythonExtraRequirements(object): +class TestValidatePythonExtraRequirements: def test_it_defaults_to_list(self): build = get_build_config({'python': {}}) @@ -243,7 +243,7 @@ def test_it_uses_validate_string(self, validate_string): validate_string.assert_any_call('tests') -class TestValidateUseSystemSitePackages(object): +class TestValidateUseSystemSitePackages: def test_it_defaults_to_false(self): build = get_build_config({'python': {}}) @@ -269,7 +269,7 @@ def test_it_uses_validate_bool(self, validate_bool): validate_bool.assert_any_call('to-validate') -class TestValidateSetupPyInstall(object): +class TestValidateSetupPyInstall: def test_it_defaults_to_false(self): build = get_build_config({'python': {}}) @@ -295,7 +295,7 @@ def test_it_uses_validate_bool(self, validate_bool): validate_bool.assert_any_call('to-validate') -class TestValidatePythonVersion(object): +class TestValidatePythonVersion: def test_it_defaults_to_a_valid_version(self): build = get_build_config({'python': {}}) @@ -386,7 +386,7 @@ def test_it_respects_default_value(self, value): assert build.python.version == value -class TestValidateFormats(object): +class TestValidateFormats: def test_it_defaults_to_empty(self): build = get_build_config({}) @@ -459,7 +459,7 @@ def test_valid_build_config(): assert build.python.use_system_site_packages is False -class TestValidateBuild(object): +class TestValidateBuild: def test_it_fails_if_build_is_invalid_option(self, tmpdir): apply_fs(tmpdir, yaml_config_dir) @@ -704,7 +704,7 @@ def test_as_dict(tmpdir): assert build.as_dict() == expected_dict -class TestBuildConfigV2(object): +class TestBuildConfigV2: def get_build_config( self, config, env_config=None, source_file='readthedocs.yml'): diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index c6d4bc91188..6ed9855b60c 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -156,14 +156,14 @@ def _build_url(url, projects, branches): for project_slug, built in list(all_built.items()): if built: - msg = '(URL Build) Build Started: %s [%s]' % ( + msg = '(URL Build) Build Started: {} [{}]'.format( url, ' '.join(built)) log_info(project_slug, msg=msg) ret += msg for project_slug, not_building in list(all_not_building.items()): if not_building: - msg = '(URL Build) Not Building: %s [%s]' % ( + msg = '(URL Build) Not Building: {} [{}]'.format( url, ' '.join(not_building)) log_info(project_slug, msg=msg) ret += msg @@ -300,7 +300,7 @@ def bitbucket_build(request): repository = data['repository'] if not repository['absolute_url']: return HttpResponse('Invalid request', status=400) - search_url = 'bitbucket.org{0}'.format( + search_url = 'bitbucket.org{}'.format( repository['absolute_url'].rstrip('/') ) elif version == 2: @@ -309,7 +309,7 @@ def bitbucket_build(request): for change in changes] if not data['repository']['full_name']: return HttpResponse('Invalid request', status=400) - search_url = 'bitbucket.org/{0}'.format( + search_url = 'bitbucket.org/{}'.format( data['repository']['full_name'] ) except (TypeError, ValueError, KeyError): diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 2b24b45d95c..168b1aac21b 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -117,7 +117,7 @@ def redirect_page_with_filename(request, project, subproject, filename): # pyli def _serve_401(request, project): res = render(request, '401.html') res.status_code = 401 - log.debug('Unauthorized access to {0} documentation'.format(project.slug)) + log.debug('Unauthorized access to {} documentation'.format(project.slug)) return res diff --git a/readthedocs/projects/admin.py b/readthedocs/projects/admin.py index 9f6da41ddd9..6bead7e3586 100644 --- a/readthedocs/projects/admin.py +++ b/readthedocs/projects/admin.py @@ -164,12 +164,12 @@ def ban_owner(self, request, queryset): total += count else: messages.add_message(request, messages.ERROR, - 'Project has multiple owners: {0}'.format(project)) + 'Project has multiple owners: {}'.format(project)) if total == 0: messages.add_message(request, messages.ERROR, 'No users banned') else: messages.add_message(request, messages.INFO, - 'Banned {0} user(s)'.format(total)) + 'Banned {} user(s)'.format(total)) ban_owner.short_description = 'Ban project owner' @@ -190,7 +190,7 @@ def delete_selected_and_artifacts(self, request, queryset): return delete_selected(self, request, queryset) def get_actions(self, request): - actions = super(ProjectAdmin, self).get_actions(request) + actions = super().get_actions(request) actions['delete_selected'] = ( self.__class__.delete_selected_and_artifacts, 'delete_selected', diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index 70fcc88d7e6..6486472e17e 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -65,17 +65,17 @@ class ProjectForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) - super(ProjectForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, commit=True): - project = super(ProjectForm, self).save(commit) + project = super().save(commit) if commit: if self.user and not project.users.filter(pk=self.user.pk).exists(): project.users.add(self.user) return project -class ProjectTriggerBuildMixin(object): +class ProjectTriggerBuildMixin: """ Mixin to trigger build on form save. @@ -86,7 +86,7 @@ class ProjectTriggerBuildMixin(object): def save(self, commit=True): """Trigger build on commit save.""" - project = super(ProjectTriggerBuildMixin, self).save(commit) + project = super().save(commit) if commit: trigger_build(project=project) return project @@ -103,7 +103,7 @@ class ProjectBasicsForm(ProjectForm): """Form for basic project fields.""" - class Meta(object): + class Meta: model = Project fields = ('name', 'repo', 'repo_type') @@ -114,7 +114,7 @@ class Meta(object): def __init__(self, *args, **kwargs): show_advanced = kwargs.pop('show_advanced', False) - super(ProjectBasicsForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if show_advanced: self.fields['advanced'] = forms.BooleanField( required=False, @@ -125,7 +125,7 @@ def __init__(self, *args, **kwargs): def save(self, commit=True): """Add remote repository relationship to the project instance.""" - instance = super(ProjectBasicsForm, self).save(commit) + instance = super().save(commit) remote_repo = self.cleaned_data.get('remote_repository', None) if remote_repo: if commit: @@ -178,7 +178,7 @@ class ProjectExtraForm(ProjectForm): """Additional project information form.""" - class Meta(object): + class Meta: model = Project fields = ( 'description', @@ -216,7 +216,7 @@ class ProjectAdvancedForm(ProjectTriggerBuildMixin, ProjectForm): 'environment.'), ) - class Meta(object): + class Meta: model = Project fields = ( # Standard build edits @@ -240,7 +240,7 @@ class Meta(object): ) def __init__(self, *args, **kwargs): - super(ProjectAdvancedForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) default_choice = (None, '-' * 9) all_versions = self.instance.versions.values_list( @@ -268,7 +268,7 @@ def clean_conf_py_file(self): class UpdateProjectForm(ProjectTriggerBuildMixin, ProjectBasicsForm, ProjectExtraForm): - class Meta(object): + class Meta: model = Project fields = ( # Basics @@ -321,14 +321,14 @@ class ProjectRelationshipBaseForm(forms.ModelForm): parent = forms.CharField(widget=forms.HiddenInput(), required=False) - class Meta(object): + class Meta: model = ProjectRelationship fields = '__all__' def __init__(self, *args, **kwargs): self.project = kwargs.pop('project') self.user = kwargs.pop('user') - super(ProjectRelationshipBaseForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Don't display the update form with an editable child, as it will be # filtered out from the queryset anyways. if hasattr(self, 'instance') and self.instance.pk is not None: @@ -375,11 +375,11 @@ class DualCheckboxWidget(forms.CheckboxInput): """Checkbox with link to the version's built documentation.""" def __init__(self, version, attrs=None, check_test=bool): - super(DualCheckboxWidget, self).__init__(attrs, check_test) + super().__init__(attrs, check_test) self.version = version def render(self, name, value, attrs=None, renderer=None): - checkbox = super(DualCheckboxWidget, self).render(name, value, attrs, renderer) + checkbox = super().render(name, value, attrs, renderer) icon = self.render_icon() return mark_safe('{}{}'.format(checkbox, icon)) @@ -472,7 +472,7 @@ class BaseUploadHTMLForm(forms.Form): def __init__(self, *args, **kwargs): self.request = kwargs.pop('request', None) - super(BaseUploadHTMLForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean(self): version_slug = self.cleaned_data['version'] @@ -512,7 +512,7 @@ class UserForm(forms.Form): def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(UserForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean_user(self): name = self.cleaned_data['user'] @@ -538,7 +538,7 @@ class EmailHookForm(forms.Form): def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(EmailHookForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean_email(self): self.email = EmailHook.objects.get_or_create( @@ -556,7 +556,7 @@ class WebHookForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(WebHookForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, commit=True): self.webhook = WebHook.objects.get_or_create( @@ -578,7 +578,7 @@ class TranslationBaseForm(forms.Form): def __init__(self, *args, **kwargs): self.parent = kwargs.pop('parent', None) self.user = kwargs.pop('user') - super(TranslationBaseForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields['project'].choices = self.get_choices() def get_choices(self): @@ -659,13 +659,13 @@ class RedirectForm(forms.ModelForm): """Form for project redirects.""" - class Meta(object): + class Meta: model = Redirect fields = ['redirect_type', 'from_url', 'to_url'] def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(RedirectForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def save(self, **_): # pylint: disable=arguments-differ # TODO this should respect the unused argument `commit`. It's not clear @@ -686,13 +686,13 @@ class DomainBaseForm(forms.ModelForm): project = forms.CharField(widget=forms.HiddenInput(), required=False) - class Meta(object): + class Meta: model = Domain exclude = ['machine', 'cname', 'count'] # pylint: disable=modelform-uses-exclude def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(DomainBaseForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean_project(self): return self.project @@ -730,13 +730,13 @@ class IntegrationForm(forms.ModelForm): project = forms.CharField(widget=forms.HiddenInput(), required=False) - class Meta(object): + class Meta: model = Integration exclude = ['provider_data', 'exchanges'] # pylint: disable=modelform-uses-exclude def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(IntegrationForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Alter the integration type choices to only provider webhooks self.fields['integration_type'].choices = Integration.WEBHOOK_INTEGRATIONS # yapf: disable # noqa @@ -745,20 +745,20 @@ def clean_project(self): def save(self, commit=True): self.instance = Integration.objects.subclass(self.instance) - return super(IntegrationForm, self).save(commit) + return super().save(commit) class ProjectAdvertisingForm(forms.ModelForm): """Project promotion opt-out form.""" - class Meta(object): + class Meta: model = Project fields = ['allow_promos'] def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(ProjectAdvertisingForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) class FeatureForm(forms.ModelForm): @@ -773,12 +773,12 @@ class FeatureForm(forms.ModelForm): feature_id = forms.ChoiceField() - class Meta(object): + class Meta: model = Feature fields = ['projects', 'feature_id', 'default_true'] def __init__(self, *args, **kwargs): - super(FeatureForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fields['feature_id'].choices = Feature.FEATURES @@ -792,13 +792,13 @@ class EnvironmentVariableForm(forms.ModelForm): project = forms.CharField(widget=forms.HiddenInput(), required=False) - class Meta(object): + class Meta: model = EnvironmentVariable fields = ('name', 'value', 'project') def __init__(self, *args, **kwargs): self.project = kwargs.pop('project', None) - super(EnvironmentVariableForm, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def clean_project(self): return self.project diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index f4fdea8f0ba..78d7793a402 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -57,12 +57,12 @@ class ProjectRelationship(models.Model): objects = ChildRelatedProjectQuerySet.as_manager() def __str__(self): - return '%s -> %s' % (self.parent, self.child) + return '{} -> {}'.format(self.parent, self.child) def save(self, *args, **kwargs): # pylint: disable=arguments-differ if not self.alias: self.alias = self.child.slug - super(ProjectRelationship, self).save(*args, **kwargs) + super().save(*args, **kwargs) # HACK def get_absolute_url(self): @@ -252,7 +252,7 @@ class Project(models.Model): objects = ProjectQuerySet.as_manager() all_objects = models.Manager() - class Meta(object): + class Meta: ordering = ('slug',) permissions = ( # Translators: Permission around whether a user can view the @@ -271,7 +271,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ self.slug = slugify(self.name) if not self.slug: raise Exception(_('Model must have slug')) - super(Project, self).save(*args, **kwargs) + super().save(*args, **kwargs) for owner in self.users.all(): assign('view_project', owner, self) try: @@ -375,7 +375,7 @@ def get_production_media_path(self, type_, version_slug, include_file=True): settings.PRODUCTION_MEDIA_ARTIFACTS, type_, self.slug, version_slug) if include_file: path = os.path.join( - path, '%s.%s' % (self.slug, type_.replace('htmlzip', 'zip'))) + path, '{}.{}'.format(self.slug, type_.replace('htmlzip', 'zip'))) return path def get_production_media_url(self, type_, version_slug, full_path=True): @@ -389,7 +389,7 @@ def get_production_media_url(self, type_, version_slug, full_path=True): except NoReverseMatch: return '' if full_path: - path = '//%s%s' % (settings.PRODUCTION_DOMAIN, path) + path = '//{}{}'.format(settings.PRODUCTION_DOMAIN, path) return path def subdomain(self): @@ -852,7 +852,7 @@ def __init__(self, *args, **kwargs): del kwargs[key] except KeyError: pass - super(APIProject, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # Overwrite the database property with the value from the API self.ad_free = ad_free @@ -899,7 +899,7 @@ def get_absolute_url(self): return resolve(project=self.project, version_slug=self.version.slug, filename=self.path) def __str__(self): - return '%s: %s' % (self.name, self.project) + return '{}: {}'.format(self.name, self.project) class Notification(models.Model): @@ -907,7 +907,7 @@ class Notification(models.Model): related_name='%(class)s_notifications') objects = RelatedProjectQuerySet.as_manager() - class Meta(object): + class Meta: abstract = True @@ -958,7 +958,7 @@ class Domain(models.Model): objects = RelatedProjectQuerySet.as_manager() - class Meta(object): + class Meta: ordering = ('-canonical', '-machine', 'domain') def __str__(self): @@ -971,7 +971,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ self.domain = parsed.netloc else: self.domain = parsed.path - super(Domain, self).save(*args, **kwargs) + super().save(*args, **kwargs) broadcast(type='app', task=tasks.symlink_domain, args=[self.project.pk, self.pk],) @@ -979,7 +979,7 @@ def delete(self, *args, **kwargs): # pylint: disable=arguments-differ from readthedocs.projects import tasks broadcast(type='app', task=tasks.symlink_domain, args=[self.project.pk, self.pk, True],) - super(Domain, self).delete(*args, **kwargs) + super().delete(*args, **kwargs) @python_2_unicode_compatible @@ -1052,7 +1052,7 @@ def add_features(sender, **kwargs): objects = FeatureQuerySet.as_manager() def __str__(self): - return '{0} feature'.format( + return '{} feature'.format( self.get_feature_display(), ) @@ -1087,4 +1087,4 @@ def __str__(self): def save(self, *args, **kwargs): # pylint: disable=arguments-differ self.value = shlex_quote(self.value) - return super(EnvironmentVariable, self).save(*args, **kwargs) + return super().save(*args, **kwargs) diff --git a/readthedocs/projects/views/private.py b/readthedocs/projects/views/private.py index 7a48331b190..81998a7e8c9 100644 --- a/readthedocs/projects/views/private.py +++ b/readthedocs/projects/views/private.py @@ -725,7 +725,7 @@ def get_success_url(self): class DomainList(DomainMixin, ListViewWithForm): def get_context_data(self, **kwargs): - ctx = super(DomainList, self).get_context_data(**kwargs) + ctx = super().get_context_data(**kwargs) # Retry validation on all domains if applicable for domain in ctx['domain_list']: diff --git a/readthedocs/rtd_tests/tests/test_celery.py b/readthedocs/rtd_tests/tests/test_celery.py index 7de03a97bf2..ccd2b70e394 100644 --- a/readthedocs/rtd_tests/tests/test_celery.py +++ b/readthedocs/rtd_tests/tests/test_celery.py @@ -31,7 +31,7 @@ class TestCeleryBuilding(RTDTestCase): def setUp(self): repo = make_test_git() self.repo = repo - super(TestCeleryBuilding, self).setUp() + super().setUp() self.eric = User(username='eric') self.eric.set_password('test') self.eric.save() @@ -45,7 +45,7 @@ def setUp(self): def tearDown(self): shutil.rmtree(self.repo) - super(TestCeleryBuilding, self).tearDown() + super().tearDown() def test_remove_dirs(self): directory = mkdtemp() diff --git a/readthedocs/rtd_tests/tests/test_notifications.py b/readthedocs/rtd_tests/tests/test_notifications.py index 1fa16323df4..4230f8f3e0a 100644 --- a/readthedocs/rtd_tests/tests/test_notifications.py +++ b/readthedocs/rtd_tests/tests/test_notifications.py @@ -51,7 +51,7 @@ class TestNotification(Notification): self.assertEqual(notify.get_template_names('site'), ['builds/notifications/foo_site.html']) self.assertEqual(notify.get_subject(), - 'This is {0}'.format(build.id)) + 'This is {}'.format(build.id)) self.assertEqual(notify.get_context_data(), {'foo': build, 'production_uri': 'https://readthedocs.org', @@ -90,7 +90,7 @@ class TestNotification(Notification): request=mock.ANY, template='core/email/common.txt', context={'content': 'Test'}, - subject=u'This is {}'.format(build.id), + subject='This is {}'.format(build.id), template_html='core/email/common.html', recipient=user.email, ) diff --git a/readthedocs/rtd_tests/tests/test_privacy_urls.py b/readthedocs/rtd_tests/tests/test_privacy_urls.py index 937574fbafe..52ec89d74f1 100644 --- a/readthedocs/rtd_tests/tests/test_privacy_urls.py +++ b/readthedocs/rtd_tests/tests/test_privacy_urls.py @@ -19,7 +19,7 @@ from readthedocs.rtd_tests.utils import create_user -class URLAccessMixin(object): +class URLAccessMixin: default_kwargs = {} response_data = {} @@ -93,10 +93,10 @@ def setUp(self): for not_obj in self.context_data: if isinstance(obj, list) or isinstance(obj, set) or isinstance(obj, tuple): self.assertNotIn(not_obj, obj) - print("%s not in %s" % (not_obj, obj)) + print("{} not in {}".format(not_obj, obj)) else: self.assertNotEqual(not_obj, obj) - print("%s is not %s" % (not_obj, obj)) + print("{} is not {}".format(not_obj, obj)) def _test_url(self, urlpatterns): deconstructed_urls = extract_views_from_urlpatterns(urlpatterns) @@ -134,7 +134,7 @@ def setUp(self): class ProjectMixin(URLAccessMixin): def setUp(self): - super(ProjectMixin, self).setUp() + super().setUp() self.build = get(Build, project=self.pip) self.tag = get(Tag, slug='coolness') self.subproject = get(Project, slug='sub', language='ja', @@ -313,7 +313,7 @@ def is_admin(self): class APIMixin(URLAccessMixin): def setUp(self): - super(APIMixin, self).setUp() + super().setUp() self.build = get(Build, project=self.pip) self.build_command_result = get(BuildCommandResult, project=self.pip) self.domain = get(Domain, url='http://docs.foobar.com', project=self.pip) From 1ffc03e14747cffad0aae0f3fd2bf271f7ea95c3 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:13:20 +0100 Subject: [PATCH 29/42] isort new files --- readthedocs/builds/models.py | 11 ++--------- readthedocs/config/config.py | 5 +---- readthedocs/core/urls/subdomain.py | 16 +++++++--------- readthedocs/core/views/hooks.py | 8 +------- readthedocs/core/views/serve.py | 9 +++------ readthedocs/projects/admin.py | 7 ------- readthedocs/projects/forms.py | 21 +-------------------- readthedocs/projects/models.py | 21 ++++++++++++--------- readthedocs/projects/notifications.py | 5 +++-- readthedocs/projects/signals.py | 1 - readthedocs/projects/tasks.py | 2 +- readthedocs/projects/urls/private.py | 10 ++-------- readthedocs/projects/views/private.py | 1 + 13 files changed, 34 insertions(+), 83 deletions(-) diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index d4b58bb593b..eaf4614a11c 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -1,21 +1,14 @@ # -*- coding: utf-8 -*- """Models for the builds app.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import logging import os.path import re from shutil import rmtree -from builtins import object from django.conf import settings from django.db import models +from django.urls import reverse from django.utils import timezone from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext @@ -23,7 +16,6 @@ from guardian.shortcuts import assign from jsonfield import JSONField from taggit.managers import TaggableManager -from django.urls import reverse from readthedocs.core.utils import broadcast from readthedocs.projects.constants import ( @@ -55,6 +47,7 @@ ) from .version_slug import VersionSlugField + DEFAULT_VERSION_PRIVACY_LEVEL = getattr( settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public') diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index 5f7cd382f49..c860b018c34 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -3,13 +3,9 @@ # pylint: disable=too-many-lines """Build configuration for rtd.""" -from __future__ import division, print_function, unicode_literals - import os from contextlib import contextmanager -import six - from readthedocs.projects.constants import DOCUMENTATION_CHOICES from .find import find_one @@ -26,6 +22,7 @@ validate_string, ) + __all__ = ( 'ALL', 'load', diff --git a/readthedocs/core/urls/subdomain.py b/readthedocs/core/urls/subdomain.py index 23f1553245f..75df07eb396 100644 --- a/readthedocs/core/urls/subdomain.py +++ b/readthedocs/core/urls/subdomain.py @@ -1,24 +1,22 @@ # -*- coding: utf-8 -*- """URL configurations for subdomains.""" -from __future__ import absolute_import - from functools import reduce from operator import add -from django.conf.urls import url from django.conf import settings +from django.conf.urls import url from django.conf.urls.static import static +from readthedocs.constants import pattern_opts +from readthedocs.core.views import server_error_404, server_error_500 from readthedocs.core.views.serve import ( redirect_page_with_filename, - redirect_project_slug, serve_docs, robots_txt, + redirect_project_slug, + robots_txt, + serve_docs, ) -from readthedocs.core.views import ( - server_error_500, - server_error_404, -) -from readthedocs.constants import pattern_opts + handler500 = server_error_500 handler404 = server_error_404 diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index 6ed9855b60c..624f325d900 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -1,12 +1,5 @@ """Views pertaining to builds.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - import json import logging import re @@ -21,6 +14,7 @@ from readthedocs.projects.models import Feature, Project from readthedocs.projects.tasks import sync_repository_task + log = logging.getLogger(__name__) diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 168b1aac21b..fa5245cfedd 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -25,18 +25,14 @@ SERVE_DOCS (['private']) - The list of ['private', 'public'] docs to serve. """ -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import logging import mimetypes import os from functools import wraps from django.conf import settings -from django.http import HttpResponse, HttpResponseRedirect, Http404 -from django.shortcuts import get_object_or_404 -from django.shortcuts import render +from django.http import Http404, HttpResponse, HttpResponseRedirect +from django.shortcuts import get_object_or_404, render from django.utils.encoding import iri_to_uri from django.views.static import serve @@ -47,6 +43,7 @@ from readthedocs.projects import constants from readthedocs.projects.models import Project, ProjectRelationship + log = logging.getLogger(__name__) diff --git a/readthedocs/projects/admin.py b/readthedocs/projects/admin.py index 6bead7e3586..95e7f316319 100644 --- a/readthedocs/projects/admin.py +++ b/readthedocs/projects/admin.py @@ -1,13 +1,6 @@ # -*- coding: utf-8 -*- """Django administration interface for `projects.models`""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from django.contrib import admin, messages from django.contrib.admin.actions import delete_selected from django.utils.translation import ugettext_lazy as _ diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index 6486472e17e..4cc9f0ae4f5 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -2,28 +2,9 @@ """Project forms.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - -try: - # TODO: remove this when we deprecate Python2 - # re.fullmatch is >= Py3.4 only - from re import fullmatch -except ImportError: - # https://stackoverflow.com/questions/30212413/backport-python-3-4s-regular-expression-fullmatch-to-python-2 - import re - - def fullmatch(regex, string, flags=0): - """Emulate python-3.4 re.fullmatch().""" # noqa - return re.match("(?:" + regex + r")\Z", string, flags=flags) - from random import choice +from re import fullmatch -from builtins import object from django import forms from django.conf import settings from django.contrib.auth.models import User diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index 78d7793a402..ed4ac89b180 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -1,24 +1,20 @@ # -*- coding: utf-8 -*- """Project models.""" -from __future__ import ( - absolute_import, division, print_function, unicode_literals) - import fnmatch import logging import os -from builtins import object # pylint: disable=redefined-builtin -from six.moves import shlex_quote from django.conf import settings from django.contrib.auth.models import User -from django.urls import NoReverseMatch, reverse from django.db import models +from django.urls import NoReverseMatch, reverse from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from django_extensions.db.models import TimeStampedModel from future.backports.urllib.parse import urlparse # noqa from guardian.shortcuts import assign +from six.moves import shlex_quote from taggit.managers import TaggableManager from readthedocs.builds.constants import LATEST, STABLE @@ -27,15 +23,22 @@ from readthedocs.projects import constants from readthedocs.projects.exceptions import ProjectConfigurationError from readthedocs.projects.querysets import ( - ChildRelatedProjectQuerySet, FeatureQuerySet, ProjectQuerySet, - RelatedProjectQuerySet) + ChildRelatedProjectQuerySet, + FeatureQuerySet, + ProjectQuerySet, + RelatedProjectQuerySet, +) from readthedocs.projects.templatetags.projects_tags import sort_version_aware -from readthedocs.projects.validators import validate_domain_name, validate_repository_url +from readthedocs.projects.validators import ( + validate_domain_name, + validate_repository_url, +) from readthedocs.projects.version_handling import determine_stable_version from readthedocs.restapi.client import api from readthedocs.vcs_support.backends import backend_cls from readthedocs.vcs_support.utils import Lock, NonBlockingLock + log = logging.getLogger(__name__) diff --git a/readthedocs/projects/notifications.py b/readthedocs/projects/notifications.py index db7838bc80e..ddf6033a53c 100644 --- a/readthedocs/projects/notifications.py +++ b/readthedocs/projects/notifications.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- """Project notifications""" -from __future__ import absolute_import from datetime import timedelta -from django.utils import timezone + from django.http import HttpRequest +from django.utils import timezone from messages_extends.models import Message + from readthedocs.notifications import Notification from readthedocs.notifications.constants import REQUIREMENT diff --git a/readthedocs/projects/signals.py b/readthedocs/projects/signals.py index 151259c4ccc..513150b11e6 100644 --- a/readthedocs/projects/signals.py +++ b/readthedocs/projects/signals.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Project signals""" -from __future__ import absolute_import import django.dispatch diff --git a/readthedocs/projects/tasks.py b/readthedocs/projects/tasks.py index 0927bae2152..b9af472d915 100644 --- a/readthedocs/projects/tasks.py +++ b/readthedocs/projects/tasks.py @@ -72,8 +72,8 @@ after_vcs, before_build, before_vcs, - files_changed, domain_verify, + files_changed, ) diff --git a/readthedocs/projects/urls/private.py b/readthedocs/projects/urls/private.py index 5dc7b649de7..f0c035891ed 100644 --- a/readthedocs/projects/urls/private.py +++ b/readthedocs/projects/urls/private.py @@ -1,12 +1,5 @@ """Project URLs for authenticated users.""" -from __future__ import ( - absolute_import, - division, - print_function, - unicode_literals, -) - from django.conf.urls import url from readthedocs.constants import pattern_opts @@ -19,8 +12,8 @@ DomainUpdate, EnvironmentVariableCreate, EnvironmentVariableDelete, - EnvironmentVariableList, EnvironmentVariableDetail, + EnvironmentVariableList, ImportView, IntegrationCreate, IntegrationDelete, @@ -34,6 +27,7 @@ ProjectUpdate, ) + urlpatterns = [ url(r'^$', ProjectDashboard.as_view(), diff --git a/readthedocs/projects/views/private.py b/readthedocs/projects/views/private.py index 81998a7e8c9..87076f4504b 100644 --- a/readthedocs/projects/views/private.py +++ b/readthedocs/projects/views/private.py @@ -60,6 +60,7 @@ ) from readthedocs.projects.signals import project_import from readthedocs.projects.views.base import ProjectAdminMixin, ProjectSpamMixin + from ..tasks import retry_domain_verification From e363f6fe9fda5b6e7dddb9f17d4276376d7d0a40 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:15:51 +0100 Subject: [PATCH 30/42] Remove future leafovers --- readthedocs/projects/forms.py | 2 +- readthedocs/projects/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index 4cc9f0ae4f5..af68c7b8f15 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -4,6 +4,7 @@ from random import choice from re import fullmatch +from urllib.parse import urlparse from django import forms from django.conf import settings @@ -11,7 +12,6 @@ from django.template.loader import render_to_string from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ -from future.backports.urllib.parse import urlparse from guardian.shortcuts import assign from textclassifier.validators import ClassifierValidator diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index ed4ac89b180..d3ae9b67a90 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -4,6 +4,7 @@ import fnmatch import logging import os +from urllib.parse import urlparse from django.conf import settings from django.contrib.auth.models import User @@ -12,7 +13,6 @@ from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from django_extensions.db.models import TimeStampedModel -from future.backports.urllib.parse import urlparse # noqa from guardian.shortcuts import assign from six.moves import shlex_quote from taggit.managers import TaggableManager From 5798557a0d60635b925aa143370738804727cc83 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:16:25 +0100 Subject: [PATCH 31/42] isort over migration files Removes `from __future__ import ...` --- readthedocs/builds/migrations/0001_initial.py | 6 ++---- .../builds/migrations/0002_build_command_initial.py | 4 +--- readthedocs/builds/migrations/0003_add-cold-storage.py | 2 -- .../builds/migrations/0004_add-apiversion-proxy-model.py | 2 -- .../builds/migrations/0005_remove-version-alias.py | 3 +-- readthedocs/builds/migrations/0006_add_config_field.py | 4 +--- readthedocs/core/migrations/0001_initial.py | 5 +---- .../0002_make_userprofile_user_a_onetoonefield.py | 5 +---- readthedocs/core/migrations/0003_add_banned_status.py | 5 +---- readthedocs/core/migrations/0004_ad-opt-out.py | 4 +--- readthedocs/core/migrations/0005_migrate-old-passwords.py | 4 +--- readthedocs/gold/migrations/0001_initial.py | 5 +---- readthedocs/gold/migrations/0002_rename_last_4_digits.py | 2 -- .../0003_add_missing_model_change_migrations.py | 2 -- readthedocs/gold/migrations/0004_add_vat_id.py | 2 -- .../integrations/migrations/0001_add_http_exchange.py | 6 ++---- readthedocs/integrations/migrations/0002_add-webhook.py | 5 +---- .../0003_add_missing_model_change_migrations.py | 2 -- readthedocs/oauth/migrations/0001_initial.py | 5 +---- readthedocs/oauth/migrations/0002_combine_services.py | 7 ++----- readthedocs/oauth/migrations/0003_move_github.py | 8 +++----- .../migrations/0004_drop_github_and_bitbucket_models.py | 5 +---- readthedocs/oauth/migrations/0005_add_account_relation.py | 5 +---- readthedocs/oauth/migrations/0006_move_oauth_source.py | 5 +---- readthedocs/oauth/migrations/0007_org_slug_nonunique.py | 5 +---- readthedocs/oauth/migrations/0008_add-project-relation.py | 5 +---- .../0009_add_missing_model_change_migrations.py | 2 -- readthedocs/projects/migrations/0001_initial.py | 7 ++----- .../projects/migrations/0002_add_importedfile_model.py | 5 +---- .../projects/migrations/0003_project_cdn_enabled.py | 5 +---- .../migrations/0004_add_project_container_image.py | 5 +---- .../projects/migrations/0005_sync_project_model.py | 5 +---- readthedocs/projects/migrations/0006_add_domain_models.py | 5 +---- .../projects/migrations/0007_migrate_canonical_data.py | 6 +----- .../migrations/0008_add_subproject_alias_prefix.py | 5 +---- readthedocs/projects/migrations/0009_add_domain_field.py | 7 ++----- .../projects/migrations/0010_migrate_domain_data.py | 6 +++--- readthedocs/projects/migrations/0011_delete-url.py | 5 +---- .../migrations/0012_proper-name-for-install-project.py | 5 +---- .../projects/migrations/0013_add-container-limits.py | 5 +---- .../projects/migrations/0014_add-state-tracking.py | 5 +---- .../projects/migrations/0015_add_project_allow_promos.py | 5 +---- readthedocs/projects/migrations/0016_build-queue-name.py | 5 +---- readthedocs/projects/migrations/0017_add_domain_https.py | 5 +---- .../projects/migrations/0018_fix-translation-model.py | 5 +---- readthedocs/projects/migrations/0019_add-features.py | 2 -- .../projects/migrations/0020_add-api-project-proxy.py | 2 -- .../migrations/0021_add-webhook-deprecation-feature.py | 2 -- readthedocs/projects/migrations/0022_add-alias-slug.py | 2 -- .../projects/migrations/0023_migrate-alias-slug.py | 4 +--- .../projects/migrations/0024_add-show-version-warning.py | 3 +-- .../0025_show-version-warning-existing-projects.py | 2 -- readthedocs/projects/migrations/0026_ad-free-option.py | 2 -- .../migrations/0027_remove_json_with_html_feature.py | 2 -- .../0028_remove_comments_and_update_old_migration.py | 2 -- .../projects/migrations/0029_add_additional_languages.py | 2 -- .../migrations/0030_change-max-length-project-slug.py | 2 -- .../migrations/0031_add_modified_date_importedfile.py | 2 -- .../projects/migrations/0032_increase_webhook_maxsize.py | 2 -- .../projects/migrations/0033_add_environment_variables.py | 4 +--- .../migrations/0034_remove_unused_project_model_fields.py | 2 -- .../migrations/0035_container_time_limit_as_integer.py | 2 -- .../projects/migrations/0036_remove-auto-doctype.py | 2 -- readthedocs/redirects/migrations/0001_initial.py | 5 +---- .../0002_add_missing_model_change_migrations.py | 2 -- 65 files changed, 51 insertions(+), 208 deletions(-) diff --git a/readthedocs/builds/migrations/0001_initial.py b/readthedocs/builds/migrations/0001_initial.py index 73ff3a648fe..27f61efb797 100644 --- a/readthedocs/builds/migrations/0001_initial.py +++ b/readthedocs/builds/migrations/0001_initial.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals +import taggit.managers +from django.db import migrations, models -from __future__ import absolute_import -from django.db import models, migrations import readthedocs.builds.version_slug -import taggit.managers class Migration(migrations.Migration): diff --git a/readthedocs/builds/migrations/0002_build_command_initial.py b/readthedocs/builds/migrations/0002_build_command_initial.py index 7b45f946830..d78b9d13d2f 100644 --- a/readthedocs/builds/migrations/0002_build_command_initial.py +++ b/readthedocs/builds/migrations/0002_build_command_initial.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals +from django.db import migrations, models -from __future__ import absolute_import -from django.db import models, migrations import readthedocs.builds.models diff --git a/readthedocs/builds/migrations/0003_add-cold-storage.py b/readthedocs/builds/migrations/0003_add-cold-storage.py index 7c474a973c0..2c53cc144dd 100644 --- a/readthedocs/builds/migrations/0003_add-cold-storage.py +++ b/readthedocs/builds/migrations/0003_add-cold-storage.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-10-09 20:14 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/builds/migrations/0004_add-apiversion-proxy-model.py b/readthedocs/builds/migrations/0004_add-apiversion-proxy-model.py index b96db28e95a..8247d9a0249 100644 --- a/readthedocs/builds/migrations/0004_add-apiversion-proxy-model.py +++ b/readthedocs/builds/migrations/0004_add-apiversion-proxy-model.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-10-27 00:17 -from __future__ import unicode_literals - from django.db import migrations diff --git a/readthedocs/builds/migrations/0005_remove-version-alias.py b/readthedocs/builds/migrations/0005_remove-version-alias.py index a41af51e2df..65f6aadd3a5 100644 --- a/readthedocs/builds/migrations/0005_remove-version-alias.py +++ b/readthedocs/builds/migrations/0005_remove-version-alias.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2018-10-17 04:20 -from __future__ import unicode_literals - from django.db import migrations, models + import readthedocs.builds.version_slug diff --git a/readthedocs/builds/migrations/0006_add_config_field.py b/readthedocs/builds/migrations/0006_add_config_field.py index 7af36e8ad79..deb3700278c 100644 --- a/readthedocs/builds/migrations/0006_add_config_field.py +++ b/readthedocs/builds/migrations/0006_add_config_field.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-11-02 13:24 -from __future__ import unicode_literals - -from django.db import migrations import jsonfield.fields +from django.db import migrations class Migration(migrations.Migration): diff --git a/readthedocs/core/migrations/0001_initial.py b/readthedocs/core/migrations/0001_initial.py index e3d5b948469..bb5bde04285 100644 --- a/readthedocs/core/migrations/0001_initial.py +++ b/readthedocs/core/migrations/0001_initial.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/core/migrations/0002_make_userprofile_user_a_onetoonefield.py b/readthedocs/core/migrations/0002_make_userprofile_user_a_onetoonefield.py index f5e6255cc6b..5f7d04ff910 100644 --- a/readthedocs/core/migrations/0002_make_userprofile_user_a_onetoonefield.py +++ b/readthedocs/core/migrations/0002_make_userprofile_user_a_onetoonefield.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/core/migrations/0003_add_banned_status.py b/readthedocs/core/migrations/0003_add_banned_status.py index f3dfbb8b777..95d26eefc42 100644 --- a/readthedocs/core/migrations/0003_add_banned_status.py +++ b/readthedocs/core/migrations/0003_add_banned_status.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/core/migrations/0004_ad-opt-out.py b/readthedocs/core/migrations/0004_ad-opt-out.py index 9e8c5bf3209..f5d6ae3d029 100644 --- a/readthedocs/core/migrations/0004_ad-opt-out.py +++ b/readthedocs/core/migrations/0004_ad-opt-out.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-06-14 18:06 -from __future__ import unicode_literals - import annoying.fields +import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import django.db.models.deletion class Migration(migrations.Migration): diff --git a/readthedocs/core/migrations/0005_migrate-old-passwords.py b/readthedocs/core/migrations/0005_migrate-old-passwords.py index 2ef614d0db6..8a44107c90b 100644 --- a/readthedocs/core/migrations/0005_migrate-old-passwords.py +++ b/readthedocs/core/migrations/0005_migrate-old-passwords.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-10-11 17:28 -from __future__ import unicode_literals - -from django.db import migrations from django.contrib.auth.hashers import make_password +from django.db import migrations def forwards_func(apps, schema_editor): diff --git a/readthedocs/gold/migrations/0001_initial.py b/readthedocs/gold/migrations/0001_initial.py index baeb6d28361..80a61461da8 100644 --- a/readthedocs/gold/migrations/0001_initial.py +++ b/readthedocs/gold/migrations/0001_initial.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/gold/migrations/0002_rename_last_4_digits.py b/readthedocs/gold/migrations/0002_rename_last_4_digits.py index 2ed345fd3ce..fc0aad4531d 100644 --- a/readthedocs/gold/migrations/0002_rename_last_4_digits.py +++ b/readthedocs/gold/migrations/0002_rename_last_4_digits.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2018-07-16 15:45 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/gold/migrations/0003_add_missing_model_change_migrations.py b/readthedocs/gold/migrations/0003_add_missing_model_change_migrations.py index 2e919ac202e..f1f9f1dbd5f 100644 --- a/readthedocs/gold/migrations/0003_add_missing_model_change_migrations.py +++ b/readthedocs/gold/migrations/0003_add_missing_model_change_migrations.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-10-31 11:25 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/gold/migrations/0004_add_vat_id.py b/readthedocs/gold/migrations/0004_add_vat_id.py index eab1771f1a2..ee4899eebac 100644 --- a/readthedocs/gold/migrations/0004_add_vat_id.py +++ b/readthedocs/gold/migrations/0004_add_vat_id.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2018-10-22 07:13 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/integrations/migrations/0001_add_http_exchange.py b/readthedocs/integrations/migrations/0001_add_http_exchange.py index b4440b6eab5..c1ee6c0b714 100644 --- a/readthedocs/integrations/migrations/0001_add_http_exchange.py +++ b/readthedocs/integrations/migrations/0001_add_http_exchange.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-03-16 18:30 -from __future__ import unicode_literals +import uuid -from __future__ import absolute_import -from django.db import migrations, models import django.db.models.deletion import jsonfield.fields -import uuid +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/integrations/migrations/0002_add-webhook.py b/readthedocs/integrations/migrations/0002_add-webhook.py index 3d061993c7a..e42ef2a8613 100644 --- a/readthedocs/integrations/migrations/0002_add-webhook.py +++ b/readthedocs/integrations/migrations/0002_add-webhook.py @@ -1,11 +1,8 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-03-29 21:29 -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import migrations, models import django.db.models.deletion import jsonfield.fields +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/integrations/migrations/0003_add_missing_model_change_migrations.py b/readthedocs/integrations/migrations/0003_add_missing_model_change_migrations.py index a1356c48fa2..d1a4314417d 100644 --- a/readthedocs/integrations/migrations/0003_add_missing_model_change_migrations.py +++ b/readthedocs/integrations/migrations/0003_add_missing_model_change_migrations.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-10-31 11:25 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/oauth/migrations/0001_initial.py b/readthedocs/oauth/migrations/0001_initial.py index 352af573c87..3b3c4b1547b 100644 --- a/readthedocs/oauth/migrations/0001_initial.py +++ b/readthedocs/oauth/migrations/0001_initial.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/oauth/migrations/0002_combine_services.py b/readthedocs/oauth/migrations/0002_combine_services.py index ab053be97f0..1290dc5d95e 100644 --- a/readthedocs/oauth/migrations/0002_combine_services.py +++ b/readthedocs/oauth/migrations/0002_combine_services.py @@ -1,10 +1,7 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations -from django.conf import settings import django.core.validators +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/oauth/migrations/0003_move_github.py b/readthedocs/oauth/migrations/0003_move_github.py index 7ff1dbc8e10..14998f5e985 100644 --- a/readthedocs/oauth/migrations/0003_move_github.py +++ b/readthedocs/oauth/migrations/0003_move_github.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -import json import gc +import json import logging -from django.db import models, migrations +from django.db import migrations, models + log = logging.getLogger(__name__) diff --git a/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py b/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py index 628891ff795..17bfef29833 100644 --- a/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py +++ b/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models def forwards_remove_content_types(apps, schema_editor): diff --git a/readthedocs/oauth/migrations/0005_add_account_relation.py b/readthedocs/oauth/migrations/0005_add_account_relation.py index 100bcd71aef..c8c466db37f 100644 --- a/readthedocs/oauth/migrations/0005_add_account_relation.py +++ b/readthedocs/oauth/migrations/0005_add_account_relation.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/oauth/migrations/0006_move_oauth_source.py b/readthedocs/oauth/migrations/0006_move_oauth_source.py index a19d0be04a7..66d0db28f44 100644 --- a/readthedocs/oauth/migrations/0006_move_oauth_source.py +++ b/readthedocs/oauth/migrations/0006_move_oauth_source.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models def forwards_move_repo_source(apps, schema_editor): diff --git a/readthedocs/oauth/migrations/0007_org_slug_nonunique.py b/readthedocs/oauth/migrations/0007_org_slug_nonunique.py index 65f6f4f4f70..97078038491 100644 --- a/readthedocs/oauth/migrations/0007_org_slug_nonunique.py +++ b/readthedocs/oauth/migrations/0007_org_slug_nonunique.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/oauth/migrations/0008_add-project-relation.py b/readthedocs/oauth/migrations/0008_add-project-relation.py index 1e2a478e69f..070b57e654c 100644 --- a/readthedocs/oauth/migrations/0008_add-project-relation.py +++ b/readthedocs/oauth/migrations/0008_add-project-relation.py @@ -1,10 +1,7 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-03-22 20:10 -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/oauth/migrations/0009_add_missing_model_change_migrations.py b/readthedocs/oauth/migrations/0009_add_missing_model_change_migrations.py index 015c233ac20..c23743a846a 100644 --- a/readthedocs/oauth/migrations/0009_add_missing_model_change_migrations.py +++ b/readthedocs/oauth/migrations/0009_add_missing_model_change_migrations.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-10-31 11:25 -from __future__ import unicode_literals - import django.core.validators from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0001_initial.py b/readthedocs/projects/migrations/0001_initial.py index 734358ad0b0..00d2a7915b0 100644 --- a/readthedocs/projects/migrations/0001_initial.py +++ b/readthedocs/projects/migrations/0001_initial.py @@ -1,10 +1,7 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations -from django.conf import settings import taggit.managers +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0002_add_importedfile_model.py b/readthedocs/projects/migrations/0002_add_importedfile_model.py index a03fff529cb..cfa6f3b9e63 100644 --- a/readthedocs/projects/migrations/0002_add_importedfile_model.py +++ b/readthedocs/projects/migrations/0002_add_importedfile_model.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0003_project_cdn_enabled.py b/readthedocs/projects/migrations/0003_project_cdn_enabled.py index 471df331910..e89cfed99ac 100644 --- a/readthedocs/projects/migrations/0003_project_cdn_enabled.py +++ b/readthedocs/projects/migrations/0003_project_cdn_enabled.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0004_add_project_container_image.py b/readthedocs/projects/migrations/0004_add_project_container_image.py index 70c969d1be5..724e62e45fc 100644 --- a/readthedocs/projects/migrations/0004_add_project_container_image.py +++ b/readthedocs/projects/migrations/0004_add_project_container_image.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0005_sync_project_model.py b/readthedocs/projects/migrations/0005_sync_project_model.py index 75b9e6d5e06..12537572ce8 100644 --- a/readthedocs/projects/migrations/0005_sync_project_model.py +++ b/readthedocs/projects/migrations/0005_sync_project_model.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0006_add_domain_models.py b/readthedocs/projects/migrations/0006_add_domain_models.py index 78e05b81e28..e50617a6931 100644 --- a/readthedocs/projects/migrations/0006_add_domain_models.py +++ b/readthedocs/projects/migrations/0006_add_domain_models.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0007_migrate_canonical_data.py b/readthedocs/projects/migrations/0007_migrate_canonical_data.py index 8c842a6a0b9..79647aa8f1c 100644 --- a/readthedocs/projects/migrations/0007_migrate_canonical_data.py +++ b/readthedocs/projects/migrations/0007_migrate_canonical_data.py @@ -1,9 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals, print_function - -from __future__ import absolute_import -from django.db import migrations -from django.db import transaction +from django.db import migrations, transaction def migrate_canonical(apps, schema_editor): diff --git a/readthedocs/projects/migrations/0008_add_subproject_alias_prefix.py b/readthedocs/projects/migrations/0008_add_subproject_alias_prefix.py index b3eb933882c..ec61ecabd15 100644 --- a/readthedocs/projects/migrations/0008_add_subproject_alias_prefix.py +++ b/readthedocs/projects/migrations/0008_add_subproject_alias_prefix.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0009_add_domain_field.py b/readthedocs/projects/migrations/0009_add_domain_field.py index 20230a4738c..20703e89d27 100644 --- a/readthedocs/projects/migrations/0009_add_domain_field.py +++ b/readthedocs/projects/migrations/0009_add_domain_field.py @@ -1,11 +1,8 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals +import uuid -from __future__ import absolute_import -from django.db import models, migrations import django.contrib.sites.models - -import uuid +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0010_migrate_domain_data.py b/readthedocs/projects/migrations/0010_migrate_domain_data.py index 2d18756d6d0..3014423333a 100644 --- a/readthedocs/projects/migrations/0010_migrate_domain_data.py +++ b/readthedocs/projects/migrations/0010_migrate_domain_data.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -from __future__ import (absolute_import, print_function, unicode_literals) - -from django.db import models, migrations from urllib.parse import urlparse + +from django.db import migrations, models + import readthedocs.projects.validators diff --git a/readthedocs/projects/migrations/0011_delete-url.py b/readthedocs/projects/migrations/0011_delete-url.py index fcd83c02753..caa9c612db8 100644 --- a/readthedocs/projects/migrations/0011_delete-url.py +++ b/readthedocs/projects/migrations/0011_delete-url.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0012_proper-name-for-install-project.py b/readthedocs/projects/migrations/0012_proper-name-for-install-project.py index 8f5f116269b..9f143977ca0 100644 --- a/readthedocs/projects/migrations/0012_proper-name-for-install-project.py +++ b/readthedocs/projects/migrations/0012_proper-name-for-install-project.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0013_add-container-limits.py b/readthedocs/projects/migrations/0013_add-container-limits.py index c2820609037..9a052e00fc1 100644 --- a/readthedocs/projects/migrations/0013_add-container-limits.py +++ b/readthedocs/projects/migrations/0013_add-container-limits.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0014_add-state-tracking.py b/readthedocs/projects/migrations/0014_add-state-tracking.py index 628bf970dce..d2c34c28e5a 100644 --- a/readthedocs/projects/migrations/0014_add-state-tracking.py +++ b/readthedocs/projects/migrations/0014_add-state-tracking.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0015_add_project_allow_promos.py b/readthedocs/projects/migrations/0015_add_project_allow_promos.py index 5c50eeac924..882893160fc 100644 --- a/readthedocs/projects/migrations/0015_add_project_allow_promos.py +++ b/readthedocs/projects/migrations/0015_add_project_allow_promos.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0016_build-queue-name.py b/readthedocs/projects/migrations/0016_build-queue-name.py index d4e2201b230..6baa0a92267 100644 --- a/readthedocs/projects/migrations/0016_build-queue-name.py +++ b/readthedocs/projects/migrations/0016_build-queue-name.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models def update_build_queue(apps, schema): diff --git a/readthedocs/projects/migrations/0017_add_domain_https.py b/readthedocs/projects/migrations/0017_add_domain_https.py index 9bf94eeb5cb..18788581ccf 100644 --- a/readthedocs/projects/migrations/0017_add_domain_https.py +++ b/readthedocs/projects/migrations/0017_add_domain_https.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0018_fix-translation-model.py b/readthedocs/projects/migrations/0018_fix-translation-model.py index bfe283d27cb..2541fb0d36b 100644 --- a/readthedocs/projects/migrations/0018_fix-translation-model.py +++ b/readthedocs/projects/migrations/0018_fix-translation-model.py @@ -1,9 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0019_add-features.py b/readthedocs/projects/migrations/0019_add-features.py index 6d1036dd123..6b7ee7a8bcd 100644 --- a/readthedocs/projects/migrations/0019_add-features.py +++ b/readthedocs/projects/migrations/0019_add-features.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-10-27 12:55 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0020_add-api-project-proxy.py b/readthedocs/projects/migrations/0020_add-api-project-proxy.py index 34eafa4846f..9e1c8458b59 100644 --- a/readthedocs/projects/migrations/0020_add-api-project-proxy.py +++ b/readthedocs/projects/migrations/0020_add-api-project-proxy.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-10-27 12:56 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py b/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py index 84dc7cf923b..a3572c84cdb 100644 --- a/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py +++ b/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py @@ -2,8 +2,6 @@ """Add feature for allowing access to deprecated webhook endpoints""" -from __future__ import unicode_literals - from django.db import migrations diff --git a/readthedocs/projects/migrations/0022_add-alias-slug.py b/readthedocs/projects/migrations/0022_add-alias-slug.py index 8439c56e85e..90c434a2752 100644 --- a/readthedocs/projects/migrations/0022_add-alias-slug.py +++ b/readthedocs/projects/migrations/0022_add-alias-slug.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-12-21 16:30 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0023_migrate-alias-slug.py b/readthedocs/projects/migrations/0023_migrate-alias-slug.py index 4942848b952..c86ea6e07df 100644 --- a/readthedocs/projects/migrations/0023_migrate-alias-slug.py +++ b/readthedocs/projects/migrations/0023_migrate-alias-slug.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-12-21 16:31 -from __future__ import unicode_literals +import re from django.db import migrations -import re - class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0024_add-show-version-warning.py b/readthedocs/projects/migrations/0024_add-show-version-warning.py index 6bc60e4aeeb..bfa0b2edb9e 100644 --- a/readthedocs/projects/migrations/0024_add-show-version-warning.py +++ b/readthedocs/projects/migrations/0024_add-show-version-warning.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2018-05-02 01:27 -from __future__ import unicode_literals - from django.db import migrations, models + import readthedocs.projects.validators diff --git a/readthedocs/projects/migrations/0025_show-version-warning-existing-projects.py b/readthedocs/projects/migrations/0025_show-version-warning-existing-projects.py index 5d073258caf..e38349fc52f 100644 --- a/readthedocs/projects/migrations/0025_show-version-warning-existing-projects.py +++ b/readthedocs/projects/migrations/0025_show-version-warning-existing-projects.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2018-05-07 19:25 -from __future__ import unicode_literals - from django.db import migrations diff --git a/readthedocs/projects/migrations/0026_ad-free-option.py b/readthedocs/projects/migrations/0026_ad-free-option.py index a32fe5e74b7..d108f25e190 100644 --- a/readthedocs/projects/migrations/0026_ad-free-option.py +++ b/readthedocs/projects/migrations/0026_ad-free-option.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2018-06-29 15:53 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0027_remove_json_with_html_feature.py b/readthedocs/projects/migrations/0027_remove_json_with_html_feature.py index c5daf0ece54..d0fb5d14e93 100644 --- a/readthedocs/projects/migrations/0027_remove_json_with_html_feature.py +++ b/readthedocs/projects/migrations/0027_remove_json_with_html_feature.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2018-08-22 09:19 -from __future__ import unicode_literals - from django.db import migrations diff --git a/readthedocs/projects/migrations/0028_remove_comments_and_update_old_migration.py b/readthedocs/projects/migrations/0028_remove_comments_and_update_old_migration.py index 056e9b8e61a..4d4fc04a5a8 100644 --- a/readthedocs/projects/migrations/0028_remove_comments_and_update_old_migration.py +++ b/readthedocs/projects/migrations/0028_remove_comments_and_update_old_migration.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-10-31 10:08 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0029_add_additional_languages.py b/readthedocs/projects/migrations/0029_add_additional_languages.py index b4cfc77535b..8e9e48d4b6f 100644 --- a/readthedocs/projects/migrations/0029_add_additional_languages.py +++ b/readthedocs/projects/migrations/0029_add_additional_languages.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-11-01 13:38 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0030_change-max-length-project-slug.py b/readthedocs/projects/migrations/0030_change-max-length-project-slug.py index 7e9b48da270..ee27e9602a9 100644 --- a/readthedocs/projects/migrations/0030_change-max-length-project-slug.py +++ b/readthedocs/projects/migrations/0030_change-max-length-project-slug.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-11-01 20:55 -from __future__ import unicode_literals - from django.db import migrations, models from django.db.models.functions import Length diff --git a/readthedocs/projects/migrations/0031_add_modified_date_importedfile.py b/readthedocs/projects/migrations/0031_add_modified_date_importedfile.py index 255da1c003a..617a420c2a3 100644 --- a/readthedocs/projects/migrations/0031_add_modified_date_importedfile.py +++ b/readthedocs/projects/migrations/0031_add_modified_date_importedfile.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-11-01 14:37 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0032_increase_webhook_maxsize.py b/readthedocs/projects/migrations/0032_increase_webhook_maxsize.py index eed6d3de06a..49b231590ed 100644 --- a/readthedocs/projects/migrations/0032_increase_webhook_maxsize.py +++ b/readthedocs/projects/migrations/0032_increase_webhook_maxsize.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-11-06 23:12 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0033_add_environment_variables.py b/readthedocs/projects/migrations/0033_add_environment_variables.py index de9e3d18e5b..9279fa8b338 100644 --- a/readthedocs/projects/migrations/0033_add_environment_variables.py +++ b/readthedocs/projects/migrations/0033_add_environment_variables.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-11-12 13:57 -from __future__ import unicode_literals - -from django.db import migrations, models import django.db.models.deletion import django_extensions.db.fields +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0034_remove_unused_project_model_fields.py b/readthedocs/projects/migrations/0034_remove_unused_project_model_fields.py index 996cdd9c6d3..4ad8b27f8a5 100644 --- a/readthedocs/projects/migrations/0034_remove_unused_project_model_fields.py +++ b/readthedocs/projects/migrations/0034_remove_unused_project_model_fields.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-11-05 12:20 -from __future__ import unicode_literals - from django.db import migrations diff --git a/readthedocs/projects/migrations/0035_container_time_limit_as_integer.py b/readthedocs/projects/migrations/0035_container_time_limit_as_integer.py index 28dab124004..7256548fd6f 100644 --- a/readthedocs/projects/migrations/0035_container_time_limit_as_integer.py +++ b/readthedocs/projects/migrations/0035_container_time_limit_as_integer.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-12-10 11:19 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0036_remove-auto-doctype.py b/readthedocs/projects/migrations/0036_remove-auto-doctype.py index 8688fc4d07c..b0f9ad28165 100644 --- a/readthedocs/projects/migrations/0036_remove-auto-doctype.py +++ b/readthedocs/projects/migrations/0036_remove-auto-doctype.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-12-17 17:32 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/readthedocs/redirects/migrations/0001_initial.py b/readthedocs/redirects/migrations/0001_initial.py index 010f36342b3..0bb2fb946eb 100644 --- a/readthedocs/redirects/migrations/0001_initial.py +++ b/readthedocs/redirects/migrations/0001_initial.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from __future__ import absolute_import -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/readthedocs/redirects/migrations/0002_add_missing_model_change_migrations.py b/readthedocs/redirects/migrations/0002_add_missing_model_change_migrations.py index a837e6fb146..e1d83010c0a 100644 --- a/readthedocs/redirects/migrations/0002_add_missing_model_change_migrations.py +++ b/readthedocs/redirects/migrations/0002_add_missing_model_change_migrations.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.16 on 2018-10-31 11:25 -from __future__ import unicode_literals - from django.db import migrations, models From 55e1a2f37eebaef8a4a47384e36a39cb6b3b1541 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:19:04 +0100 Subject: [PATCH 32/42] isort on all tests files --- readthedocs/analytics/tests.py | 1 + readthedocs/config/tests/test_config.py | 3 +-- readthedocs/config/tests/test_find.py | 1 + readthedocs/config/tests/test_parser.py | 1 + readthedocs/config/tests/test_utils.py | 1 + readthedocs/config/tests/test_validation.py | 1 + readthedocs/core/tests/test_signals.py | 1 + readthedocs/gold/tests/test_forms.py | 1 + readthedocs/gold/tests/test_signals.py | 1 + readthedocs/rtd_tests/base.py | 1 + readthedocs/rtd_tests/files/conf.py | 3 +-- readthedocs/rtd_tests/mocks/environment.py | 1 + readthedocs/rtd_tests/mocks/mock_api.py | 1 + readthedocs/rtd_tests/mocks/paths.py | 1 + .../tests/projects/test_admin_actions.py | 1 + readthedocs/rtd_tests/tests/test_api.py | 1 + .../rtd_tests/tests/test_api_permissions.py | 1 + .../tests/test_api_version_compare.py | 1 + readthedocs/rtd_tests/tests/test_backend.py | 1 + .../rtd_tests/tests/test_backend_svn.py | 1 + .../rtd_tests/tests/test_build_config.py | 1 + .../rtd_tests/tests/test_build_forms.py | 1 + .../tests/test_build_notifications.py | 1 + readthedocs/rtd_tests/tests/test_builds.py | 1 + readthedocs/rtd_tests/tests/test_celery.py | 20 +++++++++---------- .../tests/test_config_integration.py | 1 + readthedocs/rtd_tests/tests/test_core_tags.py | 1 + .../rtd_tests/tests/test_core_utils.py | 1 + .../rtd_tests/tests/test_doc_builder.py | 1 + .../rtd_tests/tests/test_doc_building.py | 1 + .../rtd_tests/tests/test_doc_serving.py | 15 ++++++-------- readthedocs/rtd_tests/tests/test_domains.py | 1 + readthedocs/rtd_tests/tests/test_extend.py | 1 + readthedocs/rtd_tests/tests/test_footer.py | 1 + readthedocs/rtd_tests/tests/test_gold.py | 1 + .../rtd_tests/tests/test_imported_file.py | 1 + .../rtd_tests/tests/test_integrations.py | 1 + .../rtd_tests/tests/test_middleware.py | 1 + .../rtd_tests/tests/test_notifications.py | 16 +++++++++------ readthedocs/rtd_tests/tests/test_oauth.py | 1 + .../rtd_tests/tests/test_post_commit_hooks.py | 3 ++- readthedocs/rtd_tests/tests/test_privacy.py | 1 + .../rtd_tests/tests/test_privacy_urls.py | 9 +++------ .../rtd_tests/tests/test_profile_views.py | 1 + readthedocs/rtd_tests/tests/test_project.py | 1 + .../rtd_tests/tests/test_project_forms.py | 3 ++- .../rtd_tests/tests/test_project_querysets.py | 10 ++++++---- .../rtd_tests/tests/test_project_symlinks.py | 1 + .../rtd_tests/tests/test_project_views.py | 1 + readthedocs/rtd_tests/tests/test_redirects.py | 1 + .../rtd_tests/tests/test_repo_parsing.py | 1 + readthedocs/rtd_tests/tests/test_resolver.py | 1 + .../rtd_tests/tests/test_restapi_client.py | 1 + .../tests/test_search_json_parsing.py | 1 + .../rtd_tests/tests/test_single_version.py | 1 + .../rtd_tests/tests/test_subprojects.py | 1 + .../rtd_tests/tests/test_sync_versions.py | 1 + readthedocs/rtd_tests/tests/test_urls.py | 1 + .../tests/test_version_commit_name.py | 1 + .../rtd_tests/tests/test_version_config.py | 1 + .../rtd_tests/tests/test_version_slug.py | 1 + readthedocs/rtd_tests/tests/test_views.py | 4 +++- readthedocs/rtd_tests/utils.py | 1 + readthedocs/search/tests/conftest.py | 1 + readthedocs/search/tests/dummy_data.py | 1 + readthedocs/search/tests/test_views.py | 1 + readthedocs/search/tests/utils.py | 1 + readthedocs/vcs_support/tests.py | 1 + 68 files changed, 102 insertions(+), 42 deletions(-) diff --git a/readthedocs/analytics/tests.py b/readthedocs/analytics/tests.py index 93435551684..3a18165eb94 100644 --- a/readthedocs/analytics/tests.py +++ b/readthedocs/analytics/tests.py @@ -2,6 +2,7 @@ from .utils import anonymize_ip_address, anonymize_user_agent + class UtilsTests(TestCase): def test_anonymize_ip(self): self.assertEqual(anonymize_ip_address('127.0.0.1'), '127.0.0.0') diff --git a/readthedocs/config/tests/test_config.py b/readthedocs/config/tests/test_config.py index 0d223a671da..09b57adecee 100644 --- a/readthedocs/config/tests/test_config.py +++ b/readthedocs/config/tests/test_config.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - import os import re import textwrap @@ -38,6 +36,7 @@ from .utils import apply_fs + yaml_config_dir = { 'readthedocs.yml': textwrap.dedent( ''' diff --git a/readthedocs/config/tests/test_find.py b/readthedocs/config/tests/test_find.py index 4ebcb783cde..b982245fb71 100644 --- a/readthedocs/config/tests/test_find.py +++ b/readthedocs/config/tests/test_find.py @@ -4,6 +4,7 @@ from .utils import apply_fs + def test_find_no_files(tmpdir): with tmpdir.as_cwd(): path = find_one(os.getcwd(), r'readthedocs.yml') diff --git a/readthedocs/config/tests/test_parser.py b/readthedocs/config/tests/test_parser.py index 37476af9162..d140c8c0c19 100644 --- a/readthedocs/config/tests/test_parser.py +++ b/readthedocs/config/tests/test_parser.py @@ -4,6 +4,7 @@ from readthedocs.config.parser import ParseError, parse + def test_parse_empty_config_file(): buf = StringIO('') with raises(ParseError): diff --git a/readthedocs/config/tests/test_utils.py b/readthedocs/config/tests/test_utils.py index 2332b30716b..04f0192763d 100644 --- a/readthedocs/config/tests/test_utils.py +++ b/readthedocs/config/tests/test_utils.py @@ -1,5 +1,6 @@ from .utils import apply_fs + def test_apply_fs_with_empty_contents(tmpdir): # Doesn't do anything if second parameter is empty. apply_fs(tmpdir, {}) diff --git a/readthedocs/config/tests/test_validation.py b/readthedocs/config/tests/test_validation.py index fd7d9e781fb..780883e00ed 100644 --- a/readthedocs/config/tests/test_validation.py +++ b/readthedocs/config/tests/test_validation.py @@ -22,6 +22,7 @@ validate_string, ) + class TestValidateBool: def test_it_accepts_true(self): assert validate_bool(True) is True diff --git a/readthedocs/core/tests/test_signals.py b/readthedocs/core/tests/test_signals.py index f96bb30d566..b6fa0d8ad12 100644 --- a/readthedocs/core/tests/test_signals.py +++ b/readthedocs/core/tests/test_signals.py @@ -5,6 +5,7 @@ from readthedocs.oauth.models import RemoteOrganization from readthedocs.projects.models import Project + @pytest.mark.django_db class TestProjectOrganizationSignal: diff --git a/readthedocs/gold/tests/test_forms.py b/readthedocs/gold/tests/test_forms.py index 5ffaf918384..0741023368b 100644 --- a/readthedocs/gold/tests/test_forms.py +++ b/readthedocs/gold/tests/test_forms.py @@ -8,6 +8,7 @@ from ..forms import GoldSubscriptionForm from ..models import GoldUser + class GoldSubscriptionFormTests(TestCase): def setUp(self): diff --git a/readthedocs/gold/tests/test_signals.py b/readthedocs/gold/tests/test_signals.py index 947f042e035..55103d15546 100644 --- a/readthedocs/gold/tests/test_signals.py +++ b/readthedocs/gold/tests/test_signals.py @@ -9,6 +9,7 @@ from ..models import GoldUser from ..signals import delete_customer + class GoldSignalTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/base.py b/readthedocs/rtd_tests/base.py index b5ab431150a..270fe48281c 100644 --- a/readthedocs/rtd_tests/base.py +++ b/readthedocs/rtd_tests/base.py @@ -12,6 +12,7 @@ from django.test import RequestFactory, TestCase from mock import patch + log = logging.getLogger(__name__) diff --git a/readthedocs/rtd_tests/files/conf.py b/readthedocs/rtd_tests/files/conf.py index 11f872849dd..972ec96be0b 100644 --- a/readthedocs/rtd_tests/files/conf.py +++ b/readthedocs/rtd_tests/files/conf.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - from datetime import datetime from recommonmark.parser import CommonMarkParser + extensions = [] templates_path = ['templates', '_templates', '.templates'] source_suffix = ['.rst', '.md'] diff --git a/readthedocs/rtd_tests/mocks/environment.py b/readthedocs/rtd_tests/mocks/environment.py index a5aa1b8fe21..0d3ef1a56bc 100644 --- a/readthedocs/rtd_tests/mocks/environment.py +++ b/readthedocs/rtd_tests/mocks/environment.py @@ -1,6 +1,7 @@ # pylint: disable=missing-docstring import mock + class EnvironmentMockGroup: """Mock out necessary environment pieces""" diff --git a/readthedocs/rtd_tests/mocks/mock_api.py b/readthedocs/rtd_tests/mocks/mock_api.py index 3dd54d6635a..eeff8aadb5c 100644 --- a/readthedocs/rtd_tests/mocks/mock_api.py +++ b/readthedocs/rtd_tests/mocks/mock_api.py @@ -4,6 +4,7 @@ import mock + # Mock tastypi API. diff --git a/readthedocs/rtd_tests/mocks/paths.py b/readthedocs/rtd_tests/mocks/paths.py index ee8b29b7a26..3e4539ebcd3 100644 --- a/readthedocs/rtd_tests/mocks/paths.py +++ b/readthedocs/rtd_tests/mocks/paths.py @@ -4,6 +4,7 @@ import mock + def fake_paths(check): """ Usage: diff --git a/readthedocs/rtd_tests/tests/projects/test_admin_actions.py b/readthedocs/rtd_tests/tests/projects/test_admin_actions.py index 5c856ebad1a..c33f90ac9b9 100644 --- a/readthedocs/rtd_tests/tests/projects/test_admin_actions.py +++ b/readthedocs/rtd_tests/tests/projects/test_admin_actions.py @@ -8,6 +8,7 @@ from readthedocs.core.models import UserProfile from readthedocs.projects.models import Project + class ProjectAdminActionsTest(TestCase): @classmethod diff --git a/readthedocs/rtd_tests/tests/test_api.py b/readthedocs/rtd_tests/tests/test_api.py index 01693da1838..03d8f5f13d2 100644 --- a/readthedocs/rtd_tests/tests/test_api.py +++ b/readthedocs/rtd_tests/tests/test_api.py @@ -34,6 +34,7 @@ ) from readthedocs.restapi.views.task_views import get_status_data + super_auth = base64.b64encode(b'super:test').decode('utf-8') eric_auth = base64.b64encode(b'eric:test').decode('utf-8') diff --git a/readthedocs/rtd_tests/tests/test_api_permissions.py b/readthedocs/rtd_tests/tests/test_api_permissions.py index 40d348c6ccf..d887be4f546 100644 --- a/readthedocs/rtd_tests/tests/test_api_permissions.py +++ b/readthedocs/rtd_tests/tests/test_api_permissions.py @@ -5,6 +5,7 @@ from readthedocs.restapi.permissions import APIRestrictedPermission + class APIRestrictedPermissionTests(TestCase): def get_request(self, method, is_admin): request = Mock() diff --git a/readthedocs/rtd_tests/tests/test_api_version_compare.py b/readthedocs/rtd_tests/tests/test_api_version_compare.py index df4df452cff..509cbcc764c 100644 --- a/readthedocs/rtd_tests/tests/test_api_version_compare.py +++ b/readthedocs/rtd_tests/tests/test_api_version_compare.py @@ -4,6 +4,7 @@ from readthedocs.projects.models import Project from readthedocs.restapi.views.footer_views import get_version_compare_data + class VersionCompareTests(TestCase): fixtures = ['eric.json', 'test_data.json'] diff --git a/readthedocs/rtd_tests/tests/test_backend.py b/readthedocs/rtd_tests/tests/test_backend.py index 20320b29dd7..500646797b2 100644 --- a/readthedocs/rtd_tests/tests/test_backend.py +++ b/readthedocs/rtd_tests/tests/test_backend.py @@ -21,6 +21,7 @@ make_test_hg, ) + class TestGitBackend(RTDTestCase): def setUp(self): git_repo = make_test_git() diff --git a/readthedocs/rtd_tests/tests/test_backend_svn.py b/readthedocs/rtd_tests/tests/test_backend_svn.py index e3cf9e2e217..bd6f4551196 100644 --- a/readthedocs/rtd_tests/tests/test_backend_svn.py +++ b/readthedocs/rtd_tests/tests/test_backend_svn.py @@ -9,6 +9,7 @@ from readthedocs.rtd_tests.base import RTDTestCase from readthedocs.vcs_support.backends.svn import Backend as SvnBackend + class TestSvnBackend(RTDTestCase): def test_get_url(self): diff --git a/readthedocs/rtd_tests/tests/test_build_config.py b/readthedocs/rtd_tests/tests/test_build_config.py index 90a28ce25f6..b3ffbb7cd6a 100644 --- a/readthedocs/rtd_tests/tests/test_build_config.py +++ b/readthedocs/rtd_tests/tests/test_build_config.py @@ -6,6 +6,7 @@ from readthedocs.config.tests import utils + V2_SCHEMA = path.join( path.dirname(__file__), '../fixtures/spec/v2/schema.yml' diff --git a/readthedocs/rtd_tests/tests/test_build_forms.py b/readthedocs/rtd_tests/tests/test_build_forms.py index d69ca289481..f85ae741974 100644 --- a/readthedocs/rtd_tests/tests/test_build_forms.py +++ b/readthedocs/rtd_tests/tests/test_build_forms.py @@ -8,6 +8,7 @@ from readthedocs.projects.constants import PRIVATE from readthedocs.projects.models import Project + class TestVersionForm(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_build_notifications.py b/readthedocs/rtd_tests/tests/test_build_notifications.py index b276d1d935e..4964f32c78d 100644 --- a/readthedocs/rtd_tests/tests/test_build_notifications.py +++ b/readthedocs/rtd_tests/tests/test_build_notifications.py @@ -11,6 +11,7 @@ from readthedocs.projects.models import EmailHook, Project, WebHook from readthedocs.projects.tasks import send_notifications + class BuildNotificationsTests(TestCase): def setUp(self): self.project = fixture.get(Project) diff --git a/readthedocs/rtd_tests/tests/test_builds.py b/readthedocs/rtd_tests/tests/test_builds.py index 6f8808bc1ad..2b117fa4fac 100644 --- a/readthedocs/rtd_tests/tests/test_builds.py +++ b/readthedocs/rtd_tests/tests/test_builds.py @@ -14,6 +14,7 @@ from ..mocks.environment import EnvironmentMockGroup + class BuildEnvironmentTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_celery.py b/readthedocs/rtd_tests/tests/test_celery.py index ccd2b70e394..f7f16d0aed2 100644 --- a/readthedocs/rtd_tests/tests/test_celery.py +++ b/readthedocs/rtd_tests/tests/test_celery.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import division, print_function, unicode_literals - import os import shutil from os.path import exists @@ -8,20 +6,22 @@ from django.contrib.auth.models import User from django_dynamic_fixture import get -from mock import patch, MagicMock +from mock import MagicMock, patch from readthedocs.builds.constants import LATEST -from readthedocs.projects.exceptions import RepositoryError from readthedocs.builds.models import Build -from readthedocs.projects.models import Project +from readthedocs.doc_builder.exceptions import VersionLockedError from readthedocs.projects import tasks - -from readthedocs.rtd_tests.utils import ( - create_git_branch, create_git_tag, delete_git_branch) -from readthedocs.rtd_tests.utils import make_test_git +from readthedocs.projects.exceptions import RepositoryError +from readthedocs.projects.models import Project from readthedocs.rtd_tests.base import RTDTestCase from readthedocs.rtd_tests.mocks.mock_api import mock_api -from readthedocs.doc_builder.exceptions import VersionLockedError +from readthedocs.rtd_tests.utils import ( + create_git_branch, + create_git_tag, + delete_git_branch, + make_test_git, +) class TestCeleryBuilding(RTDTestCase): diff --git a/readthedocs/rtd_tests/tests/test_config_integration.py b/readthedocs/rtd_tests/tests/test_config_integration.py index 9938a267816..84c7fde763e 100644 --- a/readthedocs/rtd_tests/tests/test_config_integration.py +++ b/readthedocs/rtd_tests/tests/test_config_integration.py @@ -19,6 +19,7 @@ from readthedocs.projects.models import Feature, Project from readthedocs.rtd_tests.utils import create_git_submodule, make_git_repo + def create_load(config=None): """ Mock out the function of the build load function. diff --git a/readthedocs/rtd_tests/tests/test_core_tags.py b/readthedocs/rtd_tests/tests/test_core_tags.py index 20dd966ad4a..43a6b4f3858 100644 --- a/readthedocs/rtd_tests/tests/test_core_tags.py +++ b/readthedocs/rtd_tests/tests/test_core_tags.py @@ -9,6 +9,7 @@ from readthedocs.core.templatetags import core_tags from readthedocs.projects.models import Project + @override_settings(USE_SUBDOMAIN=False, PRODUCTION_DOMAIN='readthedocs.org') class CoreTagsTests(TestCase): fixtures = ["eric", "test_data"] diff --git a/readthedocs/rtd_tests/tests/test_core_utils.py b/readthedocs/rtd_tests/tests/test_core_utils.py index e480f6398ad..b44de84ce0c 100644 --- a/readthedocs/rtd_tests/tests/test_core_utils.py +++ b/readthedocs/rtd_tests/tests/test_core_utils.py @@ -9,6 +9,7 @@ from readthedocs.core.utils import slugify, trigger_build from readthedocs.projects.models import Project + class CoreUtilTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_doc_builder.py b/readthedocs/rtd_tests/tests/test_doc_builder.py index 555cbec9004..ff740302b54 100644 --- a/readthedocs/rtd_tests/tests/test_doc_builder.py +++ b/readthedocs/rtd_tests/tests/test_doc_builder.py @@ -20,6 +20,7 @@ from readthedocs.projects.exceptions import ProjectConfigurationError from readthedocs.projects.models import Feature, Project + class SphinxBuilderTest(TestCase): fixtures = ['test_data'] diff --git a/readthedocs/rtd_tests/tests/test_doc_building.py b/readthedocs/rtd_tests/tests/test_doc_building.py index 5daa03df160..91e27907a3d 100644 --- a/readthedocs/rtd_tests/tests/test_doc_building.py +++ b/readthedocs/rtd_tests/tests/test_doc_building.py @@ -35,6 +35,7 @@ from readthedocs.rtd_tests.mocks.paths import fake_paths_lookup from readthedocs.rtd_tests.tests.test_config_integration import create_load + DUMMY_BUILD_ID = 123 SAMPLE_UNICODE = 'HérÉ îß sömê ünïçó∂é' SAMPLE_UTF8_BYTES = SAMPLE_UNICODE.encode('utf-8') diff --git a/readthedocs/rtd_tests/tests/test_doc_serving.py b/readthedocs/rtd_tests/tests/test_doc_serving.py index 56798ad8499..2f9bf98ed3c 100644 --- a/readthedocs/rtd_tests/tests/test_doc_serving.py +++ b/readthedocs/rtd_tests/tests/test_doc_serving.py @@ -1,23 +1,20 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals, division, print_function -import mock -from mock import patch, mock_open import django_dynamic_fixture as fixture +import mock import pytest -import six - +from django.conf import settings from django.contrib.auth.models import User +from django.http import Http404 from django.test import TestCase from django.test.utils import override_settings -from django.http import Http404 -from django.conf import settings from django.urls import reverse +from mock import mock_open, patch -from readthedocs.rtd_tests.base import RequestFactoryTestMixin +from readthedocs.core.views.serve import _serve_symlink_docs from readthedocs.projects import constants from readthedocs.projects.models import Project -from readthedocs.core.views.serve import _serve_symlink_docs +from readthedocs.rtd_tests.base import RequestFactoryTestMixin @override_settings( diff --git a/readthedocs/rtd_tests/tests/test_domains.py b/readthedocs/rtd_tests/tests/test_domains.py index 963b07a91f1..377f0c8f64b 100644 --- a/readthedocs/rtd_tests/tests/test_domains.py +++ b/readthedocs/rtd_tests/tests/test_domains.py @@ -12,6 +12,7 @@ from readthedocs.projects.forms import DomainForm from readthedocs.projects.models import Domain, Project + class MiddlewareTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_extend.py b/readthedocs/rtd_tests/tests/test_extend.py index 21017e3088d..e4bb82a98dc 100644 --- a/readthedocs/rtd_tests/tests/test_extend.py +++ b/readthedocs/rtd_tests/tests/test_extend.py @@ -5,6 +5,7 @@ get_override_class, ) + # Top level to ensure module name is correct class FooBase: def bar(self): diff --git a/readthedocs/rtd_tests/tests/test_footer.py b/readthedocs/rtd_tests/tests/test_footer.py index 692e85cd121..60f820a7574 100644 --- a/readthedocs/rtd_tests/tests/test_footer.py +++ b/readthedocs/rtd_tests/tests/test_footer.py @@ -13,6 +13,7 @@ ) from readthedocs.rtd_tests.mocks.paths import fake_paths_by_regex + class Testmaker(APITestCase): fixtures = ['test_data'] url = '/api/v2/footer_html/?project=pip&version=latest&page=index&docroot=/' diff --git a/readthedocs/rtd_tests/tests/test_gold.py b/readthedocs/rtd_tests/tests/test_gold.py index 9475f4fd110..db7c9f927b5 100644 --- a/readthedocs/rtd_tests/tests/test_gold.py +++ b/readthedocs/rtd_tests/tests/test_gold.py @@ -6,6 +6,7 @@ from readthedocs.projects.models import Project from readthedocs.rtd_tests.utils import create_user + class GoldViewTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_imported_file.py b/readthedocs/rtd_tests/tests/test_imported_file.py index d005fc9a182..fc1df679363 100644 --- a/readthedocs/rtd_tests/tests/test_imported_file.py +++ b/readthedocs/rtd_tests/tests/test_imported_file.py @@ -5,6 +5,7 @@ from readthedocs.projects.models import ImportedFile, Project from readthedocs.projects.tasks import _manage_imported_files + base_dir = os.path.dirname(os.path.dirname(__file__)) diff --git a/readthedocs/rtd_tests/tests/test_integrations.py b/readthedocs/rtd_tests/tests/test_integrations.py index 360fdb0f1ed..c35fffc78fa 100644 --- a/readthedocs/rtd_tests/tests/test_integrations.py +++ b/readthedocs/rtd_tests/tests/test_integrations.py @@ -11,6 +11,7 @@ ) from readthedocs.projects.models import Project + class HttpExchangeTests(TestCase): """Test HttpExchange model by using existing views diff --git a/readthedocs/rtd_tests/tests/test_middleware.py b/readthedocs/rtd_tests/tests/test_middleware.py index f7e5d607624..093957f71f4 100644 --- a/readthedocs/rtd_tests/tests/test_middleware.py +++ b/readthedocs/rtd_tests/tests/test_middleware.py @@ -15,6 +15,7 @@ from readthedocs.projects.models import Domain, Project, ProjectRelationship from readthedocs.rtd_tests.utils import create_user + @override_settings(USE_SUBDOMAIN=True) class MiddlewareTests(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_notifications.py b/readthedocs/rtd_tests/tests/test_notifications.py index 4230f8f3e0a..0758e4d474c 100644 --- a/readthedocs/rtd_tests/tests/test_notifications.py +++ b/readthedocs/rtd_tests/tests/test_notifications.py @@ -1,26 +1,30 @@ # -*- coding: utf-8 -*- """Notification tests""" -from __future__ import absolute_import from datetime import timedelta -import mock + import django_dynamic_fixture as fixture +import mock +from django.contrib.auth.models import AnonymousUser, User from django.http import HttpRequest from django.test import TestCase from django.test.utils import override_settings -from django.contrib.auth.models import User, AnonymousUser from django.utils import timezone from messages_extends.models import Message as PersistentMessage +from readthedocs.builds.models import Build from readthedocs.notifications import Notification, SiteNotification from readthedocs.notifications.backends import EmailBackend, SiteBackend -from readthedocs.notifications.constants import ERROR, INFO_NON_PERSISTENT, WARNING_NON_PERSISTENT +from readthedocs.notifications.constants import ( + ERROR, + INFO_NON_PERSISTENT, + WARNING_NON_PERSISTENT, +) from readthedocs.projects.models import Project from readthedocs.projects.notifications import ( - DeprecatedGitHubWebhookNotification, DeprecatedBuildWebhookNotification, + DeprecatedGitHubWebhookNotification, ) -from readthedocs.builds.models import Build @override_settings( diff --git a/readthedocs/rtd_tests/tests/test_oauth.py b/readthedocs/rtd_tests/tests/test_oauth.py index 45c2983bb15..87d9439a16a 100644 --- a/readthedocs/rtd_tests/tests/test_oauth.py +++ b/readthedocs/rtd_tests/tests/test_oauth.py @@ -14,6 +14,7 @@ from readthedocs.projects import constants from readthedocs.projects.models import Project + class GitHubOAuthTests(TestCase): fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py index f9b310a6207..1db3332cb49 100644 --- a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py +++ b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py @@ -1,14 +1,15 @@ import json import logging +from urllib.parse import urlencode, urlparse import mock from django.test import TestCase from django_dynamic_fixture import get -from urllib.parse import urlparse, urlencode from readthedocs.builds.models import Version from readthedocs.projects.models import Feature, Project + log = logging.getLogger(__name__) diff --git a/readthedocs/rtd_tests/tests/test_privacy.py b/readthedocs/rtd_tests/tests/test_privacy.py index 7844ece2e5e..a5977ad86b6 100644 --- a/readthedocs/rtd_tests/tests/test_privacy.py +++ b/readthedocs/rtd_tests/tests/test_privacy.py @@ -12,6 +12,7 @@ from readthedocs.projects.forms import UpdateProjectForm from readthedocs.projects.models import Project + log = logging.getLogger(__name__) diff --git a/readthedocs/rtd_tests/tests/test_privacy_urls.py b/readthedocs/rtd_tests/tests/test_privacy_urls.py index 52ec89d74f1..ed5fff8c2bc 100644 --- a/readthedocs/rtd_tests/tests/test_privacy_urls.py +++ b/readthedocs/rtd_tests/tests/test_privacy_urls.py @@ -1,21 +1,18 @@ -from __future__ import absolute_import -from __future__ import print_function import re +import mock from allauth.socialaccount.models import SocialAccount -from builtins import object from django.contrib.admindocs.views import extract_views_from_urlpatterns from django.test import TestCase from django.urls import reverse from django_dynamic_fixture import get -import mock from taggit.models import Tag from readthedocs.builds.models import Build, BuildCommandResult from readthedocs.core.utils.tasks import TaskNoPermission from readthedocs.integrations.models import HttpExchange, Integration -from readthedocs.projects.models import Project, Domain, EnvironmentVariable -from readthedocs.oauth.models import RemoteRepository, RemoteOrganization +from readthedocs.oauth.models import RemoteOrganization, RemoteRepository +from readthedocs.projects.models import Domain, EnvironmentVariable, Project from readthedocs.rtd_tests.utils import create_user diff --git a/readthedocs/rtd_tests/tests/test_profile_views.py b/readthedocs/rtd_tests/tests/test_profile_views.py index 239f093beae..69dde65c877 100644 --- a/readthedocs/rtd_tests/tests/test_profile_views.py +++ b/readthedocs/rtd_tests/tests/test_profile_views.py @@ -3,6 +3,7 @@ from django.urls import reverse from django_dynamic_fixture import get + class ProfileViewsTest(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_project.py b/readthedocs/rtd_tests/tests/test_project.py index b6bee71d091..5562d3e80a3 100644 --- a/readthedocs/rtd_tests/tests/test_project.py +++ b/readthedocs/rtd_tests/tests/test_project.py @@ -22,6 +22,7 @@ from readthedocs.projects.tasks import finish_inactive_builds from readthedocs.rtd_tests.mocks.paths import fake_paths_by_regex + class ProjectMixin: fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_project_forms.py b/readthedocs/rtd_tests/tests/test_project_forms.py index eccfe9f3380..166426263cd 100644 --- a/readthedocs/rtd_tests/tests/test_project_forms.py +++ b/readthedocs/rtd_tests/tests/test_project_forms.py @@ -25,7 +25,8 @@ TranslationForm, UpdateProjectForm, ) -from readthedocs.projects.models import Project, EnvironmentVariable +from readthedocs.projects.models import EnvironmentVariable, Project + class TestProjectForms(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_project_querysets.py b/readthedocs/rtd_tests/tests/test_project_querysets.py index b8d5d9e1f57..316525c9178 100644 --- a/readthedocs/rtd_tests/tests/test_project_querysets.py +++ b/readthedocs/rtd_tests/tests/test_project_querysets.py @@ -1,13 +1,15 @@ # -*- coding: utf-8 -*- -from django.contrib.auth.models import User from datetime import timedelta import django_dynamic_fixture as fixture +from django.contrib.auth.models import User from django.test import TestCase -from readthedocs.projects.models import Project, Feature -from readthedocs.projects.querysets import (ParentRelatedProjectQuerySet, - ChildRelatedProjectQuerySet) +from readthedocs.projects.models import Feature, Project +from readthedocs.projects.querysets import ( + ChildRelatedProjectQuerySet, + ParentRelatedProjectQuerySet, +) class ProjectQuerySetTests(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_project_symlinks.py b/readthedocs/rtd_tests/tests/test_project_symlinks.py index bd9cb76ef73..473963b4455 100644 --- a/readthedocs/rtd_tests/tests/test_project_symlinks.py +++ b/readthedocs/rtd_tests/tests/test_project_symlinks.py @@ -19,6 +19,7 @@ symlink_project, ) + def get_filesystem(path, top_level_path=None): """Recurse into path, return dictionary mapping of path and files diff --git a/readthedocs/rtd_tests/tests/test_project_views.py b/readthedocs/rtd_tests/tests/test_project_views.py index 74aeed201bb..20dd5f5ec01 100644 --- a/readthedocs/rtd_tests/tests/test_project_views.py +++ b/readthedocs/rtd_tests/tests/test_project_views.py @@ -23,6 +23,7 @@ WizardTestCase, ) + @patch('readthedocs.projects.views.private.trigger_build', lambda x: None) class TestProfileMiddleware(RequestFactoryTestMixin, TestCase): diff --git a/readthedocs/rtd_tests/tests/test_redirects.py b/readthedocs/rtd_tests/tests/test_redirects.py index d53d55125b8..fad6cd2f4bd 100644 --- a/readthedocs/rtd_tests/tests/test_redirects.py +++ b/readthedocs/rtd_tests/tests/test_redirects.py @@ -11,6 +11,7 @@ from readthedocs.projects.models import Project from readthedocs.redirects.models import Redirect + @override_settings(PUBLIC_DOMAIN='readthedocs.org', USE_SUBDOMAIN=False, APPEND_SLASH=False) class RedirectTests(TestCase): fixtures = ["eric", "test_data"] diff --git a/readthedocs/rtd_tests/tests/test_repo_parsing.py b/readthedocs/rtd_tests/tests/test_repo_parsing.py index d77c51ecb2d..85ffbbf9997 100644 --- a/readthedocs/rtd_tests/tests/test_repo_parsing.py +++ b/readthedocs/rtd_tests/tests/test_repo_parsing.py @@ -3,6 +3,7 @@ from readthedocs.projects.models import Project + class TestRepoParsing(TestCase): fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_resolver.py b/readthedocs/rtd_tests/tests/test_resolver.py index 929ccc8dbee..2918f1ce009 100644 --- a/readthedocs/rtd_tests/tests/test_resolver.py +++ b/readthedocs/rtd_tests/tests/test_resolver.py @@ -13,6 +13,7 @@ from readthedocs.projects.models import Domain, Project, ProjectRelationship from readthedocs.rtd_tests.utils import create_user + @override_settings(PUBLIC_DOMAIN='readthedocs.org') class ResolverBase(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_restapi_client.py b/readthedocs/rtd_tests/tests/test_restapi_client.py index 1aeca51ee54..7f75d0f5ead 100644 --- a/readthedocs/rtd_tests/tests/test_restapi_client.py +++ b/readthedocs/rtd_tests/tests/test_restapi_client.py @@ -3,6 +3,7 @@ from readthedocs.restapi.client import DrfJsonSerializer + class TestDrfJsonSerializer(TestCase): data = { 'proper': 'json' diff --git a/readthedocs/rtd_tests/tests/test_search_json_parsing.py b/readthedocs/rtd_tests/tests/test_search_json_parsing.py index b51ea834f1e..3836f72d1f0 100644 --- a/readthedocs/rtd_tests/tests/test_search_json_parsing.py +++ b/readthedocs/rtd_tests/tests/test_search_json_parsing.py @@ -4,6 +4,7 @@ from readthedocs.search.parse_json import process_file + base_dir = os.path.dirname(os.path.dirname(__file__)) class TestHacks(TestCase): diff --git a/readthedocs/rtd_tests/tests/test_single_version.py b/readthedocs/rtd_tests/tests/test_single_version.py index de1bc9300cd..bb850d93275 100644 --- a/readthedocs/rtd_tests/tests/test_single_version.py +++ b/readthedocs/rtd_tests/tests/test_single_version.py @@ -4,6 +4,7 @@ from readthedocs.projects.models import Project + @override_settings( USE_SUBDOMAIN=True, PUBLIC_DOMAIN='public.readthedocs.org', SERVE_PUBLIC_DOCS=True ) diff --git a/readthedocs/rtd_tests/tests/test_subprojects.py b/readthedocs/rtd_tests/tests/test_subprojects.py index 542b2d8b02d..030fb1ecc6d 100644 --- a/readthedocs/rtd_tests/tests/test_subprojects.py +++ b/readthedocs/rtd_tests/tests/test_subprojects.py @@ -8,6 +8,7 @@ from readthedocs.projects.models import Project, ProjectRelationship from readthedocs.rtd_tests.utils import create_user + class SubprojectFormTests(TestCase): def test_empty_child(self): diff --git a/readthedocs/rtd_tests/tests/test_sync_versions.py b/readthedocs/rtd_tests/tests/test_sync_versions.py index 731cc0675bc..6c41de5b8bf 100644 --- a/readthedocs/rtd_tests/tests/test_sync_versions.py +++ b/readthedocs/rtd_tests/tests/test_sync_versions.py @@ -10,6 +10,7 @@ from readthedocs.builds.models import Version from readthedocs.projects.models import Project + class TestSyncVersions(TestCase): fixtures = ['eric', 'test_data'] diff --git a/readthedocs/rtd_tests/tests/test_urls.py b/readthedocs/rtd_tests/tests/test_urls.py index 55a4a840065..4a10e149591 100644 --- a/readthedocs/rtd_tests/tests/test_urls.py +++ b/readthedocs/rtd_tests/tests/test_urls.py @@ -1,6 +1,7 @@ from django.test import TestCase from django.urls import NoReverseMatch, reverse + class WipeUrlTests(TestCase): def test_wipe_no_params(self): diff --git a/readthedocs/rtd_tests/tests/test_version_commit_name.py b/readthedocs/rtd_tests/tests/test_version_commit_name.py index 23875955f00..b690ef3dbb5 100644 --- a/readthedocs/rtd_tests/tests/test_version_commit_name.py +++ b/readthedocs/rtd_tests/tests/test_version_commit_name.py @@ -6,6 +6,7 @@ from readthedocs.projects.constants import REPO_TYPE_GIT, REPO_TYPE_HG from readthedocs.projects.models import Project + class VersionCommitNameTests(TestCase): def test_branch_name_unicode_non_ascii(self): unicode_name = b'abc_\xd1\x84_\xe2\x99\x98'.decode('utf-8') diff --git a/readthedocs/rtd_tests/tests/test_version_config.py b/readthedocs/rtd_tests/tests/test_version_config.py index 4fd8d085454..2c79686aa7f 100644 --- a/readthedocs/rtd_tests/tests/test_version_config.py +++ b/readthedocs/rtd_tests/tests/test_version_config.py @@ -4,6 +4,7 @@ from readthedocs.builds.models import Build, Version from readthedocs.projects.models import Project + class VersionConfigTests(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/tests/test_version_slug.py b/readthedocs/rtd_tests/tests/test_version_slug.py index 44d8500f251..a193156e19f 100644 --- a/readthedocs/rtd_tests/tests/test_version_slug.py +++ b/readthedocs/rtd_tests/tests/test_version_slug.py @@ -6,6 +6,7 @@ from readthedocs.builds.version_slug import VERSION_SLUG_REGEX, VersionSlugField from readthedocs.projects.models import Project + class VersionSlugPatternTests(TestCase): pattern = re.compile('^{pattern}$'.format(pattern=VERSION_SLUG_REGEX)) diff --git a/readthedocs/rtd_tests/tests/test_views.py b/readthedocs/rtd_tests/tests/test_views.py index 56b669979f4..ca9cc42edac 100644 --- a/readthedocs/rtd_tests/tests/test_views.py +++ b/readthedocs/rtd_tests/tests/test_views.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- +from urllib.parse import urlsplit + import mock from django.contrib.auth.models import User from django.test import TestCase from django.urls import reverse from django_dynamic_fixture import get, new -from urllib.parse import urlsplit from readthedocs.builds.constants import LATEST from readthedocs.builds.models import Build @@ -12,6 +13,7 @@ from readthedocs.projects.forms import UpdateProjectForm from readthedocs.projects.models import ImportedFile, Project + class Testmaker(TestCase): def setUp(self): diff --git a/readthedocs/rtd_tests/utils.py b/readthedocs/rtd_tests/utils.py index a5768a19d7d..323dc07ad5a 100644 --- a/readthedocs/rtd_tests/utils.py +++ b/readthedocs/rtd_tests/utils.py @@ -15,6 +15,7 @@ from readthedocs.doc_builder.base import restoring_chdir + log = logging.getLogger(__name__) diff --git a/readthedocs/search/tests/conftest.py b/readthedocs/search/tests/conftest.py index ab46332f7a2..1577b6b04f9 100644 --- a/readthedocs/search/tests/conftest.py +++ b/readthedocs/search/tests/conftest.py @@ -15,6 +15,7 @@ from .dummy_data import ALL_PROJECTS, DUMMY_PAGE_JSON + @pytest.fixture(autouse=True) def mock_elastic_index(mocker): index_name = ''.join([random.choice(string.ascii_letters) for _ in range(5)]) diff --git a/readthedocs/search/tests/dummy_data.py b/readthedocs/search/tests/dummy_data.py index fbd4eed1f11..f46449374de 100644 --- a/readthedocs/search/tests/dummy_data.py +++ b/readthedocs/search/tests/dummy_data.py @@ -1,6 +1,7 @@ import json import os + _DATA_FILES = { 'pipeline': ['installation.json', 'signals.json'], 'kuma': ['documentation.json', 'docker.json'], diff --git a/readthedocs/search/tests/test_views.py b/readthedocs/search/tests/test_views.py index 37072e94948..d5ac18bafc9 100644 --- a/readthedocs/search/tests/test_views.py +++ b/readthedocs/search/tests/test_views.py @@ -11,6 +11,7 @@ from readthedocs.projects.models import Project from readthedocs.search.tests.utils import get_search_query_from_project_file + @pytest.mark.django_db @pytest.mark.search class TestElasticSearch: diff --git a/readthedocs/search/tests/utils.py b/readthedocs/search/tests/utils.py index 90ce5f70566..a48ea83dd74 100644 --- a/readthedocs/search/tests/utils.py +++ b/readthedocs/search/tests/utils.py @@ -1,5 +1,6 @@ from readthedocs.search.tests.dummy_data import DUMMY_PAGE_JSON + def get_search_query_from_project_file(project_slug, page_num=0, data_type='title'): """Return search query from the project's page file. Query is generated from the value of `data_type` diff --git a/readthedocs/vcs_support/tests.py b/readthedocs/vcs_support/tests.py index df9702ce7c8..2b16090baa4 100644 --- a/readthedocs/vcs_support/tests.py +++ b/readthedocs/vcs_support/tests.py @@ -7,6 +7,7 @@ from readthedocs.vcs_support import utils + TEST_STATICS = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_statics') From 4aded8b1db59f6fc09bd54e58e23e6b6952d9d1b Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:20:09 +0100 Subject: [PATCH 33/42] Remove skipif for six.PY2 --- readthedocs/rtd_tests/tests/test_doc_serving.py | 1 - 1 file changed, 1 deletion(-) diff --git a/readthedocs/rtd_tests/tests/test_doc_serving.py b/readthedocs/rtd_tests/tests/test_doc_serving.py index 2f9bf98ed3c..7e06a2a3baf 100644 --- a/readthedocs/rtd_tests/tests/test_doc_serving.py +++ b/readthedocs/rtd_tests/tests/test_doc_serving.py @@ -161,7 +161,6 @@ def test_default_robots_txt(self): read_data='My own robots.txt', ) @patch('readthedocs.core.views.serve.os') - @pytest.mark.skipif(six.PY2, reason='In Python2 the mock is __builtins__.open') def test_custom_robots_txt(self, os_mock, open_mock): os_mock.path.exists.return_value = True self.public.versions.update(active=True, built=True) From 447e3b722ef5663effb6c3c8d1935483f1f049dd Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:40:49 +0100 Subject: [PATCH 34/42] Update common submodule with the proper PR config --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 46aad68c905..2c428603279 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 46aad68c905ff843559b39cb52b5d54e586115c4 +Subproject commit 2c42860327916ec66f3aed7cf3d7bab809438ab4 From b5bc9c7ab2fd32d1d8d79fc0b43255468254d11a Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:41:15 +0100 Subject: [PATCH 35/42] pre-commit all the files --- readthedocs/analytics/tests.py | 1 + readthedocs/builds/models.py | 98 +++-- readthedocs/config/config.py | 56 +-- readthedocs/config/tests/test_config.py | 6 +- readthedocs/config/tests/test_find.py | 1 + readthedocs/config/tests/test_parser.py | 1 + readthedocs/config/tests/test_utils.py | 1 + readthedocs/config/tests/test_validation.py | 2 +- readthedocs/config/tests/utils.py | 8 +- readthedocs/core/tests/test_signals.py | 15 +- readthedocs/core/urls/subdomain.py | 32 +- readthedocs/core/views/hooks.py | 62 +-- readthedocs/core/views/serve.py | 25 +- .../migrations/0002_rename_last_4_digits.py | 2 +- readthedocs/gold/tests/test_forms.py | 7 +- readthedocs/gold/tests/test_signals.py | 5 +- .../oauth/migrations/0003_move_github.py | 10 +- .../0004_drop_github_and_bitbucket_models.py | 2 +- .../migrations/0006_move_oauth_source.py | 12 +- readthedocs/projects/admin.py | 41 +- readthedocs/projects/exceptions.py | 4 +- readthedocs/projects/forms.py | 62 +-- .../migrations/0007_migrate_canonical_data.py | 6 +- .../migrations/0009_add_domain_field.py | 1 - .../migrations/0010_migrate_domain_data.py | 8 +- .../projects/migrations/0011_delete-url.py | 2 +- .../migrations/0016_build-queue-name.py | 7 +- .../migrations/0020_add-api-project-proxy.py | 2 +- .../0021_add-webhook-deprecation-feature.py | 2 +- .../migrations/0023_migrate-alias-slug.py | 2 +- readthedocs/projects/models.py | 367 +++++++++++------- readthedocs/projects/notifications.py | 5 +- readthedocs/projects/signals.py | 17 +- readthedocs/projects/tasks.py | 2 +- readthedocs/projects/urls/private.py | 315 ++++++++------- readthedocs/projects/views/private.py | 5 +- readthedocs/rtd_tests/base.py | 3 +- readthedocs/rtd_tests/files/api.fjson | 2 +- .../fixtures/sample_repo/source/index.rst | 1 - .../rtd_tests/fixtures/spec/v2/schema.yml | 2 +- readthedocs/rtd_tests/mocks/environment.py | 7 +- readthedocs/rtd_tests/mocks/mock_api.py | 1 + readthedocs/rtd_tests/mocks/paths.py | 1 + .../tests/projects/test_admin_actions.py | 3 +- readthedocs/rtd_tests/tests/test_api.py | 20 +- .../rtd_tests/tests/test_api_permissions.py | 1 + .../tests/test_api_version_compare.py | 1 + readthedocs/rtd_tests/tests/test_backend.py | 6 +- .../rtd_tests/tests/test_backend_svn.py | 3 +- .../rtd_tests/tests/test_build_config.py | 6 +- readthedocs/rtd_tests/tests/test_builds.py | 15 +- readthedocs/rtd_tests/tests/test_celery.py | 10 +- .../tests/test_config_integration.py | 4 +- readthedocs/rtd_tests/tests/test_core_tags.py | 4 +- .../rtd_tests/tests/test_core_utils.py | 12 +- .../rtd_tests/tests/test_doc_serving.py | 1 - readthedocs/rtd_tests/tests/test_domains.py | 4 +- readthedocs/rtd_tests/tests/test_extend.py | 5 +- readthedocs/rtd_tests/tests/test_gold.py | 1 + .../rtd_tests/tests/test_imported_file.py | 3 +- .../rtd_tests/tests/test_integrations.py | 10 +- .../rtd_tests/tests/test_notifications.py | 6 +- .../rtd_tests/tests/test_post_commit_hooks.py | 339 ++++++++-------- readthedocs/rtd_tests/tests/test_privacy.py | 39 +- .../rtd_tests/tests/test_privacy_urls.py | 5 +- .../rtd_tests/tests/test_profile_views.py | 1 + .../rtd_tests/tests/test_project_forms.py | 2 +- .../rtd_tests/tests/test_project_symlinks.py | 24 +- .../rtd_tests/tests/test_project_views.py | 32 +- readthedocs/rtd_tests/tests/test_redirects.py | 11 +- .../tests/test_search_json_parsing.py | 1 + .../rtd_tests/tests/test_single_version.py | 1 + .../rtd_tests/tests/test_subprojects.py | 1 + .../rtd_tests/tests/test_sync_versions.py | 37 +- readthedocs/rtd_tests/tests/test_urls.py | 1 + .../tests/test_version_commit_name.py | 1 + .../rtd_tests/tests/test_version_config.py | 1 + .../rtd_tests/tests/test_version_slug.py | 3 +- readthedocs/rtd_tests/utils.py | 4 +- readthedocs/search/tests/conftest.py | 1 + readthedocs/search/tests/data/docs/story.json | 2 +- .../search/tests/data/docs/wiping.json | 2 +- .../search/tests/data/kuma/docker.json | 2 +- .../search/tests/data/kuma/documentation.json | 2 +- .../tests/data/pipeline/installation.json | 2 +- .../search/tests/data/pipeline/signals.json | 2 +- readthedocs/search/tests/dummy_data.py | 3 +- readthedocs/search/tests/test_views.py | 15 +- readthedocs/search/tests/utils.py | 7 +- readthedocs/vcs_support/tests.py | 2 +- 90 files changed, 1012 insertions(+), 857 deletions(-) diff --git a/readthedocs/analytics/tests.py b/readthedocs/analytics/tests.py index 3a18165eb94..d3507d8642e 100644 --- a/readthedocs/analytics/tests.py +++ b/readthedocs/analytics/tests.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django.test import TestCase from .utils import anonymize_ip_address, anonymize_user_agent diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index eaf4614a11c..e4fcad03753 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Models for the builds app.""" import logging @@ -49,7 +50,8 @@ DEFAULT_VERSION_PRIVACY_LEVEL = getattr( - settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public') + settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public' +) log = logging.getLogger(__name__) @@ -89,7 +91,8 @@ class Version(models.Model): #: filesystem to determine how the paths for this version are called. It #: must not be used for any other identifying purposes. slug = VersionSlugField( - _('Slug'), max_length=255, populate_from='verbose_name') + _('Slug'), max_length=255, populate_from='verbose_name' + ) supported = models.BooleanField(_('Supported'), default=True) active = models.BooleanField(_('Active'), default=False) @@ -113,7 +116,8 @@ class Meta: permissions = ( # Translators: Permission around whether a user can view the # version - ('view_version', _('View Version')),) + ('view_version', _('View Version')), + ) def __str__(self): return ugettext( @@ -121,7 +125,8 @@ def __str__(self): version=self.verbose_name, project=self.project, pk=self.pk, - )) + ) + ) @property def config(self): @@ -132,9 +137,8 @@ def config(self): :rtype: dict """ last_build = ( - self.builds.filter(state='finished', success=True) - .order_by('-date') - .first() + self.builds.filter(state='finished', + success=True).order_by('-date').first() ) return last_build.config @@ -177,7 +181,9 @@ def commit_name(self): # If we came that far it's not a special version nor a branch or tag. # Therefore just return the identifier to make a safe guess. - log.debug('TODO: Raise an exception here. Testing what cases it happens') + log.debug( + 'TODO: Raise an exception here. Testing what cases it happens' + ) return self.identifier def get_absolute_url(self): @@ -191,7 +197,8 @@ def get_absolute_url(self): ) private = self.privacy_level == PRIVATE return self.project.get_docs_url( - version_slug=self.slug, private=private) + version_slug=self.slug, private=private + ) def save(self, *args, **kwargs): # pylint: disable=arguments-differ """Add permissions to the Version for all owners on save.""" @@ -200,7 +207,8 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ for owner in self.project.users.all(): assign('view_version', owner, self) broadcast( - type='app', task=tasks.symlink_project, args=[self.project.pk]) + type='app', task=tasks.symlink_project, args=[self.project.pk] + ) return obj def delete(self, *args, **kwargs): # pylint: disable=arguments-differ @@ -246,19 +254,23 @@ def get_downloads(self, pretty=False): data['PDF'] = project.get_production_media_url('pdf', self.slug) if project.has_htmlzip(self.slug): data['HTML'] = project.get_production_media_url( - 'htmlzip', self.slug) + 'htmlzip', self.slug + ) if project.has_epub(self.slug): data['Epub'] = project.get_production_media_url( - 'epub', self.slug) + 'epub', self.slug + ) else: if project.has_pdf(self.slug): data['pdf'] = project.get_production_media_url('pdf', self.slug) if project.has_htmlzip(self.slug): data['htmlzip'] = project.get_production_media_url( - 'htmlzip', self.slug) + 'htmlzip', self.slug + ) if project.has_epub(self.slug): data['epub'] = project.get_production_media_url( - 'epub', self.slug) + 'epub', self.slug + ) return data def get_conf_py_path(self): @@ -284,9 +296,8 @@ def get_artifact_paths(self): for type_ in ('pdf', 'epub', 'htmlzip'): paths.append( - self.project.get_production_media_path( - type_=type_, - version_slug=self.slug), + self.project + .get_production_media_path(type_=type_, version_slug=self.slug), ) paths.append(self.project.rtd_build_path(version=self.slug)) @@ -308,7 +319,8 @@ def clean_build_path(self): log.exception('Build path cleanup failed') def get_github_url( - self, docroot, filename, source_suffix='.rst', action='view'): + self, docroot, filename, source_suffix='.rst', action='view' + ): """ Return a GitHub URL for a given filename. @@ -350,7 +362,8 @@ def get_github_url( ) def get_gitlab_url( - self, docroot, filename, source_suffix='.rst', action='view'): + self, docroot, filename, source_suffix='.rst', action='view' + ): repo_url = self.project.repo if 'gitlab' not in repo_url: return '' @@ -447,13 +460,17 @@ class Build(models.Model): """Build data.""" project = models.ForeignKey( - Project, verbose_name=_('Project'), related_name='builds') + Project, verbose_name=_('Project'), related_name='builds' + ) version = models.ForeignKey( - Version, verbose_name=_('Version'), null=True, related_name='builds') + Version, verbose_name=_('Version'), null=True, related_name='builds' + ) type = models.CharField( - _('Type'), max_length=55, choices=BUILD_TYPES, default='html') + _('Type'), max_length=55, choices=BUILD_TYPES, default='html' + ) state = models.CharField( - _('State'), max_length=55, choices=BUILD_STATE, default='finished') + _('State'), max_length=55, choices=BUILD_STATE, default='finished' + ) date = models.DateTimeField(_('Date'), auto_now_add=True) success = models.BooleanField(_('Success'), default=True) @@ -463,16 +480,19 @@ class Build(models.Model): error = models.TextField(_('Error'), default='', blank=True) exit_code = models.IntegerField(_('Exit code'), null=True, blank=True) commit = models.CharField( - _('Commit'), max_length=255, null=True, blank=True) + _('Commit'), max_length=255, null=True, blank=True + ) _config = JSONField(_('Configuration used in the build'), default=dict) length = models.IntegerField(_('Build Length'), null=True, blank=True) builder = models.CharField( - _('Builder'), max_length=255, null=True, blank=True) + _('Builder'), max_length=255, null=True, blank=True + ) cold_storage = models.NullBooleanField( - _('Cold Storage'), help_text='Build steps stored outside the database.') + _('Cold Storage'), help_text='Build steps stored outside the database.' + ) # Manager @@ -499,14 +519,11 @@ def previous(self): date = self.date or timezone.now() if self.project is not None and self.version is not None: return ( - Build.objects - .filter( + Build.objects.filter( project=self.project, version=self.version, date__lt=date, - ) - .order_by('-date') - .first() + ).order_by('-date').first() ) return None @@ -516,9 +533,9 @@ def config(self): Get the config used for this build. Since we are saving the config into the JSON field only when it differs - from the previous one, this helper returns the correct JSON used in - this Build object (it could be stored in this object or one of the - previous ones). + from the previous one, this helper returns the correct JSON used in this + Build object (it could be stored in this object or one of the previous + ones). """ if self.CONFIG_KEY in self._config: return Build.objects.get(pk=self._config[self.CONFIG_KEY])._config @@ -546,8 +563,8 @@ def save(self, *args, **kwargs): # noqa """ if self.pk is None or self._config_changed: previous = self.previous - if (previous is not None and - self._config and self._config == previous.config): + if (previous is not None and self._config and + self._config == previous.config): previous_pk = previous._config.get(self.CONFIG_KEY, previous.pk) self._config = {self.CONFIG_KEY: previous_pk} super().save(*args, **kwargs) @@ -561,7 +578,8 @@ def __str__(self): self.project.users.all().values_list('username', flat=True), ), pk=self.pk, - )) + ) + ) def get_absolute_url(self): return reverse('builds_detail', args=[self.project.slug, self.pk]) @@ -602,7 +620,8 @@ class BuildCommandResult(BuildCommandResultMixin, models.Model): """Build command for a ``Build``.""" build = models.ForeignKey( - Build, verbose_name=_('Build'), related_name='commands') + Build, verbose_name=_('Build'), related_name='commands' + ) command = models.TextField(_('Command')) description = models.TextField(_('Description'), blank=True) @@ -621,7 +640,8 @@ class Meta: def __str__(self): return ( ugettext('Build command {pk} for build {build}') - .format(pk=self.pk, build=self.build)) + .format(pk=self.pk, build=self.build) + ) @property def run_time(self): diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index c860b018c34..a3a6e7877a0 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -131,9 +131,15 @@ class BuildConfigBase: """ PUBLIC_ATTRIBUTES = [ - 'version', 'formats', 'python', - 'conda', 'build', 'doctype', - 'sphinx', 'mkdocs', 'submodules', + 'version', + 'formats', + 'python', + 'conda', + 'build', + 'doctype', + 'sphinx', + 'mkdocs', + 'submodules', ] version = None @@ -336,14 +342,11 @@ def validate_build(self): if ':' not in build['image']: # Prepend proper image name to user's image name build['image'] = '{}:{}'.format( - DOCKER_DEFAULT_IMAGE, - build['image'] + DOCKER_DEFAULT_IMAGE, build['image'] ) # Update docker default settings from image name if build['image'] in DOCKER_IMAGE_SETTINGS: - self.env_config.update( - DOCKER_IMAGE_SETTINGS[build['image']] - ) + self.env_config.update(DOCKER_IMAGE_SETTINGS[build['image']]) # Allow to override specific project config_image = self.defaults.get('build_image') @@ -368,22 +371,22 @@ def validate_python(self): raw_python = self.raw_config['python'] if not isinstance(raw_python, dict): self.error( - 'python', - self.PYTHON_INVALID_MESSAGE, - code=PYTHON_INVALID) + 'python', self.PYTHON_INVALID_MESSAGE, code=PYTHON_INVALID, + ) # Validate use_system_site_packages. if 'use_system_site_packages' in raw_python: - with self.catch_validation_error( - 'python.use_system_site_packages'): + with self.catch_validation_error('python.use_system_site_packages'): python['use_system_site_packages'] = validate_bool( - raw_python['use_system_site_packages']) + raw_python['use_system_site_packages'] + ) # Validate pip_install. if 'pip_install' in raw_python: with self.catch_validation_error('python.pip_install'): python['install_with_pip'] = validate_bool( - raw_python['pip_install']) + raw_python['pip_install'] + ) # Validate extra_requirements. if 'extra_requirements' in raw_python: @@ -392,13 +395,13 @@ def validate_python(self): self.error( 'python.extra_requirements', self.PYTHON_EXTRA_REQUIREMENTS_INVALID_MESSAGE, - code=PYTHON_INVALID) + code=PYTHON_INVALID + ) if not python['install_with_pip']: python['extra_requirements'] = [] else: for extra_name in raw_extra_requirements: - with self.catch_validation_error( - 'python.extra_requirements'): + with self.catch_validation_error('python.extra_requirements'): python['extra_requirements'].append( validate_string(extra_name) ) @@ -407,7 +410,8 @@ def validate_python(self): if 'setup_py_install' in raw_python: with self.catch_validation_error('python.setup_py_install'): python['install_with_setup'] = validate_bool( - raw_python['setup_py_install']) + raw_python['setup_py_install'] + ) if 'version' in raw_python: with self.catch_validation_error('python.version'): @@ -813,9 +817,8 @@ def validate_final_doc_type(self): """ Validates that the doctype is the same as the admin panel. - This a temporal validation, as the configuration file - should support per version doctype, but we need to - adapt the rtd code for that. + This a temporal validation, as the configuration file should support per + version doctype, but we need to adapt the rtd code for that. """ dashboard_doctype = self.defaults.get('doctype', 'sphinx') if self.doctype != dashboard_doctype: @@ -887,8 +890,8 @@ def validate_keys(self): """ Checks that we don't have extra keys (invalid ones). - This should be called after all the validations are done - and all keys are popped from `self.raw_config`. + This should be called after all the validations are done and all keys + are popped from `self.raw_config`. """ msg = ( 'Invalid configuration option: {}. ' @@ -978,10 +981,7 @@ def load(path, env_config): filename = find_one(path, CONFIG_FILENAME_REGEX) if not filename: - raise ConfigError( - 'No configuration file found', - code=CONFIG_REQUIRED - ) + raise ConfigError('No configuration file found', code=CONFIG_REQUIRED) with open(filename, 'r') as configuration_file: try: config = parse(configuration_file.read()) diff --git a/readthedocs/config/tests/test_config.py b/readthedocs/config/tests/test_config.py index 09b57adecee..29fbac1be6f 100644 --- a/readthedocs/config/tests/test_config.py +++ b/readthedocs/config/tests/test_config.py @@ -121,7 +121,7 @@ def test_load_unknow_version(tmpdir): def test_yaml_extension(tmpdir): - """Make sure it's capable of loading the 'readthedocs' file with a 'yaml' extension.""" + """Make sure loading the 'readthedocs' file with a 'yaml' extension.""" apply_fs(tmpdir, { 'readthedocs.yaml': textwrap.dedent( ''' @@ -643,8 +643,8 @@ def test_raise_config_not_supported(): @pytest.mark.parametrize('correct_config_filename', - [prefix + 'readthedocs.' + extension for prefix in {"", "."} - for extension in {"yml", "yaml"}]) + [prefix + 'readthedocs.' + extension for prefix in {'', '.'} + for extension in {'yml', 'yaml'}]) def test_config_filenames_regex(correct_config_filename): assert re.match(CONFIG_FILENAME_REGEX, correct_config_filename) diff --git a/readthedocs/config/tests/test_find.py b/readthedocs/config/tests/test_find.py index b982245fb71..ab813cdb6a5 100644 --- a/readthedocs/config/tests/test_find.py +++ b/readthedocs/config/tests/test_find.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import os from readthedocs.config.find import find_one diff --git a/readthedocs/config/tests/test_parser.py b/readthedocs/config/tests/test_parser.py index d140c8c0c19..9062f32ca5e 100644 --- a/readthedocs/config/tests/test_parser.py +++ b/readthedocs/config/tests/test_parser.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from io import StringIO from pytest import raises diff --git a/readthedocs/config/tests/test_utils.py b/readthedocs/config/tests/test_utils.py index 04f0192763d..3d4b57254e3 100644 --- a/readthedocs/config/tests/test_utils.py +++ b/readthedocs/config/tests/test_utils.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from .utils import apply_fs diff --git a/readthedocs/config/tests/test_validation.py b/readthedocs/config/tests/test_validation.py index 780883e00ed..8a0e98b98c4 100644 --- a/readthedocs/config/tests/test_validation.py +++ b/readthedocs/config/tests/test_validation.py @@ -79,7 +79,7 @@ def iterator(): def test_it_rejects_string_types(self): with raises(ValidationError) as excinfo: - result = validate_list('choice') + validate_list('choice') assert excinfo.value.code == INVALID_LIST diff --git a/readthedocs/config/tests/utils.py b/readthedocs/config/tests/utils.py index 1e390fa8a75..4dd6a53313c 100644 --- a/readthedocs/config/tests/utils.py +++ b/readthedocs/config/tests/utils.py @@ -1,9 +1,11 @@ +# -*- coding: utf-8 -*- def apply_fs(tmpdir, contents): """ - Create the directory structure specified in ``contents``. It's a dict of - filenames as keys and the file contents as values. If the value is another - dict, it's a subdirectory. + Create the directory structure specified in ``contents``. + + It's a dict of filenames as keys and the file contents as values. If the + value is another dict, it's a subdirectory. """ for filename, content in contents.items(): if hasattr(content, 'items'): diff --git a/readthedocs/core/tests/test_signals.py b/readthedocs/core/tests/test_signals.py index b6fa0d8ad12..40dccdaa3a7 100644 --- a/readthedocs/core/tests/test_signals.py +++ b/readthedocs/core/tests/test_signals.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import django_dynamic_fixture import pytest from django.contrib.auth.models import User @@ -11,11 +12,9 @@ class TestProjectOrganizationSignal: @pytest.mark.parametrize('model_class', [Project, RemoteOrganization]) def test_project_organization_get_deleted_upon_user_delete(self, model_class): - """ - If the user has Project or RemoteOrganization where he is the only user, - upon deleting his account, the Project or RemoteOrganization should also get - deleted. - """ + """If the user has Project or RemoteOrganization where he is the only + user, upon deleting his account, the Project or RemoteOrganization + should also get deleted.""" obj = django_dynamic_fixture.get(model_class) user1 = django_dynamic_fixture.get(User) @@ -32,10 +31,8 @@ def test_project_organization_get_deleted_upon_user_delete(self, model_class): @pytest.mark.parametrize('model_class', [Project, RemoteOrganization]) def test_multiple_users_project_organization_not_delete(self, model_class): - """ - Check Project or RemoteOrganization which have multiple users do not get deleted - when any of the user delete his account. - """ + """Check Project or RemoteOrganization which have multiple users do not + get deleted when any of the user delete his account.""" obj = django_dynamic_fixture.get(model_class) user1 = django_dynamic_fixture.get(User) diff --git a/readthedocs/core/urls/subdomain.py b/readthedocs/core/urls/subdomain.py index 75df07eb396..506688b75a8 100644 --- a/readthedocs/core/urls/subdomain.py +++ b/readthedocs/core/urls/subdomain.py @@ -23,28 +23,28 @@ subdomain_urls = [ url(r'robots.txt$', robots_txt, name='robots_txt'), - - url(r'^(?:|projects/(?P{project_slug})/)' + url( + r'^(?:|projects/(?P{project_slug})/)' r'page/(?P.*)$'.format(**pattern_opts), - redirect_page_with_filename, - name='docs_detail'), - - url((r'^(?:|projects/(?P{project_slug})/)$').format(**pattern_opts), - redirect_project_slug, - name='redirect_project_slug'), - - url((r'^(?:|projects/(?P{project_slug})/)' - r'(?P{lang_slug})/' - r'(?P{version_slug})/' - r'(?P{filename_slug})$'.format(**pattern_opts)), - serve_docs, - name='docs_detail'), + redirect_page_with_filename, name='docs_detail', + ), + url((r'^(?:|projects/(?P{project_slug})/)$').format( + **pattern_opts + ), redirect_project_slug, name='redirect_project_slug',), + url(( + r'^(?:|projects/(?P{project_slug})/)' + r'(?P{lang_slug})/' + r'(?P{version_slug})/' + r'(?P{filename_slug})$'.format(**pattern_opts) + ), serve_docs, name='docs_detail',), ] groups = [subdomain_urls] # Needed to serve media locally if getattr(settings, 'DEBUG', False): - groups.insert(0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)) + groups.insert( + 0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT,), + ) urlpatterns = reduce(add, groups) diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index 624f325d900..6302e9302dc 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Views pertaining to builds.""" import json @@ -41,13 +42,14 @@ def _build_version(project, slug, already_built=()): version = project.versions.filter(active=True, slug=slug).first() if version and slug not in already_built: log.info( - "(Version build) Building %s:%s", - project.slug, version.slug, + '(Version build) Building %s:%s', + project.slug, + version.slug, ) trigger_build(project=project, version=version, force=True) return slug - log.info("(Version build) Not Building %s", slug) + log.info('(Version build) Not Building %s', slug) return None @@ -64,8 +66,9 @@ def build_branches(project, branch_list): for branch in branch_list: versions = project.versions_from_branch_name(branch) for version in versions: - log.info("(Branch Build) Processing %s:%s", - project.slug, version.slug) + log.info( + '(Branch Build) Processing %s:%s', project.slug, version.slug + ) ret = _build_version(project, version.slug, already_built=to_build) if ret: to_build.add(ret) @@ -89,9 +92,7 @@ def sync_versions(project): try: version_identifier = project.get_default_branch() version = ( - project.versions - .filter(identifier=version_identifier) - .first() + project.versions.filter(identifier=version_identifier,).first() ) if not version: log.info('Unable to sync from %s version', version_identifier) @@ -114,10 +115,9 @@ def get_project_from_url(url): def log_info(project, msg): - log.info(constants.LOG_TEMPLATE - .format(project=project, - version='', - msg=msg)) + log.info( + constants.LOG_TEMPLATE.format(project=project, version='', msg=msg,), + ) def _build_url(url, projects, branches): @@ -127,7 +127,7 @@ def _build_url(url, projects, branches): Check each of the ``branches`` to see if they are active and should be built. """ - ret = "" + ret = '' all_built = {} all_not_building = {} @@ -151,14 +151,16 @@ def _build_url(url, projects, branches): for project_slug, built in list(all_built.items()): if built: msg = '(URL Build) Build Started: {} [{}]'.format( - url, ' '.join(built)) + url, ' '.join(built) + ) log_info(project_slug, msg=msg) ret += msg for project_slug, not_building in list(all_not_building.items()): if not_building: msg = '(URL Build) Not Building: {} [{}]'.format( - url, ' '.join(not_building)) + url, ' '.join(not_building) + ) log_info(project_slug, msg=msg) ret += msg @@ -204,15 +206,13 @@ def github_build(request): # noqa: D205 if repo_projects: log.info( 'GitHub webhook search: url=%s branches=%s', - http_search_url, - branches + http_search_url, branches, ) ssh_projects = get_project_from_url(ssh_search_url) if ssh_projects: log.info( - 'GitHub webhook search: url=%s branches=%s', - ssh_search_url, - branches + 'GitHub webhook search: url=%s branches=%s', ssh_search_url, + branches, ) projects = repo_projects | ssh_projects return _build_url(http_search_url, projects, branches) @@ -287,10 +287,11 @@ def bitbucket_build(request): else: data = json.loads(request.body) - version = 2 if request.META.get('HTTP_USER_AGENT') == 'Bitbucket-Webhooks/2.0' else 1 + version = 2 if request.META.get('HTTP_USER_AGENT') == 'Bitbucket-Webhooks/2.0' else 1 # yapf: disabled # noqa if version == 1: - branches = [commit.get('branch', '') - for commit in data['commits']] + branches = [ + commit.get('branch', '') for commit in data['commits'] + ] repository = data['repository'] if not repository['absolute_url']: return HttpResponse('Invalid request', status=400) @@ -299,8 +300,7 @@ def bitbucket_build(request): ) elif version == 2: changes = data['push']['changes'] - branches = [change['new']['name'] - for change in changes] + branches = [change['new']['name'] for change in changes] if not data['repository']['full_name']: return HttpResponse('Invalid request', status=400) search_url = 'bitbucket.org/{}'.format( @@ -352,10 +352,12 @@ def generic_build(request, project_id_or_slug=None): project = Project.objects.get(slug=project_id_or_slug) except (Project.DoesNotExist, ValueError): log.exception( - "(Incoming Generic Build) Repo not found: %s", - project_id_or_slug) + '(Incoming Generic Build) Repo not found: %s', + project_id_or_slug + ) return HttpResponseNotFound( - 'Repo not found: %s' % project_id_or_slug) + 'Repo not found: %s' % project_id_or_slug + ) # This endpoint doesn't require authorization, we shouldn't allow builds to # be triggered from this any longer. Deprecation plan is to selectively # allow access to this endpoint for now. @@ -364,11 +366,11 @@ def generic_build(request, project_id_or_slug=None): if request.method == 'POST': slug = request.POST.get('version_slug', project.default_version) log.info( - "(Incoming Generic Build) %s [%s]", + '(Incoming Generic Build) %s [%s]', project.slug, slug, ) _build_version(project, slug) else: - return HttpResponse("You must POST to this resource.") + return HttpResponse('You must POST to this resource.') return redirect('builds_project_list', project.slug) diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index fa5245cfedd..388c8ce7675 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """ Doc serving from Python. @@ -55,8 +56,11 @@ def map_subproject_slug(view_func): .. warning:: Does not take into account any kind of privacy settings. """ + @wraps(view_func) - def inner_view(request, subproject=None, subproject_slug=None, *args, **kwargs): # noqa + def inner_view( + request, subproject=None, subproject_slug=None, *args, **kwargs + ): # noqa if subproject is None and subproject_slug: # Try to fetch by subproject alias first, otherwise we might end up # redirected to an unrelated project. @@ -82,8 +86,11 @@ def map_project_slug(view_func): .. warning:: Does not take into account any kind of privacy settings. """ + @wraps(view_func) - def inner_view(request, project=None, project_slug=None, *args, **kwargs): # noqa + def inner_view( + request, project=None, project_slug=None, *args, **kwargs + ): # noqa if project is None: if not project_slug: project_slug = request.slug @@ -108,7 +115,8 @@ def redirect_project_slug(request, project, subproject): # pylint: disable=unus def redirect_page_with_filename(request, project, subproject, filename): # pylint: disable=unused-argument # noqa """Redirect /page/file.html to /en/latest/file.html.""" return HttpResponseRedirect( - resolve(subproject or project, filename=filename)) + resolve(subproject or project, filename=filename) + ) def _serve_401(request, project): @@ -126,7 +134,8 @@ def _serve_file(request, filename, basepath): # Serve from Nginx content_type, encoding = mimetypes.guess_type( - os.path.join(basepath, filename)) + os.path.join(basepath, filename) + ) content_type = content_type or 'application/octet-stream' response = HttpResponse(content_type=content_type) if encoding: @@ -153,8 +162,9 @@ def _serve_file(request, filename, basepath): @map_subproject_slug def serve_docs( request, project, subproject, lang_slug=None, version_slug=None, - filename=''): - """Exists to map existing proj, lang, version, filename views to the file format.""" + filename='' +): + """Map existing proj, lang, version, filename views to the file format.""" if not version_slug: version_slug = project.get_default_version() try: @@ -219,7 +229,8 @@ def _serve_symlink_docs(request, project, privacy_level, filename=''): files_tried.append(os.path.join(basepath, filename)) raise Http404( - 'File not found. Tried these files: %s' % ','.join(files_tried)) + 'File not found. Tried these files: %s' % ','.join(files_tried) + ) @map_project_slug diff --git a/readthedocs/gold/migrations/0002_rename_last_4_digits.py b/readthedocs/gold/migrations/0002_rename_last_4_digits.py index fc0aad4531d..478681e4ad3 100644 --- a/readthedocs/gold/migrations/0002_rename_last_4_digits.py +++ b/readthedocs/gold/migrations/0002_rename_last_4_digits.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.13 on 2018-07-16 15:45 -from django.db import migrations, models +from django.db import migrations class Migration(migrations.Migration): diff --git a/readthedocs/gold/tests/test_forms.py b/readthedocs/gold/tests/test_forms.py index 0741023368b..4231c713dcf 100644 --- a/readthedocs/gold/tests/test_forms.py +++ b/readthedocs/gold/tests/test_forms.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import django_dynamic_fixture as fixture import mock from django.contrib.auth.models import User @@ -31,7 +32,7 @@ def mock_request(self, resp): self.mocks['request'].request = mock.Mock(side_effect=resp) def test_add_subscription(self): - """Valid subscription form""" + """Valid subscription form.""" subscription_list = { 'object': 'list', 'data': [], @@ -97,7 +98,7 @@ def test_add_subscription(self): ]) def test_add_subscription_update_user(self): - """Valid subscription form""" + """Valid subscription form.""" subscription_list = { 'object': 'list', 'data': [], @@ -167,7 +168,7 @@ def test_add_subscription_update_user(self): ]) def test_update_subscription_plan(self): - """Update subcription plan""" + """Update subcription plan.""" subscription_obj = { 'id': 'sub_12345', 'object': 'subscription', diff --git a/readthedocs/gold/tests/test_signals.py b/readthedocs/gold/tests/test_signals.py index 55103d15546..f55e0b53be8 100644 --- a/readthedocs/gold/tests/test_signals.py +++ b/readthedocs/gold/tests/test_signals.py @@ -1,13 +1,10 @@ +# -*- coding: utf-8 -*- import django_dynamic_fixture as fixture import mock from django.contrib.auth.models import User -from django.db.models.signals import pre_delete from django.test import TestCase -from readthedocs.projects.models import Project - from ..models import GoldUser -from ..signals import delete_customer class GoldSignalTests(TestCase): diff --git a/readthedocs/oauth/migrations/0003_move_github.py b/readthedocs/oauth/migrations/0003_move_github.py index 14998f5e985..2b4837f4e1a 100644 --- a/readthedocs/oauth/migrations/0003_move_github.py +++ b/readthedocs/oauth/migrations/0003_move_github.py @@ -3,7 +3,7 @@ import json import logging -from django.db import migrations, models +from django.db import migrations log = logging.getLogger(__name__) @@ -23,7 +23,7 @@ def chunks(queryset, chunksize=1000): def forwards_move_repos(apps, schema_editor): - """Moves OAuth repos""" + """Moves OAuth repos.""" db = schema_editor.connection.alias # Organizations @@ -107,7 +107,7 @@ def forwards_move_repos(apps, schema_editor): else: new_repo.clone_url = data.get('clone_url') new_repo.json = json.dumps(data) - except (SyntaxError, ValueError) as e: + except (SyntaxError, ValueError): pass new_repo.save() log.info('Migrated project: %s', project.name) @@ -148,14 +148,14 @@ def forwards_move_repos(apps, schema_editor): new_repo.clone_url = clone_urls.get('ssh', project.git_url) else: new_repo.clone_url = clone_urls.get('https', project.html_url) - except (SyntaxError, ValueError) as e: + except (SyntaxError, ValueError): pass new_repo.save() log.info('Migrated project: %s', project.name) def reverse_move_repos(apps, schema_editor): - """Drop OAuth repos""" + """Drop OAuth repos.""" db = schema_editor.connection.alias RemoteRepository = apps.get_model('oauth', 'RemoteRepository') RemoteOrganization = apps.get_model('oauth', 'RemoteOrganization') diff --git a/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py b/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py index 17bfef29833..c79a997f1c4 100644 --- a/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py +++ b/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from django.db import migrations, models +from django.db import migrations def forwards_remove_content_types(apps, schema_editor): diff --git a/readthedocs/oauth/migrations/0006_move_oauth_source.py b/readthedocs/oauth/migrations/0006_move_oauth_source.py index 66d0db28f44..341f7ef4441 100644 --- a/readthedocs/oauth/migrations/0006_move_oauth_source.py +++ b/readthedocs/oauth/migrations/0006_move_oauth_source.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -from django.db import migrations, models +from django.db import migrations def forwards_move_repo_source(apps, schema_editor): - """Use source field to set repository account""" + """Use source field to set repository account.""" RemoteRepository = apps.get_model('oauth', 'RemoteRepository') SocialAccount = apps.get_model('socialaccount', 'SocialAccount') for account in SocialAccount.objects.all(): @@ -13,7 +13,7 @@ def forwards_move_repo_source(apps, schema_editor): def backwards_move_repo_source(apps, schema_editor): - RemoteRepository = apps.get_model('oauth', 'RemoteRepository') + apps.get_model('oauth', 'RemoteRepository') SocialAccount = apps.get_model('socialaccount', 'SocialAccount') for account in SocialAccount.objects.all(): rows = (account.remote_repositories @@ -21,7 +21,7 @@ def backwards_move_repo_source(apps, schema_editor): def forwards_move_org_source(apps, schema_editor): - """Use source field to set organization account""" + """Use source field to set organization account.""" RemoteOrganization = apps.get_model('oauth', 'RemoteOrganization') SocialAccount = apps.get_model('socialaccount', 'SocialAccount') for account in SocialAccount.objects.all(): @@ -31,8 +31,8 @@ def forwards_move_org_source(apps, schema_editor): def backwards_move_org_source(apps, schema_editor): - """Use source field to set organization account""" - RemoteOrganization = apps.get_model('oauth', 'RemoteOrganization') + """Use source field to set organization account.""" + apps.get_model('oauth', 'RemoteOrganization') SocialAccount = apps.get_model('socialaccount', 'SocialAccount') for account in SocialAccount.objects.all(): rows = (account.remote_organizations diff --git a/readthedocs/projects/admin.py b/readthedocs/projects/admin.py index 95e7f316319..324e4986486 100644 --- a/readthedocs/projects/admin.py +++ b/readthedocs/projects/admin.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Django administration interface for `projects.models`""" from django.contrib import admin, messages @@ -102,9 +103,7 @@ class ProjectOwnerBannedFilter(admin.SimpleListFilter): OWNER_BANNED = 'true' def lookups(self, request, model_admin): - return ( - (self.OWNER_BANNED, _('Yes')), - ) + return ((self.OWNER_BANNED, _('Yes')),) def queryset(self, request, queryset): if self.value() == self.OWNER_BANNED: @@ -118,13 +117,15 @@ class ProjectAdmin(GuardedModelAdmin): prepopulated_fields = {'slug': ('name',)} list_display = ('name', 'slug', 'repo', 'repo_type', 'featured') - list_filter = ('repo_type', 'featured', 'privacy_level', - 'documentation_type', 'programming_language', - 'feature__feature_id', ProjectOwnerBannedFilter) + list_filter = ( + 'repo_type', 'featured', 'privacy_level', 'documentation_type', + 'programming_language', 'feature__feature_id', ProjectOwnerBannedFilter, + ) list_editable = ('featured',) search_fields = ('slug', 'repo') - inlines = [ProjectRelationshipInline, RedirectInline, - VersionInline, DomainInline] + inlines = [ + ProjectRelationshipInline, RedirectInline, VersionInline, DomainInline, + ] readonly_fields = ('feature_flags',) raw_id_fields = ('users', 'main_language_project') actions = ['send_owner_email', 'ban_owner'] @@ -151,18 +152,23 @@ def ban_owner(self, request, queryset): total = 0 for project in queryset: if project.users.count() == 1: - count = (UserProfile.objects - .filter(user__projects=project) - .update(banned=True)) + count = ( + UserProfile.objects + .filter(user__projects=project) + .update(banned=True) + ) # yapf: disabled total += count else: - messages.add_message(request, messages.ERROR, - 'Project has multiple owners: {}'.format(project)) + messages.add_message( + request, messages.ERROR, + 'Project has multiple owners: {}'.format(project), + ) if total == 0: messages.add_message(request, messages.ERROR, 'No users banned') else: - messages.add_message(request, messages.INFO, - 'Banned {} user(s)'.format(total)) + messages.add_message( + request, messages.INFO, 'Banned {} user(s)'.format(total), + ) ban_owner.short_description = 'Ban project owner' @@ -185,9 +191,8 @@ def delete_selected_and_artifacts(self, request, queryset): def get_actions(self, request): actions = super().get_actions(request) actions['delete_selected'] = ( - self.__class__.delete_selected_and_artifacts, - 'delete_selected', - delete_selected.short_description + self.__class__.delete_selected_and_artifacts, 'delete_selected', + delete_selected.short_description, ) return actions diff --git a/readthedocs/projects/exceptions.py b/readthedocs/projects/exceptions.py index 54a3e89160e..85b439400df 100644 --- a/readthedocs/projects/exceptions.py +++ b/readthedocs/projects/exceptions.py @@ -44,9 +44,7 @@ class RepositoryError(BuildEnvironmentError): 'You can not have two versions with the name latest or stable.', ) - FAILED_TO_CHECKOUT = _( - 'Failed to checkout revision: {}' - ) + FAILED_TO_CHECKOUT = _('Failed to checkout revision: {}') def get_default_message(self): if settings.ALLOW_PRIVATE_REPOS: diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index af68c7b8f15..fe9e9d80106 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -125,9 +125,7 @@ def clean_name(self): _('Invalid project name, a project already exists with that name')) # yapf: disable # noqa if not potential_slug: # Check the generated slug won't be empty - raise forms.ValidationError( - _('Invalid project name'), - ) + raise forms.ValidationError(_('Invalid project name'),) return name @@ -181,7 +179,9 @@ def clean_tags(self): for tag in tags: if len(tag) > 100: raise forms.ValidationError( - _('Length of each tag must be less than or equal to 100 characters.') + _( + 'Length of each tag must be less than or equal to 100 characters.' + ) ) return tags @@ -193,8 +193,10 @@ class ProjectAdvancedForm(ProjectTriggerBuildMixin, ProjectForm): python_interpreter = forms.ChoiceField( choices=constants.PYTHON_CHOICES, initial='python', - help_text=_('The Python interpreter used to create the virtual ' - 'environment.'), + help_text=_( + 'The Python interpreter used to create the virtual ' + 'environment.' + ), ) class Meta: @@ -231,9 +233,7 @@ def __init__(self, *args, **kwargs): choices=[default_choice] + list(all_versions) ) - active_versions = self.instance.all_active_versions().values_list( - 'slug', 'verbose_name' - ) + active_versions = self.instance.all_active_versions().values_list('slug', 'verbose_name') # yapf: disabled self.fields['default_version'].widget = forms.Select( choices=active_versions ) @@ -249,6 +249,7 @@ def clean_conf_py_file(self): class UpdateProjectForm(ProjectTriggerBuildMixin, ProjectBasicsForm, ProjectExtraForm): + class Meta: model = Project fields = ( @@ -322,14 +323,16 @@ def clean_parent(self): # This validation error is mostly for testing, users shouldn't see # this in normal circumstances raise forms.ValidationError( - _('Subproject nesting is not supported')) + _('Subproject nesting is not supported') + ) return self.project def clean_child(self): child = self.cleaned_data['child'] if child == self.project: raise forms.ValidationError( - _('A project can not be a subproject of itself')) + _('A project can not be a subproject of itself') + ) return child def get_subproject_queryset(self): @@ -448,8 +451,9 @@ def build_versions_form(project): class BaseUploadHTMLForm(forms.Form): content = forms.FileField(label=_('Zip file of HTML')) - overwrite = forms.BooleanField(required=False, - label=_('Overwrite existing HTML?')) + overwrite = forms.BooleanField( + required=False, label=_('Overwrite existing HTML?') + ) def __init__(self, *args, **kwargs): self.request = kwargs.pop('request', None) @@ -500,7 +504,8 @@ def clean_user(self): user_qs = User.objects.filter(username=name) if not user_qs.exists(): raise forms.ValidationError( - _('User {name} does not exist').format(name=name)) + _('User {name} does not exist').format(name=name) + ) self.user = user_qs[0] return name @@ -523,7 +528,8 @@ def __init__(self, *args, **kwargs): def clean_email(self): self.email = EmailHook.objects.get_or_create( - email=self.cleaned_data['email'], project=self.project)[0] + email=self.cleaned_data['email'], project=self.project + )[0] return self.email def save(self): @@ -541,7 +547,8 @@ def __init__(self, *args, **kwargs): def save(self, commit=True): self.webhook = WebHook.objects.get_or_create( - url=self.cleaned_data['url'], project=self.project)[0] + url=self.cleaned_data['url'], project=self.project + )[0] self.project.webhook_notifications.add(self.webhook) return self.project @@ -589,21 +596,15 @@ def clean_project(self): ) self.translation = project_translation_qs.first() if self.translation.language == self.parent.language: - msg = ( - 'Both projects can not have the same language ({lang}).' - ) + msg = ('Both projects can not have the same language ({lang}).') raise forms.ValidationError( _(msg).format(lang=self.parent.get_language_display()) ) exists_translation = ( - self.parent.translations - .filter(language=self.translation.language) - .exists() + self.parent.translations.filter(language=self.translation.language).exists() # yapf: disabled ) if exists_translation: - msg = ( - 'This project already has a translation for {lang}.' - ) + msg = ('This project already has a translation for {lang}.') raise forms.ValidationError( _(msg).format(lang=self.translation.get_language_display()) ) @@ -689,11 +690,10 @@ def clean_domain(self): def clean_canonical(self): canonical = self.cleaned_data['canonical'] _id = self.initial.get('id') - if canonical and Domain.objects.filter( - project=self.project, canonical=True - ).exclude(pk=_id).exists(): + if canonical and Domain.objects.filter(project=self.project, canonical=True).exclude(pk=_id).exists(): # yapf: disabled # noqa raise forms.ValidationError( - _('Only 1 Domain can be canonical at a time.')) + _('Only 1 Domain can be canonical at a time.') + ) return canonical @@ -796,7 +796,9 @@ def clean_name(self): ) elif self.project.environmentvariable_set.filter(name=name).exists(): raise forms.ValidationError( - _('There is already a variable with this name for this project'), + _( + 'There is already a variable with this name for this project' + ), ) elif ' ' in name: raise forms.ValidationError( diff --git a/readthedocs/projects/migrations/0007_migrate_canonical_data.py b/readthedocs/projects/migrations/0007_migrate_canonical_data.py index 79647aa8f1c..f1f8fe05703 100644 --- a/readthedocs/projects/migrations/0007_migrate_canonical_data.py +++ b/readthedocs/projects/migrations/0007_migrate_canonical_data.py @@ -3,7 +3,7 @@ def migrate_canonical(apps, schema_editor): - Project = apps.get_model("projects", "Project") + Project = apps.get_model('projects', 'Project') for project in Project.objects.all(): if project.canonical_url: try: @@ -12,10 +12,10 @@ def migrate_canonical(apps, schema_editor): url=project.canonical_url, canonical=True, ) - print("Added {url} to {project}".format(url=project.canonical_url, project=project.name)) + print('Added {url} to {project}'.format(url=project.canonical_url, project=project.name)) except Exception as e: print(e) - print("Failed adding {url} to {project}".format( + print('Failed adding {url} to {project}'.format( url=project.canonical_url, project=project.name )) diff --git a/readthedocs/projects/migrations/0009_add_domain_field.py b/readthedocs/projects/migrations/0009_add_domain_field.py index 20703e89d27..4c910c74e44 100644 --- a/readthedocs/projects/migrations/0009_add_domain_field.py +++ b/readthedocs/projects/migrations/0009_add_domain_field.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import uuid import django.contrib.sites.models from django.db import migrations, models diff --git a/readthedocs/projects/migrations/0010_migrate_domain_data.py b/readthedocs/projects/migrations/0010_migrate_domain_data.py index 3014423333a..bcdee33d1db 100644 --- a/readthedocs/projects/migrations/0010_migrate_domain_data.py +++ b/readthedocs/projects/migrations/0010_migrate_domain_data.py @@ -7,11 +7,11 @@ def migrate_url(apps, schema_editor): - Domain = apps.get_model("projects", "Domain") + Domain = apps.get_model('projects', 'Domain') Domain.objects.filter(count=0).delete() for domain in Domain.objects.all(): if domain.project.superprojects.count() or domain.project.main_language_project: - print("{project} is a subproject or translation. Deleting domain.".format( + print('{project} is a subproject or translation. Deleting domain.'.format( project=domain.project.slug)) domain.delete() continue @@ -23,10 +23,10 @@ def migrate_url(apps, schema_editor): try: domain.domain = domain_string domain.save() - print("Added {domain} from {url}".format(url=domain.url, domain=domain_string)) + print('Added {domain} from {url}'.format(url=domain.url, domain=domain_string)) except Exception as e: print(e) - print("Failed {domain} from {url}".format(url=domain.url, domain=domain_string)) + print('Failed {domain} from {url}'.format(url=domain.url, domain=domain_string)) dms = Domain.objects.filter(domain=domain_string).order_by('-count') if dms.count() > 1: diff --git a/readthedocs/projects/migrations/0011_delete-url.py b/readthedocs/projects/migrations/0011_delete-url.py index caa9c612db8..3b01ed32cf9 100644 --- a/readthedocs/projects/migrations/0011_delete-url.py +++ b/readthedocs/projects/migrations/0011_delete-url.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from django.db import migrations, models +from django.db import migrations class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0016_build-queue-name.py b/readthedocs/projects/migrations/0016_build-queue-name.py index 6baa0a92267..0350bae8690 100644 --- a/readthedocs/projects/migrations/0016_build-queue-name.py +++ b/readthedocs/projects/migrations/0016_build-queue-name.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from django.db import migrations, models +from django.db import migrations def update_build_queue(apps, schema): - """Update project build queue to include the previously implied build- prefix""" - Project = apps.get_model("projects", "Project") + """Update project build queue to include the previously implied build- + prefix.""" + Project = apps.get_model('projects', 'Project') for project in Project.objects.all(): if project.build_queue is not None: if not project.build_queue.startswith('build-'): diff --git a/readthedocs/projects/migrations/0020_add-api-project-proxy.py b/readthedocs/projects/migrations/0020_add-api-project-proxy.py index 9e1c8458b59..0040581f77b 100644 --- a/readthedocs/projects/migrations/0020_add-api-project-proxy.py +++ b/readthedocs/projects/migrations/0020_add-api-project-proxy.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.12 on 2017-10-27 12:56 -from django.db import migrations, models +from django.db import migrations class Migration(migrations.Migration): diff --git a/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py b/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py index a3572c84cdb..0b14b4bd791 100644 --- a/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py +++ b/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -"""Add feature for allowing access to deprecated webhook endpoints""" +"""Add feature for allowing access to deprecated webhook endpoints.""" from django.db import migrations diff --git a/readthedocs/projects/migrations/0023_migrate-alias-slug.py b/readthedocs/projects/migrations/0023_migrate-alias-slug.py index c86ea6e07df..c38907700c3 100644 --- a/readthedocs/projects/migrations/0023_migrate-alias-slug.py +++ b/readthedocs/projects/migrations/0023_migrate-alias-slug.py @@ -12,7 +12,7 @@ def migrate_data(apps, schema_editor): # so that we don't break a bunch of folks URL's. # They will have to change them on update. invalid_chars_re = re.compile('[^-._a-zA-Z0-9]') - ProjectRelationship = apps.get_model("projects", "ProjectRelationship") + ProjectRelationship = apps.get_model('projects', 'ProjectRelationship') for p in ProjectRelationship.objects.all(): if p.alias and invalid_chars_re.match(p.alias): new_alias = invalid_chars_re.sub('', p.alias) diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index d3ae9b67a90..ae3cbdc0e45 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Project models.""" import fnmatch @@ -51,11 +52,15 @@ class ProjectRelationship(models.Model): This is used for subprojects """ - parent = models.ForeignKey('Project', verbose_name=_('Parent'), - related_name='subprojects') - child = models.ForeignKey('Project', verbose_name=_('Child'), - related_name='superprojects') - alias = models.SlugField(_('Alias'), max_length=255, null=True, blank=True, db_index=False) + parent = models.ForeignKey( + 'Project', verbose_name=_('Parent'), related_name='subprojects', + ) + child = models.ForeignKey( + 'Project', verbose_name=_('Child'), related_name='superprojects', + ) + alias = models.SlugField( + _('Alias'), max_length=255, null=True, blank=True, db_index=False, + ) objects = ChildRelatedProjectQuerySet.as_manager() @@ -82,76 +87,105 @@ class Project(models.Model): modified_date = models.DateTimeField(_('Modified date'), auto_now=True) # Generally from conf.py - users = models.ManyToManyField(User, verbose_name=_('User'), - related_name='projects') + users = models.ManyToManyField( + User, verbose_name=_('User'), related_name='projects' + ) # A DNS label can contain up to 63 characters. name = models.CharField(_('Name'), max_length=63) slug = models.SlugField(_('Slug'), max_length=63, unique=True) - description = models.TextField(_('Description'), blank=True, - help_text=_('The reStructuredText ' - 'description of the project')) - repo = models.CharField(_('Repository URL'), max_length=255, - validators=[validate_repository_url], - help_text=_('Hosted documentation repository URL')) - repo_type = models.CharField(_('Repository type'), max_length=10, - choices=constants.REPO_CHOICES, default='git') - project_url = models.URLField(_('Project homepage'), blank=True, - help_text=_('The project\'s homepage')) - canonical_url = models.URLField(_('Canonical URL'), blank=True, - help_text=_('URL that documentation is expected to serve from')) + description = models.TextField( + _('Description'), blank=True, + help_text=_('The reStructuredText ' + 'description of the project') + ) + repo = models.CharField( + _('Repository URL'), max_length=255, + validators=[validate_repository_url], + help_text=_('Hosted documentation repository URL') + ) + repo_type = models.CharField( + _('Repository type'), max_length=10, choices=constants.REPO_CHOICES, + default='git' + ) + project_url = models.URLField( + _('Project homepage'), blank=True, + help_text=_('The project\'s homepage') + ) + canonical_url = models.URLField( + _('Canonical URL'), blank=True, + help_text=_('URL that documentation is expected to serve from') + ) single_version = models.BooleanField( - _('Single version'), default=False, - help_text=_('A single version site has no translations and only your ' - '"latest" version, served at the root of the domain. Use ' - 'this with caution, only turn it on if you will never ' - 'have multiple versions of your docs.')) + _('Single version'), default=False, help_text=_( + 'A single version site has no translations and only your ' + '"latest" version, served at the root of the domain. Use ' + 'this with caution, only turn it on if you will never ' + 'have multiple versions of your docs.' + ) + ) default_version = models.CharField( _('Default version'), max_length=255, default=LATEST, - help_text=_('The version of your project that / redirects to')) + help_text=_('The version of your project that / redirects to') + ) # In default_branch, None means the backend should choose the # appropriate branch. Eg 'master' for git default_branch = models.CharField( _('Default branch'), max_length=255, default=None, null=True, - blank=True, help_text=_('What branch "latest" points to. Leave empty ' - 'to use the default value for your VCS (eg. ' - 'trunk or master).')) + blank=True, help_text=_( + 'What branch "latest" points to. Leave empty ' + 'to use the default value for your VCS (eg. ' + 'trunk or master).' + ) + ) requirements_file = models.CharField( _('Requirements file'), max_length=255, default=None, null=True, blank=True, help_text=_( 'A ' 'pip requirements file needed to build your documentation. ' - 'Path from the root of your project.')) + 'Path from the root of your project.' + ) + ) documentation_type = models.CharField( _('Documentation type'), max_length=20, - choices=constants.DOCUMENTATION_CHOICES, default='sphinx', - help_text=_('Type of documentation you are building. More info.')) + choices=constants.DOCUMENTATION_CHOICES, default='sphinx', help_text=_( + 'Type of documentation you are building. More info.' + ) + ) # Project features cdn_enabled = models.BooleanField(_('CDN Enabled'), default=False) analytics_code = models.CharField( - _('Analytics code'), max_length=50, null=True, blank=True, - help_text=_('Google Analytics Tracking ID ' - '(ex. UA-22345342-1). ' - 'This may slow down your page loads.')) + _('Analytics code'), max_length=50, null=True, blank=True, help_text=_( + 'Google Analytics Tracking ID ' + '(ex. UA-22345342-1). ' + 'This may slow down your page loads.' + ) + ) container_image = models.CharField( - _('Alternative container image'), max_length=64, null=True, blank=True) + _('Alternative container image'), max_length=64, null=True, blank=True + ) container_mem_limit = models.CharField( _('Container memory limit'), max_length=10, null=True, blank=True, - help_text=_('Memory limit in Docker format ' - '-- example: 512m or 1g')) + help_text=_( + 'Memory limit in Docker format ' + '-- example: 512m or 1g' + ) + ) container_time_limit = models.IntegerField( _('Container time limit in seconds'), null=True, blank=True, ) build_queue = models.CharField( - _('Alternate build queue id'), max_length=32, null=True, blank=True) + _('Alternate build queue id'), max_length=32, null=True, blank=True + ) allow_promos = models.BooleanField( - _('Allow paid advertising'), default=True, help_text=_( - 'If unchecked, users will still see community ads.')) + _('Allow paid advertising'), default=True, + help_text=_('If unchecked, users will still see community ads.') + ) ad_free = models.BooleanField( _('Ad-free'), default=False, @@ -164,88 +198,103 @@ class Project(models.Model): # Sphinx specific build options. enable_epub_build = models.BooleanField( - _('Enable EPUB build'), default=True, - help_text=_( - 'Create a EPUB version of your documentation with each build.')) + _('Enable EPUB build'), default=True, help_text=_( + 'Create a EPUB version of your documentation with each build.' + ) + ) enable_pdf_build = models.BooleanField( - _('Enable PDF build'), default=True, - help_text=_( - 'Create a PDF version of your documentation with each build.')) + _('Enable PDF build'), default=True, help_text=_( + 'Create a PDF version of your documentation with each build.' + ) + ) # Other model data. - path = models.CharField(_('Path'), max_length=255, editable=False, - help_text=_('The directory where ' - 'conf.py lives')) + path = models.CharField( + _('Path'), max_length=255, editable=False, + help_text=_('The directory where ' + 'conf.py lives') + ) conf_py_file = models.CharField( _('Python configuration file'), max_length=255, default='', blank=True, - help_text=_('Path from project root to conf.py file ' - '(ex. docs/conf.py). ' - 'Leave blank if you want us to find it for you.')) + help_text=_( + 'Path from project root to conf.py file ' + '(ex. docs/conf.py). ' + 'Leave blank if you want us to find it for you.' + ) + ) featured = models.BooleanField(_('Featured'), default=False) skip = models.BooleanField(_('Skip'), default=False) install_project = models.BooleanField( - _('Install Project'), - help_text=_('Install your project inside a virtualenv using setup.py ' - 'install'), - default=False + _('Install Project'), help_text=_( + 'Install your project inside a virtualenv using setup.py ' + 'install' + ), default=False ) # This model attribute holds the python interpreter used to create the # virtual environment python_interpreter = models.CharField( - _('Python Interpreter'), - max_length=20, - choices=constants.PYTHON_CHOICES, - default='python', - help_text=_('The Python interpreter used to create the virtual ' - 'environment.')) + _('Python Interpreter'), max_length=20, + choices=constants.PYTHON_CHOICES, default='python', help_text=_( + 'The Python interpreter used to create the virtual ' + 'environment.' + ) + ) use_system_packages = models.BooleanField( - _('Use system packages'), - help_text=_('Give the virtual environment access to the global ' - 'site-packages dir.'), - default=False + _('Use system packages'), help_text=_( + 'Give the virtual environment access to the global ' + 'site-packages dir.' + ), default=False ) privacy_level = models.CharField( _('Privacy Level'), max_length=20, choices=constants.PRIVACY_CHOICES, - default=getattr(settings, 'DEFAULT_PRIVACY_LEVEL', 'public'), - help_text=_('Level of privacy that you want on the repository. ' - 'Protected means public but not in listings.')) + default=getattr(settings, 'DEFAULT_PRIVACY_LEVEL', + 'public'), help_text=_( + 'Level of privacy that you want on the repository. ' + 'Protected means public but not in listings.' + ) + ) version_privacy_level = models.CharField( _('Version Privacy Level'), max_length=20, - choices=constants.PRIVACY_CHOICES, default=getattr( - settings, 'DEFAULT_PRIVACY_LEVEL', 'public'), - help_text=_('Default level of privacy you want on built ' - 'versions of documentation.')) + choices=constants.PRIVACY_CHOICES, + default=getattr(settings, 'DEFAULT_PRIVACY_LEVEL', + 'public'), help_text=_( + 'Default level of privacy you want on built ' + 'versions of documentation.' + ) + ) # Subprojects related_projects = models.ManyToManyField( 'self', verbose_name=_('Related projects'), blank=True, - symmetrical=False, through=ProjectRelationship) + symmetrical=False, through=ProjectRelationship + ) # Language bits - language = models.CharField(_('Language'), max_length=20, default='en', - help_text=_('The language the project ' - 'documentation is rendered in. ' - "Note: this affects your project's URL."), - choices=constants.LANGUAGES) + language = models.CharField( + _('Language'), max_length=20, default='en', help_text=_( + 'The language the project ' + 'documentation is rendered in. ' + "Note: this affects your project's URL." + ), choices=constants.LANGUAGES + ) programming_language = models.CharField( - _('Programming Language'), - max_length=20, - default='words', - help_text=_( - 'The primary programming language the project is written in.'), - choices=constants.PROGRAMMING_LANGUAGES, blank=True) + _('Programming Language'), max_length=20, default='words', help_text=_( + 'The primary programming language the project is written in.' + ), choices=constants.PROGRAMMING_LANGUAGES, blank=True + ) # A subproject pointed at its main language, so it can be tracked - main_language_project = models.ForeignKey('self', - related_name='translations', - on_delete=models.SET_NULL, - blank=True, null=True) + main_language_project = models.ForeignKey( + 'self', related_name='translations', on_delete=models.SET_NULL, + blank=True, null=True + ) has_valid_webhook = models.BooleanField( - default=False, help_text=_('This project has been built with a webhook') + default=False, + help_text=_('This project has been built with a webhook') ) has_valid_clone = models.BooleanField( default=False, help_text=_('This project has been successfully cloned') @@ -313,7 +362,10 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ try: if not first_save: broadcast( - type='app', task=tasks.update_static_metadata, args=[self.pk],) + type='app', + task=tasks.update_static_metadata, + args=[self.pk], + ) except Exception: log.exception('failed to update static metadata') try: @@ -332,12 +384,17 @@ def get_docs_url(self, version_slug=None, lang_slug=None, private=None): Always use http for now, to avoid content warnings. """ - return resolve(project=self, version_slug=version_slug, language=lang_slug, private=private) + return resolve( + project=self, version_slug=version_slug, language=lang_slug, + private=private + ) def get_builds_url(self): - return reverse('builds_project_list', kwargs={ - 'project_slug': self.slug, - }) + return reverse( + 'builds_project_list', kwargs={ + 'project_slug': self.slug, + } + ) def get_canonical_url(self): if getattr(settings, 'DONT_HIT_DB', True): @@ -402,11 +459,14 @@ def subdomain(self): def get_downloads(self): downloads = {} downloads['htmlzip'] = self.get_production_media_url( - 'htmlzip', self.get_default_version()) + 'htmlzip', self.get_default_version() + ) downloads['epub'] = self.get_production_media_url( - 'epub', self.get_default_version()) + 'epub', self.get_default_version() + ) downloads['pdf'] = self.get_production_media_url( - 'pdf', self.get_default_version()) + 'pdf', self.get_default_version() + ) return downloads @property @@ -506,7 +566,9 @@ def conf_file(self, version=LATEST): """Find a ``conf.py`` file in the project checkout.""" if self.conf_py_file: conf_path = os.path.join( - self.checkout_path(version), self.conf_py_file,) + self.checkout_path(version), + self.conf_py_file, + ) if os.path.exists(conf_path): log.info('Inserting conf.py file path from model') @@ -532,9 +594,7 @@ def conf_file(self, version=LATEST): ProjectConfigurationError.MULTIPLE_CONF_FILES ) - raise ProjectConfigurationError( - ProjectConfigurationError.NOT_FOUND - ) + raise ProjectConfigurationError(ProjectConfigurationError.NOT_FOUND) def conf_dir(self, version=LATEST): conf_file = self.conf_file(version) @@ -671,8 +731,10 @@ def api_versions(self): def active_versions(self): from readthedocs.builds.models import Version versions = Version.objects.public(project=self, only_active=True) - return (versions.filter(built=True, active=True) | - versions.filter(active=True, uploaded=True)) + return ( + versions.filter(built=True, active=True) | + versions.filter(active=True, uploaded=True) + ) def ordered_active_versions(self, user=None): from readthedocs.builds.models import Version @@ -713,23 +775,25 @@ def update_stable_version(self): current_stable = self.get_stable_version() if current_stable: identifier_updated = ( - new_stable.identifier != current_stable.identifier) + new_stable.identifier != current_stable.identifier + ) if identifier_updated and current_stable.active and current_stable.machine: log.info( 'Update stable version: {project}:{version}'.format( - project=self.slug, - version=new_stable.identifier)) + project=self.slug, version=new_stable.identifier + ) + ) current_stable.identifier = new_stable.identifier current_stable.save() return new_stable else: log.info( - 'Creating new stable version: {project}:{version}'.format( - project=self.slug, - version=new_stable.identifier)) + 'Creating new stable version: {project}:{version}' + .format(project=self.slug, version=new_stable.identifier) + ) current_stable = self.versions.create_stable( - type=new_stable.type, - identifier=new_stable.identifier) + type=new_stable.type, identifier=new_stable.identifier + ) return new_stable def versions_from_branch_name(self, branch): @@ -766,7 +830,9 @@ def get_default_branch(self): def add_subproject(self, child, alias=None): subproject, __ = ProjectRelationship.objects.get_or_create( - parent=self, child=child, alias=alias, + parent=self, + child=child, + alias=alias, ) return subproject @@ -799,7 +865,7 @@ def get_feature_value(self, feature, positive, negative): @property def show_advertising(self): """ - Whether this project is ad-free + Whether this project is ad-free. :returns: ``True`` if advertising should be shown and ``False`` otherwise :rtype: bool @@ -887,10 +953,13 @@ class ImportedFile(models.Model): things like CDN invalidation. """ - project = models.ForeignKey('Project', verbose_name=_('Project'), - related_name='imported_files') - version = models.ForeignKey('builds.Version', verbose_name=_('Version'), - related_name='imported_files', null=True) + project = models.ForeignKey( + 'Project', verbose_name=_('Project'), related_name='imported_files' + ) + version = models.ForeignKey( + 'builds.Version', verbose_name=_('Version'), + related_name='imported_files', null=True + ) name = models.CharField(_('Name'), max_length=255) slug = models.SlugField(_('Slug')) path = models.CharField(_('Path'), max_length=255) @@ -899,15 +968,17 @@ class ImportedFile(models.Model): modified_date = models.DateTimeField(_('Modified date'), auto_now=True) def get_absolute_url(self): - return resolve(project=self.project, version_slug=self.version.slug, filename=self.path) + return resolve( + project=self.project, version_slug=self.version.slug, + filename=self.path + ) def __str__(self): return '{}: {}'.format(self.name, self.project) class Notification(models.Model): - project = models.ForeignKey(Project, - related_name='%(class)s_notifications') + project = models.ForeignKey(Project, related_name='%(class)s_notifications') objects = RelatedProjectQuerySet.as_manager() class Meta: @@ -924,8 +995,9 @@ def __str__(self): @python_2_unicode_compatible class WebHook(Notification): - url = models.URLField(max_length=600, blank=True, - help_text=_('URL to send the webhook to')) + url = models.URLField( + max_length=600, blank=True, help_text=_('URL to send the webhook to') + ) def __str__(self): return self.url @@ -937,8 +1009,10 @@ class Domain(models.Model): """A custom domain name for a project.""" project = models.ForeignKey(Project, related_name='domains') - domain = models.CharField(_('Domain'), unique=True, max_length=255, - validators=[validate_domain_name]) + domain = models.CharField( + _('Domain'), unique=True, max_length=255, + validators=[validate_domain_name] + ) machine = models.BooleanField( default=False, help_text=_('This Domain was auto-created') ) @@ -946,18 +1020,19 @@ class Domain(models.Model): default=False, help_text=_('This Domain is a CNAME for the project') ) canonical = models.BooleanField( - default=False, - help_text=_( + default=False, help_text=_( 'This Domain is the primary one where the documentation is ' - 'served from') + 'served from' + ) ) https = models.BooleanField( - _('Use HTTPS'), - default=False, + _('Use HTTPS'), default=False, help_text=_('Always use HTTPS for this domain') ) - count = models.IntegerField(default=0, help_text=_( - 'Number of times this domain has been hit'),) + count = models.IntegerField( + default=0, + help_text=_('Number of times this domain has been hit'), + ) objects = RelatedProjectQuerySet.as_manager() @@ -965,7 +1040,9 @@ class Meta: ordering = ('-canonical', '-machine', 'domain') def __str__(self): - return '{domain} pointed at {project}'.format(domain=self.domain, project=self.project.name) + return '{domain} pointed at {project}'.format( + domain=self.domain, project=self.project.name + ) def save(self, *args, **kwargs): # pylint: disable=arguments-differ from readthedocs.projects import tasks @@ -975,13 +1052,19 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ else: self.domain = parsed.path super().save(*args, **kwargs) - broadcast(type='app', task=tasks.symlink_domain, - args=[self.project.pk, self.pk],) + broadcast( + type='app', + task=tasks.symlink_domain, + args=[self.project.pk, self.pk], + ) def delete(self, *args, **kwargs): # pylint: disable=arguments-differ from readthedocs.projects import tasks - broadcast(type='app', task=tasks.symlink_domain, - args=[self.project.pk, self.pk, True],) + broadcast( + type='app', + task=tasks.symlink_domain, + args=[self.project.pk, self.pk, True], + ) super().delete(*args, **kwargs) @@ -1055,9 +1138,7 @@ def add_features(sender, **kwargs): objects = FeatureQuerySet.as_manager() def __str__(self): - return '{} feature'.format( - self.get_feature_display(), - ) + return '{} feature'.format(self.get_feature_display(),) def get_feature_display(self): """ diff --git a/readthedocs/projects/notifications.py b/readthedocs/projects/notifications.py index ddf6033a53c..4848b575f18 100644 --- a/readthedocs/projects/notifications.py +++ b/readthedocs/projects/notifications.py @@ -1,11 +1,8 @@ # -*- coding: utf-8 -*- -"""Project notifications""" -from datetime import timedelta +"""Project notifications.""" from django.http import HttpRequest -from django.utils import timezone -from messages_extends.models import Message from readthedocs.notifications import Notification from readthedocs.notifications.constants import REQUIREMENT diff --git a/readthedocs/projects/signals.py b/readthedocs/projects/signals.py index 513150b11e6..1d1788ef28a 100644 --- a/readthedocs/projects/signals.py +++ b/readthedocs/projects/signals.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- -"""Project signals""" + +"""Project signals.""" import django.dispatch -before_vcs = django.dispatch.Signal(providing_args=["version"]) -after_vcs = django.dispatch.Signal(providing_args=["version"]) +before_vcs = django.dispatch.Signal(providing_args=['version']) +after_vcs = django.dispatch.Signal(providing_args=['version']) -before_build = django.dispatch.Signal(providing_args=["version"]) -after_build = django.dispatch.Signal(providing_args=["version"]) +before_build = django.dispatch.Signal(providing_args=['version']) +after_build = django.dispatch.Signal(providing_args=['version']) -project_import = django.dispatch.Signal(providing_args=["project"]) +project_import = django.dispatch.Signal(providing_args=['project']) -files_changed = django.dispatch.Signal(providing_args=["project", "files"]) +files_changed = django.dispatch.Signal(providing_args=['project', 'files']) # Used to force verify a domain (eg. for SSL cert issuance) -domain_verify = django.dispatch.Signal(providing_args=["domain"]) +domain_verify = django.dispatch.Signal(providing_args=['domain']) diff --git a/readthedocs/projects/tasks.py b/readthedocs/projects/tasks.py index b9af472d915..95ec9dbbe51 100644 --- a/readthedocs/projects/tasks.py +++ b/readthedocs/projects/tasks.py @@ -1447,7 +1447,7 @@ def finish_inactive_builds(): @app.task(queue='web') def retry_domain_verification(domain_pk): """ - Trigger domain verification on a domain + Trigger domain verification on a domain. :param domain_pk: a `Domain` pk to verify """ diff --git a/readthedocs/projects/urls/private.py b/readthedocs/projects/urls/private.py index f0c035891ed..eebec75c1f5 100644 --- a/readthedocs/projects/urls/private.py +++ b/readthedocs/projects/urls/private.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Project URLs for authenticated users.""" from django.conf.urls import url @@ -29,176 +30,204 @@ urlpatterns = [ - url(r'^$', - ProjectDashboard.as_view(), - name='projects_dashboard'), - - url(r'^import/$', - ImportView.as_view(wizard_class=ImportWizardView), - {'wizard': ImportWizardView}, - name='projects_import'), - - url(r'^import/manual/$', - ImportWizardView.as_view(), - name='projects_import_manual'), - - url(r'^import/manual/demo/$', - ImportDemoView.as_view(), - name='projects_import_demo'), - - url(r'^(?P[-\w]+)/$', - private.project_manage, - name='projects_manage'), - - url(r'^(?P[-\w]+)/edit/$', - ProjectUpdate.as_view(), - name='projects_edit'), - - url(r'^(?P[-\w]+)/advanced/$', - ProjectAdvancedUpdate.as_view(), - name='projects_advanced'), - - url(r'^(?P[-\w]+)/version/(?P[^/]+)/delete_html/$', - private.project_version_delete_html, - name='project_version_delete_html'), - - url(r'^(?P[-\w]+)/version/(?P[^/]+)/$', - private.project_version_detail, - name='project_version_detail'), - - url(r'^(?P[-\w]+)/versions/$', - private.project_versions, - name='projects_versions'), - - url(r'^(?P[-\w]+)/delete/$', - private.project_delete, - name='projects_delete'), - - url(r'^(?P[-\w]+)/users/$', - private.project_users, - name='projects_users'), - - url(r'^(?P[-\w]+)/users/delete/$', - private.project_users_delete, - name='projects_users_delete'), - - url(r'^(?P[-\w]+)/notifications/$', - private.project_notifications, - name='projects_notifications'), - - url(r'^(?P[-\w]+)/notifications/delete/$', - private.project_notifications_delete, - name='projects_notification_delete'), - - url(r'^(?P[-\w]+)/translations/$', - private.project_translations, - name='projects_translations'), - - url(r'^(?P[-\w]+)/translations/delete/(?P[-\w]+)/$', # noqa + url(r'^$', ProjectDashboard.as_view(), name='projects_dashboard'), + url( + r'^import/$', ImportView.as_view(wizard_class=ImportWizardView), + {'wizard': ImportWizardView}, name='projects_import' + ), + url( + r'^import/manual/$', ImportWizardView.as_view(), + name='projects_import_manual' + ), + url( + r'^import/manual/demo/$', ImportDemoView.as_view(), + name='projects_import_demo' + ), + url( + r'^(?P[-\w]+)/$', private.project_manage, + name='projects_manage' + ), + url( + r'^(?P[-\w]+)/edit/$', ProjectUpdate.as_view(), + name='projects_edit' + ), + url( + r'^(?P[-\w]+)/advanced/$', + ProjectAdvancedUpdate.as_view(), name='projects_advanced' + ), + url( + r'^(?P[-\w]+)/version/(?P[^/]+)/delete_html/$', + private.project_version_delete_html, name='project_version_delete_html' + ), + url( + r'^(?P[-\w]+)/version/(?P[^/]+)/$', + private.project_version_detail, name='project_version_detail' + ), + url( + r'^(?P[-\w]+)/versions/$', private.project_versions, + name='projects_versions' + ), + url( + r'^(?P[-\w]+)/delete/$', private.project_delete, + name='projects_delete' + ), + url( + r'^(?P[-\w]+)/users/$', private.project_users, + name='projects_users' + ), + url( + r'^(?P[-\w]+)/users/delete/$', + private.project_users_delete, name='projects_users_delete' + ), + url( + r'^(?P[-\w]+)/notifications/$', + private.project_notifications, name='projects_notifications' + ), + url( + r'^(?P[-\w]+)/notifications/delete/$', + private.project_notifications_delete, name='projects_notification_delete' + ), + url( + r'^(?P[-\w]+)/translations/$', + private.project_translations, name='projects_translations' + ), + url( + r'^(?P[-\w]+)/translations/delete/(?P[-\w]+)/$', # noqa private.project_translations_delete, - name='projects_translations_delete'), - - url(r'^(?P[-\w]+)/redirects/$', - private.project_redirects, - name='projects_redirects'), - - url(r'^(?P[-\w]+)/redirects/delete/$', - private.project_redirects_delete, - name='projects_redirects_delete'), - - url(r'^(?P[-\w]+)/advertising/$', - ProjectAdvertisingUpdate.as_view(), - name='projects_advertising'), + name='projects_translations_delete' + ), + url( + r'^(?P[-\w]+)/redirects/$', private.project_redirects, + name='projects_redirects' + ), + url( + r'^(?P[-\w]+)/redirects/delete/$', + private.project_redirects_delete, name='projects_redirects_delete' + ), + url( + r'^(?P[-\w]+)/advertising/$', + ProjectAdvertisingUpdate.as_view(), name='projects_advertising' + ), ] domain_urls = [ - url(r'^(?P[-\w]+)/domains/$', - DomainList.as_view(), - name='projects_domains'), - url(r'^(?P[-\w]+)/domains/create/$', - DomainCreate.as_view(), - name='projects_domains_create'), - url(r'^(?P[-\w]+)/domains/(?P[-\w]+)/edit/$', - DomainUpdate.as_view(), - name='projects_domains_edit'), - url(r'^(?P[-\w]+)/domains/(?P[-\w]+)/delete/$', - DomainDelete.as_view(), - name='projects_domains_delete'), + url( + r'^(?P[-\w]+)/domains/$', DomainList.as_view(), + name='projects_domains' + ), + url( + r'^(?P[-\w]+)/domains/create/$', DomainCreate.as_view(), + name='projects_domains_create' + ), + url( + r'^(?P[-\w]+)/domains/(?P[-\w]+)/edit/$', + DomainUpdate.as_view(), name='projects_domains_edit' + ), + url( + r'^(?P[-\w]+)/domains/(?P[-\w]+)/delete/$', + DomainDelete.as_view(), name='projects_domains_delete' + ), ] urlpatterns += domain_urls integration_urls = [ - url(r'^(?P{project_slug})/integrations/$'.format(**pattern_opts), - IntegrationList.as_view(), - name='projects_integrations'), - url(r'^(?P{project_slug})/integrations/sync/$'.format(**pattern_opts), - IntegrationWebhookSync.as_view(), - name='projects_integrations_webhooks_sync'), - url((r'^(?P{project_slug})/integrations/create/$' - .format(**pattern_opts)), - IntegrationCreate.as_view(), - name='projects_integrations_create'), - url((r'^(?P{project_slug})/' - r'integrations/(?P{integer_pk})/$' - .format(**pattern_opts)), - IntegrationDetail.as_view(), - name='projects_integrations_detail'), - url((r'^(?P{project_slug})/' - r'integrations/(?P{integer_pk})/' - r'exchange/(?P[-\w]+)/$' - .format(**pattern_opts)), - IntegrationExchangeDetail.as_view(), + url( + r'^(?P{project_slug})/integrations/$'.format( + **pattern_opts + ), IntegrationList.as_view(), name='projects_integrations' + ), + url( + r'^(?P{project_slug})/integrations/sync/$'.format( + **pattern_opts + ), IntegrationWebhookSync.as_view(), + name='projects_integrations_webhooks_sync' + ), + url(( + r'^(?P{project_slug})/integrations/create/$'.format( + **pattern_opts + ) + ), IntegrationCreate.as_view(), name='projects_integrations_create'), + url(( + r'^(?P{project_slug})/' + r'integrations/(?P{integer_pk})/$'.format( + **pattern_opts + ) + ), IntegrationDetail.as_view(), name='projects_integrations_detail'), + url(( + r'^(?P{project_slug})/' + r'integrations/(?P{integer_pk})/' + r'exchange/(?P[-\w]+)/$'.format(**pattern_opts) + ), IntegrationExchangeDetail.as_view(), name='projects_integrations_exchanges_detail'), - url((r'^(?P{project_slug})/' - r'integrations/(?P{integer_pk})/sync/$' - .format(**pattern_opts)), - IntegrationWebhookSync.as_view(), + url(( + r'^(?P{project_slug})/' + r'integrations/(?P{integer_pk})/sync/$'.format( + **pattern_opts + ) + ), IntegrationWebhookSync.as_view(), name='projects_integrations_webhooks_sync'), - url((r'^(?P{project_slug})/' - r'integrations/(?P{integer_pk})/delete/$' - .format(**pattern_opts)), - IntegrationDelete.as_view(), - name='projects_integrations_delete'), + url(( + r'^(?P{project_slug})/' + r'integrations/(?P{integer_pk})/delete/$'.format( + **pattern_opts + ) + ), IntegrationDelete.as_view(), name='projects_integrations_delete'), ] urlpatterns += integration_urls subproject_urls = [ - url(r'^(?P{project_slug})/subprojects/$'.format(**pattern_opts), - private.ProjectRelationshipList.as_view(), - name='projects_subprojects'), - url((r'^(?P{project_slug})/subprojects/create/$' - .format(**pattern_opts)), - private.ProjectRelationshipCreate.as_view(), + url( + r'^(?P{project_slug})/subprojects/$'.format( + **pattern_opts + ), private.ProjectRelationshipList.as_view(), + name='projects_subprojects' + ), + url(( + r'^(?P{project_slug})/subprojects/create/$'.format( + **pattern_opts + ) + ), private.ProjectRelationshipCreate.as_view(), name='projects_subprojects_create'), - url((r'^(?P{project_slug})/' - r'subprojects/(?P{project_slug})/edit/$' - .format(**pattern_opts)), - private.ProjectRelationshipUpdate.as_view(), + url(( + r'^(?P{project_slug})/' + r'subprojects/(?P{project_slug})/edit/$'.format( + **pattern_opts + ) + ), private.ProjectRelationshipUpdate.as_view(), name='projects_subprojects_update'), - url((r'^(?P{project_slug})/' - r'subprojects/(?P{project_slug})/delete/$' - .format(**pattern_opts)), - private.ProjectRelationshipDelete.as_view(), + url(( + r'^(?P{project_slug})/' + r'subprojects/(?P{project_slug})/delete/$'.format( + **pattern_opts + ) + ), private.ProjectRelationshipDelete.as_view(), name='projects_subprojects_delete'), ] urlpatterns += subproject_urls environmentvariable_urls = [ - url(r'^(?P[-\w]+)/environmentvariables/$', - EnvironmentVariableList.as_view(), - name='projects_environmentvariables'), - url(r'^(?P[-\w]+)/environmentvariables/create/$', + url( + r'^(?P[-\w]+)/environmentvariables/$', + EnvironmentVariableList.as_view(), name='projects_environmentvariables' + ), + url( + r'^(?P[-\w]+)/environmentvariables/create/$', EnvironmentVariableCreate.as_view(), - name='projects_environmentvariables_create'), - url(r'^(?P[-\w]+)/environmentvariables/(?P[-\w]+)/$', + name='projects_environmentvariables_create' + ), + url( + r'^(?P[-\w]+)/environmentvariables/(?P[-\w]+)/$', EnvironmentVariableDetail.as_view(), - name='projects_environmentvariables_detail'), - url(r'^(?P[-\w]+)/environmentvariables/(?P[-\w]+)/delete/$', + name='projects_environmentvariables_detail' + ), + url( + r'^(?P[-\w]+)/environmentvariables/(?P[-\w]+)/delete/$', EnvironmentVariableDelete.as_view(), - name='projects_environmentvariables_delete'), + name='projects_environmentvariables_delete' + ), ] urlpatterns += environmentvariable_urls diff --git a/readthedocs/projects/views/private.py b/readthedocs/projects/views/private.py index 87076f4504b..aa85f2d6443 100644 --- a/readthedocs/projects/views/private.py +++ b/readthedocs/projects/views/private.py @@ -220,9 +220,7 @@ def project_delete(request, project_slug): if request.method == 'POST': broadcast( - type='app', - task=tasks.remove_dirs, - args=[(project.doc_path,)] + type='app', task=tasks.remove_dirs, args=[(project.doc_path,)] ) project.delete() messages.success(request, _('Project deleted')) @@ -725,6 +723,7 @@ def get_success_url(self): class DomainList(DomainMixin, ListViewWithForm): + def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) diff --git a/readthedocs/rtd_tests/base.py b/readthedocs/rtd_tests/base.py index 270fe48281c..123e8b7f780 100644 --- a/readthedocs/rtd_tests/base.py +++ b/readthedocs/rtd_tests/base.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Base classes and mixins for unit tests.""" import logging import os @@ -21,7 +22,7 @@ def setUp(self): self.original_DOCROOT = settings.DOCROOT self.cwd = os.path.dirname(__file__) self.build_dir = tempfile.mkdtemp() - log.info("build dir: %s", self.build_dir) + log.info('build dir: %s', self.build_dir) if not os.path.exists(self.build_dir): os.makedirs(self.build_dir) settings.DOCROOT = self.build_dir diff --git a/readthedocs/rtd_tests/files/api.fjson b/readthedocs/rtd_tests/files/api.fjson index 0e6077c56f8..7972e09c87f 100644 --- a/readthedocs/rtd_tests/files/api.fjson +++ b/readthedocs/rtd_tests/files/api.fjson @@ -43,4 +43,4 @@ "title": "Internationalization" }, "metatags": "" -} \ No newline at end of file +} diff --git a/readthedocs/rtd_tests/fixtures/sample_repo/source/index.rst b/readthedocs/rtd_tests/fixtures/sample_repo/source/index.rst index d86e67de5be..164c84a69c0 100644 --- a/readthedocs/rtd_tests/fixtures/sample_repo/source/index.rst +++ b/readthedocs/rtd_tests/fixtures/sample_repo/source/index.rst @@ -17,4 +17,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/readthedocs/rtd_tests/fixtures/spec/v2/schema.yml b/readthedocs/rtd_tests/fixtures/spec/v2/schema.yml index add1eeafe9b..3ff7b7fb730 100644 --- a/readthedocs/rtd_tests/fixtures/spec/v2/schema.yml +++ b/readthedocs/rtd_tests/fixtures/spec/v2/schema.yml @@ -123,7 +123,7 @@ submodules: # List of submodules to be ignored # Default: [] exclude: any(list(str()), enum('all'), required=False) - + # Do a recursive clone? # Default: false recursive: bool(required=False) diff --git a/readthedocs/rtd_tests/mocks/environment.py b/readthedocs/rtd_tests/mocks/environment.py index 0d3ef1a56bc..c848fedd631 100644 --- a/readthedocs/rtd_tests/mocks/environment.py +++ b/readthedocs/rtd_tests/mocks/environment.py @@ -1,10 +1,11 @@ +# -*- coding: utf-8 -*- # pylint: disable=missing-docstring import mock class EnvironmentMockGroup: - """Mock out necessary environment pieces""" + """Mock out necessary environment pieces.""" def __init__(self): self.patches = { @@ -58,7 +59,7 @@ def __init__(self): self.mocks = {} def start(self): - """Create a patch object for class patches""" + """Create a patch object for class patches.""" for patch in self.patches: self.mocks[patch] = self.patches[patch].start() self.mocks['process'].communicate.return_value = ('', '') @@ -76,7 +77,7 @@ def stop(self): pass def configure_mock(self, mock, kwargs): - """Configure object mocks""" + """Configure object mocks.""" self.mocks[mock].configure_mock(**kwargs) def __getattr__(self, name): diff --git a/readthedocs/rtd_tests/mocks/mock_api.py b/readthedocs/rtd_tests/mocks/mock_api.py index eeff8aadb5c..9c6ff251515 100644 --- a/readthedocs/rtd_tests/mocks/mock_api.py +++ b/readthedocs/rtd_tests/mocks/mock_api.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Mock versions of many API-related classes.""" import json from contextlib import contextmanager diff --git a/readthedocs/rtd_tests/mocks/paths.py b/readthedocs/rtd_tests/mocks/paths.py index 3e4539ebcd3..787eabff923 100644 --- a/readthedocs/rtd_tests/mocks/paths.py +++ b/readthedocs/rtd_tests/mocks/paths.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Context managers to patch os.path.exists calls.""" import os import re diff --git a/readthedocs/rtd_tests/tests/projects/test_admin_actions.py b/readthedocs/rtd_tests/tests/projects/test_admin_actions.py index c33f90ac9b9..e1a9267ad68 100644 --- a/readthedocs/rtd_tests/tests/projects/test_admin_actions.py +++ b/readthedocs/rtd_tests/tests/projects/test_admin_actions.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import django_dynamic_fixture as fixture import mock from django import urls @@ -59,7 +60,7 @@ def test_project_ban_multiple_owners(self): @mock.patch('readthedocs.projects.admin.broadcast') def test_project_delete(self, broadcast): - """Test project and artifacts are removed""" + """Test project and artifacts are removed.""" from readthedocs.projects.tasks import remove_dirs action_data = { ACTION_CHECKBOX_NAME: [self.project.pk], diff --git a/readthedocs/rtd_tests/tests/test_api.py b/readthedocs/rtd_tests/tests/test_api.py index 03d8f5f13d2..d7c4ab06445 100644 --- a/readthedocs/rtd_tests/tests/test_api.py +++ b/readthedocs/rtd_tests/tests/test_api.py @@ -196,10 +196,7 @@ def test_save_same_config_using_patch(self): ) def test_response_building(self): - """ - The ``view docs`` attr should return a link - to the dashboard. - """ + """The ``view docs`` attr should return a link to the dashboard.""" client = APIClient() client.login(username='super', password='test') project = get( @@ -238,10 +235,7 @@ def test_response_building(self): self.assertEqual(build['docs_url'], dashboard_url) def test_response_finished_and_success(self): - """ - The ``view docs`` attr should return a link - to the docs. - """ + """The ``view docs`` attr should return a link to the docs.""" client = APIClient() client.login(username='super', password='test') project = get( @@ -276,10 +270,7 @@ def test_response_finished_and_success(self): self.assertEqual(build['docs_url'], docs_url) def test_response_finished_and_fail(self): - """ - The ``view docs`` attr should return a link - to the dashboard. - """ + """The ``view docs`` attr should return a link to the dashboard.""" client = APIClient() client.login(username='super', password='test') project = get( @@ -1482,9 +1473,8 @@ def test_get_version_by_id(self): ) def test_get_active_versions(self): - """ - Test the full response of ``/api/v2/version/?project__slug=pip&active=true`` - """ + """Test the full response of + ``/api/v2/version/?project__slug=pip&active=true``""" pip = Project.objects.get(slug='pip') data = QueryDict('', mutable=True) diff --git a/readthedocs/rtd_tests/tests/test_api_permissions.py b/readthedocs/rtd_tests/tests/test_api_permissions.py index d887be4f546..5a7d10a0a54 100644 --- a/readthedocs/rtd_tests/tests/test_api_permissions.py +++ b/readthedocs/rtd_tests/tests/test_api_permissions.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from functools import partial from unittest import TestCase diff --git a/readthedocs/rtd_tests/tests/test_api_version_compare.py b/readthedocs/rtd_tests/tests/test_api_version_compare.py index 509cbcc764c..24b94ad39d6 100644 --- a/readthedocs/rtd_tests/tests/test_api_version_compare.py +++ b/readthedocs/rtd_tests/tests/test_api_version_compare.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django.test import TestCase from readthedocs.builds.constants import LATEST diff --git a/readthedocs/rtd_tests/tests/test_backend.py b/readthedocs/rtd_tests/tests/test_backend.py index 500646797b2..8b31de91306 100644 --- a/readthedocs/rtd_tests/tests/test_backend.py +++ b/readthedocs/rtd_tests/tests/test_backend.py @@ -30,8 +30,8 @@ def setUp(self): self.eric.set_password('test') self.eric.save() self.project = Project.objects.create( - name="Test Project", - repo_type="git", + name='Test Project', + repo_type='git', #Our top-level checkout repo=git_repo ) @@ -179,7 +179,7 @@ def test_check_submodule_urls(self): def test_check_invalid_submodule_urls(self): repo = self.project.vcs_repo() repo.update() - r = repo.checkout('invalidsubmodule') + repo.checkout('invalidsubmodule') with self.assertRaises(RepositoryError) as e: repo.update_submodules(self.dummy_conf) # `invalid` is created in `make_test_git` diff --git a/readthedocs/rtd_tests/tests/test_backend_svn.py b/readthedocs/rtd_tests/tests/test_backend_svn.py index bd6f4551196..eec70268267 100644 --- a/readthedocs/rtd_tests/tests/test_backend_svn.py +++ b/readthedocs/rtd_tests/tests/test_backend_svn.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- -"""Tests For SVN""" +"""Tests For SVN.""" from django_dynamic_fixture import get -from mock import patch from readthedocs.builds.models import Version from readthedocs.projects.models import Project diff --git a/readthedocs/rtd_tests/tests/test_build_config.py b/readthedocs/rtd_tests/tests/test_build_config.py index b3ffbb7cd6a..f1129407b61 100644 --- a/readthedocs/rtd_tests/tests/test_build_config.py +++ b/readthedocs/rtd_tests/tests/test_build_config.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from os import path import pytest @@ -16,10 +17,9 @@ class PathValidator(Validator): """ - Path validator + Path validator. - Checks if the given value is a string and a existing - file. + Checks if the given value is a string and a existing file. """ tag = 'path' diff --git a/readthedocs/rtd_tests/tests/test_builds.py b/readthedocs/rtd_tests/tests/test_builds.py index 2b117fa4fac..c5611780483 100644 --- a/readthedocs/rtd_tests/tests/test_builds.py +++ b/readthedocs/rtd_tests/tests/test_builds.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import os import mock @@ -26,7 +27,7 @@ def tearDown(self): @mock.patch('readthedocs.doc_builder.config.load_config') def test_build(self, load_config): - '''Test full build''' + """Test full build.""" load_config.side_effect = create_load() project = get(Project, slug='project-1', @@ -36,7 +37,7 @@ def test_build(self, load_config): version = project.versions.all()[0] self.mocks.configure_mock('api_versions', {'return_value': [version]}) self.mocks.configure_mock('api', { - 'get.return_value': {'downloads': "no_url_here"} + 'get.return_value': {'downloads': 'no_url_here'} }) self.mocks.patches['html_build'].stop() @@ -57,7 +58,7 @@ def test_build(self, load_config): @mock.patch('readthedocs.doc_builder.config.load_config') def test_build_respects_pdf_flag(self, load_config): - '''Build output format control''' + """Build output format control.""" load_config.side_effect = create_load() project = get(Project, slug='project-1', @@ -120,7 +121,7 @@ def test_dont_localmedia_build_pdf_epub_search_in_mkdocs(self, load_config): @mock.patch('readthedocs.doc_builder.config.load_config') def test_build_respects_epub_flag(self, load_config): - '''Test build with epub enabled''' + """Test build with epub enabled.""" load_config.side_effect = create_load() project = get(Project, slug='project-1', @@ -148,7 +149,7 @@ def test_build_respects_epub_flag(self, load_config): @mock.patch('readthedocs.doc_builder.config.load_config') def test_build_respects_yaml(self, load_config): - '''Test YAML build options''' + """Test YAML build options.""" load_config.side_effect = create_load({'formats': ['epub']}) project = get(Project, slug='project-1', @@ -177,7 +178,7 @@ def test_build_respects_yaml(self, load_config): @mock.patch('readthedocs.doc_builder.config.load_config') def test_build_pdf_latex_failures(self, load_config): - '''Build failure if latex fails''' + """Build failure if latex fails.""" load_config.side_effect = create_load() self.mocks.patches['html_build'].stop() @@ -223,7 +224,7 @@ def test_build_pdf_latex_failures(self, load_config): @mock.patch('readthedocs.doc_builder.config.load_config') def test_build_pdf_latex_not_failure(self, load_config): - '''Test pass during PDF builds and bad latex failure status code''' + """Test pass during PDF builds and bad latex failure status code.""" load_config.side_effect = create_load() self.mocks.patches['html_build'].stop() diff --git a/readthedocs/rtd_tests/tests/test_celery.py b/readthedocs/rtd_tests/tests/test_celery.py index f7f16d0aed2..48613710a1e 100644 --- a/readthedocs/rtd_tests/tests/test_celery.py +++ b/readthedocs/rtd_tests/tests/test_celery.py @@ -26,7 +26,11 @@ class TestCeleryBuilding(RTDTestCase): - """These tests run the build functions directly. They don't use celery""" + """ + These tests run the build functions directly. + + They don't use celery + """ def setUp(self): repo = make_test_git() @@ -36,8 +40,8 @@ def setUp(self): self.eric.set_password('test') self.eric.save() self.project = Project.objects.create( - name="Test Project", - repo_type="git", + name='Test Project', + repo_type='git', # Our top-level checkout repo=repo, ) diff --git a/readthedocs/rtd_tests/tests/test_config_integration.py b/readthedocs/rtd_tests/tests/test_config_integration.py index 84c7fde763e..2df44a08eef 100644 --- a/readthedocs/rtd_tests/tests/test_config_integration.py +++ b/readthedocs/rtd_tests/tests/test_config_integration.py @@ -371,9 +371,7 @@ def test_build_formats_default_empty( def test_build_formats_only_pdf( self, append_conf, html_build, build_docs_class, checkout_path, tmpdir): - """ - Only the pdf format is build. - """ + """Only the pdf format is build.""" checkout_path.return_value = str(tmpdir) self.create_config_file(tmpdir, {'formats': ['pdf']}) diff --git a/readthedocs/rtd_tests/tests/test_core_tags.py b/readthedocs/rtd_tests/tests/test_core_tags.py index 43a6b4f3858..d119a5d5bbe 100644 --- a/readthedocs/rtd_tests/tests/test_core_tags.py +++ b/readthedocs/rtd_tests/tests/test_core_tags.py @@ -12,7 +12,7 @@ @override_settings(USE_SUBDOMAIN=False, PRODUCTION_DOMAIN='readthedocs.org') class CoreTagsTests(TestCase): - fixtures = ["eric", "test_data"] + fixtures = ['eric', 'test_data'] def setUp(self): url_base = '{scheme}://{domain}/docs/pip{{version}}'.format( @@ -36,7 +36,7 @@ def setUp(self): with mock.patch('readthedocs.projects.models.broadcast'): self.client.login(username='eric', password='test') self.pip = Project.objects.get(slug='pip') - self.pip_fr = Project.objects.create(name="PIP-FR", slug='pip-fr', language='fr', main_language_project=self.pip) + self.pip_fr = Project.objects.create(name='PIP-FR', slug='pip-fr', language='fr', main_language_project=self.pip) def test_project_only(self): proj = Project.objects.get(slug='pip') diff --git a/readthedocs/rtd_tests/tests/test_core_utils.py b/readthedocs/rtd_tests/tests/test_core_utils.py index b44de84ce0c..a19b5db6a2e 100644 --- a/readthedocs/rtd_tests/tests/test_core_utils.py +++ b/readthedocs/rtd_tests/tests/test_core_utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Test core util functions""" +"""Test core util functions.""" import mock from django.test import TestCase @@ -30,7 +30,7 @@ def test_trigger_skipped_project(self, update_docs_task): @mock.patch('readthedocs.projects.tasks.update_docs_task') def test_trigger_custom_queue(self, update_docs): - """Use a custom queue when routing the task""" + """Use a custom queue when routing the task.""" self.project.build_queue = 'build03' trigger_build(project=self.project, version=self.version) kwargs = { @@ -56,7 +56,7 @@ def test_trigger_custom_queue(self, update_docs): @mock.patch('readthedocs.projects.tasks.update_docs_task') def test_trigger_build_time_limit(self, update_docs): - """Pass of time limit""" + """Pass of time limit.""" trigger_build(project=self.project, version=self.version) kwargs = { 'version_pk': self.version.pk, @@ -81,7 +81,7 @@ def test_trigger_build_time_limit(self, update_docs): @mock.patch('readthedocs.projects.tasks.update_docs_task') def test_trigger_build_invalid_time_limit(self, update_docs): - """Time limit as string""" + """Time limit as string.""" self.project.container_time_limit = '200s' trigger_build(project=self.project, version=self.version) kwargs = { @@ -107,7 +107,7 @@ def test_trigger_build_invalid_time_limit(self, update_docs): @mock.patch('readthedocs.projects.tasks.update_docs_task') def test_trigger_build_rounded_time_limit(self, update_docs): - """Time limit should round down""" + """Time limit should round down.""" self.project.container_time_limit = 3 trigger_build(project=self.project, version=self.version) kwargs = { @@ -132,7 +132,7 @@ def test_trigger_build_rounded_time_limit(self, update_docs): update_docs.signature().apply_async.assert_called() def test_slugify(self): - """Test additional slugify""" + """Test additional slugify.""" self.assertEqual(slugify('This is a test'), 'this-is-a-test') self.assertEqual(slugify('project_with_underscores-v.1.0'), diff --git a/readthedocs/rtd_tests/tests/test_doc_serving.py b/readthedocs/rtd_tests/tests/test_doc_serving.py index 7e06a2a3baf..a8e42afdc96 100644 --- a/readthedocs/rtd_tests/tests/test_doc_serving.py +++ b/readthedocs/rtd_tests/tests/test_doc_serving.py @@ -2,7 +2,6 @@ import django_dynamic_fixture as fixture import mock -import pytest from django.conf import settings from django.contrib.auth.models import User from django.http import Http404 diff --git a/readthedocs/rtd_tests/tests/test_domains.py b/readthedocs/rtd_tests/tests/test_domains.py index 377f0c8f64b..cfa13b0c449 100644 --- a/readthedocs/rtd_tests/tests/test_domains.py +++ b/readthedocs/rtd_tests/tests/test_domains.py @@ -70,7 +70,7 @@ def setUp(self): self.project = get(Project, slug='kong') def test_https(self): - """Make sure https is an admin-only attribute""" + """Make sure https is an admin-only attribute.""" form = DomainForm({'domain': 'example.com', 'canonical': True}, project=self.project) self.assertTrue(form.is_valid()) @@ -82,7 +82,7 @@ def test_https(self): self.assertFalse(form.is_valid()) def test_canonical_change(self): - """Make sure canonical can be properly changed""" + """Make sure canonical can be properly changed.""" form = DomainForm({'domain': 'example.com', 'canonical': True}, project=self.project) self.assertTrue(form.is_valid()) diff --git a/readthedocs/rtd_tests/tests/test_extend.py b/readthedocs/rtd_tests/tests/test_extend.py index e4bb82a98dc..1d452cf828a 100644 --- a/readthedocs/rtd_tests/tests/test_extend.py +++ b/readthedocs/rtd_tests/tests/test_extend.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django.test import TestCase, override_settings from readthedocs.core.utils.extend import ( @@ -34,7 +35,7 @@ class ExtendTests(TestCase): @override_settings(FOO_OVERRIDE_CLASS=None) def test_no_override(self): - """Test class without override""" + """Test class without override.""" class Foo(SettingsOverrideObject): _default_class = FooBase _override_setting = 'FOO_OVERRIDE_CLASS' @@ -49,7 +50,7 @@ class Foo(SettingsOverrideObject): @override_settings(FOO_OVERRIDE_CLASS=EXTEND_OVERRIDE_PATH) def test_with_basic_override(self): - """Test class override setting defined""" + """Test class override setting defined.""" class Foo(SettingsOverrideObject): _default_class = FooBase _override_setting = 'FOO_OVERRIDE_CLASS' diff --git a/readthedocs/rtd_tests/tests/test_gold.py b/readthedocs/rtd_tests/tests/test_gold.py index db7c9f927b5..51d891eef4c 100644 --- a/readthedocs/rtd_tests/tests/test_gold.py +++ b/readthedocs/rtd_tests/tests/test_gold.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django.test import TestCase from django.urls import reverse from django_dynamic_fixture import fixture, get diff --git a/readthedocs/rtd_tests/tests/test_imported_file.py b/readthedocs/rtd_tests/tests/test_imported_file.py index fc1df679363..187b1af9830 100644 --- a/readthedocs/rtd_tests/tests/test_imported_file.py +++ b/readthedocs/rtd_tests/tests/test_imported_file.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import os from django.test import TestCase @@ -10,7 +11,7 @@ class ImportedFileTests(TestCase): - fixtures = ["eric", "test_data"] + fixtures = ['eric', 'test_data'] def setUp(self): self.project = Project.objects.get(slug='pip') diff --git a/readthedocs/rtd_tests/tests/test_integrations.py b/readthedocs/rtd_tests/tests/test_integrations.py index c35fffc78fa..e7be6588361 100644 --- a/readthedocs/rtd_tests/tests/test_integrations.py +++ b/readthedocs/rtd_tests/tests/test_integrations.py @@ -1,8 +1,7 @@ +# -*- coding: utf-8 -*- import django_dynamic_fixture as fixture -from django.contrib.contenttypes.models import ContentType -from django.test import RequestFactory, TestCase -from rest_framework.response import Response -from rest_framework.test import APIClient, APIRequestFactory +from django.test import TestCase +from rest_framework.test import APIClient from readthedocs.integrations.models import ( GitHubWebhook, @@ -14,7 +13,8 @@ class HttpExchangeTests(TestCase): - """Test HttpExchange model by using existing views + """ + Test HttpExchange model by using existing views. This doesn't mock out a req/resp cycle, as manually creating these outside views misses a number of attributes on the request object. diff --git a/readthedocs/rtd_tests/tests/test_notifications.py b/readthedocs/rtd_tests/tests/test_notifications.py index 0758e4d474c..cd3df63ec8c 100644 --- a/readthedocs/rtd_tests/tests/test_notifications.py +++ b/readthedocs/rtd_tests/tests/test_notifications.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- -"""Notification tests""" +"""Notification tests.""" -from datetime import timedelta import django_dynamic_fixture as fixture import mock @@ -9,7 +8,6 @@ from django.http import HttpRequest from django.test import TestCase from django.test.utils import override_settings -from django.utils import timezone from messages_extends.models import Message as PersistentMessage from readthedocs.builds.models import Build @@ -122,7 +120,7 @@ class TestNotification(Notification): self.assertEqual(message.user, user) def test_message_anonymous_user(self, render_to_string): - """Anonymous user still throwns exception on persistent messages""" + """Anonymous user still throwns exception on persistent messages.""" render_to_string.return_value = 'Test' class TestNotification(Notification): diff --git a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py index 1db3332cb49..cc8c1009d05 100644 --- a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py +++ b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py @@ -1,6 +1,7 @@ +# -*- coding: utf-8 -*- import json import logging -from urllib.parse import urlencode, urlparse +from urllib.parse import urlencode import mock from django.test import TestCase @@ -46,79 +47,79 @@ def _setup(self): class GitLabWebHookTest(BasePostCommitTest): - fixtures = ["eric"] + fixtures = ['eric'] def setUp(self): self._setup() self.payload = { - "object_kind": "push", - "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", - "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "ref": "refs/heads/awesome", - "checkout_sha": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "user_id": 4, - "user_name": "John Smith", - "user_email": "john@example.com", - "project_id": 15, - "project":{ - "name":"readthedocs", - "description":"", - "web_url":"http://example.com/mike/diaspora", - "avatar_url": None, - "git_ssh_url":"git@github.com:rtfd/readthedocs.org.git", - "git_http_url":"http://github.com/rtfd/readthedocs.org.git", - "namespace":"Mike", - "visibility_level":0, - "path_with_namespace":"mike/diaspora", - "default_branch":"master", - "homepage":"http://example.com/mike/diaspora", - "url":"git@github.com/rtfd/readthedocs.org.git", - "ssh_url":"git@github.com/rtfd/readthedocs.org.git", - "http_url":"http://github.com/rtfd/readthedocs.org.git" + 'object_kind': 'push', + 'before': '95790bf891e76fee5e1747ab589903a6a1f80f22', + 'after': 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7', + 'ref': 'refs/heads/awesome', + 'checkout_sha': 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7', + 'user_id': 4, + 'user_name': 'John Smith', + 'user_email': 'john@example.com', + 'project_id': 15, + 'project':{ + 'name':'readthedocs', + 'description':'', + 'web_url':'http://example.com/mike/diaspora', + 'avatar_url': None, + 'git_ssh_url':'git@github.com:rtfd/readthedocs.org.git', + 'git_http_url':'http://github.com/rtfd/readthedocs.org.git', + 'namespace':'Mike', + 'visibility_level':0, + 'path_with_namespace':'mike/diaspora', + 'default_branch':'master', + 'homepage':'http://example.com/mike/diaspora', + 'url':'git@github.com/rtfd/readthedocs.org.git', + 'ssh_url':'git@github.com/rtfd/readthedocs.org.git', + 'http_url':'http://github.com/rtfd/readthedocs.org.git' }, - "repository":{ - "name": "Diaspora", - "url": "git@github.com:rtfd/readthedocs.org.git", - "description": "", - "homepage": "http://github.com/rtfd/readthedocs.org", - "git_http_url": "http://github.com/rtfd/readthedocs.org.git", - "git_ssh_url": "git@github.com:rtfd/readthedocs.org.git", - "visibility_level": 0 + 'repository':{ + 'name': 'Diaspora', + 'url': 'git@github.com:rtfd/readthedocs.org.git', + 'description': '', + 'homepage': 'http://github.com/rtfd/readthedocs.org', + 'git_http_url': 'http://github.com/rtfd/readthedocs.org.git', + 'git_ssh_url': 'git@github.com:rtfd/readthedocs.org.git', + 'visibility_level': 0 }, - "commits": [ + 'commits': [ { - "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - "message": "Update Catalan translation to e38cb41.", - "timestamp": "2011-12-12T14:27:31+02:00", - "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - "author": { - "name": "Jordi Mallach", - "email": "jordi@softcatala.org" + 'id': 'b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327', + 'message': 'Update Catalan translation to e38cb41.', + 'timestamp': '2011-12-12T14:27:31+02:00', + 'url': 'http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327', + 'author': { + 'name': 'Jordi Mallach', + 'email': 'jordi@softcatala.org' }, - "added": ["CHANGELOG"], - "modified": ["app/controller/application.rb"], - "removed": [] + 'added': ['CHANGELOG'], + 'modified': ['app/controller/application.rb'], + 'removed': [] }, { - "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "message": "fixed readme", - "timestamp": "2012-01-03T23:36:29+02:00", - "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "author": { - "name": "GitLab dev user", - "email": "gitlabdev@dv6700.(none)" + 'id': 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7', + 'message': 'fixed readme', + 'timestamp': '2012-01-03T23:36:29+02:00', + 'url': 'http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7', + 'author': { + 'name': 'GitLab dev user', + 'email': 'gitlabdev@dv6700.(none)' }, - "added": ["CHANGELOG"], - "modified": ["app/controller/application.rb"], - "removed": [] + 'added': ['CHANGELOG'], + 'modified': ['app/controller/application.rb'], + 'removed': [] } ], - "total_commits_count": 4 + 'total_commits_count': 4 } def test_gitlab_post_commit_hook_builds_branch_docs_if_it_should(self): - """GitLab webhook should only build active versions""" + """GitLab webhook should only build active versions.""" r = self.client.post('/gitlab/', data=json.dumps(self.payload), content_type='application/json') self.assertContains(r, '(URL Build) Build Started: github.com/rtfd/readthedocs.org [awesome]') @@ -182,72 +183,72 @@ def test_gitlab_webhook_is_deprecated(self): class GitHubWebHookTest(BasePostCommitTest): - fixtures = ["eric"] + fixtures = ['eric'] def setUp(self): self._setup() self.payload = { - "after": "5ad757394b926e5637ffeafe340f952ef48bd270", - "base_ref": "refs/heads/master", - "before": "5b4e453dc913b08642b1d4fb10ed23c9d6e5b129", - "commits": [ + 'after': '5ad757394b926e5637ffeafe340f952ef48bd270', + 'base_ref': 'refs/heads/master', + 'before': '5b4e453dc913b08642b1d4fb10ed23c9d6e5b129', + 'commits': [ { - "added": [], - "author": { - "email": "eric@ericholscher.com", - "name": "Eric Holscher", - "username": "ericholscher" + 'added': [], + 'author': { + 'email': 'eric@ericholscher.com', + 'name': 'Eric Holscher', + 'username': 'ericholscher' }, - "distinct": False, - "id": "11f229c6a78f5bc8cb173104a3f7a68cdb7eb15a", - "message": "Fix it on the front list as well.", - "modified": [ - "readthedocs/templates/core/project_list_detailed.html" + 'distinct': False, + 'id': '11f229c6a78f5bc8cb173104a3f7a68cdb7eb15a', + 'message': 'Fix it on the front list as well.', + 'modified': [ + 'readthedocs/templates/core/project_list_detailed.html' ], - "removed": [], - "timestamp": "2011-09-12T19:38:55-07:00", - "url": ("https://github.com/wraithan/readthedocs.org/" - "commit/11f229c6a78f5bc8cb173104a3f7a68cdb7eb15a") + 'removed': [], + 'timestamp': '2011-09-12T19:38:55-07:00', + 'url': ('https://github.com/wraithan/readthedocs.org/' + 'commit/11f229c6a78f5bc8cb173104a3f7a68cdb7eb15a') }, ], - "compare": ("https://github.com/wraithan/readthedocs.org/compare/" - "5b4e453...5ad7573"), - "created": False, - "deleted": False, - "forced": False, - "pusher": { - "name": "none" + 'compare': ('https://github.com/wraithan/readthedocs.org/compare/' + '5b4e453...5ad7573'), + 'created': False, + 'deleted': False, + 'forced': False, + 'pusher': { + 'name': 'none' }, - "ref": "refs/heads/awesome", - "repository": { - "created_at": "2011/09/09 14:20:13 -0700", - "description": "source code to readthedocs.org", - "fork": True, - "forks": 0, - "has_downloads": True, - "has_issues": False, - "has_wiki": True, - "homepage": "http://rtfd.org/", - "language": "Python", - "name": "readthedocs.org", - "open_issues": 0, - "owner": { - "email": "XWraithanX@gmail.com", - "name": "wraithan" + 'ref': 'refs/heads/awesome', + 'repository': { + 'created_at': '2011/09/09 14:20:13 -0700', + 'description': 'source code to readthedocs.org', + 'fork': True, + 'forks': 0, + 'has_downloads': True, + 'has_issues': False, + 'has_wiki': True, + 'homepage': 'http://rtfd.org/', + 'language': 'Python', + 'name': 'readthedocs.org', + 'open_issues': 0, + 'owner': { + 'email': 'XWraithanX@gmail.com', + 'name': 'wraithan' }, - "private": False, - "pushed_at": "2011/09/12 22:33:34 -0700", - "size": 140, - "url": "https://github.com/rtfd/readthedocs.org", - "ssh_url": "git@github.com:rtfd/readthedocs.org.git", - "watchers": 1 + 'private': False, + 'pushed_at': '2011/09/12 22:33:34 -0700', + 'size': 140, + 'url': 'https://github.com/rtfd/readthedocs.org', + 'ssh_url': 'git@github.com:rtfd/readthedocs.org.git', + 'watchers': 1 } } def test_post_types(self): - """Ensure various POST formats""" + """Ensure various POST formats.""" r = self.client.post('/github/', data=json.dumps(self.payload), content_type='application/json') @@ -261,6 +262,7 @@ def test_github_upper_case_repo(self): """ Test the github post commit hook will build properly with upper case repository. + This allows for capitalization differences in post-commit hook URL's. """ payload = self.payload.copy() @@ -273,8 +275,8 @@ def test_github_upper_case_repo(self): def test_400_on_no_ref(self): """ GitHub sometimes sends us a post-commit hook without a ref. - This means we don't know what branch to build, - so return a 400. + + This means we don't know what branch to build, so return a 400. """ payload = self.payload.copy() del payload['ref'] @@ -299,10 +301,9 @@ def test_private_repo_mapping(self): """ Test for private GitHub repo mapping. - Previously we were missing triggering post-commit hooks because - we only compared against the *public* ``github.com/user/repo`` URL. - Users can also enter a ``github.com:user/repo`` URL, - which we should support. + Previously we were missing triggering post-commit hooks because we only + compared against the *public* ``github.com/user/repo`` URL. Users can + also enter a ``github.com:user/repo`` URL, which we should support. """ self.rtfd.repo = 'git@github.com:rtfd/readthedocs.org' self.rtfd.save() @@ -368,7 +369,7 @@ def test_github_webhook_is_deprecated(self): class CorePostCommitTest(BasePostCommitTest): - fixtures = ["eric"] + fixtures = ['eric'] def setUp(self): self._setup() @@ -402,83 +403,83 @@ def setUp(self): self._setup() self.hg_payload = { - "canon_url": "https://bitbucket.org", - "commits": [ + 'canon_url': 'https://bitbucket.org', + 'commits': [ { - "author": "marcus", - "branch": "default", - "files": [ + 'author': 'marcus', + 'branch': 'default', + 'files': [ { - "file": "somefile.py", - "type": "modified" + 'file': 'somefile.py', + 'type': 'modified' } ], - "message": "Added some feature things", - "node": "d14d26a93fd2", - "parents": [ - "1b458191f31a" + 'message': 'Added some feature things', + 'node': 'd14d26a93fd2', + 'parents': [ + '1b458191f31a' ], - "raw_author": "Marcus Bertrand ", - "raw_node": "d14d26a93fd28d3166fa81c0cd3b6f339bb95bfe", - "revision": 3, - "size": -1, - "timestamp": "2012-05-30 06:07:03", - "utctimestamp": "2012-05-30 04:07:03+00:00" + 'raw_author': 'Marcus Bertrand ', + 'raw_node': 'd14d26a93fd28d3166fa81c0cd3b6f339bb95bfe', + 'revision': 3, + 'size': -1, + 'timestamp': '2012-05-30 06:07:03', + 'utctimestamp': '2012-05-30 04:07:03+00:00' } ], - "repository": { - "absolute_url": "/pip/pip/", - "fork": False, - "is_private": True, - "name": "Project X", - "owner": "marcus", - "scm": "hg", - "slug": "project-x", - "website": "" + 'repository': { + 'absolute_url': '/pip/pip/', + 'fork': False, + 'is_private': True, + 'name': 'Project X', + 'owner': 'marcus', + 'scm': 'hg', + 'slug': 'project-x', + 'website': '' }, - "user": "marcus" + 'user': 'marcus' } self.git_payload = { - "canon_url": "https://bitbucket.org", - "commits": [ + 'canon_url': 'https://bitbucket.org', + 'commits': [ { - "author": "marcus", - "branch": "master", - "files": [ + 'author': 'marcus', + 'branch': 'master', + 'files': [ { - "file": "somefile.py", - "type": "modified" + 'file': 'somefile.py', + 'type': 'modified' } ], - "message": "Added some more things to somefile.py\n", - "node": "620ade18607a", - "parents": [ - "702c70160afc" + 'message': 'Added some more things to somefile.py\n', + 'node': '620ade18607a', + 'parents': [ + '702c70160afc' ], - "raw_author": "Marcus Bertrand ", - "raw_node": "620ade18607ac42d872b568bb92acaa9a28620e9", - "revision": None, - "size": -1, - "timestamp": "2012-05-30 05:58:56", - "utctimestamp": "2012-05-30 03:58:56+00:00" + 'raw_author': 'Marcus Bertrand ', + 'raw_node': '620ade18607ac42d872b568bb92acaa9a28620e9', + 'revision': None, + 'size': -1, + 'timestamp': '2012-05-30 05:58:56', + 'utctimestamp': '2012-05-30 03:58:56+00:00' } ], - "repository": { - "absolute_url": "/sphinx/sphinx/", - "fork": False, - "is_private": True, - "name": "Project X", - "owner": "marcus", - "scm": "git", - "slug": "project-x", - "website": "https://atlassian.com/" + 'repository': { + 'absolute_url': '/sphinx/sphinx/', + 'fork': False, + 'is_private': True, + 'name': 'Project X', + 'owner': 'marcus', + 'scm': 'git', + 'slug': 'project-x', + 'website': 'https://atlassian.com/' }, - "user": "marcus" + 'user': 'marcus' } def test_post_types(self): - """Ensure various POST formats""" + """Ensure various POST formats.""" r = self.client.post('/bitbucket/', data=json.dumps(self.hg_payload), content_type='application/json') @@ -554,7 +555,7 @@ def test_bitbucket_default_branch(self): self.feature.projects.add(self.test_project) self.git_payload['commits'] = [{ - "branch": "integration", + 'branch': 'integration', }] self.git_payload['repository'] = { 'absolute_url': '/test/project/' diff --git a/readthedocs/rtd_tests/tests/test_privacy.py b/readthedocs/rtd_tests/tests/test_privacy.py index a5977ad86b6..e273eafc4e1 100644 --- a/readthedocs/rtd_tests/tests/test_privacy.py +++ b/readthedocs/rtd_tests/tests/test_privacy.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import json import logging @@ -33,7 +34,7 @@ def _create_kong(self, privacy_level='private', version_privacy_level='private'): self.client.login(username='eric', password='test') log.info( - "Making kong with privacy: %s and version privacy: %s", + 'Making kong with privacy: %s and version privacy: %s', privacy_level, version_privacy_level, ) @@ -165,22 +166,22 @@ def test_public_branch(self): def test_public_repo_api(self): self._create_kong('public', 'public') self.client.login(username='eric', password='test') - resp = self.client.get("http://testserver/api/v1/project/django-kong/", - data={"format": "json"}) + resp = self.client.get('http://testserver/api/v1/project/django-kong/', + data={'format': 'json'}) self.assertEqual(resp.status_code, 200) - resp = self.client.get("http://testserver/api/v1/project/", - data={"format": "json"}) + resp = self.client.get('http://testserver/api/v1/project/', + data={'format': 'json'}) self.assertEqual(resp.status_code, 200) data = json.loads(resp.content) self.assertEqual(data['meta']['total_count'], 1) self.client.login(username='tester', password='test') - resp = self.client.get("http://testserver/api/v1/project/django-kong/", - data={"format": "json"}) + resp = self.client.get('http://testserver/api/v1/project/django-kong/', + data={'format': 'json'}) self.assertEqual(resp.status_code, 200) - resp = self.client.get("http://testserver/api/v1/project/", - data={"format": "json"}) + resp = self.client.get('http://testserver/api/v1/project/', + data={'format': 'json'}) self.assertEqual(resp.status_code, 200) data = json.loads(resp.content) self.assertEqual(data['meta']['total_count'], 1) @@ -188,21 +189,21 @@ def test_public_repo_api(self): def test_private_repo_api(self): self._create_kong('private', 'private') self.client.login(username='eric', password='test') - resp = self.client.get("http://testserver/api/v1/project/django-kong/", - data={"format": "json"}) + resp = self.client.get('http://testserver/api/v1/project/django-kong/', + data={'format': 'json'}) self.assertEqual(resp.status_code, 200) - resp = self.client.get("http://testserver/api/v1/project/", - data={"format": "json"}) + resp = self.client.get('http://testserver/api/v1/project/', + data={'format': 'json'}) self.assertEqual(resp.status_code, 200) data = json.loads(resp.content) self.assertEqual(data['meta']['total_count'], 1) self.client.login(username='tester', password='test') - resp = self.client.get("http://testserver/api/v1/project/django-kong/", - data={"format": "json"}) + resp = self.client.get('http://testserver/api/v1/project/django-kong/', + data={'format': 'json'}) self.assertEqual(resp.status_code, 404) - resp = self.client.get("http://testserver/api/v1/project/", - data={"format": "json"}) + resp = self.client.get('http://testserver/api/v1/project/', + data={'format': 'json'}) self.assertEqual(resp.status_code, 200) data = json.loads(resp.content) self.assertEqual(data['meta']['total_count'], 0) @@ -365,9 +366,7 @@ def test_build_filtering(self): self.assertNotContains(r, 'test-slug') def test_queryset_chaining(self): - """ - Test that manager methods get set on related querysets. - """ + """Test that manager methods get set on related querysets.""" kong = self._create_kong('public', 'private') self.assertEqual( kong.versions.private().get(slug='latest').slug, diff --git a/readthedocs/rtd_tests/tests/test_privacy_urls.py b/readthedocs/rtd_tests/tests/test_privacy_urls.py index ed5fff8c2bc..4c874db49bb 100644 --- a/readthedocs/rtd_tests/tests/test_privacy_urls.py +++ b/readthedocs/rtd_tests/tests/test_privacy_urls.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import re import mock @@ -90,10 +91,10 @@ def setUp(self): for not_obj in self.context_data: if isinstance(obj, list) or isinstance(obj, set) or isinstance(obj, tuple): self.assertNotIn(not_obj, obj) - print("{} not in {}".format(not_obj, obj)) + print('{} not in {}'.format(not_obj, obj)) else: self.assertNotEqual(not_obj, obj) - print("{} is not {}".format(not_obj, obj)) + print('{} is not {}'.format(not_obj, obj)) def _test_url(self, urlpatterns): deconstructed_urls = extract_views_from_urlpatterns(urlpatterns) diff --git a/readthedocs/rtd_tests/tests/test_profile_views.py b/readthedocs/rtd_tests/tests/test_profile_views.py index 69dde65c877..1131de23b97 100644 --- a/readthedocs/rtd_tests/tests/test_profile_views.py +++ b/readthedocs/rtd_tests/tests/test_profile_views.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django.contrib.auth.models import User from django.test import TestCase from django.urls import reverse diff --git a/readthedocs/rtd_tests/tests/test_project_forms.py b/readthedocs/rtd_tests/tests/test_project_forms.py index 166426263cd..3e52496a5b1 100644 --- a/readthedocs/rtd_tests/tests/test_project_forms.py +++ b/readthedocs/rtd_tests/tests/test_project_forms.py @@ -169,7 +169,7 @@ def test_length_of_tags(self): data['tags'] = '{},{}'.format('a'*90, 'b'*100) form = ProjectExtraForm(data) self.assertTrue(form.is_valid()) - + data['tags'] = '{},{}'.format('a'*99, 'b'*101) form = ProjectExtraForm(data) self.assertFalse(form.is_valid()) diff --git a/readthedocs/rtd_tests/tests/test_project_symlinks.py b/readthedocs/rtd_tests/tests/test_project_symlinks.py index 473963b4455..3e992e6bd97 100644 --- a/readthedocs/rtd_tests/tests/test_project_symlinks.py +++ b/readthedocs/rtd_tests/tests/test_project_symlinks.py @@ -21,7 +21,8 @@ def get_filesystem(path, top_level_path=None): - """Recurse into path, return dictionary mapping of path and files + """ + Recurse into path, return dictionary mapping of path and files. This will return the path `path` as a nested dictionary of path objects. Directories are mapped to dictionary objects, file objects will have a @@ -255,7 +256,7 @@ def test_broadcast_remove_orphan_symlinks(self): ) def test_symlink_cname_dont_link_missing_domains(self): - """Domains should be relinked after deletion""" + """Domains should be relinked after deletion.""" self.domain = get(Domain, project=self.project, domain='woot.com', url='http://woot.com', cname=True) self.symlink.symlink_cnames() @@ -320,7 +321,7 @@ def setUp(self): self.symlink = self.symlink_class(self.project) def test_subproject_normal(self): - """Symlink pass adds symlink for subproject""" + """Symlink pass adds symlink for subproject.""" self.project.add_subproject(self.subproject) self.symlink.symlink_subprojects() filesystem = { @@ -362,7 +363,7 @@ def test_subproject_normal(self): self.assertFilesystem(filesystem) def test_subproject_alias(self): - """Symlink pass adds symlink for subproject alias""" + """Symlink pass adds symlink for subproject alias.""" self.project.add_subproject(self.subproject, alias='sweet-alias') self.symlink.symlink_subprojects() filesystem = { @@ -409,7 +410,7 @@ def test_subproject_alias(self): self.assertFilesystem(filesystem) def test_subproject_alias_with_spaces(self): - """Symlink pass adds symlink for subproject alias""" + """Symlink pass adds symlink for subproject alias.""" self.project.add_subproject(self.subproject, alias='Sweet Alias') self.symlink.symlink_subprojects() filesystem = { @@ -456,7 +457,7 @@ def test_subproject_alias_with_spaces(self): self.assertFilesystem(filesystem) def test_remove_subprojects(self): - """Nonexistent subprojects are unlinked""" + """Nonexistent subprojects are unlinked.""" self.project.add_subproject(self.subproject) self.symlink.symlink_subprojects() filesystem = { @@ -538,7 +539,7 @@ def setUp(self): self.assertIn(self.translation, self.project.translations.all()) def test_symlink_basic(self): - """Test basic scenario, language english, translation german""" + """Test basic scenario, language english, translation german.""" self.symlink.symlink_translations() filesystem = { 'private_cname_project': {}, @@ -589,7 +590,7 @@ def test_symlink_basic(self): self.assertFilesystem(filesystem) def test_symlink_non_english(self): - """Test language german, translation english""" + """Test language german, translation english.""" self.project.language = 'de' self.translation.language = 'en' self.project.save() @@ -644,7 +645,8 @@ def test_symlink_non_english(self): self.assertFilesystem(filesystem) def test_symlink_no_english(self): - """Test language german, no english + """ + Test language german, no english. This should symlink the translation to 'en' even though there is no 'en' language in translations or project language @@ -1057,8 +1059,8 @@ def test_change_subproject_privacy(self): """ Change subproject's ``privacy_level`` creates proper symlinks. - When the ``privacy_level`` changes in the subprojects, we need to - re-symlink the superproject also to keep in sync its symlink under the + When the ``privacy_level`` changes in the subprojects, we need to re- + symlink the superproject also to keep in sync its symlink under the private/public roots. """ filesystem_before = { diff --git a/readthedocs/rtd_tests/tests/test_project_views.py b/readthedocs/rtd_tests/tests/test_project_views.py index 20dd5f5ec01..b2d5c752cc0 100644 --- a/readthedocs/rtd_tests/tests/test_project_views.py +++ b/readthedocs/rtd_tests/tests/test_project_views.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from datetime import timedelta from django.contrib.auth.models import User @@ -51,7 +52,7 @@ def setUp(self): self.data['{}-current_step'.format(self.wizard_class_slug)] = 'extra' def test_profile_middleware_no_profile(self): - """User without profile and isn't banned""" + """User without profile and isn't banned.""" req = self.request('/projects/import', method='post', data=self.data) req.user = get(User, profile=None) resp = ImportWizardView.as_view()(req) @@ -60,7 +61,7 @@ def test_profile_middleware_no_profile(self): @patch('readthedocs.projects.views.private.ProjectBasicsForm.clean') def test_profile_middleware_spam(self, form): - """User will be banned""" + """User will be banned.""" form.side_effect = ProjectSpamError req = self.request('/projects/import', method='post', data=self.data) req.user = get(User) @@ -70,7 +71,7 @@ def test_profile_middleware_spam(self, form): self.assertTrue(req.user.profile.banned) def test_profile_middleware_banned(self): - """User is banned""" + """User is banned.""" req = self.request('/projects/import', method='post', data=self.data) req.user = get(User) req.user.profile.banned = True @@ -103,7 +104,7 @@ def request(self, *args, **kwargs): return super().request(*args, **kwargs) def test_form_pass(self): - """Only submit the basics""" + """Only submit the basics.""" resp = self.post_step('basics') self.assertIsInstance(resp, HttpResponseRedirect) self.assertEqual(resp.status_code, 302) @@ -135,7 +136,7 @@ def test_remote_repository_is_not_added_for_wrong_user(self): self.assertWizardFailure(resp, 'remote_repository') def test_form_missing(self): - """Submit form with missing data, expect to get failures""" + """Submit form with missing data, expect to get failures.""" self.step_data['basics'] = {'advanced': True} resp = self.post_step('basics') self.assertWizardFailure(resp, 'name') @@ -155,7 +156,7 @@ def setUp(self): } def test_form_pass(self): - """Test all forms pass validation""" + """Test all forms pass validation.""" resp = self.post_step('basics') self.assertWizardResponse(resp, 'extra') resp = self.post_step('extra', session=list(resp._request.session.items())) @@ -176,7 +177,7 @@ def test_form_pass(self): self.assertEqual(getattr(proj, key), val) def test_form_missing_extra(self): - """Submit extra form with missing data, expect to get failures""" + """Submit extra form with missing data, expect to get failures.""" # Remove extra data to trigger validation errors self.step_data['extra'] = {} @@ -204,7 +205,7 @@ def test_remote_repository_is_added(self): @patch('readthedocs.projects.views.private.ProjectExtraForm.clean_description', create=True) def test_form_spam(self, mocked_validator): - """Don't add project on a spammy description""" + """Don't add project on a spammy description.""" self.user.date_joined = timezone.now() - timedelta(days=365) self.user.save() mocked_validator.side_effect = ProjectSpamError @@ -226,7 +227,7 @@ def test_form_spam(self, mocked_validator): @patch('readthedocs.projects.views.private.ProjectExtraForm.clean_description', create=True) def test_form_spam_ban_user(self, mocked_validator): - """Don't add spam and ban new user""" + """Don't add spam and ban new user.""" self.user.date_joined = timezone.now() self.user.save() mocked_validator.side_effect = ProjectSpamError @@ -247,7 +248,7 @@ def test_form_spam_ban_user(self, mocked_validator): class TestImportDemoView(MockBuildTestCase): - """Test project import demo view""" + """Test project import demo view.""" fixtures = ['test_data', 'eric'] @@ -264,7 +265,7 @@ def test_import_demo_pass(self): self.assertEqual(messages[0].level, message_const.SUCCESS) def test_import_demo_already_imported(self): - """Import demo project multiple times, expect failure 2nd post""" + """Import demo project multiple times, expect failure 2nd post.""" self.test_import_demo_pass() project = Project.objects.get(slug='eric-demo') @@ -281,7 +282,7 @@ def test_import_demo_already_imported(self): Project.objects.get(slug='eric-demo')) def test_import_demo_another_user_imported(self): - """Import demo project after another user, expect success""" + """Import demo project after another user, expect success.""" self.test_import_demo_pass() project = Project.objects.get(slug='eric-demo') @@ -297,7 +298,7 @@ def test_import_demo_another_user_imported(self): self.assertEqual(messages[0].level, message_const.SUCCESS) def test_import_demo_imported_renamed(self): - """If the demo project is renamed, don't import another""" + """If the demo project is renamed, don't import another.""" self.test_import_demo_pass() project = Project.objects.get(slug='eric-demo') project.name = 'eric-demo-foobar' @@ -318,7 +319,8 @@ def test_import_demo_imported_renamed(self): Project.objects.get(slug='eric-demo')) def test_import_demo_imported_duplicate(self): - """If a project exists with same name, expect a failure importing demo + """ + If a project exists with same name, expect a failure importing demo. This should be edge case, user would have to import a project (not the demo project), named user-demo, and then manually enter the demo import @@ -403,7 +405,7 @@ def setUp(self): self.domain = get(Domain, project=self.project) def test_project_relation(self): - """Class using project relation mixin class""" + """Class using project relation mixin class.""" class FoobarView(ProjectRelationMixin, ContextMixin): model = Domain diff --git a/readthedocs/rtd_tests/tests/test_redirects.py b/readthedocs/rtd_tests/tests/test_redirects.py index fad6cd2f4bd..834b4656893 100644 --- a/readthedocs/rtd_tests/tests/test_redirects.py +++ b/readthedocs/rtd_tests/tests/test_redirects.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import logging from django.http import Http404 @@ -14,7 +15,7 @@ @override_settings(PUBLIC_DOMAIN='readthedocs.org', USE_SUBDOMAIN=False, APPEND_SLASH=False) class RedirectTests(TestCase): - fixtures = ["eric", "test_data"] + fixtures = ['eric', 'test_data'] def setUp(self): logging.disable(logging.DEBUG) @@ -105,7 +106,7 @@ def test_improper_subdomain_filename_only(self): @override_settings(PUBLIC_DOMAIN='readthedocs.org', USE_SUBDOMAIN=False) class RedirectAppTests(TestCase): - fixtures = ["eric", "test_data"] + fixtures = ['eric', 'test_data'] def setUp(self): self.client.login(username='eric', password='test') @@ -336,7 +337,7 @@ def test_redirect_fragment(self): @override_settings(PUBLIC_DOMAIN='readthedocs.org', USE_SUBDOMAIN=False) class RedirectBuildTests(TestCase): - fixtures = ["eric", "test_data"] + fixtures = ['eric', 'test_data'] def setUp(self): self.project = get(Project, @@ -359,10 +360,10 @@ def test_redirect_detail(self): @override_settings(PUBLIC_DOMAIN='readthedocs.org', USE_SUBDOMAIN=False) class GetFullPathTests(TestCase): - fixtures = ["eric", "test_data"] + fixtures = ['eric', 'test_data'] def setUp(self): - self.proj = Project.objects.get(slug="read-the-docs") + self.proj = Project.objects.get(slug='read-the-docs') self.redirect = get(Redirect, project=self.proj) def test_http_filenames_return_themselves(self): diff --git a/readthedocs/rtd_tests/tests/test_search_json_parsing.py b/readthedocs/rtd_tests/tests/test_search_json_parsing.py index 3836f72d1f0..d2ac34e34c9 100644 --- a/readthedocs/rtd_tests/tests/test_search_json_parsing.py +++ b/readthedocs/rtd_tests/tests/test_search_json_parsing.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import os from django.test import TestCase diff --git a/readthedocs/rtd_tests/tests/test_single_version.py b/readthedocs/rtd_tests/tests/test_single_version.py index bb850d93275..567e96b5f70 100644 --- a/readthedocs/rtd_tests/tests/test_single_version.py +++ b/readthedocs/rtd_tests/tests/test_single_version.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import django_dynamic_fixture as fixture from django.test import TestCase from django.test.utils import override_settings diff --git a/readthedocs/rtd_tests/tests/test_subprojects.py b/readthedocs/rtd_tests/tests/test_subprojects.py index 030fb1ecc6d..d6545189e2e 100644 --- a/readthedocs/rtd_tests/tests/test_subprojects.py +++ b/readthedocs/rtd_tests/tests/test_subprojects.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import django_dynamic_fixture as fixture import mock from django.contrib.auth.models import User diff --git a/readthedocs/rtd_tests/tests/test_sync_versions.py b/readthedocs/rtd_tests/tests/test_sync_versions.py index 6c41de5b8bf..d5686dcec2b 100644 --- a/readthedocs/rtd_tests/tests/test_sync_versions.py +++ b/readthedocs/rtd_tests/tests/test_sync_versions.py @@ -2,7 +2,6 @@ import json -import pytest from django.test import TestCase from django.urls import reverse @@ -447,13 +446,11 @@ def test_machine_attr_when_user_define_stable_branch_and_delete_it(self): self.assertTrue(current_stable.machine) def test_machine_attr_when_user_define_stable_branch_and_delete_it_new_project(self): - """ - The user imports a new project with a branch named ``stable``, - when syncing the versions, the RTD's ``stable`` is lost - (set to machine=False) and doesn't update automatically anymore, - when the branch is deleted on the user repository, the RTD's ``stable`` - is back (set to machine=True). - """ + """The user imports a new project with a branch named ``stable``, when + syncing the versions, the RTD's ``stable`` is lost (set to + machine=False) and doesn't update automatically anymore, when the branch + is deleted on the user repository, the RTD's ``stable`` is back (set to + machine=True).""" # There isn't a stable version yet self.pip.versions.exclude(slug='master').delete() current_stable = self.pip.get_stable_version() @@ -525,13 +522,11 @@ def test_machine_attr_when_user_define_stable_branch_and_delete_it_new_project(s self.assertTrue(current_stable.machine) def test_machine_attr_when_user_define_latest_tag_and_delete_it(self): - """ - The user creates a tag named ``latest`` on an existing repo, - when syncing the versions, the RTD's ``latest`` is lost - (set to machine=False) and doesn't update automatically anymore, - when the tag is deleted on the user repository, the RTD's ``latest`` - is back (set to machine=True). - """ + """The user creates a tag named ``latest`` on an existing repo, when + syncing the versions, the RTD's ``latest`` is lost (set to + machine=False) and doesn't update automatically anymore, when the tag is + deleted on the user repository, the RTD's ``latest`` is back (set to + machine=True).""" version_post_data = { 'branches': [ { @@ -589,13 +584,11 @@ def test_machine_attr_when_user_define_latest_tag_and_delete_it(self): self.assertTrue(version_latest.machine) def test_machine_attr_when_user_define_latest_branch_and_delete_it(self): - """ - The user creates a branch named ``latest`` on an existing repo, - when syncing the versions, the RTD's ``latest`` is lost - (set to machine=False) and doesn't update automatically anymore, - when the branch is deleted on the user repository, the RTD's ``latest`` - is back (set to machine=True). - """ + """The user creates a branch named ``latest`` on an existing repo, when + syncing the versions, the RTD's ``latest`` is lost (set to + machine=False) and doesn't update automatically anymore, when the branch + is deleted on the user repository, the RTD's ``latest`` is back (set to + machine=True).""" version_post_data = { 'branches': [ { diff --git a/readthedocs/rtd_tests/tests/test_urls.py b/readthedocs/rtd_tests/tests/test_urls.py index 4a10e149591..cfa81237e43 100644 --- a/readthedocs/rtd_tests/tests/test_urls.py +++ b/readthedocs/rtd_tests/tests/test_urls.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django.test import TestCase from django.urls import NoReverseMatch, reverse diff --git a/readthedocs/rtd_tests/tests/test_version_commit_name.py b/readthedocs/rtd_tests/tests/test_version_commit_name.py index b690ef3dbb5..2dfd4725870 100644 --- a/readthedocs/rtd_tests/tests/test_version_commit_name.py +++ b/readthedocs/rtd_tests/tests/test_version_commit_name.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django.test import TestCase from django_dynamic_fixture import get, new diff --git a/readthedocs/rtd_tests/tests/test_version_config.py b/readthedocs/rtd_tests/tests/test_version_config.py index 2c79686aa7f..f97c9c871a8 100644 --- a/readthedocs/rtd_tests/tests/test_version_config.py +++ b/readthedocs/rtd_tests/tests/test_version_config.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from django.test import TestCase from django_dynamic_fixture import get diff --git a/readthedocs/rtd_tests/tests/test_version_slug.py b/readthedocs/rtd_tests/tests/test_version_slug.py index a193156e19f..63d3ae385f8 100644 --- a/readthedocs/rtd_tests/tests/test_version_slug.py +++ b/readthedocs/rtd_tests/tests/test_version_slug.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import re from django.test import TestCase @@ -26,7 +27,7 @@ def test_multiple_words(self): class VersionSlugFieldTests(TestCase): - fixtures = ["eric", "test_data"] + fixtures = ['eric', 'test_data'] def setUp(self): self.pip = Project.objects.get(slug='pip') diff --git a/readthedocs/rtd_tests/utils.py b/readthedocs/rtd_tests/utils.py index 323dc07ad5a..95c0d99385b 100644 --- a/readthedocs/rtd_tests/utils.py +++ b/readthedocs/rtd_tests/utils.py @@ -20,9 +20,7 @@ def get_readthedocs_app_path(): - """ - Return the absolute path of the ``readthedocs`` app. - """ + """Return the absolute path of the ``readthedocs`` app.""" try: import readthedocs diff --git a/readthedocs/search/tests/conftest.py b/readthedocs/search/tests/conftest.py index 1577b6b04f9..7b21c7c2137 100644 --- a/readthedocs/search/tests/conftest.py +++ b/readthedocs/search/tests/conftest.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import random import string from random import shuffle diff --git a/readthedocs/search/tests/data/docs/story.json b/readthedocs/search/tests/data/docs/story.json index 69226b65209..10c81a97832 100644 --- a/readthedocs/search/tests/data/docs/story.json +++ b/readthedocs/search/tests/data/docs/story.json @@ -29,4 +29,4 @@ } ], "path": "open-source-philosophy" -} \ No newline at end of file +} diff --git a/readthedocs/search/tests/data/docs/wiping.json b/readthedocs/search/tests/data/docs/wiping.json index a54889e05fa..bbdbc8860a8 100644 --- a/readthedocs/search/tests/data/docs/wiping.json +++ b/readthedocs/search/tests/data/docs/wiping.json @@ -12,4 +12,4 @@ } ], "path": "guides/wipe-environment" -} \ No newline at end of file +} diff --git a/readthedocs/search/tests/data/kuma/docker.json b/readthedocs/search/tests/data/kuma/docker.json index 3f86764073a..6e16f7e9784 100644 --- a/readthedocs/search/tests/data/kuma/docker.json +++ b/readthedocs/search/tests/data/kuma/docker.json @@ -22,4 +22,4 @@ } ], "path": "docker" -} \ No newline at end of file +} diff --git a/readthedocs/search/tests/data/kuma/documentation.json b/readthedocs/search/tests/data/kuma/documentation.json index 310a01d05c8..8c7b44a42e6 100644 --- a/readthedocs/search/tests/data/kuma/documentation.json +++ b/readthedocs/search/tests/data/kuma/documentation.json @@ -18,4 +18,4 @@ } ], "path": "documentation" -} \ No newline at end of file +} diff --git a/readthedocs/search/tests/data/pipeline/installation.json b/readthedocs/search/tests/data/pipeline/installation.json index 30fb78d1d78..22bba4f08fe 100644 --- a/readthedocs/search/tests/data/pipeline/installation.json +++ b/readthedocs/search/tests/data/pipeline/installation.json @@ -30,4 +30,4 @@ } ], "path": "installation" -} \ No newline at end of file +} diff --git a/readthedocs/search/tests/data/pipeline/signals.json b/readthedocs/search/tests/data/pipeline/signals.json index 3bf3a80537c..abed6187b3b 100644 --- a/readthedocs/search/tests/data/pipeline/signals.json +++ b/readthedocs/search/tests/data/pipeline/signals.json @@ -24,4 +24,4 @@ } ], "path": "signals" -} \ No newline at end of file +} diff --git a/readthedocs/search/tests/dummy_data.py b/readthedocs/search/tests/dummy_data.py index f46449374de..2e99d4e7711 100644 --- a/readthedocs/search/tests/dummy_data.py +++ b/readthedocs/search/tests/dummy_data.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import json import os @@ -15,7 +16,7 @@ def _get_dummy_json(): data = [] for file_name in value: current_path = os.path.abspath(os.path.dirname(__file__)) - path = os.path.join(current_path, "data", key, file_name) + path = os.path.join(current_path, 'data', key, file_name) with open(path) as f: content = json.load(f) data.append(content) diff --git a/readthedocs/search/tests/test_views.py b/readthedocs/search/tests/test_views.py index d5ac18bafc9..fa740285d1e 100644 --- a/readthedocs/search/tests/test_views.py +++ b/readthedocs/search/tests/test_views.py @@ -41,7 +41,7 @@ def test_search_by_project_name(self, client, project): assert project.name.encode('utf-8') in result.text().encode('utf-8') def test_search_project_show_languages(self, client, project, es_index): - """Test that searching project should show all available languages""" + """Test that searching project should show all available languages.""" # Create a project in bn and add it as a translation G(Project, language='bn', name=project.name) self._reindex_elasticsearch(es_index=es_index) @@ -55,7 +55,7 @@ def test_search_project_show_languages(self, client, project, es_index): assert 'bn' in content.text() def test_search_project_filter_language(self, client, project, es_index): - """Test that searching project filtered according to language""" + """Test that searching project filtered according to language.""" # Create a project in bn and add it as a translation translate = G(Project, language='bn', name=project.name) self._reindex_elasticsearch(es_index=es_index) @@ -83,7 +83,8 @@ def test_search_by_file_content(self, client, project, data_type, page_num): assert len(result) == 1 def test_file_search_show_projects(self, client): - """Test that search result page shows list of projects while searching for files""" + """Test that search result page shows list of projects while searching + for files.""" # `Github` word is present both in `kuma` and `pipeline` files # so search with this phrase @@ -102,7 +103,7 @@ def test_file_search_show_projects(self, client): assert 'kuma' and 'pipeline' in text def test_file_search_filter_by_project(self, client): - """Test that search result are filtered according to project""" + """Test that search result are filtered according to project.""" # `Github` word is present both in `kuma` and `pipeline` files # so search with this phrase but filter through `kuma` project @@ -122,11 +123,11 @@ def test_file_search_filter_by_project(self, client): # as the query is present in both projects content = page.find('.navigable .project-list') if len(content) != 2: - pytest.xfail("failing because currently all projects are not showing in project list") + pytest.xfail('failing because currently all projects are not showing in project list') else: assert 'kuma' and 'pipeline' in content.text() - @pytest.mark.xfail(reason="Versions are not showing correctly! Fixme while rewrite!") + @pytest.mark.xfail(reason='Versions are not showing correctly! Fixme while rewrite!') def test_file_search_show_versions(self, client, all_projects, es_index, settings): # override the settings to index all versions settings.INDEX_ONLY_LATEST = False @@ -161,7 +162,7 @@ def test_file_search_show_versions(self, client, all_projects, es_index, setting assert sorted(project_versions) == sorted(content_versions) def test_file_search_subprojects(self, client, all_projects, es_index): - """File search should return results from subprojects also""" + """File search should return results from subprojects also.""" project = all_projects[0] subproject = all_projects[1] # Add another project as subproject of the project diff --git a/readthedocs/search/tests/utils.py b/readthedocs/search/tests/utils.py index a48ea83dd74..80bf0fadff6 100644 --- a/readthedocs/search/tests/utils.py +++ b/readthedocs/search/tests/utils.py @@ -1,9 +1,12 @@ +# -*- coding: utf-8 -*- from readthedocs.search.tests.dummy_data import DUMMY_PAGE_JSON def get_search_query_from_project_file(project_slug, page_num=0, data_type='title'): - """Return search query from the project's page file. - Query is generated from the value of `data_type` + """ + Return search query from the project's page file. + + Query is generated from the value of `data_type` """ all_pages = DUMMY_PAGE_JSON[project_slug] diff --git a/readthedocs/vcs_support/tests.py b/readthedocs/vcs_support/tests.py index 2b16090baa4..d43f8c0f401 100644 --- a/readthedocs/vcs_support/tests.py +++ b/readthedocs/vcs_support/tests.py @@ -1,9 +1,9 @@ +# -*- coding: utf-8 -*- import os import shutil import unittest import mock -from django.conf import settings from readthedocs.vcs_support import utils From 7d08eed4283ec33628b96c9041c41b45de5b10a0 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:49:11 +0100 Subject: [PATCH 36/42] Run add-trailing-comma over all the files --- readthedocs/api/utils.py | 2 +- readthedocs/builds/admin.py | 6 +- readthedocs/builds/models.py | 54 ++-- readthedocs/builds/querysets.py | 2 +- readthedocs/builds/views.py | 2 +- readthedocs/config/config.py | 22 +- readthedocs/config/tests/test_config.py | 204 ++++++++------ readthedocs/config/tests/test_parser.py | 3 +- readthedocs/core/context_processors.py | 4 +- .../core/management/commands/archive.py | 5 +- .../core/management/commands/clean_builds.py | 2 +- .../core/management/commands/import_github.py | 7 +- .../commands/import_github_language.py | 14 +- .../core/management/commands/set_metadata.py | 2 +- .../core/management/commands/symlink.py | 5 +- readthedocs/core/middleware.py | 43 +-- readthedocs/core/models.py | 2 +- readthedocs/core/resolver.py | 2 +- readthedocs/core/symlink.py | 17 +- readthedocs/core/tasks.py | 2 +- readthedocs/core/templatetags/core_tags.py | 2 +- readthedocs/core/templatetags/privacy_tags.py | 2 +- readthedocs/core/urls/single_version.py | 2 +- readthedocs/core/urls/subdomain.py | 22 +- readthedocs/core/views/hooks.py | 14 +- readthedocs/core/views/serve.py | 8 +- readthedocs/doc_builder/backends/mkdocs.py | 6 +- readthedocs/doc_builder/constants.py | 2 +- readthedocs/doc_builder/environments.py | 18 +- readthedocs/doc_builder/exceptions.py | 2 +- readthedocs/gold/forms.py | 4 +- readthedocs/gold/tests/test_forms.py | 147 +++++----- readthedocs/gold/urls.py | 4 +- readthedocs/integrations/admin.py | 6 +- readthedocs/notifications/forms.py | 6 +- .../0004_drop_github_and_bitbucket_models.py | 6 +- .../migrations/0006_move_oauth_source.py | 12 +- readthedocs/oauth/models.py | 20 +- readthedocs/oauth/services/bitbucket.py | 2 +- readthedocs/payments/forms.py | 10 +- readthedocs/projects/admin.py | 2 +- readthedocs/projects/forms.py | 72 ++--- .../migrations/0007_migrate_canonical_data.py | 4 +- .../migrations/0010_migrate_domain_data.py | 3 +- .../0021_add-webhook-deprecation-feature.py | 2 +- .../migrations/0023_migrate-alias-slug.py | 2 +- readthedocs/projects/models.py | 227 ++++++++------- readthedocs/projects/tasks.py | 41 +-- readthedocs/projects/urls/private.py | 176 ++++++------ readthedocs/projects/version_handling.py | 8 +- readthedocs/projects/views/private.py | 2 +- readthedocs/projects/views/public.py | 19 +- readthedocs/restapi/views/core_views.py | 8 +- readthedocs/restapi/views/footer_views.py | 6 +- readthedocs/restapi/views/integrations.py | 5 +- readthedocs/restapi/views/model_views.py | 14 +- readthedocs/restapi/views/search_views.py | 6 +- readthedocs/rtd_tests/base.py | 2 +- readthedocs/rtd_tests/files/conf.py | 8 +- .../fixtures/sample_repo/source/conf.py | 12 +- readthedocs/rtd_tests/mocks/environment.py | 54 ++-- .../tests/projects/test_admin_actions.py | 8 +- readthedocs/rtd_tests/tests/test_api.py | 102 ++++--- .../rtd_tests/tests/test_api_permissions.py | 12 +- readthedocs/rtd_tests/tests/test_backend.py | 20 +- .../rtd_tests/tests/test_build_config.py | 48 ++-- .../rtd_tests/tests/test_build_forms.py | 4 +- .../tests/test_build_notifications.py | 12 +- readthedocs/rtd_tests/tests/test_builds.py | 142 +++++----- readthedocs/rtd_tests/tests/test_celery.py | 54 ++-- .../tests/test_config_integration.py | 239 +++++++++------- .../rtd_tests/tests/test_core_utils.py | 30 +- .../rtd_tests/tests/test_doc_builder.py | 44 +-- .../rtd_tests/tests/test_doc_building.py | 143 ++++++---- .../rtd_tests/tests/test_doc_serving.py | 12 +- readthedocs/rtd_tests/tests/test_domains.py | 36 ++- readthedocs/rtd_tests/tests/test_extend.py | 20 +- readthedocs/rtd_tests/tests/test_gold.py | 2 +- .../rtd_tests/tests/test_integrations.py | 84 +++--- .../rtd_tests/tests/test_middleware.py | 4 +- .../rtd_tests/tests/test_notifications.py | 38 ++- readthedocs/rtd_tests/tests/test_oauth.py | 62 +++-- .../rtd_tests/tests/test_post_commit_hooks.py | 258 +++++++++++------- readthedocs/rtd_tests/tests/test_privacy.py | 117 +++++--- .../rtd_tests/tests/test_privacy_urls.py | 27 +- .../rtd_tests/tests/test_profile_views.py | 12 +- readthedocs/rtd_tests/tests/test_project.py | 77 +++--- .../rtd_tests/tests/test_project_forms.py | 44 +-- .../rtd_tests/tests/test_project_querysets.py | 4 +- .../rtd_tests/tests/test_project_symlinks.py | 244 ++++++++++------- .../rtd_tests/tests/test_project_views.py | 51 ++-- readthedocs/rtd_tests/tests/test_redirects.py | 177 +++++++----- readthedocs/rtd_tests/tests/test_resolver.py | 107 +++++--- .../rtd_tests/tests/test_restapi_client.py | 2 +- .../tests/test_search_json_parsing.py | 2 +- .../rtd_tests/tests/test_single_version.py | 26 +- .../rtd_tests/tests/test_subprojects.py | 52 ++-- .../rtd_tests/tests/test_sync_versions.py | 76 +++--- readthedocs/rtd_tests/tests/test_urls.py | 14 +- .../tests/test_version_commit_name.py | 54 ++-- .../rtd_tests/tests/test_version_config.py | 8 +- .../rtd_tests/tests/test_version_slug.py | 30 +- readthedocs/rtd_tests/tests/test_views.py | 14 +- readthedocs/rtd_tests/utils.py | 16 +- readthedocs/search/indexes.py | 10 +- readthedocs/search/lib.py | 16 +- readthedocs/search/parse_json.py | 6 +- readthedocs/search/tests/test_views.py | 56 ++-- readthedocs/search/utils.py | 8 +- readthedocs/urls.py | 4 +- readthedocs/vcs_support/backends/bzr.py | 2 +- readthedocs/vcs_support/backends/git.py | 8 +- readthedocs/vcs_support/backends/hg.py | 4 +- readthedocs/vcs_support/tests.py | 36 ++- readthedocs/vcs_support/utils.py | 4 +- 115 files changed, 2397 insertions(+), 1653 deletions(-) diff --git a/readthedocs/api/utils.py b/readthedocs/api/utils.py index ab820d21d0b..c5ecd60cd1e 100644 --- a/readthedocs/api/utils.py +++ b/readthedocs/api/utils.py @@ -48,7 +48,7 @@ def obj_get_list(self, request=None, *_, **kwargs): # noqa ugettext( 'Invalid resource lookup data provided ' '(mismatched type).: %(error)s', - ) % {'error': e} + ) % {'error': e}, ) diff --git a/readthedocs/builds/admin.py b/readthedocs/builds/admin.py index b4bc390b980..fbd83d8e3bf 100644 --- a/readthedocs/builds/admin.py +++ b/readthedocs/builds/admin.py @@ -16,10 +16,10 @@ class BuildCommandResultInline(admin.TabularInline): class BuildAdmin(admin.ModelAdmin): fields = ( 'project', 'version', 'type', 'state', 'error', 'success', 'length', - 'cold_storage' + 'cold_storage', ) list_display = ( - 'id', 'project', 'version_name', 'success', 'type', 'state', 'date' + 'id', 'project', 'version_name', 'success', 'type', 'state', 'date', ) list_filter = ('type', 'state', 'success') list_select_related = ('project', 'version') @@ -34,7 +34,7 @@ def version_name(self, obj): class VersionAdmin(GuardedModelAdmin): search_fields = ('slug', 'project__name') list_display = ( - 'slug', 'type', 'project', 'privacy_level', 'active', 'built' + 'slug', 'type', 'project', 'privacy_level', 'active', 'built', ) list_filter = ('type', 'privacy_level', 'active', 'built') raw_id_fields = ('project',) diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index e4fcad03753..2f8eeae8bad 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -50,7 +50,7 @@ DEFAULT_VERSION_PRIVACY_LEVEL = getattr( - settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public' + settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public', ) log = logging.getLogger(__name__) @@ -91,7 +91,7 @@ class Version(models.Model): #: filesystem to determine how the paths for this version are called. It #: must not be used for any other identifying purposes. slug = VersionSlugField( - _('Slug'), max_length=255, populate_from='verbose_name' + _('Slug'), max_length=255, populate_from='verbose_name', ) supported = models.BooleanField(_('Supported'), default=True) @@ -125,7 +125,7 @@ def __str__(self): version=self.verbose_name, project=self.project, pk=self.pk, - ) + ), ) @property @@ -137,8 +137,10 @@ def config(self): :rtype: dict """ last_build = ( - self.builds.filter(state='finished', - success=True).order_by('-date').first() + self.builds.filter( + state='finished', + success=True, + ).order_by('-date').first() ) return last_build.config @@ -182,7 +184,7 @@ def commit_name(self): # If we came that far it's not a special version nor a branch or tag. # Therefore just return the identifier to make a safe guess. log.debug( - 'TODO: Raise an exception here. Testing what cases it happens' + 'TODO: Raise an exception here. Testing what cases it happens', ) return self.identifier @@ -197,7 +199,7 @@ def get_absolute_url(self): ) private = self.privacy_level == PRIVATE return self.project.get_docs_url( - version_slug=self.slug, private=private + version_slug=self.slug, private=private, ) def save(self, *args, **kwargs): # pylint: disable=arguments-differ @@ -207,7 +209,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ for owner in self.project.users.all(): assign('view_version', owner, self) broadcast( - type='app', task=tasks.symlink_project, args=[self.project.pk] + type='app', task=tasks.symlink_project, args=[self.project.pk], ) return obj @@ -254,22 +256,22 @@ def get_downloads(self, pretty=False): data['PDF'] = project.get_production_media_url('pdf', self.slug) if project.has_htmlzip(self.slug): data['HTML'] = project.get_production_media_url( - 'htmlzip', self.slug + 'htmlzip', self.slug, ) if project.has_epub(self.slug): data['Epub'] = project.get_production_media_url( - 'epub', self.slug + 'epub', self.slug, ) else: if project.has_pdf(self.slug): data['pdf'] = project.get_production_media_url('pdf', self.slug) if project.has_htmlzip(self.slug): data['htmlzip'] = project.get_production_media_url( - 'htmlzip', self.slug + 'htmlzip', self.slug, ) if project.has_epub(self.slug): data['epub'] = project.get_production_media_url( - 'epub', self.slug + 'epub', self.slug, ) return data @@ -319,7 +321,7 @@ def clean_build_path(self): log.exception('Build path cleanup failed') def get_github_url( - self, docroot, filename, source_suffix='.rst', action='view' + self, docroot, filename, source_suffix='.rst', action='view', ): """ Return a GitHub URL for a given filename. @@ -362,7 +364,7 @@ def get_github_url( ) def get_gitlab_url( - self, docroot, filename, source_suffix='.rst', action='view' + self, docroot, filename, source_suffix='.rst', action='view', ): repo_url = self.project.repo if 'gitlab' not in repo_url: @@ -460,16 +462,16 @@ class Build(models.Model): """Build data.""" project = models.ForeignKey( - Project, verbose_name=_('Project'), related_name='builds' + Project, verbose_name=_('Project'), related_name='builds', ) version = models.ForeignKey( - Version, verbose_name=_('Version'), null=True, related_name='builds' + Version, verbose_name=_('Version'), null=True, related_name='builds', ) type = models.CharField( - _('Type'), max_length=55, choices=BUILD_TYPES, default='html' + _('Type'), max_length=55, choices=BUILD_TYPES, default='html', ) state = models.CharField( - _('State'), max_length=55, choices=BUILD_STATE, default='finished' + _('State'), max_length=55, choices=BUILD_STATE, default='finished', ) date = models.DateTimeField(_('Date'), auto_now_add=True) success = models.BooleanField(_('Success'), default=True) @@ -480,18 +482,18 @@ class Build(models.Model): error = models.TextField(_('Error'), default='', blank=True) exit_code = models.IntegerField(_('Exit code'), null=True, blank=True) commit = models.CharField( - _('Commit'), max_length=255, null=True, blank=True + _('Commit'), max_length=255, null=True, blank=True, ) _config = JSONField(_('Configuration used in the build'), default=dict) length = models.IntegerField(_('Build Length'), null=True, blank=True) builder = models.CharField( - _('Builder'), max_length=255, null=True, blank=True + _('Builder'), max_length=255, null=True, blank=True, ) cold_storage = models.NullBooleanField( - _('Cold Storage'), help_text='Build steps stored outside the database.' + _('Cold Storage'), help_text='Build steps stored outside the database.', ) # Manager @@ -563,8 +565,10 @@ def save(self, *args, **kwargs): # noqa """ if self.pk is None or self._config_changed: previous = self.previous - if (previous is not None and self._config and - self._config == previous.config): + if ( + previous is not None and self._config and + self._config == previous.config + ): previous_pk = previous._config.get(self.CONFIG_KEY, previous.pk) self._config = {self.CONFIG_KEY: previous_pk} super().save(*args, **kwargs) @@ -578,7 +582,7 @@ def __str__(self): self.project.users.all().values_list('username', flat=True), ), pk=self.pk, - ) + ), ) def get_absolute_url(self): @@ -620,7 +624,7 @@ class BuildCommandResult(BuildCommandResultMixin, models.Model): """Build command for a ``Build``.""" build = models.ForeignKey( - Build, verbose_name=_('Build'), related_name='commands' + Build, verbose_name=_('Build'), related_name='commands', ) command = models.TextField(_('Command')) diff --git a/readthedocs/builds/querysets.py b/readthedocs/builds/querysets.py index 2039aa0ec6e..ae7e5a8fb79 100644 --- a/readthedocs/builds/querysets.py +++ b/readthedocs/builds/querysets.py @@ -38,7 +38,7 @@ def public(self, user=None, project=None, only_active=True): def protected(self, user=None, project=None, only_active=True): queryset = self.filter( - privacy_level__in=[constants.PUBLIC, constants.PROTECTED] + privacy_level__in=[constants.PUBLIC, constants.PROTECTED], ) if user: queryset = self._add_user_repos(queryset, user) diff --git a/readthedocs/builds/views.py b/readthedocs/builds/views.py index 69ed90dadd8..00f08ab2d47 100644 --- a/readthedocs/builds/views.py +++ b/readthedocs/builds/views.py @@ -59,7 +59,7 @@ def post(self, request, project_slug): ) update_docs_task, build = trigger_build( - project=project, version=version + project=project, version=version, ) if (update_docs_task, build) == (None, None): # Build was skipped diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index a3a6e7877a0..50eba8b3ed9 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -90,7 +90,7 @@ def __init__(self, configuration): ) super().__init__( template.format(self.configuration), - CONFIG_NOT_SUPPORTED + CONFIG_NOT_SUPPORTED, ) @@ -342,7 +342,7 @@ def validate_build(self): if ':' not in build['image']: # Prepend proper image name to user's image name build['image'] = '{}:{}'.format( - DOCKER_DEFAULT_IMAGE, build['image'] + DOCKER_DEFAULT_IMAGE, build['image'], ) # Update docker default settings from image name if build['image'] in DOCKER_IMAGE_SETTINGS: @@ -378,14 +378,14 @@ def validate_python(self): if 'use_system_site_packages' in raw_python: with self.catch_validation_error('python.use_system_site_packages'): python['use_system_site_packages'] = validate_bool( - raw_python['use_system_site_packages'] + raw_python['use_system_site_packages'], ) # Validate pip_install. if 'pip_install' in raw_python: with self.catch_validation_error('python.pip_install'): python['install_with_pip'] = validate_bool( - raw_python['pip_install'] + raw_python['pip_install'], ) # Validate extra_requirements. @@ -395,7 +395,7 @@ def validate_python(self): self.error( 'python.extra_requirements', self.PYTHON_EXTRA_REQUIREMENTS_INVALID_MESSAGE, - code=PYTHON_INVALID + code=PYTHON_INVALID, ) if not python['install_with_pip']: python['extra_requirements'] = [] @@ -403,14 +403,14 @@ def validate_python(self): for extra_name in raw_extra_requirements: with self.catch_validation_error('python.extra_requirements'): python['extra_requirements'].append( - validate_string(extra_name) + validate_string(extra_name), ) # Validate setup_py_install. if 'setup_py_install' in raw_python: with self.catch_validation_error('python.setup_py_install'): python['install_with_setup'] = validate_bool( - raw_python['setup_py_install'] + raw_python['setup_py_install'], ) if 'version' in raw_python: @@ -445,7 +445,7 @@ def validate_conda(self): if 'file' in raw_conda: with self.catch_validation_error('conda.file'): conda_environment = validate_file( - raw_conda['file'], self.base_path + raw_conda['file'], self.base_path, ) conda['environment'] = conda_environment @@ -683,7 +683,7 @@ def validate_python(self): with self.catch_validation_error('python.extra_requirements'): extra_requirements = self.pop_config( - 'python.extra_requirements', [] + 'python.extra_requirements', [], ) extra_requirements = validate_list(extra_requirements) if extra_requirements and not python['install_with_pip']: @@ -801,7 +801,7 @@ def validate_sphinx(self): if not configuration: configuration = None configuration = self.pop_config( - 'sphinx.configuration', configuration + 'sphinx.configuration', configuration, ) if configuration is not None: configuration = validate_file(configuration, self.base_path) @@ -828,7 +828,7 @@ def validate_final_doc_type(self): if dashboard_doctype == 'mkdocs' or not self.sphinx: error_msg += ' but there is no "{}" key specified.'.format( - 'mkdocs' if dashboard_doctype == 'mkdocs' else 'sphinx' + 'mkdocs' if dashboard_doctype == 'mkdocs' else 'sphinx', ) else: error_msg += ' but your "sphinx.builder" key does not match.' diff --git a/readthedocs/config/tests/test_config.py b/readthedocs/config/tests/test_config.py index 29fbac1be6f..98e579b1ceb 100644 --- a/readthedocs/config/tests/test_config.py +++ b/readthedocs/config/tests/test_config.py @@ -55,13 +55,15 @@ def get_build_config(config, env_config=None, source_file='readthedocs.yml'): ) -@pytest.mark.parametrize('files', [ - {'readthedocs.ymlmore': ''}, {'first': {'readthedocs.yml': ''}}, - {'startreadthedocs.yml': ''}, {'second': {'confuser.txt': 'content'}}, - {'noroot': {'readthedocs.ymlmore': ''}}, {'third': {'readthedocs.yml': 'content', 'Makefile': ''}}, - {'noroot': {'startreadthedocs.yml': ''}}, {'fourth': {'samplefile.yaml': 'content'}}, - {'readthebots.yaml': ''}, {'fifth': {'confuser.txt': '', 'readthedocs.yml': 'content'}}, -]) +@pytest.mark.parametrize( + 'files', [ + {'readthedocs.ymlmore': ''}, {'first': {'readthedocs.yml': ''}}, + {'startreadthedocs.yml': ''}, {'second': {'confuser.txt': 'content'}}, + {'noroot': {'readthedocs.ymlmore': ''}}, {'third': {'readthedocs.yml': 'content', 'Makefile': ''}}, + {'noroot': {'startreadthedocs.yml': ''}}, {'fourth': {'samplefile.yaml': 'content'}}, + {'readthebots.yaml': ''}, {'fifth': {'confuser.txt': '', 'readthedocs.yml': 'content'}}, + ], +) def test_load_no_config_file(tmpdir, files): apply_fs(tmpdir, files) base = str(tmpdir) @@ -71,9 +73,11 @@ def test_load_no_config_file(tmpdir, files): def test_load_empty_config_file(tmpdir): - apply_fs(tmpdir, { - 'readthedocs.yml': '' - }) + apply_fs( + tmpdir, { + 'readthedocs.yml': '', + }, + ) base = str(tmpdir) with raises(ConfigError): load(base, {}) @@ -87,33 +91,39 @@ def test_minimal_config(tmpdir): def test_load_version1(tmpdir): - apply_fs(tmpdir, { - 'readthedocs.yml': textwrap.dedent(''' + apply_fs( + tmpdir, { + 'readthedocs.yml': textwrap.dedent(''' version: 1 - ''') - }) + '''), + }, + ) base = str(tmpdir) build = load(base, {'allow_v2': True}) assert isinstance(build, BuildConfigV1) def test_load_version2(tmpdir): - apply_fs(tmpdir, { - 'readthedocs.yml': textwrap.dedent(''' + apply_fs( + tmpdir, { + 'readthedocs.yml': textwrap.dedent(''' version: 2 - ''') - }) + '''), + }, + ) base = str(tmpdir) build = load(base, {'allow_v2': True}) assert isinstance(build, BuildConfigV2) def test_load_unknow_version(tmpdir): - apply_fs(tmpdir, { - 'readthedocs.yml': textwrap.dedent(''' + apply_fs( + tmpdir, { + 'readthedocs.yml': textwrap.dedent(''' version: 9 - ''') - }) + '''), + }, + ) base = str(tmpdir) with raises(ConfigError) as excinfo: load(base, {'allow_v2': True}) @@ -122,14 +132,16 @@ def test_load_unknow_version(tmpdir): def test_yaml_extension(tmpdir): """Make sure loading the 'readthedocs' file with a 'yaml' extension.""" - apply_fs(tmpdir, { - 'readthedocs.yaml': textwrap.dedent( - ''' + apply_fs( + tmpdir, { + 'readthedocs.yaml': textwrap.dedent( + ''' python: version: 3 ''' - ), - }) + ), + }, + ) base = str(tmpdir) config = load(base, {}) assert isinstance(config, BuildConfigV1) @@ -142,13 +154,15 @@ def test_build_config_has_source_file(tmpdir): def test_build_config_has_list_with_single_empty_value(tmpdir): - base = str(apply_fs(tmpdir, { - 'readthedocs.yml': textwrap.dedent( - ''' + base = str(apply_fs( + tmpdir, { + 'readthedocs.yml': textwrap.dedent( + ''' formats: [] ''' - ) - })) + ), + }, + )) build = load(base, {}) assert isinstance(build, BuildConfigV1) assert build.formats == [] @@ -166,7 +180,7 @@ def test_doc_type(): 'defaults': { 'doctype': 'sphinx', }, - } + }, ) build.validate() assert build.doctype == 'sphinx' @@ -522,7 +536,8 @@ def test_default(self, tmpdir): assert build.build.image == 'readthedocs/build:2.0' @pytest.mark.parametrize( - 'image', ['latest', 'readthedocs/build:3.0', 'rtd/build:latest']) + 'image', ['latest', 'readthedocs/build:3.0', 'rtd/build:latest'], + ) def test_it_priorities_image_from_env_config(self, tmpdir, image): apply_fs(tmpdir, yaml_config_dir) defaults = { @@ -642,9 +657,11 @@ def test_raise_config_not_supported(): assert excinfo.value.code == CONFIG_NOT_SUPPORTED -@pytest.mark.parametrize('correct_config_filename', - [prefix + 'readthedocs.' + extension for prefix in {'', '.'} - for extension in {'yml', 'yaml'}]) +@pytest.mark.parametrize( + 'correct_config_filename', + [prefix + 'readthedocs.' + extension for prefix in {'', '.'} + for extension in {'yml', 'yaml'}], +) def test_config_filenames_regex(correct_config_filename): assert re.match(CONFIG_FILENAME_REGEX, correct_config_filename) @@ -706,7 +723,8 @@ def test_as_dict(tmpdir): class TestBuildConfigV2: def get_build_config( - self, config, env_config=None, source_file='readthedocs.yml'): + self, config, env_config=None, source_file='readthedocs.yml', + ): return BuildConfigV2( env_config or {}, config, @@ -739,7 +757,7 @@ def test_formats_check_invalid_value(self, value): def test_formats_check_invalid_type(self): build = self.get_build_config( - {'formats': ['htmlzip', 'invalid', 'epub']} + {'formats': ['htmlzip', 'invalid', 'epub']}, ) with raises(InvalidConfig) as excinfo: build.validate() @@ -836,7 +854,8 @@ def test_build_image_check_invalid(self, value): assert excinfo.value.key == 'build.image' @pytest.mark.parametrize( - 'image', ['latest', 'readthedocs/build:3.0', 'rtd/build:latest']) + 'image', ['latest', 'readthedocs/build:3.0', 'rtd/build:latest'], + ) def test_build_image_priorities_default(self, image): build = self.get_build_config( {'build': {'image': 'latest'}}, @@ -880,9 +899,13 @@ def test_python_check_invalid_types(self, value): build.validate() assert excinfo.value.key == 'python' - @pytest.mark.parametrize('image,versions', - [('latest', [2, 2.7, 3, 3.5, 3.6]), - ('stable', [2, 2.7, 3, 3.5, 3.6])]) + @pytest.mark.parametrize( + 'image,versions', + [ + ('latest', [2, 2.7, 3, 3.5, 3.6]), + ('stable', [2, 2.7, 3, 3.5, 3.6]), + ], + ) def test_python_version(self, image, versions): for version in versions: build = self.get_build_config({ @@ -908,9 +931,13 @@ def test_python_version_accepts_string(self): build.validate() assert build.python.version == 3.6 - @pytest.mark.parametrize('image,versions', - [('latest', [1, 2.8, 4, 3.8]), - ('stable', [1, 2.8, 4, 3.8])]) + @pytest.mark.parametrize( + 'image,versions', + [ + ('latest', [1, 2.8, 4, 3.8]), + ('stable', [1, 2.8, 4, 3.8]), + ], + ) def test_python_version_invalid(self, image, versions): for version in versions: build = self.get_build_config({ @@ -1086,7 +1113,7 @@ def test_python_extra_requirements_and_pip(self): 'python': { 'install': 'pip', 'extra_requirements': ['docs', 'tests'], - } + }, }) build.validate() assert build.python.extra_requirements == ['docs', 'tests'] @@ -1095,7 +1122,7 @@ def test_python_extra_requirements_not_install(self): build = self.get_build_config({ 'python': { 'extra_requirements': ['docs', 'tests'], - } + }, }) with raises(InvalidConfig) as excinfo: build.validate() @@ -1106,7 +1133,7 @@ def test_python_extra_requirements_and_setup(self): 'python': { 'install': 'setup.py', 'extra_requirements': ['docs', 'tests'], - } + }, }) with raises(InvalidConfig) as excinfo: build.validate() @@ -1202,10 +1229,14 @@ def test_sphinx_is_default_doc_type(self): assert build.mkdocs is None assert build.doctype == 'sphinx' - @pytest.mark.parametrize('value,expected', - [('html', 'sphinx'), - ('htmldir', 'sphinx_htmldir'), - ('singlehtml', 'sphinx_singlehtml')]) + @pytest.mark.parametrize( + 'value,expected', + [ + ('html', 'sphinx'), + ('htmldir', 'sphinx_htmldir'), + ('singlehtml', 'sphinx_singlehtml'), + ], + ) def test_sphinx_builder_check_valid(self, value, expected): build = self.get_build_config( {'sphinx': {'builder': value}}, @@ -1228,7 +1259,8 @@ def test_sphinx_builder_default(self): build.sphinx.builder == 'sphinx' @pytest.mark.skip( - 'This test is not compatible with the new validation around doctype.') + 'This test is not compatible with the new validation around doctype.', + ) def test_sphinx_builder_ignores_default(self): build = self.get_build_config( {}, @@ -1460,7 +1492,7 @@ def test_submodules_check_invalid_type(self, value): def test_submodules_include_check_valid(self): build = self.get_build_config({ 'submodules': { - 'include': ['one', 'two'] + 'include': ['one', 'two'], }, }) build.validate() @@ -1493,8 +1525,8 @@ def test_submodules_include_allows_all_keyword(self): def test_submodules_exclude_check_valid(self): build = self.get_build_config({ 'submodules': { - 'exclude': ['one', 'two'] - } + 'exclude': ['one', 'two'], + }, }) build.validate() assert build.submodules.include == [] @@ -1594,26 +1626,28 @@ def test_submodules_recursive_explict_default(self): assert build.submodules.exclude == [] assert build.submodules.recursive is False - @pytest.mark.parametrize('value,key', [ - ({'typo': 'something'}, 'typo'), - ( - { - 'pyton': { - 'version': 'another typo', - } - }, - 'pyton.version' - ), - ( - { - 'build': { - 'image': 'latest', - 'extra': 'key', - } - }, - 'build.extra' - ) - ]) + @pytest.mark.parametrize( + 'value,key', [ + ({'typo': 'something'}, 'typo'), + ( + { + 'pyton': { + 'version': 'another typo', + }, + }, + 'pyton.version', + ), + ( + { + 'build': { + 'image': 'latest', + 'extra': 'key', + }, + }, + 'build.extra', + ), + ], + ) def test_strict_validation(self, value, key): build = self.get_build_config(value) with raises(InvalidConfig) as excinfo: @@ -1631,13 +1665,15 @@ def test_strict_validation_pops_all_keys(self): build.validate() assert build.raw_config == {} - @pytest.mark.parametrize('value,expected', [ - ({}, []), - ({'one': 1}, ['one']), - ({'one': {'two': 3}}, ['one', 'two']), - (OrderedDict([('one', 1), ('two', 2)]), ['one']), - (OrderedDict([('one', {'two': 2}), ('three', 3)]), ['one', 'two']), - ]) + @pytest.mark.parametrize( + 'value,expected', [ + ({}, []), + ({'one': 1}, ['one']), + ({'one': {'two': 3}}, ['one', 'two']), + (OrderedDict([('one', 1), ('two', 2)]), ['one']), + (OrderedDict([('one', {'two': 2}), ('three', 3)]), ['one', 'two']), + ], + ) def test_get_extra_key(self, value, expected): build = self.get_build_config({}) assert build._get_extra_key(value) == expected diff --git a/readthedocs/config/tests/test_parser.py b/readthedocs/config/tests/test_parser.py index 9062f32ca5e..64a7e1f7292 100644 --- a/readthedocs/config/tests/test_parser.py +++ b/readthedocs/config/tests/test_parser.py @@ -64,6 +64,7 @@ def test_do_not_parse_multiple_configs_in_one_file(): name: second nested: works: true - ''') + ''' + ) with raises(ParseError): parse(buf) diff --git a/readthedocs/core/context_processors.py b/readthedocs/core/context_processors.py index 536c1fe46e5..467110e9842 100644 --- a/readthedocs/core/context_processors.py +++ b/readthedocs/core/context_processors.py @@ -13,12 +13,12 @@ def readthedocs_processor(request): 'USE_SUBDOMAINS': getattr(settings, 'USE_SUBDOMAINS', None), 'GLOBAL_ANALYTICS_CODE': getattr(settings, 'GLOBAL_ANALYTICS_CODE'), 'DASHBOARD_ANALYTICS_CODE': getattr( - settings, 'DASHBOARD_ANALYTICS_CODE' + settings, 'DASHBOARD_ANALYTICS_CODE', ), 'SITE_ROOT': getattr(settings, 'SITE_ROOT', '') + '/', 'TEMPLATE_ROOT': getattr(settings, 'TEMPLATE_ROOT', '') + '/', 'DO_NOT_TRACK_ENABLED': getattr( - settings, 'DO_NOT_TRACK_ENABLED', False + settings, 'DO_NOT_TRACK_ENABLED', False, ), 'USE_PROMOS': getattr(settings, 'USE_PROMOS', False), } diff --git a/readthedocs/core/management/commands/archive.py b/readthedocs/core/management/commands/archive.py index 14e87f7df7d..037b7319883 100644 --- a/readthedocs/core/management/commands/archive.py +++ b/readthedocs/core/management/commands/archive.py @@ -33,6 +33,7 @@ def handle(self, *args, **options): 'doc_index': doc_index, 'MEDIA_URL': settings.MEDIA_URL, } - html = template_loader.get_template('archive/index.html' - ).render(context) + html = template_loader.get_template( + 'archive/index.html', + ).render(context) print(html) diff --git a/readthedocs/core/management/commands/clean_builds.py b/readthedocs/core/management/commands/clean_builds.py index e6f03282490..d46b8cd1089 100644 --- a/readthedocs/core/management/commands/clean_builds.py +++ b/readthedocs/core/management/commands/clean_builds.py @@ -40,7 +40,7 @@ def handle(self, *args, **options): max_date = timezone.now() - timedelta(days=options['days']) queryset = ( Build.objects.values('project', 'version').annotate( - max_date=Max('date') + max_date=Max('date'), ).filter(max_date__lt=max_date).order_by('-max_date') ) for build in queryset: diff --git a/readthedocs/core/management/commands/import_github.py b/readthedocs/core/management/commands/import_github.py index 6c8cb5b5c96..90ce69e9e6a 100644 --- a/readthedocs/core/management/commands/import_github.py +++ b/readthedocs/core/management/commands/import_github.py @@ -15,6 +15,9 @@ class Command(BaseCommand): def handle(self, *args, **options): if args: for slug in args: - for service in GitHubService.for_user(User.objects.get(username=slug - ),): + for service in GitHubService.for_user( + User.objects.get( + username=slug, + ), + ): service.sync() diff --git a/readthedocs/core/management/commands/import_github_language.py b/readthedocs/core/management/commands/import_github_language.py index f0da49cdd07..f3813f21e63 100644 --- a/readthedocs/core/management/commands/import_github_language.py +++ b/readthedocs/core/management/commands/import_github_language.py @@ -37,8 +37,11 @@ def handle(self, *args, **options): print('Invalid GitHub token, exiting') return - for project in Project.objects.filter(programming_language__in=[ - 'none', '', 'words'],).filter(repo__contains='github',): + for project in Project.objects.filter( + programming_language__in=[ + 'none', '', 'words', + ], + ).filter(repo__contains='github',): user = repo = '' repo_url = project.repo for regex in GITHUB_REGEXS: @@ -65,7 +68,7 @@ def handle(self, *args, **options): if not languages: continue sorted_langs = sorted( - list(languages.items()), key=lambda x: x[1], reverse=True + list(languages.items()), key=lambda x: x[1], reverse=True, ) print('Sorted langs: %s ' % sorted_langs) top_lang = sorted_langs[0][0] @@ -74,8 +77,9 @@ def handle(self, *args, **options): if top_lang in PL_DICT: slug = PL_DICT[top_lang] print('Setting {} to {}'.format(repo_url, slug)) - Project.objects.filter(pk=project.pk - ).update(programming_language=slug) + Project.objects.filter( + pk=project.pk, + ).update(programming_language=slug) else: print('Language unknown: %s' % top_lang) cache.set(cache_key, top_lang, 60 * 600) diff --git a/readthedocs/core/management/commands/set_metadata.py b/readthedocs/core/management/commands/set_metadata.py index 33736b7a765..6fe4bb22e9e 100644 --- a/readthedocs/core/management/commands/set_metadata.py +++ b/readthedocs/core/management/commands/set_metadata.py @@ -24,7 +24,7 @@ def handle(self, *args, **options): log.info('Generating metadata for %s', p) try: broadcast( - type='app', task=tasks.update_static_metadata, args=[p.pk] + type='app', task=tasks.update_static_metadata, args=[p.pk], ) except Exception: log.exception('Build failed for %s', p) diff --git a/readthedocs/core/management/commands/symlink.py b/readthedocs/core/management/commands/symlink.py index 41719354c7c..2d843b9baab 100644 --- a/readthedocs/core/management/commands/symlink.py +++ b/readthedocs/core/management/commands/symlink.py @@ -25,8 +25,9 @@ def handle(self, *args, **options): if 'all' in projects: pks = Project.objects.values_list('pk', flat=True) else: - pks = Project.objects.filter(slug__in=projects - ).values_list('pk', flat=True) + pks = Project.objects.filter( + slug__in=projects, + ).values_list('pk', flat=True) for proj in pks: try: tasks.symlink_project(project_pk=proj) diff --git a/readthedocs/core/middleware.py b/readthedocs/core/middleware.py index c40dbfd0f41..6fc5bbe55c8 100644 --- a/readthedocs/core/middleware.py +++ b/readthedocs/core/middleware.py @@ -68,7 +68,8 @@ def process_request(self, request): subdomain = domain_parts[0] is_www = subdomain.lower() == 'www' if not is_www and ( # Support ports during local dev - public_domain in host or public_domain in full_host): + public_domain in host or public_domain in full_host + ): if not Project.objects.filter(slug=subdomain).exists(): raise Http404(_('Project not found')) request.subdomain = True @@ -77,8 +78,10 @@ def process_request(self, request): return None # Serve CNAMEs - if (public_domain not in host and production_domain not in host and - 'localhost' not in host and 'testserver' not in host): + if ( + public_domain not in host and production_domain not in host and + 'localhost' not in host and 'testserver' not in host + ): request.cname = True domains = Domain.objects.filter(domain=host) if domains.count(): @@ -91,11 +94,13 @@ def process_request(self, request): LOG_TEMPLATE.format( msg='Domain Object Detected: %s' % domain.domain, **log_kwargs - ) + ), ) break - if (not hasattr(request, 'domain_object') and - 'HTTP_X_RTD_SLUG' in request.META): + if ( + not hasattr(request, 'domain_object') and + 'HTTP_X_RTD_SLUG' in request.META + ): request.slug = request.META['HTTP_X_RTD_SLUG'].lower() request.urlconf = SUBDOMAIN_URLCONF request.rtdheader = True @@ -103,7 +108,7 @@ def process_request(self, request): LOG_TEMPLATE.format( msg='X-RTD-Slug header detected: %s' % request.slug, **log_kwargs - ) + ), ) # Try header first, then DNS elif not hasattr(request, 'domain_object'): @@ -117,7 +122,7 @@ def process_request(self, request): LOG_TEMPLATE.format( msg='CNAME cached: {}->{}'.format(slug, host), **log_kwargs - ) + ), ) request.slug = slug request.urlconf = SUBDOMAIN_URLCONF @@ -125,12 +130,12 @@ def process_request(self, request): LOG_TEMPLATE.format( msg='CNAME detected: %s' % request.slug, **log_kwargs - ) + ), ) except: # noqa # Some crazy person is CNAMEing to us. 404. log.warning( - LOG_TEMPLATE.format(msg='CNAME 404', **log_kwargs) + LOG_TEMPLATE.format(msg='CNAME 404', **log_kwargs), ) raise Http404(_('Invalid hostname')) # Google was finding crazy www.blah.readthedocs.org domains. @@ -139,12 +144,12 @@ def process_request(self, request): # Stop www.fooo.readthedocs.org if domain_parts[0] == 'www': log.debug( - LOG_TEMPLATE.format(msg='404ing long domain', **log_kwargs) + LOG_TEMPLATE.format(msg='404ing long domain', **log_kwargs), ) return HttpResponseBadRequest(_('Invalid hostname')) log.debug( LOG_TEMPLATE - .format(msg='Allowing long domain name', **log_kwargs) + .format(msg='Allowing long domain name', **log_kwargs), ) # raise Http404(_('Invalid hostname')) # Normal request. @@ -248,13 +253,15 @@ class FooterNoSessionMiddleware(SessionMiddleware): """ IGNORE_URLS = [ - '/api/v2/footer_html', '/sustainability/view', '/sustainability/click' + '/api/v2/footer_html', '/sustainability/view', '/sustainability/click', ] def process_request(self, request): for url in self.IGNORE_URLS: - if (request.path_info.startswith(url) and - settings.SESSION_COOKIE_NAME not in request.COOKIES): + if ( + request.path_info.startswith(url) and + settings.SESSION_COOKIE_NAME not in request.COOKIES + ): # Hack request.session otherwise the Authentication middleware complains. request.session = {} return @@ -262,7 +269,9 @@ def process_request(self, request): def process_response(self, request, response): for url in self.IGNORE_URLS: - if (request.path_info.startswith(url) and - settings.SESSION_COOKIE_NAME not in request.COOKIES): + if ( + request.path_info.startswith(url) and + settings.SESSION_COOKIE_NAME not in request.COOKIES + ): return response return super().process_response(request, response) diff --git a/readthedocs/core/models.py b/readthedocs/core/models.py index 63ac39d102d..91313ff2a89 100644 --- a/readthedocs/core/models.py +++ b/readthedocs/core/models.py @@ -48,7 +48,7 @@ def __str__(self): def get_absolute_url(self): return reverse( - 'profiles_profile_detail', kwargs={'username': self.user.username} + 'profiles_profile_detail', kwargs={'username': self.user.username}, ) def get_contribution_details(self): diff --git a/readthedocs/core/resolver.py b/readthedocs/core/resolver.py index 389fedc9843..f66910a7254 100644 --- a/readthedocs/core/resolver.py +++ b/readthedocs/core/resolver.py @@ -246,7 +246,7 @@ def _get_private(self, project, version_slug): private = version.privacy_level == PRIVATE except Version.DoesNotExist: private = getattr( - settings, 'DEFAULT_PRIVACY_LEVEL', PUBLIC + settings, 'DEFAULT_PRIVACY_LEVEL', PUBLIC, ) == PRIVATE return private diff --git a/readthedocs/core/symlink.py b/readthedocs/core/symlink.py index 3de4c2b3742..67e0cd47a81 100644 --- a/readthedocs/core/symlink.py +++ b/readthedocs/core/symlink.py @@ -101,7 +101,7 @@ def sanity_check(self): project=self.project.slug, version='', msg='Removing single version symlink', - ) + ), ) safe_unlink(self.project_root) safe_makedirs(self.project_root) @@ -190,7 +190,7 @@ def remove_symlink_cname(self, domain): project=self.project.slug, version='', msg=log_msg, - ) + ), ) symlink = os.path.join(self.CNAME_ROOT, domain.domain) safe_unlink(symlink) @@ -217,14 +217,14 @@ def symlink_subprojects(self): subprojects.add(rel.alias) for from_slug, to_slug in list(from_to.items()): log_msg = 'Symlinking subproject: {} -> {}'.format( - from_slug, to_slug + from_slug, to_slug, ) log.info( constants.LOG_TEMPLATE.format( project=self.project.slug, version='', msg=log_msg, - ) + ), ) symlink = os.path.join(self.subproject_root, from_slug) docs_dir = os.path.join( @@ -378,13 +378,14 @@ class PublicSymlinkBase(Symlink): CNAME_ROOT = os.path.join(settings.SITE_ROOT, 'public_cname_root') WEB_ROOT = os.path.join(settings.SITE_ROOT, 'public_web_root') PROJECT_CNAME_ROOT = os.path.join( - settings.SITE_ROOT, 'public_cname_project' + settings.SITE_ROOT, 'public_cname_project', ) def get_version_queryset(self): return ( - self.project.versions.protected(only_active=False - ).filter(built=True) | + self.project.versions.protected( + only_active=False, + ).filter(built=True) | self.project.versions.protected(only_active=True) ) @@ -399,7 +400,7 @@ class PrivateSymlinkBase(Symlink): CNAME_ROOT = os.path.join(settings.SITE_ROOT, 'private_cname_root') WEB_ROOT = os.path.join(settings.SITE_ROOT, 'private_web_root') PROJECT_CNAME_ROOT = os.path.join( - settings.SITE_ROOT, 'private_cname_project' + settings.SITE_ROOT, 'private_cname_project', ) def get_version_queryset(self): diff --git a/readthedocs/core/tasks.py b/readthedocs/core/tasks.py index e4f3e329c10..7c1b86f477f 100644 --- a/readthedocs/core/tasks.py +++ b/readthedocs/core/tasks.py @@ -67,6 +67,6 @@ def clear_persistent_messages(): # Delete all expired message_extend's messages log.info("Deleting all expired message_extend's messages") expired_messages = PersistentMessage.objects.filter( - expires__lt=timezone.now() + expires__lt=timezone.now(), ) expired_messages.delete() diff --git a/readthedocs/core/templatetags/core_tags.py b/readthedocs/core/templatetags/core_tags.py index 3df7c57697b..a94e511acf0 100644 --- a/readthedocs/core/templatetags/core_tags.py +++ b/readthedocs/core/templatetags/core_tags.py @@ -61,7 +61,7 @@ def restructuredtext(value, short=False): 'file_insertion_enabled': False, } docutils_settings.update( - getattr(settings, 'RESTRUCTUREDTEXT_FILTER_SETTINGS', {}) + getattr(settings, 'RESTRUCTUREDTEXT_FILTER_SETTINGS', {}), ) try: parts = publish_parts( diff --git a/readthedocs/core/templatetags/privacy_tags.py b/readthedocs/core/templatetags/privacy_tags.py index e81a6f3a3b9..f105a300e74 100644 --- a/readthedocs/core/templatetags/privacy_tags.py +++ b/readthedocs/core/templatetags/privacy_tags.py @@ -19,7 +19,7 @@ def is_admin(user, project): @register.simple_tag(takes_context=True) def get_public_projects(context, user): projects = Project.objects.for_user_and_viewer( - user=user, viewer=context['request'].user + user=user, viewer=context['request'].user, ) context['public_projects'] = projects return '' diff --git a/readthedocs/core/urls/single_version.py b/readthedocs/core/urls/single_version.py index dedc458e998..545617a553d 100644 --- a/readthedocs/core/urls/single_version.py +++ b/readthedocs/core/urls/single_version.py @@ -37,7 +37,7 @@ # Needed to serve media locally if getattr(settings, 'DEBUG', False): groups.insert( - 0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + 0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), ) # Allow `/docs/` URL's when not using subdomains or during local dev diff --git a/readthedocs/core/urls/subdomain.py b/readthedocs/core/urls/subdomain.py index 506688b75a8..90f3b1865ea 100644 --- a/readthedocs/core/urls/subdomain.py +++ b/readthedocs/core/urls/subdomain.py @@ -28,15 +28,19 @@ r'page/(?P.*)$'.format(**pattern_opts), redirect_page_with_filename, name='docs_detail', ), - url((r'^(?:|projects/(?P{project_slug})/)$').format( - **pattern_opts - ), redirect_project_slug, name='redirect_project_slug',), - url(( - r'^(?:|projects/(?P{project_slug})/)' - r'(?P{lang_slug})/' - r'(?P{version_slug})/' - r'(?P{filename_slug})$'.format(**pattern_opts) - ), serve_docs, name='docs_detail',), + url( + (r'^(?:|projects/(?P{project_slug})/)$').format( + **pattern_opts + ), redirect_project_slug, name='redirect_project_slug', + ), + url( + ( + r'^(?:|projects/(?P{project_slug})/)' + r'(?P{lang_slug})/' + r'(?P{version_slug})/' + r'(?P{filename_slug})$'.format(**pattern_opts) + ), serve_docs, name='docs_detail', + ), ] groups = [subdomain_urls] diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index 6302e9302dc..162ff932cc4 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -67,7 +67,7 @@ def build_branches(project, branch_list): versions = project.versions_from_branch_name(branch) for version in versions: log.info( - '(Branch Build) Processing %s:%s', project.slug, version.slug + '(Branch Build) Processing %s:%s', project.slug, version.slug, ) ret = _build_version(project, version.slug, already_built=to_build) if ret: @@ -151,7 +151,7 @@ def _build_url(url, projects, branches): for project_slug, built in list(all_built.items()): if built: msg = '(URL Build) Build Started: {} [{}]'.format( - url, ' '.join(built) + url, ' '.join(built), ) log_info(project_slug, msg=msg) ret += msg @@ -159,7 +159,7 @@ def _build_url(url, projects, branches): for project_slug, not_building in list(all_not_building.items()): if not_building: msg = '(URL Build) Not Building: {} [{}]'.format( - url, ' '.join(not_building) + url, ' '.join(not_building), ) log_info(project_slug, msg=msg) ret += msg @@ -296,7 +296,7 @@ def bitbucket_build(request): if not repository['absolute_url']: return HttpResponse('Invalid request', status=400) search_url = 'bitbucket.org{}'.format( - repository['absolute_url'].rstrip('/') + repository['absolute_url'].rstrip('/'), ) elif version == 2: changes = data['push']['changes'] @@ -304,7 +304,7 @@ def bitbucket_build(request): if not data['repository']['full_name']: return HttpResponse('Invalid request', status=400) search_url = 'bitbucket.org/{}'.format( - data['repository']['full_name'] + data['repository']['full_name'], ) except (TypeError, ValueError, KeyError): log.exception('Invalid Bitbucket webhook payload') @@ -353,10 +353,10 @@ def generic_build(request, project_id_or_slug=None): except (Project.DoesNotExist, ValueError): log.exception( '(Incoming Generic Build) Repo not found: %s', - project_id_or_slug + project_id_or_slug, ) return HttpResponseNotFound( - 'Repo not found: %s' % project_id_or_slug + 'Repo not found: %s' % project_id_or_slug, ) # This endpoint doesn't require authorization, we shouldn't allow builds to # be triggered from this any longer. Deprecation plan is to selectively diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 388c8ce7675..46a6c8ec926 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -115,7 +115,7 @@ def redirect_project_slug(request, project, subproject): # pylint: disable=unus def redirect_page_with_filename(request, project, subproject, filename): # pylint: disable=unused-argument # noqa """Redirect /page/file.html to /en/latest/file.html.""" return HttpResponseRedirect( - resolve(subproject or project, filename=filename) + resolve(subproject or project, filename=filename), ) @@ -134,7 +134,7 @@ def _serve_file(request, filename, basepath): # Serve from Nginx content_type, encoding = mimetypes.guess_type( - os.path.join(basepath, filename) + os.path.join(basepath, filename), ) content_type = content_type or 'application/octet-stream' response = HttpResponse(content_type=content_type) @@ -162,7 +162,7 @@ def _serve_file(request, filename, basepath): @map_subproject_slug def serve_docs( request, project, subproject, lang_slug=None, version_slug=None, - filename='' + filename='', ): """Map existing proj, lang, version, filename views to the file format.""" if not version_slug: @@ -229,7 +229,7 @@ def _serve_symlink_docs(request, project, privacy_level, filename=''): files_tried.append(os.path.join(basepath, filename)) raise Http404( - 'File not found. Tried these files: %s' % ','.join(files_tried) + 'File not found. Tried these files: %s' % ','.join(files_tried), ) diff --git a/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py index ba5d010b442..8d80ecc3582 100644 --- a/readthedocs/doc_builder/backends/mkdocs.py +++ b/readthedocs/doc_builder/backends/mkdocs.py @@ -97,7 +97,7 @@ def load_yaml_config(self): if hasattr(exc, 'problem_mark'): mark = exc.problem_mark note = ' (line %d, column %d)' % ( - mark.line + 1, mark.column + 1 + mark.line + 1, mark.column + 1, ) raise MkDocsYAMLParseError( 'Your mkdocs.yml could not be loaded, ' @@ -209,12 +209,12 @@ def generate_rtd_data(self, docs_dir, mkdocs_config): 'docroot': docs_dir, 'source_suffix': '.md', 'api_host': getattr( - settings, 'PUBLIC_API_URL', 'https://readthedocs.org' + settings, 'PUBLIC_API_URL', 'https://readthedocs.org', ), 'ad_free': not self.project.show_advertising, 'commit': self.version.project.vcs_repo(self.version.slug).commit, 'global_analytics_code': getattr( - settings, 'GLOBAL_ANALYTICS_CODE', 'UA-17997319-1' + settings, 'GLOBAL_ANALYTICS_CODE', 'UA-17997319-1', ), 'user_analytics_code': analytics_code, } diff --git a/readthedocs/doc_builder/constants.py b/readthedocs/doc_builder/constants.py index 1abacebba0a..4f6deeb6174 100644 --- a/readthedocs/doc_builder/constants.py +++ b/readthedocs/doc_builder/constants.py @@ -33,7 +33,7 @@ old_config = getattr(settings, 'DOCKER_BUILD_IMAGES', None) if old_config: log.warning( - 'Old config detected, DOCKER_BUILD_IMAGES->DOCKER_IMAGE_SETTINGS' + 'Old config detected, DOCKER_BUILD_IMAGES->DOCKER_IMAGE_SETTINGS', ) DOCKER_IMAGE_SETTINGS.update(old_config) diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py index 70a716e68be..cb27a88dded 100644 --- a/readthedocs/doc_builder/environments.py +++ b/readthedocs/doc_builder/environments.py @@ -315,8 +315,10 @@ def run(self): killed_in_output = 'Killed' in '\n'.join( self.output.splitlines()[-15:], ) - if self.exit_code == DOCKER_OOM_EXIT_CODE or (self.exit_code == 1 and - killed_in_output): + if self.exit_code == DOCKER_OOM_EXIT_CODE or ( + self.exit_code == 1 and + killed_in_output + ): self.output += str( _( '\n\nCommand killed due to excessive memory consumption\n', @@ -975,11 +977,13 @@ def update_build_from_container_state(self): _('Build exited due to excessive memory consumption'), ) elif state.get('Error'): - self.failure = BuildEnvironmentError(( - _('Build exited due to unknown error: {0}').format( - state.get('Error'), - ) - ),) + self.failure = BuildEnvironmentError( + ( + _('Build exited due to unknown error: {0}').format( + state.get('Error'), + ) + ), + ) def create_container(self): """Create docker container.""" diff --git a/readthedocs/doc_builder/exceptions.py b/readthedocs/doc_builder/exceptions.py index afe35dce5f7..dff39908df8 100644 --- a/readthedocs/doc_builder/exceptions.py +++ b/readthedocs/doc_builder/exceptions.py @@ -11,7 +11,7 @@ class BuildEnvironmentException(Exception): def __init__(self, message=None, **kwargs): self.status_code = kwargs.pop( - 'status_code', None + 'status_code', None, ) or self.status_code or 1 message = message or self.get_default_message() super().__init__(message, **kwargs) diff --git a/readthedocs/gold/forms.py b/readthedocs/gold/forms.py index 730f2929636..b60b58e0b70 100644 --- a/readthedocs/gold/forms.py +++ b/readthedocs/gold/forms.py @@ -33,7 +33,7 @@ class Meta: widget=forms.HiddenInput( attrs={ 'data-bind': 'valueInit: last_4_card_digits, value: last_4_card_digits', - } + }, ), ) @@ -120,5 +120,5 @@ def clean(self): return cleaned_data self.add_error( - None, 'You already have the max number of supported projects.' + None, 'You already have the max number of supported projects.', ) diff --git a/readthedocs/gold/tests/test_forms.py b/readthedocs/gold/tests/test_forms.py index 4231c713dcf..c379bba3b79 100644 --- a/readthedocs/gold/tests/test_forms.py +++ b/readthedocs/gold/tests/test_forms.py @@ -44,7 +44,7 @@ def test_add_subscription(self): 'id': 'cus_12345', 'description': self.user.get_full_name(), 'email': self.user.email, - 'subscriptions': subscription_list + 'subscriptions': subscription_list, } subscription_obj = { 'id': 'sub_12345', @@ -56,7 +56,7 @@ def test_add_subscription(self): 'amount': 1000, 'currency': 'usd', 'name': 'Test', - } + }, } self.mock_request([ (customer_obj, ''), @@ -65,13 +65,14 @@ def test_add_subscription(self): ]) # Create user and subscription - subscription_form = GoldSubscriptionForm({ - 'level': 'v1-org-5', - 'last_4_card_digits': '0000', - 'stripe_token': 'GARYBUSEY', - 'business_vat_id': 'business-vat-id', - }, - customer=self.user, + subscription_form = GoldSubscriptionForm( + { + 'level': 'v1-org-5', + 'last_4_card_digits': '0000', + 'stripe_token': 'GARYBUSEY', + 'business_vat_id': 'business-vat-id', + }, + customer=self.user, ) self.assertTrue(subscription_form.is_valid()) subscription = subscription_form.save() @@ -83,18 +84,24 @@ def test_add_subscription(self): self.assertEqual(self.user.gold.first().level, 'v1-org-5') self.mocks['request'].request.assert_has_calls([ - mock.call('post', - '/v1/customers', - {'description': mock.ANY, 'email': mock.ANY, 'business_vat_id': 'business-vat-id'}, - mock.ANY), - mock.call('get', - '/v1/customers/cus_12345/subscriptions', - mock.ANY, - mock.ANY), - mock.call('post', - '/v1/customers/cus_12345/subscriptions', - {'source': mock.ANY, 'plan': 'v1-org-5'}, - mock.ANY), + mock.call( + 'post', + '/v1/customers', + {'description': mock.ANY, 'email': mock.ANY, 'business_vat_id': 'business-vat-id'}, + mock.ANY, + ), + mock.call( + 'get', + '/v1/customers/cus_12345/subscriptions', + mock.ANY, + mock.ANY, + ), + mock.call( + 'post', + '/v1/customers/cus_12345/subscriptions', + {'source': mock.ANY, 'plan': 'v1-org-5'}, + mock.ANY, + ), ]) def test_add_subscription_update_user(self): @@ -110,7 +117,7 @@ def test_add_subscription_update_user(self): 'id': 'cus_12345', 'description': self.user.get_full_name(), 'email': self.user.email, - 'subscriptions': subscription_list + 'subscriptions': subscription_list, } subscription_obj = { 'id': 'sub_12345', @@ -122,7 +129,7 @@ def test_add_subscription_update_user(self): 'amount': 1000, 'currency': 'usd', 'name': 'Test', - } + }, } self.mock_request([ (customer_obj, ''), @@ -134,11 +141,13 @@ def test_add_subscription_update_user(self): # Create user and update the current gold subscription golduser = fixture.get(GoldUser, user=self.user, stripe_id='cus_12345') subscription_form = GoldSubscriptionForm( - {'level': 'v1-org-5', - 'last_4_card_digits': '0000', - 'stripe_token': 'GARYBUSEY'}, + { + 'level': 'v1-org-5', + 'last_4_card_digits': '0000', + 'stripe_token': 'GARYBUSEY', + }, customer=self.user, - instance=golduser + instance=golduser, ) self.assertTrue(subscription_form.is_valid()) subscription = subscription_form.save() @@ -149,22 +158,30 @@ def test_add_subscription_update_user(self): self.assertEqual(self.user.gold.first().level, 'v1-org-5') self.mocks['request'].request.assert_has_calls([ - mock.call('get', - '/v1/customers/cus_12345', - {}, - mock.ANY), - mock.call('post', - '/v1/customers/cus_12345', - {'description': mock.ANY, 'email': mock.ANY}, - mock.ANY), - mock.call('get', - '/v1/customers/cus_12345/subscriptions', - mock.ANY, - mock.ANY), - mock.call('post', - '/v1/customers/cus_12345/subscriptions', - {'source': mock.ANY, 'plan': 'v1-org-5'}, - mock.ANY), + mock.call( + 'get', + '/v1/customers/cus_12345', + {}, + mock.ANY, + ), + mock.call( + 'post', + '/v1/customers/cus_12345', + {'description': mock.ANY, 'email': mock.ANY}, + mock.ANY, + ), + mock.call( + 'get', + '/v1/customers/cus_12345/subscriptions', + mock.ANY, + mock.ANY, + ), + mock.call( + 'post', + '/v1/customers/cus_12345/subscriptions', + {'source': mock.ANY, 'plan': 'v1-org-5'}, + mock.ANY, + ), ]) def test_update_subscription_plan(self): @@ -179,7 +196,7 @@ def test_update_subscription_plan(self): 'amount': 1000, 'currency': 'usd', 'name': 'Test', - } + }, } subscription_list = { 'object': 'list', @@ -192,7 +209,7 @@ def test_update_subscription_plan(self): 'id': 'cus_12345', 'description': self.user.get_full_name(), 'email': self.user.email, - 'subscriptions': subscription_list + 'subscriptions': subscription_list, } self.mock_request([ (customer_obj, ''), @@ -200,10 +217,12 @@ def test_update_subscription_plan(self): (subscription_obj, ''), ]) subscription_form = GoldSubscriptionForm( - {'level': 'v1-org-5', - 'last_4_card_digits': '0000', - 'stripe_token': 'GARYBUSEY'}, - customer=self.user + { + 'level': 'v1-org-5', + 'last_4_card_digits': '0000', + 'stripe_token': 'GARYBUSEY', + }, + customer=self.user, ) self.assertTrue(subscription_form.is_valid()) subscription = subscription_form.save() @@ -213,16 +232,22 @@ def test_update_subscription_plan(self): self.assertEqual(self.user.gold.first().level, 'v1-org-5') self.mocks['request'].request.assert_has_calls([ - mock.call('post', - '/v1/customers', - {'description': mock.ANY, 'email': mock.ANY}, - mock.ANY), - mock.call('get', - '/v1/customers/cus_12345/subscriptions', - mock.ANY, - mock.ANY), - mock.call('post', - '/v1/subscriptions/sub_12345', - {'source': mock.ANY, 'plan': 'v1-org-5'}, - mock.ANY), + mock.call( + 'post', + '/v1/customers', + {'description': mock.ANY, 'email': mock.ANY}, + mock.ANY, + ), + mock.call( + 'get', + '/v1/customers/cus_12345/subscriptions', + mock.ANY, + mock.ANY, + ), + mock.call( + 'post', + '/v1/subscriptions/sub_12345', + {'source': mock.ANY, 'plan': 'v1-org-5'}, + mock.ANY, + ), ]) diff --git a/readthedocs/gold/urls.py b/readthedocs/gold/urls.py index 2a6b2047b0c..7f679686fe0 100644 --- a/readthedocs/gold/urls.py +++ b/readthedocs/gold/urls.py @@ -16,13 +16,13 @@ name='gold_subscription', ), url( - r'^cancel/$', views.DeleteGoldSubscription.as_view(), name='gold_cancel' + r'^cancel/$', views.DeleteGoldSubscription.as_view(), name='gold_cancel', ), url(r'^projects/$', views.projects, name='gold_projects'), url( ( r'^projects/remove/(?P{project_slug})/$'.format( - project_slug=PROJECT_SLUG_REGEX + project_slug=PROJECT_SLUG_REGEX, ) ), views.projects_remove, diff --git a/readthedocs/integrations/admin.py b/readthedocs/integrations/admin.py index 9b328647343..1b27ed8f5c8 100644 --- a/readthedocs/integrations/admin.py +++ b/readthedocs/integrations/admin.py @@ -24,7 +24,7 @@ def inner(_, obj): 'float: left;', obj.formatted_json(field), styles, - ) + ), ) inner.short_description = description @@ -103,7 +103,7 @@ def exchanges(self, obj): 'admin:{}_{}_changelist'.format( HttpExchange._meta.app_label, # pylint: disable=protected-access HttpExchange._meta.model_name, # pylint: disable=protected-access - ) + ), ) return mark_safe( '{} HTTP transactions'.format( @@ -111,7 +111,7 @@ def exchanges(self, obj): 'integrations', obj.pk, obj.exchanges.count(), - ) + ), ) exchanges.short_description = 'HTTP exchanges' diff --git a/readthedocs/notifications/forms.py b/readthedocs/notifications/forms.py index 83b68b2cff7..a5ed64e966d 100644 --- a/readthedocs/notifications/forms.py +++ b/readthedocs/notifications/forms.py @@ -31,8 +31,10 @@ class SendNotificationForm(forms.Form): def __init__(self, *args, **kwargs): self.notification_classes = kwargs.pop('notification_classes', []) super().__init__(*args, **kwargs) - self.fields['source'].choices = [(cls.name, cls.name) - for cls in self.notification_classes] + self.fields['source'].choices = [ + (cls.name, cls.name) + for cls in self.notification_classes + ] def clean_source(self): """Get the source class from the class name.""" diff --git a/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py b/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py index c79a997f1c4..5b00b8377e5 100644 --- a/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py +++ b/readthedocs/oauth/migrations/0004_drop_github_and_bitbucket_models.py @@ -7,8 +7,10 @@ def forwards_remove_content_types(apps, schema_editor): ContentType = apps.get_model('contenttypes', 'ContentType') ContentType.objects.using(db).filter( app_label='oauth', - model__in=['githubproject', 'githuborganization', - 'bitbucketproject', 'bitbucketteam'] + model__in=[ + 'githubproject', 'githuborganization', + 'bitbucketproject', 'bitbucketteam', + ], ).delete() diff --git a/readthedocs/oauth/migrations/0006_move_oauth_source.py b/readthedocs/oauth/migrations/0006_move_oauth_source.py index 341f7ef4441..8689b134fa2 100644 --- a/readthedocs/oauth/migrations/0006_move_oauth_source.py +++ b/readthedocs/oauth/migrations/0006_move_oauth_source.py @@ -46,8 +46,12 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(forwards_move_repo_source, - backwards_move_repo_source), - migrations.RunPython(forwards_move_org_source, - backwards_move_org_source), + migrations.RunPython( + forwards_move_repo_source, + backwards_move_repo_source, + ), + migrations.RunPython( + forwards_move_org_source, + backwards_move_org_source, + ), ] diff --git a/readthedocs/oauth/models.py b/readthedocs/oauth/models.py index 40d224df6b3..65522c2bcbf 100644 --- a/readthedocs/oauth/models.py +++ b/readthedocs/oauth/models.py @@ -189,12 +189,14 @@ def matches(self, user): .filter(Q(repo=self.clone_url) | Q(repo__iendswith=fuzzy_url) | Q(repo__iendswith=fuzzy_url + '.git'))) # yapf: disable - return [{ - 'id': project.slug, - 'url': reverse( - 'projects_detail', - kwargs={ - 'project_slug': project.slug, - }, - ), - } for project in projects] + return [ + { + 'id': project.slug, + 'url': reverse( + 'projects_detail', + kwargs={ + 'project_slug': project.slug, + }, + ), + } for project in projects + ] diff --git a/readthedocs/oauth/services/bitbucket.py b/readthedocs/oauth/services/bitbucket.py index e267e5afe73..817ea98bd75 100644 --- a/readthedocs/oauth/services/bitbucket.py +++ b/readthedocs/oauth/services/bitbucket.py @@ -190,7 +190,7 @@ def get_webhook_data(self, project, integration): """Get webhook JSON data to post to the API.""" return json.dumps({ 'description': 'Read the Docs ({domain})'.format( - domain=settings.PRODUCTION_DOMAIN + domain=settings.PRODUCTION_DOMAIN, ), 'url': 'https://{domain}{path}'.format( domain=settings.PRODUCTION_DOMAIN, diff --git a/readthedocs/payments/forms.py b/readthedocs/payments/forms.py index d313c9e552f..59f9bce97eb 100644 --- a/readthedocs/payments/forms.py +++ b/readthedocs/payments/forms.py @@ -88,7 +88,7 @@ class StripeModelForm(forms.ModelForm): widget=forms.HiddenInput( attrs={ 'data-bind': 'valueInit: stripe_token', - } + }, ), ) @@ -103,7 +103,7 @@ class StripeModelForm(forms.ModelForm): 'textInput: cc_number, ' '''css: {'field-error': error_cc_number() != null}''' ), - } + }, ), max_length=25, required=False, @@ -117,7 +117,7 @@ class StripeModelForm(forms.ModelForm): 'textInput: cc_expiry, ' '''css: {'field-error': error_cc_expiry() != null}''' ), - } + }, ), max_length=10, required=False, @@ -132,7 +132,7 @@ class StripeModelForm(forms.ModelForm): '''css: {'field-error': error_cc_cvv() != null}''' ), 'autocomplete': 'off', - } + }, ), max_length=8, required=False, @@ -211,7 +211,7 @@ def clear_card_data(self): self.data['stripe_token'] = None except AttributeError: raise AttributeError( - 'Form was passed immutable QueryDict POST data' + 'Form was passed immutable QueryDict POST data', ) def fields_with_cc_group(self): diff --git a/readthedocs/projects/admin.py b/readthedocs/projects/admin.py index 324e4986486..165cf7491b3 100644 --- a/readthedocs/projects/admin.py +++ b/readthedocs/projects/admin.py @@ -135,7 +135,7 @@ def feature_flags(self, obj): def send_owner_email(self, request, queryset): view = ProjectSendNotificationView.as_view( - action_name='send_owner_email' + action_name='send_owner_email', ) return view(request, queryset=queryset) diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index fe9e9d80106..115819060b3 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -122,7 +122,8 @@ def clean_name(self): potential_slug = slugify(name) if Project.objects.filter(slug=potential_slug).exists(): raise forms.ValidationError( - _('Invalid project name, a project already exists with that name')) # yapf: disable # noqa + _('Invalid project name, a project already exists with that name'), + ) # yapf: disable # noqa if not potential_slug: # Check the generated slug won't be empty raise forms.ValidationError(_('Invalid project name'),) @@ -180,8 +181,8 @@ def clean_tags(self): if len(tag) > 100: raise forms.ValidationError( _( - 'Length of each tag must be less than or equal to 100 characters.' - ) + 'Length of each tag must be less than or equal to 100 characters.', + ), ) return tags @@ -195,7 +196,7 @@ class ProjectAdvancedForm(ProjectTriggerBuildMixin, ProjectForm): initial='python', help_text=_( 'The Python interpreter used to create the virtual ' - 'environment.' + 'environment.', ), ) @@ -227,28 +228,33 @@ def __init__(self, *args, **kwargs): default_choice = (None, '-' * 9) all_versions = self.instance.versions.values_list( - 'identifier', 'verbose_name' + 'identifier', 'verbose_name', ) self.fields['default_branch'].widget = forms.Select( - choices=[default_choice] + list(all_versions) + choices=[default_choice] + list(all_versions), ) active_versions = self.instance.all_active_versions().values_list('slug', 'verbose_name') # yapf: disabled self.fields['default_version'].widget = forms.Select( - choices=active_versions + choices=active_versions, ) def clean_conf_py_file(self): filename = self.cleaned_data.get('conf_py_file', '').strip() if filename and 'conf.py' not in filename: raise forms.ValidationError( - _('Your configuration file is invalid, make sure it contains ' - 'conf.py in it.')) # yapf: disable + _( + 'Your configuration file is invalid, make sure it contains ' + 'conf.py in it.', + ), + ) # yapf: disable return filename -class UpdateProjectForm(ProjectTriggerBuildMixin, ProjectBasicsForm, - ProjectExtraForm): +class UpdateProjectForm( + ProjectTriggerBuildMixin, ProjectBasicsForm, + ProjectExtraForm, +): class Meta: model = Project @@ -272,17 +278,17 @@ def clean_language(self): if project: msg = _( 'There is already a "{lang}" translation ' - 'for the {proj} project.' + 'for the {proj} project.', ) if project.translations.filter(language=language).exists(): raise forms.ValidationError( - msg.format(lang=language, proj=project.slug) + msg.format(lang=language, proj=project.slug), ) main_project = project.main_language_project if main_project: if main_project.language == language: raise forms.ValidationError( - msg.format(lang=language, proj=main_project.slug) + msg.format(lang=language, proj=main_project.slug), ) siblings = ( main_project.translations @@ -292,7 +298,7 @@ def clean_language(self): ) if siblings: raise forms.ValidationError( - msg.format(lang=language, proj=main_project.slug) + msg.format(lang=language, proj=main_project.slug), ) return language @@ -323,7 +329,7 @@ def clean_parent(self): # This validation error is mostly for testing, users shouldn't see # this in normal circumstances raise forms.ValidationError( - _('Subproject nesting is not supported') + _('Subproject nesting is not supported'), ) return self.project @@ -331,7 +337,7 @@ def clean_child(self): child = self.cleaned_data['child'] if child == self.project: raise forms.ValidationError( - _('A project can not be a subproject of itself') + _('A project can not be a subproject of itself'), ) return child @@ -346,7 +352,8 @@ def get_subproject_queryset(self): Project.objects.for_admin_user(self.user) .exclude(subprojects__isnull=False) .exclude(superprojects__isnull=False) - .exclude(pk=self.project.pk)) + .exclude(pk=self.project.pk) + ) return queryset @@ -452,7 +459,7 @@ def build_versions_form(project): class BaseUploadHTMLForm(forms.Form): content = forms.FileField(label=_('Zip file of HTML')) overwrite = forms.BooleanField( - required=False, label=_('Overwrite existing HTML?') + required=False, label=_('Overwrite existing HTML?'), ) def __init__(self, *args, **kwargs): @@ -504,7 +511,7 @@ def clean_user(self): user_qs = User.objects.filter(username=name) if not user_qs.exists(): raise forms.ValidationError( - _('User {name} does not exist').format(name=name) + _('User {name} does not exist').format(name=name), ) self.user = user_qs[0] return name @@ -528,7 +535,7 @@ def __init__(self, *args, **kwargs): def clean_email(self): self.email = EmailHook.objects.get_or_create( - email=self.cleaned_data['email'], project=self.project + email=self.cleaned_data['email'], project=self.project, )[0] return self.email @@ -547,7 +554,7 @@ def __init__(self, *args, **kwargs): def save(self, commit=True): self.webhook = WebHook.objects.get_or_create( - url=self.cleaned_data['url'], project=self.project + url=self.cleaned_data['url'], project=self.project, )[0] self.project.webhook_notifications.add(self.webhook) return self.project @@ -571,8 +578,11 @@ def __init__(self, *args, **kwargs): def get_choices(self): return [ - (project.slug, '{project} ({lang})'.format( - project=project.slug, lang=project.get_language_display())) + ( + project.slug, '{project} ({lang})'.format( + project=project.slug, lang=project.get_language_display(), + ), + ) for project in self.get_translation_queryset().all() ] @@ -583,22 +593,22 @@ def clean_project(self): if self.parent.main_language_project is not None: msg = 'Project "{project}" is already a translation' raise forms.ValidationError( - (_(msg).format(project=self.parent.slug)) + (_(msg).format(project=self.parent.slug)), ) project_translation_qs = self.get_translation_queryset().filter( - slug=translation_project_slug + slug=translation_project_slug, ) if not project_translation_qs.exists(): msg = 'Project "{project}" does not exist.' raise forms.ValidationError( - (_(msg).format(project=translation_project_slug)) + (_(msg).format(project=translation_project_slug)), ) self.translation = project_translation_qs.first() if self.translation.language == self.parent.language: msg = ('Both projects can not have the same language ({lang}).') raise forms.ValidationError( - _(msg).format(lang=self.parent.get_language_display()) + _(msg).format(lang=self.parent.get_language_display()), ) exists_translation = ( self.parent.translations.filter(language=self.translation.language).exists() # yapf: disabled @@ -606,7 +616,7 @@ def clean_project(self): if exists_translation: msg = ('This project already has a translation for {lang}.') raise forms.ValidationError( - _(msg).format(lang=self.translation.get_language_display()) + _(msg).format(lang=self.translation.get_language_display()), ) is_parent = self.translation.translations.exists() if is_parent: @@ -692,7 +702,7 @@ def clean_canonical(self): _id = self.initial.get('id') if canonical and Domain.objects.filter(project=self.project, canonical=True).exclude(pk=_id).exists(): # yapf: disabled # noqa raise forms.ValidationError( - _('Only 1 Domain can be canonical at a time.') + _('Only 1 Domain can be canonical at a time.'), ) return canonical @@ -797,7 +807,7 @@ def clean_name(self): elif self.project.environmentvariable_set.filter(name=name).exists(): raise forms.ValidationError( _( - 'There is already a variable with this name for this project' + 'There is already a variable with this name for this project', ), ) elif ' ' in name: diff --git a/readthedocs/projects/migrations/0007_migrate_canonical_data.py b/readthedocs/projects/migrations/0007_migrate_canonical_data.py index f1f8fe05703..633f975fc5c 100644 --- a/readthedocs/projects/migrations/0007_migrate_canonical_data.py +++ b/readthedocs/projects/migrations/0007_migrate_canonical_data.py @@ -16,7 +16,7 @@ def migrate_canonical(apps, schema_editor): except Exception as e: print(e) print('Failed adding {url} to {project}'.format( - url=project.canonical_url, project=project.name + url=project.canonical_url, project=project.name, )) @@ -27,5 +27,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(migrate_canonical) + migrations.RunPython(migrate_canonical), ] diff --git a/readthedocs/projects/migrations/0010_migrate_domain_data.py b/readthedocs/projects/migrations/0010_migrate_domain_data.py index bcdee33d1db..49d3dc07bb9 100644 --- a/readthedocs/projects/migrations/0010_migrate_domain_data.py +++ b/readthedocs/projects/migrations/0010_migrate_domain_data.py @@ -12,7 +12,8 @@ def migrate_url(apps, schema_editor): for domain in Domain.objects.all(): if domain.project.superprojects.count() or domain.project.main_language_project: print('{project} is a subproject or translation. Deleting domain.'.format( - project=domain.project.slug)) + project=domain.project.slug, + )) domain.delete() continue parsed = urlparse(domain.url) diff --git a/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py b/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py index 0b14b4bd791..91f27b6d001 100644 --- a/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py +++ b/readthedocs/projects/migrations/0021_add-webhook-deprecation-feature.py @@ -28,5 +28,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(forward_add_feature, reverse_add_feature) + migrations.RunPython(forward_add_feature, reverse_add_feature), ] diff --git a/readthedocs/projects/migrations/0023_migrate-alias-slug.py b/readthedocs/projects/migrations/0023_migrate-alias-slug.py index c38907700c3..531c3dc332f 100644 --- a/readthedocs/projects/migrations/0023_migrate-alias-slug.py +++ b/readthedocs/projects/migrations/0023_migrate-alias-slug.py @@ -27,5 +27,5 @@ def reverse(apps, schema_editor): ] operations = [ - migrations.RunPython(migrate_data, reverse) + migrations.RunPython(migrate_data, reverse), ] diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index ae3cbdc0e45..925b9d477d3 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -88,44 +88,46 @@ class Project(models.Model): # Generally from conf.py users = models.ManyToManyField( - User, verbose_name=_('User'), related_name='projects' + User, verbose_name=_('User'), related_name='projects', ) # A DNS label can contain up to 63 characters. name = models.CharField(_('Name'), max_length=63) slug = models.SlugField(_('Slug'), max_length=63, unique=True) description = models.TextField( _('Description'), blank=True, - help_text=_('The reStructuredText ' - 'description of the project') + help_text=_( + 'The reStructuredText ' + 'description of the project', + ), ) repo = models.CharField( _('Repository URL'), max_length=255, validators=[validate_repository_url], - help_text=_('Hosted documentation repository URL') + help_text=_('Hosted documentation repository URL'), ) repo_type = models.CharField( _('Repository type'), max_length=10, choices=constants.REPO_CHOICES, - default='git' + default='git', ) project_url = models.URLField( _('Project homepage'), blank=True, - help_text=_('The project\'s homepage') + help_text=_('The project\'s homepage'), ) canonical_url = models.URLField( _('Canonical URL'), blank=True, - help_text=_('URL that documentation is expected to serve from') + help_text=_('URL that documentation is expected to serve from'), ) single_version = models.BooleanField( _('Single version'), default=False, help_text=_( 'A single version site has no translations and only your ' '"latest" version, served at the root of the domain. Use ' 'this with caution, only turn it on if you will never ' - 'have multiple versions of your docs.' - ) + 'have multiple versions of your docs.', + ), ) default_version = models.CharField( _('Default version'), max_length=255, default=LATEST, - help_text=_('The version of your project that / redirects to') + help_text=_('The version of your project that / redirects to'), ) # In default_branch, None means the backend should choose the # appropriate branch. Eg 'master' for git @@ -134,8 +136,8 @@ class Project(models.Model): blank=True, help_text=_( 'What branch "latest" points to. Leave empty ' 'to use the default value for your VCS (eg. ' - 'trunk or master).' - ) + 'trunk or master).', + ), ) requirements_file = models.CharField( _('Requirements file'), max_length=255, default=None, null=True, @@ -143,16 +145,16 @@ class Project(models.Model): 'A ' 'pip requirements file needed to build your documentation. ' - 'Path from the root of your project.' - ) + 'Path from the root of your project.', + ), ) documentation_type = models.CharField( _('Documentation type'), max_length=20, choices=constants.DOCUMENTATION_CHOICES, default='sphinx', help_text=_( 'Type of documentation you are building. More info.' - ) + 'DirectoryHTMLBuilder">More info.', + ), ) # Project features @@ -161,18 +163,18 @@ class Project(models.Model): _('Analytics code'), max_length=50, null=True, blank=True, help_text=_( 'Google Analytics Tracking ID ' '(ex. UA-22345342-1). ' - 'This may slow down your page loads.' - ) + 'This may slow down your page loads.', + ), ) container_image = models.CharField( - _('Alternative container image'), max_length=64, null=True, blank=True + _('Alternative container image'), max_length=64, null=True, blank=True, ) container_mem_limit = models.CharField( _('Container memory limit'), max_length=10, null=True, blank=True, help_text=_( 'Memory limit in Docker format ' - '-- example: 512m or 1g' - ) + '-- example: 512m or 1g', + ), ) container_time_limit = models.IntegerField( _('Container time limit in seconds'), @@ -180,11 +182,11 @@ class Project(models.Model): blank=True, ) build_queue = models.CharField( - _('Alternate build queue id'), max_length=32, null=True, blank=True + _('Alternate build queue id'), max_length=32, null=True, blank=True, ) allow_promos = models.BooleanField( _('Allow paid advertising'), default=True, - help_text=_('If unchecked, users will still see community ads.') + help_text=_('If unchecked, users will still see community ads.'), ) ad_free = models.BooleanField( _('Ad-free'), @@ -193,34 +195,36 @@ class Project(models.Model): ) show_version_warning = models.BooleanField( _('Show version warning'), default=False, - help_text=_('Show warning banner in non-stable nor latest versions.') + help_text=_('Show warning banner in non-stable nor latest versions.'), ) # Sphinx specific build options. enable_epub_build = models.BooleanField( _('Enable EPUB build'), default=True, help_text=_( - 'Create a EPUB version of your documentation with each build.' - ) + 'Create a EPUB version of your documentation with each build.', + ), ) enable_pdf_build = models.BooleanField( _('Enable PDF build'), default=True, help_text=_( - 'Create a PDF version of your documentation with each build.' - ) + 'Create a PDF version of your documentation with each build.', + ), ) # Other model data. path = models.CharField( _('Path'), max_length=255, editable=False, - help_text=_('The directory where ' - 'conf.py lives') + help_text=_( + 'The directory where ' + 'conf.py lives', + ), ) conf_py_file = models.CharField( _('Python configuration file'), max_length=255, default='', blank=True, help_text=_( 'Path from project root to conf.py file ' '(ex. docs/conf.py). ' - 'Leave blank if you want us to find it for you.' - ) + 'Leave blank if you want us to find it for you.', + ), ) featured = models.BooleanField(_('Featured'), default=False) @@ -228,8 +232,8 @@ class Project(models.Model): install_project = models.BooleanField( _('Install Project'), help_text=_( 'Install your project inside a virtualenv using setup.py ' - 'install' - ), default=False + 'install', + ), default=False, ) # This model attribute holds the python interpreter used to create the @@ -238,38 +242,42 @@ class Project(models.Model): _('Python Interpreter'), max_length=20, choices=constants.PYTHON_CHOICES, default='python', help_text=_( 'The Python interpreter used to create the virtual ' - 'environment.' - ) + 'environment.', + ), ) use_system_packages = models.BooleanField( _('Use system packages'), help_text=_( 'Give the virtual environment access to the global ' - 'site-packages dir.' - ), default=False + 'site-packages dir.', + ), default=False, ) privacy_level = models.CharField( _('Privacy Level'), max_length=20, choices=constants.PRIVACY_CHOICES, - default=getattr(settings, 'DEFAULT_PRIVACY_LEVEL', - 'public'), help_text=_( + default=getattr( + settings, 'DEFAULT_PRIVACY_LEVEL', + 'public', + ), help_text=_( 'Level of privacy that you want on the repository. ' - 'Protected means public but not in listings.' - ) + 'Protected means public but not in listings.', + ), ) version_privacy_level = models.CharField( _('Version Privacy Level'), max_length=20, choices=constants.PRIVACY_CHOICES, - default=getattr(settings, 'DEFAULT_PRIVACY_LEVEL', - 'public'), help_text=_( + default=getattr( + settings, 'DEFAULT_PRIVACY_LEVEL', + 'public', + ), help_text=_( 'Default level of privacy you want on built ' - 'versions of documentation.' - ) + 'versions of documentation.', + ), ) # Subprojects related_projects = models.ManyToManyField( 'self', verbose_name=_('Related projects'), blank=True, - symmetrical=False, through=ProjectRelationship + symmetrical=False, through=ProjectRelationship, ) # Language bits @@ -277,27 +285,27 @@ class Project(models.Model): _('Language'), max_length=20, default='en', help_text=_( 'The language the project ' 'documentation is rendered in. ' - "Note: this affects your project's URL." - ), choices=constants.LANGUAGES + "Note: this affects your project's URL.", + ), choices=constants.LANGUAGES, ) programming_language = models.CharField( _('Programming Language'), max_length=20, default='words', help_text=_( - 'The primary programming language the project is written in.' - ), choices=constants.PROGRAMMING_LANGUAGES, blank=True + 'The primary programming language the project is written in.', + ), choices=constants.PROGRAMMING_LANGUAGES, blank=True, ) # A subproject pointed at its main language, so it can be tracked main_language_project = models.ForeignKey( 'self', related_name='translations', on_delete=models.SET_NULL, - blank=True, null=True + blank=True, null=True, ) has_valid_webhook = models.BooleanField( default=False, - help_text=_('This project has been built with a webhook') + help_text=_('This project has been built with a webhook'), ) has_valid_clone = models.BooleanField( - default=False, help_text=_('This project has been successfully cloned') + default=False, help_text=_('This project has been successfully cloned'), ) tags = TaggableManager(blank=True) @@ -386,14 +394,14 @@ def get_docs_url(self, version_slug=None, lang_slug=None, private=None): """ return resolve( project=self, version_slug=version_slug, language=lang_slug, - private=private + private=private, ) def get_builds_url(self): return reverse( 'builds_project_list', kwargs={ 'project_slug': self.slug, - } + }, ) def get_canonical_url(self): @@ -412,7 +420,8 @@ def get_subproject_urls(self): for proj in ( api.project(self.pk) .subprojects() - .get()['subprojects'])] + .get()['subprojects'] + )] return [(proj.child.slug, proj.child.get_docs_url()) for proj in self.subprojects.all()] @@ -429,23 +438,28 @@ def get_production_media_path(self, type_, version_slug, include_file=True): """ if getattr(settings, 'DEFAULT_PRIVACY_LEVEL', 'public') == 'public' or settings.DEBUG: path = os.path.join( - settings.MEDIA_ROOT, type_, self.slug, version_slug) + settings.MEDIA_ROOT, type_, self.slug, version_slug, + ) else: path = os.path.join( - settings.PRODUCTION_MEDIA_ARTIFACTS, type_, self.slug, version_slug) + settings.PRODUCTION_MEDIA_ARTIFACTS, type_, self.slug, version_slug, + ) if include_file: path = os.path.join( - path, '{}.{}'.format(self.slug, type_.replace('htmlzip', 'zip'))) + path, '{}.{}'.format(self.slug, type_.replace('htmlzip', 'zip')), + ) return path def get_production_media_url(self, type_, version_slug, full_path=True): """Get the URL for downloading a specific media file.""" try: - path = reverse('project_download_media', kwargs={ - 'project_slug': self.slug, - 'type_': type_, - 'version_slug': version_slug, - }) + path = reverse( + 'project_download_media', kwargs={ + 'project_slug': self.slug, + 'type_': type_, + 'version_slug': version_slug, + }, + ) except NoReverseMatch: return '' if full_path: @@ -459,13 +473,13 @@ def subdomain(self): def get_downloads(self): downloads = {} downloads['htmlzip'] = self.get_production_media_url( - 'htmlzip', self.get_default_version() + 'htmlzip', self.get_default_version(), ) downloads['epub'] = self.get_production_media_url( - 'epub', self.get_default_version() + 'epub', self.get_default_version(), ) downloads['pdf'] = self.get_production_media_url( - 'pdf', self.get_default_version() + 'pdf', self.get_default_version(), ) return downloads @@ -591,7 +605,7 @@ def conf_file(self, version=LATEST): # the `doc` word in the path, we raise an error informing this to the user if len(files) > 1: raise ProjectConfigurationError( - ProjectConfigurationError.MULTIPLE_CONF_FILES + ProjectConfigurationError.MULTIPLE_CONF_FILES, ) raise ProjectConfigurationError(ProjectConfigurationError.NOT_FOUND) @@ -621,17 +635,20 @@ def has_pdf(self, version_slug=LATEST): if not self.enable_pdf_build: return False return os.path.exists(self.get_production_media_path( - type_='pdf', version_slug=version_slug)) + type_='pdf', version_slug=version_slug, + )) def has_epub(self, version_slug=LATEST): if not self.enable_epub_build: return False return os.path.exists(self.get_production_media_path( - type_='epub', version_slug=version_slug)) + type_='epub', version_slug=version_slug, + )) def has_htmlzip(self, version_slug=LATEST): return os.path.exists(self.get_production_media_path( - type_='htmlzip', version_slug=version_slug)) + type_='htmlzip', version_slug=version_slug, + )) @property def sponsored(self): @@ -780,8 +797,8 @@ def update_stable_version(self): if identifier_updated and current_stable.active and current_stable.machine: log.info( 'Update stable version: {project}:{version}'.format( - project=self.slug, version=new_stable.identifier - ) + project=self.slug, version=new_stable.identifier, + ), ) current_stable.identifier = new_stable.identifier current_stable.save() @@ -789,10 +806,10 @@ def update_stable_version(self): else: log.info( 'Creating new stable version: {project}:{version}' - .format(project=self.slug, version=new_stable.identifier) + .format(project=self.slug, version=new_stable.identifier), ) current_stable = self.versions.create_stable( - type=new_stable.type, identifier=new_stable.identifier + type=new_stable.type, identifier=new_stable.identifier, ) return new_stable @@ -816,7 +833,7 @@ def get_default_version(self): return self.default_version # check if the default_version exists version_qs = self.versions.filter( - slug=self.default_version, active=True + slug=self.default_version, active=True, ) if version_qs.exists(): return self.default_version @@ -915,8 +932,10 @@ def __init__(self, *args, **kwargs): ad_free = (not kwargs.pop('show_advertising', True)) # These fields only exist on the API return, not on the model, so we'll # remove them to avoid throwing exceptions due to unexpected fields - for key in ['users', 'resource_uri', 'absolute_url', 'downloads', - 'main_language_project', 'related_projects']: + for key in [ + 'users', 'resource_uri', 'absolute_url', 'downloads', + 'main_language_project', 'related_projects', + ]: try: del kwargs[key] except KeyError: @@ -954,11 +973,11 @@ class ImportedFile(models.Model): """ project = models.ForeignKey( - 'Project', verbose_name=_('Project'), related_name='imported_files' + 'Project', verbose_name=_('Project'), related_name='imported_files', ) version = models.ForeignKey( 'builds.Version', verbose_name=_('Version'), - related_name='imported_files', null=True + related_name='imported_files', null=True, ) name = models.CharField(_('Name'), max_length=255) slug = models.SlugField(_('Slug')) @@ -970,7 +989,7 @@ class ImportedFile(models.Model): def get_absolute_url(self): return resolve( project=self.project, version_slug=self.version.slug, - filename=self.path + filename=self.path, ) def __str__(self): @@ -996,7 +1015,7 @@ def __str__(self): @python_2_unicode_compatible class WebHook(Notification): url = models.URLField( - max_length=600, blank=True, help_text=_('URL to send the webhook to') + max_length=600, blank=True, help_text=_('URL to send the webhook to'), ) def __str__(self): @@ -1011,23 +1030,23 @@ class Domain(models.Model): project = models.ForeignKey(Project, related_name='domains') domain = models.CharField( _('Domain'), unique=True, max_length=255, - validators=[validate_domain_name] + validators=[validate_domain_name], ) machine = models.BooleanField( - default=False, help_text=_('This Domain was auto-created') + default=False, help_text=_('This Domain was auto-created'), ) cname = models.BooleanField( - default=False, help_text=_('This Domain is a CNAME for the project') + default=False, help_text=_('This Domain is a CNAME for the project'), ) canonical = models.BooleanField( default=False, help_text=_( 'This Domain is the primary one where the documentation is ' - 'served from' - ) + 'served from', + ), ) https = models.BooleanField( _('Use HTTPS'), default=False, - help_text=_('Always use HTTPS for this domain') + help_text=_('Always use HTTPS for this domain'), ) count = models.IntegerField( default=0, @@ -1041,7 +1060,7 @@ class Meta: def __str__(self): return '{domain} pointed at {project}'.format( - domain=self.domain, project=self.project.name + domain=self.domain, project=self.project.name, ) def save(self, *args, **kwargs): # pylint: disable=arguments-differ @@ -1104,15 +1123,27 @@ def add_features(sender, **kwargs): (ALLOW_DEPRECATED_WEBHOOKS, _('Allow deprecated webhook views')), (PIP_ALWAYS_UPGRADE, _('Always run pip install --upgrade')), (SKIP_SUBMODULES, _('Skip git submodule checkout')), - (DONT_OVERWRITE_SPHINX_CONTEXT, _( - 'Do not overwrite context vars in conf.py with Read the Docs context')), - (ALLOW_V2_CONFIG_FILE, _( - 'Allow to use the v2 of the configuration file')), + ( + DONT_OVERWRITE_SPHINX_CONTEXT, _( + 'Do not overwrite context vars in conf.py with Read the Docs context', + ), + ), + ( + ALLOW_V2_CONFIG_FILE, _( + 'Allow to use the v2 of the configuration file', + ), + ), (MKDOCS_THEME_RTD, _('Use Read the Docs theme for MkDocs as default theme')), - (DONT_SHALLOW_CLONE, _( - 'Do not shallow clone when cloning git repos')), - (USE_TESTING_BUILD_IMAGE, _( - 'Use Docker image labelled as `testing` to build the docs')), + ( + DONT_SHALLOW_CLONE, _( + 'Do not shallow clone when cloning git repos', + ), + ), + ( + USE_TESTING_BUILD_IMAGE, _( + 'Use Docker image labelled as `testing` to build the docs', + ), + ), ) projects = models.ManyToManyField( diff --git a/readthedocs/projects/tasks.py b/readthedocs/projects/tasks.py index 95ec9dbbe51..640a08c9c9b 100644 --- a/readthedocs/projects/tasks.py +++ b/readthedocs/projects/tasks.py @@ -142,7 +142,7 @@ def sync_repo(self): project=self.project.slug, version=self.version.slug, msg=msg, - ) + ), ) version_repo = self.get_vcs_repo() version_repo.update() @@ -161,16 +161,20 @@ def sync_versions(self, version_repo): version_post_data = {'repo': version_repo.repo_url} if version_repo.supports_tags: - version_post_data['tags'] = [{ - 'identifier': v.identifier, - 'verbose_name': v.verbose_name, - } for v in version_repo.tags] + version_post_data['tags'] = [ + { + 'identifier': v.identifier, + 'verbose_name': v.verbose_name, + } for v in version_repo.tags + ] if version_repo.supports_branches: - version_post_data['branches'] = [{ - 'identifier': v.identifier, - 'verbose_name': v.verbose_name, - } for v in version_repo.branches] + version_post_data['branches'] = [ + { + 'identifier': v.identifier, + 'verbose_name': v.verbose_name, + } for v in version_repo.branches + ] self.validate_duplicate_reserved_versions(version_post_data) @@ -471,7 +475,7 @@ def run_setup(self, record=True): project=self.project.slug, version=self.version.slug, msg=msg, - ) + ), ) # Send notification to users only if the build didn't fail because @@ -532,7 +536,7 @@ def run_build(self, docker, record): project=self.project.slug, version=self.version.slug, msg='Using conda', - ) + ), ) python_env_cls = Conda self.python_env = python_env_cls( @@ -613,7 +617,7 @@ def setup_vcs(self): project=self.project.slug, version=self.version.slug, msg='Updating docs from VCS', - ) + ), ) try: self.sync_repo() @@ -1096,7 +1100,7 @@ def remove_orphan_symlinks(): for symlink in [PublicSymlink, PrivateSymlink]: for domain_path in [symlink.PROJECT_CNAME_ROOT, symlink.CNAME_ROOT]: valid_cnames = set( - Domain.objects.all().values_list('domain', flat=True) + Domain.objects.all().values_list('domain', flat=True), ) orphan_cnames = set(os.listdir(domain_path)) - valid_cnames for cname in orphan_cnames: @@ -1225,8 +1229,10 @@ def send_notifications(version_pk, build_pk): for hook in version.project.webhook_notifications.all(): webhook_notification(version, build, hook.url) - for email in version.project.emailhook_notifications.all().values_list('email', - flat=True): + for email in version.project.emailhook_notifications.all().values_list( + 'email', + flat=True, + ): email_notification(version, build, email) @@ -1270,8 +1276,9 @@ def email_notification(version, build, email): } if build.commit: - title = _('Failed: {project[name]} ({commit})' - ).format(commit=build.commit[:8], **context) + title = _( + 'Failed: {project[name]} ({commit})', + ).format(commit=build.commit[:8], **context) else: title = _('Failed: {project[name]} ({version[verbose_name]})').format( **context diff --git a/readthedocs/projects/urls/private.py b/readthedocs/projects/urls/private.py index eebec75c1f5..c1dd9b20cc5 100644 --- a/readthedocs/projects/urls/private.py +++ b/readthedocs/projects/urls/private.py @@ -33,99 +33,99 @@ url(r'^$', ProjectDashboard.as_view(), name='projects_dashboard'), url( r'^import/$', ImportView.as_view(wizard_class=ImportWizardView), - {'wizard': ImportWizardView}, name='projects_import' + {'wizard': ImportWizardView}, name='projects_import', ), url( r'^import/manual/$', ImportWizardView.as_view(), - name='projects_import_manual' + name='projects_import_manual', ), url( r'^import/manual/demo/$', ImportDemoView.as_view(), - name='projects_import_demo' + name='projects_import_demo', ), url( r'^(?P[-\w]+)/$', private.project_manage, - name='projects_manage' + name='projects_manage', ), url( r'^(?P[-\w]+)/edit/$', ProjectUpdate.as_view(), - name='projects_edit' + name='projects_edit', ), url( r'^(?P[-\w]+)/advanced/$', - ProjectAdvancedUpdate.as_view(), name='projects_advanced' + ProjectAdvancedUpdate.as_view(), name='projects_advanced', ), url( r'^(?P[-\w]+)/version/(?P[^/]+)/delete_html/$', - private.project_version_delete_html, name='project_version_delete_html' + private.project_version_delete_html, name='project_version_delete_html', ), url( r'^(?P[-\w]+)/version/(?P[^/]+)/$', - private.project_version_detail, name='project_version_detail' + private.project_version_detail, name='project_version_detail', ), url( r'^(?P[-\w]+)/versions/$', private.project_versions, - name='projects_versions' + name='projects_versions', ), url( r'^(?P[-\w]+)/delete/$', private.project_delete, - name='projects_delete' + name='projects_delete', ), url( r'^(?P[-\w]+)/users/$', private.project_users, - name='projects_users' + name='projects_users', ), url( r'^(?P[-\w]+)/users/delete/$', - private.project_users_delete, name='projects_users_delete' + private.project_users_delete, name='projects_users_delete', ), url( r'^(?P[-\w]+)/notifications/$', - private.project_notifications, name='projects_notifications' + private.project_notifications, name='projects_notifications', ), url( r'^(?P[-\w]+)/notifications/delete/$', - private.project_notifications_delete, name='projects_notification_delete' + private.project_notifications_delete, name='projects_notification_delete', ), url( r'^(?P[-\w]+)/translations/$', - private.project_translations, name='projects_translations' + private.project_translations, name='projects_translations', ), url( r'^(?P[-\w]+)/translations/delete/(?P[-\w]+)/$', # noqa private.project_translations_delete, - name='projects_translations_delete' + name='projects_translations_delete', ), url( r'^(?P[-\w]+)/redirects/$', private.project_redirects, - name='projects_redirects' + name='projects_redirects', ), url( r'^(?P[-\w]+)/redirects/delete/$', - private.project_redirects_delete, name='projects_redirects_delete' + private.project_redirects_delete, name='projects_redirects_delete', ), url( r'^(?P[-\w]+)/advertising/$', - ProjectAdvertisingUpdate.as_view(), name='projects_advertising' + ProjectAdvertisingUpdate.as_view(), name='projects_advertising', ), ] domain_urls = [ url( r'^(?P[-\w]+)/domains/$', DomainList.as_view(), - name='projects_domains' + name='projects_domains', ), url( r'^(?P[-\w]+)/domains/create/$', DomainCreate.as_view(), - name='projects_domains_create' + name='projects_domains_create', ), url( r'^(?P[-\w]+)/domains/(?P[-\w]+)/edit/$', - DomainUpdate.as_view(), name='projects_domains_edit' + DomainUpdate.as_view(), name='projects_domains_edit', ), url( r'^(?P[-\w]+)/domains/(?P[-\w]+)/delete/$', - DomainDelete.as_view(), name='projects_domains_delete' + DomainDelete.as_view(), name='projects_domains_delete', ), ] @@ -135,44 +135,54 @@ url( r'^(?P{project_slug})/integrations/$'.format( **pattern_opts - ), IntegrationList.as_view(), name='projects_integrations' + ), IntegrationList.as_view(), name='projects_integrations', ), url( r'^(?P{project_slug})/integrations/sync/$'.format( **pattern_opts ), IntegrationWebhookSync.as_view(), - name='projects_integrations_webhooks_sync' + name='projects_integrations_webhooks_sync', + ), + url( + ( + r'^(?P{project_slug})/integrations/create/$'.format( + **pattern_opts + ) + ), IntegrationCreate.as_view(), name='projects_integrations_create', + ), + url( + ( + r'^(?P{project_slug})/' + r'integrations/(?P{integer_pk})/$'.format( + **pattern_opts + ) + ), IntegrationDetail.as_view(), name='projects_integrations_detail', + ), + url( + ( + r'^(?P{project_slug})/' + r'integrations/(?P{integer_pk})/' + r'exchange/(?P[-\w]+)/$'.format(**pattern_opts) + ), IntegrationExchangeDetail.as_view(), + name='projects_integrations_exchanges_detail', + ), + url( + ( + r'^(?P{project_slug})/' + r'integrations/(?P{integer_pk})/sync/$'.format( + **pattern_opts + ) + ), IntegrationWebhookSync.as_view(), + name='projects_integrations_webhooks_sync', + ), + url( + ( + r'^(?P{project_slug})/' + r'integrations/(?P{integer_pk})/delete/$'.format( + **pattern_opts + ) + ), IntegrationDelete.as_view(), name='projects_integrations_delete', ), - url(( - r'^(?P{project_slug})/integrations/create/$'.format( - **pattern_opts - ) - ), IntegrationCreate.as_view(), name='projects_integrations_create'), - url(( - r'^(?P{project_slug})/' - r'integrations/(?P{integer_pk})/$'.format( - **pattern_opts - ) - ), IntegrationDetail.as_view(), name='projects_integrations_detail'), - url(( - r'^(?P{project_slug})/' - r'integrations/(?P{integer_pk})/' - r'exchange/(?P[-\w]+)/$'.format(**pattern_opts) - ), IntegrationExchangeDetail.as_view(), - name='projects_integrations_exchanges_detail'), - url(( - r'^(?P{project_slug})/' - r'integrations/(?P{integer_pk})/sync/$'.format( - **pattern_opts - ) - ), IntegrationWebhookSync.as_view(), - name='projects_integrations_webhooks_sync'), - url(( - r'^(?P{project_slug})/' - r'integrations/(?P{integer_pk})/delete/$'.format( - **pattern_opts - ) - ), IntegrationDelete.as_view(), name='projects_integrations_delete'), ] urlpatterns += integration_urls @@ -182,28 +192,34 @@ r'^(?P{project_slug})/subprojects/$'.format( **pattern_opts ), private.ProjectRelationshipList.as_view(), - name='projects_subprojects' + name='projects_subprojects', + ), + url( + ( + r'^(?P{project_slug})/subprojects/create/$'.format( + **pattern_opts + ) + ), private.ProjectRelationshipCreate.as_view(), + name='projects_subprojects_create', + ), + url( + ( + r'^(?P{project_slug})/' + r'subprojects/(?P{project_slug})/edit/$'.format( + **pattern_opts + ) + ), private.ProjectRelationshipUpdate.as_view(), + name='projects_subprojects_update', + ), + url( + ( + r'^(?P{project_slug})/' + r'subprojects/(?P{project_slug})/delete/$'.format( + **pattern_opts + ) + ), private.ProjectRelationshipDelete.as_view(), + name='projects_subprojects_delete', ), - url(( - r'^(?P{project_slug})/subprojects/create/$'.format( - **pattern_opts - ) - ), private.ProjectRelationshipCreate.as_view(), - name='projects_subprojects_create'), - url(( - r'^(?P{project_slug})/' - r'subprojects/(?P{project_slug})/edit/$'.format( - **pattern_opts - ) - ), private.ProjectRelationshipUpdate.as_view(), - name='projects_subprojects_update'), - url(( - r'^(?P{project_slug})/' - r'subprojects/(?P{project_slug})/delete/$'.format( - **pattern_opts - ) - ), private.ProjectRelationshipDelete.as_view(), - name='projects_subprojects_delete'), ] urlpatterns += subproject_urls @@ -211,22 +227,22 @@ environmentvariable_urls = [ url( r'^(?P[-\w]+)/environmentvariables/$', - EnvironmentVariableList.as_view(), name='projects_environmentvariables' + EnvironmentVariableList.as_view(), name='projects_environmentvariables', ), url( r'^(?P[-\w]+)/environmentvariables/create/$', EnvironmentVariableCreate.as_view(), - name='projects_environmentvariables_create' + name='projects_environmentvariables_create', ), url( r'^(?P[-\w]+)/environmentvariables/(?P[-\w]+)/$', EnvironmentVariableDetail.as_view(), - name='projects_environmentvariables_detail' + name='projects_environmentvariables_detail', ), url( r'^(?P[-\w]+)/environmentvariables/(?P[-\w]+)/delete/$', EnvironmentVariableDelete.as_view(), - name='projects_environmentvariables_delete' + name='projects_environmentvariables_delete', ), ] diff --git a/readthedocs/projects/version_handling.py b/readthedocs/projects/version_handling.py index 09f7592d751..7a730e61fd0 100644 --- a/readthedocs/projects/version_handling.py +++ b/readthedocs/projects/version_handling.py @@ -118,9 +118,11 @@ def determine_stable_version(version_list): :rtype: readthedocs.builds.models.Version """ versions = sort_versions(version_list) - versions = [(version_obj, comparable) - for version_obj, comparable in versions - if not comparable.is_prerelease] + versions = [ + (version_obj, comparable) + for version_obj, comparable in versions + if not comparable.is_prerelease + ] if versions: # We take preference for tags over branches. If we don't find any tag, diff --git a/readthedocs/projects/views/private.py b/readthedocs/projects/views/private.py index aa85f2d6443..41b3acda3cf 100644 --- a/readthedocs/projects/views/private.py +++ b/readthedocs/projects/views/private.py @@ -220,7 +220,7 @@ def project_delete(request, project_slug): if request.method == 'POST': broadcast( - type='app', task=tasks.remove_dirs, args=[(project.doc_path,)] + type='app', task=tasks.remove_dirs, args=[(project.doc_path,)], ) project.delete() messages.success(request, _('Project deleted')) diff --git a/readthedocs/projects/views/public.py b/readthedocs/projects/views/public.py index 3a15e756c46..9256e92e038 100644 --- a/readthedocs/projects/views/public.py +++ b/readthedocs/projects/views/public.py @@ -115,8 +115,10 @@ def get_context_data(self, **kwargs): def project_badge(request, project_slug): """Return a sweet badge for the project.""" style = request.GET.get('style', 'flat') - if style not in ('flat', 'plastic', 'flat-square', 'for-the-badge', - 'social'): + if style not in ( + 'flat', 'plastic', 'flat-square', 'for-the-badge', + 'social', + ): style = 'flat' # Get the local path to the badge files @@ -138,8 +140,10 @@ def project_badge(request, project_slug): ).first() if version: - last_build = version.builds.filter(type='html', - state='finished').order_by('-date').first() + last_build = version.builds.filter( + type='html', + state='finished', + ).order_by('-date').first() if last_build: if last_build.success: file_path = badge_path % 'passing' @@ -154,7 +158,7 @@ def project_badge(request, project_slug): ) except (IOError, OSError): log.exception( - 'Failed to read local filesystem while serving a docs badge' + 'Failed to read local filesystem while serving a docs badge', ) return HttpResponse(status=503) @@ -415,8 +419,9 @@ def project_embed(request, project_slug): slug=project_slug, ) version = project.versions.get(slug=LATEST) - files = version.imported_files.filter(name__endswith='.html' - ).order_by('path') + files = version.imported_files.filter( + name__endswith='.html', + ).order_by('path') return render( request, diff --git a/readthedocs/restapi/views/core_views.py b/readthedocs/restapi/views/core_views.py index 31d1d5f7508..578f1123232 100644 --- a/readthedocs/restapi/views/core_views.py +++ b/readthedocs/restapi/views/core_views.py @@ -28,8 +28,10 @@ def docurl(request): version = request.GET.get('version', LATEST) doc = request.GET.get('doc', 'index') if project is None: - return Response({'error': 'Need project and doc'}, - status=status.HTTP_400_BAD_REQUEST) + return Response( + {'error': 'Need project and doc'}, + status=status.HTTP_400_BAD_REQUEST, + ) project = get_object_or_404(Project, slug=project) version = get_object_or_404( @@ -39,6 +41,6 @@ def docurl(request): ) return Response({ 'url': make_document_url( - project=project, version=version.slug, page=doc + project=project, version=version.slug, page=doc, ), }) diff --git a/readthedocs/restapi/views/footer_views.py b/readthedocs/restapi/views/footer_views.py index 779371b073a..bf66f5f80b2 100644 --- a/readthedocs/restapi/views/footer_views.py +++ b/readthedocs/restapi/views/footer_views.py @@ -95,8 +95,10 @@ def footer_html(request): main_project = project.main_language_project or project if page_slug and page_slug != 'index': - if (main_project.documentation_type == 'sphinx_htmldir' or - main_project.documentation_type == 'mkdocs'): + if ( + main_project.documentation_type == 'sphinx_htmldir' or + main_project.documentation_type == 'mkdocs' + ): path = page_slug + '/' elif main_project.documentation_type == 'sphinx_singlehtml': path = 'index.html#document-' + page_slug diff --git a/readthedocs/restapi/views/integrations.py b/readthedocs/restapi/views/integrations.py index ffb150363b0..7f44fb4f11a 100644 --- a/readthedocs/restapi/views/integrations.py +++ b/readthedocs/restapi/views/integrations.py @@ -379,8 +379,9 @@ def get_project(self, **kwargs): if self.request.user.is_authenticated: try: return ( - Project.objects.for_admin_user(self.request.user - ).get(**kwargs) + Project.objects.for_admin_user( + self.request.user, + ).get(**kwargs) ) except Project.DoesNotExist: pass diff --git a/readthedocs/restapi/views/model_views.py b/readthedocs/restapi/views/model_views.py index 692f5ac0691..f0586e2fadd 100644 --- a/readthedocs/restapi/views/model_views.py +++ b/readthedocs/restapi/views/model_views.py @@ -80,8 +80,10 @@ class UserSelectViewSet(viewsets.ModelViewSet): def get_serializer_class(self): try: - if (self.request.user.is_staff and - self.admin_serializer_class is not None): + if ( + self.request.user.is_staff and + self.admin_serializer_class is not None + ): return self.admin_serializer_class except AttributeError: pass @@ -136,7 +138,7 @@ def active_versions(self, request, **kwargs): }) @decorators.action( - detail=True, permission_classes=[permissions.IsAdminUser] + detail=True, permission_classes=[permissions.IsAdminUser], ) def token(self, request, **kwargs): project = get_object_or_404( @@ -225,8 +227,10 @@ def sync_versions(self, request, **kwargs): # noqa: D205 # Marking the tag that is considered the new stable version as # active and building it if it was just added. - if (activate_new_stable and - promoted_version.slug in added_versions): + if ( + activate_new_stable and + promoted_version.slug in added_versions + ): promoted_version.active = True promoted_version.save() trigger_build(project=project, version=promoted_version) diff --git a/readthedocs/restapi/views/search_views.py b/readthedocs/restapi/views/search_views.py index da0dab870cb..aa2acc1fdbe 100644 --- a/readthedocs/restapi/views/search_views.py +++ b/readthedocs/restapi/views/search_views.py @@ -103,8 +103,10 @@ def search(request): def project_search(request): query = request.GET.get('q', None) if query is None: - return Response({'error': 'Need project and q'}, - status=status.HTTP_400_BAD_REQUEST) + return Response( + {'error': 'Need project and q'}, + status=status.HTTP_400_BAD_REQUEST, + ) log.debug('(API Project Search) %s', (query)) results = search_project(request=request, query=query) return Response({'results': results}) diff --git a/readthedocs/rtd_tests/base.py b/readthedocs/rtd_tests/base.py index 123e8b7f780..e7d8f8409e0 100644 --- a/readthedocs/rtd_tests/base.py +++ b/readthedocs/rtd_tests/base.py @@ -145,7 +145,7 @@ def assertWizardResponse(self, response, step=None): # noqa response.render() self.assertContains( response, - 'name="{}-current_step"'.format(self.wizard_class_slug) + 'name="{}-current_step"'.format(self.wizard_class_slug), ) # We use camelCase on purpose here to conform with unittest's naming diff --git a/readthedocs/rtd_tests/files/conf.py b/readthedocs/rtd_tests/files/conf.py index 972ec96be0b..c4c2ded077c 100644 --- a/readthedocs/rtd_tests/files/conf.py +++ b/readthedocs/rtd_tests/files/conf.py @@ -10,7 +10,7 @@ source_suffix = ['.rst', '.md'] source_parsers = { '.md': CommonMarkParser, - } +} master_doc = 'index' project = 'Pip' copyright = str(datetime.now().year) @@ -22,6 +22,8 @@ html_theme = 'sphinx_rtd_theme' file_insertion_enabled = False latex_documents = [ - ('index', 'pip.tex', 'Pip Documentation', - '', 'manual'), + ( + 'index', 'pip.tex', 'Pip Documentation', + '', 'manual', + ), ] diff --git a/readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py b/readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py index 68b327ca8af..e6c4dad0f4b 100644 --- a/readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py +++ b/readthedocs/rtd_tests/fixtures/sample_repo/source/conf.py @@ -180,8 +180,10 @@ # Grouping the document tree into LaTeX files. List of tuples (source start # file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'sample.tex', 'sample Documentation', - 'Dan', 'manual'), + ( + 'index', 'sample.tex', 'sample Documentation', + 'Dan', 'manual', + ), ] # The name of an image file (relative to this directory) to place at the top of @@ -213,6 +215,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'sample', 'sample Documentation', - ['Dan'], 1) + ( + 'index', 'sample', 'sample Documentation', + ['Dan'], 1, + ), ] diff --git a/readthedocs/rtd_tests/mocks/environment.py b/readthedocs/rtd_tests/mocks/environment.py index c848fedd631..6928d4cec3c 100644 --- a/readthedocs/rtd_tests/mocks/environment.py +++ b/readthedocs/rtd_tests/mocks/environment.py @@ -14,43 +14,61 @@ def __init__(self): 'api': mock.patch('slumber.Resource'), 'api_v2.command': mock.patch( 'readthedocs.doc_builder.environments.api_v2.command', - mock.Mock(**{'get.return_value': {}})), + mock.Mock(**{'get.return_value': {}}), + ), 'api_v2.build': mock.patch( 'readthedocs.doc_builder.environments.api_v2.build', - mock.Mock(**{'get.return_value': {}})), + mock.Mock(**{'get.return_value': {}}), + ), 'api_versions': mock.patch( - 'readthedocs.projects.models.Project.api_versions'), + 'readthedocs.projects.models.Project.api_versions', + ), 'non_blocking_lock': mock.patch( - 'readthedocs.vcs_support.utils.NonBlockingLock.__enter__'), + 'readthedocs.vcs_support.utils.NonBlockingLock.__enter__', + ), 'append_conf': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.BaseSphinx.append_conf'), + 'readthedocs.doc_builder.backends.sphinx.BaseSphinx.append_conf', + ), 'move': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.BaseSphinx.move'), + 'readthedocs.doc_builder.backends.sphinx.BaseSphinx.move', + ), 'conf_dir': mock.patch( - 'readthedocs.projects.models.Project.conf_dir'), + 'readthedocs.projects.models.Project.conf_dir', + ), 'html_build': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.HtmlBuilder.build'), + 'readthedocs.doc_builder.backends.sphinx.HtmlBuilder.build', + ), 'html_move': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.HtmlBuilder.move'), + 'readthedocs.doc_builder.backends.sphinx.HtmlBuilder.move', + ), 'localmedia_build': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.LocalMediaBuilder.build'), + 'readthedocs.doc_builder.backends.sphinx.LocalMediaBuilder.build', + ), 'localmedia_move': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.LocalMediaBuilder.move'), + 'readthedocs.doc_builder.backends.sphinx.LocalMediaBuilder.move', + ), 'pdf_build': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.PdfBuilder.build'), + 'readthedocs.doc_builder.backends.sphinx.PdfBuilder.build', + ), 'pdf_move': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.PdfBuilder.move'), + 'readthedocs.doc_builder.backends.sphinx.PdfBuilder.move', + ), 'epub_build': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.EpubBuilder.build'), + 'readthedocs.doc_builder.backends.sphinx.EpubBuilder.build', + ), 'epub_move': mock.patch( - 'readthedocs.doc_builder.backends.sphinx.EpubBuilder.move'), + 'readthedocs.doc_builder.backends.sphinx.EpubBuilder.move', + ), 'move_mkdocs': mock.patch( - 'readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.move'), + 'readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.move', + ), 'append_conf_mkdocs': mock.patch( - 'readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.append_conf'), + 'readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.append_conf', + ), 'html_build_mkdocs': mock.patch( - 'readthedocs.doc_builder.backends.mkdocs.MkdocsHTML.build'), + 'readthedocs.doc_builder.backends.mkdocs.MkdocsHTML.build', + ), 'glob': mock.patch('readthedocs.doc_builder.backends.sphinx.glob'), 'docker': mock.patch('readthedocs.doc_builder.environments.APIClient'), diff --git a/readthedocs/rtd_tests/tests/projects/test_admin_actions.py b/readthedocs/rtd_tests/tests/projects/test_admin_actions.py index e1a9267ad68..dd25f4a13b4 100644 --- a/readthedocs/rtd_tests/tests/projects/test_admin_actions.py +++ b/readthedocs/rtd_tests/tests/projects/test_admin_actions.py @@ -35,7 +35,7 @@ def test_project_ban_owner(self): } resp = self.client.post( urls.reverse('admin:projects_project_changelist'), - action_data + action_data, ) self.assertTrue(self.project.users.filter(profile__banned=True).exists()) self.assertFalse(self.project.users.filter(profile__banned=False).exists()) @@ -53,7 +53,7 @@ def test_project_ban_multiple_owners(self): } resp = self.client.post( urls.reverse('admin:projects_project_changelist'), - action_data + action_data, ) self.assertFalse(self.project.users.filter(profile__banned=True).exists()) self.assertEqual(self.project.users.filter(profile__banned=False).count(), 2) @@ -70,11 +70,11 @@ def test_project_delete(self, broadcast): } resp = self.client.post( urls.reverse('admin:projects_project_changelist'), - action_data + action_data, ) self.assertFalse(Project.objects.filter(pk=self.project.pk).exists()) broadcast.assert_has_calls([ mock.call( - type='app', task=remove_dirs, args=[(self.project.doc_path,)] + type='app', task=remove_dirs, args=[(self.project.doc_path,)], ), ]) diff --git a/readthedocs/rtd_tests/tests/test_api.py b/readthedocs/rtd_tests/tests/test_api.py index d7c4ab06445..f5dcb1cae06 100644 --- a/readthedocs/rtd_tests/tests/test_api.py +++ b/readthedocs/rtd_tests/tests/test_api.py @@ -414,13 +414,13 @@ def test_get_raw_log_success(self): BuildCommandResult, build=build, command='python setup.py install', - output='Installing dependencies...' + output='Installing dependencies...', ) get( BuildCommandResult, build=build, command='git checkout master', - output='Switched to branch "master"' + output='Switched to branch "master"', ) client = APIClient() @@ -440,11 +440,11 @@ def test_get_raw_log_success(self): self.assertIn('[rtd-command-info]', resp.content.decode()) self.assertIn( 'python setup.py install\nInstalling dependencies...', - resp.content.decode() + resp.content.decode(), ) self.assertIn( 'git checkout master\nSwitched to branch "master"', - resp.content.decode() + resp.content.decode(), ) def test_get_raw_log_building(self): @@ -464,7 +464,7 @@ def test_get_raw_log_building(self): BuildCommandResult, build=build, command='git checkout master', - output='Switched to branch "master"' + output='Switched to branch "master"', ) client = APIClient() @@ -484,17 +484,17 @@ def test_get_raw_log_building(self): self.assertIn('[rtd-command-info]', resp.content.decode()) self.assertIn( 'python setup.py install\nInstalling dependencies...', - resp.content.decode() + resp.content.decode(), ) self.assertIn( 'git checkout master\nSwitched to branch "master"', - resp.content.decode() + resp.content.decode(), ) def test_get_raw_log_failure(self): build = get( Build, project_id=1, version_id=1, - builder='foo', success=False, exit_code=1 + builder='foo', success=False, exit_code=1, ) get( BuildCommandResult, @@ -507,7 +507,7 @@ def test_get_raw_log_failure(self): BuildCommandResult, build=build, command='git checkout master', - output='Switched to branch "master"' + output='Switched to branch "master"', ) client = APIClient() @@ -527,11 +527,11 @@ def test_get_raw_log_failure(self): self.assertIn('[rtd-command-info]', resp.content.decode()) self.assertIn( 'python setup.py install\nInstalling dependencies...', - resp.content.decode() + resp.content.decode(), ) self.assertIn( 'git checkout master\nSwitched to branch "master"', - resp.content.decode() + resp.content.decode(), ) def test_get_invalid_raw_log(self): @@ -814,11 +814,11 @@ def setUp(self): self.project = get(Project) self.version = get( Version, slug='master', verbose_name='master', - active=True, project=self.project + active=True, project=self.project, ) self.version_tag = get( Version, slug='v1.0', verbose_name='v1.0', - active=True, project=self.project + active=True, project=self.project, ) self.github_payload = { 'ref': 'master', @@ -870,7 +870,8 @@ def test_github_webhook_for_branches(self, trigger_build): format='json', ) trigger_build.assert_has_calls( - [mock.call(force=True, version=self.version, project=self.project)]) + [mock.call(force=True, version=self.version, project=self.project)], + ) client.post( '/api/v2/webhook/github/{}/'.format(self.project.slug), @@ -878,7 +879,8 @@ def test_github_webhook_for_branches(self, trigger_build): format='json', ) trigger_build.assert_has_calls( - [mock.call(force=True, version=mock.ANY, project=self.project)]) + [mock.call(force=True, version=mock.ANY, project=self.project)], + ) client.post( '/api/v2/webhook/github/{}/'.format(self.project.slug), @@ -886,7 +888,8 @@ def test_github_webhook_for_branches(self, trigger_build): format='json', ) trigger_build.assert_has_calls( - [mock.call(force=True, version=self.version, project=self.project)]) + [mock.call(force=True, version=self.version, project=self.project)], + ) def test_github_webhook_for_tags(self, trigger_build): """GitHub webhook API.""" @@ -898,7 +901,8 @@ def test_github_webhook_for_tags(self, trigger_build): format='json', ) trigger_build.assert_has_calls( - [mock.call(force=True, version=self.version_tag, project=self.project)]) + [mock.call(force=True, version=self.version_tag, project=self.project)], + ) client.post( '/api/v2/webhook/github/{}/'.format(self.project.slug), @@ -906,7 +910,8 @@ def test_github_webhook_for_tags(self, trigger_build): format='json', ) trigger_build.assert_has_calls( - [mock.call(force=True, version=mock.ANY, project=self.project)]) + [mock.call(force=True, version=mock.ANY, project=self.project)], + ) client.post( '/api/v2/webhook/github/{}/'.format(self.project.slug), @@ -914,7 +919,8 @@ def test_github_webhook_for_tags(self, trigger_build): format='json', ) trigger_build.assert_has_calls( - [mock.call(force=True, version=self.version_tag, project=self.project)]) + [mock.call(force=True, version=self.version_tag, project=self.project)], + ) @mock.patch('readthedocs.core.views.hooks.sync_repository_task') def test_github_create_event(self, sync_repository_task, trigger_build): @@ -985,7 +991,7 @@ def test_gitlab_webhook_for_branches(self, trigger_build): format='json', ) trigger_build.assert_called_with( - force=True, version=mock.ANY, project=self.project + force=True, version=mock.ANY, project=self.project, ) trigger_build.reset_mock() @@ -1011,7 +1017,7 @@ def test_gitlab_webhook_for_tags(self, trigger_build): format='json', ) trigger_build.assert_called_with( - force=True, version=self.version_tag, project=self.project + force=True, version=self.version_tag, project=self.project, ) trigger_build.reset_mock() @@ -1024,7 +1030,7 @@ def test_gitlab_webhook_for_tags(self, trigger_build): format='json', ) trigger_build.assert_called_with( - force=True, version=self.version_tag, project=self.project + force=True, version=self.version_tag, project=self.project, ) trigger_build.reset_mock() @@ -1040,7 +1046,8 @@ def test_gitlab_webhook_for_tags(self, trigger_build): @mock.patch('readthedocs.core.views.hooks.sync_repository_task') def test_gitlab_push_hook_creation( - self, sync_repository_task, trigger_build): + self, sync_repository_task, trigger_build, + ): client = APIClient() self.gitlab_payload.update( before=GITLAB_NULL_HASH, @@ -1061,7 +1068,8 @@ def test_gitlab_push_hook_creation( @mock.patch('readthedocs.core.views.hooks.sync_repository_task') def test_gitlab_push_hook_deletion( - self, sync_repository_task, trigger_build): + self, sync_repository_task, trigger_build, + ): client = APIClient() self.gitlab_payload.update( before='95790bf891e76fee5e1747ab589903a6a1f80f22', @@ -1082,7 +1090,8 @@ def test_gitlab_push_hook_deletion( @mock.patch('readthedocs.core.views.hooks.sync_repository_task') def test_gitlab_tag_push_hook_creation( - self, sync_repository_task, trigger_build): + self, sync_repository_task, trigger_build, + ): client = APIClient() self.gitlab_payload.update( object_kind=GITLAB_TAG_PUSH, @@ -1104,7 +1113,8 @@ def test_gitlab_tag_push_hook_creation( @mock.patch('readthedocs.core.views.hooks.sync_repository_task') def test_gitlab_tag_push_hook_deletion( - self, sync_repository_task, trigger_build): + self, sync_repository_task, trigger_build, + ): client = APIClient() self.gitlab_payload.update( object_kind=GITLAB_TAG_PUSH, @@ -1144,7 +1154,8 @@ def test_bitbucket_webhook(self, trigger_build): format='json', ) trigger_build.assert_has_calls( - [mock.call(force=True, version=mock.ANY, project=self.project)]) + [mock.call(force=True, version=mock.ANY, project=self.project)], + ) client.post( '/api/v2/webhook/bitbucket/{}/'.format(self.project.slug), { @@ -1160,7 +1171,8 @@ def test_bitbucket_webhook(self, trigger_build): format='json', ) trigger_build.assert_has_calls( - [mock.call(force=True, version=mock.ANY, project=self.project)]) + [mock.call(force=True, version=mock.ANY, project=self.project)], + ) trigger_build_call_count = trigger_build.call_count client.post( @@ -1180,7 +1192,8 @@ def test_bitbucket_webhook(self, trigger_build): @mock.patch('readthedocs.core.views.hooks.sync_repository_task') def test_bitbucket_push_hook_creation( - self, sync_repository_task, trigger_build): + self, sync_repository_task, trigger_build, + ): client = APIClient() self.bitbucket_payload['push']['changes'][0]['old'] = None resp = client.post( @@ -1198,7 +1211,8 @@ def test_bitbucket_push_hook_creation( @mock.patch('readthedocs.core.views.hooks.sync_repository_task') def test_bitbucket_push_hook_deletion( - self, sync_repository_task, trigger_build): + self, sync_repository_task, trigger_build, + ): client = APIClient() self.bitbucket_payload['push']['changes'][0]['new'] = None resp = client.post( @@ -1219,7 +1233,8 @@ def test_bitbucket_invalid_webhook(self, trigger_build): client = APIClient() resp = client.post( '/api/v2/webhook/bitbucket/{}/'.format(self.project.slug), - {'foo': 'bar'}, format='json', HTTP_X_EVENT_KEY='pull_request') + {'foo': 'bar'}, format='json', HTTP_X_EVENT_KEY='pull_request', + ) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.data['detail'], 'Unhandled webhook event') @@ -1283,7 +1298,8 @@ def test_generic_api_falls_back_to_token_auth(self, trigger_build): user = get(User) client.force_authenticate(user=user) integration = Integration.objects.create( - project=self.project, integration_type=Integration.API_WEBHOOK) + project=self.project, integration_type=Integration.API_WEBHOOK, + ) self.assertIsNotNone(integration.token) resp = client.post( '/api/v2/webhook/{}/{}/'.format( @@ -1484,7 +1500,7 @@ def test_get_active_versions(self): }) url = '{base_url}?{querystring}'.format( base_url=reverse('version-list'), - querystring=data.urlencode() + querystring=data.urlencode(), ) resp = self.client.get(url, content_type='application/json') @@ -1498,7 +1514,7 @@ def test_get_active_versions(self): }) url = '{base_url}?{querystring}'.format( base_url=reverse('version-list'), - querystring=data.urlencode() + querystring=data.urlencode(), ) resp = self.client.get(url, content_type='application/json') @@ -1515,11 +1531,13 @@ def test_get_status_data(self): {'data': 'public'}, 'Something bad happened', ) - self.assertEqual(data, { - 'name': 'public_task_exception', - 'data': {'data': 'public'}, - 'started': True, - 'finished': True, - 'success': False, - 'error': 'Something bad happened', - }) + self.assertEqual( + data, { + 'name': 'public_task_exception', + 'data': {'data': 'public'}, + 'started': True, + 'finished': True, + 'success': False, + 'error': 'Something bad happened', + }, + ) diff --git a/readthedocs/rtd_tests/tests/test_api_permissions.py b/readthedocs/rtd_tests/tests/test_api_permissions.py index 5a7d10a0a54..7367d8f5d6a 100644 --- a/readthedocs/rtd_tests/tests/test_api_permissions.py +++ b/readthedocs/rtd_tests/tests/test_api_permissions.py @@ -18,23 +18,27 @@ def assertAllow(self, handler, method, is_admin, obj=None): if obj is None: self.assertTrue(handler.has_permission( request=self.get_request(method, is_admin=is_admin), - view=None)) + view=None, + )) else: self.assertTrue(handler.has_object_permission( request=self.get_request(method, is_admin=is_admin), view=None, - obj=obj)) + obj=obj, + )) def assertDisallow(self, handler, method, is_admin, obj=None): if obj is None: self.assertFalse(handler.has_permission( request=self.get_request(method, is_admin=is_admin), - view=None)) + view=None, + )) else: self.assertFalse(handler.has_object_permission( request=self.get_request(method, is_admin=is_admin), view=None, - obj=obj)) + obj=obj, + )) def test_non_object_permissions(self): handler = APIRestrictedPermission() diff --git a/readthedocs/rtd_tests/tests/test_backend.py b/readthedocs/rtd_tests/tests/test_backend.py index 8b31de91306..5618dd92151 100644 --- a/readthedocs/rtd_tests/tests/test_backend.py +++ b/readthedocs/rtd_tests/tests/test_backend.py @@ -33,7 +33,7 @@ def setUp(self): name='Test Project', repo_type='git', #Our top-level checkout - repo=git_repo + repo=git_repo, ) self.project.users.add(self.eric) self.dummy_conf = Mock() @@ -116,7 +116,7 @@ def test_git_checkout_invalid_revision(self): repo.checkout(version) self.assertEqual( str(e.exception), - RepositoryError.FAILED_TO_CHECKOUT.format(version) + RepositoryError.FAILED_TO_CHECKOUT.format(version), ) def test_git_tags(self): @@ -186,7 +186,7 @@ def test_check_invalid_submodule_urls(self): # it's a url in ssh form. self.assertEqual( str(e.exception), - RepositoryError.INVALID_SUBMODULES.format(['invalid']) + RepositoryError.INVALID_SUBMODULES.format(['invalid']), ) @patch('readthedocs.projects.models.Project.checkout_path') @@ -209,13 +209,13 @@ def test_fetch_clean_tags_and_branches(self, checkout_path): # We still have all branches and tags in the local repo self.assertEqual( {'v01', 'v02'}, - {vcs.verbose_name for vcs in repo.tags} + {vcs.verbose_name for vcs in repo.tags}, ) self.assertEqual( { 'invalidsubmodule', 'master', 'submodule', 'newbranch', }, - {vcs.verbose_name for vcs in repo.branches} + {vcs.verbose_name for vcs in repo.branches}, ) repo.update() @@ -223,13 +223,13 @@ def test_fetch_clean_tags_and_branches(self, checkout_path): # We don't have the eliminated branches and tags in the local repo self.assertEqual( {'v01'}, - {vcs.verbose_name for vcs in repo.tags} + {vcs.verbose_name for vcs in repo.tags}, ) self.assertEqual( { - 'invalidsubmodule', 'master', 'submodule' + 'invalidsubmodule', 'master', 'submodule', }, - {vcs.verbose_name for vcs in repo.branches} + {vcs.verbose_name for vcs in repo.branches}, ) @@ -245,7 +245,7 @@ def setUp(self): name='Test Project', repo_type='hg', # Our top-level checkout - repo=hg_repo + repo=hg_repo, ) self.project.users.add(self.eric) @@ -276,7 +276,7 @@ def test_checkout_invalid_revision(self): repo.checkout(version) self.assertEqual( str(e.exception), - RepositoryError.FAILED_TO_CHECKOUT.format(version) + RepositoryError.FAILED_TO_CHECKOUT.format(version), ) def test_parse_tags(self): diff --git a/readthedocs/rtd_tests/tests/test_build_config.py b/readthedocs/rtd_tests/tests/test_build_config.py index f1129407b61..a0fd290b327 100644 --- a/readthedocs/rtd_tests/tests/test_build_config.py +++ b/readthedocs/rtd_tests/tests/test_build_config.py @@ -10,7 +10,7 @@ V2_SCHEMA = path.join( path.dirname(__file__), - '../fixtures/spec/v2/schema.yml' + '../fixtures/spec/v2/schema.yml', ) @@ -30,7 +30,7 @@ def _is_valid(self, value): if isinstance(value, str): file_ = path.join( path.dirname(self.configuration_file), - value + value, ) return path.exists(file_) return False @@ -58,7 +58,7 @@ def validate_schema(file): data = yamale.make_data(file) schema = yamale.make_schema( V2_SCHEMA, - validators=validators + validators=validators, ) yamale.validate(schema, data) @@ -84,7 +84,7 @@ def test_invalid_version(tmpdir): assertInvalidConfig( tmpdir, 'version: "latest"', - ['version:', "'latest' not in"] + ['version:', "'latest' not in"], ) @@ -92,7 +92,7 @@ def test_invalid_version_1(tmpdir): assertInvalidConfig( tmpdir, 'version: "1"', - ['version', "'1' not in"] + ['version', "'1' not in"], ) @@ -134,7 +134,7 @@ def test_formats_invalid(tmpdir): assertInvalidConfig( tmpdir, content, - ['formats', "'invalidformat' not in"] + ['formats', "'invalidformat' not in"], ) @@ -164,7 +164,7 @@ def test_conda_invalid(tmpdir): assertInvalidConfig( tmpdir, content, - ['environment.yaml', 'is not a path'] + ['environment.yaml', 'is not a path'], ) @@ -177,7 +177,7 @@ def test_conda_missing_key(tmpdir): assertInvalidConfig( tmpdir, content, - ['conda.environment: Required'] + ['conda.environment: Required'], ) @@ -209,7 +209,7 @@ def test_build_invalid(tmpdir): assertInvalidConfig( tmpdir, content, - ["build.image: '9.0' not in"] + ["build.image: '9.0' not in"], ) @@ -232,7 +232,7 @@ def test_python_version_invalid(tmpdir): assertInvalidConfig( tmpdir, content, - ["version: '4' not in"] + ["version: '4' not in"], ) @@ -265,7 +265,7 @@ def test_python_install_requirements(tmpdir): assertInvalidConfig( tmpdir, content, - ['requirements:', "'23' is not a path"] + ['requirements:', "'23' is not a path"], ) @@ -291,7 +291,7 @@ def test_python_install_invalid(tmpdir): assertInvalidConfig( tmpdir, content, - ["python.install: 'guido' is not a list"] + ["python.install: 'guido' is not a list"], ) @@ -331,13 +331,15 @@ def test_python_install_extra_requirements_empty(tmpdir, value): @pytest.mark.parametrize('pipfile', ['another_docs/', '.', 'project/']) def test_python_install_pipfile(tmpdir, pipfile): - utils.apply_fs(tmpdir, { - 'another_docs': { + utils.apply_fs( + tmpdir, { + 'another_docs': { + 'Pipfile': '', + }, + 'project': {}, 'Pipfile': '', }, - 'project': {}, - 'Pipfile': '', - }) + ) content = ''' version: "2" python: @@ -416,7 +418,7 @@ def test_python_system_packages_invalid(tmpdir, value): assertInvalidConfig( tmpdir, content.format(value=value), - ['is not a bool'] + ['is not a bool'], ) @@ -448,7 +450,7 @@ def test_sphinx_invalid(tmpdir, value): assertInvalidConfig( tmpdir, content, - ['is not a path'] + ['is not a path'], ) @@ -471,7 +473,7 @@ def test_sphinx_fail_on_warning_invalid(tmpdir, value): assertInvalidConfig( tmpdir, content.format(value=value), - ['is not a bool'] + ['is not a bool'], ) @@ -503,7 +505,7 @@ def test_mkdocs_invalid(tmpdir, value): assertInvalidConfig( tmpdir, content, - ['is not a path'] + ['is not a path'], ) @@ -526,7 +528,7 @@ def test_mkdocs_fail_on_warning_invalid(tmpdir, value): assertInvalidConfig( tmpdir, content.format(value=value), - ['is not a bool'] + ['is not a bool'], ) @@ -594,7 +596,7 @@ def test_redirects_invalid(tmpdir): assertInvalidConfig( tmpdir, content, - ['is not a str'] + ['is not a str'], ) diff --git a/readthedocs/rtd_tests/tests/test_build_forms.py b/readthedocs/rtd_tests/tests/test_build_forms.py index f85ae741974..1e901771632 100644 --- a/readthedocs/rtd_tests/tests/test_build_forms.py +++ b/readthedocs/rtd_tests/tests/test_build_forms.py @@ -28,7 +28,7 @@ def test_default_version_is_active(self): 'active': True, 'privacy_level': PRIVATE, }, - instance=version + instance=version, ) self.assertTrue(form.is_valid()) @@ -46,7 +46,7 @@ def test_default_version_is_inactive(self): 'active': False, 'privacy_level': PRIVATE, }, - instance=version + instance=version, ) self.assertFalse(form.is_valid()) self.assertIn('active', form.errors) diff --git a/readthedocs/rtd_tests/tests/test_build_notifications.py b/readthedocs/rtd_tests/tests/test_build_notifications.py index 4964f32c78d..81acedb013c 100644 --- a/readthedocs/rtd_tests/tests/test_build_notifications.py +++ b/readthedocs/rtd_tests/tests/test_build_notifications.py @@ -71,7 +71,11 @@ def test_webhook_form_url_length(self): self.assertFalse(form.is_valid()) self.assertEqual( form.errors, - {'url': - ['Enter a valid URL.', - 'Ensure this value has at most 600 characters (it has 1507).'] - }) + { + 'url': + [ + 'Enter a valid URL.', + 'Ensure this value has at most 600 characters (it has 1507).', + ], + }, + ) diff --git a/readthedocs/rtd_tests/tests/test_builds.py b/readthedocs/rtd_tests/tests/test_builds.py index c5611780483..afdbe46a3c9 100644 --- a/readthedocs/rtd_tests/tests/test_builds.py +++ b/readthedocs/rtd_tests/tests/test_builds.py @@ -29,16 +29,20 @@ def tearDown(self): def test_build(self, load_config): """Test full build.""" load_config.side_effect = create_load() - project = get(Project, - slug='project-1', - documentation_type='sphinx', - conf_py_file='test_conf.py', - versions=[fixture()]) + project = get( + Project, + slug='project-1', + documentation_type='sphinx', + conf_py_file='test_conf.py', + versions=[fixture()], + ) version = project.versions.all()[0] self.mocks.configure_mock('api_versions', {'return_value': [version]}) - self.mocks.configure_mock('api', { - 'get.return_value': {'downloads': 'no_url_here'} - }) + self.mocks.configure_mock( + 'api', { + 'get.return_value': {'downloads': 'no_url_here'}, + }, + ) self.mocks.patches['html_build'].stop() build_env = LocalBuildEnvironment(project=project, version=version, build={}) @@ -46,7 +50,7 @@ def test_build(self, load_config): config = load_yaml_config(version) task = UpdateDocsTaskStep( build_env=build_env, project=project, python_env=python_env, - version=version, config=config + version=version, config=config, ) task.build_docs() @@ -60,13 +64,15 @@ def test_build(self, load_config): def test_build_respects_pdf_flag(self, load_config): """Build output format control.""" load_config.side_effect = create_load() - project = get(Project, - slug='project-1', - documentation_type='sphinx', - conf_py_file='test_conf.py', - enable_pdf_build=True, - enable_epub_build=False, - versions=[fixture()]) + project = get( + Project, + slug='project-1', + documentation_type='sphinx', + conf_py_file='test_conf.py', + enable_pdf_build=True, + enable_epub_build=False, + versions=[fixture()], + ) version = project.versions.all()[0] build_env = LocalBuildEnvironment(project=project, version=version, build={}) @@ -74,7 +80,7 @@ def test_build_respects_pdf_flag(self, load_config): config = load_yaml_config(version) task = UpdateDocsTaskStep( build_env=build_env, project=project, python_env=python_env, - version=version, config=config + version=version, config=config, ) task.build_docs() @@ -94,20 +100,20 @@ def test_dont_localmedia_build_pdf_epub_search_in_mkdocs(self, load_config): documentation_type='mkdocs', enable_pdf_build=True, enable_epub_build=True, - versions=[fixture()] + versions=[fixture()], ) version = project.versions.all().first() build_env = LocalBuildEnvironment( project=project, version=version, - build={} + build={}, ) python_env = Virtualenv(version=version, build_env=build_env) config = load_yaml_config(version) task = UpdateDocsTaskStep( build_env=build_env, project=project, python_env=python_env, - version=version, config=config + version=version, config=config, ) task.build_docs() @@ -123,13 +129,15 @@ def test_dont_localmedia_build_pdf_epub_search_in_mkdocs(self, load_config): def test_build_respects_epub_flag(self, load_config): """Test build with epub enabled.""" load_config.side_effect = create_load() - project = get(Project, - slug='project-1', - documentation_type='sphinx', - conf_py_file='test_conf.py', - enable_pdf_build=False, - enable_epub_build=True, - versions=[fixture()]) + project = get( + Project, + slug='project-1', + documentation_type='sphinx', + conf_py_file='test_conf.py', + enable_pdf_build=False, + enable_epub_build=True, + versions=[fixture()], + ) version = project.versions.all()[0] build_env = LocalBuildEnvironment(project=project, version=version, build={}) @@ -137,7 +145,7 @@ def test_build_respects_epub_flag(self, load_config): config = load_yaml_config(version) task = UpdateDocsTaskStep( build_env=build_env, project=project, python_env=python_env, - version=version, config=config + version=version, config=config, ) task.build_docs() @@ -151,13 +159,15 @@ def test_build_respects_epub_flag(self, load_config): def test_build_respects_yaml(self, load_config): """Test YAML build options.""" load_config.side_effect = create_load({'formats': ['epub']}) - project = get(Project, - slug='project-1', - documentation_type='sphinx', - conf_py_file='test_conf.py', - enable_pdf_build=False, - enable_epub_build=False, - versions=[fixture()]) + project = get( + Project, + slug='project-1', + documentation_type='sphinx', + conf_py_file='test_conf.py', + enable_pdf_build=False, + enable_epub_build=False, + versions=[fixture()], + ) version = project.versions.all()[0] build_env = LocalBuildEnvironment(project=project, version=version, build={}) @@ -166,7 +176,7 @@ def test_build_respects_yaml(self, load_config): config = load_yaml_config(version) task = UpdateDocsTaskStep( build_env=build_env, project=project, python_env=python_env, - version=version, config=config + version=version, config=config, ) task.build_docs() @@ -184,13 +194,15 @@ def test_build_pdf_latex_failures(self, load_config): self.mocks.patches['html_build'].stop() self.mocks.patches['pdf_build'].stop() - project = get(Project, - slug='project-1', - documentation_type='sphinx', - conf_py_file='test_conf.py', - enable_pdf_build=True, - enable_epub_build=False, - versions=[fixture()]) + project = get( + Project, + slug='project-1', + documentation_type='sphinx', + conf_py_file='test_conf.py', + enable_pdf_build=True, + enable_epub_build=False, + versions=[fixture()], + ) version = project.versions.all()[0] assert project.conf_dir() == '/tmp/rtd' @@ -199,7 +211,7 @@ def test_build_pdf_latex_failures(self, load_config): config = load_yaml_config(version) task = UpdateDocsTaskStep( build_env=build_env, project=project, python_env=python_env, - version=version, config=config + version=version, config=config, ) # Mock out the separate calls to Popen using an iterable side_effect @@ -211,10 +223,13 @@ def test_build_pdf_latex_failures(self, load_config): ((b'', b''), 0), # latex ] mock_obj = mock.Mock() - mock_obj.communicate.side_effect = [output for (output, status) - in returns] + mock_obj.communicate.side_effect = [ + output for (output, status) + in returns + ] type(mock_obj).returncode = mock.PropertyMock( - side_effect=[status for (output, status) in returns]) + side_effect=[status for (output, status) in returns], + ) self.mocks.popen.return_value = mock_obj with build_env: @@ -230,13 +245,15 @@ def test_build_pdf_latex_not_failure(self, load_config): self.mocks.patches['html_build'].stop() self.mocks.patches['pdf_build'].stop() - project = get(Project, - slug='project-2', - documentation_type='sphinx', - conf_py_file='test_conf.py', - enable_pdf_build=True, - enable_epub_build=False, - versions=[fixture()]) + project = get( + Project, + slug='project-2', + documentation_type='sphinx', + conf_py_file='test_conf.py', + enable_pdf_build=True, + enable_epub_build=False, + versions=[fixture()], + ) version = project.versions.all()[0] assert project.conf_dir() == '/tmp/rtd' @@ -245,7 +262,7 @@ def test_build_pdf_latex_not_failure(self, load_config): config = load_yaml_config(version) task = UpdateDocsTaskStep( build_env=build_env, project=project, python_env=python_env, - version=version, config=config + version=version, config=config, ) # Mock out the separate calls to Popen using an iterable side_effect @@ -257,10 +274,13 @@ def test_build_pdf_latex_not_failure(self, load_config): ((b'', b''), 0), # latex ] mock_obj = mock.Mock() - mock_obj.communicate.side_effect = [output for (output, status) - in returns] + mock_obj.communicate.side_effect = [ + output for (output, status) + in returns + ] type(mock_obj).returncode = mock.PropertyMock( - side_effect=[status for (output, status) in returns]) + side_effect=[status for (output, status) in returns], + ) self.mocks.popen.return_value = mock_obj with build_env: @@ -281,7 +301,7 @@ def test_save_config_in_build_model(self, load_config, api_v2): build = get(Build) version = get(Version, slug='1.8', project=project) task = UpdateDocsTaskStep( - project=project, version=version, build={'id': build.pk} + project=project, version=version, build={'id': build.pk}, ) task.setup_vcs = mock.Mock() task.run_setup() @@ -353,13 +373,13 @@ def test_get_previous_build(self): Build, project=self.project, version=self.version, - config={'version': 1} + config={'version': 1}, ) build_two = get( Build, project=self.project, version=self.version, - config={'version': 2} + config={'version': 2}, ) build_three = get( Build, diff --git a/readthedocs/rtd_tests/tests/test_celery.py b/readthedocs/rtd_tests/tests/test_celery.py index 48613710a1e..5ac9fac8883 100644 --- a/readthedocs/rtd_tests/tests/test_celery.py +++ b/readthedocs/rtd_tests/tests/test_celery.py @@ -78,14 +78,17 @@ def test_clear_artifacts(self): @patch('readthedocs.projects.tasks.UpdateDocsTaskStep.build_docs', new=MagicMock) @patch('readthedocs.projects.tasks.UpdateDocsTaskStep.setup_vcs', new=MagicMock) def test_update_docs(self): - build = get(Build, project=self.project, - version=self.project.versions.first()) + build = get( + Build, project=self.project, + version=self.project.versions.first(), + ) with mock_api(self.repo) as mapi: result = tasks.update_docs_task.delay( self.project.pk, build_pk=build.pk, record=False, - intersphinx=False) + intersphinx=False, + ) self.assertTrue(result.successful()) @patch('readthedocs.projects.tasks.UpdateDocsTaskStep.setup_python_environment', new=MagicMock) @@ -95,14 +98,17 @@ def test_update_docs(self): def test_update_docs_unexpected_setup_exception(self, mock_setup_vcs): exc = Exception() mock_setup_vcs.side_effect = exc - build = get(Build, project=self.project, - version=self.project.versions.first()) + build = get( + Build, project=self.project, + version=self.project.versions.first(), + ) with mock_api(self.repo) as mapi: result = tasks.update_docs_task.delay( self.project.pk, build_pk=build.pk, record=False, - intersphinx=False) + intersphinx=False, + ) self.assertTrue(result.successful()) @patch('readthedocs.projects.tasks.UpdateDocsTaskStep.setup_python_environment', new=MagicMock) @@ -112,14 +118,17 @@ def test_update_docs_unexpected_setup_exception(self, mock_setup_vcs): def test_update_docs_unexpected_build_exception(self, mock_build_docs): exc = Exception() mock_build_docs.side_effect = exc - build = get(Build, project=self.project, - version=self.project.versions.first()) + build = get( + Build, project=self.project, + version=self.project.versions.first(), + ) with mock_api(self.repo) as mapi: result = tasks.update_docs_task.delay( self.project.pk, build_pk=build.pk, record=False, - intersphinx=False) + intersphinx=False, + ) self.assertTrue(result.successful()) @patch('readthedocs.projects.tasks.UpdateDocsTaskStep.setup_python_environment', new=MagicMock) @@ -129,14 +138,17 @@ def test_update_docs_unexpected_build_exception(self, mock_build_docs): def test_no_notification_on_version_locked_error(self, mock_setup_vcs, mock_send_notifications): mock_setup_vcs.side_effect = VersionLockedError() - build = get(Build, project=self.project, - version=self.project.versions.first()) + build = get( + Build, project=self.project, + version=self.project.versions.first(), + ) with mock_api(self.repo) as mapi: result = tasks.update_docs_task.delay( self.project.pk, build_pk=build.pk, record=False, - intersphinx=False) + intersphinx=False, + ) mock_send_notifications.assert_not_called() self.assertTrue(result.successful()) @@ -166,7 +178,7 @@ def test_check_duplicate_reserved_version_latest(self, checkout_path, api_v2): sync_repository.sync_repo() self.assertEqual( str(e.exception), - RepositoryError.DUPLICATED_RESERVED_VERSIONS + RepositoryError.DUPLICATED_RESERVED_VERSIONS, ) delete_git_branch(self.repo, 'latest') @@ -192,7 +204,7 @@ def test_check_duplicate_reserved_version_stable(self, checkout_path, api_v2): sync_repository.sync_repo() self.assertEqual( str(e.exception), - RepositoryError.DUPLICATED_RESERVED_VERSIONS + RepositoryError.DUPLICATED_RESERVED_VERSIONS, ) # TODO: Check that we can build properly after @@ -230,9 +242,11 @@ def public_task_exception(): # although the task risen an exception, it's success since we add the # exception into the ``info`` attributes self.assertEqual(result.status, 'SUCCESS') - self.assertEqual(result.info, { - 'task_name': 'public_task_exception', - 'context': {}, - 'public_data': {}, - 'error': 'Something bad happened', - }) + self.assertEqual( + result.info, { + 'task_name': 'public_task_exception', + 'context': {}, + 'public_data': {}, + 'error': 'Something bad happened', + }, + ) diff --git a/readthedocs/rtd_tests/tests/test_config_integration.py b/readthedocs/rtd_tests/tests/test_config_integration.py index 2df44a08eef..e09e82199bb 100644 --- a/readthedocs/rtd_tests/tests/test_config_integration.py +++ b/readthedocs/rtd_tests/tests/test_config_integration.py @@ -90,7 +90,7 @@ def test_python_supported_versions_default_image_1_0(self, load_config): 'formats': [ 'htmlzip', 'epub', - 'pdf' + 'pdf', ], 'use_system_packages': self.project.use_system_packages, 'requirements_file': self.project.requirements_file, @@ -110,8 +110,10 @@ def test_python_supported_versions_image_1_0(self, load_config): self.project.container_image = 'readthedocs/build:1.0' self.project.save() config = load_yaml_config(self.version) - self.assertEqual(config.get_valid_python_versions(), - [2, 2.7, 3, 3.4]) + self.assertEqual( + config.get_valid_python_versions(), + [2, 2.7, 3, 3.4], + ) @mock.patch('readthedocs.doc_builder.config.load_config') def test_python_supported_versions_image_2_0(self, load_config): @@ -119,8 +121,10 @@ def test_python_supported_versions_image_2_0(self, load_config): self.project.container_image = 'readthedocs/build:2.0' self.project.save() config = load_yaml_config(self.version) - self.assertEqual(config.get_valid_python_versions(), - [2, 2.7, 3, 3.5]) + self.assertEqual( + config.get_valid_python_versions(), + [2, 2.7, 3, 3.5], + ) @mock.patch('readthedocs.doc_builder.config.load_config') def test_python_supported_versions_image_latest(self, load_config): @@ -128,8 +132,10 @@ def test_python_supported_versions_image_latest(self, load_config): self.project.container_image = 'readthedocs/build:latest' self.project.save() config = load_yaml_config(self.version) - self.assertEqual(config.get_valid_python_versions(), - [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]) + self.assertEqual( + config.get_valid_python_versions(), + [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6], + ) @mock.patch('readthedocs.doc_builder.config.load_config') def test_python_default_version(self, load_config): @@ -162,7 +168,7 @@ def test_python_set_python_version_in_config(self, load_config): @mock.patch('readthedocs.doc_builder.config.load_config') def test_python_invalid_version_in_config(self, load_config): load_config.side_effect = create_load({ - 'python': {'version': 2.6} + 'python': {'version': 2.6}, }) self.project.container_image = 'readthedocs/build:2.0' self.project.save() @@ -175,11 +181,11 @@ def test_install_project(self, load_config): config = load_yaml_config(self.version) self.assertEqual( config.python.install_with_pip or config.python.install_with_setup, - False + False, ) load_config.side_effect = create_load({ - 'python': {'setup_py_install': True} + 'python': {'setup_py_install': True}, }) config = load_yaml_config(self.version) self.assertEqual(config.python.install_with_setup, True) @@ -189,16 +195,16 @@ def test_extra_requirements(self, load_config): load_config.side_effect = create_load({ 'python': { 'pip_install': True, - 'extra_requirements': ['tests', 'docs'] - } + 'extra_requirements': ['tests', 'docs'], + }, }) config = load_yaml_config(self.version) self.assertEqual(config.python.extra_requirements, ['tests', 'docs']) load_config.side_effect = create_load({ 'python': { - 'extra_requirements': ['tests', 'docs'] - } + 'extra_requirements': ['tests', 'docs'], + }, }) config = load_yaml_config(self.version) self.assertEqual(config.python.extra_requirements, []) @@ -210,8 +216,8 @@ def test_extra_requirements(self, load_config): load_config.side_effect = create_load({ 'python': { 'setup_py_install': True, - 'extra_requirements': ['tests', 'docs'] - } + 'extra_requirements': ['tests', 'docs'], + }, }) config = load_yaml_config(self.version) self.assertEqual(config.python.extra_requirements, []) @@ -228,7 +234,7 @@ def test_conda_with_cofig(self, checkout_path): { 'conda': { 'file': conda_file, - } + }, }, base_path=base_path, ) @@ -302,9 +308,11 @@ def create_project(self): ) def create_config_file(self, tmpdir, config): - base_path = apply_fs(tmpdir, { - 'readthedocs.yml': '', - }) + base_path = apply_fs( + tmpdir, { + 'readthedocs.yml': '', + }, + ) config.setdefault('version', 2) config_file = path.join(str(base_path), 'readthedocs.yml') yaml.safe_dump(config, open(config_file, 'w')) @@ -312,7 +320,7 @@ def create_config_file(self, tmpdir, config): def get_update_docs_task(self): build_env = LocalBuildEnvironment( - self.project, self.version, record=False + self.project, self.version, record=False, ) update_docs = tasks.UpdateDocsTaskStep( @@ -341,7 +349,8 @@ def test_report_using_invalid_version(self, checkout_path, tmpdir): @patch('readthedocs.doc_builder.backends.sphinx.HtmlBuilder.build') @patch('readthedocs.doc_builder.backends.sphinx.HtmlBuilder.append_conf') def test_build_formats_default_empty( - self, append_conf, html_build, checkout_path, config, tmpdir): + self, append_conf, html_build, checkout_path, config, tmpdir, + ): """ The default value for formats is [], which means no extra formats are build. @@ -353,7 +362,7 @@ def test_build_formats_default_empty( python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=update_docs.config + config=update_docs.config, ) update_docs.python_env = python_env outcomes = update_docs.build_docs() @@ -370,7 +379,8 @@ def test_build_formats_default_empty( @patch('readthedocs.doc_builder.backends.sphinx.HtmlBuilder.append_conf') def test_build_formats_only_pdf( self, append_conf, html_build, build_docs_class, - checkout_path, tmpdir): + checkout_path, tmpdir, + ): """Only the pdf format is build.""" checkout_path.return_value = str(tmpdir) self.create_config_file(tmpdir, {'formats': ['pdf']}) @@ -379,7 +389,7 @@ def test_build_formats_only_pdf( python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=update_docs.config + config=update_docs.config, ) update_docs.python_env = python_env @@ -403,8 +413,8 @@ def test_conda_environment(self, build_failed, checkout_path, tmpdir): base_path = self.create_config_file( tmpdir, { - 'conda': {'environment': conda_file} - } + 'conda': {'environment': conda_file}, + }, ) update_docs = self.get_update_docs_task() @@ -461,8 +471,8 @@ def test_python_requirements(self, run, checkout_path, tmpdir): base_path = self.create_config_file( tmpdir, { - 'python': {'requirements': requirements_file} - } + 'python': {'requirements': requirements_file}, + }, ) update_docs = self.get_update_docs_task() @@ -471,7 +481,7 @@ def test_python_requirements(self, run, checkout_path, tmpdir): python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env update_docs.python_env.install_user_requirements() @@ -488,8 +498,8 @@ def test_python_requirements_empty(self, run, checkout_path, tmpdir): self.create_config_file( tmpdir, { - 'python': {'requirements': ''} - } + 'python': {'requirements': ''}, + }, ) update_docs = self.get_update_docs_task() @@ -498,7 +508,7 @@ def test_python_requirements_empty(self, run, checkout_path, tmpdir): python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env update_docs.python_env.install_user_requirements() @@ -512,8 +522,8 @@ def test_python_install_setup(self, run, checkout_path, tmpdir): self.create_config_file( tmpdir, { - 'python': {'install': 'setup.py'} - } + 'python': {'install': 'setup.py'}, + }, ) update_docs = self.get_update_docs_task() @@ -522,7 +532,7 @@ def test_python_install_setup(self, run, checkout_path, tmpdir): python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env update_docs.python_env.install_package() @@ -540,8 +550,8 @@ def test_python_install_pip(self, run, checkout_path, tmpdir): self.create_config_file( tmpdir, { - 'python': {'install': 'pip'} - } + 'python': {'install': 'pip'}, + }, ) update_docs = self.get_update_docs_task() @@ -550,7 +560,7 @@ def test_python_install_pip(self, run, checkout_path, tmpdir): python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env update_docs.python_env.install_package() @@ -583,8 +593,8 @@ def test_python_extra_requirements(self, run, checkout_path, tmpdir): 'python': { 'install': 'pip', 'extra_requirements': ['docs'], - } - } + }, + }, ) update_docs = self.get_update_docs_task() @@ -593,7 +603,7 @@ def test_python_extra_requirements(self, run, checkout_path, tmpdir): python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env update_docs.python_env.install_package() @@ -614,8 +624,8 @@ def test_system_packages(self, run, checkout_path, tmpdir): { 'python': { 'system_packages': True, - } - } + }, + }, ) update_docs = self.get_update_docs_task() @@ -624,7 +634,7 @@ def test_system_packages(self, run, checkout_path, tmpdir): python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env update_docs.python_env.setup_base() @@ -634,13 +644,18 @@ def test_system_packages(self, run, checkout_path, tmpdir): assert '--system-site-packages' in args assert config.python.use_system_site_packages - @pytest.mark.parametrize('value,result', - [('html', 'sphinx'), - ('htmldir', 'sphinx_htmldir'), - ('singlehtml', 'sphinx_singlehtml')]) + @pytest.mark.parametrize( + 'value,result', + [ + ('html', 'sphinx'), + ('htmldir', 'sphinx_htmldir'), + ('singlehtml', 'sphinx_singlehtml'), + ], + ) @patch('readthedocs.projects.tasks.get_builder_class') def test_sphinx_builder( - self, get_builder_class, checkout_path, value, result, tmpdir): + self, get_builder_class, checkout_path, value, result, tmpdir, + ): checkout_path.return_value = str(tmpdir) self.create_config_file(tmpdir, {'sphinx': {'builder': value}}) @@ -653,10 +668,12 @@ def test_sphinx_builder( get_builder_class.assert_called_with(result) @pytest.mark.skip( - 'This test is not compatible with the new validation around doctype.') + 'This test is not compatible with the new validation around doctype.', + ) @patch('readthedocs.projects.tasks.get_builder_class') def test_sphinx_builder_default( - self, get_builder_class, checkout_path, tmpdir): + self, get_builder_class, checkout_path, tmpdir, + ): checkout_path.return_value = str(tmpdir) self.create_config_file(tmpdir, {}) @@ -672,7 +689,8 @@ def test_sphinx_builder_default( @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.append_conf') @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.run') def test_sphinx_configuration_default( - self, run, append_conf, move, checkout_path, tmpdir): + self, run, append_conf, move, checkout_path, tmpdir, + ): """Should be default to find a conf.py file.""" checkout_path.return_value = str(tmpdir) @@ -686,7 +704,7 @@ def test_sphinx_configuration_default( python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env @@ -701,7 +719,8 @@ def test_sphinx_configuration_default( @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.append_conf') @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.run') def test_sphinx_configuration_default( - self, run, append_conf, move, checkout_path, tmpdir): + self, run, append_conf, move, checkout_path, tmpdir, + ): """Should be default to find a conf.py file.""" checkout_path.return_value = str(tmpdir) @@ -715,7 +734,7 @@ def test_sphinx_configuration_default( python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env @@ -730,21 +749,24 @@ def test_sphinx_configuration_default( @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.append_conf') @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.run') def test_sphinx_configuration( - self, run, append_conf, move, checkout_path, tmpdir): + self, run, append_conf, move, checkout_path, tmpdir, + ): checkout_path.return_value = str(tmpdir) - apply_fs(tmpdir, { - 'conf.py': '', - 'docx': { + apply_fs( + tmpdir, { 'conf.py': '', + 'docx': { + 'conf.py': '', + }, }, - }) + ) self.create_config_file( tmpdir, { 'sphinx': { 'configuration': 'docx/conf.py', }, - } + }, ) update_docs = self.get_update_docs_task() @@ -752,7 +774,7 @@ def test_sphinx_configuration( python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env @@ -767,13 +789,16 @@ def test_sphinx_configuration( @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.append_conf') @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.run') def test_sphinx_fail_on_warning( - self, run, append_conf, move, checkout_path, tmpdir): + self, run, append_conf, move, checkout_path, tmpdir, + ): checkout_path.return_value = str(tmpdir) - apply_fs(tmpdir, { - 'docx': { - 'conf.py': '', + apply_fs( + tmpdir, { + 'docx': { + 'conf.py': '', + }, }, - }) + ) self.create_config_file( tmpdir, { @@ -781,7 +806,7 @@ def test_sphinx_fail_on_warning( 'configuration': 'docx/conf.py', 'fail_on_warning': True, }, - } + }, ) update_docs = self.get_update_docs_task() @@ -789,7 +814,7 @@ def test_sphinx_fail_on_warning( python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env @@ -804,21 +829,24 @@ def test_sphinx_fail_on_warning( @patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.append_conf') @patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.run') def test_mkdocs_configuration( - self, run, append_conf, move, checkout_path, tmpdir): + self, run, append_conf, move, checkout_path, tmpdir, + ): checkout_path.return_value = str(tmpdir) - apply_fs(tmpdir, { - 'mkdocs.yml': '', - 'docx': { + apply_fs( + tmpdir, { 'mkdocs.yml': '', + 'docx': { + 'mkdocs.yml': '', + }, }, - }) + ) self.create_config_file( tmpdir, { 'mkdocs': { 'configuration': 'docx/mkdocs.yml', }, - } + }, ) self.project.documentation_type = 'mkdocs' self.project.save() @@ -828,7 +856,7 @@ def test_mkdocs_configuration( python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env @@ -844,13 +872,16 @@ def test_mkdocs_configuration( @patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.append_conf') @patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.run') def test_mkdocs_fail_on_warning( - self, run, append_conf, move, checkout_path, tmpdir): + self, run, append_conf, move, checkout_path, tmpdir, + ): checkout_path.return_value = str(tmpdir) - apply_fs(tmpdir, { - 'docx': { - 'mkdocs.yml': '', + apply_fs( + tmpdir, { + 'docx': { + 'mkdocs.yml': '', + }, }, - }) + ) self.create_config_file( tmpdir, { @@ -858,7 +889,7 @@ def test_mkdocs_fail_on_warning( 'configuration': 'docx/mkdocs.yml', 'fail_on_warning': True, }, - } + }, ) self.project.documentation_type = 'mkdocs' self.project.save() @@ -868,7 +899,7 @@ def test_mkdocs_fail_on_warning( python_env = Virtualenv( version=self.version, build_env=update_docs.build_env, - config=config + config=config, ) update_docs.python_env = python_env @@ -879,11 +910,17 @@ def test_mkdocs_fail_on_warning( append_conf.assert_called_once() move.assert_called_once() - @pytest.mark.parametrize('value,expected', [(ALL, ['one', 'two', 'three']), - (['one', 'two'], ['one', 'two'])]) + @pytest.mark.parametrize( + 'value,expected', [ + (ALL, ['one', 'two', 'three']), + (['one', 'two'], ['one', 'two']), + ], + ) @patch('readthedocs.vcs_support.backends.git.Backend.checkout_submodules') - def test_submodules_include(self, checkout_submodules, - checkout_path, tmpdir, value, expected): + def test_submodules_include( + self, checkout_submodules, + checkout_path, tmpdir, value, expected, + ): checkout_path.return_value = str(tmpdir) self.create_config_file( tmpdir, @@ -891,7 +928,7 @@ def test_submodules_include(self, checkout_submodules, 'submodules': { 'include': value, }, - } + }, ) git_repo = make_git_repo(str(tmpdir)) @@ -908,8 +945,10 @@ def test_submodules_include(self, checkout_submodules, assert update_docs.config.submodules.recursive is False @patch('readthedocs.vcs_support.backends.git.Backend.checkout_submodules') - def test_submodules_exclude(self, checkout_submodules, - checkout_path, tmpdir): + def test_submodules_exclude( + self, checkout_submodules, + checkout_path, tmpdir, + ): checkout_path.return_value = str(tmpdir) self.create_config_file( tmpdir, @@ -918,7 +957,7 @@ def test_submodules_exclude(self, checkout_submodules, 'exclude': ['one'], 'recursive': True, }, - } + }, ) git_repo = make_git_repo(str(tmpdir)) @@ -935,8 +974,10 @@ def test_submodules_exclude(self, checkout_submodules, assert update_docs.config.submodules.recursive is True @patch('readthedocs.vcs_support.backends.git.Backend.checkout_submodules') - def test_submodules_exclude_all(self, checkout_submodules, - checkout_path, tmpdir): + def test_submodules_exclude_all( + self, checkout_submodules, + checkout_path, tmpdir, + ): checkout_path.return_value = str(tmpdir) self.create_config_file( tmpdir, @@ -945,7 +986,7 @@ def test_submodules_exclude_all(self, checkout_submodules, 'exclude': ALL, 'recursive': True, }, - } + }, ) git_repo = make_git_repo(str(tmpdir)) @@ -960,13 +1001,15 @@ def test_submodules_exclude_all(self, checkout_submodules, checkout_submodules.assert_not_called() @patch('readthedocs.vcs_support.backends.git.Backend.checkout_submodules') - def test_submodules_default_exclude_all(self, checkout_submodules, - checkout_path, tmpdir): + def test_submodules_default_exclude_all( + self, checkout_submodules, + checkout_path, tmpdir, + ): checkout_path.return_value = str(tmpdir) self.create_config_file( tmpdir, - {} + {}, ) git_repo = make_git_repo(str(tmpdir)) diff --git a/readthedocs/rtd_tests/tests/test_core_utils.py b/readthedocs/rtd_tests/tests/test_core_utils.py index a19b5db6a2e..e57b46c0b17 100644 --- a/readthedocs/rtd_tests/tests/test_core_utils.py +++ b/readthedocs/rtd_tests/tests/test_core_utils.py @@ -133,13 +133,23 @@ def test_trigger_build_rounded_time_limit(self, update_docs): def test_slugify(self): """Test additional slugify.""" - self.assertEqual(slugify('This is a test'), - 'this-is-a-test') - self.assertEqual(slugify('project_with_underscores-v.1.0'), - 'project-with-underscores-v10') - self.assertEqual(slugify('project_with_underscores-v.1.0', dns_safe=False), - 'project_with_underscores-v10') - self.assertEqual(slugify('A title_-_with separated parts'), - 'a-title-with-separated-parts') - self.assertEqual(slugify('A title_-_with separated parts', dns_safe=False), - 'a-title_-_with-separated-parts') + self.assertEqual( + slugify('This is a test'), + 'this-is-a-test', + ) + self.assertEqual( + slugify('project_with_underscores-v.1.0'), + 'project-with-underscores-v10', + ) + self.assertEqual( + slugify('project_with_underscores-v.1.0', dns_safe=False), + 'project_with_underscores-v10', + ) + self.assertEqual( + slugify('A title_-_with separated parts'), + 'a-title-with-separated-parts', + ) + self.assertEqual( + slugify('A title_-_with separated parts', dns_safe=False), + 'a-title_-_with-separated-parts', + ) diff --git a/readthedocs/rtd_tests/tests/test_doc_builder.py b/readthedocs/rtd_tests/tests/test_doc_builder.py index ff740302b54..57bd6a15460 100644 --- a/readthedocs/rtd_tests/tests/test_doc_builder.py +++ b/readthedocs/rtd_tests/tests/test_doc_builder.py @@ -61,12 +61,12 @@ def test_conf_py_path(self, checkout_path, docs_dir): for value, expected in (('conf.py', '/'), ('docs/conf.py', '/docs/')): base_sphinx.config_file = os.path.join( - tmp_dir, value + tmp_dir, value, ) params = base_sphinx.get_config_params() self.assertEqual( params['conf_py_path'], - expected + expected, ) @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.docs_dir') @@ -77,7 +77,8 @@ def test_conf_py_path(self, checkout_path, docs_dir): @patch('readthedocs.projects.models.Project.checkout_path') def test_create_conf_py( self, checkout_path, get_conf_py_path, _, - get_config_params, create_index, docs_dir): + get_config_params, create_index, docs_dir, + ): """ Test for a project without ``conf.py`` file. @@ -116,13 +117,13 @@ def test_create_conf_py( os.path.dirname(__file__), '..', 'files', - 'conf.py' + 'conf.py', ) with open(generated_conf_py) as gf, open(expected_conf_py) as ef: autogenerated_confpy_lines = 28 self.assertEqual( gf.readlines()[:autogenerated_confpy_lines], - ef.readlines()[:autogenerated_confpy_lines] + ef.readlines()[:autogenerated_confpy_lines], ) @patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.docs_dir') @@ -133,7 +134,8 @@ def test_create_conf_py( @patch('readthedocs.projects.models.Project.checkout_path') def test_multiple_conf_py( self, checkout_path, get_conf_py_path, _, get_config_params, - create_index, docs_dir): + create_index, docs_dir, + ): """ Test for a project with multiple ``conf.py`` files. @@ -304,14 +306,14 @@ def test_append_conf_create_yaml(self, checkout_path, run): config = yaml.safe_load(open(generated_yaml)) self.assertEqual( config['docs_dir'], - os.path.join(tmpdir, 'docs') + os.path.join(tmpdir, 'docs'), ) self.assertEqual( config['extra_css'], [ 'http://readthedocs.org/static/css/badge_only.css', - 'http://readthedocs.org/static/css/readthedocs-doc-embed.css' - ] + 'http://readthedocs.org/static/css/readthedocs-doc-embed.css', + ], ) self.assertEqual( config['extra_javascript'], @@ -319,14 +321,14 @@ def test_append_conf_create_yaml(self, checkout_path, run): 'readthedocs-data.js', 'http://readthedocs.org/static/core/js/readthedocs-doc-embed.js', 'http://readthedocs.org/static/javascript/readthedocs-analytics.js', - ] + ], ) self.assertIsNone( config['google_analytics'], ) self.assertEqual( config['site_name'], - 'mkdocs' + 'mkdocs', ) @patch('readthedocs.doc_builder.base.BaseBuilder.run') @@ -341,7 +343,7 @@ def test_append_conf_existing_yaml_on_root(self, checkout_path, run): 'google_analytics': ['UA-1234-5', 'mkdocs.org'], 'docs_dir': 'docs', }, - open(yaml_file, 'w') + open(yaml_file, 'w'), ) checkout_path.return_value = tmpdir @@ -361,14 +363,14 @@ def test_append_conf_existing_yaml_on_root(self, checkout_path, run): config = yaml.safe_load(open(yaml_file)) self.assertEqual( config['docs_dir'], - 'docs' + 'docs', ) self.assertEqual( config['extra_css'], [ 'http://readthedocs.org/static/css/badge_only.css', - 'http://readthedocs.org/static/css/readthedocs-doc-embed.css' - ] + 'http://readthedocs.org/static/css/readthedocs-doc-embed.css', + ], ) self.assertEqual( config['extra_javascript'], @@ -376,14 +378,14 @@ def test_append_conf_existing_yaml_on_root(self, checkout_path, run): 'readthedocs-data.js', 'http://readthedocs.org/static/core/js/readthedocs-doc-embed.js', 'http://readthedocs.org/static/javascript/readthedocs-analytics.js', - ] + ], ) self.assertIsNone( config['google_analytics'], ) self.assertEqual( config['site_name'], - 'mkdocs' + 'mkdocs', ) @patch('readthedocs.doc_builder.base.BaseBuilder.run') @@ -432,7 +434,7 @@ def test_dont_override_theme(self, checkout_path, run): 'site_name': 'mkdocs', 'docs_dir': 'docs', }, - open(yaml_file, 'w') + open(yaml_file, 'w'), ) checkout_path.return_value = tmpdir @@ -452,7 +454,7 @@ def test_dont_override_theme(self, checkout_path, run): config = yaml.safe_load(open(yaml_file)) self.assertEqual( config['theme_dir'], - 'not-readthedocs' + 'not-readthedocs', ) @patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.generate_rtd_data') @@ -467,7 +469,7 @@ def test_write_js_data_docs_dir(self, checkout_path, run, generate_rtd_data): 'site_name': 'mkdocs', 'docs_dir': 'docs', }, - open(yaml_file, 'w') + open(yaml_file, 'w'), ) checkout_path.return_value = tmpdir generate_rtd_data.return_value = '' @@ -485,5 +487,5 @@ def test_write_js_data_docs_dir(self, checkout_path, run, generate_rtd_data): generate_rtd_data.assert_called_with( docs_dir='docs', - mkdocs_config=mock.ANY + mkdocs_config=mock.ANY, ) diff --git a/readthedocs/rtd_tests/tests/test_doc_building.py b/readthedocs/rtd_tests/tests/test_doc_building.py index 91e27907a3d..25ce88bcb45 100644 --- a/readthedocs/rtd_tests/tests/test_doc_building.py +++ b/readthedocs/rtd_tests/tests/test_doc_building.py @@ -58,9 +58,11 @@ def tearDown(self): def test_normal_execution(self): """Normal build in passing state.""" - self.mocks.configure_mock('process', { - 'communicate.return_value': (b'This is okay', '') - }) + self.mocks.configure_mock( + 'process', { + 'communicate.return_value': (b'This is okay', ''), + }, + ) type(self.mocks.process).returncode = PropertyMock(return_value=0) build_env = LocalBuildEnvironment( @@ -107,9 +109,11 @@ def test_normal_execution(self): def test_command_not_recorded(self): """Normal build in passing state with no command recorded.""" - self.mocks.configure_mock('process', { - 'communicate.return_value': (b'This is okay', '') - }) + self.mocks.configure_mock( + 'process', { + 'communicate.return_value': (b'This is okay', ''), + }, + ) type(self.mocks.process).returncode = PropertyMock(return_value=0) build_env = LocalBuildEnvironment( @@ -144,9 +148,11 @@ def test_command_not_recorded(self): }) def test_record_command_as_success(self): - self.mocks.configure_mock('process', { - 'communicate.return_value': (b'This is okay', '') - }) + self.mocks.configure_mock( + 'process', { + 'communicate.return_value': (b'This is okay', ''), + }, + ) type(self.mocks.process).returncode = PropertyMock(return_value=1) build_env = LocalBuildEnvironment( @@ -228,9 +234,11 @@ def test_incremental_state_update_with_no_update(self): def test_failing_execution(self): """Build in failing state.""" - self.mocks.configure_mock('process', { - 'communicate.return_value': (b'This is not okay', '') - }) + self.mocks.configure_mock( + 'process', { + 'communicate.return_value': (b'This is not okay', ''), + }, + ) type(self.mocks.process).returncode = PropertyMock(return_value=1) build_env = LocalBuildEnvironment( @@ -505,8 +513,10 @@ def test_api_failure(self): 'docker_client', { 'create_container.side_effect': DockerAPIError( 'Failure creating container', response, - 'Failure creating container') - }) + 'Failure creating container', + ), + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -546,8 +556,10 @@ def test_api_failure_on_docker_memory_limit(self): 'docker_client', { 'exec_create.side_effect': DockerAPIError( 'Failure creating container', response, - 'Failure creating container'), - }) + 'Failure creating container', + ), + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -592,9 +604,11 @@ def test_api_failure_on_docker_memory_limit(self): def test_api_failure_on_error_in_exit(self): response = Mock(status_code=500, reason='Internal Server Error') - self.mocks.configure_mock('docker_client', { - 'kill.side_effect': BuildEnvironmentError('Failed') - }) + self.mocks.configure_mock( + 'docker_client', { + 'kill.side_effect': BuildEnvironmentError('Failed'), + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -632,9 +646,11 @@ def test_api_failure_returns_previous_error_on_error_in_exit(self): usable error to show the user. """ response = Mock(status_code=500, reason='Internal Server Error') - self.mocks.configure_mock('docker_client', { - 'kill.side_effect': BuildEnvironmentError('Outer failed') - }) + self.mocks.configure_mock( + 'docker_client', { + 'kill.side_effect': BuildEnvironmentError('Outer failed'), + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -671,7 +687,8 @@ def test_command_execution(self): 'exec_create.return_value': {'Id': b'container-foobar'}, 'exec_start.return_value': b'This is the return', 'exec_inspect.return_value': {'ExitCode': 1}, - }) + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -684,7 +701,8 @@ def test_command_execution(self): self.mocks.docker_client.exec_create.assert_called_with( container='build-123-project-6-pip', - cmd="/bin/sh -c 'cd /tmp && echo\\ test'", stderr=True, stdout=True) + cmd="/bin/sh -c 'cd /tmp && echo\\ test'", stderr=True, stdout=True, + ) self.assertEqual(build_env.commands[0].exit_code, 1) self.assertEqual(build_env.commands[0].output, 'This is the return') self.assertEqual(build_env.commands[0].error, None) @@ -725,7 +743,8 @@ def test_command_not_recorded(self): 'exec_create.return_value': {'Id': b'container-foobar'}, 'exec_start.return_value': b'This is the return', 'exec_inspect.return_value': {'ExitCode': 1}, - }) + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -738,7 +757,8 @@ def test_command_not_recorded(self): self.mocks.docker_client.exec_create.assert_called_with( container='build-123-project-6-pip', - cmd="/bin/sh -c 'cd /tmp && echo\\ test'", stderr=True, stdout=True) + cmd="/bin/sh -c 'cd /tmp && echo\\ test'", stderr=True, stdout=True, + ) self.assertEqual(len(build_env.commands), 0) self.assertFalse(build_env.failed) @@ -766,7 +786,8 @@ def test_record_command_as_success(self): 'exec_create.return_value': {'Id': b'container-foobar'}, 'exec_start.return_value': b'This is the return', 'exec_inspect.return_value': {'ExitCode': 1}, - }) + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -779,7 +800,8 @@ def test_record_command_as_success(self): self.mocks.docker_client.exec_create.assert_called_with( container='build-123-project-6-pip', - cmd="/bin/sh -c 'cd /tmp && echo\\ test'", stderr=True, stdout=True) + cmd="/bin/sh -c 'cd /tmp && echo\\ test'", stderr=True, stdout=True, + ) self.assertEqual(build_env.commands[0].exit_code, 0) self.assertEqual(build_env.commands[0].output, 'This is the return') self.assertEqual(build_env.commands[0].error, None) @@ -825,8 +847,9 @@ def test_command_execution_cleanup_exception(self): 'Failure killing container', response, 'Failure killing container', - ) - }) + ), + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -837,7 +860,8 @@ def test_command_execution_cleanup_exception(self): build_env.run('echo', 'test', cwd='/tmp') self.mocks.docker_client.kill.assert_called_with( - 'build-123-project-6-pip') + 'build-123-project-6-pip', + ) self.assertTrue(build_env.successful) # api() is not called anymore, we use api_v2 instead @@ -876,7 +900,8 @@ def test_container_already_exists(self): 'exec_create.return_value': {'Id': b'container-foobar'}, 'exec_start.return_value': b'This is the return', 'exec_inspect.return_value': {'ExitCode': 0}, - }) + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -891,7 +916,8 @@ def _inner(): self.assertRaises(BuildEnvironmentError, _inner) self.assertEqual( str(build_env.failure), - 'A build environment is currently running for this version') + 'A build environment is currently running for this version', + ) self.assertEqual(self.mocks.docker_client.exec_create.call_count, 0) self.assertTrue(build_env.failed) @@ -930,7 +956,8 @@ def test_container_timeout(self): 'exec_create.return_value': {'Id': b'container-foobar'}, 'exec_start.return_value': b'This is the return', 'exec_inspect.return_value': {'ExitCode': 0}, - }) + }, + ) build_env = DockerBuildEnvironment( version=self.version, @@ -1032,8 +1059,10 @@ def test_error_output(self): self.assertEqual(cmd.output, 'FOOBAR') self.assertIsNone(cmd.error) # Test non-combined streams - cmd = BuildCommand(['/bin/bash', '-c', 'echo -n FOOBAR 1>&2'], - combine_output=False) + cmd = BuildCommand( + ['/bin/bash', '-c', 'echo -n FOOBAR 1>&2'], + combine_output=False, + ) cmd.run() self.assertEqual(cmd.output, '') self.assertEqual(cmd.error, 'FOOBAR') @@ -1060,7 +1089,8 @@ def test_unicode_output(self, mock_subprocess): cmd.run() self.assertEqual( cmd.output, - 'H\xe9r\xc9 \xee\xdf s\xf6m\xea \xfcn\xef\xe7\xf3\u2202\xe9') + 'H\xe9r\xc9 \xee\xdf s\xf6m\xea \xfcn\xef\xe7\xf3\u2202\xe9', + ) class TestDockerBuildCommand(TestCase): @@ -1076,8 +1106,10 @@ def tearDown(self): def test_wrapped_command(self): """Test shell wrapping for Docker chdir.""" - cmd = DockerBuildCommand(['pip', 'install', 'requests'], - cwd='/tmp/foobar') + cmd = DockerBuildCommand( + ['pip', 'install', 'requests'], + cwd='/tmp/foobar', + ) self.assertEqual( cmd.get_wrapped_command(), "/bin/sh -c 'cd /tmp/foobar && pip install requests'", @@ -1089,9 +1121,11 @@ def test_wrapped_command(self): ) self.assertEqual( cmd.get_wrapped_command(), - ('/bin/sh -c ' - "'cd /tmp/foobar && PATH=/tmp/foo:$PATH " - r"python /tmp/foo/pip install Django\>1.7'"), + ( + '/bin/sh -c ' + "'cd /tmp/foobar && PATH=/tmp/foo:$PATH " + r"python /tmp/foo/pip install Django\>1.7'" + ), ) def test_unicode_output(self): @@ -1101,7 +1135,8 @@ def test_unicode_output(self): 'exec_create.return_value': {'Id': b'container-foobar'}, 'exec_start.return_value': SAMPLE_UTF8_BYTES, 'exec_inspect.return_value': {'ExitCode': 0}, - }) + }, + ) cmd = DockerBuildCommand(['echo', 'test'], cwd='/tmp/foobar') cmd.build_env = Mock() cmd.build_env.get_client.return_value = self.mocks.docker_client @@ -1109,7 +1144,8 @@ def test_unicode_output(self): cmd.run() self.assertEqual( cmd.output, - 'H\xe9r\xc9 \xee\xdf s\xf6m\xea \xfcn\xef\xe7\xf3\u2202\xe9') + 'H\xe9r\xc9 \xee\xdf s\xf6m\xea \xfcn\xef\xe7\xf3\u2202\xe9', + ) self.assertEqual(self.mocks.docker_client.exec_start.call_count, 1) self.assertEqual(self.mocks.docker_client.exec_create.call_count, 1) self.assertEqual(self.mocks.docker_client.exec_inspect.call_count, 1) @@ -1121,7 +1157,8 @@ def test_command_oom_kill(self): 'exec_create.return_value': {'Id': b'container-foobar'}, 'exec_start.return_value': b'Killed\n', 'exec_inspect.return_value': {'ExitCode': 137}, - }) + }, + ) cmd = DockerBuildCommand(['echo', 'test'], cwd='/tmp/foobar') cmd.build_env = Mock() cmd.build_env.get_client.return_value = self.mocks.docker_client @@ -1129,7 +1166,7 @@ def test_command_oom_kill(self): cmd.run() self.assertIn( 'Command killed due to excessive memory consumption\n', - str(cmd.output) + str(cmd.output), ) @@ -1204,7 +1241,7 @@ def test_install_core_requirements_mkdocs(self, checkout_path): checkout_path.return_value = tmpdir python_env = Virtualenv( version=self.version_mkdocs, - build_env=self.build_env_mock + build_env=self.build_env_mock, ) python_env.install_core_requirements() requirements_mkdocs = [ @@ -1235,15 +1272,15 @@ def test_install_user_requirements(self, checkout_path): self.build_env_mock.version = self.version_sphinx python_env = Virtualenv( version=self.version_sphinx, - build_env=self.build_env_mock + build_env=self.build_env_mock, ) checkout_path = python_env.checkout_path docs_requirements = os.path.join( - checkout_path, 'docs', 'requirements.txt' + checkout_path, 'docs', 'requirements.txt', ) root_requirements = os.path.join( - checkout_path, 'requirements.txt' + checkout_path, 'requirements.txt', ) paths = { os.path.join(checkout_path, 'docs'): True, @@ -1257,7 +1294,7 @@ def test_install_user_requirements(self, checkout_path): '--cache-dir', mock.ANY, # cache path '-r', - 'requirements_file' + 'requirements_file', ] # One requirements file on the docs/ dir @@ -1344,7 +1381,7 @@ def test_install_core_requirements_sphinx_conda(self, checkout_path): self.build_env_mock.run.assert_has_calls([ mock.call(*args_conda, cwd=mock.ANY), - mock.call(*args_pip, bin_path=mock.ANY, cwd=mock.ANY) + mock.call(*args_pip, bin_path=mock.ANY, cwd=mock.ANY), ]) @patch('readthedocs.projects.models.Project.checkout_path') @@ -1385,7 +1422,7 @@ def test_install_core_requirements_mkdocs_conda(self, checkout_path): self.build_env_mock.run.assert_has_calls([ mock.call(*args_conda, cwd=mock.ANY), - mock.call(*args_pip, bin_path=mock.ANY, cwd=mock.ANY) + mock.call(*args_pip, bin_path=mock.ANY, cwd=mock.ANY), ]) @patch('readthedocs.projects.models.Project.checkout_path') diff --git a/readthedocs/rtd_tests/tests/test_doc_serving.py b/readthedocs/rtd_tests/tests/test_doc_serving.py index a8e42afdc96..d3e5ba74989 100644 --- a/readthedocs/rtd_tests/tests/test_doc_serving.py +++ b/readthedocs/rtd_tests/tests/test_doc_serving.py @@ -17,7 +17,7 @@ @override_settings( - USE_SUBDOMAIN=False, PUBLIC_DOMAIN='public.readthedocs.org', DEBUG=False + USE_SUBDOMAIN=False, PUBLIC_DOMAIN='public.readthedocs.org', DEBUG=False, ) class BaseDocServing(RequestFactoryTestMixin, TestCase): @@ -46,7 +46,7 @@ def test_private_python_media_serving(self): serve_mock.assert_called_with( request, 'en/latest/usage.html', - settings.SITE_ROOT + '/private_web_root/private' + settings.SITE_ROOT + '/private_web_root/private', ) @override_settings(PYTHON_MEDIA=False) @@ -56,7 +56,7 @@ def test_private_nginx_serving(self): r = _serve_symlink_docs(request, project=self.private, filename='/en/latest/usage.html', privacy_level='private') self.assertEqual(r.status_code, 200) self.assertEqual( - r._headers['x-accel-redirect'][1], '/private_web_root/private/en/latest/usage.html' + r._headers['x-accel-redirect'][1], '/private_web_root/private/en/latest/usage.html', ) @override_settings(PYTHON_MEDIA=False) @@ -66,7 +66,7 @@ def test_private_nginx_serving_unicode_filename(self): r = _serve_symlink_docs(request, project=self.private, filename='/en/latest/úñíčódé.html', privacy_level='private') self.assertEqual(r.status_code, 200) self.assertEqual( - r._headers['x-accel-redirect'][1], '/private_web_root/private/en/latest/%C3%BA%C3%B1%C3%AD%C4%8D%C3%B3d%C3%A9.html' + r._headers['x-accel-redirect'][1], '/private_web_root/private/en/latest/%C3%BA%C3%B1%C3%AD%C4%8D%C3%B3d%C3%A9.html', ) @override_settings(PYTHON_MEDIA=False) @@ -112,7 +112,7 @@ def test_public_python_media_serving(self): serve_mock.assert_called_with( request, 'en/latest/usage.html', - settings.SITE_ROOT + '/public_web_root/public' + settings.SITE_ROOT + '/public_web_root/public', ) @override_settings(PYTHON_MEDIA=False) @@ -122,7 +122,7 @@ def test_public_nginx_serving(self): r = _serve_symlink_docs(request, project=self.public, filename='/en/latest/usage.html', privacy_level='public') self.assertEqual(r.status_code, 200) self.assertEqual( - r._headers['x-accel-redirect'][1], '/public_web_root/public/en/latest/usage.html' + r._headers['x-accel-redirect'][1], '/public_web_root/public/en/latest/usage.html', ) @override_settings(PYTHON_MEDIA=False) diff --git a/readthedocs/rtd_tests/tests/test_domains.py b/readthedocs/rtd_tests/tests/test_domains.py index cfa13b0c449..8c9610ac420 100644 --- a/readthedocs/rtd_tests/tests/test_domains.py +++ b/readthedocs/rtd_tests/tests/test_domains.py @@ -71,32 +71,44 @@ def setUp(self): def test_https(self): """Make sure https is an admin-only attribute.""" - form = DomainForm({'domain': 'example.com', 'canonical': True}, - project=self.project) + form = DomainForm( + {'domain': 'example.com', 'canonical': True}, + project=self.project, + ) self.assertTrue(form.is_valid()) domain = form.save() self.assertFalse(domain.https) - form = DomainForm({'domain': 'example.com', 'canonical': True, - 'https': True}, - project=self.project) + form = DomainForm( + { + 'domain': 'example.com', 'canonical': True, + 'https': True, + }, + project=self.project, + ) self.assertFalse(form.is_valid()) def test_canonical_change(self): """Make sure canonical can be properly changed.""" - form = DomainForm({'domain': 'example.com', 'canonical': True}, - project=self.project) + form = DomainForm( + {'domain': 'example.com', 'canonical': True}, + project=self.project, + ) self.assertTrue(form.is_valid()) domain = form.save() self.assertEqual(domain.domain, 'example.com') - form = DomainForm({'domain': 'example2.com', 'canonical': True}, - project=self.project) + form = DomainForm( + {'domain': 'example2.com', 'canonical': True}, + project=self.project, + ) self.assertFalse(form.is_valid()) self.assertEqual(form.errors['canonical'][0], 'Only 1 Domain can be canonical at a time.') - form = DomainForm({'domain': 'example2.com', 'canonical': True}, - project=self.project, - instance=domain) + form = DomainForm( + {'domain': 'example2.com', 'canonical': True}, + project=self.project, + instance=domain, + ) self.assertTrue(form.is_valid()) domain = form.save() self.assertEqual(domain.domain, 'example2.com') diff --git a/readthedocs/rtd_tests/tests/test_extend.py b/readthedocs/rtd_tests/tests/test_extend.py index 1d452cf828a..74ddbcf8d1f 100644 --- a/readthedocs/rtd_tests/tests/test_extend.py +++ b/readthedocs/rtd_tests/tests/test_extend.py @@ -63,10 +63,12 @@ class Foo(SettingsOverrideObject): override_class = get_override_class(Foo, Foo._default_class) self.assertEqual(override_class, NewFoo) - @override_settings(FOO_OVERRIDE_CLASS=None, - CLASS_OVERRIDES={ - EXTEND_PATH: EXTEND_OVERRIDE_PATH, - }) + @override_settings( + FOO_OVERRIDE_CLASS=None, + CLASS_OVERRIDES={ + EXTEND_PATH: EXTEND_OVERRIDE_PATH, + }, + ) def test_with_advanced_override(self): """Test class with override using `CLASS_OVERRIDES`""" class Foo(SettingsOverrideObject): @@ -81,10 +83,12 @@ class Foo(SettingsOverrideObject): override_class = get_override_class(Foo, Foo._default_class) self.assertEqual(override_class, NewFoo) - @override_settings(FOO_OVERRIDE_CLASS=None, - CLASS_OVERRIDES={ - EXTEND_PATH: EXTEND_OVERRIDE_PATH, - }) + @override_settings( + FOO_OVERRIDE_CLASS=None, + CLASS_OVERRIDES={ + EXTEND_PATH: EXTEND_OVERRIDE_PATH, + }, + ) def test_with_advanced_override_only(self): """Test class with no `override_setting`""" class Foo(SettingsOverrideObject): diff --git a/readthedocs/rtd_tests/tests/test_gold.py b/readthedocs/rtd_tests/tests/test_gold.py index 51d891eef4c..b52ec2938ae 100644 --- a/readthedocs/rtd_tests/tests/test_gold.py +++ b/readthedocs/rtd_tests/tests/test_gold.py @@ -34,7 +34,7 @@ def test_too_many_projects(self): self.assertEqual(resp.status_code, 302) resp = self.client.post(reverse('gold_projects'), data={'project': self.project2.slug}) self.assertFormError( - resp, form='form', field=None, errors='You already have the max number of supported projects.' + resp, form='form', field=None, errors='You already have the max number of supported projects.', ) self.assertEqual(resp.status_code, 200) self.assertEqual(self.golduser.projects.count(), 1) diff --git a/readthedocs/rtd_tests/tests/test_integrations.py b/readthedocs/rtd_tests/tests/test_integrations.py index e7be6588361..54e6922a4c6 100644 --- a/readthedocs/rtd_tests/tests/test_integrations.py +++ b/readthedocs/rtd_tests/tests/test_integrations.py @@ -24,23 +24,27 @@ def test_exchange_json_request_body(self): client = APIClient() client.login(username='super', password='test') project = fixture.get(Project, main_language_project=None) - integration = fixture.get(Integration, project=project, - integration_type=Integration.GITHUB_WEBHOOK, - provider_data='') + integration = fixture.get( + Integration, project=project, + integration_type=Integration.GITHUB_WEBHOOK, + provider_data='', + ) resp = client.post( '/api/v2/webhook/github/{}/'.format(project.slug), {'ref': 'exchange_json'}, - format='json' + format='json', ) exchange = HttpExchange.objects.get(integrations=integration) self.assertEqual( exchange.request_body, - '{"ref": "exchange_json"}' + '{"ref": "exchange_json"}', ) self.assertEqual( exchange.request_headers, - {'Content-Type': 'application/json; charset=None', - 'Cookie': ''} + { + 'Content-Type': 'application/json; charset=None', + 'Cookie': '', + }, ) self.assertEqual( exchange.response_body, @@ -49,17 +53,21 @@ def test_exchange_json_request_body(self): ) self.assertEqual( exchange.response_headers, - {'Allow': 'POST, OPTIONS', - 'Content-Type': 'text/html; charset=utf-8'} + { + 'Allow': 'POST, OPTIONS', + 'Content-Type': 'text/html; charset=utf-8', + }, ) def test_exchange_form_request_body(self): client = APIClient() client.login(username='super', password='test') project = fixture.get(Project, main_language_project=None) - integration = fixture.get(Integration, project=project, - integration_type=Integration.GITHUB_WEBHOOK, - provider_data='') + integration = fixture.get( + Integration, project=project, + integration_type=Integration.GITHUB_WEBHOOK, + provider_data='', + ) resp = client.post( '/api/v2/webhook/github/{}/'.format(project.slug), 'payload=%7B%22ref%22%3A+%22exchange_form%22%7D', @@ -68,12 +76,14 @@ def test_exchange_form_request_body(self): exchange = HttpExchange.objects.get(integrations=integration) self.assertEqual( exchange.request_body, - '{"ref": "exchange_form"}' + '{"ref": "exchange_form"}', ) self.assertEqual( exchange.request_headers, - {'Content-Type': 'application/x-www-form-urlencoded', - 'Cookie': ''} + { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Cookie': '', + }, ) self.assertEqual( exchange.response_body, @@ -82,55 +92,61 @@ def test_exchange_form_request_body(self): ) self.assertEqual( exchange.response_headers, - {'Allow': 'POST, OPTIONS', - 'Content-Type': 'text/html; charset=utf-8'} + { + 'Allow': 'POST, OPTIONS', + 'Content-Type': 'text/html; charset=utf-8', + }, ) def test_extraneous_exchanges_deleted_in_correct_order(self): client = APIClient() client.login(username='super', password='test') project = fixture.get(Project, main_language_project=None) - integration = fixture.get(Integration, project=project, - integration_type=Integration.GITHUB_WEBHOOK, - provider_data='') + integration = fixture.get( + Integration, project=project, + integration_type=Integration.GITHUB_WEBHOOK, + provider_data='', + ) self.assertEqual( HttpExchange.objects.filter(integrations=integration).count(), - 0 + 0, ) for _ in range(10): resp = client.post( '/api/v2/webhook/github/{}/'.format(project.slug), {'ref': 'deleted'}, - format='json' + format='json', ) for _ in range(10): resp = client.post( '/api/v2/webhook/github/{}/'.format(project.slug), {'ref': 'preserved'}, - format='json' + format='json', ) self.assertEqual( HttpExchange.objects.filter(integrations=integration).count(), - 10 + 10, ) self.assertEqual( HttpExchange.objects.filter( integrations=integration, request_body='{"ref": "preserved"}', ).count(), - 10 + 10, ) def test_request_headers_are_removed(self): client = APIClient() client.login(username='super', password='test') project = fixture.get(Project, main_language_project=None) - integration = fixture.get(Integration, project=project, - integration_type=Integration.GITHUB_WEBHOOK, - provider_data='') + integration = fixture.get( + Integration, project=project, + integration_type=Integration.GITHUB_WEBHOOK, + provider_data='', + ) resp = client.post( '/api/v2/webhook/github/{}/'.format(project.slug), {'ref': 'exchange_json'}, @@ -142,9 +158,11 @@ def test_request_headers_are_removed(self): exchange = HttpExchange.objects.get(integrations=integration) self.assertEqual( exchange.request_headers, - {'Content-Type': 'application/json; charset=None', - 'Cookie': '', - 'X-Foo': 'bar'} + { + 'Content-Type': 'application/json; charset=None', + 'Cookie': '', + 'X-Foo': 'bar', + }, ) @@ -154,7 +172,7 @@ def test_subclass_is_replaced_on_get(self): project = fixture.get(Project, main_language_project=None) integration = Integration.objects.create( project=project, - integration_type=Integration.GITHUB_WEBHOOK + integration_type=Integration.GITHUB_WEBHOOK, ) integration = Integration.objects.get(pk=integration.pk) self.assertIsInstance(integration, GitHubWebhook) @@ -163,7 +181,7 @@ def test_subclass_is_replaced_on_subclass(self): project = fixture.get(Project, main_language_project=None) integration = Integration.objects.create( project=project, - integration_type=Integration.GITHUB_WEBHOOK + integration_type=Integration.GITHUB_WEBHOOK, ) integration = Integration.objects.subclass(integration) self.assertIsInstance(integration, GitHubWebhook) diff --git a/readthedocs/rtd_tests/tests/test_middleware.py b/readthedocs/rtd_tests/tests/test_middleware.py index 093957f71f4..4a93274da4a 100644 --- a/readthedocs/rtd_tests/tests/test_middleware.py +++ b/readthedocs/rtd_tests/tests/test_middleware.py @@ -147,7 +147,7 @@ def setUp(self): self.project = get( Project, slug='pip', users=[self.owner], privacy_level='public', - mail_language_project=None + mail_language_project=None, ) self.subproject = get( Project, @@ -158,7 +158,7 @@ def setUp(self): self.relationship = get( ProjectRelationship, parent=self.project, - child=self.subproject + child=self.subproject, ) self.domain = get(Domain, domain='my.valid.domain', project=self.project) diff --git a/readthedocs/rtd_tests/tests/test_notifications.py b/readthedocs/rtd_tests/tests/test_notifications.py index cd3df63ec8c..1ead82e534a 100644 --- a/readthedocs/rtd_tests/tests/test_notifications.py +++ b/readthedocs/rtd_tests/tests/test_notifications.py @@ -48,21 +48,33 @@ class TestNotification(Notification): req = mock.MagicMock() notify = TestNotification(context_object=build, request=req) - self.assertEqual(notify.get_template_names('email'), - ['builds/notifications/foo_email.html']) - self.assertEqual(notify.get_template_names('site'), - ['builds/notifications/foo_site.html']) - self.assertEqual(notify.get_subject(), - 'This is {}'.format(build.id)) - self.assertEqual(notify.get_context_data(), - {'foo': build, - 'production_uri': 'https://readthedocs.org', - 'request': req}) + self.assertEqual( + notify.get_template_names('email'), + ['builds/notifications/foo_email.html'], + ) + self.assertEqual( + notify.get_template_names('site'), + ['builds/notifications/foo_site.html'], + ) + self.assertEqual( + notify.get_subject(), + 'This is {}'.format(build.id), + ) + self.assertEqual( + notify.get_context_data(), + { + 'foo': build, + 'production_uri': 'https://readthedocs.org', + 'request': req, + }, + ) notify.render('site') render_to_string.assert_has_calls([ - mock.call(context=mock.ANY, - template_name=['builds/notifications/foo_site.html']) + mock.call( + context=mock.ANY, + template_name=['builds/notifications/foo_site.html'], + ), ]) @@ -95,7 +107,7 @@ class TestNotification(Notification): subject='This is {}'.format(build.id), template_html='core/email/common.html', recipient=user.email, - ) + ), ]) def test_message_backend(self, render_to_string): diff --git a/readthedocs/rtd_tests/tests/test_oauth.py b/readthedocs/rtd_tests/tests/test_oauth.py index 87d9439a16a..1ef675cc42c 100644 --- a/readthedocs/rtd_tests/tests/test_oauth.py +++ b/readthedocs/rtd_tests/tests/test_oauth.py @@ -39,7 +39,8 @@ def test_make_project_pass(self): 'clone_url': 'https://github.com/testuser/testrepo.git', } repo = self.service.create_repository( - repo_json, organization=self.org, privacy=self.privacy) + repo_json, organization=self.org, privacy=self.privacy, + ) self.assertIsInstance(repo, RemoteRepository) self.assertEqual(repo.name, 'testrepo') self.assertEqual(repo.full_name, 'testuser/testrepo') @@ -51,9 +52,11 @@ def test_make_project_pass(self): self.assertIn(self.user, repo.users.all()) self.assertEqual(repo.organization, self.org) self.assertEqual( - repo.clone_url, 'https://github.com/testuser/testrepo.git') + repo.clone_url, 'https://github.com/testuser/testrepo.git', + ) self.assertEqual( - repo.ssh_url, 'ssh://git@github.com:testuser/testrepo.git') + repo.ssh_url, 'ssh://git@github.com:testuser/testrepo.git', + ) self.assertEqual(repo.html_url, 'https://github.com/testuser/testrepo') def test_make_project_fail(self): @@ -68,7 +71,8 @@ def test_make_project_fail(self): 'clone_url': '', } github_project = self.service.create_repository( - repo_json, organization=self.org, privacy=self.privacy) + repo_json, organization=self.org, privacy=self.privacy, + ) self.assertIsNone(github_project) def test_make_organization(self): @@ -105,29 +109,35 @@ def test_multiple_users_same_repo(self): } github_project = self.service.create_repository( - repo_json, organization=self.org, privacy=self.privacy) + repo_json, organization=self.org, privacy=self.privacy, + ) user2 = User.objects.get(pk=2) service = GitHubService(user=user2, account=None) github_project_2 = service.create_repository( - repo_json, organization=self.org, privacy=self.privacy) + repo_json, organization=self.org, privacy=self.privacy, + ) self.assertIsInstance(github_project, RemoteRepository) self.assertIsInstance(github_project_2, RemoteRepository) self.assertNotEqual(github_project_2, github_project) github_project_3 = self.service.create_repository( - repo_json, organization=self.org, privacy=self.privacy) + repo_json, organization=self.org, privacy=self.privacy, + ) github_project_4 = service.create_repository( - repo_json, organization=self.org, privacy=self.privacy) + repo_json, organization=self.org, privacy=self.privacy, + ) self.assertIsInstance(github_project_3, RemoteRepository) self.assertIsInstance(github_project_4, RemoteRepository) self.assertEqual(github_project, github_project_3) self.assertEqual(github_project_2, github_project_4) github_project_5 = self.service.create_repository( - repo_json, organization=self.org, privacy=self.privacy) + repo_json, organization=self.org, privacy=self.privacy, + ) github_project_6 = service.create_repository( - repo_json, organization=self.org, privacy=self.privacy) + repo_json, organization=self.org, privacy=self.privacy, + ) self.assertEqual(github_project, github_project_5) self.assertEqual(github_project_2, github_project_6) @@ -261,7 +271,8 @@ def setUp(self): def test_make_project_pass(self): repo = self.service.create_repository( self.repo_response_data, organization=self.org, - privacy=self.privacy) + privacy=self.privacy, + ) self.assertIsInstance(repo, RemoteRepository) self.assertEqual(repo.name, 'tutorials.bitbucket.org') self.assertEqual(repo.full_name, 'tutorials/tutorials.bitbucket.org') @@ -269,24 +280,30 @@ def test_make_project_pass(self): self.assertEqual( repo.avatar_url, ( 'https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2012/Nov/28/' - 'tutorials.bitbucket.org-logo-1456883302-9_avatar.png')) + 'tutorials.bitbucket.org-logo-1456883302-9_avatar.png' + ), + ) self.assertIn(self.user, repo.users.all()) self.assertEqual(repo.organization, self.org) self.assertEqual( repo.clone_url, - 'https://bitbucket.org/tutorials/tutorials.bitbucket.org') + 'https://bitbucket.org/tutorials/tutorials.bitbucket.org', + ) self.assertEqual( repo.ssh_url, - 'ssh://hg@bitbucket.org/tutorials/tutorials.bitbucket.org') + 'ssh://hg@bitbucket.org/tutorials/tutorials.bitbucket.org', + ) self.assertEqual( repo.html_url, - 'https://bitbucket.org/tutorials/tutorials.bitbucket.org') + 'https://bitbucket.org/tutorials/tutorials.bitbucket.org', + ) def test_make_project_fail(self): data = self.repo_response_data.copy() data['is_private'] = True repo = self.service.create_repository( - data, organization=self.org, privacy=self.privacy) + data, organization=self.org, privacy=self.privacy, + ) self.assertIsNone(repo) @override_settings(DEFAULT_PRIVACY_LEVEL='private') @@ -307,7 +324,9 @@ def test_make_organization(self): self.assertEqual( org.avatar_url, ( 'https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2014/Sep/24/' - 'teamsinspace-avatar-3731530358-7_avatar.png')) + 'teamsinspace-avatar-3731530358-7_avatar.png' + ), + ) self.assertEqual(org.url, 'https://bitbucket.org/teamsinspace') def test_import_with_no_token(self): @@ -430,7 +449,8 @@ def test_make_project_pass(self): m.return_value = True repo = self.service.create_repository( self.repo_response_data, organization=self.org, - privacy=self.privacy) + privacy=self.privacy, + ) self.assertIsInstance(repo, RemoteRepository) self.assertEqual(repo.name, 'testrepo') self.assertEqual(repo.full_name, 'testorga / testrepo') @@ -453,13 +473,15 @@ def test_make_project_pass(self): def test_make_private_project_fail(self): repo = self.service.create_repository( self.get_private_repo_data(), organization=self.org, - privacy=self.privacy) + privacy=self.privacy, + ) self.assertIsNone(repo) def test_make_private_project_success(self): repo = self.service.create_repository( self.get_private_repo_data(), organization=self.org, - privacy=constants.PRIVATE) + privacy=constants.PRIVATE, + ) self.assertIsInstance(repo, RemoteRepository) self.assertTrue(repo.private, True) diff --git a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py index cc8c1009d05..4dd40865e02 100644 --- a/readthedocs/rtd_tests/tests/test_post_commit_hooks.py +++ b/readthedocs/rtd_tests/tests/test_post_commit_hooks.py @@ -17,15 +17,19 @@ class BasePostCommitTest(TestCase): def _setup(self): self.rtfd = get( - Project, repo='https://github.com/rtfd/readthedocs.org', slug='read-the-docs') + Project, repo='https://github.com/rtfd/readthedocs.org', slug='read-the-docs', + ) self.rtfd_not_ok = get( - Version, project=self.rtfd, slug='not_ok', identifier='not_ok', active=False) + Version, project=self.rtfd, slug='not_ok', identifier='not_ok', active=False, + ) self.rtfd_awesome = get( - Version, project=self.rtfd, slug='awesome', identifier='awesome', active=True) + Version, project=self.rtfd, slug='awesome', identifier='awesome', active=True, + ) self.pip = get(Project, repo='https://bitbucket.org/pip/pip', repo_type='hg') self.pip_not_ok = get( - Version, project=self.pip, slug='not_ok', identifier='not_ok', active=False) + Version, project=self.pip, slug='not_ok', identifier='not_ok', active=False, + ) self.sphinx = get(Project, repo='https://bitbucket.org/sphinx/sphinx', repo_type='git') self.mocks = [mock.patch('readthedocs.core.views.hooks.trigger_build')] @@ -76,7 +80,7 @@ def setUp(self): 'homepage':'http://example.com/mike/diaspora', 'url':'git@github.com/rtfd/readthedocs.org.git', 'ssh_url':'git@github.com/rtfd/readthedocs.org.git', - 'http_url':'http://github.com/rtfd/readthedocs.org.git' + 'http_url':'http://github.com/rtfd/readthedocs.org.git', }, 'repository':{ 'name': 'Diaspora', @@ -85,7 +89,7 @@ def setUp(self): 'homepage': 'http://github.com/rtfd/readthedocs.org', 'git_http_url': 'http://github.com/rtfd/readthedocs.org.git', 'git_ssh_url': 'git@github.com:rtfd/readthedocs.org.git', - 'visibility_level': 0 + 'visibility_level': 0, }, 'commits': [ { @@ -95,11 +99,11 @@ def setUp(self): 'url': 'http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327', 'author': { 'name': 'Jordi Mallach', - 'email': 'jordi@softcatala.org' + 'email': 'jordi@softcatala.org', }, 'added': ['CHANGELOG'], 'modified': ['app/controller/application.rb'], - 'removed': [] + 'removed': [], }, { 'id': 'da1560886d4f094c3e6c9ef40349f7d38b5d27d7', @@ -108,30 +112,36 @@ def setUp(self): 'url': 'http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7', 'author': { 'name': 'GitLab dev user', - 'email': 'gitlabdev@dv6700.(none)' + 'email': 'gitlabdev@dv6700.(none)', }, 'added': ['CHANGELOG'], 'modified': ['app/controller/application.rb'], - 'removed': [] - } + 'removed': [], + }, ], - 'total_commits_count': 4 + 'total_commits_count': 4, } def test_gitlab_post_commit_hook_builds_branch_docs_if_it_should(self): """GitLab webhook should only build active versions.""" - r = self.client.post('/gitlab/', data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/gitlab/', data=json.dumps(self.payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: github.com/rtfd/readthedocs.org [awesome]') self.payload['ref'] = 'refs/heads/not_ok' - r = self.client.post('/gitlab/', data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/gitlab/', data=json.dumps(self.payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Not Building: github.com/rtfd/readthedocs.org [not_ok]') self.payload['ref'] = 'refs/heads/unknown' - r = self.client.post('/gitlab/', data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/gitlab/', data=json.dumps(self.payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) No known branches were pushed to.') def test_gitlab_post_commit_knows_default_branches(self): @@ -145,8 +155,10 @@ def test_gitlab_post_commit_knows_default_branches(self): rtd.save() self.payload['ref'] = 'refs/heads/master' - r = self.client.post('/gitlab/', data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/gitlab/', data=json.dumps(self.payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: github.com/rtfd/readthedocs.org [latest]') rtd.default_branch = old_default @@ -160,7 +172,7 @@ def test_gitlab_request_empty_url(self): self.payload['project']['http_url'] = '' r = self.client.post( '/gitlab/', data=json.dumps(self.payload), - content_type='application/json' + content_type='application/json', ) self.assertEqual(r.status_code, 404) @@ -177,7 +189,7 @@ def test_gitlab_webhook_is_deprecated(self): r = self.client.post( '/gitlab/', data=json.dumps(payload), - content_type='application/json' + content_type='application/json', ) self.assertEqual(r.status_code, 403) @@ -198,27 +210,31 @@ def setUp(self): 'author': { 'email': 'eric@ericholscher.com', 'name': 'Eric Holscher', - 'username': 'ericholscher' + 'username': 'ericholscher', }, 'distinct': False, 'id': '11f229c6a78f5bc8cb173104a3f7a68cdb7eb15a', 'message': 'Fix it on the front list as well.', 'modified': [ - 'readthedocs/templates/core/project_list_detailed.html' + 'readthedocs/templates/core/project_list_detailed.html', ], 'removed': [], 'timestamp': '2011-09-12T19:38:55-07:00', - 'url': ('https://github.com/wraithan/readthedocs.org/' - 'commit/11f229c6a78f5bc8cb173104a3f7a68cdb7eb15a') + 'url': ( + 'https://github.com/wraithan/readthedocs.org/' + 'commit/11f229c6a78f5bc8cb173104a3f7a68cdb7eb15a' + ), }, ], - 'compare': ('https://github.com/wraithan/readthedocs.org/compare/' - '5b4e453...5ad7573'), + 'compare': ( + 'https://github.com/wraithan/readthedocs.org/compare/' + '5b4e453...5ad7573' + ), 'created': False, 'deleted': False, 'forced': False, 'pusher': { - 'name': 'none' + 'name': 'none', }, 'ref': 'refs/heads/awesome', 'repository': { @@ -235,27 +251,31 @@ def setUp(self): 'open_issues': 0, 'owner': { 'email': 'XWraithanX@gmail.com', - 'name': 'wraithan' + 'name': 'wraithan', }, 'private': False, 'pushed_at': '2011/09/12 22:33:34 -0700', 'size': 140, 'url': 'https://github.com/rtfd/readthedocs.org', 'ssh_url': 'git@github.com:rtfd/readthedocs.org.git', - 'watchers': 1 + 'watchers': 1, - } + }, } def test_post_types(self): """Ensure various POST formats.""" - r = self.client.post('/github/', - data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/github/', + data=json.dumps(self.payload), + content_type='application/json', + ) self.assertEqual(r.status_code, 200) - r = self.client.post('/github/', - data=urlencode({'payload': json.dumps(self.payload)}), - content_type='application/x-www-form-urlencoded') + r = self.client.post( + '/github/', + data=urlencode({'payload': json.dumps(self.payload)}), + content_type='application/x-www-form-urlencoded', + ) self.assertEqual(r.status_code, 200) def test_github_upper_case_repo(self): @@ -267,8 +287,10 @@ def test_github_upper_case_repo(self): """ payload = self.payload.copy() payload['repository']['url'] = payload['repository']['url'].upper() - r = self.client.post('/github/', data=json.dumps(payload), - content_type='application/json') + r = self.client.post( + '/github/', data=json.dumps(payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: HTTPS://GITHUB.COM/RTFD/READTHEDOCS.ORG [awesome]') self.payload['ref'] = 'refs/heads/not_ok' @@ -280,8 +302,10 @@ def test_400_on_no_ref(self): """ payload = self.payload.copy() del payload['ref'] - r = self.client.post('/github/', data=json.dumps(payload), - content_type='application/json') + r = self.client.post( + '/github/', data=json.dumps(payload), + content_type='application/json', + ) self.assertEqual(r.status_code, 400) def test_github_request_empty_url(self): @@ -293,7 +317,7 @@ def test_github_request_empty_url(self): self.payload['repository']['ssh_url'] = '' r = self.client.post( '/github/', data=json.dumps(self.payload), - content_type='application/json' + content_type='application/json', ) self.assertEqual(r.status_code, 403) @@ -308,8 +332,10 @@ def test_private_repo_mapping(self): self.rtfd.repo = 'git@github.com:rtfd/readthedocs.org' self.rtfd.save() payload = self.payload.copy() - r = self.client.post('/github/', data=json.dumps(payload), - content_type='application/json') + r = self.client.post( + '/github/', data=json.dumps(payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: github.com/rtfd/readthedocs.org [awesome]') def test_github_post_commit_hook_builds_branch_docs_if_it_should(self): @@ -318,18 +344,24 @@ def test_github_post_commit_hook_builds_branch_docs_if_it_should(self): versions that are set to be built if the branch they refer to is updated. Otherwise it is no op. """ - r = self.client.post('/github/', data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/github/', data=json.dumps(self.payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: github.com/rtfd/readthedocs.org [awesome]') self.payload['ref'] = 'refs/heads/not_ok' - r = self.client.post('/github/', data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/github/', data=json.dumps(self.payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Not Building: github.com/rtfd/readthedocs.org [not_ok]') self.payload['ref'] = 'refs/heads/unknown' - r = self.client.post('/github/', data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/github/', data=json.dumps(self.payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) No known branches were pushed to.') def test_github_post_commit_knows_default_branches(self): @@ -343,8 +375,10 @@ def test_github_post_commit_knows_default_branches(self): rtd.save() self.payload['ref'] = 'refs/heads/master' - r = self.client.post('/github/', data=json.dumps(self.payload), - content_type='application/json') + r = self.client.post( + '/github/', data=json.dumps(self.payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: github.com/rtfd/readthedocs.org [latest]') rtd.default_branch = old_default @@ -363,7 +397,7 @@ def test_github_webhook_is_deprecated(self): r = self.client.post( '/github/', data=json.dumps(payload), - content_type='application/json' + content_type='application/json', ) self.assertEqual(r.status_code, 403) @@ -380,8 +414,10 @@ def test_core_commit_hook(self): rtd.save() r = self.client.post('/build/%s' % rtd.pk, {'version_slug': 'master'}) self.assertEqual(r.status_code, 302) - self.assertEqual(r._headers['location'][1], - '/projects/read-the-docs/builds/') + self.assertEqual( + r._headers['location'][1], + '/projects/read-the-docs/builds/', + ) def test_hook_state_tracking(self): rtd = Project.objects.get(slug='read-the-docs') @@ -411,21 +447,21 @@ def setUp(self): 'files': [ { 'file': 'somefile.py', - 'type': 'modified' - } + 'type': 'modified', + }, ], 'message': 'Added some feature things', 'node': 'd14d26a93fd2', 'parents': [ - '1b458191f31a' + '1b458191f31a', ], 'raw_author': 'Marcus Bertrand ', 'raw_node': 'd14d26a93fd28d3166fa81c0cd3b6f339bb95bfe', 'revision': 3, 'size': -1, 'timestamp': '2012-05-30 06:07:03', - 'utctimestamp': '2012-05-30 04:07:03+00:00' - } + 'utctimestamp': '2012-05-30 04:07:03+00:00', + }, ], 'repository': { 'absolute_url': '/pip/pip/', @@ -435,9 +471,9 @@ def setUp(self): 'owner': 'marcus', 'scm': 'hg', 'slug': 'project-x', - 'website': '' + 'website': '', }, - 'user': 'marcus' + 'user': 'marcus', } self.git_payload = { @@ -449,21 +485,21 @@ def setUp(self): 'files': [ { 'file': 'somefile.py', - 'type': 'modified' - } + 'type': 'modified', + }, ], 'message': 'Added some more things to somefile.py\n', 'node': '620ade18607a', 'parents': [ - '702c70160afc' + '702c70160afc', ], 'raw_author': 'Marcus Bertrand ', 'raw_node': '620ade18607ac42d872b568bb92acaa9a28620e9', 'revision': None, 'size': -1, 'timestamp': '2012-05-30 05:58:56', - 'utctimestamp': '2012-05-30 03:58:56+00:00' - } + 'utctimestamp': '2012-05-30 03:58:56+00:00', + }, ], 'repository': { 'absolute_url': '/sphinx/sphinx/', @@ -473,53 +509,69 @@ def setUp(self): 'owner': 'marcus', 'scm': 'git', 'slug': 'project-x', - 'website': 'https://atlassian.com/' + 'website': 'https://atlassian.com/', }, - 'user': 'marcus' + 'user': 'marcus', } def test_post_types(self): """Ensure various POST formats.""" - r = self.client.post('/bitbucket/', - data=json.dumps(self.hg_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', + data=json.dumps(self.hg_payload), + content_type='application/json', + ) self.assertEqual(r.status_code, 200) - r = self.client.post('/bitbucket/', - data=urlencode({'payload': json.dumps(self.hg_payload)}), - content_type='application/x-www-form-urlencoded') + r = self.client.post( + '/bitbucket/', + data=urlencode({'payload': json.dumps(self.hg_payload)}), + content_type='application/x-www-form-urlencoded', + ) self.assertEqual(r.status_code, 200) def test_bitbucket_post_commit(self): - r = self.client.post('/bitbucket/', data=json.dumps(self.hg_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.hg_payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: bitbucket.org/pip/pip [latest]') - r = self.client.post('/bitbucket/', data=json.dumps(self.git_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.git_payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: bitbucket.org/sphinx/sphinx [latest]') def test_bitbucket_post_commit_empty_commit_list(self): self.hg_payload['commits'] = [] self.git_payload['commits'] = [] - r = self.client.post('/bitbucket/', data=json.dumps(self.hg_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.hg_payload), + content_type='application/json', + ) self.assertContains(r, 'Commit/branch not found', status_code=404) - r = self.client.post('/bitbucket/', data=json.dumps(self.git_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.git_payload), + content_type='application/json', + ) self.assertContains(r, 'Commit/branch not found', status_code=404) def test_bitbucket_post_commit_non_existent_url(self): self.hg_payload['repository']['absolute_url'] = '/invalid/repository' self.git_payload['repository']['absolute_url'] = '/invalid/repository' - r = self.client.post('/bitbucket/', data=json.dumps(self.hg_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.hg_payload), + content_type='application/json', + ) self.assertContains(r, 'Project match not found', status_code=404) - r = self.client.post('/bitbucket/', data=json.dumps(self.git_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.git_payload), + content_type='application/json', + ) self.assertContains(r, 'Project match not found', status_code=404) @@ -529,22 +581,28 @@ def test_bitbucket_post_commit_hook_builds_branch_docs_if_it_should(self): versions that are set to be built if the branch they refer to is updated. Otherwise it is no op. """ - r = self.client.post('/bitbucket/', data=json.dumps(self.hg_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.hg_payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: bitbucket.org/pip/pip [latest]') self.hg_payload['commits'] = [{ "branch": "not_ok", }] - r = self.client.post('/bitbucket/', data=json.dumps(self.hg_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.hg_payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Not Building: bitbucket.org/pip/pip [not_ok]') self.hg_payload['commits'] = [{ "branch": "unknown", }] - r = self.client.post('/bitbucket/', data=json.dumps(self.hg_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.hg_payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) No known branches were pushed to.') def test_bitbucket_default_branch(self): @@ -558,11 +616,13 @@ def test_bitbucket_default_branch(self): 'branch': 'integration', }] self.git_payload['repository'] = { - 'absolute_url': '/test/project/' + 'absolute_url': '/test/project/', } - r = self.client.post('/bitbucket/', data=json.dumps(self.git_payload), - content_type='application/json') + r = self.client.post( + '/bitbucket/', data=json.dumps(self.git_payload), + content_type='application/json', + ) self.assertContains(r, '(URL Build) Build Started: bitbucket.org/test/project [latest]') def test_bitbucket_request_empty_url(self): @@ -573,7 +633,7 @@ def test_bitbucket_request_empty_url(self): self.git_payload['repository']['absolute_url'] = '' r = self.client.post( '/bitbucket/', data=json.dumps(self.git_payload), - content_type='application/json' + content_type='application/json', ) self.assertEqual(r.status_code, 400) @@ -590,6 +650,6 @@ def test_bitbucket_webhook_is_deprecated(self): r = self.client.post( '/bitbucket/', data=json.dumps(payload), - content_type='application/json' + content_type='application/json', ) self.assertEqual(r.status_code, 403) diff --git a/readthedocs/rtd_tests/tests/test_privacy.py b/readthedocs/rtd_tests/tests/test_privacy.py index e273eafc4e1..2f55a0fd9e3 100644 --- a/readthedocs/rtd_tests/tests/test_privacy.py +++ b/readthedocs/rtd_tests/tests/test_privacy.py @@ -30,8 +30,10 @@ def setUp(self): tasks.update_docs_task.delay = mock.Mock() - def _create_kong(self, privacy_level='private', - version_privacy_level='private'): + def _create_kong( + self, privacy_level='private', + version_privacy_level='private', + ): self.client.login(username='eric', password='test') log.info( 'Making kong with privacy: %s and version privacy: %s', @@ -40,17 +42,20 @@ def _create_kong(self, privacy_level='private', ) # Create project via project form, simulate import wizard without magic form = UpdateProjectForm( - data={'repo_type': 'git', - 'repo': 'https://github.com/ericholscher/django-kong', - 'name': 'Django Kong', - 'language': 'en', - 'default_branch': '', - 'project_url': 'http://django-kong.rtfd.org', - 'default_version': LATEST, - 'python_interpreter': 'python', - 'description': 'OOHHH AH AH AH KONG SMASH', - 'documentation_type': 'sphinx'}, - user=User.objects.get(username='eric')) + data={ + 'repo_type': 'git', + 'repo': 'https://github.com/ericholscher/django-kong', + 'name': 'Django Kong', + 'language': 'en', + 'default_branch': '', + 'project_url': 'http://django-kong.rtfd.org', + 'default_version': LATEST, + 'python_interpreter': 'python', + 'description': 'OOHHH AH AH AH KONG SMASH', + 'documentation_type': 'sphinx', + }, + user=User.objects.get(username='eric'), + ) proj = form.save() # Update these directly, no form has all the fields we need proj.privacy_level = privacy_level @@ -130,8 +135,10 @@ def test_private_branch(self): kong = self._create_kong('public', 'private') self.client.login(username='eric', password='test') - Version.objects.create(project=kong, identifier='test id', - verbose_name='test verbose', privacy_level='private', slug='test-slug', active=True) + Version.objects.create( + project=kong, identifier='test id', + verbose_name='test verbose', privacy_level='private', slug='test-slug', active=True, + ) self.assertEqual(Version.objects.count(), 2) self.assertEqual(Version.objects.get(slug='test-slug').privacy_level, 'private') r = self.client.get('/projects/django-kong/') @@ -150,9 +157,11 @@ def test_public_branch(self): kong = self._create_kong('public', 'public') self.client.login(username='eric', password='test') - Version.objects.create(project=kong, identifier='test id', - verbose_name='test verbose', slug='test-slug', - active=True, built=True) + Version.objects.create( + project=kong, identifier='test id', + verbose_name='test verbose', slug='test-slug', + active=True, built=True, + ) self.assertEqual(Version.objects.count(), 2) self.assertEqual(Version.objects.all()[0].privacy_level, 'public') r = self.client.get('/projects/django-kong/') @@ -166,22 +175,30 @@ def test_public_branch(self): def test_public_repo_api(self): self._create_kong('public', 'public') self.client.login(username='eric', password='test') - resp = self.client.get('http://testserver/api/v1/project/django-kong/', - data={'format': 'json'}) + resp = self.client.get( + 'http://testserver/api/v1/project/django-kong/', + data={'format': 'json'}, + ) self.assertEqual(resp.status_code, 200) - resp = self.client.get('http://testserver/api/v1/project/', - data={'format': 'json'}) + resp = self.client.get( + 'http://testserver/api/v1/project/', + data={'format': 'json'}, + ) self.assertEqual(resp.status_code, 200) data = json.loads(resp.content) self.assertEqual(data['meta']['total_count'], 1) self.client.login(username='tester', password='test') - resp = self.client.get('http://testserver/api/v1/project/django-kong/', - data={'format': 'json'}) + resp = self.client.get( + 'http://testserver/api/v1/project/django-kong/', + data={'format': 'json'}, + ) self.assertEqual(resp.status_code, 200) - resp = self.client.get('http://testserver/api/v1/project/', - data={'format': 'json'}) + resp = self.client.get( + 'http://testserver/api/v1/project/', + data={'format': 'json'}, + ) self.assertEqual(resp.status_code, 200) data = json.loads(resp.content) self.assertEqual(data['meta']['total_count'], 1) @@ -189,21 +206,29 @@ def test_public_repo_api(self): def test_private_repo_api(self): self._create_kong('private', 'private') self.client.login(username='eric', password='test') - resp = self.client.get('http://testserver/api/v1/project/django-kong/', - data={'format': 'json'}) + resp = self.client.get( + 'http://testserver/api/v1/project/django-kong/', + data={'format': 'json'}, + ) self.assertEqual(resp.status_code, 200) - resp = self.client.get('http://testserver/api/v1/project/', - data={'format': 'json'}) + resp = self.client.get( + 'http://testserver/api/v1/project/', + data={'format': 'json'}, + ) self.assertEqual(resp.status_code, 200) data = json.loads(resp.content) self.assertEqual(data['meta']['total_count'], 1) self.client.login(username='tester', password='test') - resp = self.client.get('http://testserver/api/v1/project/django-kong/', - data={'format': 'json'}) + resp = self.client.get( + 'http://testserver/api/v1/project/django-kong/', + data={'format': 'json'}, + ) self.assertEqual(resp.status_code, 404) - resp = self.client.get('http://testserver/api/v1/project/', - data={'format': 'json'}) + resp = self.client.get( + 'http://testserver/api/v1/project/', + data={'format': 'json'}, + ) self.assertEqual(resp.status_code, 200) data = json.loads(resp.content) self.assertEqual(data['meta']['total_count'], 0) @@ -212,11 +237,17 @@ def test_private_doc_serving(self): kong = self._create_kong('public', 'private') self.client.login(username='eric', password='test') - Version.objects.create(project=kong, identifier='test id', - verbose_name='test verbose', privacy_level='private', slug='test-slug', active=True) - self.client.post('/dashboard/django-kong/versions/', - {'version-test-slug': 'on', - 'privacy-test-slug': 'private'}) + Version.objects.create( + project=kong, identifier='test id', + verbose_name='test verbose', privacy_level='private', slug='test-slug', active=True, + ) + self.client.post( + '/dashboard/django-kong/versions/', + { + 'version-test-slug': 'on', + 'privacy-test-slug': 'private', + }, + ) r = self.client.get('/docs/django-kong/en/test-slug/') self.client.login(username='eric', password='test') self.assertEqual(r.status_code, 404) @@ -350,8 +381,10 @@ def test_build_filtering(self): kong = self._create_kong('public', 'private') self.client.login(username='eric', password='test') - ver = Version.objects.create(project=kong, identifier='test id', - verbose_name='test verbose', privacy_level='private', slug='test-slug', active=True) + ver = Version.objects.create( + project=kong, identifier='test id', + verbose_name='test verbose', privacy_level='private', slug='test-slug', active=True, + ) r = self.client.get('/projects/django-kong/builds/') self.assertContains(r, 'test-slug') @@ -370,5 +403,5 @@ def test_queryset_chaining(self): kong = self._create_kong('public', 'private') self.assertEqual( kong.versions.private().get(slug='latest').slug, - 'latest' + 'latest', ) diff --git a/readthedocs/rtd_tests/tests/test_privacy_urls.py b/readthedocs/rtd_tests/tests/test_privacy_urls.py index 4c874db49bb..9bffbb82558 100644 --- a/readthedocs/rtd_tests/tests/test_privacy_urls.py +++ b/readthedocs/rtd_tests/tests/test_privacy_urls.py @@ -71,8 +71,10 @@ def assertResponse(self, path, name=None, method=None, data=None, **kwargs): val, ('Attribute mismatch for view {view} ({path}): ' '{key} != {expected} (got {value})' - .format(view=name, path=path, key=key, expected=val, - value=resp_val)) + .format( + view=name, path=path, key=key, expected=val, + value=resp_val, + )), ) return response @@ -104,7 +106,8 @@ def _test_url(self, urlpatterns): url_ctx = self.get_url_path_ctx() if url_ctx: self.response_data = { - url.format(**url_ctx): data for url, data in self.response_data.items()} + url.format(**url_ctx): data for url, data in self.response_data.items() + } for (view, regex, namespace, name) in deconstructed_urls: request_data = self.request_data.get(name, {}).copy() @@ -123,10 +126,14 @@ def setUp(self): # Previous Fixtures self.owner = create_user(username='owner', password='test') self.tester = create_user(username='tester', password='test') - self.pip = get(Project, slug='pip', users=[self.owner], - privacy_level='public', main_language_project=None) - self.private = get(Project, slug='private', privacy_level='private', - main_language_project=None) + self.pip = get( + Project, slug='pip', users=[self.owner], + privacy_level='public', main_language_project=None, + ) + self.private = get( + Project, slug='private', privacy_level='private', + main_language_project=None, + ) class ProjectMixin(URLAccessMixin): @@ -135,8 +142,10 @@ def setUp(self): super().setUp() self.build = get(Build, project=self.pip) self.tag = get(Tag, slug='coolness') - self.subproject = get(Project, slug='sub', language='ja', - users=[self.owner], main_language_project=None) + self.subproject = get( + Project, slug='sub', language='ja', + users=[self.owner], main_language_project=None, + ) self.pip.add_subproject(self.subproject) self.pip.translations.add(self.subproject) self.integration = get(Integration, project=self.pip, provider_data='') diff --git a/readthedocs/rtd_tests/tests/test_profile_views.py b/readthedocs/rtd_tests/tests/test_profile_views.py index 1131de23b97..de7c244bf1b 100644 --- a/readthedocs/rtd_tests/tests/test_profile_views.py +++ b/readthedocs/rtd_tests/tests/test_profile_views.py @@ -24,7 +24,7 @@ def test_edit_profile(self): 'first_name': 'Read', 'last_name': 'Docs', 'homepage': 'readthedocs.org', - } + }, ) self.assertTrue(resp.status_code, 200) @@ -46,7 +46,7 @@ def test_edit_profile_with_invalid_values(self): 'first_name': 'a' * 31, 'last_name': 'b' * 31, 'homepage': 'c' * 101, - } + }, ) FORM_ERROR_FORMAT = 'Ensure this value has at most {} characters (it has {}).' @@ -57,20 +57,20 @@ def test_edit_profile_with_invalid_values(self): def test_delete_account(self): resp = self.client.get( - reverse('delete_account') + reverse('delete_account'), ) self.assertEqual(resp.status_code, 200) resp = self.client.post( reverse('delete_account'), data={ 'username': self.user.username, - } + }, ) self.assertEqual(resp.status_code, 302) self.assertEqual(resp['Location'], reverse('homepage')) self.assertFalse( - User.objects.filter(username=self.user.username).exists() + User.objects.filter(username=self.user.username).exists(), ) def test_profile_detail(self): @@ -94,7 +94,7 @@ def test_profile_detail_not_found(self): def test_account_advertising(self): resp = self.client.get( - reverse('account_advertising') + reverse('account_advertising'), ) self.assertEqual(resp.status_code, 200) self.assertTrue(self.user.profile.allow_ads) diff --git a/readthedocs/rtd_tests/tests/test_project.py b/readthedocs/rtd_tests/tests/test_project.py index 5562d3e80a3..4a56bb9395e 100644 --- a/readthedocs/rtd_tests/tests/test_project.py +++ b/readthedocs/rtd_tests/tests/test_project.py @@ -104,7 +104,8 @@ def test_conf_file_not_found(self, find_method, full_find_method): full_find_method.return_value = [] with self.assertRaisesMessage( ProjectConfigurationError, - ProjectConfigurationError.NOT_FOUND) as cm: + ProjectConfigurationError.NOT_FOUND, + ) as cm: self.pip.conf_file() @patch('readthedocs.projects.models.Project.find') @@ -116,7 +117,8 @@ def test_multiple_conf_files(self, find_method): ] with self.assertRaisesMessage( ProjectConfigurationError, - ProjectConfigurationError.MULTIPLE_CONF_FILES) as cm: + ProjectConfigurationError.MULTIPLE_CONF_FILES, + ) as cm: self.pip.conf_file() @@ -163,23 +165,24 @@ def test_translation_delete(self): self.assertFalse(Project.objects.filter(pk=project_delete.pk).exists()) self.assertTrue(Project.objects.filter(pk=project_keep.pk).exists()) self.assertIsNone( - Project.objects.get(pk=project_keep.pk).main_language_project) + Project.objects.get(pk=project_keep.pk).main_language_project, + ) def test_user_can_add_own_project_as_translation(self): user_a = User.objects.get(username='eric') project_a = get( Project, users=[user_a], - language='en', main_language_project=None + language='en', main_language_project=None, ) project_b = get( Project, users=[user_a], - language='es', main_language_project=None + language='es', main_language_project=None, ) self.client.login(username=user_a.username, password='test') self.client.post( reverse('projects_translations', args=[project_a.slug]), - data={'project': project_b.slug} + data={'project': project_b.slug}, ) self.assertEqual(project_a.translations.first(), project_b) @@ -191,20 +194,20 @@ def test_user_can_add_project_as_translation_if_is_owner(self): user_a = User.objects.get(username='eric') project_a = get( Project, users=[user_a], - language='es', main_language_project=None + language='es', main_language_project=None, ) user_b = User.objects.get(username='tester') # User A and B are owners of project B project_b = get( Project, users=[user_b, user_a], - language='en', main_language_project=None + language='en', main_language_project=None, ) self.client.login(username=user_a.username, password='test') self.client.post( reverse('projects_translations', args=[project_a.slug]), - data={'project': project_b.slug} + data={'project': project_b.slug}, ) self.assertEqual(project_a.translations.first(), project_b) @@ -214,20 +217,20 @@ def test_user_can_not_add_other_user_project_as_translation(self): user_a = User.objects.get(username='eric') project_a = get( Project, users=[user_a], - language='es', main_language_project=None + language='es', main_language_project=None, ) user_b = User.objects.get(username='tester') project_b = get( Project, users=[user_b], - language='en', main_language_project=None + language='en', main_language_project=None, ) # User A try to add project B as translation of project A self.client.login(username=user_a.username, password='test') resp = self.client.post( reverse('projects_translations', args=[project_a.slug]), - data={'project': project_b.slug} + data={'project': project_b.slug}, ) self.assertContains(resp, 'Select a valid choice') @@ -243,13 +246,13 @@ def test_previous_users_can_list_and_delete_translations_not_owner(self): user_a = User.objects.get(username='eric') project_a = get( Project, users=[user_a], - language='es', main_language_project=None + language='es', main_language_project=None, ) user_b = User.objects.get(username='tester') project_b = get( Project, users=[user_b], - language='en', main_language_project=None + language='en', main_language_project=None, ) project_a.translations.add(project_b) @@ -259,16 +262,16 @@ def test_previous_users_can_list_and_delete_translations_not_owner(self): # Project B is listed under user A translations resp = self.client.get( - reverse('projects_translations', args=[project_a.slug]) + reverse('projects_translations', args=[project_a.slug]), ) self.assertContains(resp, project_b.slug) resp = self.client.post( reverse( 'projects_translations_delete', - args=[project_a.slug, project_b.slug] + args=[project_a.slug, project_b.slug], ), - follow=True + follow=True, ) self.assertEqual(resp.status_code, 200) self.assertNotIn(project_b, project_a.translations.all()) @@ -277,11 +280,11 @@ def test_user_cant_delete_other_user_translations(self): user_a = User.objects.get(username='eric') project_a = get( Project, users=[user_a], - language='es', main_language_project=None + language='es', main_language_project=None, ) project_b = get( Project, users=[user_a], - language='en', main_language_project=None + language='en', main_language_project=None, ) project_a.translations.add(project_b) @@ -290,11 +293,11 @@ def test_user_cant_delete_other_user_translations(self): user_b = User.objects.get(username='tester') project_c = get( Project, users=[user_b], - language='es', main_language_project=None + language='es', main_language_project=None, ) project_d = get( Project, users=[user_b, user_a], - language='en', main_language_project=None + language='en', main_language_project=None, ) project_d.translations.add(project_c) project_d.save() @@ -305,9 +308,9 @@ def test_user_cant_delete_other_user_translations(self): resp = self.client.post( reverse( 'projects_translations_delete', - args=[project_a.slug, project_b.slug] + args=[project_a.slug, project_b.slug], ), - follow=True + follow=True, ) self.assertEqual(resp.status_code, 404) self.assertIn(project_b, project_a.translations.all()) @@ -319,9 +322,9 @@ def test_user_cant_delete_other_user_translations(self): resp = self.client.post( reverse( 'projects_translations_delete', - args=[project_d.slug, project_b.slug] + args=[project_d.slug, project_b.slug], ), - follow=True + follow=True, ) self.assertEqual(resp.status_code, 404) self.assertIn(project_b, project_a.translations.all()) @@ -333,9 +336,9 @@ def test_user_cant_delete_other_user_translations(self): resp = self.client.post( reverse( 'projects_translations_delete', - args=[project_b.slug, project_b.slug] + args=[project_b.slug, project_b.slug], ), - follow=True + follow=True, ) self.assertEqual(resp.status_code, 404) self.assertIn(project_b, project_a.translations.all()) @@ -345,7 +348,7 @@ def test_user_cant_change_lang_to_translation_lang(self): project_a = Project.objects.get(slug='read-the-docs') project_b = get( Project, users=[user_a], - language='es', main_language_project=None + language='es', main_language_project=None, ) project_a.translations.add(project_b) @@ -362,16 +365,16 @@ def test_user_cant_change_lang_to_translation_lang(self): resp = self.client.post( reverse( 'projects_edit', - args=[project_a.slug] + args=[project_a.slug], ), data=data, - follow=True + follow=True, ) self.assertEqual(resp.status_code, 200) self.assertContains( resp, 'There is already a "es" translation ' - 'for the read-the-docs project' + 'for the read-the-docs project', ) def test_user_can_change_project_with_same_lang(self): @@ -379,7 +382,7 @@ def test_user_can_change_project_with_same_lang(self): project_a = Project.objects.get(slug='read-the-docs') project_b = get( Project, users=[user_a], - language='es', main_language_project=None + language='es', main_language_project=None, ) project_a.translations.add(project_b) @@ -396,10 +399,10 @@ def test_user_can_change_project_with_same_lang(self): resp = self.client.post( reverse( 'projects_edit', - args=[project_a.slug] + args=[project_a.slug], ), data=data, - follow=True + follow=True, ) self.assertEqual(resp.status_code, 200) self.assertNotContains(resp, 'There is already a') @@ -430,7 +433,8 @@ def setUp(self): state=BUILD_STATE_TRIGGERED, ) self.build_2.date = ( - timezone.now() - datetime.timedelta(hours=1)) + timezone.now() - datetime.timedelta(hours=1) + ) self.build_2.save() # Build started an hour ago with custom time (2 hours) @@ -440,7 +444,8 @@ def setUp(self): state=BUILD_STATE_TRIGGERED, ) self.build_3.date = ( - timezone.now() - datetime.timedelta(hours=1)) + timezone.now() - datetime.timedelta(hours=1) + ) self.build_3.save() def test_finish_inactive_builds_task(self): diff --git a/readthedocs/rtd_tests/tests/test_project_forms.py b/readthedocs/rtd_tests/tests/test_project_forms.py index 3e52496a5b1..cd927eed564 100644 --- a/readthedocs/rtd_tests/tests/test_project_forms.py +++ b/readthedocs/rtd_tests/tests/test_project_forms.py @@ -160,7 +160,7 @@ def test_changing_vcs_should_not_change_latest_is_not_none(self): def test_length_of_tags(self): data = { 'documentation_type': 'sphinx', - 'language': 'en' + 'language': 'en', } data['tags'] = '{},{}'.format('a'*50, 'b'*99) form = ProjectExtraForm(data) @@ -212,7 +212,7 @@ def setUp(self): slug='public-4', active=False, privacy_level=PUBLIC, - identifier='public/4' + identifier='public/4', ) get( Version, @@ -254,7 +254,7 @@ def test_list_all_versions_on_default_branch(self): }, { None, 'master', 'public-1', 'public-2', - 'public-3', 'public/4', 'protected', 'private' + 'public-3', 'public/4', 'protected', 'private', }, ) @@ -275,7 +275,7 @@ def setUp(self): self.project_s_fr = self.get_project( lang='fr', - users=[self.user_b, self.user_a] + users=[self.user_b, self.user_a], ) def get_project(self, lang, users, **kwargs): @@ -300,7 +300,7 @@ def test_list_only_owner_projects(self): ] self.assertEqual( {proj_slug for proj_slug, _ in form.fields['project'].choices}, - {project.slug for project in expected_projects} + {project.slug for project in expected_projects}, ) form = TranslationForm( @@ -315,7 +315,7 @@ def test_list_only_owner_projects(self): ] self.assertEqual( {proj_slug for proj_slug, _ in form.fields['project'].choices}, - {project.slug for project in expected_projects} + {project.slug for project in expected_projects}, ) def test_excludes_existing_translations(self): @@ -336,7 +336,7 @@ def test_excludes_existing_translations(self): ] self.assertEqual( {proj_slug for proj_slug, _ in form.fields['project'].choices}, - {project.slug for project in expected_projects} + {project.slug for project in expected_projects}, ) def test_user_cant_add_other_user_project(self): @@ -348,11 +348,11 @@ def test_user_cant_add_other_user_project(self): self.assertFalse(form.is_valid()) self.assertIn( 'Select a valid choice', - ''.join(form.errors['project']) + ''.join(form.errors['project']), ) self.assertNotIn( self.project_f_ar, - [proj_slug for proj_slug, _ in form.fields['project'].choices] + [proj_slug for proj_slug, _ in form.fields['project'].choices], ) def test_user_cant_add_project_with_same_lang(self): @@ -364,7 +364,7 @@ def test_user_cant_add_project_with_same_lang(self): self.assertFalse(form.is_valid()) self.assertIn( 'Both projects can not have the same language (English).', - ''.join(form.errors['project']) + ''.join(form.errors['project']), ) def test_user_cant_add_project_with_same_lang_of_other_translation(self): @@ -379,7 +379,7 @@ def test_user_cant_add_project_with_same_lang_of_other_translation(self): self.assertFalse(form.is_valid()) self.assertIn( 'This project already has a translation for English.', - ''.join(form.errors['project']) + ''.join(form.errors['project']), ) def test_no_nesting_translation(self): @@ -394,7 +394,7 @@ def test_no_nesting_translation(self): self.assertFalse(form.is_valid()) self.assertIn( 'Select a valid choice', - ''.join(form.errors['project']) + ''.join(form.errors['project']), ) def test_no_nesting_translation_case_2(self): @@ -409,7 +409,7 @@ def test_no_nesting_translation_case_2(self): self.assertFalse(form.is_valid()) self.assertIn( 'A project with existing translations can not', - ''.join(form.errors['project']) + ''.join(form.errors['project']), ) def test_not_already_translation(self): @@ -424,7 +424,7 @@ def test_not_already_translation(self): self.assertFalse(form.is_valid()) self.assertIn( 'is already a translation', - ''.join(form.errors['project']) + ''.join(form.errors['project']), ) def test_cant_change_language_to_translation_lang(self): @@ -438,12 +438,12 @@ def test_cant_change_language_to_translation_lang(self): 'documentation_type': 'sphinx', 'language': 'en', }, - instance=self.project_a_es + instance=self.project_a_es, ) self.assertFalse(form.is_valid()) self.assertIn( 'There is already a "en" translation', - ''.join(form.errors['language']) + ''.join(form.errors['language']), ) # Translation tries to change lang @@ -452,12 +452,12 @@ def test_cant_change_language_to_translation_lang(self): 'documentation_type': 'sphinx', 'language': 'es', }, - instance=self.project_b_en + instance=self.project_b_en, ) self.assertFalse(form.is_valid()) self.assertIn( 'There is already a "es" translation', - ''.join(form.errors['language']) + ''.join(form.errors['language']), ) # Translation tries to change lang @@ -467,12 +467,12 @@ def test_cant_change_language_to_translation_lang(self): 'documentation_type': 'sphinx', 'language': 'br', }, - instance=self.project_b_en + instance=self.project_b_en, ) self.assertFalse(form.is_valid()) self.assertIn( 'There is already a "br" translation', - ''.join(form.errors['language']) + ''.join(form.errors['language']), ) def test_can_change_language_to_self_lang(self): @@ -489,7 +489,7 @@ def test_can_change_language_to_self_lang(self): 'documentation_type': 'sphinx', 'language': 'es', }, - instance=self.project_a_es + instance=self.project_a_es, ) self.assertTrue(form.is_valid()) @@ -502,7 +502,7 @@ def test_can_change_language_to_self_lang(self): 'documentation_type': 'sphinx', 'language': 'en', }, - instance=self.project_b_en + instance=self.project_b_en, ) self.assertTrue(form.is_valid()) diff --git a/readthedocs/rtd_tests/tests/test_project_querysets.py b/readthedocs/rtd_tests/tests/test_project_querysets.py index 316525c9178..01f02e986a3 100644 --- a/readthedocs/rtd_tests/tests/test_project_querysets.py +++ b/readthedocs/rtd_tests/tests/test_project_querysets.py @@ -24,12 +24,12 @@ def test_subproject_queryset_as_manager_gets_correct_class(self): mgr = ChildRelatedProjectQuerySet.as_manager() self.assertEqual( mgr.__class__.__name__, - 'ManagerFromChildRelatedProjectQuerySetBase' + 'ManagerFromChildRelatedProjectQuerySetBase', ) mgr = ParentRelatedProjectQuerySet.as_manager() self.assertEqual( mgr.__class__.__name__, - 'ManagerFromParentRelatedProjectQuerySetBase' + 'ManagerFromParentRelatedProjectQuerySetBase', ) def test_is_active(self): diff --git a/readthedocs/rtd_tests/tests/test_project_symlinks.py b/readthedocs/rtd_tests/tests/test_project_symlinks.py index 3e992e6bd97..b3075167eb4 100644 --- a/readthedocs/rtd_tests/tests/test_project_symlinks.py +++ b/readthedocs/rtd_tests/tests/test_project_symlinks.py @@ -42,8 +42,10 @@ def get_filesystem(path, top_level_path=None): if os.path.islink(full_path): fs[child] = { 'type': 'link', - 'target': os.path.relpath(os.path.realpath(full_path), - top_level_path) + 'target': os.path.relpath( + os.path.realpath(full_path), + top_level_path, + ), } elif os.path.isfile(full_path): fs[child] = { @@ -77,47 +79,47 @@ def setUp(self): self.mocks = { 'PublicSymlinkBase.CNAME_ROOT': mock.patch( 'readthedocs.core.symlink.PublicSymlinkBase.CNAME_ROOT', - new_callable=mock.PropertyMock + new_callable=mock.PropertyMock, ), 'PublicSymlinkBase.WEB_ROOT': mock.patch( 'readthedocs.core.symlink.PublicSymlinkBase.WEB_ROOT', - new_callable=mock.PropertyMock + new_callable=mock.PropertyMock, ), 'PublicSymlinkBase.PROJECT_CNAME_ROOT': mock.patch( 'readthedocs.core.symlink.PublicSymlinkBase.PROJECT_CNAME_ROOT', - new_callable=mock.PropertyMock + new_callable=mock.PropertyMock, ), 'PrivateSymlinkBase.CNAME_ROOT': mock.patch( 'readthedocs.core.symlink.PrivateSymlinkBase.CNAME_ROOT', - new_callable=mock.PropertyMock + new_callable=mock.PropertyMock, ), 'PrivateSymlinkBase.WEB_ROOT': mock.patch( 'readthedocs.core.symlink.PrivateSymlinkBase.WEB_ROOT', - new_callable=mock.PropertyMock + new_callable=mock.PropertyMock, ), 'PrivateSymlinkBase.PROJECT_CNAME_ROOT': mock.patch( 'readthedocs.core.symlink.PrivateSymlinkBase.PROJECT_CNAME_ROOT', - new_callable=mock.PropertyMock + new_callable=mock.PropertyMock, ), } self.patches = {key: mock.start() for (key, mock) in list(self.mocks.items())} self.patches['PublicSymlinkBase.CNAME_ROOT'].return_value = os.path.join( - settings.SITE_ROOT, 'public_cname_root' + settings.SITE_ROOT, 'public_cname_root', ) self.patches['PublicSymlinkBase.WEB_ROOT'].return_value = os.path.join( - settings.SITE_ROOT, 'public_web_root' + settings.SITE_ROOT, 'public_web_root', ) self.patches['PublicSymlinkBase.PROJECT_CNAME_ROOT'].return_value = os.path.join( - settings.SITE_ROOT, 'public_cname_project' + settings.SITE_ROOT, 'public_cname_project', ) self.patches['PrivateSymlinkBase.CNAME_ROOT'].return_value = os.path.join( - settings.SITE_ROOT, 'private_cname_root' + settings.SITE_ROOT, 'private_cname_root', ) self.patches['PrivateSymlinkBase.WEB_ROOT'].return_value = os.path.join( - settings.SITE_ROOT, 'private_web_root' + settings.SITE_ROOT, 'private_web_root', ) self.patches['PrivateSymlinkBase.PROJECT_CNAME_ROOT'].return_value = os.path.join( - settings.SITE_ROOT, 'private_cname_project' + settings.SITE_ROOT, 'private_cname_project', ) def tearDown(self): @@ -134,26 +136,30 @@ class BaseSymlinkCnames: def setUp(self): super().setUp() - self.project = get(Project, slug='kong', privacy_level=self.privacy, - main_language_project=None) + self.project = get( + Project, slug='kong', privacy_level=self.privacy, + main_language_project=None, + ) self.project.versions.update(privacy_level=self.privacy) self.project.save() self.symlink = self.symlink_class(self.project) def test_symlink_cname(self): - self.domain = get(Domain, project=self.project, domain='woot.com', - url='http://woot.com', cname=True) + self.domain = get( + Domain, project=self.project, domain='woot.com', + url='http://woot.com', cname=True, + ) self.symlink.symlink_cnames() filesystem = { 'private_cname_project': { - 'woot.com': {'type': 'link', 'target': 'user_builds/kong'} + 'woot.com': {'type': 'link', 'target': 'user_builds/kong'}, }, 'private_cname_root': { 'woot.com': {'type': 'link', 'target': 'private_web_root/kong'}, }, 'private_web_root': {'kong': {'en': {}}}, 'public_cname_project': { - 'woot.com': {'type': 'link', 'target': 'user_builds/kong'} + 'woot.com': {'type': 'link', 'target': 'user_builds/kong'}, }, 'public_cname_root': { 'woot.com': {'type': 'link', 'target': 'public_web_root/kong'}, @@ -162,8 +168,8 @@ def test_symlink_cname(self): 'kong': {'en': {'latest': { 'type': 'link', 'target': 'user_builds/kong/rtd-builds/latest', - }}} - } + }}}, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -173,8 +179,10 @@ def test_symlink_cname(self): self.assertFilesystem(filesystem) def test_symlink_remove_orphan_symlinks(self): - self.domain = get(Domain, project=self.project, domain='woot.com', - url='http://woot.com', cname=True) + self.domain = get( + Domain, project=self.project, domain='woot.com', + url='http://woot.com', cname=True, + ) self.symlink.symlink_cnames() # Editing the Domain and calling save will symlink the new domain and @@ -204,8 +212,8 @@ def test_symlink_remove_orphan_symlinks(self): 'kong': {'en': {'latest': { 'type': 'link', 'target': 'user_builds/kong/rtd-builds/latest', - }}} - } + }}}, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -257,19 +265,21 @@ def test_broadcast_remove_orphan_symlinks(self): def test_symlink_cname_dont_link_missing_domains(self): """Domains should be relinked after deletion.""" - self.domain = get(Domain, project=self.project, domain='woot.com', - url='http://woot.com', cname=True) + self.domain = get( + Domain, project=self.project, domain='woot.com', + url='http://woot.com', cname=True, + ) self.symlink.symlink_cnames() filesystem = { 'private_cname_project': { - 'woot.com': {'type': 'link', 'target': 'user_builds/kong'} + 'woot.com': {'type': 'link', 'target': 'user_builds/kong'}, }, 'private_cname_root': { 'woot.com': {'type': 'link', 'target': 'private_web_root/kong'}, }, 'private_web_root': {'kong': {'en': {}}}, 'public_cname_project': { - 'woot.com': {'type': 'link', 'target': 'user_builds/kong'} + 'woot.com': {'type': 'link', 'target': 'user_builds/kong'}, }, 'public_cname_root': { 'woot.com': {'type': 'link', 'target': 'public_web_root/kong'}, @@ -278,8 +288,8 @@ def test_symlink_cname_dont_link_missing_domains(self): 'kong': {'en': {'latest': { 'type': 'link', 'target': 'user_builds/kong/rtd-builds/latest', - }}} - } + }}}, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -310,12 +320,16 @@ class BaseSubprojects: def setUp(self): super().setUp() - self.project = get(Project, slug='kong', privacy_level=self.privacy, - main_language_project=None) + self.project = get( + Project, slug='kong', privacy_level=self.privacy, + main_language_project=None, + ) self.project.versions.update(privacy_level=self.privacy) self.project.save() - self.subproject = get(Project, slug='sub', privacy_level=self.privacy, - main_language_project=None) + self.subproject = get( + Project, slug='sub', privacy_level=self.privacy, + main_language_project=None, + ) self.subproject.versions.update(privacy_level=self.privacy) self.subproject.save() self.symlink = self.symlink_class(self.project) @@ -343,16 +357,16 @@ def test_subproject_normal(self): 'sub': { 'type': 'link', 'target': 'public_web_root/sub', - } - } + }, + }, }, 'sub': { 'en': {'latest': { 'type': 'link', 'target': 'user_builds/sub/rtd-builds/latest', - }} - } - } + }}, + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -390,15 +404,15 @@ def test_subproject_alias(self): 'type': 'link', 'target': 'public_web_root/sub', }, - } + }, }, 'sub': { 'en': {'latest': { 'type': 'link', 'target': 'user_builds/sub/rtd-builds/latest', - }} - } - } + }}, + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -437,15 +451,15 @@ def test_subproject_alias_with_spaces(self): 'type': 'link', 'target': 'public_web_root/sub', }, - } + }, }, 'sub': { 'en': {'latest': { 'type': 'link', 'target': 'user_builds/sub/rtd-builds/latest', - }} - } - } + }}, + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -479,16 +493,16 @@ def test_remove_subprojects(self): 'sub': { 'type': 'link', 'target': 'public_web_root/sub', - } - } + }, + }, }, 'sub': { 'en': {'latest': { 'type': 'link', 'target': 'user_builds/sub/rtd-builds/latest', - }} - } - } + }}, + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -521,21 +535,29 @@ class BaseSymlinkTranslations: def setUp(self): super().setUp() - self.project = get(Project, slug='kong', privacy_level=self.privacy, - main_language_project=None) + self.project = get( + Project, slug='kong', privacy_level=self.privacy, + main_language_project=None, + ) self.project.versions.update(privacy_level=self.privacy) self.project.save() - self.translation = get(Project, slug='pip', language='de', - privacy_level=self.privacy, - main_language_project=None) + self.translation = get( + Project, slug='pip', language='de', + privacy_level=self.privacy, + main_language_project=None, + ) self.translation.versions.update(privacy_level=self.privacy) self.translation.save() self.project.translations.add(self.translation) self.symlink = self.symlink_class(self.project) - get(Version, slug='master', verbose_name='master', active=True, - project=self.project, privacy_level=self.privacy) - get(Version, slug='master', verbose_name='master', active=True, - project=self.translation, privacy_level=self.privacy) + get( + Version, slug='master', verbose_name='master', active=True, + project=self.project, privacy_level=self.privacy, + ) + get( + Version, slug='master', verbose_name='master', active=True, + project=self.translation, privacy_level=self.privacy, + ) self.assertIn(self.translation, self.project.translations.all()) def test_symlink_basic(self): @@ -577,9 +599,9 @@ def test_symlink_basic(self): 'type': 'link', 'target': 'user_builds/pip/rtd-builds/master', }, - } - } - } + }, + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -632,9 +654,9 @@ def test_symlink_non_english(self): 'type': 'link', 'target': 'user_builds/pip/rtd-builds/master', }, - } - } - } + }, + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -689,9 +711,9 @@ def test_symlink_no_english(self): 'type': 'link', 'target': 'user_builds/pip/rtd-builds/master', }, - } - } - } + }, + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -738,9 +760,9 @@ def test_remove_language(self): 'type': 'link', 'target': 'user_builds/pip/rtd-builds/master', }, - } - } - } + }, + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -774,8 +796,10 @@ class BaseSymlinkSingleVersion: def setUp(self): super().setUp() - self.project = get(Project, slug='kong', privacy_level=self.privacy, - main_language_project=None) + self.project = get( + Project, slug='kong', privacy_level=self.privacy, + main_language_project=None, + ) self.project.versions.update(privacy_level=self.privacy) self.project.save() self.version = self.project.versions.get(slug='latest') @@ -798,7 +822,7 @@ def test_symlink_single_version(self): 'type': 'link', 'target': 'user_builds/kong/rtd-builds/latest', }, - } + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -824,8 +848,8 @@ def test_symlink_single_version_missing(self): 'kong': { 'type': 'link', 'target': 'user_builds/kong/rtd-builds/latest', - } - } + }, + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -849,13 +873,17 @@ class BaseSymlinkVersions: def setUp(self): super().setUp() - self.project = get(Project, slug='kong', privacy_level=self.privacy, - main_language_project=None) + self.project = get( + Project, slug='kong', privacy_level=self.privacy, + main_language_project=None, + ) self.project.versions.update(privacy_level=self.privacy) self.project.save() - self.stable = get(Version, slug='stable', verbose_name='stable', - active=True, project=self.project, - privacy_level=self.privacy) + self.stable = get( + Version, slug='stable', verbose_name='stable', + active=True, project=self.project, + privacy_level=self.privacy, + ) self.project.versions.update(privacy_level=self.privacy) self.symlink = self.symlink_class(self.project) @@ -882,7 +910,7 @@ def test_symlink_versions(self): }, }, }, - } + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -912,7 +940,7 @@ def test_removed_versions(self): 'target': 'user_builds/kong/rtd-builds/stable', }, }}, - } + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -951,7 +979,7 @@ def test_symlink_other_versions(self): 'type': 'link', 'target': 'user_builds/kong/rtd-builds/latest', }}}, - } + }, } if self.privacy == 'private': public_root = filesystem['public_web_root'].copy() @@ -975,11 +1003,15 @@ class TestPublicSymlinkUnicode(TempSiteRootTestCase): def setUp(self): super().setUp() - self.project = get(Project, slug='kong', name='foo-∫', - main_language_project=None) + self.project = get( + Project, slug='kong', name='foo-∫', + main_language_project=None, + ) self.project.save() - self.stable = get(Version, slug='foo-a', verbose_name='foo-∂', - active=True, project=self.project) + self.stable = get( + Version, slug='foo-a', verbose_name='foo-∂', + active=True, project=self.project, + ) self.symlink = PublicSymlink(self.project) def test_symlink_no_error(self): @@ -1045,13 +1077,15 @@ def setUp(self): self.user = get(User) self.project = get( Project, name='project', slug='project', privacy_level='public', - users=[self.user], main_language_project=None) + users=[self.user], main_language_project=None, + ) self.project.versions.update(privacy_level='public') self.project.save() self.subproject = get( Project, name='subproject', slug='subproject', privacy_level='public', - users=[self.user], main_language_project=None) + users=[self.user], main_language_project=None, + ) self.subproject.versions.update(privacy_level='public') self.subproject.save() @@ -1156,11 +1190,13 @@ def test_change_subproject_privacy(self): self.client.force_login(self.user) self.client.post( - reverse('project_version_detail', - kwargs={ - 'project_slug': self.subproject.slug, - 'version_slug': self.subproject.versions.first().slug, - }), + reverse( + 'project_version_detail', + kwargs={ + 'project_slug': self.subproject.slug, + 'version_slug': self.subproject.versions.first().slug, + }, + ), data={'privacy_level': 'private', 'active': True}, ) @@ -1168,10 +1204,12 @@ def test_change_subproject_privacy(self): self.assertTrue(self.subproject.versions.first().active) self.client.post( - reverse('projects_advanced', - kwargs={ - 'project_slug': self.subproject.slug, - }), + reverse( + 'projects_advanced', + kwargs={ + 'project_slug': self.subproject.slug, + }, + ), data={ # Required defaults 'python_interpreter': 'python', diff --git a/readthedocs/rtd_tests/tests/test_project_views.py b/readthedocs/rtd_tests/tests/test_project_views.py index b2d5c752cc0..0e7225ac65e 100644 --- a/readthedocs/rtd_tests/tests/test_project_views.py +++ b/readthedocs/rtd_tests/tests/test_project_views.py @@ -171,7 +171,8 @@ def test_form_pass(self): del self.step_data['extra']['tags'] self.assertCountEqual( [tag.name for tag in proj.tags.all()], - ['bar', 'baz', 'foo']) + ['bar', 'baz', 'foo'], + ) data.update(self.step_data['extra']) for (key, val) in list(data.items()): self.assertEqual(getattr(proj, key), val) @@ -202,8 +203,10 @@ def test_remote_repository_is_added(self): self.assertIsNotNone(proj) self.assertEqual(proj.remote_repository, remote_repo) - @patch('readthedocs.projects.views.private.ProjectExtraForm.clean_description', - create=True) + @patch( + 'readthedocs.projects.views.private.ProjectExtraForm.clean_description', + create=True, + ) def test_form_spam(self, mocked_validator): """Don't add project on a spammy description.""" self.user.date_joined = timezone.now() - timedelta(days=365) @@ -224,8 +227,10 @@ def test_form_spam(self, mocked_validator): proj = Project.objects.get(name='foobar') self.assertFalse(self.user.profile.banned) - @patch('readthedocs.projects.views.private.ProjectExtraForm.clean_description', - create=True) + @patch( + 'readthedocs.projects.views.private.ProjectExtraForm.clean_description', + create=True, + ) def test_form_spam_ban_user(self, mocked_validator): """Don't add spam and ban new user.""" self.user.date_joined = timezone.now() @@ -278,8 +283,10 @@ def test_import_demo_already_imported(self): messages = list(resp_redir.context['messages']) self.assertEqual(messages[0].level, message_const.SUCCESS) - self.assertEqual(project, - Project.objects.get(slug='eric-demo')) + self.assertEqual( + project, + Project.objects.get(slug='eric-demo'), + ) def test_import_demo_another_user_imported(self): """Import demo project after another user, expect success.""" @@ -312,11 +319,15 @@ def test_import_demo_imported_renamed(self): self.assertEqual(resp_redir.status_code, 200) messages = list(resp_redir.context['messages']) self.assertEqual(messages[0].level, message_const.SUCCESS) - self.assertRegex(messages[0].message, - r'already imported') + self.assertRegex( + messages[0].message, + r'already imported', + ) - self.assertEqual(project, - Project.objects.get(slug='eric-demo')) + self.assertEqual( + project, + Project.objects.get(slug='eric-demo'), + ) def test_import_demo_imported_duplicate(self): """ @@ -339,11 +350,15 @@ def test_import_demo_imported_duplicate(self): self.assertEqual(resp_redir.status_code, 200) messages = list(resp_redir.context['messages']) self.assertEqual(messages[0].level, message_const.ERROR) - self.assertRegex(messages[0].message, - r'There was a problem') + self.assertRegex( + messages[0].message, + r'There was a problem', + ) - self.assertEqual(project, - Project.objects.get(slug='eric-demo')) + self.assertEqual( + project, + Project.objects.get(slug='eric-demo'), + ) class TestPrivateViews(MockBuildTestCase): @@ -380,7 +395,8 @@ def test_delete_project(self): broadcast.assert_called_with( type='app', task=tasks.remove_dirs, - args=[(project.doc_path,)]) + args=[(project.doc_path,)], + ) def test_subproject_create(self): project = get(Project, slug='pip', users=[self.user]) @@ -395,7 +411,8 @@ def test_subproject_create(self): broadcast.assert_called_with( type='app', task=tasks.symlink_subproject, - args=[project.pk]) + args=[project.pk], + ) class TestPrivateMixins(MockBuildTestCase): diff --git a/readthedocs/rtd_tests/tests/test_redirects.py b/readthedocs/rtd_tests/tests/test_redirects.py index 834b4656893..6fbafebf3bc 100644 --- a/readthedocs/rtd_tests/tests/test_redirects.py +++ b/readthedocs/rtd_tests/tests/test_redirects.py @@ -22,16 +22,19 @@ def setUp(self): self.client.login(username='eric', password='test') self.client.post( '/dashboard/import/', - {'repo_type': 'git', 'name': 'Pip', - 'tags': 'big, fucking, monkey', 'default_branch': '', - 'project_url': 'http://pip.rtfd.org', - 'repo': 'https://github.com/fail/sauce', - 'csrfmiddlewaretoken': '34af7c8a5ba84b84564403a280d9a9be', - 'default_version': LATEST, - 'privacy_level': 'public', - 'version_privacy_level': 'public', - 'description': 'wat', - 'documentation_type': 'sphinx'}) + { + 'repo_type': 'git', 'name': 'Pip', + 'tags': 'big, fucking, monkey', 'default_branch': '', + 'project_url': 'http://pip.rtfd.org', + 'repo': 'https://github.com/fail/sauce', + 'csrfmiddlewaretoken': '34af7c8a5ba84b84564403a280d9a9be', + 'default_version': LATEST, + 'privacy_level': 'public', + 'version_privacy_level': 'public', + 'description': 'wat', + 'documentation_type': 'sphinx', + }, + ) pip = Project.objects.get(slug='pip') pip.versions.create_latest() @@ -43,14 +46,17 @@ def test_proper_url(self): r = self.client.get('/docs/pip/') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://readthedocs.org/docs/pip/en/latest/') + r['Location'], 'http://readthedocs.org/docs/pip/en/latest/', + ) # Specific Page Redirects def test_proper_page_on_main_site(self): r = self.client.get('/docs/pip/page/test.html') self.assertEqual(r.status_code, 302) - self.assertEqual(r['Location'], - 'http://readthedocs.org/docs/pip/en/latest/test.html') + self.assertEqual( + r['Location'], + 'http://readthedocs.org/docs/pip/en/latest/test.html', + ) # If slug is neither valid lang nor valid version, it should 404. # TODO: This should 404 directly, not redirect first @@ -88,15 +94,18 @@ def test_proper_subdomain(self): r = self.client.get('/', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/') + r['Location'], 'http://pip.readthedocs.org/en/latest/', + ) # Specific Page Redirects @override_settings(USE_SUBDOMAIN=True) def test_proper_page_on_subdomain(self): r = self.client.get('/page/test.html', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) - self.assertEqual(r['Location'], - 'http://pip.readthedocs.org/en/latest/test.html') + self.assertEqual( + r['Location'], + 'http://pip.readthedocs.org/en/latest/test.html', + ) @override_settings(USE_SUBDOMAIN=True) def test_improper_subdomain_filename_only(self): @@ -112,16 +121,19 @@ def setUp(self): self.client.login(username='eric', password='test') self.client.post( '/dashboard/import/', - {'repo_type': 'git', 'name': 'Pip', - 'tags': 'big, fucking, monkey', 'default_branch': '', - 'project_url': 'http://pip.rtfd.org', - 'repo': 'https://github.com/fail/sauce', - 'csrfmiddlewaretoken': '34af7c8a5ba84b84564403a280d9a9be', - 'default_version': LATEST, - 'privacy_level': 'public', - 'version_privacy_level': 'public', - 'description': 'wat', - 'documentation_type': 'sphinx'}) + { + 'repo_type': 'git', 'name': 'Pip', + 'tags': 'big, fucking, monkey', 'default_branch': '', + 'project_url': 'http://pip.rtfd.org', + 'repo': 'https://github.com/fail/sauce', + 'csrfmiddlewaretoken': '34af7c8a5ba84b84564403a280d9a9be', + 'default_version': LATEST, + 'privacy_level': 'public', + 'version_privacy_level': 'public', + 'description': 'wat', + 'documentation_type': 'sphinx', + }, + ) self.pip = Project.objects.get(slug='pip') self.pip.versions.create_latest() @@ -143,12 +155,14 @@ def test_redirect_prefix_infinite(self): r = self.client.get('/redirect', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/redirect.html') + r['Location'], 'http://pip.readthedocs.org/en/latest/redirect.html', + ) r = self.client.get('/redirect/', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/redirect/') + r['Location'], 'http://pip.readthedocs.org/en/latest/redirect/', + ) r = self.client.get('/en/latest/redirect/', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 404) @@ -156,33 +170,37 @@ def test_redirect_prefix_infinite(self): @override_settings(USE_SUBDOMAIN=True) def test_redirect_root(self): Redirect.objects.create( - project=self.pip, redirect_type='prefix', from_url='/woot/') + project=self.pip, redirect_type='prefix', from_url='/woot/', + ) r = self.client.get('/woot/faq.html', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/faq.html') + r['Location'], 'http://pip.readthedocs.org/en/latest/faq.html', + ) @override_settings(USE_SUBDOMAIN=True) def test_redirect_page(self): Redirect.objects.create( project=self.pip, redirect_type='page', - from_url='/install.html', to_url='/tutorial/install.html' + from_url='/install.html', to_url='/tutorial/install.html', ) r = self.client.get('/install.html', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/tutorial/install.html') + r['Location'], 'http://pip.readthedocs.org/en/latest/tutorial/install.html', + ) @override_settings(USE_SUBDOMAIN=True) def test_redirect_exact(self): Redirect.objects.create( project=self.pip, redirect_type='exact', - from_url='/en/latest/install.html', to_url='/en/latest/tutorial/install.html' + from_url='/en/latest/install.html', to_url='/en/latest/tutorial/install.html', ) r = self.client.get('/en/latest/install.html', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/tutorial/install.html') + r['Location'], 'http://pip.readthedocs.org/en/latest/tutorial/install.html', + ) @override_settings(USE_SUBDOMAIN=True) def test_redirect_exact_with_rest(self): @@ -200,7 +218,8 @@ def test_redirect_exact_with_rest(self): r = self.client.get('/en/latest/guides/install.html', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/version/guides/install.html') + r['Location'], 'http://pip.readthedocs.org/en/version/guides/install.html', + ) Redirect.objects.create( project=self.pip, redirect_type='exact', @@ -209,7 +228,8 @@ def test_redirect_exact_with_rest(self): r = self.client.get('/es/version/guides/install.html', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/master/guides/install.html') + r['Location'], 'http://pip.readthedocs.org/en/master/guides/install.html', + ) @override_settings(USE_SUBDOMAIN=True) def test_redirect_inactive_version(self): @@ -234,75 +254,94 @@ def test_redirect_inactive_version(self): r = self.client.get('/en/oldversion/', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/newversion/') + r['Location'], 'http://pip.readthedocs.org/en/newversion/', + ) @override_settings(USE_SUBDOMAIN=True) def test_redirect_keeps_version_number(self): Redirect.objects.create( project=self.pip, redirect_type='page', - from_url='/how_to_install.html', to_url='/install.html') + from_url='/how_to_install.html', to_url='/install.html', + ) with patch('readthedocs.core.views.serve._serve_symlink_docs') as _serve_docs: _serve_docs.side_effect = Http404() - r = self.client.get('/en/0.8.1/how_to_install.html', - HTTP_HOST='pip.readthedocs.org') + r = self.client.get( + '/en/0.8.1/how_to_install.html', + HTTP_HOST='pip.readthedocs.org', + ) self.assertEqual(r.status_code, 302) self.assertEqual( r['Location'], - 'http://pip.readthedocs.org/en/0.8.1/install.html') + 'http://pip.readthedocs.org/en/0.8.1/install.html', + ) @override_settings(USE_SUBDOMAIN=True) def test_redirect_keeps_language(self): Redirect.objects.create( project=self.pip, redirect_type='page', - from_url='/how_to_install.html', to_url='/install.html') + from_url='/how_to_install.html', to_url='/install.html', + ) with patch('readthedocs.core.views.serve._serve_symlink_docs') as _serve_docs: _serve_docs.side_effect = Http404() - r = self.client.get('/de/0.8.1/how_to_install.html', - HTTP_HOST='pip.readthedocs.org') + r = self.client.get( + '/de/0.8.1/how_to_install.html', + HTTP_HOST='pip.readthedocs.org', + ) self.assertEqual(r.status_code, 302) self.assertEqual( r['Location'], - 'http://pip.readthedocs.org/de/0.8.1/install.html') + 'http://pip.readthedocs.org/de/0.8.1/install.html', + ) @override_settings(USE_SUBDOMAIN=True) def test_redirect_recognizes_custom_cname(self): Redirect.objects.create( project=self.pip, redirect_type='page', from_url='/install.html', - to_url='/tutorial/install.html') - r = self.client.get('/install.html', - HTTP_HOST='pip.pypa.io', - HTTP_X_RTD_SLUG='pip') + to_url='/tutorial/install.html', + ) + r = self.client.get( + '/install.html', + HTTP_HOST='pip.pypa.io', + HTTP_X_RTD_SLUG='pip', + ) self.assertEqual(r.status_code, 302) self.assertEqual( r['Location'], - 'http://pip.pypa.io/en/latest/tutorial/install.html') + 'http://pip.pypa.io/en/latest/tutorial/install.html', + ) @override_settings(USE_SUBDOMAIN=True, PYTHON_MEDIA=True) def test_redirect_html(self): Redirect.objects.create( - project=self.pip, redirect_type='sphinx_html') + project=self.pip, redirect_type='sphinx_html', + ) r = self.client.get('/en/latest/faq/', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/faq.html') + r['Location'], 'http://pip.readthedocs.org/en/latest/faq.html', + ) @override_settings(USE_SUBDOMAIN=True, PYTHON_MEDIA=True) def test_redirect_html_index(self): Redirect.objects.create( - project=self.pip, redirect_type='sphinx_html') + project=self.pip, redirect_type='sphinx_html', + ) r = self.client.get('/en/latest/faq/index.html', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/faq.html') + r['Location'], 'http://pip.readthedocs.org/en/latest/faq.html', + ) @override_settings(USE_SUBDOMAIN=True, PYTHON_MEDIA=True) def test_redirect_htmldir(self): Redirect.objects.create( - project=self.pip, redirect_type='sphinx_htmldir') + project=self.pip, redirect_type='sphinx_htmldir', + ) r = self.client.get('/en/latest/faq.html', HTTP_HOST='pip.readthedocs.org') self.assertEqual(r.status_code, 302) self.assertEqual( - r['Location'], 'http://pip.readthedocs.org/en/latest/faq/') + r['Location'], 'http://pip.readthedocs.org/en/latest/faq/', + ) class CustomRedirectTests(TestCase): @@ -340,11 +379,13 @@ class RedirectBuildTests(TestCase): fixtures = ['eric', 'test_data'] def setUp(self): - self.project = get(Project, - slug='project-1', - documentation_type='sphinx', - conf_py_file='test_conf.py', - versions=[fixture()]) + self.project = get( + Project, + slug='project-1', + documentation_type='sphinx', + conf_py_file='test_conf.py', + versions=[fixture()], + ) self.version = self.project.versions.all()[0] def test_redirect_list(self): @@ -369,37 +410,37 @@ def setUp(self): def test_http_filenames_return_themselves(self): self.assertEqual( self.redirect.get_full_path('http://rtfd.org'), - 'http://rtfd.org' + 'http://rtfd.org', ) def test_redirects_no_subdomain(self): self.assertEqual( self.redirect.get_full_path('index.html'), - '/docs/read-the-docs/en/latest/' + '/docs/read-the-docs/en/latest/', ) @override_settings( - USE_SUBDOMAIN=True, PRODUCTION_DOMAIN='rtfd.org' + USE_SUBDOMAIN=True, PRODUCTION_DOMAIN='rtfd.org', ) def test_redirects_with_subdomain(self): self.assertEqual( self.redirect.get_full_path('faq.html'), - '/en/latest/faq.html' + '/en/latest/faq.html', ) @override_settings( - USE_SUBDOMAIN=True, PRODUCTION_DOMAIN='rtfd.org' + USE_SUBDOMAIN=True, PRODUCTION_DOMAIN='rtfd.org', ) def test_single_version_with_subdomain(self): self.redirect.project.single_version = True self.assertEqual( self.redirect.get_full_path('faq.html'), - '/faq.html' + '/faq.html', ) def test_single_version_no_subdomain(self): self.redirect.project.single_version = True self.assertEqual( self.redirect.get_full_path('faq.html'), - '/docs/read-the-docs/faq.html' + '/docs/read-the-docs/faq.html', ) diff --git a/readthedocs/rtd_tests/tests/test_resolver.py b/readthedocs/rtd_tests/tests/test_resolver.py index 2918f1ce009..81f2f3ef5ab 100644 --- a/readthedocs/rtd_tests/tests/test_resolver.py +++ b/readthedocs/rtd_tests/tests/test_resolver.py @@ -67,7 +67,8 @@ def test_resolver_filename_index(self): url = resolve_path(project=self.pip, filename='foo/bar/index.html') self.assertEqual(url, '/docs/pip/en/latest/foo/bar/') url = resolve_path( - project=self.pip, filename='foo/index/index.html') + project=self.pip, filename='foo/index/index.html', + ) self.assertEqual(url, '/docs/pip/en/latest/foo/index/') def test_resolver_filename_false_index(self): @@ -75,9 +76,11 @@ def test_resolver_filename_false_index(self): url = resolve_path(project=self.pip, filename='foo/foo_index.html') self.assertEqual(url, '/docs/pip/en/latest/foo/foo_index.html') url = resolve_path( - project=self.pip, filename='foo_index/foo_index.html') + project=self.pip, filename='foo_index/foo_index.html', + ) self.assertEqual( - url, '/docs/pip/en/latest/foo_index/foo_index.html') + url, '/docs/pip/en/latest/foo_index/foo_index.html', + ) def test_resolver_filename_sphinx(self): self.pip.documentation_type = 'sphinx' @@ -173,21 +176,25 @@ def test_resolver_force_single_version(self): self.pip.single_version = False with override_settings(USE_SUBDOMAIN=False): url = resolve_path( - project=self.pip, filename='index.html', single_version=True) + project=self.pip, filename='index.html', single_version=True, + ) self.assertEqual(url, '/docs/pip/') with override_settings(USE_SUBDOMAIN=True): url = resolve_path( - project=self.pip, filename='index.html', single_version=True) + project=self.pip, filename='index.html', single_version=True, + ) self.assertEqual(url, '/') def test_resolver_force_domain(self): with override_settings(USE_SUBDOMAIN=False): url = resolve_path( - project=self.pip, filename='index.html', cname=True) + project=self.pip, filename='index.html', cname=True, + ) self.assertEqual(url, '/en/latest/') with override_settings(USE_SUBDOMAIN=True): url = resolve_path( - project=self.pip, filename='index.html', cname=True) + project=self.pip, filename='index.html', cname=True, + ) self.assertEqual(url, '/en/latest/') def test_resolver_force_domain_single_version(self): @@ -195,66 +202,78 @@ def test_resolver_force_domain_single_version(self): with override_settings(USE_SUBDOMAIN=False): url = resolve_path( project=self.pip, filename='index.html', single_version=True, - cname=True) + cname=True, + ) self.assertEqual(url, '/') with override_settings(USE_SUBDOMAIN=True): url = resolve_path( project=self.pip, filename='index.html', single_version=True, - cname=True) + cname=True, + ) self.assertEqual(url, '/') def test_resolver_force_language(self): with override_settings(USE_SUBDOMAIN=False): url = resolve_path( - project=self.pip, filename='index.html', language='cz') + project=self.pip, filename='index.html', language='cz', + ) self.assertEqual(url, '/docs/pip/cz/latest/') with override_settings(USE_SUBDOMAIN=True): url = resolve_path( - project=self.pip, filename='index.html', language='cz') + project=self.pip, filename='index.html', language='cz', + ) self.assertEqual(url, '/cz/latest/') def test_resolver_force_version(self): with override_settings(USE_SUBDOMAIN=False): url = resolve_path( - project=self.pip, filename='index.html', version_slug='foo') + project=self.pip, filename='index.html', version_slug='foo', + ) self.assertEqual(url, '/docs/pip/en/foo/') with override_settings(USE_SUBDOMAIN=True): url = resolve_path( - project=self.pip, filename='index.html', version_slug='foo') + project=self.pip, filename='index.html', version_slug='foo', + ) self.assertEqual(url, '/en/foo/') def test_resolver_force_language_version(self): with override_settings(USE_SUBDOMAIN=False): url = resolve_path( project=self.pip, filename='index.html', language='cz', - version_slug='foo') + version_slug='foo', + ) self.assertEqual(url, '/docs/pip/cz/foo/') with override_settings(USE_SUBDOMAIN=True): url = resolve_path( project=self.pip, filename='index.html', language='cz', - version_slug='foo') + version_slug='foo', + ) self.assertEqual(url, '/cz/foo/') def test_resolver_no_force_translation(self): with override_settings(USE_SUBDOMAIN=False): url = resolve_path( - project=self.translation, filename='index.html', language='cz') + project=self.translation, filename='index.html', language='cz', + ) self.assertEqual(url, '/docs/pip/ja/latest/') with override_settings(USE_SUBDOMAIN=True): url = resolve_path( - project=self.translation, filename='index.html', language='cz') + project=self.translation, filename='index.html', language='cz', + ) self.assertEqual(url, '/ja/latest/') def test_resolver_no_force_translation_with_version(self): with override_settings(USE_SUBDOMAIN=False): url = resolve_path( project=self.translation, filename='index.html', language='cz', - version_slug='foo') + version_slug='foo', + ) self.assertEqual(url, '/docs/pip/ja/foo/') with override_settings(USE_SUBDOMAIN=True): url = resolve_path( project=self.translation, filename='index.html', language='cz', - version_slug='foo') + version_slug='foo', + ) self.assertEqual(url, '/ja/foo/') @@ -274,7 +293,7 @@ def test_project_with_same_translation_and_main_language(self): proj1.save() self.assertEqual( proj1.main_language_project.main_language_project, - proj1 + proj1, ) # This tests that we aren't going to re-recurse back to resolving proj1 @@ -428,7 +447,8 @@ def test_domain_resolver_translation_itself(self): @override_settings( PRODUCTION_DOMAIN='readthedocs.org', - PUBLIC_DOMAIN='public.readthedocs.org') + PUBLIC_DOMAIN='public.readthedocs.org', + ) def test_domain_public(self): with override_settings(USE_SUBDOMAIN=False): url = resolve_domain(project=self.translation) @@ -492,11 +512,13 @@ def test_resolver_subproject(self): with override_settings(USE_SUBDOMAIN=False): url = resolve(project=self.subproject) self.assertEqual( - url, 'http://readthedocs.org/docs/pip/projects/sub/ja/latest/') + url, 'http://readthedocs.org/docs/pip/projects/sub/ja/latest/', + ) with override_settings(USE_SUBDOMAIN=True): url = resolve(project=self.subproject) self.assertEqual( - url, 'http://pip.readthedocs.org/projects/sub/ja/latest/') + url, 'http://pip.readthedocs.org/projects/sub/ja/latest/', + ) @override_settings(PRODUCTION_DOMAIN='readthedocs.org') def test_resolver_translation(self): @@ -576,7 +598,8 @@ def test_resolver_private_version_override(self): @override_settings( PRODUCTION_DOMAIN='readthedocs.org', - PUBLIC_DOMAIN='public.readthedocs.org') + PUBLIC_DOMAIN='public.readthedocs.org', + ) def test_resolver_public_domain_overrides(self): with override_settings(USE_SUBDOMAIN=False): url = resolve(project=self.pip, private=True) @@ -586,10 +609,12 @@ def test_resolver_public_domain_overrides(self): with override_settings(USE_SUBDOMAIN=True): url = resolve(project=self.pip, private=True) self.assertEqual( - url, 'http://pip.public.readthedocs.org/en/latest/') + url, 'http://pip.public.readthedocs.org/en/latest/', + ) url = resolve(project=self.pip, private=False) self.assertEqual( - url, 'http://pip.public.readthedocs.org/en/latest/') + url, 'http://pip.public.readthedocs.org/en/latest/', + ) # Domain overrides PUBLIC_DOMAIN self.domain = fixture.get( @@ -720,20 +745,24 @@ def test_subproject_with_translation_without_custom_domain(self): self.assertEqual( url, 'http://{project.slug}.readthedocs.io/en/latest/'.format( project=self.superproject_en, - )) + ), + ) url = resolve(self.superproject_es, filename='') self.assertEqual( url, 'http://{project.slug}.readthedocs.io/es/latest/'.format( project=self.superproject_en, - )) + ), + ) url = resolve(self.subproject_en, filename='') # yapf: disable self.assertEqual( url, - ('http://{project.slug}.readthedocs.io/projects/' - '{subproject.slug}/en/latest/').format( + ( + 'http://{project.slug}.readthedocs.io/projects/' + '{subproject.slug}/en/latest/' + ).format( project=self.superproject_en, subproject=self.subproject_en, ), @@ -742,8 +771,10 @@ def test_subproject_with_translation_without_custom_domain(self): url = resolve(self.subproject_es, filename='') self.assertEqual( url, - ('http://{project.slug}.readthedocs.io/projects/' - '{subproject.slug}/es/latest/').format( + ( + 'http://{project.slug}.readthedocs.io/projects/' + '{subproject.slug}/es/latest/' + ).format( project=self.superproject_en, subproject=self.subproject_en, ), @@ -770,8 +801,10 @@ def test_subproject_with_translation_with_custom_domain(self): url = resolve(self.subproject_en, filename='') self.assertEqual( url, - ('http://docs.example.com/projects/' - '{subproject.slug}/en/latest/').format( + ( + 'http://docs.example.com/projects/' + '{subproject.slug}/en/latest/' + ).format( subproject=self.subproject_en, ), ) @@ -779,8 +812,10 @@ def test_subproject_with_translation_with_custom_domain(self): url = resolve(self.subproject_es, filename='') self.assertEqual( url, - ('http://docs.example.com/projects/' - '{subproject.slug}/es/latest/').format( + ( + 'http://docs.example.com/projects/' + '{subproject.slug}/es/latest/' + ).format( subproject=self.subproject_en, ), ) diff --git a/readthedocs/rtd_tests/tests/test_restapi_client.py b/readthedocs/rtd_tests/tests/test_restapi_client.py index 7f75d0f5ead..cf555b74883 100644 --- a/readthedocs/rtd_tests/tests/test_restapi_client.py +++ b/readthedocs/rtd_tests/tests/test_restapi_client.py @@ -6,7 +6,7 @@ class TestDrfJsonSerializer(TestCase): data = { - 'proper': 'json' + 'proper': 'json', } serialized_data = '{"proper":"json"}' diff --git a/readthedocs/rtd_tests/tests/test_search_json_parsing.py b/readthedocs/rtd_tests/tests/test_search_json_parsing.py index d2ac34e34c9..42b0839c4e9 100644 --- a/readthedocs/rtd_tests/tests/test_search_json_parsing.py +++ b/readthedocs/rtd_tests/tests/test_search_json_parsing.py @@ -15,7 +15,7 @@ def test_h2_parsing(self): os.path.join( base_dir, 'files/api.fjson', - ) + ), ) self.assertEqual(data['sections'][1]['id'], 'a-basic-api-client-using-slumber') # Only capture h2's after the first section diff --git a/readthedocs/rtd_tests/tests/test_single_version.py b/readthedocs/rtd_tests/tests/test_single_version.py index 567e96b5f70..e69b80375af 100644 --- a/readthedocs/rtd_tests/tests/test_single_version.py +++ b/readthedocs/rtd_tests/tests/test_single_version.py @@ -7,7 +7,7 @@ @override_settings( - USE_SUBDOMAIN=True, PUBLIC_DOMAIN='public.readthedocs.org', SERVE_PUBLIC_DOCS=True + USE_SUBDOMAIN=True, PUBLIC_DOMAIN='public.readthedocs.org', SERVE_PUBLIC_DOCS=True, ) class RedirectSingleVersionTests(TestCase): @@ -16,16 +16,24 @@ def setUp(self): def test_docs_url_generation(self): with override_settings(USE_SUBDOMAIN=False): - self.assertEqual(self.pip.get_docs_url(), - 'http://readthedocs.org/docs/pip/') + self.assertEqual( + self.pip.get_docs_url(), + 'http://readthedocs.org/docs/pip/', + ) with override_settings(USE_SUBDOMAIN=True): - self.assertEqual(self.pip.get_docs_url(), - 'http://pip.public.readthedocs.org/') + self.assertEqual( + self.pip.get_docs_url(), + 'http://pip.public.readthedocs.org/', + ) self.pip.single_version = False with override_settings(USE_SUBDOMAIN=False): - self.assertEqual(self.pip.get_docs_url(), - 'http://readthedocs.org/docs/pip/en/latest/') + self.assertEqual( + self.pip.get_docs_url(), + 'http://readthedocs.org/docs/pip/en/latest/', + ) with override_settings(USE_SUBDOMAIN=True): - self.assertEqual(self.pip.get_docs_url(), - 'http://pip.public.readthedocs.org/en/latest/') + self.assertEqual( + self.pip.get_docs_url(), + 'http://pip.public.readthedocs.org/en/latest/', + ) diff --git a/readthedocs/rtd_tests/tests/test_subprojects.py b/readthedocs/rtd_tests/tests/test_subprojects.py index d6545189e2e..255325d7a2f 100644 --- a/readthedocs/rtd_tests/tests/test_subprojects.py +++ b/readthedocs/rtd_tests/tests/test_subprojects.py @@ -18,13 +18,13 @@ def test_empty_child(self): form = ProjectRelationshipForm( {}, project=project, - user=user + user=user, ) form.full_clean() self.assertEqual(len(form.errors['child']), 1) self.assertRegex( form.errors['child'][0], - r'This field is required.' + r'This field is required.', ) def test_nonexistent_child(self): @@ -34,13 +34,13 @@ def test_nonexistent_child(self): form = ProjectRelationshipForm( {'child': 9999}, project=project, - user=user + user=user, ) form.full_clean() self.assertEqual(len(form.errors['child']), 1) self.assertRegex( form.errors['child'][0], - r'Select a valid choice.' + r'Select a valid choice.', ) def test_adding_subproject_fails_when_user_is_not_admin(self): @@ -57,13 +57,13 @@ def test_adding_subproject_fails_when_user_is_not_admin(self): form = ProjectRelationshipForm( {'child': subproject.pk}, project=project, - user=user + user=user, ) form.full_clean() self.assertEqual(len(form.errors['child']), 1) self.assertRegex( form.errors['child'][0], - r'Select a valid choice.' + r'Select a valid choice.', ) def test_adding_subproject_passes_when_user_is_admin(self): @@ -81,14 +81,14 @@ def test_adding_subproject_passes_when_user_is_admin(self): form = ProjectRelationshipForm( {'child': subproject.pk}, project=project, - user=user + user=user, ) form.full_clean() self.assertTrue(form.is_valid()) form.save() self.assertEqual( [r.child for r in project.subprojects.all()], - [subproject] + [subproject], ) def test_subproject_form_cant_create_sub_sub_project(self): @@ -97,7 +97,7 @@ def test_subproject_form_cant_create_sub_sub_project(self): subproject = fixture.get(Project, users=[user]) subsubproject = fixture.get(Project, users=[user]) relation = fixture.get( - ProjectRelationship, parent=project, child=subproject + ProjectRelationship, parent=project, child=subproject, ) self.assertQuerysetEqual( Project.objects.for_admin_user(user), @@ -108,7 +108,7 @@ def test_subproject_form_cant_create_sub_sub_project(self): form = ProjectRelationshipForm( {'child': subsubproject.pk}, project=subproject, - user=user + user=user, ) # The subsubproject is valid here, as far as the child check is # concerned, but the parent check should fail. @@ -120,7 +120,7 @@ def test_subproject_form_cant_create_sub_sub_project(self): self.assertEqual(len(form.errors['parent']), 1) self.assertRegex( form.errors['parent'][0], - r'Subproject nesting is not supported' + r'Subproject nesting is not supported', ) def test_excludes_existing_subprojects(self): @@ -128,7 +128,7 @@ def test_excludes_existing_subprojects(self): project = fixture.get(Project, users=[user]) subproject = fixture.get(Project, users=[user]) relation = fixture.get( - ProjectRelationship, parent=project, child=subproject + ProjectRelationship, parent=project, child=subproject, ) self.assertQuerysetEqual( Project.objects.for_admin_user(user), @@ -139,7 +139,7 @@ def test_excludes_existing_subprojects(self): form = ProjectRelationshipForm( {'child': subproject.pk}, project=project, - user=user + user=user, ) self.assertEqual( [proj_id for (proj_id, __) in form.fields['child'].choices], @@ -153,12 +153,12 @@ def test_exclude_self_project_as_subproject(self): form = ProjectRelationshipForm( {'child': project.pk}, project=project, - user=user + user=user, ) self.assertFalse(form.is_valid()) self.assertNotIn( project.id, - [proj_id for (proj_id, __) in form.fields['child'].choices] + [proj_id for (proj_id, __) in form.fields['child'].choices], ) @@ -170,12 +170,16 @@ def setUp(self): self.owner = create_user(username='owner', password='test') self.tester = create_user(username='tester', password='test') self.pip = fixture.get(Project, slug='pip', users=[self.owner], main_language_project=None) - self.subproject = fixture.get(Project, slug='sub', language='ja', - users=[ self.owner], - main_language_project=None) - self.translation = fixture.get(Project, slug='trans', language='ja', - users=[ self.owner], - main_language_project=None) + self.subproject = fixture.get( + Project, slug='sub', language='ja', + users=[ self.owner], + main_language_project=None, + ) + self.translation = fixture.get( + Project, slug='trans', language='ja', + users=[ self.owner], + main_language_project=None, + ) self.pip.add_subproject(self.subproject) self.pip.translations.add(self.translation) @@ -187,13 +191,13 @@ def setUp(self): @override_settings( PRODUCTION_DOMAIN='readthedocs.org', USE_SUBDOMAIN=False, - ) + ) def test_resolver_subproject_alias(self): resp = self.client.get('/docs/pip/projects/sub_alias/') self.assertEqual(resp.status_code, 302) self.assertEqual( resp._headers['location'][1], - 'http://readthedocs.org/docs/pip/projects/sub_alias/ja/latest/' + 'http://readthedocs.org/docs/pip/projects/sub_alias/ja/latest/', ) @override_settings(USE_SUBDOMAIN=True) @@ -202,5 +206,5 @@ def test_resolver_subproject_subdomain_alias(self): self.assertEqual(resp.status_code, 302) self.assertEqual( resp._headers['location'][1], - 'http://pip.readthedocs.org/projects/sub_alias/ja/latest/' + 'http://pip.readthedocs.org/projects/sub_alias/ja/latest/', ) diff --git a/readthedocs/rtd_tests/tests/test_sync_versions.py b/readthedocs/rtd_tests/tests/test_sync_versions.py index d5686dcec2b..37339d2a0e5 100644 --- a/readthedocs/rtd_tests/tests/test_sync_versions.py +++ b/readthedocs/rtd_tests/tests/test_sync_versions.py @@ -169,7 +169,7 @@ def test_delete_version(self): } self.assertTrue( - Version.objects.filter(slug='0.8.3').exists() + Version.objects.filter(slug='0.8.3').exists(), ) self.client.post( @@ -180,7 +180,7 @@ def test_delete_version(self): # There isn't a v0.8.3 self.assertFalse( - Version.objects.filter(slug='0.8.3').exists() + Version.objects.filter(slug='0.8.3').exists(), ) def test_machine_attr_when_user_define_stable_tag_and_delete_it(self): @@ -205,7 +205,7 @@ def test_machine_attr_when_user_define_stable_tag_and_delete_it(self): # 0.8.3 is the current stable self.assertEqual( version8.identifier, - current_stable.identifier + current_stable.identifier, ) self.assertTrue(current_stable.machine) @@ -239,7 +239,7 @@ def test_machine_attr_when_user_define_stable_tag_and_delete_it(self): current_stable = self.pip.get_stable_version() self.assertEqual( '1abc2def3', - current_stable.identifier + current_stable.identifier, ) # Deleting the tag should return the RTD's stable @@ -270,7 +270,7 @@ def test_machine_attr_when_user_define_stable_tag_and_delete_it(self): current_stable = self.pip.get_stable_version() self.assertEqual( '0.8.3', - current_stable.identifier + current_stable.identifier, ) self.assertTrue(current_stable.machine) @@ -317,7 +317,7 @@ def test_machine_attr_when_user_define_stable_tag_and_delete_it_new_project(self current_stable = self.pip.get_stable_version() self.assertEqual( '1abc2def3', - current_stable.identifier + current_stable.identifier, ) # User activates the stable version @@ -352,7 +352,7 @@ def test_machine_attr_when_user_define_stable_tag_and_delete_it_new_project(self current_stable = self.pip.get_stable_version() self.assertEqual( '0.8.3', - current_stable.identifier + current_stable.identifier, ) self.assertTrue(current_stable.machine) @@ -380,7 +380,7 @@ def test_machine_attr_when_user_define_stable_branch_and_delete_it(self): # 0.8.3 is the current stable self.assertEqual( '0.8.3', - current_stable.identifier + current_stable.identifier, ) self.assertTrue(current_stable.machine) @@ -412,7 +412,7 @@ def test_machine_attr_when_user_define_stable_branch_and_delete_it(self): current_stable = self.pip.get_stable_version() self.assertEqual( 'origin/stable', - current_stable.identifier + current_stable.identifier, ) # Deleting the branch should return the RTD's stable @@ -441,7 +441,7 @@ def test_machine_attr_when_user_define_stable_branch_and_delete_it(self): current_stable = self.pip.get_stable_version() self.assertEqual( 'origin/0.8.3', - current_stable.identifier + current_stable.identifier, ) self.assertTrue(current_stable.machine) @@ -484,7 +484,7 @@ def test_machine_attr_when_user_define_stable_branch_and_delete_it_new_project(s current_stable = self.pip.get_stable_version() self.assertEqual( 'origin/stable', - current_stable.identifier + current_stable.identifier, ) # User activates the stable version @@ -517,7 +517,7 @@ def test_machine_attr_when_user_define_stable_branch_and_delete_it_new_project(s current_stable = self.pip.get_stable_version() self.assertEqual( 'origin/0.8.3', - current_stable.identifier + current_stable.identifier, ) self.assertTrue(current_stable.machine) @@ -554,7 +554,7 @@ def test_machine_attr_when_user_define_latest_tag_and_delete_it(self): version_latest = self.pip.versions.get(slug='latest') self.assertEqual( '1abc2def3', - version_latest.identifier + version_latest.identifier, ) # Deleting the tag should return the RTD's latest @@ -565,7 +565,7 @@ def test_machine_attr_when_user_define_latest_tag_and_delete_it(self): 'verbose_name': 'master', }, ], - 'tags': [] + 'tags': [], } resp = self.client.post( @@ -579,7 +579,7 @@ def test_machine_attr_when_user_define_latest_tag_and_delete_it(self): version_latest = self.pip.versions.get(slug='latest') self.assertEqual( 'master', - version_latest.identifier + version_latest.identifier, ) self.assertTrue(version_latest.machine) @@ -614,7 +614,7 @@ def test_machine_attr_when_user_define_latest_branch_and_delete_it(self): version_latest = self.pip.versions.get(slug='latest') self.assertEqual( 'origin/latest', - version_latest.identifier + version_latest.identifier, ) # Deleting the branch should return the RTD's latest @@ -668,7 +668,7 @@ def test_deletes_version_with_same_identifier(self): # We only have one version with an identifier `1234` self.assertEqual( self.pip.versions.filter(identifier='1234').count(), - 1 + 1, ) # We add a new tag with the same identifier @@ -701,7 +701,7 @@ def test_deletes_version_with_same_identifier(self): # We have two versions with an identifier `1234` self.assertEqual( self.pip.versions.filter(identifier='1234').count(), - 2 + 2, ) # We delete one version with identifier `1234` @@ -730,7 +730,7 @@ def test_deletes_version_with_same_identifier(self): # We have only one version with an identifier `1234` self.assertEqual( self.pip.versions.filter(identifier='1234').count(), - 1 + 1, ) @@ -826,7 +826,7 @@ def test_invalid_version_numbers_are_not_stable(self): 'tags': [ { 'identifier': 'this.is.invalid', - 'verbose_name': 'this.is.invalid' + 'verbose_name': 'this.is.invalid', }, ], } @@ -847,7 +847,7 @@ def test_invalid_version_numbers_are_not_stable(self): }, { 'identifier': 'this.is.invalid', - 'verbose_name': 'this.is.invalid' + 'verbose_name': 'this.is.invalid', }, ], } @@ -897,7 +897,7 @@ def test_update_stable_version(self): 'identifier': '1.0.0', 'verbose_name': '1.0.0', }, - ] + ], } self.client.post( @@ -1109,12 +1109,12 @@ def test_user_defined_stable_version_tag_with_tags(self): self.assertTrue(version_stable.active) self.assertEqual( '1abc2def3', - self.pip.get_stable_version().identifier + self.pip.get_stable_version().identifier, ) # There arent others stable slugs like stable_a other_stable = self.pip.versions.filter( - slug__startswith='stable_' + slug__startswith='stable_', ) self.assertFalse(other_stable.exists()) @@ -1131,10 +1131,10 @@ def test_user_defined_stable_version_tag_with_tags(self): self.assertTrue(version_stable.active) self.assertEqual( '1abc2def3', - self.pip.get_stable_version().identifier + self.pip.get_stable_version().identifier, ) other_stable = self.pip.versions.filter( - slug__startswith='stable_' + slug__startswith='stable_', ) self.assertFalse(other_stable.exists()) @@ -1197,11 +1197,11 @@ def test_user_defined_stable_version_branch_with_tags(self): self.assertTrue(version_stable.active) self.assertEqual( 'origin/stable', - self.pip.get_stable_version().identifier + self.pip.get_stable_version().identifier, ) # There arent others stable slugs like stable_a other_stable = self.pip.versions.filter( - slug__startswith='stable_' + slug__startswith='stable_', ) self.assertFalse(other_stable.exists()) @@ -1218,10 +1218,10 @@ def test_user_defined_stable_version_branch_with_tags(self): self.assertTrue(version_stable.active) self.assertEqual( 'origin/stable', - self.pip.get_stable_version().identifier + self.pip.get_stable_version().identifier, ) other_stable = self.pip.versions.filter( - slug__startswith='stable_' + slug__startswith='stable_', ) self.assertFalse(other_stable.exists()) @@ -1277,12 +1277,12 @@ def test_user_defined_latest_version_tag(self): self.assertTrue(version_latest.active) self.assertEqual( '1abc2def3', - version_latest.identifier + version_latest.identifier, ) # There arent others latest slugs like latest_a other_latest = self.pip.versions.filter( - slug__startswith='latest_' + slug__startswith='latest_', ) self.assertFalse(other_latest.exists()) @@ -1299,10 +1299,10 @@ def test_user_defined_latest_version_tag(self): self.assertTrue(version_latest.active) self.assertEqual( '1abc2def3', - version_latest.identifier + version_latest.identifier, ) other_latest = self.pip.versions.filter( - slug__startswith='latest_' + slug__startswith='latest_', ) self.assertFalse(other_latest.exists()) @@ -1334,12 +1334,12 @@ def test_user_defined_latest_version_branch(self): self.assertTrue(version_latest.active) self.assertEqual( 'origin/latest', - version_latest.identifier + version_latest.identifier, ) # There arent others latest slugs like latest_a other_latest = self.pip.versions.filter( - slug__startswith='latest_' + slug__startswith='latest_', ) self.assertFalse(other_latest.exists()) @@ -1356,9 +1356,9 @@ def test_user_defined_latest_version_branch(self): self.assertTrue(version_latest.active) self.assertEqual( 'origin/latest', - version_latest.identifier + version_latest.identifier, ) other_latest = self.pip.versions.filter( - slug__startswith='latest_' + slug__startswith='latest_', ) self.assertFalse(other_latest.exists()) diff --git a/readthedocs/rtd_tests/tests/test_urls.py b/readthedocs/rtd_tests/tests/test_urls.py index cfa81237e43..b01c9997648 100644 --- a/readthedocs/rtd_tests/tests/test_urls.py +++ b/readthedocs/rtd_tests/tests/test_urls.py @@ -47,7 +47,7 @@ class TestVersionURLs(TestCase): def test_version_url_with_caps(self): url = reverse( 'project_download_media', - kwargs={'type_': 'pdf', 'version_slug': '1.4.X', 'project_slug': 'django'} + kwargs={'type_': 'pdf', 'version_slug': '1.4.X', 'project_slug': 'django'}, ) self.assertTrue(url) @@ -57,18 +57,18 @@ class TestProfileDetailURLs(TestCase): def test_profile_detail_url(self): url = reverse( 'profiles_profile_detail', - kwargs={'username': 'foo+bar'} - ) + kwargs={'username': 'foo+bar'}, + ) self.assertEqual(url, '/profiles/foo+bar/') url = reverse( 'profiles_profile_detail', - kwargs={'username': 'abc+def@ghi.jkl'} - ) + kwargs={'username': 'abc+def@ghi.jkl'}, + ) self.assertEqual(url, '/profiles/abc+def@ghi.jkl/') url = reverse( 'profiles_profile_detail', - kwargs={'username': 'abc-def+ghi'} - ) + kwargs={'username': 'abc-def+ghi'}, + ) self.assertEqual(url, '/profiles/abc-def+ghi/') diff --git a/readthedocs/rtd_tests/tests/test_version_commit_name.py b/readthedocs/rtd_tests/tests/test_version_commit_name.py index 2dfd4725870..bf181c3d5da 100644 --- a/readthedocs/rtd_tests/tests/test_version_commit_name.py +++ b/readthedocs/rtd_tests/tests/test_version_commit_name.py @@ -16,43 +16,59 @@ def test_branch_name_unicode_non_ascii(self): def test_branch_name_made_friendly_when_sha(self): commit_hash = '3d92b728b7d7b842259ac2020c2fa389f13aff0d' - version = new(Version, identifier=commit_hash, - slug=STABLE, verbose_name=STABLE, type=TAG) + version = new( + Version, identifier=commit_hash, + slug=STABLE, verbose_name=STABLE, type=TAG, + ) # we shorten commit hashes to keep things readable self.assertEqual(version.identifier_friendly, '3d92b728') def test_branch_name(self): - version = new(Version, identifier='release-2.5.x', - slug='release-2.5.x', verbose_name='release-2.5.x', - type=BRANCH) + version = new( + Version, identifier='release-2.5.x', + slug='release-2.5.x', verbose_name='release-2.5.x', + type=BRANCH, + ) self.assertEqual(version.commit_name, 'release-2.5.x') def test_tag_name(self): - version = new(Version, identifier='10f1b29a2bd2', slug='release-2.5.0', - verbose_name='release-2.5.0', type=TAG) + version = new( + Version, identifier='10f1b29a2bd2', slug='release-2.5.0', + verbose_name='release-2.5.0', type=TAG, + ) self.assertEqual(version.commit_name, 'release-2.5.0') def test_branch_with_name_stable(self): - version = new(Version, identifier='origin/stable', slug=STABLE, - verbose_name='stable', type=BRANCH) + version = new( + Version, identifier='origin/stable', slug=STABLE, + verbose_name='stable', type=BRANCH, + ) self.assertEqual(version.commit_name, 'stable') def test_stable_version_tag(self): - version = new(Version, - identifier='3d92b728b7d7b842259ac2020c2fa389f13aff0d', - slug=STABLE, verbose_name=STABLE, type=TAG) - self.assertEqual(version.commit_name, - '3d92b728b7d7b842259ac2020c2fa389f13aff0d') + version = new( + Version, + identifier='3d92b728b7d7b842259ac2020c2fa389f13aff0d', + slug=STABLE, verbose_name=STABLE, type=TAG, + ) + self.assertEqual( + version.commit_name, + '3d92b728b7d7b842259ac2020c2fa389f13aff0d', + ) def test_hg_latest_branch(self): hg_project = get(Project, repo_type=REPO_TYPE_HG) - version = new(Version, identifier='default', slug=LATEST, - verbose_name=LATEST, type=BRANCH, project=hg_project) + version = new( + Version, identifier='default', slug=LATEST, + verbose_name=LATEST, type=BRANCH, project=hg_project, + ) self.assertEqual(version.commit_name, 'default') def test_git_latest_branch(self): git_project = get(Project, repo_type=REPO_TYPE_GIT) - version = new(Version, project=git_project, - identifier='origin/master', slug=LATEST, - verbose_name=LATEST, type=BRANCH) + version = new( + Version, project=git_project, + identifier='origin/master', slug=LATEST, + verbose_name=LATEST, type=BRANCH, + ) self.assertEqual(version.commit_name, 'master') diff --git a/readthedocs/rtd_tests/tests/test_version_config.py b/readthedocs/rtd_tests/tests/test_version_config.py index f97c9c871a8..2bcd61104de 100644 --- a/readthedocs/rtd_tests/tests/test_version_config.py +++ b/readthedocs/rtd_tests/tests/test_version_config.py @@ -16,12 +16,12 @@ def test_get_correct_config(self): build_old = Build.objects.create( project=self.project, version=self.version, - config={'version': 1} + config={'version': 1}, ) build_new = Build.objects.create( project=self.project, version=self.version, - config={'version': 2} + config={'version': 2}, ) build_new_error = Build.objects.create( project=self.project, @@ -42,7 +42,7 @@ def test_get_correct_config_when_same_config(self): Build, project=self.project, version=self.version, - config={} + config={}, ) build_old.config = {'version': 1} build_old.save() @@ -51,7 +51,7 @@ def test_get_correct_config_when_same_config(self): Build, project=self.project, version=self.version, - config={} + config={}, ) build_new.config = {'version': 1} build_new.save() diff --git a/readthedocs/rtd_tests/tests/test_version_slug.py b/readthedocs/rtd_tests/tests/test_version_slug.py index 63d3ae385f8..a0d4b4b28b8 100644 --- a/readthedocs/rtd_tests/tests/test_version_slug.py +++ b/readthedocs/rtd_tests/tests/test_version_slug.py @@ -35,58 +35,68 @@ def setUp(self): def test_saving(self): version = Version.objects.create( verbose_name='1.0', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, '1.0') def test_normalizing(self): version = Version.objects.create( verbose_name='1%0', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, '1-0') def test_normalizing_slashes(self): version = Version.objects.create( verbose_name='releases/1.0', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, 'releases-1.0') def test_uppercase(self): version = Version.objects.create( verbose_name='SomeString-charclass', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, 'somestring-charclass') def test_placeholder_as_name(self): version = Version.objects.create( verbose_name='-', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, 'unknown') def test_multiple_empty_names(self): version = Version.objects.create( verbose_name='-', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, 'unknown') version = Version.objects.create( verbose_name='-./.-', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, 'unknown_a') def test_uniqueness(self): version = Version.objects.create( verbose_name='1!0', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, '1-0') version = Version.objects.create( verbose_name='1%0', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, '1-0_a') version = Version.objects.create( verbose_name='1?0', - project=self.pip) + project=self.pip, + ) self.assertEqual(version.slug, '1-0_b') def test_uniquifying_suffix(self): diff --git a/readthedocs/rtd_tests/tests/test_views.py b/readthedocs/rtd_tests/tests/test_views.py index ca9cc42edac..21515ad886b 100644 --- a/readthedocs/rtd_tests/tests/test_views.py +++ b/readthedocs/rtd_tests/tests/test_views.py @@ -151,7 +151,7 @@ def test_project_translations(self): def test_project_translations_delete(self): response = self.client.get( - '/dashboard/pip/translations/delete/a-translation/' + '/dashboard/pip/translations/delete/a-translation/', ) self.assertRedirectToLogin(response) @@ -214,13 +214,13 @@ def setUp(self): def test_deny_delete_for_non_project_admins(self): response = self.client.get( - '/dashboard/my-mainproject/subprojects/delete/my-subproject/' + '/dashboard/my-mainproject/subprojects/delete/my-subproject/', ) self.assertEqual(response.status_code, 404) self.assertTrue( self.subproject in - [r.child for r in self.project.subprojects.all()] + [r.child for r in self.project.subprojects.all()], ) def test_admins_can_delete_subprojects(self): @@ -239,7 +239,7 @@ def test_admins_can_delete_subprojects(self): self.assertEqual(response.status_code, 405) self.assertTrue( self.subproject in - [r.child for r in self.project.subprojects.all()] + [r.child for r in self.project.subprojects.all()], ) # Test POST response = self.client.post( @@ -248,11 +248,11 @@ def test_admins_can_delete_subprojects(self): self.assertEqual(response.status_code, 302) self.assertTrue( self.subproject not in - [r.child for r in self.project.subprojects.all()] + [r.child for r in self.project.subprojects.all()], ) def test_project_admins_can_delete_subprojects_that_they_are_not_admin_of( - self + self, ): self.project.users.add(self.user) self.assertFalse(AdminPermission.is_admin(self.user, self.subproject)) @@ -263,7 +263,7 @@ def test_project_admins_can_delete_subprojects_that_they_are_not_admin_of( self.assertEqual(response.status_code, 302) self.assertTrue( self.subproject not in - [r.child for r in self.project.subprojects.all()] + [r.child for r in self.project.subprojects.all()], ) diff --git a/readthedocs/rtd_tests/utils.py b/readthedocs/rtd_tests/utils.py index 95c0d99385b..79f6eb0bbef 100644 --- a/readthedocs/rtd_tests/utils.py +++ b/readthedocs/rtd_tests/utils.py @@ -34,7 +34,7 @@ def get_readthedocs_app_path(): def check_output(command, env=None): output = subprocess.Popen( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - env=env + env=env, ).communicate()[0] log.info(output) return output @@ -53,7 +53,7 @@ def make_test_git(): # the repo check_output(['git', 'checkout', '-b', 'submodule', 'master'], env=env) add_git_submodule_without_cloning( - directory, 'foobar', 'https://foobar.com/git' + directory, 'foobar', 'https://foobar.com/git', ) check_output(['git', 'add', '.'], env=env) check_output(['git', 'commit', '-m"Add submodule"'], env=env) @@ -61,7 +61,7 @@ def make_test_git(): # Add an invalid submodule URL in the invalidsubmodule branch check_output(['git', 'checkout', '-b', 'invalidsubmodule', 'master'], env=env) add_git_submodule_without_cloning( - directory, 'invalid', 'git@github.com:rtfd/readthedocs.org.git' + directory, 'invalid', 'git@github.com:rtfd/readthedocs.org.git', ) check_output(['git', 'add', '.'], env=env) check_output(['git', 'commit', '-m"Add invalid submodule"'], env=env) @@ -122,11 +122,11 @@ def make_git_repo(directory, name='sample_repo'): check_output(['git', 'init'] + [directory], env=env) check_output( ['git', 'config', 'user.email', 'dev@readthedocs.org'], - env=env + env=env, ) check_output( ['git', 'config', 'user.name', 'Read the Docs'], - env=env + env=env, ) # Set up the actual repository @@ -179,8 +179,10 @@ def delete_git_branch(directory, branch): @restoring_chdir -def create_git_submodule(directory, submodule, - msg='Add realative submodule', branch='master'): +def create_git_submodule( + directory, submodule, + msg='Add realative submodule', branch='master', +): env = environ.copy() env['GIT_DIR'] = pjoin(directory, '.git') chdir(directory) diff --git a/readthedocs/search/indexes.py b/readthedocs/search/indexes.py index a9fe07b0267..f8c88a4197b 100644 --- a/readthedocs/search/indexes.py +++ b/readthedocs/search/indexes.py @@ -206,7 +206,7 @@ def update_aliases(self, new_index, delete=True): 'remove': { 'index': old_index, 'alias': self._index, - } + }, }) actions.append({'add': {'index': new_index, 'alias': self._index}}) @@ -237,7 +237,7 @@ def get_mapping(self): 'id': {'type': 'long'}, 'name': {'type': 'string', 'analyzer': 'default_icu'}, 'description': { - 'type': 'string', 'analyzer': 'default_icu' + 'type': 'string', 'analyzer': 'default_icu', }, 'slug': {'type': 'string', 'index': 'not_analyzed'}, 'lang': {'type': 'string', 'index': 'not_analyzed'}, @@ -266,7 +266,7 @@ def extract_document(self, data): doc = {} attrs = ( - 'id', 'name', 'slug', 'description', 'lang', 'tags', 'author', 'url' + 'id', 'name', 'slug', 'description', 'lang', 'tags', 'author', 'url', ) for attr in attrs: doc[attr] = data.get(attr, '') @@ -367,7 +367,7 @@ def get_mapping(self): 'type': 'object', 'properties': { 'code': { - 'type': 'string', 'analyzer': 'default_icu' + 'type': 'string', 'analyzer': 'default_icu', }, }, }, @@ -384,7 +384,7 @@ def extract_document(self, data): attrs = ( 'id', 'project', 'title', 'page_id', 'version', 'path', 'content', - 'commit' + 'commit', ) for attr in attrs: doc[attr] = data.get(attr, '') diff --git a/readthedocs/search/lib.py b/readthedocs/search/lib.py index a1f0cc2095f..16297e6414c 100644 --- a/readthedocs/search/lib.py +++ b/readthedocs/search/lib.py @@ -44,7 +44,7 @@ def search_project(request, query, language=None): if language: body['facets']['language']['facet_filter'] = { - 'term': {'lang': language} + 'term': {'lang': language}, } body['filter'] = {'term': {'lang': language}} @@ -54,7 +54,7 @@ def search_project(request, query, language=None): def search_file( - request, query, project_slug=None, version_slug=LATEST, taxonomy=None + request, query, project_slug=None, version_slug=LATEST, taxonomy=None, ): """ Search index for files matching query. @@ -79,7 +79,7 @@ def search_file( 'boost': 10, 'slop': 2, }, - } + }, }, { 'match_phrase': { @@ -88,7 +88,7 @@ def search_file( 'boost': 5, 'slop': 3, }, - } + }, }, { 'match_phrase': { @@ -96,7 +96,7 @@ def search_file( 'query': query, 'slop': 5, }, - } + }, }, ], }, @@ -142,7 +142,7 @@ def search_file( ) ) final_filter['and'].append({ - 'terms': {'project': project_slugs} + 'terms': {'project': project_slugs}, }) # Add routing to optimize search by hitting the right shard. @@ -212,7 +212,7 @@ def search_section( 'boost': 10, 'slop': 2, }, - } + }, }, { 'match_phrase': { @@ -220,7 +220,7 @@ def search_section( 'query': query, 'slop': 5, }, - } + }, }, ], }, diff --git a/readthedocs/search/parse_json.py b/readthedocs/search/parse_json.py index 354df8b1544..8f99aa012de 100644 --- a/readthedocs/search/parse_json.py +++ b/readthedocs/search/parse_json.py @@ -27,8 +27,10 @@ def process_all_json_files(version, build_dir=True): html_files = [] for root, _, files in os.walk(full_path): for filename in fnmatch.filter(files, '*.fjson'): - if filename in ['search.fjson', 'genindex.fjson', - 'py-modindex.fjson']: + if filename in [ + 'search.fjson', 'genindex.fjson', + 'py-modindex.fjson', + ]: continue html_files.append(os.path.join(root, filename)) page_list = [] diff --git a/readthedocs/search/tests/test_views.py b/readthedocs/search/tests/test_views.py index fa740285d1e..bfb7058fc05 100644 --- a/readthedocs/search/tests/test_views.py +++ b/readthedocs/search/tests/test_views.py @@ -35,8 +35,10 @@ def elastic_index(self, mock_parse_json, all_projects, es_index): self._reindex_elasticsearch(es_index=es_index) def test_search_by_project_name(self, client, project): - result, _ = self._get_search_result(url=self.url, client=client, - search_params={'q': project.name}) + result, _ = self._get_search_result( + url=self.url, client=client, + search_params={'q': project.name}, + ) assert project.name.encode('utf-8') in result.text().encode('utf-8') @@ -46,8 +48,10 @@ def test_search_project_show_languages(self, client, project, es_index): G(Project, language='bn', name=project.name) self._reindex_elasticsearch(es_index=es_index) - result, page = self._get_search_result(url=self.url, client=client, - search_params={'q': project.name}) + result, page = self._get_search_result( + url=self.url, client=client, + search_params={'q': project.name}, + ) content = page.find('.navigable .language-list') # There should be 2 languages @@ -61,8 +65,10 @@ def test_search_project_filter_language(self, client, project, es_index): self._reindex_elasticsearch(es_index=es_index) search_params = {'q': project.name, 'language': 'bn'} - result, page = self._get_search_result(url=self.url, client=client, - search_params=search_params) + result, page = self._get_search_result( + url=self.url, client=client, + search_params=search_params, + ) # There should be only 1 result assert len(result) == 1 @@ -75,11 +81,15 @@ def test_search_project_filter_language(self, client, project, es_index): @pytest.mark.parametrize('data_type', ['content', 'headers', 'title']) @pytest.mark.parametrize('page_num', [0, 1]) def test_search_by_file_content(self, client, project, data_type, page_num): - query = get_search_query_from_project_file(project_slug=project.slug, page_num=page_num, - data_type=data_type) - - result, _ = self._get_search_result(url=self.url, client=client, - search_params={'q': query, 'type': 'file'}) + query = get_search_query_from_project_file( + project_slug=project.slug, page_num=page_num, + data_type=data_type, + ) + + result, _ = self._get_search_result( + url=self.url, client=client, + search_params={'q': query, 'type': 'file'}, + ) assert len(result) == 1 def test_file_search_show_projects(self, client): @@ -88,8 +98,10 @@ def test_file_search_show_projects(self, client): # `Github` word is present both in `kuma` and `pipeline` files # so search with this phrase - result, page = self._get_search_result(url=self.url, client=client, - search_params={'q': 'GitHub', 'type': 'file'}) + result, page = self._get_search_result( + url=self.url, client=client, + search_params={'q': 'GitHub', 'type': 'file'}, + ) # There should be 2 search result assert len(result) == 2 @@ -108,8 +120,10 @@ def test_file_search_filter_by_project(self, client): # `Github` word is present both in `kuma` and `pipeline` files # so search with this phrase but filter through `kuma` project search_params = {'q': 'GitHub', 'type': 'file', 'project': 'kuma'} - result, page = self._get_search_result(url=self.url, client=client, - search_params=search_params) + result, page = self._get_search_result( + url=self.url, client=client, + search_params=search_params, + ) # There should be 1 search result as we have filtered assert len(result) == 1 @@ -139,8 +153,10 @@ def test_file_search_show_versions(self, client, all_projects, es_index, setting query = get_search_query_from_project_file(project_slug=project.slug) - result, page = self._get_search_result(url=self.url, client=client, - search_params={'q': query, 'type': 'file'}) + result, page = self._get_search_result( + url=self.url, client=client, + search_params={'q': query, 'type': 'file'}, + ) # There should be only one result because by default # only latest version result should be there @@ -172,7 +188,9 @@ def test_file_search_subprojects(self, client, all_projects, es_index): # Now search with subproject content but explicitly filter by the parent project query = get_search_query_from_project_file(project_slug=subproject.slug) search_params = {'q': query, 'type': 'file', 'project': project.slug} - result, page = self._get_search_result(url=self.url, client=client, - search_params=search_params) + result, page = self._get_search_result( + url=self.url, client=client, + search_params=search_params, + ) assert len(result) == 1 diff --git a/readthedocs/search/utils.py b/readthedocs/search/utils.py index a55da2dfa1c..1e50b84148d 100644 --- a/readthedocs/search/utils.py +++ b/readthedocs/search/utils.py @@ -37,10 +37,10 @@ def process_mkdocs_json(version, build_dir=True): relative_path = parse_path_from_file(file_path=filename) html = parse_content_from_file(file_path=filename) headers = parse_headers_from_file( - documentation_type='mkdocs', file_path=filename + documentation_type='mkdocs', file_path=filename, ) sections = parse_sections_from_file( - documentation_type='mkdocs', file_path=filename + documentation_type='mkdocs', file_path=filename, ) try: title = sections[0]['title'] @@ -81,7 +81,7 @@ def valid_mkdocs_json(file_path): if to_check not in page_json: log.warning( '(Search Index) Unable to index file: %s error: Invalid JSON', - file_path + file_path, ) return None @@ -137,7 +137,7 @@ def parse_content_from_file(file_path): if not content: log.info( - '(Search Index) Unable to index file: %s, empty file', file_path + '(Search Index) Unable to index file: %s, empty file', file_path, ) else: log.debug('(Search Index) %s length: %s', file_path, len(content)) diff --git a/readthedocs/urls.py b/readthedocs/urls.py index de18fcd3f77..cb723c64fb1 100644 --- a/readthedocs/urls.py +++ b/readthedocs/urls.py @@ -75,7 +75,7 @@ url(r'^api/v2/', include('readthedocs.restapi.urls')), url( r'^api-auth/', - include('rest_framework.urls', namespace='rest_framework') + include('rest_framework.urls', namespace='rest_framework'), ), ] @@ -121,7 +121,7 @@ # Export URLs groups = [ basic_urls, rtd_urls, project_urls, api_urls, core_urls, i18n_urls, - deprecated_urls + deprecated_urls, ] if settings.DO_NOT_TRACK_ENABLED: diff --git a/readthedocs/vcs_support/backends/bzr.py b/readthedocs/vcs_support/backends/bzr.py index fa51ed0ae56..e228ac720d3 100644 --- a/readthedocs/vcs_support/backends/bzr.py +++ b/readthedocs/vcs_support/backends/bzr.py @@ -88,6 +88,6 @@ def checkout(self, identifier=None): exit_code, stdout, stderr = self.run('bzr', 'switch', identifier) if exit_code != 0: raise RepositoryError( - RepositoryError.FAILED_TO_CHECKOUT.format(identifier) + RepositoryError.FAILED_TO_CHECKOUT.format(identifier), ) return exit_code, stdout, stderr diff --git a/readthedocs/vcs_support/backends/git.py b/readthedocs/vcs_support/backends/git.py index 7bde178c34d..aa3adc6c2bc 100644 --- a/readthedocs/vcs_support/backends/git.py +++ b/readthedocs/vcs_support/backends/git.py @@ -73,8 +73,10 @@ def are_submodules_available(self, config): submodules_in_config = ( config.submodules.exclude != ALL or config.submodules.include ) - if (self.project.has_feature(Feature.SKIP_SUBMODULES) or - not submodules_in_config): + if ( + self.project.has_feature(Feature.SKIP_SUBMODULES) or + not submodules_in_config + ): return False # Keep compatibility with previous projects @@ -160,7 +162,7 @@ def checkout_revision(self, revision=None): code, out, err = self.run('git', 'checkout', '--force', revision) if code != 0: raise RepositoryError( - RepositoryError.FAILED_TO_CHECKOUT.format(revision) + RepositoryError.FAILED_TO_CHECKOUT.format(revision), ) return [code, out, err] diff --git a/readthedocs/vcs_support/backends/hg.py b/readthedocs/vcs_support/backends/hg.py index fd97ff2cc53..515e74a7710 100644 --- a/readthedocs/vcs_support/backends/hg.py +++ b/readthedocs/vcs_support/backends/hg.py @@ -109,10 +109,10 @@ def checkout(self, identifier=None): if not identifier: identifier = 'tip' exit_code, stdout, stderr = self.run( - 'hg', 'update', '--clean', identifier + 'hg', 'update', '--clean', identifier, ) if exit_code != 0: raise RepositoryError( - RepositoryError.FAILED_TO_CHECKOUT.format(identifier) + RepositoryError.FAILED_TO_CHECKOUT.format(identifier), ) return exit_code, stdout, stderr diff --git a/readthedocs/vcs_support/tests.py b/readthedocs/vcs_support/tests.py index d43f8c0f401..0a271f52e7d 100644 --- a/readthedocs/vcs_support/tests.py +++ b/readthedocs/vcs_support/tests.py @@ -31,23 +31,31 @@ def setUp(self): self.version_mock.slug = 'test-version-slug' def test_simplelock(self): - with utils.NonBlockingLock(project=self.project_mock, - version=self.version_mock) as f_lock: + with utils.NonBlockingLock( + project=self.project_mock, + version=self.version_mock, + ) as f_lock: self.assertTrue(os.path.exists(f_lock.fpath)) def test_simplelock_cleanup(self): lock_path = None - with utils.NonBlockingLock(project=self.project_mock, - version=self.version_mock) as f_lock: + with utils.NonBlockingLock( + project=self.project_mock, + version=self.version_mock, + ) as f_lock: lock_path = f_lock.fpath self.assertTrue(lock_path is not None and not os.path.exists(lock_path)) def test_nonreentrant(self): - with utils.NonBlockingLock(project=self.project_mock, - version=self.version_mock) as f_lock: + with utils.NonBlockingLock( + project=self.project_mock, + version=self.version_mock, + ) as f_lock: try: - with utils.NonBlockingLock(project=self.project_mock, - version=self.version_mock) as f_lock: + with utils.NonBlockingLock( + project=self.project_mock, + version=self.version_mock, + ) as f_lock: pass except utils.LockTimeout: pass @@ -55,11 +63,15 @@ def test_nonreentrant(self): raise AssertionError('Should have thrown LockTimeout') def test_forceacquire(self): - with utils.NonBlockingLock(project=self.project_mock, - version=self.version_mock) as f_lock: + with utils.NonBlockingLock( + project=self.project_mock, + version=self.version_mock, + ) as f_lock: try: - with utils.NonBlockingLock(project=self.project_mock, - version=self.version_mock, max_lock_age=0) as f_lock: + with utils.NonBlockingLock( + project=self.project_mock, + version=self.version_mock, max_lock_age=0, + ) as f_lock: pass except utils.LockTimeout: raise AssertionError('Should have thrown LockTimeout') diff --git a/readthedocs/vcs_support/utils.py b/readthedocs/vcs_support/utils.py index 65f9e3839d3..c948d36fdd7 100644 --- a/readthedocs/vcs_support/utils.py +++ b/readthedocs/vcs_support/utils.py @@ -29,7 +29,7 @@ class Lock: def __init__(self, project, version, timeout=5, polling_interval=0.1): self.name = project.slug self.fpath = os.path.join( - project.doc_path, '%s__rtdlock' % version.slug + project.doc_path, '%s__rtdlock' % version.slug, ) self.timeout = timeout self.polling_interval = polling_interval @@ -95,7 +95,7 @@ class NonBlockingLock: def __init__(self, project, version, max_lock_age=None): self.fpath = os.path.join( - project.doc_path, '%s__rtdlock' % version.slug + project.doc_path, '%s__rtdlock' % version.slug, ) self.max_lock_age = max_lock_age self.name = project.slug From 022594f1bba1f5ac08547913e2bacdaef7c8086d Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 18:50:45 +0100 Subject: [PATCH 37/42] Do not touch conf.py for testing --- readthedocs/rtd_tests/files/conf.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/readthedocs/rtd_tests/files/conf.py b/readthedocs/rtd_tests/files/conf.py index c4c2ded077c..11f872849dd 100644 --- a/readthedocs/rtd_tests/files/conf.py +++ b/readthedocs/rtd_tests/files/conf.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- +from __future__ import division, print_function, unicode_literals + from datetime import datetime from recommonmark.parser import CommonMarkParser - extensions = [] templates_path = ['templates', '_templates', '.templates'] source_suffix = ['.rst', '.md'] source_parsers = { '.md': CommonMarkParser, -} + } master_doc = 'index' project = 'Pip' copyright = str(datetime.now().year) @@ -22,8 +23,6 @@ html_theme = 'sphinx_rtd_theme' file_insertion_enabled = False latex_documents = [ - ( - 'index', 'pip.tex', 'Pip Documentation', - '', 'manual', - ), + ('index', 'pip.tex', 'Pip Documentation', + '', 'manual'), ] From 91a205e5ab94ccd20c0bb8bc85f78a1c7798a3b6 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 19:00:50 +0100 Subject: [PATCH 38/42] Last pre-commit pass --- readthedocs/builds/admin.py | 23 +- readthedocs/builds/models.py | 75 +++- readthedocs/builds/views.py | 3 +- readthedocs/config/config.py | 16 +- readthedocs/core/context_processors.py | 7 +- .../commands/import_github_language.py | 4 +- .../core/management/commands/set_metadata.py | 4 +- readthedocs/core/models.py | 3 +- readthedocs/core/resolver.py | 4 +- readthedocs/core/symlink.py | 9 +- readthedocs/core/templatetags/privacy_tags.py | 3 +- readthedocs/core/urls/single_version.py | 3 +- readthedocs/core/urls/subdomain.py | 17 +- readthedocs/core/views/hooks.py | 27 +- readthedocs/core/views/serve.py | 6 +- readthedocs/doc_builder/backends/mkdocs.py | 11 +- readthedocs/doc_builder/environments.py | 12 +- readthedocs/doc_builder/exceptions.py | 3 +- readthedocs/gold/forms.py | 3 +- readthedocs/gold/urls.py | 4 +- readthedocs/oauth/models.py | 20 +- readthedocs/projects/admin.py | 29 +- readthedocs/projects/forms.py | 36 +- readthedocs/projects/models.py | 338 ++++++++++++------ readthedocs/projects/tasks.py | 24 +- readthedocs/projects/urls/private.py | 63 ++-- readthedocs/projects/views/private.py | 4 +- readthedocs/projects/views/public.py | 7 +- readthedocs/restapi/views/core_views.py | 4 +- readthedocs/restapi/views/model_views.py | 3 +- readthedocs/search/indexes.py | 23 +- readthedocs/search/lib.py | 6 +- readthedocs/search/parse_json.py | 6 +- readthedocs/search/utils.py | 9 +- readthedocs/urls.py | 7 +- readthedocs/vcs_support/backends/hg.py | 5 +- readthedocs/vcs_support/utils.py | 6 +- 37 files changed, 571 insertions(+), 256 deletions(-) diff --git a/readthedocs/builds/admin.py b/readthedocs/builds/admin.py index fbd83d8e3bf..571536f03a2 100644 --- a/readthedocs/builds/admin.py +++ b/readthedocs/builds/admin.py @@ -15,11 +15,23 @@ class BuildCommandResultInline(admin.TabularInline): class BuildAdmin(admin.ModelAdmin): fields = ( - 'project', 'version', 'type', 'state', 'error', 'success', 'length', + 'project', + 'version', + 'type', + 'state', + 'error', + 'success', + 'length', 'cold_storage', ) list_display = ( - 'id', 'project', 'version_name', 'success', 'type', 'state', 'date', + 'id', + 'project', + 'version_name', + 'success', + 'type', + 'state', + 'date', ) list_filter = ('type', 'state', 'success') list_select_related = ('project', 'version') @@ -34,7 +46,12 @@ def version_name(self, obj): class VersionAdmin(GuardedModelAdmin): search_fields = ('slug', 'project__name') list_display = ( - 'slug', 'type', 'project', 'privacy_level', 'active', 'built', + 'slug', + 'type', + 'project', + 'privacy_level', + 'active', + 'built', ) list_filter = ('type', 'privacy_level', 'active', 'built') raw_id_fields = ('project',) diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index 2f8eeae8bad..e44d426b381 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -50,7 +50,9 @@ DEFAULT_VERSION_PRIVACY_LEVEL = getattr( - settings, 'DEFAULT_VERSION_PRIVACY_LEVEL', 'public', + settings, + 'DEFAULT_VERSION_PRIVACY_LEVEL', + 'public', ) log = logging.getLogger(__name__) @@ -91,7 +93,9 @@ class Version(models.Model): #: filesystem to determine how the paths for this version are called. It #: must not be used for any other identifying purposes. slug = VersionSlugField( - _('Slug'), max_length=255, populate_from='verbose_name', + _('Slug'), + max_length=255, + populate_from='verbose_name', ) supported = models.BooleanField(_('Supported'), default=True) @@ -199,7 +203,8 @@ def get_absolute_url(self): ) private = self.privacy_level == PRIVATE return self.project.get_docs_url( - version_slug=self.slug, private=private, + version_slug=self.slug, + private=private, ) def save(self, *args, **kwargs): # pylint: disable=arguments-differ @@ -209,7 +214,9 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ for owner in self.project.users.all(): assign('view_version', owner, self) broadcast( - type='app', task=tasks.symlink_project, args=[self.project.pk], + type='app', + task=tasks.symlink_project, + args=[self.project.pk], ) return obj @@ -256,22 +263,26 @@ def get_downloads(self, pretty=False): data['PDF'] = project.get_production_media_url('pdf', self.slug) if project.has_htmlzip(self.slug): data['HTML'] = project.get_production_media_url( - 'htmlzip', self.slug, + 'htmlzip', + self.slug, ) if project.has_epub(self.slug): data['Epub'] = project.get_production_media_url( - 'epub', self.slug, + 'epub', + self.slug, ) else: if project.has_pdf(self.slug): data['pdf'] = project.get_production_media_url('pdf', self.slug) if project.has_htmlzip(self.slug): data['htmlzip'] = project.get_production_media_url( - 'htmlzip', self.slug, + 'htmlzip', + self.slug, ) if project.has_epub(self.slug): data['epub'] = project.get_production_media_url( - 'epub', self.slug, + 'epub', + self.slug, ) return data @@ -321,7 +332,11 @@ def clean_build_path(self): log.exception('Build path cleanup failed') def get_github_url( - self, docroot, filename, source_suffix='.rst', action='view', + self, + docroot, + filename, + source_suffix='.rst', + action='view', ): """ Return a GitHub URL for a given filename. @@ -364,7 +379,11 @@ def get_github_url( ) def get_gitlab_url( - self, docroot, filename, source_suffix='.rst', action='view', + self, + docroot, + filename, + source_suffix='.rst', + action='view', ): repo_url = self.project.repo if 'gitlab' not in repo_url: @@ -462,16 +481,27 @@ class Build(models.Model): """Build data.""" project = models.ForeignKey( - Project, verbose_name=_('Project'), related_name='builds', + Project, + verbose_name=_('Project'), + related_name='builds', ) version = models.ForeignKey( - Version, verbose_name=_('Version'), null=True, related_name='builds', + Version, + verbose_name=_('Version'), + null=True, + related_name='builds', ) type = models.CharField( - _('Type'), max_length=55, choices=BUILD_TYPES, default='html', + _('Type'), + max_length=55, + choices=BUILD_TYPES, + default='html', ) state = models.CharField( - _('State'), max_length=55, choices=BUILD_STATE, default='finished', + _('State'), + max_length=55, + choices=BUILD_STATE, + default='finished', ) date = models.DateTimeField(_('Date'), auto_now_add=True) success = models.BooleanField(_('Success'), default=True) @@ -482,18 +512,25 @@ class Build(models.Model): error = models.TextField(_('Error'), default='', blank=True) exit_code = models.IntegerField(_('Exit code'), null=True, blank=True) commit = models.CharField( - _('Commit'), max_length=255, null=True, blank=True, + _('Commit'), + max_length=255, + null=True, + blank=True, ) _config = JSONField(_('Configuration used in the build'), default=dict) length = models.IntegerField(_('Build Length'), null=True, blank=True) builder = models.CharField( - _('Builder'), max_length=255, null=True, blank=True, + _('Builder'), + max_length=255, + null=True, + blank=True, ) cold_storage = models.NullBooleanField( - _('Cold Storage'), help_text='Build steps stored outside the database.', + _('Cold Storage'), + help_text='Build steps stored outside the database.', ) # Manager @@ -624,7 +661,9 @@ class BuildCommandResult(BuildCommandResultMixin, models.Model): """Build command for a ``Build``.""" build = models.ForeignKey( - Build, verbose_name=_('Build'), related_name='commands', + Build, + verbose_name=_('Build'), + related_name='commands', ) command = models.TextField(_('Command')) diff --git a/readthedocs/builds/views.py b/readthedocs/builds/views.py index 00f08ab2d47..e86a673dc27 100644 --- a/readthedocs/builds/views.py +++ b/readthedocs/builds/views.py @@ -59,7 +59,8 @@ def post(self, request, project_slug): ) update_docs_task, build = trigger_build( - project=project, version=version, + project=project, + version=version, ) if (update_docs_task, build) == (None, None): # Build was skipped diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index 50eba8b3ed9..e493d09db22 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -342,7 +342,8 @@ def validate_build(self): if ':' not in build['image']: # Prepend proper image name to user's image name build['image'] = '{}:{}'.format( - DOCKER_DEFAULT_IMAGE, build['image'], + DOCKER_DEFAULT_IMAGE, + build['image'], ) # Update docker default settings from image name if build['image'] in DOCKER_IMAGE_SETTINGS: @@ -371,7 +372,9 @@ def validate_python(self): raw_python = self.raw_config['python'] if not isinstance(raw_python, dict): self.error( - 'python', self.PYTHON_INVALID_MESSAGE, code=PYTHON_INVALID, + 'python', + self.PYTHON_INVALID_MESSAGE, + code=PYTHON_INVALID, ) # Validate use_system_site_packages. @@ -445,7 +448,8 @@ def validate_conda(self): if 'file' in raw_conda: with self.catch_validation_error('conda.file'): conda_environment = validate_file( - raw_conda['file'], self.base_path, + raw_conda['file'], + self.base_path, ) conda['environment'] = conda_environment @@ -683,7 +687,8 @@ def validate_python(self): with self.catch_validation_error('python.extra_requirements'): extra_requirements = self.pop_config( - 'python.extra_requirements', [], + 'python.extra_requirements', + [], ) extra_requirements = validate_list(extra_requirements) if extra_requirements and not python['install_with_pip']: @@ -801,7 +806,8 @@ def validate_sphinx(self): if not configuration: configuration = None configuration = self.pop_config( - 'sphinx.configuration', configuration, + 'sphinx.configuration', + configuration, ) if configuration is not None: configuration = validate_file(configuration, self.base_path) diff --git a/readthedocs/core/context_processors.py b/readthedocs/core/context_processors.py index 467110e9842..8bddb9bd10f 100644 --- a/readthedocs/core/context_processors.py +++ b/readthedocs/core/context_processors.py @@ -13,12 +13,15 @@ def readthedocs_processor(request): 'USE_SUBDOMAINS': getattr(settings, 'USE_SUBDOMAINS', None), 'GLOBAL_ANALYTICS_CODE': getattr(settings, 'GLOBAL_ANALYTICS_CODE'), 'DASHBOARD_ANALYTICS_CODE': getattr( - settings, 'DASHBOARD_ANALYTICS_CODE', + settings, + 'DASHBOARD_ANALYTICS_CODE', ), 'SITE_ROOT': getattr(settings, 'SITE_ROOT', '') + '/', 'TEMPLATE_ROOT': getattr(settings, 'TEMPLATE_ROOT', '') + '/', 'DO_NOT_TRACK_ENABLED': getattr( - settings, 'DO_NOT_TRACK_ENABLED', False, + settings, + 'DO_NOT_TRACK_ENABLED', + False, ), 'USE_PROMOS': getattr(settings, 'USE_PROMOS', False), } diff --git a/readthedocs/core/management/commands/import_github_language.py b/readthedocs/core/management/commands/import_github_language.py index f3813f21e63..46159bcf651 100644 --- a/readthedocs/core/management/commands/import_github_language.py +++ b/readthedocs/core/management/commands/import_github_language.py @@ -68,7 +68,9 @@ def handle(self, *args, **options): if not languages: continue sorted_langs = sorted( - list(languages.items()), key=lambda x: x[1], reverse=True, + list(languages.items()), + key=lambda x: x[1], + reverse=True, ) print('Sorted langs: %s ' % sorted_langs) top_lang = sorted_langs[0][0] diff --git a/readthedocs/core/management/commands/set_metadata.py b/readthedocs/core/management/commands/set_metadata.py index 6fe4bb22e9e..62384fe0e0e 100644 --- a/readthedocs/core/management/commands/set_metadata.py +++ b/readthedocs/core/management/commands/set_metadata.py @@ -24,7 +24,9 @@ def handle(self, *args, **options): log.info('Generating metadata for %s', p) try: broadcast( - type='app', task=tasks.update_static_metadata, args=[p.pk], + type='app', + task=tasks.update_static_metadata, + args=[p.pk], ) except Exception: log.exception('Build failed for %s', p) diff --git a/readthedocs/core/models.py b/readthedocs/core/models.py index 91313ff2a89..bee057bdb6e 100644 --- a/readthedocs/core/models.py +++ b/readthedocs/core/models.py @@ -48,7 +48,8 @@ def __str__(self): def get_absolute_url(self): return reverse( - 'profiles_profile_detail', kwargs={'username': self.user.username}, + 'profiles_profile_detail', + kwargs={'username': self.user.username}, ) def get_contribution_details(self): diff --git a/readthedocs/core/resolver.py b/readthedocs/core/resolver.py index f66910a7254..7758226cc2f 100644 --- a/readthedocs/core/resolver.py +++ b/readthedocs/core/resolver.py @@ -246,7 +246,9 @@ def _get_private(self, project, version_slug): private = version.privacy_level == PRIVATE except Version.DoesNotExist: private = getattr( - settings, 'DEFAULT_PRIVACY_LEVEL', PUBLIC, + settings, + 'DEFAULT_PRIVACY_LEVEL', + PUBLIC, ) == PRIVATE return private diff --git a/readthedocs/core/symlink.py b/readthedocs/core/symlink.py index 67e0cd47a81..6bd19f5dd28 100644 --- a/readthedocs/core/symlink.py +++ b/readthedocs/core/symlink.py @@ -217,7 +217,8 @@ def symlink_subprojects(self): subprojects.add(rel.alias) for from_slug, to_slug in list(from_to.items()): log_msg = 'Symlinking subproject: {} -> {}'.format( - from_slug, to_slug, + from_slug, + to_slug, ) log.info( constants.LOG_TEMPLATE.format( @@ -378,7 +379,8 @@ class PublicSymlinkBase(Symlink): CNAME_ROOT = os.path.join(settings.SITE_ROOT, 'public_cname_root') WEB_ROOT = os.path.join(settings.SITE_ROOT, 'public_web_root') PROJECT_CNAME_ROOT = os.path.join( - settings.SITE_ROOT, 'public_cname_project', + settings.SITE_ROOT, + 'public_cname_project', ) def get_version_queryset(self): @@ -400,7 +402,8 @@ class PrivateSymlinkBase(Symlink): CNAME_ROOT = os.path.join(settings.SITE_ROOT, 'private_cname_root') WEB_ROOT = os.path.join(settings.SITE_ROOT, 'private_web_root') PROJECT_CNAME_ROOT = os.path.join( - settings.SITE_ROOT, 'private_cname_project', + settings.SITE_ROOT, + 'private_cname_project', ) def get_version_queryset(self): diff --git a/readthedocs/core/templatetags/privacy_tags.py b/readthedocs/core/templatetags/privacy_tags.py index f105a300e74..12d29f6624c 100644 --- a/readthedocs/core/templatetags/privacy_tags.py +++ b/readthedocs/core/templatetags/privacy_tags.py @@ -19,7 +19,8 @@ def is_admin(user, project): @register.simple_tag(takes_context=True) def get_public_projects(context, user): projects = Project.objects.for_user_and_viewer( - user=user, viewer=context['request'].user, + user=user, + viewer=context['request'].user, ) context['public_projects'] = projects return '' diff --git a/readthedocs/core/urls/single_version.py b/readthedocs/core/urls/single_version.py index 545617a553d..253cae6a319 100644 --- a/readthedocs/core/urls/single_version.py +++ b/readthedocs/core/urls/single_version.py @@ -37,7 +37,8 @@ # Needed to serve media locally if getattr(settings, 'DEBUG', False): groups.insert( - 0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), + 0, + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), ) # Allow `/docs/` URL's when not using subdomains or during local dev diff --git a/readthedocs/core/urls/subdomain.py b/readthedocs/core/urls/subdomain.py index 90f3b1865ea..4e4a6775e3a 100644 --- a/readthedocs/core/urls/subdomain.py +++ b/readthedocs/core/urls/subdomain.py @@ -26,12 +26,15 @@ url( r'^(?:|projects/(?P{project_slug})/)' r'page/(?P.*)$'.format(**pattern_opts), - redirect_page_with_filename, name='docs_detail', + redirect_page_with_filename, + name='docs_detail', ), url( (r'^(?:|projects/(?P{project_slug})/)$').format( **pattern_opts - ), redirect_project_slug, name='redirect_project_slug', + ), + redirect_project_slug, + name='redirect_project_slug', ), url( ( @@ -39,7 +42,9 @@ r'(?P{lang_slug})/' r'(?P{version_slug})/' r'(?P{filename_slug})$'.format(**pattern_opts) - ), serve_docs, name='docs_detail', + ), + serve_docs, + name='docs_detail', ), ] @@ -48,7 +53,11 @@ # Needed to serve media locally if getattr(settings, 'DEBUG', False): groups.insert( - 0, static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT,), + 0, + static( + settings.MEDIA_URL, + document_root=settings.MEDIA_ROOT, + ), ) urlpatterns = reduce(add, groups) diff --git a/readthedocs/core/views/hooks.py b/readthedocs/core/views/hooks.py index 162ff932cc4..3966ba62b2a 100644 --- a/readthedocs/core/views/hooks.py +++ b/readthedocs/core/views/hooks.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Views pertaining to builds.""" import json @@ -67,7 +68,9 @@ def build_branches(project, branch_list): versions = project.versions_from_branch_name(branch) for version in versions: log.info( - '(Branch Build) Processing %s:%s', project.slug, version.slug, + '(Branch Build) Processing %s:%s', + project.slug, + version.slug, ) ret = _build_version(project, version.slug, already_built=to_build) if ret: @@ -92,7 +95,9 @@ def sync_versions(project): try: version_identifier = project.get_default_branch() version = ( - project.versions.filter(identifier=version_identifier,).first() + project.versions.filter( + identifier=version_identifier, + ).first() ) if not version: log.info('Unable to sync from %s version', version_identifier) @@ -116,7 +121,11 @@ def get_project_from_url(url): def log_info(project, msg): log.info( - constants.LOG_TEMPLATE.format(project=project, version='', msg=msg,), + constants.LOG_TEMPLATE.format( + project=project, + version='', + msg=msg, + ), ) @@ -151,7 +160,8 @@ def _build_url(url, projects, branches): for project_slug, built in list(all_built.items()): if built: msg = '(URL Build) Build Started: {} [{}]'.format( - url, ' '.join(built), + url, + ' '.join(built), ) log_info(project_slug, msg=msg) ret += msg @@ -159,7 +169,8 @@ def _build_url(url, projects, branches): for project_slug, not_building in list(all_not_building.items()): if not_building: msg = '(URL Build) Not Building: {} [{}]'.format( - url, ' '.join(not_building), + url, + ' '.join(not_building), ) log_info(project_slug, msg=msg) ret += msg @@ -206,12 +217,14 @@ def github_build(request): # noqa: D205 if repo_projects: log.info( 'GitHub webhook search: url=%s branches=%s', - http_search_url, branches, + http_search_url, + branches, ) ssh_projects = get_project_from_url(ssh_search_url) if ssh_projects: log.info( - 'GitHub webhook search: url=%s branches=%s', ssh_search_url, + 'GitHub webhook search: url=%s branches=%s', + ssh_search_url, branches, ) projects = repo_projects | ssh_projects diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 46a6c8ec926..513451d702e 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -161,7 +161,11 @@ def _serve_file(request, filename, basepath): @map_project_slug @map_subproject_slug def serve_docs( - request, project, subproject, lang_slug=None, version_slug=None, + request, + project, + subproject, + lang_slug=None, + version_slug=None, filename='', ): """Map existing proj, lang, version, filename views to the file format.""" diff --git a/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py index 8d80ecc3582..201bd039368 100644 --- a/readthedocs/doc_builder/backends/mkdocs.py +++ b/readthedocs/doc_builder/backends/mkdocs.py @@ -97,7 +97,8 @@ def load_yaml_config(self): if hasattr(exc, 'problem_mark'): mark = exc.problem_mark note = ' (line %d, column %d)' % ( - mark.line + 1, mark.column + 1, + mark.line + 1, + mark.column + 1, ) raise MkDocsYAMLParseError( 'Your mkdocs.yml could not be loaded, ' @@ -209,12 +210,16 @@ def generate_rtd_data(self, docs_dir, mkdocs_config): 'docroot': docs_dir, 'source_suffix': '.md', 'api_host': getattr( - settings, 'PUBLIC_API_URL', 'https://readthedocs.org', + settings, + 'PUBLIC_API_URL', + 'https://readthedocs.org', ), 'ad_free': not self.project.show_advertising, 'commit': self.version.project.vcs_repo(self.version.slug).commit, 'global_analytics_code': getattr( - settings, 'GLOBAL_ANALYTICS_CODE', 'UA-17997319-1', + settings, + 'GLOBAL_ANALYTICS_CODE', + 'UA-17997319-1', ), 'user_analytics_code': analytics_code, } diff --git a/readthedocs/doc_builder/environments.py b/readthedocs/doc_builder/environments.py index cb27a88dded..ddf0b471d7d 100644 --- a/readthedocs/doc_builder/environments.py +++ b/readthedocs/doc_builder/environments.py @@ -977,13 +977,11 @@ def update_build_from_container_state(self): _('Build exited due to excessive memory consumption'), ) elif state.get('Error'): - self.failure = BuildEnvironmentError( - ( - _('Build exited due to unknown error: {0}').format( - state.get('Error'), - ) - ), - ) + self.failure = BuildEnvironmentError(( + _('Build exited due to unknown error: {0}').format( + state.get('Error'), + ) + ),) def create_container(self): """Create docker container.""" diff --git a/readthedocs/doc_builder/exceptions.py b/readthedocs/doc_builder/exceptions.py index dff39908df8..eaa2858aa6e 100644 --- a/readthedocs/doc_builder/exceptions.py +++ b/readthedocs/doc_builder/exceptions.py @@ -11,7 +11,8 @@ class BuildEnvironmentException(Exception): def __init__(self, message=None, **kwargs): self.status_code = kwargs.pop( - 'status_code', None, + 'status_code', + None, ) or self.status_code or 1 message = message or self.get_default_message() super().__init__(message, **kwargs) diff --git a/readthedocs/gold/forms.py b/readthedocs/gold/forms.py index b60b58e0b70..84c19fba387 100644 --- a/readthedocs/gold/forms.py +++ b/readthedocs/gold/forms.py @@ -120,5 +120,6 @@ def clean(self): return cleaned_data self.add_error( - None, 'You already have the max number of supported projects.', + None, + 'You already have the max number of supported projects.', ) diff --git a/readthedocs/gold/urls.py b/readthedocs/gold/urls.py index 7f679686fe0..295e5250d10 100644 --- a/readthedocs/gold/urls.py +++ b/readthedocs/gold/urls.py @@ -16,7 +16,9 @@ name='gold_subscription', ), url( - r'^cancel/$', views.DeleteGoldSubscription.as_view(), name='gold_cancel', + r'^cancel/$', + views.DeleteGoldSubscription.as_view(), + name='gold_cancel', ), url(r'^projects/$', views.projects, name='gold_projects'), url( diff --git a/readthedocs/oauth/models.py b/readthedocs/oauth/models.py index 65522c2bcbf..40d224df6b3 100644 --- a/readthedocs/oauth/models.py +++ b/readthedocs/oauth/models.py @@ -189,14 +189,12 @@ def matches(self, user): .filter(Q(repo=self.clone_url) | Q(repo__iendswith=fuzzy_url) | Q(repo__iendswith=fuzzy_url + '.git'))) # yapf: disable - return [ - { - 'id': project.slug, - 'url': reverse( - 'projects_detail', - kwargs={ - 'project_slug': project.slug, - }, - ), - } for project in projects - ] + return [{ + 'id': project.slug, + 'url': reverse( + 'projects_detail', + kwargs={ + 'project_slug': project.slug, + }, + ), + } for project in projects] diff --git a/readthedocs/projects/admin.py b/readthedocs/projects/admin.py index 165cf7491b3..73914222ce3 100644 --- a/readthedocs/projects/admin.py +++ b/readthedocs/projects/admin.py @@ -118,13 +118,21 @@ class ProjectAdmin(GuardedModelAdmin): prepopulated_fields = {'slug': ('name',)} list_display = ('name', 'slug', 'repo', 'repo_type', 'featured') list_filter = ( - 'repo_type', 'featured', 'privacy_level', 'documentation_type', - 'programming_language', 'feature__feature_id', ProjectOwnerBannedFilter, + 'repo_type', + 'featured', + 'privacy_level', + 'documentation_type', + 'programming_language', + 'feature__feature_id', + ProjectOwnerBannedFilter, ) list_editable = ('featured',) search_fields = ('slug', 'repo') inlines = [ - ProjectRelationshipInline, RedirectInline, VersionInline, DomainInline, + ProjectRelationshipInline, + RedirectInline, + VersionInline, + DomainInline, ] readonly_fields = ('feature_flags',) raw_id_fields = ('users', 'main_language_project') @@ -153,21 +161,23 @@ def ban_owner(self, request, queryset): for project in queryset: if project.users.count() == 1: count = ( - UserProfile.objects - .filter(user__projects=project) - .update(banned=True) + UserProfile.objects.filter(user__projects=project + ).update(banned=True) ) # yapf: disabled total += count else: messages.add_message( - request, messages.ERROR, + request, + messages.ERROR, 'Project has multiple owners: {}'.format(project), ) if total == 0: messages.add_message(request, messages.ERROR, 'No users banned') else: messages.add_message( - request, messages.INFO, 'Banned {} user(s)'.format(total), + request, + messages.INFO, + 'Banned {} user(s)'.format(total), ) ban_owner.short_description = 'Ban project owner' @@ -191,7 +201,8 @@ def delete_selected_and_artifacts(self, request, queryset): def get_actions(self, request): actions = super().get_actions(request) actions['delete_selected'] = ( - self.__class__.delete_selected_and_artifacts, 'delete_selected', + self.__class__.delete_selected_and_artifacts, + 'delete_selected', delete_selected.short_description, ) return actions diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index 115819060b3..9c9cd5af1aa 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -228,13 +228,16 @@ def __init__(self, *args, **kwargs): default_choice = (None, '-' * 9) all_versions = self.instance.versions.values_list( - 'identifier', 'verbose_name', + 'identifier', + 'verbose_name', ) self.fields['default_branch'].widget = forms.Select( choices=[default_choice] + list(all_versions), ) - active_versions = self.instance.all_active_versions().values_list('slug', 'verbose_name') # yapf: disabled + active_versions = self.instance.all_active_versions().values_list( + 'slug', 'verbose_name' + ) # yapf: disabled self.fields['default_version'].widget = forms.Select( choices=active_versions, ) @@ -252,8 +255,9 @@ def clean_conf_py_file(self): class UpdateProjectForm( - ProjectTriggerBuildMixin, ProjectBasicsForm, - ProjectExtraForm, + ProjectTriggerBuildMixin, + ProjectBasicsForm, + ProjectExtraForm, ): class Meta: @@ -459,7 +463,8 @@ def build_versions_form(project): class BaseUploadHTMLForm(forms.Form): content = forms.FileField(label=_('Zip file of HTML')) overwrite = forms.BooleanField( - required=False, label=_('Overwrite existing HTML?'), + required=False, + label=_('Overwrite existing HTML?'), ) def __init__(self, *args, **kwargs): @@ -535,7 +540,8 @@ def __init__(self, *args, **kwargs): def clean_email(self): self.email = EmailHook.objects.get_or_create( - email=self.cleaned_data['email'], project=self.project, + email=self.cleaned_data['email'], + project=self.project, )[0] return self.email @@ -554,7 +560,8 @@ def __init__(self, *args, **kwargs): def save(self, commit=True): self.webhook = WebHook.objects.get_or_create( - url=self.cleaned_data['url'], project=self.project, + url=self.cleaned_data['url'], + project=self.project, )[0] self.project.webhook_notifications.add(self.webhook) return self.project @@ -577,14 +584,13 @@ def __init__(self, *args, **kwargs): self.fields['project'].choices = self.get_choices() def get_choices(self): - return [ - ( - project.slug, '{project} ({lang})'.format( - project=project.slug, lang=project.get_language_display(), - ), - ) - for project in self.get_translation_queryset().all() - ] + return [( + project.slug, + '{project} ({lang})'.format( + project=project.slug, + lang=project.get_language_display(), + ), + ) for project in self.get_translation_queryset().all()] def clean_project(self): translation_project_slug = self.cleaned_data['project'] diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index 925b9d477d3..1fefb7be30b 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -53,13 +53,21 @@ class ProjectRelationship(models.Model): """ parent = models.ForeignKey( - 'Project', verbose_name=_('Parent'), related_name='subprojects', + 'Project', + verbose_name=_('Parent'), + related_name='subprojects', ) child = models.ForeignKey( - 'Project', verbose_name=_('Child'), related_name='superprojects', + 'Project', + verbose_name=_('Child'), + related_name='superprojects', ) alias = models.SlugField( - _('Alias'), max_length=255, null=True, blank=True, db_index=False, + _('Alias'), + max_length=255, + null=True, + blank=True, + db_index=False, ) objects = ChildRelatedProjectQuerySet.as_manager() @@ -88,37 +96,47 @@ class Project(models.Model): # Generally from conf.py users = models.ManyToManyField( - User, verbose_name=_('User'), related_name='projects', + User, + verbose_name=_('User'), + related_name='projects', ) # A DNS label can contain up to 63 characters. name = models.CharField(_('Name'), max_length=63) slug = models.SlugField(_('Slug'), max_length=63, unique=True) description = models.TextField( - _('Description'), blank=True, + _('Description'), + blank=True, help_text=_( 'The reStructuredText ' 'description of the project', ), ) repo = models.CharField( - _('Repository URL'), max_length=255, + _('Repository URL'), + max_length=255, validators=[validate_repository_url], help_text=_('Hosted documentation repository URL'), ) repo_type = models.CharField( - _('Repository type'), max_length=10, choices=constants.REPO_CHOICES, + _('Repository type'), + max_length=10, + choices=constants.REPO_CHOICES, default='git', ) project_url = models.URLField( - _('Project homepage'), blank=True, + _('Project homepage'), + blank=True, help_text=_('The project\'s homepage'), ) canonical_url = models.URLField( - _('Canonical URL'), blank=True, + _('Canonical URL'), + blank=True, help_text=_('URL that documentation is expected to serve from'), ) single_version = models.BooleanField( - _('Single version'), default=False, help_text=_( + _('Single version'), + default=False, + help_text=_( 'A single version site has no translations and only your ' '"latest" version, served at the root of the domain. Use ' 'this with caution, only turn it on if you will never ' @@ -126,22 +144,32 @@ class Project(models.Model): ), ) default_version = models.CharField( - _('Default version'), max_length=255, default=LATEST, + _('Default version'), + max_length=255, + default=LATEST, help_text=_('The version of your project that / redirects to'), ) # In default_branch, None means the backend should choose the # appropriate branch. Eg 'master' for git default_branch = models.CharField( - _('Default branch'), max_length=255, default=None, null=True, - blank=True, help_text=_( + _('Default branch'), + max_length=255, + default=None, + null=True, + blank=True, + help_text=_( 'What branch "latest" points to. Leave empty ' 'to use the default value for your VCS (eg. ' 'trunk or master).', ), ) requirements_file = models.CharField( - _('Requirements file'), max_length=255, default=None, null=True, - blank=True, help_text=_( + _('Requirements file'), + max_length=255, + default=None, + null=True, + blank=True, + help_text=_( 'A ' 'pip requirements file needed to build your documentation. ' @@ -149,8 +177,11 @@ class Project(models.Model): ), ) documentation_type = models.CharField( - _('Documentation type'), max_length=20, - choices=constants.DOCUMENTATION_CHOICES, default='sphinx', help_text=_( + _('Documentation type'), + max_length=20, + choices=constants.DOCUMENTATION_CHOICES, + default='sphinx', + help_text=_( 'Type of documentation you are building. More info.', @@ -160,17 +191,27 @@ class Project(models.Model): # Project features cdn_enabled = models.BooleanField(_('CDN Enabled'), default=False) analytics_code = models.CharField( - _('Analytics code'), max_length=50, null=True, blank=True, help_text=_( + _('Analytics code'), + max_length=50, + null=True, + blank=True, + help_text=_( 'Google Analytics Tracking ID ' '(ex. UA-22345342-1). ' 'This may slow down your page loads.', ), ) container_image = models.CharField( - _('Alternative container image'), max_length=64, null=True, blank=True, + _('Alternative container image'), + max_length=64, + null=True, + blank=True, ) container_mem_limit = models.CharField( - _('Container memory limit'), max_length=10, null=True, blank=True, + _('Container memory limit'), + max_length=10, + null=True, + blank=True, help_text=_( 'Memory limit in Docker format ' '-- example: 512m or 1g', @@ -182,10 +223,14 @@ class Project(models.Model): blank=True, ) build_queue = models.CharField( - _('Alternate build queue id'), max_length=32, null=True, blank=True, + _('Alternate build queue id'), + max_length=32, + null=True, + blank=True, ) allow_promos = models.BooleanField( - _('Allow paid advertising'), default=True, + _('Allow paid advertising'), + default=True, help_text=_('If unchecked, users will still see community ads.'), ) ad_free = models.BooleanField( @@ -194,32 +239,42 @@ class Project(models.Model): help_text='If checked, do not show advertising for this project', ) show_version_warning = models.BooleanField( - _('Show version warning'), default=False, + _('Show version warning'), + default=False, help_text=_('Show warning banner in non-stable nor latest versions.'), ) # Sphinx specific build options. enable_epub_build = models.BooleanField( - _('Enable EPUB build'), default=True, help_text=_( + _('Enable EPUB build'), + default=True, + help_text=_( 'Create a EPUB version of your documentation with each build.', ), ) enable_pdf_build = models.BooleanField( - _('Enable PDF build'), default=True, help_text=_( + _('Enable PDF build'), + default=True, + help_text=_( 'Create a PDF version of your documentation with each build.', ), ) # Other model data. path = models.CharField( - _('Path'), max_length=255, editable=False, + _('Path'), + max_length=255, + editable=False, help_text=_( 'The directory where ' 'conf.py lives', ), ) conf_py_file = models.CharField( - _('Python configuration file'), max_length=255, default='', blank=True, + _('Python configuration file'), + max_length=255, + default='', + blank=True, help_text=_( 'Path from project root to conf.py file ' '(ex. docs/conf.py). ' @@ -230,74 +285,103 @@ class Project(models.Model): featured = models.BooleanField(_('Featured'), default=False) skip = models.BooleanField(_('Skip'), default=False) install_project = models.BooleanField( - _('Install Project'), help_text=_( + _('Install Project'), + help_text=_( 'Install your project inside a virtualenv using setup.py ' 'install', - ), default=False, + ), + default=False, ) # This model attribute holds the python interpreter used to create the # virtual environment python_interpreter = models.CharField( - _('Python Interpreter'), max_length=20, - choices=constants.PYTHON_CHOICES, default='python', help_text=_( + _('Python Interpreter'), + max_length=20, + choices=constants.PYTHON_CHOICES, + default='python', + help_text=_( 'The Python interpreter used to create the virtual ' 'environment.', ), ) use_system_packages = models.BooleanField( - _('Use system packages'), help_text=_( + _('Use system packages'), + help_text=_( 'Give the virtual environment access to the global ' 'site-packages dir.', - ), default=False, + ), + default=False, ) privacy_level = models.CharField( - _('Privacy Level'), max_length=20, choices=constants.PRIVACY_CHOICES, + _('Privacy Level'), + max_length=20, + choices=constants.PRIVACY_CHOICES, default=getattr( - settings, 'DEFAULT_PRIVACY_LEVEL', + settings, + 'DEFAULT_PRIVACY_LEVEL', 'public', - ), help_text=_( - 'Level of privacy that you want on the repository. ' - 'Protected means public but not in listings.', + ), + help_text=_( + 'Level of privacy that you want on the repository. ' + 'Protected means public but not in listings.', ), ) version_privacy_level = models.CharField( - _('Version Privacy Level'), max_length=20, + _('Version Privacy Level'), + max_length=20, choices=constants.PRIVACY_CHOICES, default=getattr( - settings, 'DEFAULT_PRIVACY_LEVEL', + settings, + 'DEFAULT_PRIVACY_LEVEL', 'public', - ), help_text=_( - 'Default level of privacy you want on built ' - 'versions of documentation.', + ), + help_text=_( + 'Default level of privacy you want on built ' + 'versions of documentation.', ), ) # Subprojects related_projects = models.ManyToManyField( - 'self', verbose_name=_('Related projects'), blank=True, - symmetrical=False, through=ProjectRelationship, + 'self', + verbose_name=_('Related projects'), + blank=True, + symmetrical=False, + through=ProjectRelationship, ) # Language bits language = models.CharField( - _('Language'), max_length=20, default='en', help_text=_( + _('Language'), + max_length=20, + default='en', + help_text=_( 'The language the project ' 'documentation is rendered in. ' "Note: this affects your project's URL.", - ), choices=constants.LANGUAGES, + ), + choices=constants.LANGUAGES, ) programming_language = models.CharField( - _('Programming Language'), max_length=20, default='words', help_text=_( + _('Programming Language'), + max_length=20, + default='words', + help_text=_( 'The primary programming language the project is written in.', - ), choices=constants.PROGRAMMING_LANGUAGES, blank=True, + ), + choices=constants.PROGRAMMING_LANGUAGES, + blank=True, ) # A subproject pointed at its main language, so it can be tracked main_language_project = models.ForeignKey( - 'self', related_name='translations', on_delete=models.SET_NULL, - blank=True, null=True, + 'self', + related_name='translations', + on_delete=models.SET_NULL, + blank=True, + null=True, ) has_valid_webhook = models.BooleanField( @@ -305,7 +389,8 @@ class Project(models.Model): help_text=_('This project has been built with a webhook'), ) has_valid_clone = models.BooleanField( - default=False, help_text=_('This project has been successfully cloned'), + default=False, + help_text=_('This project has been successfully cloned'), ) tags = TaggableManager(blank=True) @@ -393,13 +478,16 @@ def get_docs_url(self, version_slug=None, lang_slug=None, private=None): Always use http for now, to avoid content warnings. """ return resolve( - project=self, version_slug=version_slug, language=lang_slug, + project=self, + version_slug=version_slug, + language=lang_slug, private=private, ) def get_builds_url(self): return reverse( - 'builds_project_list', kwargs={ + 'builds_project_list', + kwargs={ 'project_slug': self.slug, }, ) @@ -416,12 +504,8 @@ def get_subproject_urls(self): This is used in search result linking """ if getattr(settings, 'DONT_HIT_DB', True): - return [(proj['slug'], proj['canonical_url']) - for proj in ( - api.project(self.pk) - .subprojects() - .get()['subprojects'] - )] + return [(proj['slug'], proj['canonical_url']) for proj in + (api.project(self.pk).subprojects().get()['subprojects'])] return [(proj.child.slug, proj.child.get_docs_url()) for proj in self.subprojects.all()] @@ -436,17 +520,25 @@ def get_production_media_path(self, type_, version_slug, include_file=True): :returns: Full path to media file or path """ - if getattr(settings, 'DEFAULT_PRIVACY_LEVEL', 'public') == 'public' or settings.DEBUG: + if getattr(settings, 'DEFAULT_PRIVACY_LEVEL', + 'public') == 'public' or settings.DEBUG: path = os.path.join( - settings.MEDIA_ROOT, type_, self.slug, version_slug, + settings.MEDIA_ROOT, + type_, + self.slug, + version_slug, ) else: path = os.path.join( - settings.PRODUCTION_MEDIA_ARTIFACTS, type_, self.slug, version_slug, + settings.PRODUCTION_MEDIA_ARTIFACTS, + type_, + self.slug, + version_slug, ) if include_file: path = os.path.join( - path, '{}.{}'.format(self.slug, type_.replace('htmlzip', 'zip')), + path, + '{}.{}'.format(self.slug, type_.replace('htmlzip', 'zip')), ) return path @@ -454,7 +546,8 @@ def get_production_media_url(self, type_, version_slug, full_path=True): """Get the URL for downloading a specific media file.""" try: path = reverse( - 'project_download_media', kwargs={ + 'project_download_media', + kwargs={ 'project_slug': self.slug, 'type_': type_, 'version_slug': version_slug, @@ -473,13 +566,16 @@ def subdomain(self): def get_downloads(self): downloads = {} downloads['htmlzip'] = self.get_production_media_url( - 'htmlzip', self.get_default_version(), + 'htmlzip', + self.get_default_version(), ) downloads['epub'] = self.get_production_media_url( - 'epub', self.get_default_version(), + 'epub', + self.get_default_version(), ) downloads['pdf'] = self.get_production_media_url( - 'pdf', self.get_default_version(), + 'pdf', + self.get_default_version(), ) return downloads @@ -634,21 +730,30 @@ def has_aliases(self): def has_pdf(self, version_slug=LATEST): if not self.enable_pdf_build: return False - return os.path.exists(self.get_production_media_path( - type_='pdf', version_slug=version_slug, - )) + return os.path.exists( + self.get_production_media_path( + type_='pdf', + version_slug=version_slug, + ) + ) def has_epub(self, version_slug=LATEST): if not self.enable_epub_build: return False - return os.path.exists(self.get_production_media_path( - type_='epub', version_slug=version_slug, - )) + return os.path.exists( + self.get_production_media_path( + type_='epub', + version_slug=version_slug, + ) + ) def has_htmlzip(self, version_slug=LATEST): - return os.path.exists(self.get_production_media_path( - type_='htmlzip', version_slug=version_slug, - )) + return os.path.exists( + self.get_production_media_path( + type_='htmlzip', + version_slug=version_slug, + ) + ) @property def sponsored(self): @@ -797,7 +902,8 @@ def update_stable_version(self): if identifier_updated and current_stable.active and current_stable.machine: log.info( 'Update stable version: {project}:{version}'.format( - project=self.slug, version=new_stable.identifier, + project=self.slug, + version=new_stable.identifier, ), ) current_stable.identifier = new_stable.identifier @@ -809,7 +915,8 @@ def update_stable_version(self): .format(project=self.slug, version=new_stable.identifier), ) current_stable = self.versions.create_stable( - type=new_stable.type, identifier=new_stable.identifier, + type=new_stable.type, + identifier=new_stable.identifier, ) return new_stable @@ -833,7 +940,8 @@ def get_default_version(self): return self.default_version # check if the default_version exists version_qs = self.versions.filter( - slug=self.default_version, active=True, + slug=self.default_version, + active=True, ) if version_qs.exists(): return self.default_version @@ -933,9 +1041,12 @@ def __init__(self, *args, **kwargs): # These fields only exist on the API return, not on the model, so we'll # remove them to avoid throwing exceptions due to unexpected fields for key in [ - 'users', 'resource_uri', 'absolute_url', 'downloads', - 'main_language_project', 'related_projects', - ]: + 'users', + 'resource_uri', + 'absolute_url', + 'downloads', + 'main_language_project', + 'related_projects',]: try: del kwargs[key] except KeyError: @@ -973,11 +1084,15 @@ class ImportedFile(models.Model): """ project = models.ForeignKey( - 'Project', verbose_name=_('Project'), related_name='imported_files', + 'Project', + verbose_name=_('Project'), + related_name='imported_files', ) version = models.ForeignKey( - 'builds.Version', verbose_name=_('Version'), - related_name='imported_files', null=True, + 'builds.Version', + verbose_name=_('Version'), + related_name='imported_files', + null=True, ) name = models.CharField(_('Name'), max_length=255) slug = models.SlugField(_('Slug')) @@ -988,7 +1103,8 @@ class ImportedFile(models.Model): def get_absolute_url(self): return resolve( - project=self.project, version_slug=self.version.slug, + project=self.project, + version_slug=self.version.slug, filename=self.path, ) @@ -1015,7 +1131,9 @@ def __str__(self): @python_2_unicode_compatible class WebHook(Notification): url = models.URLField( - max_length=600, blank=True, help_text=_('URL to send the webhook to'), + max_length=600, + blank=True, + help_text=_('URL to send the webhook to'), ) def __str__(self): @@ -1029,23 +1147,29 @@ class Domain(models.Model): project = models.ForeignKey(Project, related_name='domains') domain = models.CharField( - _('Domain'), unique=True, max_length=255, + _('Domain'), + unique=True, + max_length=255, validators=[validate_domain_name], ) machine = models.BooleanField( - default=False, help_text=_('This Domain was auto-created'), + default=False, + help_text=_('This Domain was auto-created'), ) cname = models.BooleanField( - default=False, help_text=_('This Domain is a CNAME for the project'), + default=False, + help_text=_('This Domain is a CNAME for the project'), ) canonical = models.BooleanField( - default=False, help_text=_( + default=False, + help_text=_( 'This Domain is the primary one where the documentation is ' 'served from', ), ) https = models.BooleanField( - _('Use HTTPS'), default=False, + _('Use HTTPS'), + default=False, help_text=_('Always use HTTPS for this domain'), ) count = models.IntegerField( @@ -1060,7 +1184,8 @@ class Meta: def __str__(self): return '{domain} pointed at {project}'.format( - domain=self.domain, project=self.project.name, + domain=self.domain, + project=self.project.name, ) def save(self, *args, **kwargs): # pylint: disable=arguments-differ @@ -1124,24 +1249,31 @@ def add_features(sender, **kwargs): (PIP_ALWAYS_UPGRADE, _('Always run pip install --upgrade')), (SKIP_SUBMODULES, _('Skip git submodule checkout')), ( - DONT_OVERWRITE_SPHINX_CONTEXT, _( - 'Do not overwrite context vars in conf.py with Read the Docs context', + DONT_OVERWRITE_SPHINX_CONTEXT, + _( + 'Do not overwrite context vars in conf.py with Read the Docs context', ), ), ( - ALLOW_V2_CONFIG_FILE, _( - 'Allow to use the v2 of the configuration file', + ALLOW_V2_CONFIG_FILE, + _( + 'Allow to use the v2 of the configuration file', ), ), - (MKDOCS_THEME_RTD, _('Use Read the Docs theme for MkDocs as default theme')), ( - DONT_SHALLOW_CLONE, _( - 'Do not shallow clone when cloning git repos', + MKDOCS_THEME_RTD, + _('Use Read the Docs theme for MkDocs as default theme') + ), + ( + DONT_SHALLOW_CLONE, + _( + 'Do not shallow clone when cloning git repos', ), ), ( - USE_TESTING_BUILD_IMAGE, _( - 'Use Docker image labelled as `testing` to build the docs', + USE_TESTING_BUILD_IMAGE, + _( + 'Use Docker image labelled as `testing` to build the docs', ), ), ) diff --git a/readthedocs/projects/tasks.py b/readthedocs/projects/tasks.py index 640a08c9c9b..5f00374aef1 100644 --- a/readthedocs/projects/tasks.py +++ b/readthedocs/projects/tasks.py @@ -161,20 +161,16 @@ def sync_versions(self, version_repo): version_post_data = {'repo': version_repo.repo_url} if version_repo.supports_tags: - version_post_data['tags'] = [ - { - 'identifier': v.identifier, - 'verbose_name': v.verbose_name, - } for v in version_repo.tags - ] + version_post_data['tags'] = [{ + 'identifier': v.identifier, + 'verbose_name': v.verbose_name, + } for v in version_repo.tags] if version_repo.supports_branches: - version_post_data['branches'] = [ - { - 'identifier': v.identifier, - 'verbose_name': v.verbose_name, - } for v in version_repo.branches - ] + version_post_data['branches'] = [{ + 'identifier': v.identifier, + 'verbose_name': v.verbose_name, + } for v in version_repo.branches] self.validate_duplicate_reserved_versions(version_post_data) @@ -1230,8 +1226,8 @@ def send_notifications(version_pk, build_pk): for hook in version.project.webhook_notifications.all(): webhook_notification(version, build, hook.url) for email in version.project.emailhook_notifications.all().values_list( - 'email', - flat=True, + 'email', + flat=True, ): email_notification(version, build, email) diff --git a/readthedocs/projects/urls/private.py b/readthedocs/projects/urls/private.py index c1dd9b20cc5..75d6324d03b 100644 --- a/readthedocs/projects/urls/private.py +++ b/readthedocs/projects/urls/private.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- + """Project URLs for authenticated users.""" from django.conf.urls import url @@ -112,20 +113,24 @@ domain_urls = [ url( - r'^(?P[-\w]+)/domains/$', DomainList.as_view(), + r'^(?P[-\w]+)/domains/$', + DomainList.as_view(), name='projects_domains', ), url( - r'^(?P[-\w]+)/domains/create/$', DomainCreate.as_view(), + r'^(?P[-\w]+)/domains/create/$', + DomainCreate.as_view(), name='projects_domains_create', ), url( r'^(?P[-\w]+)/domains/(?P[-\w]+)/edit/$', - DomainUpdate.as_view(), name='projects_domains_edit', + DomainUpdate.as_view(), + name='projects_domains_edit', ), url( r'^(?P[-\w]+)/domains/(?P[-\w]+)/delete/$', - DomainDelete.as_view(), name='projects_domains_delete', + DomainDelete.as_view(), + name='projects_domains_delete', ), ] @@ -135,12 +140,15 @@ url( r'^(?P{project_slug})/integrations/$'.format( **pattern_opts - ), IntegrationList.as_view(), name='projects_integrations', + ), + IntegrationList.as_view(), + name='projects_integrations', ), url( r'^(?P{project_slug})/integrations/sync/$'.format( **pattern_opts - ), IntegrationWebhookSync.as_view(), + ), + IntegrationWebhookSync.as_view(), name='projects_integrations_webhooks_sync', ), url( @@ -148,7 +156,9 @@ r'^(?P{project_slug})/integrations/create/$'.format( **pattern_opts ) - ), IntegrationCreate.as_view(), name='projects_integrations_create', + ), + IntegrationCreate.as_view(), + name='projects_integrations_create', ), url( ( @@ -156,15 +166,18 @@ r'integrations/(?P{integer_pk})/$'.format( **pattern_opts ) - ), IntegrationDetail.as_view(), name='projects_integrations_detail', + ), + IntegrationDetail.as_view(), + name='projects_integrations_detail', ), url( ( r'^(?P{project_slug})/' r'integrations/(?P{integer_pk})/' r'exchange/(?P[-\w]+)/$'.format(**pattern_opts) - ), IntegrationExchangeDetail.as_view(), - name='projects_integrations_exchanges_detail', + ), + IntegrationExchangeDetail.as_view(), + name='projects_integrations_exchanges_detail', ), url( ( @@ -172,8 +185,9 @@ r'integrations/(?P{integer_pk})/sync/$'.format( **pattern_opts ) - ), IntegrationWebhookSync.as_view(), - name='projects_integrations_webhooks_sync', + ), + IntegrationWebhookSync.as_view(), + name='projects_integrations_webhooks_sync', ), url( ( @@ -181,7 +195,9 @@ r'integrations/(?P{integer_pk})/delete/$'.format( **pattern_opts ) - ), IntegrationDelete.as_view(), name='projects_integrations_delete', + ), + IntegrationDelete.as_view(), + name='projects_integrations_delete', ), ] @@ -191,7 +207,8 @@ url( r'^(?P{project_slug})/subprojects/$'.format( **pattern_opts - ), private.ProjectRelationshipList.as_view(), + ), + private.ProjectRelationshipList.as_view(), name='projects_subprojects', ), url( @@ -199,8 +216,9 @@ r'^(?P{project_slug})/subprojects/create/$'.format( **pattern_opts ) - ), private.ProjectRelationshipCreate.as_view(), - name='projects_subprojects_create', + ), + private.ProjectRelationshipCreate.as_view(), + name='projects_subprojects_create', ), url( ( @@ -208,8 +226,9 @@ r'subprojects/(?P{project_slug})/edit/$'.format( **pattern_opts ) - ), private.ProjectRelationshipUpdate.as_view(), - name='projects_subprojects_update', + ), + private.ProjectRelationshipUpdate.as_view(), + name='projects_subprojects_update', ), url( ( @@ -217,8 +236,9 @@ r'subprojects/(?P{project_slug})/delete/$'.format( **pattern_opts ) - ), private.ProjectRelationshipDelete.as_view(), - name='projects_subprojects_delete', + ), + private.ProjectRelationshipDelete.as_view(), + name='projects_subprojects_delete', ), ] @@ -227,7 +247,8 @@ environmentvariable_urls = [ url( r'^(?P[-\w]+)/environmentvariables/$', - EnvironmentVariableList.as_view(), name='projects_environmentvariables', + EnvironmentVariableList.as_view(), + name='projects_environmentvariables', ), url( r'^(?P[-\w]+)/environmentvariables/create/$', diff --git a/readthedocs/projects/views/private.py b/readthedocs/projects/views/private.py index 41b3acda3cf..02256089c81 100644 --- a/readthedocs/projects/views/private.py +++ b/readthedocs/projects/views/private.py @@ -220,7 +220,9 @@ def project_delete(request, project_slug): if request.method == 'POST': broadcast( - type='app', task=tasks.remove_dirs, args=[(project.doc_path,)], + type='app', + task=tasks.remove_dirs, + args=[(project.doc_path,)], ) project.delete() messages.success(request, _('Project deleted')) diff --git a/readthedocs/projects/views/public.py b/readthedocs/projects/views/public.py index 9256e92e038..e90bbeb3e08 100644 --- a/readthedocs/projects/views/public.py +++ b/readthedocs/projects/views/public.py @@ -116,8 +116,11 @@ def project_badge(request, project_slug): """Return a sweet badge for the project.""" style = request.GET.get('style', 'flat') if style not in ( - 'flat', 'plastic', 'flat-square', 'for-the-badge', - 'social', + 'flat', + 'plastic', + 'flat-square', + 'for-the-badge', + 'social', ): style = 'flat' diff --git a/readthedocs/restapi/views/core_views.py b/readthedocs/restapi/views/core_views.py index 578f1123232..1e4afdd8a7b 100644 --- a/readthedocs/restapi/views/core_views.py +++ b/readthedocs/restapi/views/core_views.py @@ -41,6 +41,8 @@ def docurl(request): ) return Response({ 'url': make_document_url( - project=project, version=version.slug, page=doc, + project=project, + version=version.slug, + page=doc, ), }) diff --git a/readthedocs/restapi/views/model_views.py b/readthedocs/restapi/views/model_views.py index f0586e2fadd..60a634c7b12 100644 --- a/readthedocs/restapi/views/model_views.py +++ b/readthedocs/restapi/views/model_views.py @@ -138,7 +138,8 @@ def active_versions(self, request, **kwargs): }) @decorators.action( - detail=True, permission_classes=[permissions.IsAdminUser], + detail=True, + permission_classes=[permissions.IsAdminUser], ) def token(self, request, **kwargs): project = get_object_or_404( diff --git a/readthedocs/search/indexes.py b/readthedocs/search/indexes.py index f8c88a4197b..05a2f759ac7 100644 --- a/readthedocs/search/indexes.py +++ b/readthedocs/search/indexes.py @@ -237,7 +237,8 @@ def get_mapping(self): 'id': {'type': 'long'}, 'name': {'type': 'string', 'analyzer': 'default_icu'}, 'description': { - 'type': 'string', 'analyzer': 'default_icu', + 'type': 'string', + 'analyzer': 'default_icu', }, 'slug': {'type': 'string', 'index': 'not_analyzed'}, 'lang': {'type': 'string', 'index': 'not_analyzed'}, @@ -266,7 +267,14 @@ def extract_document(self, data): doc = {} attrs = ( - 'id', 'name', 'slug', 'description', 'lang', 'tags', 'author', 'url', + 'id', + 'name', + 'slug', + 'description', + 'lang', + 'tags', + 'author', + 'url', ) for attr in attrs: doc[attr] = data.get(attr, '') @@ -367,7 +375,8 @@ def get_mapping(self): 'type': 'object', 'properties': { 'code': { - 'type': 'string', 'analyzer': 'default_icu', + 'type': 'string', + 'analyzer': 'default_icu', }, }, }, @@ -383,7 +392,13 @@ def extract_document(self, data): doc = {} attrs = ( - 'id', 'project', 'title', 'page_id', 'version', 'path', 'content', + 'id', + 'project', + 'title', + 'page_id', + 'version', + 'path', + 'content', 'commit', ) for attr in attrs: diff --git a/readthedocs/search/lib.py b/readthedocs/search/lib.py index 16297e6414c..007a95afdd9 100644 --- a/readthedocs/search/lib.py +++ b/readthedocs/search/lib.py @@ -54,7 +54,11 @@ def search_project(request, query, language=None): def search_file( - request, query, project_slug=None, version_slug=LATEST, taxonomy=None, + request, + query, + project_slug=None, + version_slug=LATEST, + taxonomy=None, ): """ Search index for files matching query. diff --git a/readthedocs/search/parse_json.py b/readthedocs/search/parse_json.py index 8f99aa012de..72e85a999af 100644 --- a/readthedocs/search/parse_json.py +++ b/readthedocs/search/parse_json.py @@ -28,9 +28,9 @@ def process_all_json_files(version, build_dir=True): for root, _, files in os.walk(full_path): for filename in fnmatch.filter(files, '*.fjson'): if filename in [ - 'search.fjson', 'genindex.fjson', - 'py-modindex.fjson', - ]: + 'search.fjson', + 'genindex.fjson', + 'py-modindex.fjson',]: continue html_files.append(os.path.join(root, filename)) page_list = [] diff --git a/readthedocs/search/utils.py b/readthedocs/search/utils.py index 1e50b84148d..85c3c5735a3 100644 --- a/readthedocs/search/utils.py +++ b/readthedocs/search/utils.py @@ -37,10 +37,12 @@ def process_mkdocs_json(version, build_dir=True): relative_path = parse_path_from_file(file_path=filename) html = parse_content_from_file(file_path=filename) headers = parse_headers_from_file( - documentation_type='mkdocs', file_path=filename, + documentation_type='mkdocs', + file_path=filename, ) sections = parse_sections_from_file( - documentation_type='mkdocs', file_path=filename, + documentation_type='mkdocs', + file_path=filename, ) try: title = sections[0]['title'] @@ -137,7 +139,8 @@ def parse_content_from_file(file_path): if not content: log.info( - '(Search Index) Unable to index file: %s, empty file', file_path, + '(Search Index) Unable to index file: %s, empty file', + file_path, ) else: log.debug('(Search Index) %s length: %s', file_path, len(content)) diff --git a/readthedocs/urls.py b/readthedocs/urls.py index cb723c64fb1..fe238508130 100644 --- a/readthedocs/urls.py +++ b/readthedocs/urls.py @@ -120,7 +120,12 @@ # Export URLs groups = [ - basic_urls, rtd_urls, project_urls, api_urls, core_urls, i18n_urls, + basic_urls, + rtd_urls, + project_urls, + api_urls, + core_urls, + i18n_urls, deprecated_urls, ] diff --git a/readthedocs/vcs_support/backends/hg.py b/readthedocs/vcs_support/backends/hg.py index 515e74a7710..0361bfa462c 100644 --- a/readthedocs/vcs_support/backends/hg.py +++ b/readthedocs/vcs_support/backends/hg.py @@ -109,7 +109,10 @@ def checkout(self, identifier=None): if not identifier: identifier = 'tip' exit_code, stdout, stderr = self.run( - 'hg', 'update', '--clean', identifier, + 'hg', + 'update', + '--clean', + identifier, ) if exit_code != 0: raise RepositoryError( diff --git a/readthedocs/vcs_support/utils.py b/readthedocs/vcs_support/utils.py index c948d36fdd7..53dd5b646c5 100644 --- a/readthedocs/vcs_support/utils.py +++ b/readthedocs/vcs_support/utils.py @@ -29,7 +29,8 @@ class Lock: def __init__(self, project, version, timeout=5, polling_interval=0.1): self.name = project.slug self.fpath = os.path.join( - project.doc_path, '%s__rtdlock' % version.slug, + project.doc_path, + '%s__rtdlock' % version.slug, ) self.timeout = timeout self.polling_interval = polling_interval @@ -95,7 +96,8 @@ class NonBlockingLock: def __init__(self, project, version, max_lock_age=None): self.fpath = os.path.join( - project.doc_path, '%s__rtdlock' % version.slug, + project.doc_path, + '%s__rtdlock' % version.slug, ) self.max_lock_age = max_lock_age self.name = project.slug From 9f11db5347b2684558d2f1466a6ea21e34f04c0f Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 19:24:01 +0100 Subject: [PATCH 39/42] Lint changes --- .../core/management/commands/import_github_language.py | 6 +----- readthedocs/core/views/serve.py | 10 +++++----- readthedocs/projects/forms.py | 7 ++++++- readthedocs/projects/models.py | 2 +- readthedocs/search/parse_json.py | 2 +- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/readthedocs/core/management/commands/import_github_language.py b/readthedocs/core/management/commands/import_github_language.py index 46159bcf651..261e2a67999 100644 --- a/readthedocs/core/management/commands/import_github_language.py +++ b/readthedocs/core/management/commands/import_github_language.py @@ -37,11 +37,7 @@ def handle(self, *args, **options): print('Invalid GitHub token, exiting') return - for project in Project.objects.filter( - programming_language__in=[ - 'none', '', 'words', - ], - ).filter(repo__contains='github',): + for project in Project.objects.filter(programming_language__in=['none', '', 'words']).filter(repo__contains='github'): user = repo = '' repo_url = project.repo for regex in GITHUB_REGEXS: diff --git a/readthedocs/core/views/serve.py b/readthedocs/core/views/serve.py index 513451d702e..a1f65a11ea5 100644 --- a/readthedocs/core/views/serve.py +++ b/readthedocs/core/views/serve.py @@ -58,9 +58,9 @@ def map_subproject_slug(view_func): """ @wraps(view_func) - def inner_view( - request, subproject=None, subproject_slug=None, *args, **kwargs - ): # noqa + def inner_view( # noqa + request, subproject=None, subproject_slug=None, *args, **kwargs, + ): if subproject is None and subproject_slug: # Try to fetch by subproject alias first, otherwise we might end up # redirected to an unrelated project. @@ -88,9 +88,9 @@ def map_project_slug(view_func): """ @wraps(view_func) - def inner_view( + def inner_view( # noqa request, project=None, project_slug=None, *args, **kwargs - ): # noqa + ): if project is None: if not project_slug: project_slug = request.slug diff --git a/readthedocs/projects/forms.py b/readthedocs/projects/forms.py index 9c9cd5af1aa..aae5e9d278c 100644 --- a/readthedocs/projects/forms.py +++ b/readthedocs/projects/forms.py @@ -616,9 +616,14 @@ def clean_project(self): raise forms.ValidationError( _(msg).format(lang=self.parent.get_language_display()), ) + + # yapf: disable exists_translation = ( - self.parent.translations.filter(language=self.translation.language).exists() # yapf: disabled + self.parent.translations + .filter(language=self.translation.language) + .exists() ) + # yapf: enable if exists_translation: msg = ('This project already has a translation for {lang}.') raise forms.ValidationError( diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index 1fefb7be30b..f39a54c4625 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -1046,7 +1046,7 @@ def __init__(self, *args, **kwargs): 'absolute_url', 'downloads', 'main_language_project', - 'related_projects',]: + 'related_projects']: try: del kwargs[key] except KeyError: diff --git a/readthedocs/search/parse_json.py b/readthedocs/search/parse_json.py index 72e85a999af..194c55ff165 100644 --- a/readthedocs/search/parse_json.py +++ b/readthedocs/search/parse_json.py @@ -30,7 +30,7 @@ def process_all_json_files(version, build_dir=True): if filename in [ 'search.fjson', 'genindex.fjson', - 'py-modindex.fjson',]: + 'py-modindex.fjson']: continue html_files.append(os.path.join(root, filename)) page_list = [] From ed67c549a5c96febac4e614307f131adcf95e856 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 19:29:39 +0100 Subject: [PATCH 40/42] noqa on one line --- readthedocs/core/management/commands/import_github_language.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readthedocs/core/management/commands/import_github_language.py b/readthedocs/core/management/commands/import_github_language.py index 261e2a67999..bf99ac265c0 100644 --- a/readthedocs/core/management/commands/import_github_language.py +++ b/readthedocs/core/management/commands/import_github_language.py @@ -37,7 +37,7 @@ def handle(self, *args, **options): print('Invalid GitHub token, exiting') return - for project in Project.objects.filter(programming_language__in=['none', '', 'words']).filter(repo__contains='github'): + for project in Project.objects.filter(programming_language__in=['none', '', 'words']).filter(repo__contains='github'): # noqa user = repo = '' repo_url = project.repo for regex in GITHUB_REGEXS: From 6092d0efaee13a045f9faa94f6a68e9927382942 Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 20:35:12 +0100 Subject: [PATCH 41/42] Remove py36 from travis.matrix.include --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e5083ec7d50..25bb76d7b19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,6 @@ env: - ES_VERSION=1.3.9 ES_DOWNLOAD_URL=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz matrix: include: - - python: 3.6 - env: TOXENV=py36 - python: 3.6 env: TOXENV=docs - python: 3.6 From f68d84a2efbf15f22b9839db079deb12358a73da Mon Sep 17 00:00:00 2001 From: Manuel Kaufmann Date: Mon, 21 Jan 2019 20:38:58 +0100 Subject: [PATCH 42/42] Move ES env vars to py36 job --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 25bb76d7b19..802c8986cc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: python python: - 3.6 -env: - - ES_VERSION=1.3.9 ES_DOWNLOAD_URL=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz matrix: include: + - python: 3.6 + env: TOXENV=py36 ES_VERSION=1.3.9 ES_DOWNLOAD_URL=https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz - python: 3.6 env: TOXENV=docs - python: 3.6

  • Table headerTable header 2