From 52df54dc0c3261d863f944648d2c883a7d685837 Mon Sep 17 00:00:00 2001 From: Massimo Di Pierro Date: Sun, 8 Oct 2023 15:04:00 -0700 Subject: [PATCH] black and lint --- py4web/__init__.py | 14 +-- py4web/core.py | 3 +- py4web/server_adapters.py | 97 +++++++++++------- py4web/utils/auth.py | 25 +++-- .../utils/auth_plugins/oauth2google_scoped.py | 98 +++++++++++-------- py4web/utils/auth_plugins/pam.py | 16 +-- py4web/utils/auth_plugins/x509_auth_plugin.py | 1 - py4web/utils/form.py | 19 +--- py4web/utils/grid.py | 25 +---- py4web/utils/mailer.py | 10 +- 10 files changed, 148 insertions(+), 160 deletions(-) diff --git a/py4web/__init__.py b/py4web/__init__.py index cd76a6fc2..0e83a806f 100644 --- a/py4web/__init__.py +++ b/py4web/__init__.py @@ -23,15 +23,5 @@ def _maybe_gevent(): from .core import Translator # from pluralize from .core import action # main py4web decorator from .core import render # yatl -from .core import ( - DAL, - Cache, - Condition, - Flash, - Session, - abort, - check_compatible, - redirect, - request, - response, -) +from .core import (DAL, Cache, Condition, Flash, Session, abort, + check_compatible, redirect, request, response) diff --git a/py4web/core.py b/py4web/core.py index 96526298c..599fdac83 100644 --- a/py4web/core.py +++ b/py4web/core.py @@ -35,7 +35,7 @@ import uuid import zipfile from collections import OrderedDict -from contextlib import redirect_stdout, redirect_stderr +from contextlib import redirect_stderr, redirect_stdout import portalocker from watchgod import awatch @@ -49,7 +49,6 @@ gunicorn = None import click - # Third party modules import ombott as bottle import pluralize diff --git a/py4web/server_adapters.py b/py4web/server_adapters.py index 2c9370483..6a1a20253 100644 --- a/py4web/server_adapters.py +++ b/py4web/server_adapters.py @@ -1,7 +1,7 @@ import logging import os -import sys import ssl +import sys from ombott.server_adapters import ServerAdapter @@ -12,8 +12,10 @@ __all__ = [ "gevent", - "geventWebSocketServer", "geventWs", # short_name - "wsgirefThreadingServer", "wsgiTh", # short_name + "geventWebSocketServer", + "geventWs", # short_name + "wsgirefThreadingServer", + "wsgiTh", # short_name "rocketServer", ] + wsservers_list @@ -22,10 +24,10 @@ # export PY4WEB_LOGS=/tmp # export PY4WEB_LOGS= def get_log_file(): log_dir = os.environ.get("PY4WEB_LOGS", None) - log_file = os.path.join (log_dir, 'server-py4web.log') if log_dir else None + log_file = os.path.join(log_dir, "server-py4web.log") if log_dir else None if log_file: print(f"log_file: {log_file}") - return log_file + return log_file def check_level(level): @@ -55,29 +57,25 @@ def check_level(level): ) -def logging_conf(level=logging.WARN, logger_name=__name__, test_log = False): +def logging_conf(level=logging.WARN, logger_name=__name__, test_log=False): log_file = get_log_file() log_to = dict() if log_file: if sys.version_info >= (3, 9): - log_to["filename" ] = log_file - log_to["filemode" ] = "w" + log_to["filename"] = log_file + log_to["filemode"] = "w" log_to["encoding"] = "utf-8" - else: - h = logging.FileHandler( - log_file, - mode = "w", - encoding = "utf-8" - ) - log_to.update( {"handlers": [h]} ) + else: + h = logging.FileHandler(log_file, mode="w", encoding="utf-8") + log_to.update({"handlers": [h]}) short_msg = "%(message)s > %(threadName)s > %(asctime)s.%(msecs)03d" - #long_msg = short_msg + " > %(funcName)s > %(filename)s:%(lineno)d > %(levelname)s" + # long_msg = short_msg + " > %(funcName)s > %(filename)s:%(lineno)d > %(levelname)s" - time_msg = '%H:%M:%S' - #date_time_msg = '%Y-%m-%d %H:%M:%S' + time_msg = "%H:%M:%S" + # date_time_msg = '%Y-%m-%d %H:%M:%S' try: logging.basicConfig( @@ -86,10 +84,13 @@ def logging_conf(level=logging.WARN, logger_name=__name__, test_log = False): level=check_level(level), **log_to, ) - except ( OSError, LookupError, KeyError, ValueError ) as ex: + except (OSError, LookupError, KeyError, ValueError) as ex: print(f"{ex}, {__file__}") - print(f'cannot open {log_file}') - logging.basicConfig( format="%(message)s", level=check_level(level),) + print(f"cannot open {log_file}") + logging.basicConfig( + format="%(message)s", + level=check_level(level), + ) if logger_name is None: return None @@ -98,8 +99,14 @@ def logging_conf(level=logging.WARN, logger_name=__name__, test_log = False): log.propagate = True if test_log: - for func in (log.debug, log.info, log.warn, log.error, log.critical, ) : - func('func: ' + func.__name__ ) + for func in ( + log.debug, + log.info, + log.warn, + log.error, + log.critical, + ): + func("func: " + func.__name__) return log @@ -110,12 +117,15 @@ def get_workers(opts, default=10): except KeyError: return default + # ---------------------- servers ----------------------------------------------- + def gevent(): # gevent version 23.9.1 import threading + from gevent import local, pywsgi # pip install gevent if not isinstance(threading.local(), local.local): @@ -129,13 +139,14 @@ def gevent(): class GeventServer(ServerAdapter): def run(self, app_handler): - logger = "default" + logger = "default" if not self.quiet: logger = logging_conf( - self.options["logging_level"], "gevent", + self.options["logging_level"], + "gevent", ) - #logger.addHandler(logging.StreamHandler()) + # logger.addHandler(logging.StreamHandler()) certfile = self.options.get("certfile", None) @@ -156,7 +167,7 @@ def run(self, app_handler): app_handler, log=logger, error_log=logger, - **ssl_args + **ssl_args, ) server.serve_forever() @@ -166,7 +177,6 @@ def run(self, app_handler): def geventWebSocketServer(): from gevent import pywsgi - # from geventwebsocket.handler import WebSocketHandler # pip install gevent-websocket from gevent_ws import WebSocketHandler # pip install gevent gevent-ws @@ -183,13 +193,14 @@ def geventWebSocketServer(): class GeventWebSocketServer(ServerAdapter): def run(self, app_handler): - logger = "default" + logger = "default" if not self.quiet: logger = logging_conf( - self.options["logging_level"], "gevent-ws", + self.options["logging_level"], + "gevent-ws", ) - + certfile = self.options.get("certfile", None) ssl_args = ( @@ -207,13 +218,16 @@ def run(self, app_handler): handler_class=WebSocketHandler, log=logger, error_log=logger, - **ssl_args + **ssl_args, ) server.serve_forever() return GeventWebSocketServer -geventWs=geventWebSocketServer + + +geventWs = geventWebSocketServer + def wsgirefThreadingServer(): # https://www.electricmonk.nl/log/2016/02/15/multithreaded-dev-web-server-for-the-python-bottle-web-framework/ @@ -221,7 +235,8 @@ def wsgirefThreadingServer(): import socket from concurrent.futures import ThreadPoolExecutor # pip install futures from socketserver import ThreadingMixIn - from wsgiref.simple_server import WSGIRequestHandler, WSGIServer, make_server + from wsgiref.simple_server import (WSGIRequestHandler, WSGIServer, + make_server) class WSGIRefThreadingServer(ServerAdapter): def run(self, app_handler): @@ -230,7 +245,8 @@ def run(self, app_handler): if not self.quiet: self.log = logging_conf( - self.options["logging_level"], "wsgiref", + self.options["logging_level"], + "wsgiref", ) self_run = self # used in internal classes to access options and logger @@ -306,7 +322,7 @@ def log_message(self, format, *args): ) self_run.log.info(msg) - #handler_cls = self.options.get("handler_class", LogHandler) + # handler_cls = self.options.get("handler_class", LogHandler) server_cls = Server if ":" in self.host: # Fix wsgiref for IPv6 addresses. @@ -317,11 +333,16 @@ class ServerClass(Server): server_cls = ServerClass - srv = make_server(self.host, self.port, app_handler, server_cls, LogHandler ) # handler_cls) + srv = make_server( + self.host, self.port, app_handler, server_cls, LogHandler + ) # handler_cls) srv.serve_forever() return WSGIRefThreadingServer -wsgiTh=wsgirefThreadingServer + + +wsgiTh = wsgirefThreadingServer + def rocketServer(): try: diff --git a/py4web/utils/auth.py b/py4web/utils/auth.py index e08d6cc29..cb8984750 100644 --- a/py4web/utils/auth.py +++ b/py4web/utils/auth.py @@ -9,13 +9,14 @@ import urllib import uuid +from pydal.validators import (CRYPT, IS_EMAIL, IS_EQUAL_TO, IS_MATCH, + IS_NOT_EMPTY, IS_NOT_IN_DB, IS_STRONG) +from yatl.helpers import DIV, A + from py4web import HTTP, URL, Field, action, redirect, request, response from py4web.core import REGEX_APPJSON, Fixture, Flash, Template, Translator from py4web.utils.form import Form, FormStyleDefault from py4web.utils.param import Param -from pydal.validators import (CRYPT, IS_EMAIL, IS_EQUAL_TO, IS_MATCH, - IS_NOT_EMPTY, IS_NOT_IN_DB, IS_STRONG) -from yatl.helpers import DIV, A """ [X] Enable and disable plugins @@ -470,11 +471,17 @@ def signature(self): @property def user(self): """Use as @action.uses(auth.user)""" - return self.param.auth_enforcer if self.param.auth_enforcer else AuthEnforcer(self) + return ( + self.param.auth_enforcer if self.param.auth_enforcer else AuthEnforcer(self) + ) def condition(self, condition): """Use as @action.uses(auth.condition(lambda user: True))""" - return self.param.auth_enforcer if self.param.auth_enforcer else AuthEnforcer(self, condition) + return ( + self.param.auth_enforcer + if self.param.auth_enforcer + else AuthEnforcer(self, condition) + ) # utilities def get_user(self, safe=True): @@ -976,7 +983,7 @@ def enable(self, route="auth", uses=(), env=None, spa=False, allow_api_routes=Tr for api_name in AuthAPI.public_api if self.allows(api_name) ] - + # Exposed Private APIs exposed_api_routes.extend( [ @@ -989,15 +996,15 @@ def enable(self, route="auth", uses=(), env=None, spa=False, allow_api_routes=Tr if self.allows(api_name) ] ) - + for item in exposed_api_routes: api_factory = getattr(AuthAPI, item["api_name"]) - + @action(item["api_route"], method=methods) @action.uses(item["uses"], *uses) def _(auth=auth, api_factory=api_factory): return api_factory(auth) - + # This exposes all plugins as /{app_name}/{route}/plugins/{path} for name in self.plugins: diff --git a/py4web/utils/auth_plugins/oauth2google_scoped.py b/py4web/utils/auth_plugins/oauth2google_scoped.py index ebb1aba29..a61cf015e 100644 --- a/py4web/utils/auth_plugins/oauth2google_scoped.py +++ b/py4web/utils/auth_plugins/oauth2google_scoped.py @@ -11,14 +11,14 @@ import time import uuid -import google_auth_oauthlib.flow import google.oauth2.credentials -from googleapiclient.discovery import build +import google_auth_oauthlib.flow from google.auth.exceptions import RefreshError - -from py4web import request, redirect, URL, HTTP +from googleapiclient.discovery import build from pydal import Field -from py4web.utils.auth import AuthEnforcer, REGEX_APPJSON + +from py4web import HTTP, URL, redirect, request +from py4web.utils.auth import REGEX_APPJSON, AuthEnforcer class AuthEnforcerGoogleScoped(AuthEnforcer): @@ -31,21 +31,21 @@ class AuthEnforcerGoogleScoped(AuthEnforcer): def __init__(self, auth, condition=None, error_page=None): super().__init__(auth, condition=condition) self.error_page = error_page - assert error_page is not None, "You need to specify an error page; can't use login." + assert ( + error_page is not None + ), "You need to specify an error page; can't use login." def on_error(self, context): if isinstance(context.get("exception"), RefreshError): del context["exception"] self.auth.session.clear() - if re.search(REGEX_APPJSON, - request.headers.get("accept", "")) and ( - request.headers.get("json-redirects", "") != "on" + if re.search(REGEX_APPJSON, request.headers.get("accept", "")) and ( + request.headers.get("json-redirects", "") != "on" ): raise HTTP(403) redirect_next = request.fullpath if request.query_string: - redirect_next = redirect_next + "?{}".format( - request.query_string) + redirect_next = redirect_next + "?{}".format(request.query_string) self.auth.flash.set("Invalid credentials") redirect( URL( @@ -80,8 +80,14 @@ class OAuth2GoogleScoped(object): label = "Google Scoped" callback_url = "auth/plugin/oauth2googlescoped/callback" - def __init__(self, secrets_file=None, scopes=None, db=None, - define_tables=True, delete_credentials_on_logout=True): + def __init__( + self, + secrets_file=None, + scopes=None, + db=None, + define_tables=True, + delete_credentials_on_logout=True, + ): """ Creates an authorization object for Google with Oauth2 and paramters. @@ -111,23 +117,28 @@ def __init__(self, secrets_file=None, scopes=None, db=None, self._secrets_file = secrets_file # Scopes for which we ask authorization scopes = scopes or [] - self._scopes = ["openid", - "https://www.googleapis.com/auth/userinfo.email", - "https://www.googleapis.com/auth/userinfo.profile"] + scopes + self._scopes = [ + "openid", + "https://www.googleapis.com/auth/userinfo.email", + "https://www.googleapis.com/auth/userinfo.profile", + ] + scopes self._db = db if db and define_tables: self._define_tables() self._delete_credentials_on_logout = delete_credentials_on_logout - def _define_tables(self): - self._db.define_table('auth_credentials', [ - Field('email'), - Field('name'), # First and last names, all together. - Field('profile_pic', 'text'), # URL of profile pic. - Field('credentials', 'text') # Credentials for access, stored in Json for generality. - ]) - + self._db.define_table( + "auth_credentials", + [ + Field("email"), + Field("name"), # First and last names, all together. + Field("profile_pic", "text"), # URL of profile pic. + Field( + "credentials", "text" + ), # Credentials for access, stored in Json for generality. + ], + ) def handle_request(self, auth, path, get_vars, post_vars): """Handles the login request or the callback.""" @@ -139,7 +150,7 @@ def handle_request(self, auth, path, get_vars, post_vars): elif path == "logout": # Deletes the credentials, and clears the session. if self._delete_credentials_on_logout: - email = auth.current_user.get('email') if auth.current_user else None + email = auth.current_user.get("email") if auth.current_user else None if email is not None: self._db(self._db.auth_credentials.email == email).delete() self._db.commit() @@ -149,11 +160,11 @@ def handle_request(self, auth, path, get_vars, post_vars): else: raise HTTP(404) - def _get_login_url(self, auth, state=None): # Creates a flow. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( - self._secrets_file, scopes=self._scopes) + self._secrets_file, scopes=self._scopes + ) # Sets its callback URL. This is the local URL that will be called # once the user gives permission. """Returns the URL to which the user is directed.""" @@ -161,9 +172,10 @@ def _get_login_url(self, auth, state=None): authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. - access_type='offline', + access_type="offline", # Enable incremental authorization. Recommended as a best practice. - include_granted_scopes='true') + include_granted_scopes="true", + ) auth.session["oauth2googlescoped:state"] = state return authorization_url @@ -171,10 +183,11 @@ def _handle_callback(self, auth, get_vars): # Builds a flow again, this time with the state in it. state = auth.session["oauth2googlescoped:state"] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( - self._secrets_file, scopes=self._scopes, state=state) + self._secrets_file, scopes=self._scopes, state=state + ) flow.redirect_uri = URL(self.callback_url, scheme=True) # Use the authorization server's response to fetch the OAuth 2.0 tokens. - if state and get_vars.get('state', None) != state: + if state and get_vars.get("state", None) != state: raise HTTP(401, "Invalid state") error = get_vars.get("error") if error: @@ -184,9 +197,9 @@ def _handle_callback(self, auth, get_vars): code = error.get("code", 401) msg = error.get("message", "Unknown error") raise HTTP(code, msg) - if not 'code' in get_vars: + if not "code" in get_vars: raise HTTP(401, "Missing code parameter in response.") - code = get_vars.get('code') + code = get_vars.get("code") flow.fetch_token(code=code) # We got the credentials! credentials = flow.credentials @@ -194,7 +207,7 @@ def _handle_callback(self, auth, get_vars): # see https://github.com/googleapis/google-api-python-client/pull/1088/files # and https://github.com/googleapis/google-api-python-client/issues/1071 # and ?? - user_info_service = build('oauth2', 'v2', credentials=credentials) + user_info_service = build("oauth2", "v2", credentials=credentials) user_info = user_info_service.userinfo().get().execute() email = user_info.get("email") if email is None: @@ -202,7 +215,7 @@ def _handle_callback(self, auth, get_vars): # Finally, we store the credentials, so we can re-use them in order # to use the scopes we requested. if self._db: - credentials_json=json.dumps(self.credentials_to_dict(credentials)) + credentials_json = json.dumps(self.credentials_to_dict(credentials)) self._db.auth_credentials.update_or_insert( self._db.auth_credentials.email == email, email=email, @@ -238,15 +251,16 @@ def _handle_callback(self, auth, get_vars): next = URL("index") redirect(next) - @staticmethod def credentials_to_dict(credentials): - return {'token': credentials.token, - 'refresh_token': credentials.refresh_token, - 'token_uri': credentials.token_uri, - 'client_id': credentials.client_id, - 'client_secret': credentials.client_secret, - 'scopes': credentials._scopes} + return { + "token": credentials.token, + "refresh_token": credentials.refresh_token, + "token_uri": credentials.token_uri, + "client_id": credentials.client_id, + "client_secret": credentials.client_secret, + "scopes": credentials._scopes, + } @staticmethod def credentials_from_dict(credentials_dict): diff --git a/py4web/utils/auth_plugins/pam.py b/py4web/utils/auth_plugins/pam.py index da77d710a..5af9c7112 100644 --- a/py4web/utils/auth_plugins/pam.py +++ b/py4web/utils/auth_plugins/pam.py @@ -14,20 +14,8 @@ __all__ = ["authenticate"] import sys -from ctypes import ( - CDLL, - CFUNCTYPE, - POINTER, - Structure, - byref, - c_char, - c_char_p, - c_int, - c_uint, - c_void_p, - cast, - sizeof, -) +from ctypes import (CDLL, CFUNCTYPE, POINTER, Structure, byref, c_char, + c_char_p, c_int, c_uint, c_void_p, cast, sizeof) from ctypes.util import find_library libpam = CDLL(find_library("pam")) diff --git a/py4web/utils/auth_plugins/x509_auth_plugin.py b/py4web/utils/auth_plugins/x509_auth_plugin.py index 87e83bee6..7194e4e61 100644 --- a/py4web/utils/auth_plugins/x509_auth_plugin.py +++ b/py4web/utils/auth_plugins/x509_auth_plugin.py @@ -15,7 +15,6 @@ from gluon.globals import current from gluon.http import HTTP, redirect from gluon.storage import Storage - # requires M2Crypto from M2Crypto import X509 diff --git a/py4web/utils/form.py b/py4web/utils/form.py index c58322ba6..53f27a312 100644 --- a/py4web/utils/form.py +++ b/py4web/utils/form.py @@ -8,23 +8,8 @@ from pydal._compat import to_native from pydal.objects import FieldVirtual from pydal.validators import Validator -from yatl.helpers import ( - CAT, - DIV, - FORM, - INPUT, - LABEL, - OPTION, - SELECT, - SPAN, - TABLE, - TD, - TEXTAREA, - TR, - XML, - A, - P, -) +from yatl.helpers import (CAT, DIV, FORM, INPUT, LABEL, OPTION, SELECT, SPAN, + TABLE, TD, TEXTAREA, TR, XML, A, P) from py4web import HTTP, request, response from py4web.utils.param import Param diff --git a/py4web/utils/grid.py b/py4web/utils/grid.py index f5fdbdd8c..18a7893ef 100644 --- a/py4web/utils/grid.py +++ b/py4web/utils/grid.py @@ -9,25 +9,8 @@ import ombott from pydal.objects import Expression, Field, FieldVirtual -from yatl.helpers import ( - CAT, - DIV, - FORM, - INPUT, - OPTION, - SELECT, - SPAN, - TABLE, - TAG, - TBODY, - TD, - TH, - THEAD, - TR, - XML, - A, - I, -) +from yatl.helpers import (CAT, DIV, FORM, INPUT, OPTION, SELECT, SPAN, TABLE, + TAG, TBODY, TD, TH, THEAD, TR, XML, A, I) from py4web import HTTP, URL, redirect, request from py4web.utils.form import Form, FormStyleDefault, join_classes @@ -1500,7 +1483,9 @@ def _make_table(self): else self.total_number_of_rows, self.total_number_of_rows, ) - ) if self.number_of_pages > 0 else row_count.append(self.T("No rows to display")) + ) if self.number_of_pages > 0 else row_count.append( + self.T("No rows to display") + ) footer.append(row_count) # build the pager diff --git a/py4web/utils/mailer.py b/py4web/utils/mailer.py index 6825ff571..14c175526 100644 --- a/py4web/utils/mailer.py +++ b/py4web/utils/mailer.py @@ -530,7 +530,7 @@ def encoded_or_raw(text): "signed", boundary=None, _subparts=None, - **dict(micalg="pgp-sha1", protocol="application/pgp-signature") + **dict(micalg="pgp-sha1", protocol="application/pgp-signature"), ) # Insert the origin payload payload.attach(payload_in) @@ -574,7 +574,7 @@ def encoded_or_raw(text): "encrypted", boundary=None, _subparts=None, - **dict(protocol="application/pgp-encrypted") + **dict(protocol="application/pgp-encrypted"), ) p = MIMEBase("application", "pgp-encrypted") p.set_payload("Version: 1\r\n") @@ -788,7 +788,7 @@ def encoded_or_raw(text): body=to_unicode(text or "", encoding), html=html, attachments=attachments, - **xcc + **xcc, ) elif html and (not raw): result = google_mail.send_mail( @@ -797,7 +797,7 @@ def encoded_or_raw(text): subject=to_unicode(subject, encoding), body=to_unicode(text or "", encoding), html=html, - **xcc + **xcc, ) else: result = google_mail.send_mail( @@ -805,7 +805,7 @@ def encoded_or_raw(text): to=origTo, subject=to_unicode(subject, encoding), body=to_unicode(text or "", encoding), - **xcc + **xcc, ) elif self.settings.server == "aws" and boto3: client = boto3.client("ses")