Misc optimisations

- Avoid using dir()
- Remove custom url quote methods that are no longer faster than urllib.parse methods in newer Python versions
- Reduce polling intervals when checking if Kodi is busy
- Use custom requests.Session class to avoid creation of unused default https adapter and ssl context
This commit is contained in:
MoojMidge 2025-11-15 14:28:31 +11:00
parent ef99864c19
commit 36f1cc6048
7 changed files with 99 additions and 98 deletions

View file

@ -375,7 +375,7 @@ class AbstractProvider(object):
self.log.warning('Multiple busy dialogs active'
' - Rerouting workaround')
return UriItem('command://{0}'.format(action))
context.sleep(1)
context.sleep(0.1)
else:
context.execute(
action,

View file

@ -15,8 +15,6 @@ __all__ = (
'available_cpu_count',
'byte_string_type',
'datetime_infolabel',
'default_quote',
'default_quote_plus',
'entity_escape',
'generate_hash',
'parse_qs',
@ -120,70 +118,6 @@ try:
for ordinal in range(128, 256)
})
def default_quote(string,
safe='',
encoding=None,
errors=None,
_encoding='utf-8',
_errors='strict',
_reserved=reserved,
_non_ascii=non_ascii,
_encode=str.encode,
_is_ascii=str.isascii,
_replace=str.replace,
_old='\\x',
_new='%',
_slice=slice(2, -1),
_str=str,
_translate=str.translate):
_string = _translate(string, _reserved)
if _is_ascii(_string):
return _string
_string = _str(_encode(_string, _encoding, _errors))[_slice]
if _string == string:
if _is_ascii(_string):
return _string
return _translate(_string, _non_ascii)
if _is_ascii(_string):
return _replace(_string, _old, _new)
return _translate(_replace(_string, _old, _new), _non_ascii)
def default_quote_plus(string,
safe='',
encoding=None,
errors=None,
_encoding='utf-8',
_errors='strict',
_reserved=reserved_plus,
_non_ascii=non_ascii,
_encode=str.encode,
_is_ascii=str.isascii,
_replace=str.replace,
_old='\\x',
_new='%',
_slice=slice(2, -1),
_str=str,
_translate=str.translate):
if (not safe and encoding is None and errors is None
and isinstance(string, str)):
_string = _translate(string, _reserved)
if _is_ascii(_string):
return _string
_string = _str(_encode(_string, _encoding, _errors))[_slice]
if _string == string:
if _is_ascii(_string):
return _string
return _translate(_string, _non_ascii)
if _is_ascii(_string):
return _replace(_string, _old, _new)
return _translate(_replace(_string, _old, _new), _non_ascii)
return quote_plus(string, safe, encoding, errors)
urlencode.__defaults__ = (False, '', None, None, default_quote_plus)
# Compatibility shims for Kodi v18 and Python v2.7
except ImportError:
import cPickle as pickle
@ -220,16 +154,10 @@ except ImportError:
return _quote(to_str(data), *args, **kwargs)
default_quote = quote
def quote_plus(data, *args, **kwargs):
return _quote_plus(to_str(data), *args, **kwargs)
default_quote_plus = quote_plus
def unquote(data):
return _unquote(to_str(data))

View file

