fixed subscription table

This commit is contained in:
2025-02-02 00:02:31 -05:00
parent a1ab31acfe
commit ef5f57e678
5389 changed files with 686710 additions and 28 deletions

View File

@@ -0,0 +1,91 @@
from __future__ import annotations
import os
import pathlib
from typing import Sequence
# vestigal things from IPython_genutils.
def cast_unicode(s: str | bytes, encoding: str = "utf-8") -> str:
if isinstance(s, bytes):
return s.decode(encoding, "replace")
return s
def filefind(filename: str, path_dirs: Sequence[str] | None = None) -> str:
"""Find a file by looking through a sequence of paths.
This iterates through a sequence of paths looking for a file and returns
the full, absolute path of the first occurrence of the file. If no set of
path dirs is given, the filename is tested as is, after running through
:func:`expandvars` and :func:`expanduser`. Thus a simple call::
filefind('myfile.txt')
will find the file in the current working dir, but::
filefind('~/myfile.txt')
Will find the file in the users home directory. This function does not
automatically try any paths, such as the cwd or the user's home directory.
Parameters
----------
filename : str
The filename to look for.
path_dirs : str, None or sequence of str
The sequence of paths to look for the file in. If None, the filename
need to be absolute or be in the cwd. If a string, the string is
put into a sequence and the searched. If a sequence, walk through
each element and join with ``filename``, calling :func:`expandvars`
and :func:`expanduser` before testing for existence.
Returns
-------
Raises :exc:`IOError` or returns absolute path to file.
"""
# If paths are quoted, abspath gets confused, strip them...
filename = filename.strip('"').strip("'")
# If the input is an absolute path, just check it exists
if os.path.isabs(filename) and os.path.isfile(filename):
return filename
if path_dirs is None:
path_dirs = ("",)
elif isinstance(path_dirs, str):
path_dirs = (path_dirs,)
elif isinstance(path_dirs, pathlib.Path):
path_dirs = (str(path_dirs),)
for path in path_dirs:
if path == ".":
path = os.getcwd()
testname = expand_path(os.path.join(path, filename))
if os.path.isfile(testname):
return os.path.abspath(testname)
raise OSError(f"File {filename!r} does not exist in any of the search paths: {path_dirs!r}")
def expand_path(s: str) -> str:
"""Expand $VARS and ~names in a string, like a shell
:Examples:
In [2]: os.environ['FOO']='test'
In [3]: expand_path('variable FOO is $FOO')
Out[3]: 'variable FOO is test'
"""
# This is a pretty subtle hack. When expand user is given a UNC path
# on Windows (\\server\share$\%username%), os.path.expandvars, removes
# the $ to get (\\server\share\%username%). I think it considered $
# alone an empty var. But, we need the $ to remains there (it indicates
# a hidden share).
if os.name == "nt":
s = s.replace("$\\", "IPYTHON_TEMP")
s = os.path.expandvars(os.path.expanduser(s))
if os.name == "nt":
s = s.replace("IPYTHON_TEMP", "$\\")
return s

View File

@@ -0,0 +1,29 @@
"""Yet another implementation of bunch
attribute-access of items on a dict.
"""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations
from typing import Any
class Bunch(dict): # type:ignore[type-arg]
"""A dict with attribute-access"""
def __getattr__(self, key: str) -> Any:
try:
return self.__getitem__(key)
except KeyError as e:
raise AttributeError(key) from e
def __setattr__(self, key: str, value: Any) -> None:
self.__setitem__(key, value)
def __dir__(self) -> list[str]:
# py2-compat: can't use super because dict doesn't have __dir__
names = dir({})
names.extend(self.keys())
return names

View File

