diff --git a/.travis.yml b/.travis.yml
index 56eb7d501..f942dd705 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,8 +2,6 @@ language: python
jobs:
include:
- - os: linux
- python: 2.7
- os: linux
python: 3.6
- os: linux
@@ -34,8 +32,8 @@ before_install:
- source activate testenv
# Pin R to 3.6.3 for now
- conda install -c conda-forge r-base=3.6.3 r-knitr
- # Install regex through conda to avoid issues with Linux 2.7 and Mac wheels
- - if [[ "$TRAVIS_OS_NAME" == "osx" || "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
+ # Install regex through conda to avoid issues with Mac wheels
+ - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
conda install -c conda-forge regex;
fi
install:
diff --git a/appveyor.yml b/appveyor.yml
index 5103b1529..c7b7a1aa8 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,13 +2,16 @@ build: false
environment:
matrix:
- - PYTHON: "C:\\Python27"
- PYTHON_VERSION: "2.7.8"
- PYTHON_ARCH: "32"
- MINICONDA: C:\Miniconda
-
- PYTHON: "C:\\Python36-x64"
- PYTHON_VERSION: "3.6.6"
+ PYTHON_VERSION: "3.6.10"
+ PYTHON_ARCH: "64"
+ MINICONDA: C:\Miniconda3
+ - PYTHON: "C:\\Python37-x64"
+ PYTHON_VERSION: "3.7.7"
+ PYTHON_ARCH: "64"
+ MINICONDA: C:\Miniconda3
+ - PYTHON: "C:\\Python38-x64"
+ PYTHON_VERSION: "3.8.3"
PYTHON_ARCH: "64"
MINICONDA: C:\Miniconda3
diff --git a/docs/requirements.txt b/docs/requirements.txt
index d1f2427a9..531d93c6c 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,3 @@
-future
-enum34
pyyaml
markdown
pygments
@@ -18,4 +16,4 @@ werkzeug>=1.0
gunicorn
inflection
pillow
-weasyprint<=0.42.3
\ No newline at end of file
+weasyprint
\ No newline at end of file
diff --git a/knowledge_repo/_version.py b/knowledge_repo/_version.py
index 0344c355d..5ba21815d 100644
--- a/knowledge_repo/_version.py
+++ b/knowledge_repo/_version.py
@@ -18,8 +18,6 @@
# should be defined elsewhere.
__dependencies__ = [
# Knowledge Repository Dependencies
- 'future', # Python 2/3 support
- 'enum34', # Python 3.4+ enum object used for Post status
'pyyaml', # Yaml parser and utilities
'markdown', # Markdown conversion utilities
'pygments', # Code highlighting support in markdown
@@ -41,13 +39,13 @@
'gunicorn', # Deployed webserver
'inflection', # String transformation library
'pillow', # Image thumbnailing
- 'weasyprint<=0.42.3', # Post PDF download option. Pinned to maintain compatibility with Python 2
+ 'weasyprint', # Post PDF download option
]
__optional_dependencies__ = {
# ipynb notebook conversion suport
'ipynb': [
- 'nbformat<5.0.0',
+ 'nbformat',
'nbconvert[execute]',
'traitlets'
],
diff --git a/knowledge_repo/app/__init__.py b/knowledge_repo/app/__init__.py
index 54ea1a785..b093934e4 100644
--- a/knowledge_repo/app/__init__.py
+++ b/knowledge_repo/app/__init__.py
@@ -1,4 +1,2 @@
-from __future__ import absolute_import
-
from . import auth_providers
from .app import KnowledgeFlask
diff --git a/knowledge_repo/app/app.py b/knowledge_repo/app/app.py
index 5943bb1bb..2e2457de3 100644
--- a/knowledge_repo/app/app.py
+++ b/knowledge_repo/app/app.py
@@ -1,14 +1,12 @@
-from __future__ import absolute_import
-
import os
-import imp
+import importlib
+import sys
import logging
import traceback
import math
import uuid
import mimetypes
-import six
from flask import Flask, current_app, render_template, request, session
from flask_login import LoginManager, user_loaded_from_request
from flask_mail import Mail
@@ -52,8 +50,12 @@ def __init__(self, repo, db_uri=None, debug=None, config=None, **kwargs):
# Load configuration from file or provided object
if config:
- if isinstance(config, six.string_types):
- config = imp.load_source('knowledge_server_config', os.path.abspath(config))
+ if isinstance(config, str):
+ module_name = 'knowledge_server_config'
+ spec = importlib.util.spec_from_file_location(module_name, os.path.abspath(config))
+ config = importlib.util.module_from_spec(spec)
+ sys.modules[module_name] = config
+ spec.loader.exec_module(config)
self.config.from_object(config)
# Add configuration passed in as keyword arguments
@@ -75,7 +77,7 @@ def __init__(self, repo, db_uri=None, debug=None, config=None, **kwargs):
# Configure database
if db_uri:
self.config['SQLALCHEMY_DATABASE_URI'] = db_uri
- logger.debug(u"Using database: {}".format(self.config['SQLALCHEMY_DATABASE_URI']))
+ logger.debug("Using database: {}".format(self.config['SQLALCHEMY_DATABASE_URI']))
# Register database schema with flask app
sqlalchemy_db.init_app(self)
@@ -235,7 +237,7 @@ def modify_query(**new_values):
for key, value in new_values.items():
args[key] = value
- return u'{}?{}'.format(request.path, url_encode(args))
+ return '{}?{}'.format(request.path, url_encode(args))
@self.template_global()
def pagination_pages(current_page, page_count, max_pages=5, extremes=True):
diff --git a/knowledge_repo/app/auth_provider.py b/knowledge_repo/app/auth_provider.py
index 154ba6ef3..91b91be29 100644
--- a/knowledge_repo/app/auth_provider.py
+++ b/knowledge_repo/app/auth_provider.py
@@ -1,16 +1,14 @@
from abc import abstractmethod
-from builtins import object
from flask_principal import identity_changed, Identity
from flask_login import login_user
-from future.utils import with_metaclass
from flask import redirect, current_app, Blueprint, url_for, session
from .utils.auth import prepare_user
from ..utils.registry import SubclassRegisteringABCMeta
-class KnowledgeAuthProvider(with_metaclass(SubclassRegisteringABCMeta, object)):
+class KnowledgeAuthProvider(object, metaclass=SubclassRegisteringABCMeta):
_registry_keys = None
@classmethod
diff --git a/knowledge_repo/app/auth_providers/oauth2.py b/knowledge_repo/app/auth_providers/oauth2.py
index 29baf159f..5c501b2a2 100644
--- a/knowledge_repo/app/auth_providers/oauth2.py
+++ b/knowledge_repo/app/auth_providers/oauth2.py
@@ -1,9 +1,8 @@
import posixpath
import json
-import six
from flask import request, redirect
-from six.moves.urllib.parse import urljoin
+from urllib.parse import urljoin
from ..models import User
from ..auth_provider import KnowledgeAuthProvider
@@ -158,7 +157,7 @@ def extract_from_dict(d, key):
key = key[0]
else:
return extract_from_dict(d[key[0]], key[1:])
- if isinstance(key, six.string_types):
+ if isinstance(key, str):
return d[key]
raise RuntimeError("Invalid key type: {}.".format(key))
diff --git a/knowledge_repo/app/deploy/common.py b/knowledge_repo/app/deploy/common.py
index 36edbe43d..fcfb34cb1 100644
--- a/knowledge_repo/app/deploy/common.py
+++ b/knowledge_repo/app/deploy/common.py
@@ -4,9 +4,7 @@
import sys
import textwrap
from abc import abstractmethod
-from future.utils import with_metaclass
-import six
import knowledge_repo
from knowledge_repo.utils.registry import SubclassRegisteringABCMeta
@@ -21,7 +19,7 @@ def get_app():
return get_app
-class KnowledgeDeployer(with_metaclass(SubclassRegisteringABCMeta, object)):
+class KnowledgeDeployer(object, metaclass=SubclassRegisteringABCMeta):
def __init__(self,
knowledge_builder,
@@ -29,8 +27,8 @@ def __init__(self,
port=7000,
workers=4,
timeout=60):
- assert isinstance(knowledge_builder, six.string_types + (types.FunctionType, )), \
- u"Unknown builder type {}".format(type(knowledge_builder))
+ assert isinstance(knowledge_builder, str + (types.FunctionType, )), \
+ "Unknown builder type {}".format(type(knowledge_builder))
self.knowledge_builder = knowledge_builder
self.host = host
self.port = port
@@ -57,17 +55,17 @@ def builder_str(self):
if isinstance(self.knowledge_builder, types.FunctionType):
out = []
for nl, cell in zip(self.knowledge_builder.__code__.co_freevars, self.knowledge_builder.__closure__):
- if isinstance(cell.cell_contents, six.string_types):
- out.append(u'{} = "{}"'.format(nl, cell.cell_contents.replace('"', '\\"')))
+ if isinstance(cell.cell_contents, str):
+ out.append('{} = "{}"'.format(nl, cell.cell_contents.replace('"', '\\"')))
else:
- out.append(u'{} = {}'.format(nl, cell.cell_contents))
+ out.append('{} = {}'.format(nl, cell.cell_contents))
out.append(textwrap.dedent(inspect.getsource(self.knowledge_builder)))
- return u'\n'.join(out)
+ return '\n'.join(out)
return self.knowledge_builder
@property
def builder_func(self):
- if isinstance(self.knowledge_builder, six.string_types):
+ if isinstance(self.knowledge_builder, str):
knowledge_builder = 'def get_app():\n\t' + self.knowledge_builder.replace('\n', '\t') + '\n\treturn app'
namespace = {}
exec(knowledge_builder, namespace)
@@ -91,16 +89,16 @@ def write_temp_files(self):
out = []
out.append('import sys')
- out.append(u'sys.path.insert(0, "{}")'.format(kr_path))
+ out.append('sys.path.insert(0, "{}")'.format(kr_path))
out.append('import knowledge_repo')
out.append(self.builder_str)
- if not isinstance(self.knowledge_builder, six.string_types):
+ if not isinstance(self.knowledge_builder, str):
out.append('app = %s()' % self.knowledge_builder.__name__)
out.append('app.start_indexing()')
with open(tmp_path, 'w') as f:
- f.write(u'\n'.join(out))
+ f.write('\n'.join(out))
return tmp_dir
diff --git a/knowledge_repo/app/deploy/gunicorn.py b/knowledge_repo/app/deploy/gunicorn.py
index 540b218ac..abb55d86c 100644
--- a/knowledge_repo/app/deploy/gunicorn.py
+++ b/knowledge_repo/app/deploy/gunicorn.py
@@ -5,8 +5,6 @@
Adapted from example in http://docs.gunicorn.org/en/stable/custom.html.
"""
-from __future__ import absolute_import
-
from gunicorn.app.base import BaseApplication
from .common import KnowledgeDeployer
@@ -30,7 +28,7 @@ def load_config(self):
# Update the configuration with the options specified via KnowledgeDeployer
options = {
- 'bind': u'{}:{}'.format(self.host, self.port),
+ 'bind': '{}:{}'.format(self.host, self.port),
'workers': self.workers,
'timeout': self.timeout,
}
diff --git a/knowledge_repo/app/deploy/uwsgi.py b/knowledge_repo/app/deploy/uwsgi.py
index ecb463836..838c31974 100644
--- a/knowledge_repo/app/deploy/uwsgi.py
+++ b/knowledge_repo/app/deploy/uwsgi.py
@@ -12,7 +12,7 @@ class uWSGIDeployer(KnowledgeDeployer):
_registry_keys = ['uwsgi']
- COMMAND = u"uwsgi {protocol} --plugin python --wsgi-file {{path}} --callable app --master --processes {{processes}} --threads {{threads}}"
+ COMMAND = "uwsgi {protocol} --plugin python --wsgi-file {{path}} --callable app --master --processes {{processes}} --threads {{threads}}"
def run(self):
if not self.app.check_thread_support():
@@ -21,7 +21,7 @@ def run(self):
tmp_dir = self.write_temp_files()
options = {
- 'socket': u'{}:{}'.format(self.host, self.port),
+ 'socket': '{}:{}'.format(self.host, self.port),
'processes': self.workers,
'threads': 2,
'timeout': self.timeout,
@@ -36,8 +36,8 @@ def run(self):
self.COMMAND = self.COMMAND.format(protocol="--http {socket}")
try:
- cmd = u"cd {};".format(tmp_dir) + self.COMMAND.format(**options)
- logger.info("Starting server with command: %s", u" ".join(cmd))
+ cmd = "cd {};".format(tmp_dir) + self.COMMAND.format(**options)
+ logger.info("Starting server with command: %s", " ".join(cmd))
subprocess.check_output(cmd, shell=True)
finally:
shutil.rmtree(tmp_dir)
diff --git a/knowledge_repo/app/index.py b/knowledge_repo/app/index.py
index 8c93fefad..43da1afc4 100644
--- a/knowledge_repo/app/index.py
+++ b/knowledge_repo/app/index.py
@@ -1,10 +1,7 @@
-from __future__ import absolute_import
-
import logging
import multiprocessing
import os
import time
-from builtins import str
from .proxies import db_session, current_repo, current_app
from .models import ErrorLog, Post, IndexMetadata
@@ -160,12 +157,12 @@ def update_index(check_timeouts=True, force=False, reindex=False):
# If UUID has changed, check if we can find it elsewhere in the repository, and if so update index path
if post.uuid and ((post.path not in kr_dir) or (post.uuid != kr_dir[post.path].uuid)):
if post.uuid in kr_uuids:
- logger.info(u'Updating location of post: {} -> {}'.format(post.path, kr_uuids[post.uuid].path))
+ logger.info('Updating location of post: {} -> {}'.format(post.path, kr_uuids[post.uuid].path))
post.path = kr_uuids[post.uuid].path
# If path of post no longer in directory, mark as unpublished
if post.path not in kr_dir:
- logger.info(u'Recording unpublished status for post at {}'.format(post.path))
+ logger.info('Recording unpublished status for post at {}'.format(post.path))
post.status = current_repo.PostStatus.UNPUBLISHED
continue
@@ -177,17 +174,17 @@ def update_index(check_timeouts=True, force=False, reindex=False):
# Update metadata of post if required
if reindex or (kp.revision > post.revision or not post.is_published or kp.uuid != post.uuid):
if kp.is_valid():
- logger.info(u'Recording update to post at: {}'.format(kp.path))
+ logger.info('Recording update to post at: {}'.format(kp.path))
post.update_metadata_from_kp(kp)
else:
- logger.warning(u'Update to post at "{}" is corrupt.'.format(kp.path))
+ logger.warning('Update to post at "{}" is corrupt.'.format(kp.path))
# Add the new posts that remain in kr_dir
for kp_path, kp in kr_dir.items():
if not kp.is_valid():
- logger.warning(u'New post at "{}" is corrupt.'.format(kp.path))
+ logger.warning('New post at "{}" is corrupt.'.format(kp.path))
continue
- logger.info(u'creating new post from path {}'.format(kp_path))
+ logger.info('creating new post from path {}'.format(kp_path))
post = Post()
db_session.add(post)
db_session.flush() # (matthew) Fix groups logic so this is not necessary
diff --git a/knowledge_repo/app/migrations/env.py b/knowledge_repo/app/migrations/env.py
index 01d6fdbb2..2b0e008ce 100755
--- a/knowledge_repo/app/migrations/env.py
+++ b/knowledge_repo/app/migrations/env.py
@@ -1,4 +1,3 @@
-from __future__ import with_statement
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
diff --git a/knowledge_repo/app/models.py b/knowledge_repo/app/models.py
index c70409438..59553c293 100644
--- a/knowledge_repo/app/models.py
+++ b/knowledge_repo/app/models.py
@@ -3,8 +3,6 @@
import datetime
import logging
import traceback
-from builtins import str
-from future.utils import raise_with_traceback
from flask import current_app, request
from flask_login import UserMixin
from flask_sqlalchemy import SQLAlchemy
@@ -124,9 +122,9 @@ def from_exception(cls, e):
filename = os.path.relpath(filename, os.path.join(os.path.dirname(__file__), '..'))
return ErrorLog(
function=function,
- location=u'{}:{}'.format(filename, linenumber),
- message=u'{}: {}'.format(e.__class__.__name__, u"; ".join(str(a) for a in e.args)),
- traceback=u"\n".join(traceback.format_tb(tb))
+ location='{}:{}'.format(filename, linenumber),
+ message='{}: {}'.format(e.__class__.__name__, "; ".join(str(a) for a in e.args)),
+ traceback="\n".join(traceback.format_tb(tb))
)
@classmethod
@@ -138,7 +136,7 @@ def wrapped(*args, **kwargs):
db_session.rollback()
db_session.add(ErrorLog.from_exception(e))
db_session.commit()
- raise_with_traceback(e)
+ raise e.with_traceback()
return wrapped
@@ -340,7 +338,7 @@ class Tag(db.Model):
def description(self):
if self._description:
return self._description
- return u"All posts with tag '{}'.".format(self.name)
+ return "All posts with tag '{}'.".format(self.name)
@description.expression
def description(self):
@@ -407,7 +405,7 @@ def authors(self, authors):
@hybrid_property
def authors_string(self):
- return u', '.join([author.format_name for author in self.authors])
+ return ', '.join([author.format_name for author in self.authors])
@authors_string.expression
def authors_string(self):
diff --git a/knowledge_repo/app/routes/debug.py b/knowledge_repo/app/routes/debug.py
index 067d3fa17..a5f6845d0 100644
--- a/knowledge_repo/app/routes/debug.py
+++ b/knowledge_repo/app/routes/debug.py
@@ -2,6 +2,8 @@
import platform
import types
import logging
+from urllib.parse import unquote
+
import tabulate
import knowledge_repo
@@ -68,7 +70,6 @@ def force_reindex():
@blueprint.route('/debug/views')
def show_views():
- import urllib
output = []
for rule in current_app.url_map.iter_rules():
options = {}
@@ -77,7 +78,7 @@ def show_views():
methods = ','.join(rule.methods)
url = url_for(rule.endpoint, **options)
- line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, url))
+ line = unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, url))
output.append(line)
return "
".join(sorted(output))
diff --git a/knowledge_repo/app/routes/editor.py b/knowledge_repo/app/routes/editor.py
index 1266dc62c..3d77bb4c5 100644
--- a/knowledge_repo/app/routes/editor.py
+++ b/knowledge_repo/app/routes/editor.py
@@ -2,8 +2,9 @@
import logging
import sys
import os
-from builtins import str
from datetime import datetime
+from urllib.parse import unquote
+
from flask import request, render_template, Blueprint, current_app, url_for, send_from_directory, g
from sqlalchemy import or_
from werkzeug.utils import secure_filename
@@ -17,10 +18,6 @@
from ..index import update_index
-if sys.version_info.major > 2:
- from urllib.parse import unquote as urlunquote
-else:
- from urllib import unquote as urlunquote
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@@ -130,7 +127,7 @@ def save_post():
if prefixes is not None:
if not any([path.startswith(prefix) for prefix in prefixes]):
- return json.dumps({'msg': (u"Your post path must begin with one of {}").format(prefixes),
+ return json.dumps({'msg': ("Your post path must begin with one of {}").format(prefixes),
'success': False})
# TODO better handling of overwriting
@@ -138,7 +135,7 @@ def save_post():
if path in current_repo:
kp = current_repo.post(path)
if current_user.identifier not in kp.headers['authors'] and current_user.identifier not in current_repo.config.editors:
- return json.dumps({'msg': (u"Post with path {} already exists and you are not an author!"
+ return json.dumps({'msg': ("Post with path {} already exists and you are not an author!"
"\nPlease try a different path").format(path),
'success': False})
@@ -159,7 +156,7 @@ def save_post():
if 'proxy' in data:
headers['proxy'] = data['proxy']
- kp.write(urlunquote(data['markdown']), headers=headers)
+ kp.write(unquote(data['markdown']), headers=headers)
# add to repo
current_repo.add(kp, update=True, message=headers['title']) # THIS IS DANGEROUS
@@ -193,7 +190,7 @@ def publish_post():
""" Publish the post by changing the status """
path = request.args.get('path', None)
if path not in current_repo:
- return json.dumps({'msg': u"Unable to retrieve post with path = {}!".format(path), 'success': False})
+ return json.dumps({'msg': "Unable to retrieve post with path = {}!".format(path), 'success': False})
current_repo.publish(path)
update_index(check_timeouts=False)
@@ -207,7 +204,7 @@ def unpublish_post():
""" Unpublish the post """
path = request.args.get('path', None)
if path not in current_repo:
- return json.dumps({'msg': u"Unable to retrieve post with path = {}!".format(path), 'success': False})
+ return json.dumps({'msg': "Unable to retrieve post with path = {}!".format(path), 'success': False})
current_repo.unpublish(path)
update_index(check_timeouts=False)
@@ -221,7 +218,7 @@ def accept():
""" Accept the post """
path = request.args.get('path', None)
if path not in current_repo:
- return json.dumps({'msg': u"Unable to retrieve post with path = {}!".format(path), 'success': False})
+ return json.dumps({'msg': "Unable to retrieve post with path = {}!".format(path), 'success': False})
current_repo.accept(path)
update_index()
return 'OK'
@@ -234,7 +231,7 @@ def delete_post():
""" Delete a post """
path = request.args.get('path', None)
if path not in current_repo:
- return json.dumps({'msg': u"Unable to retrieve post with path = {}!".format(path), 'success': False})
+ return json.dumps({'msg': "Unable to retrieve post with path = {}!".format(path), 'success': False})
kp = current_repo.post(path)
if current_user.identifier not in kp.headers['authors']:
return json.dumps({'msg': "You can only delete a post where you are an author!", 'success': False})
@@ -301,7 +298,7 @@ def file_upload():
send_from_directory(dst_folder, filename)
uploadedFiles += [url_for("static", filename=os.path.join(upload_folder, filename))]
except Exception as e:
- error_msg = u"ERROR during image upload: {}".format(str(e))
+ error_msg = "ERROR during image upload: {}".format(str(e))
logger.error(error_msg)
return json.dumps({'error_msg': error_msg, 'success': False})
@@ -313,11 +310,11 @@ def file_upload():
num_pages = src_pdf.getNumPages()
for page_num in range(num_pages):
page_png = pdf_page_to_png(src_pdf, page_num)
- page_name = u"{filename}_{page_num}.jpg".format(**locals())
+ page_name = "{filename}_{page_num}.jpg".format(**locals())
page_png.save(filename=os.path.join(dst_folder, page_name))
uploadedFiles += [url_for("static", filename=os.path.join(upload_folder, page_name))]
except Exception as e:
- error_msg = u"ERROR during pdf upload: {}".format(str(e))
+ error_msg = "ERROR during pdf upload: {}".format(str(e))
logger.error(error_msg)
return json.dumps({'error_msg': error_msg, 'success': False})
diff --git a/knowledge_repo/app/routes/index.py b/knowledge_repo/app/routes/index.py
index 980d24e59..8bb53f51d 100644
--- a/knowledge_repo/app/routes/index.py
+++ b/knowledge_repo/app/routes/index.py
@@ -8,7 +8,6 @@
"""
import os
import json
-from builtins import str
from collections import namedtuple
from flask import request, render_template, redirect, Blueprint, current_app, make_response
from flask_login import login_required
@@ -42,7 +41,7 @@ def site_map():
# url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((str(rule), rule.endpoint))
# links is now a list of url, endpoint tuples
- return u'
'.join(str(link) for link in links)
+ return '
'.join(str(link) for link in links)
@blueprint.route('/')
@@ -219,7 +218,7 @@ def unpack(d):
_, grouped_data = unpack(folder_to_posts)
else:
- raise ValueError(u"Group by `{}` not understood.".format(group_by))
+ raise ValueError("Group by `{}` not understood.".format(group_by))
def rec_sort(content, sort_by):
sorted_content = []
@@ -329,5 +328,5 @@ def generate_projects_typeahead():
if not permissions.index_view.can():
return '[]'
# return path stubs for all repositories
- stubs = [u'/'.join(p.split('/')[:-1]) for p in current_repo.dir()]
+ stubs = ['/'.join(p.split('/')[:-1]) for p in current_repo.dir()]
return json.dumps(list(set(stubs)))
diff --git a/knowledge_repo/app/routes/posts.py b/knowledge_repo/app/routes/posts.py
index 87c624399..0b6f43b55 100755
--- a/knowledge_repo/app/routes/posts.py
+++ b/knowledge_repo/app/routes/posts.py
@@ -1,6 +1,5 @@
import logging
import os
-from builtins import str
from flask import request, url_for, redirect, render_template, current_app, Blueprint, g, Response, abort
from .. import permissions
@@ -54,7 +53,7 @@ def render(path):
.filter(Post.path == knowledge_aliases[path])
.first())
if not post:
- logger.warning(u"unable to find post at {}".format(path))
+ logger.warning("unable to find post at {}".format(path))
return abort(404)
if post.contains_excluded_tag:
# It's possible that someone gets a direct link to a post that has an excluded tag
@@ -146,7 +145,7 @@ def _render_preview(path, tmpl):
post = current_repo.post(knowledge_aliases[path])
if not post:
- raise Exception(u"unable to find post at {}".format(path))
+ raise Exception("unable to find post at {}".format(path))
rendered = render_post(post, with_toc=True)
raw_post = render_post_raw(post) if (mode == 'raw') else None
@@ -159,7 +158,7 @@ def _render_preview(path, tmpl):
raw_post=raw_post,
comments=[],
username=None,
- post_author=u', '.join(post.headers['authors']),
+ post_author=', '.join(post.headers['authors']),
title=post.headers['title'],
page_views=0,
unique_views=0,
@@ -196,13 +195,13 @@ def download():
return Response(
post.to_string(format=resource_type),
mimetype="application/zip",
- headers={u"Content-disposition": "attachment; filename={}".format(filename)})
+ headers={"Content-disposition": "attachment; filename={}".format(filename)})
elif resource_type == 'pdf':
filename = os.path.basename(post.path)[:-3] + '.pdf'
return Response(
post.to_string(format=resource_type),
mimetype="application/pdf",
- headers={u"Content-disposition": "attachment; filename={}".format(filename)})
+ headers={"Content-disposition": "attachment; filename={}".format(filename)})
elif resource_type == 'source':
path = request.args.get('path', None)
assert path is not None, "Source path not provided."
@@ -210,6 +209,6 @@ def download():
return Response(
post._read_ref(path),
mimetype="application/octet-stream",
- headers={u"Content-disposition": "attachment; filename={}".format(os.path.basename(path))})
+ headers={"Content-disposition": "attachment; filename={}".format(os.path.basename(path))})
else:
- raise RuntimeError(u"Invalid resource_type: {}".format(resource_type))
+ raise RuntimeError("Invalid resource_type: {}".format(resource_type))
diff --git a/knowledge_repo/app/routes/tags.py b/knowledge_repo/app/routes/tags.py
index 1e42db265..0ba2821cd 100644
--- a/knowledge_repo/app/routes/tags.py
+++ b/knowledge_repo/app/routes/tags.py
@@ -15,7 +15,6 @@
from sqlalchemy import and_
import logging
import math
-from builtins import str
from .. import permissions
from ..proxies import db_session
diff --git a/knowledge_repo/app/utils/auth.py b/knowledge_repo/app/utils/auth.py
index f48821dee..221278759 100644
--- a/knowledge_repo/app/utils/auth.py
+++ b/knowledge_repo/app/utils/auth.py
@@ -1,6 +1,6 @@
import datetime
-from future.moves.urllib.parse import urlparse, urlencode, urljoin
+from urllib.parse import urlparse, urlencode, urljoin
from flask import request, url_for
from flask_login import AnonymousUserMixin, login_user
diff --git a/knowledge_repo/app/utils/emails.py b/knowledge_repo/app/utils/emails.py
index 25741b8f7..1130765f5 100644
--- a/knowledge_repo/app/utils/emails.py
+++ b/knowledge_repo/app/utils/emails.py
@@ -89,7 +89,7 @@ def send_subscription_email(post, tag):
return
default_recipients = ['knowledge_consumer@notreal.com']
- subject = u"New knowledge post: {}".format(post.title)
+ subject = "New knowledge post: {}".format(post.title)
post_authors = [p.format_name for p in post.authors]
post_tags = [t.name for t in post.tags]
msg = Message(subject=subject, recipients=default_recipients, bcc=recipients_bcc)
diff --git a/knowledge_repo/app/utils/knowledge_metadata.py b/knowledge_repo/app/utils/knowledge_metadata.py
index 99c3d6d16..c5c3802a6 100644
--- a/knowledge_repo/app/utils/knowledge_metadata.py
+++ b/knowledge_repo/app/utils/knowledge_metadata.py
@@ -1,7 +1,3 @@
-
-
-from __future__ import absolute_import
-from __future__ import unicode_literals
from markdown import Extension
from markdown.preprocessors import Preprocessor
diff --git a/knowledge_repo/app/utils/posts.py b/knowledge_repo/app/utils/posts.py
index 928c1d517..2855aa806 100644
--- a/knowledge_repo/app/utils/posts.py
+++ b/knowledge_repo/app/utils/posts.py
@@ -5,7 +5,6 @@
- get_all_post_stats
"""
import math
-from builtins import str
from flask import current_app
from sqlalchemy import func, distinct, or_
diff --git a/knowledge_repo/app/utils/render.py b/knowledge_repo/app/utils/render.py
index c7042fa40..e0b6f2cfd 100644
--- a/knowledge_repo/app/utils/render.py
+++ b/knowledge_repo/app/utils/render.py
@@ -34,7 +34,7 @@ def render_post_tldr(post):
def render_post_header(post):
- header_template = Template(u"""
+ header_template = Template("""