@ -14,8 +14,8 @@ import os
from .. import logging
from ..compatibility import (
default_quote,
parse_qsl,
quote,
string_type,
to_str,
unquote,
@ -387,7 +387,7 @@ class AbstractContext(object):
params = urlencode([
(
('%' + param,
','.join([default_quote(item) for item in value]))
','.join([quote(item) for item in value]))
if len(value) > 1 else
(param, value[0])
)
@ -482,7 +482,7 @@ class AbstractContext(object):
return ('/', parts) if include_parts else '/'
if kwargs.get('is_uri'):
path = default_quote(path)
path = quote(path)
return (path, parts) if include_parts else path
def get_path(self):

View file

@ -348,12 +348,15 @@ class _Encoder(json.JSONEncoder):
def encode(self, obj, nested=False):
if isinstance(obj, (date, datetime)):
class_name = obj.__class__.__name__
if 'fromisoformat' in dir(obj):
obj = {
'__class__': class_name,
'__isoformat__': obj.isoformat(),
}
else:
try:
if obj.fromisoformat:
obj = {
'__class__': class_name,
'__isoformat__': obj.isoformat(),
}
else:
raise AttributeError
except AttributeError:
if class_name == 'datetime':
if obj.tzinfo:
format_string = '%Y-%m-%dT%H:%M:%S%z'

View file

@ -11,11 +11,19 @@ from __future__ import absolute_import, division, unicode_literals
import socket
from atexit import register as atexit_register
from collections import OrderedDict
from requests import Request, Session
from requests.adapters import HTTPAdapter, Retry
from requests.exceptions import InvalidJSONError, RequestException, URLRequired
from requests.utils import DEFAULT_CA_BUNDLE_PATH, extract_zipped_paths
from requests.hooks import default_hooks
from requests.models import DEFAULT_REDIRECT_LIMIT, Request
from requests.sessions import Session
from requests.utils import (
DEFAULT_CA_BUNDLE_PATH,
cookiejar_from_dict,
default_headers,
extract_zipped_paths,
)
from urllib3.util.ssl_ import create_urllib3_context
from .. import logging
@ -64,21 +72,83 @@ class SSLHTTPAdapter(HTTPAdapter):
return super(SSLHTTPAdapter, self).cert_verify(conn, url, verify, cert)
class CustomSession(Session):
def __init__(self):
#: A case-insensitive dictionary of headers to be sent on each
#: :class:`Request <Request>` sent from this
#: :class:`Session <Session>`.
self.headers = default_headers()
#: Default Authentication tuple or object to attach to
#: :class:`Request <Request>`.
self.auth = None
#: Dictionary mapping protocol or protocol and host to the URL of the proxy
#: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to
#: be used on each :class:`Request <Request>`.
self.proxies = {}
#: Event-handling hooks.
self.hooks = default_hooks()
#: Dictionary of querystring data to attach to each
#: :class:`Request <Request>`. The dictionary values may be lists for
#: representing multivalued query parameters.
self.params = {}
#: Stream response content default.
self.stream = False
#: SSL Verification default.
#: Defaults to `True`, requiring requests to verify the TLS certificate at the
#: remote end.
#: If verify is set to `False`, requests will accept any TLS certificate
#: presented by the server, and will ignore hostname mismatches and/or
#: expired certificates, which will make your application vulnerable to
#: man-in-the-middle (MitM) attacks.
#: Only set this to `False` for testing.
self.verify = True
#: SSL client certificate default, if String, path to ssl client
#: cert file (.pem). If Tuple, ('cert', 'key') pair.
self.cert = None
#: Maximum number of redirects allowed. If the request exceeds this
#: limit, a :class:`TooManyRedirects` exception is raised.
#: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is
#: 30.
self.max_redirects = DEFAULT_REDIRECT_LIMIT
#: Trust environment settings for proxy configuration, default
#: authentication and similar.
#: CustomSession.trust_env is False
self.trust_env = False
#: A CookieJar containing all currently outstanding cookies set on this
#: session. By default it is a
#: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but
#: may be any other ``cookielib.CookieJar`` compatible object.
self.cookies = cookiejar_from_dict({})
# Default connection adapters.
self.adapters = OrderedDict()
self.mount('https://', SSLHTTPAdapter(
pool_maxsize=20,
pool_block=True,
max_retries=Retry(
total=3,
backoff_factor=0.1,
status_forcelist={500, 502, 503, 504},
allowed_methods=None,
)
))
self.mount('http://', HTTPAdapter())
class BaseRequestsClass(object):
log = logging.getLogger(__name__)
_session = Session()
_session.trust_env = False
_session.mount('https://', SSLHTTPAdapter(
pool_maxsize=10,
pool_block=True,
max_retries=Retry(
total=3,
backoff_factor=0.1,
status_forcelist={500, 502, 503, 504},
allowed_methods=None,
)
))
_session = CustomSession()
atexit_register(_session.close)
_context = None

View file

@ -439,7 +439,7 @@ class XbmcPlugin(AbstractPlugin):
@staticmethod
def post_run(context, ui, *actions, **kwargs):
timeout = kwargs.get('timeout', 30)
interval = kwargs.get('interval', 0.1)
interval = kwargs.get('interval', 0.01)
for action in actions:
while not ui.get_container(container_type=None, check_ready=True):
timeout -= interval

View file

@ -481,7 +481,7 @@ def process_items_for_playlist(context,
command = playlist_player.play_playlist_item(position,
defer=True)
return UriItem(command)
context.sleep(1)
context.sleep(0.1)
else:
playlist_player.play_playlist_item(position)
return items[position - 1]