@@ -0,0 +1,86 @@
"""Useful decorators for Traitlets users."""
from __future__ import annotations
import copy
from inspect import Parameter, Signature, signature
from typing import Any, Type, TypeVar
from ..traitlets import HasTraits, Undefined
def _get_default(value: Any) -> Any:
"""Get default argument value, given the trait default value."""
return Parameter.empty if value == Undefined else value
T = TypeVar("T", bound=HasTraits)
def signature_has_traits(cls: Type[T]) -> Type[T]:
"""Return a decorated class with a constructor signature that contain Trait names as kwargs."""
traits = [
(name, _get_default(value.default_value))
for name, value in cls.class_traits().items()
if not name.startswith("_")
]
# Taking the __init__ signature, as the cls signature is not initialized yet
old_signature = signature(cls.__init__)
old_parameter_names = list(old_signature.parameters)
old_positional_parameters = []
old_var_positional_parameter = None # This won't be None if the old signature contains *args
old_keyword_only_parameters = []
old_var_keyword_parameter = None # This won't be None if the old signature contains **kwargs
for parameter_name in old_signature.parameters:
# Copy the parameter
parameter = copy.copy(old_signature.parameters[parameter_name])
if (
parameter.kind is Parameter.POSITIONAL_ONLY
or parameter.kind is Parameter.POSITIONAL_OR_KEYWORD
):
old_positional_parameters.append(parameter)
elif parameter.kind is Parameter.VAR_POSITIONAL:
old_var_positional_parameter = parameter
elif parameter.kind is Parameter.KEYWORD_ONLY:
old_keyword_only_parameters.append(parameter)
elif parameter.kind is Parameter.VAR_KEYWORD:
old_var_keyword_parameter = parameter
# Unfortunately, if the old signature does not contain **kwargs, we can't do anything,
# because it can't accept traits as keyword arguments
if old_var_keyword_parameter is None:
raise RuntimeError(
f"The {cls} constructor does not take **kwargs, which means that the signature can not be expanded with trait names"
)
new_parameters = []
# Append the old positional parameters (except `self` which is the first parameter)
new_parameters += old_positional_parameters[1:]
# Append *args if the old signature had it
if old_var_positional_parameter is not None:
new_parameters.append(old_var_positional_parameter)
# Append the old keyword only parameters
new_parameters += old_keyword_only_parameters
# Append trait names as keyword only parameters in the signature
new_parameters += [
Parameter(name, kind=Parameter.KEYWORD_ONLY, default=default)
for name, default in traits
if name not in old_parameter_names
]
# Append **kwargs
new_parameters.append(old_var_keyword_parameter)
cls.__signature__ = Signature(new_parameters) # type:ignore[attr-defined]
return cls

View File

@@ -0,0 +1,182 @@
from __future__ import annotations
import inspect
import re
import types
from typing import Any
def describe(
article: str | None,
value: Any,
name: str | None = None,
verbose: bool = False,
capital: bool = False,
) -> str:
"""Return string that describes a value
Parameters
----------
article : str or None
A definite or indefinite article. If the article is
indefinite (i.e. "a" or "an") the appropriate one
will be inferred. Thus, the arguments of ``describe``
can themselves represent what the resulting string
will actually look like. If None, then no article
will be prepended to the result. For non-articled
description, values that are instances are treated
definitely, while classes are handled indefinitely.
value : any
The value which will be named.
name : str or None (default: None)
Only applies when ``article`` is "the" - this
``name`` is a definite reference to the value.
By default one will be inferred from the value's
type and repr methods.
verbose : bool (default: False)
Whether the name should be concise or verbose. When
possible, verbose names include the module, and/or
class name where an object was defined.
capital : bool (default: False)
Whether the first letter of the article should
be capitalized or not. By default it is not.
Examples
--------
Indefinite description:
>>> describe("a", object())
'an object'
>>> describe("a", object)
'an object'
>>> describe("a", type(object))
'a type'
Definite description:
>>> describe("the", object())
"the object at '...'"
>>> describe("the", object)
'the object object'
>>> describe("the", type(object))
'the type type'
Definitely named description:
>>> describe("the", object(), "I made")
'the object I made'
>>> describe("the", object, "I will use")
'the object I will use'
"""
if isinstance(article, str):
article = article.lower()
if not inspect.isclass(value):
typename = type(value).__name__
else:
typename = value.__name__
if verbose:
typename = _prefix(value) + typename
if article == "the" or (article is None and not inspect.isclass(value)):
if name is not None:
result = f"{typename} {name}"
if article is not None:
return add_article(result, True, capital)
else:
return result
else:
tick_wrap = False
if inspect.isclass(value):
name = value.__name__
elif isinstance(value, types.FunctionType):
name = value.__name__
tick_wrap = True
elif isinstance(value, types.MethodType):
name = value.__func__.__name__
tick_wrap = True
elif type(value).__repr__ in (
object.__repr__,
type.__repr__,
): # type:ignore[comparison-overlap]
name = "at '%s'" % hex(id(value))
verbose = False
else:
name = repr(value)
verbose = False
if verbose:
name = _prefix(value) + name
if tick_wrap:
name = name.join("''")
return describe(article, value, name=name, verbose=verbose, capital=capital)
elif article in ("a", "an") or article is None:
if article is None:
return typename
return add_article(typename, False, capital)
else:
raise ValueError(
"The 'article' argument should be 'the', 'a', 'an', or None not %r" % article
)
def _prefix(value: Any) -> str:
if isinstance(value, types.MethodType):
name = describe(None, value.__self__, verbose=True) + "."
else:
module = inspect.getmodule(value)
if module is not None and module.__name__ != "builtins":
name = module.__name__ + "."
else:
name = ""
return name
def class_of(value: Any) -> Any:
"""Returns a string of the value's type with an indefinite article.
For example 'an Image' or 'a PlotValue'.
"""
if inspect.isclass(value):
return add_article(value.__name__)
else:
return class_of(type(value))
def add_article(name: str, definite: bool = False, capital: bool = False) -> str:
"""Returns the string with a prepended article.
The input does not need to begin with a character.
Parameters
----------
name : str
Name to which to prepend an article
definite : bool (default: False)
Whether the article is definite or not.
Indefinite articles being 'a' and 'an',
while 'the' is definite.
capital : bool (default: False)
Whether the added article should have
its first letter capitalized or not.
"""
if definite:
result = "the " + name
else:
first_letters = re.compile(r"[\W_]+").sub("", name)
if first_letters[:1].lower() in "aeiou":
result = "an " + name
else:
result = "a " + name
if capital:
return result[0].upper() + result[1:]
else:
return result
def repr_type(obj: Any) -> str:
"""Return a string representation of a value and its type for readable
error messages.
"""
the_type = type(obj)
return f"{obj!r} {the_type!r}"

