#!/usr/bin/env python
"""Python 3 Compatibility Module.
This module provides common type aliases and utility functions used
throughout the backtrader framework. Originally a Python 2/3 shim,
now simplified to Python 3 only.
Exports:
string_types: Tuple of string types — ``(str,)``.
integer_types: Tuple of integer types — ``(int,)``.
MAXINT / MININT / MAXFLOAT / MINFLOAT: Numeric limits.
range, zip, map, filter: Built-in references (kept for import compat).
queue: ``import queue``.
cmp, bytes, bstr: Helper functions.
iterkeys, itervalues, iteritems, keys, values, items: Dict helpers.
urlquote, urlopen, ProxyHandler, build_opener, install_opener: URL helpers.
winreg: Windows registry module (None on non-Windows).
with_metaclass: Metaclass helper.
"""
import queue # noqa: F401 — re-exported
import sys
import urllib.request as _urllib_request
from urllib.parse import quote as _urlquote
# Kept for backward compat — always False
PY2 = False
# --- Windows registry ---
try:
import winreg # noqa: F401
except ImportError:
winreg = None
# --- URL helpers (used by feeds like Quandl, Yahoo) ---
[docs]
def urlquote(s, *args, **kwargs):
"""Quote a string for use in a URL.
Args:
s: The string to quote.
*args: Additional positional arguments passed to urllib.parse.quote.
**kwargs: Additional keyword arguments passed to urllib.parse.quote.
Returns:
The quoted string safe for use in URLs.
"""
return _urlquote(s, *args, **kwargs)
# Default network timeout (seconds) applied when a caller does not supply one.
# Without it, urllib blocks indefinitely if the remote endpoint never responds,
# which can hang the data-loading thread. Callers may override via timeout=...
_DEFAULT_URLOPEN_TIMEOUT = 30.0
[docs]
def urlopen(*args, **kwargs):
"""Open a URL.
Args:
*args: Positional arguments passed to urllib.request.urlopen.
**kwargs: Keyword arguments passed to urllib.request.urlopen. If
``timeout`` is not provided, a default of
``_DEFAULT_URLOPEN_TIMEOUT`` seconds is applied to avoid
indefinite blocking on unresponsive endpoints.
Returns:
A file-like object representing the URL response.
"""
# Thin py2/3 compat shim; callers supply trusted URLs.
kwargs.setdefault("timeout", _DEFAULT_URLOPEN_TIMEOUT)
return _urllib_request.urlopen(*args, **kwargs) # nosec B310
[docs]
def ProxyHandler(*args, **kwargs): # noqa: N802 — keep legacy name
"""Create a proxy handler for opening URLs.
Args:
*args: Positional arguments passed to urllib.request.ProxyHandler.
**kwargs: Keyword arguments passed to urllib.request.ProxyHandler.
Returns:
A ProxyHandler instance for configuring URL proxies.
"""
return _urllib_request.ProxyHandler(*args, **kwargs)
[docs]
def build_opener(*args, **kwargs):
"""Build a URL opener with a chain of handlers.
Args:
*args: Positional arguments passed to urllib.request.build_opener.
**kwargs: Keyword arguments passed to urllib.request.build_opener.
Returns:
An OpenerDirector instance configured with the specified handlers.
"""
return _urllib_request.build_opener(*args, **kwargs)
[docs]
def install_opener(*args, **kwargs):
"""Install an opener as the default global opener.
Args:
*args: Positional arguments passed to urllib.request.install_opener.
**kwargs: Keyword arguments passed to urllib.request.install_opener.
Returns:
None.
"""
return _urllib_request.install_opener(*args, **kwargs)
# --- Numeric limits ---
MAXINT = sys.maxsize
MININT = -sys.maxsize - 1
MAXFLOAT = sys.float_info.max
MINFLOAT = sys.float_info.min
# --- Type aliases ---
string_types = (str,)
integer_types = (int,)
long = int
# --- Built-in re-exports (kept so ``from .py3 import range`` still works) ---
filter = filter
map = map
range = range
zip = zip
# --- Utility functions ---
[docs]
def cmp(a, b):
"""Compare two values.
Args:
a: First value to compare.
b: Second value to compare.
Returns:
int: 1 if a > b, 0 if a == b, -1 if a < b.
"""
return (a > b) - (a < b)
[docs]
def bytes(x):
"""Encode a string to bytes using UTF-8 encoding.
Args:
x: String to encode.
Returns:
Bytes representation of the input string.
"""
return x.encode("utf-8")
[docs]
def bstr(x):
"""Convert a value to a byte string.
Args:
x: Value to convert.
Returns:
String representation of the input value.
"""
return str(x)
# --- Dict iteration helpers ---
[docs]
def iterkeys(d):
"""Return an iterator over the dictionary's keys.
Args:
d: Dictionary to iterate over.
Returns:
An iterator over the dictionary's keys.
"""
return iter(d.keys())
[docs]
def itervalues(d):
"""Return an iterator over the dictionary's values.
Args:
d: Dictionary to iterate over.
Returns:
An iterator over the dictionary's values.
"""
return iter(d.values())
[docs]
def iteritems(d):
"""Return an iterator over the dictionary's items.
Args:
d: Dictionary to iterate over.
Returns:
An iterator over (key, value) tuples.
"""
return iter(d.items())
[docs]
def keys(d):
"""Return a list of the dictionary's keys.
Args:
d: Dictionary to extract keys from.
Returns:
A list containing the dictionary's keys.
"""
return list(d.keys())
[docs]
def values(d):
"""Return a list of the dictionary's values.
Args:
d: Dictionary to extract values from.
Returns:
A list containing the dictionary's values.
"""
return list(d.values())
[docs]
def items(d):
"""Return a list of the dictionary's items.
Args:
d: Dictionary to extract items from.
Returns:
A list of (key, value) tuples.
"""
return list(d.items())
# This is from Armin Ronacher from Flash simplified later by six