View File

@@ -0,0 +1,51 @@
"""
getargspec excerpted from:
sphinx.util.inspect
~~~~~~~~~~~~~~~~~~~
Helpers for inspecting Python modules.
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import annotations
import inspect
from functools import partial
from typing import Any
# Unmodified from sphinx below this line
def getargspec(func: Any) -> inspect.FullArgSpec:
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.__func__
if type(func) is partial:
orig_func = func.func
argspec = getargspec(orig_func)
args = list(argspec[0])
defaults = list(argspec[3] or ())
kwoargs = list(argspec[4])
kwodefs = dict(argspec[5] or {})
if func.args:
args = args[len(func.args) :]
for arg in func.keywords or ():
try:
i = args.index(arg) - len(args)
del args[i]
try:
del defaults[i]
except IndexError:
pass
except ValueError: # must be a kwonly arg
i = kwoargs.index(arg)
del kwoargs[i]
del kwodefs[arg]
return inspect.FullArgSpec(
args, argspec[1], argspec[2], tuple(defaults), kwoargs, kwodefs, argspec[6]
)
while hasattr(func, "__wrapped__"):
func = func.__wrapped__
if not inspect.isfunction(func):
raise TypeError("%r is not a Python function" % func)
return inspect.getfullargspec(func)

View File

@@ -0,0 +1,41 @@
"""
A simple utility to import something by its string name.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations
from typing import Any
def import_item(name: str) -> Any:
"""Import and return ``bar`` given the string ``foo.bar``.
Calling ``bar = import_item("foo.bar")`` is the functional equivalent of
executing the code ``from foo import bar``.
Parameters
----------
name : string
The fully qualified name of the module/package being imported.
Returns
-------
mod : module object
The module that was imported.
"""
if not isinstance(name, str):
raise TypeError("import_item accepts strings, not '%s'." % type(name))
parts = name.rsplit(".", 1)
if len(parts) == 2:
# called with 'foo.bar....'
package, obj = parts
module = __import__(package, fromlist=[obj])
try:
pak = getattr(module, obj)
except AttributeError as e:
raise ImportError("No module named %s" % obj) from e
return pak
else:
# called with un-dotted string
return __import__(parts[0])

View File

@@ -0,0 +1,41 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations
from typing import Any, Dict
def nested_update(this: Dict[Any, Any], that: Dict[Any, Any]) -> Dict[Any, Any]:
"""Merge two nested dictionaries.
Effectively a recursive ``dict.update``.
Examples
--------
Merge two flat dictionaries:
>>> nested_update(
... {'a': 1, 'b': 2},
... {'b': 3, 'c': 4}
... )
{'a': 1, 'b': 3, 'c': 4}
Merge two nested dictionaries:
>>> nested_update(
... {'x': {'a': 1, 'b': 2}, 'y': 5, 'z': 6},
... {'x': {'b': 3, 'c': 4}, 'z': 7, '0': 8},
... )
{'x': {'a': 1, 'b': 3, 'c': 4}, 'y': 5, 'z': 7, '0': 8}
"""
for key, value in this.items():
if isinstance(value, dict):
if key in that and isinstance(that[key], dict):
nested_update(this[key], that[key])
elif key in that:
this[key] = that[key]
for key, value in that.items():
if key not in this:
this[key] = value
return this

View File

@@ -0,0 +1,24 @@
"""Sentinel class for constants with useful reprs"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations
import typing as t
class Sentinel:
def __init__(self, name: str, module: t.Any, docstring: str | None = None) -> None:
self.name = name
self.module = module
if docstring:
self.__doc__ = docstring
def __repr__(self) -> str:
return str(self.module) + "." + self.name
def __copy__(self) -> Sentinel:
return self
def __deepcopy__(self, memo: t.Any) -> Sentinel:
return self

View File

@@ -0,0 +1,40 @@
"""
Utilities imported from ipython_genutils
"""
from __future__ import annotations
import re
import textwrap
from textwrap import dedent
from textwrap import indent as _indent
from typing import List
def indent(val: str) -> str:
return _indent(val, " ")
def wrap_paragraphs(text: str, ncols: int = 80) -> List[str]:
"""Wrap multiple paragraphs to fit a specified width.
This is equivalent to textwrap.wrap, but with support for multiple
paragraphs, as separated by empty lines.
Returns
-------
list of complete paragraphs, wrapped to fill `ncols` columns.
"""
paragraph_re = re.compile(r"\n(\s*\n)+", re.MULTILINE)
text = dedent(text).strip()
paragraphs = paragraph_re.split(text)[::2] # every other entry is space
out_ps = []
indent_re = re.compile(r"\n\s+", re.MULTILINE)
for p in paragraphs:
# presume indentation that survives dedent is meaningful formatting,
# so don't fill unless text is flush.
if indent_re.search(p) is None:
# wrap paragraph
p = textwrap.fill(p, ncols)
out_ps.append(p)
return out_ps

View File

@@ -0,0 +1,64 @@
from __future__ import annotations
import inspect
import os
import typing as t
import warnings
def warn(msg: str, category: t.Any, *, stacklevel: int, source: t.Any = None) -> None:
"""Like warnings.warn(), but category and stacklevel are required.
You pretty much never want the default stacklevel of 1, so this helps
encourage setting it explicitly."""
warnings.warn(msg, category=category, stacklevel=stacklevel, source=source)
def deprecated_method(method: t.Any, cls: t.Any, method_name: str, msg: str) -> None:
"""Show deprecation warning about a magic method definition.
Uses warn_explicit to bind warning to method definition instead of triggering code,
which isn't relevant.
"""
warn_msg = f"{cls.__name__}.{method_name} is deprecated in traitlets 4.1: {msg}"
for parent in inspect.getmro(cls):
if method_name in parent.__dict__:
cls = parent
break
# limit deprecation messages to once per package
package_name = cls.__module__.split(".", 1)[0]
key = (package_name, msg)
if not should_warn(key):
return
try:
fname = inspect.getsourcefile(method) or "<unknown>"
lineno = inspect.getsourcelines(method)[1] or 0
except (OSError, TypeError) as e:
# Failed to inspect for some reason
warn(
warn_msg + ("\n(inspection failed) %s" % e),
DeprecationWarning,
stacklevel=2,
)
else:
warnings.warn_explicit(warn_msg, DeprecationWarning, fname, lineno)
_deprecations_shown = set()
def should_warn(key: t.Any) -> bool:
"""Add our own checks for too many deprecation warnings.
Limit to once per package.
"""
env_flag = os.environ.get("TRAITLETS_ALL_DEPRECATIONS")
if env_flag and env_flag != "0":
return True
if key not in _deprecations_shown:
_deprecations_shown.add(key)
return True
else:
return False