first commit

This commit is contained in:
Ayxan
2022-05-23 00:16:32 +04:00
commit d660f2a4ca
24786 changed files with 4428337 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
import os
# vestigal things from IPython_genutils.
def cast_unicode(s, encoding="utf-8"):
if isinstance(s, bytes):
return s.decode(encoding, "replace")
return s
def filefind(filename, path_dirs=None):
"""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 occurence 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,)
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):
"""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,26 @@
"""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.
class Bunch(dict): # type:ignore[type-arg]
"""A dict with attribute-access"""
def __getattr__(self, key):
try:
return self.__getitem__(key)
except KeyError:
raise AttributeError(key)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __dir__(self):
# 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,83 @@
"""Useful decorators for Traitlets users."""
import copy
from inspect import Parameter, Signature, signature
from ..traitlets import Undefined
def _get_default(value):
"""Get default argument value, given the trait default value."""
return Parameter.empty if value == Undefined else value
def signature_has_traits(cls):
"""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(
"The {} constructor does not take **kwargs, which means that the signature can not be expanded with trait names".format(
cls
)
)
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)
return cls

View File

@@ -0,0 +1,174 @@
import inspect
import re
import types
def describe(article, value, name=None, verbose=False, capital=False):
"""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 infered. 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 infered 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):
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):
"""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, definite=False, capital=False):
"""Returns the string with a prepended article.
The input does not need to begin with a charater.
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):
"""Return a string representation of a value and its type for readable
error messages.
"""
the_type = type(obj)
msg = f"{obj!r} {the_type!r}"
return msg

View File

@@ -0,0 +1,49 @@
"""
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.
"""
import inspect
from functools import partial
# Unmodified from sphinx below this line
def getargspec(func):
"""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,38 @@
"""
A simple utility to import something by its string name.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
def import_item(name):
"""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:
raise ImportError("No module named %s" % obj)
return pak
else:
# called with un-dotted string
return __import__(parts[0])

View File

@@ -0,0 +1,38 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
def nested_update(this, that):
"""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,21 @@
"""Sentinel class for constants with useful reprs"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
class Sentinel:
def __init__(self, name, module, docstring=None):
self.name = name
self.module = module
if docstring:
self.__doc__ = docstring
def __repr__(self):
return str(self.module) + "." + self.name
def __copy__(self):
return self
def __deepcopy__(self, memo):
return self

View File

@@ -0,0 +1,16 @@
from ..bunch import Bunch
def test_bunch():
b = Bunch(x=5, y=10)
assert "y" in b
assert "x" in b
assert b.x == 5
b["a"] = "hi"
assert b.a == "hi"
def test_bunch_dir():
b = Bunch(x=5, y=10)
assert "x" in dir(b)
assert "keys" in dir(b)

View File

@@ -0,0 +1,137 @@
from inspect import Parameter, signature
from unittest import TestCase
from ...traitlets import HasTraits, Int, Unicode
from ..decorators import signature_has_traits
class TestExpandSignature(TestCase):
def test_no_init(self):
@signature_has_traits
class Foo(HasTraits):
number1 = Int()
number2 = Int()
value = Unicode("Hello")
parameters = signature(Foo).parameters
parameter_names = list(parameters)
self.assertIs(parameters["args"].kind, Parameter.VAR_POSITIONAL)
self.assertEqual("args", parameter_names[0])
self.assertIs(parameters["number1"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["number2"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["value"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["kwargs"].kind, Parameter.VAR_KEYWORD)
self.assertEqual("kwargs", parameter_names[-1])
f = Foo(number1=32, value="World")
self.assertEqual(f.number1, 32)
self.assertEqual(f.number2, 0)
self.assertEqual(f.value, "World")
def test_partial_init(self):
@signature_has_traits
class Foo(HasTraits):
number1 = Int()
number2 = Int()
value = Unicode("Hello")
def __init__(self, arg1, **kwargs):
self.arg1 = arg1
super().__init__(**kwargs)
parameters = signature(Foo).parameters
parameter_names = list(parameters)
self.assertIs(parameters["arg1"].kind, Parameter.POSITIONAL_OR_KEYWORD)
self.assertEqual("arg1", parameter_names[0])
self.assertIs(parameters["number1"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["number2"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["value"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["kwargs"].kind, Parameter.VAR_KEYWORD)
self.assertEqual("kwargs", parameter_names[-1])
f = Foo(1, number1=32, value="World")
self.assertEqual(f.arg1, 1)
self.assertEqual(f.number1, 32)
self.assertEqual(f.number2, 0)
self.assertEqual(f.value, "World")
def test_duplicate_init(self):
@signature_has_traits
class Foo(HasTraits):
number1 = Int()
number2 = Int()
def __init__(self, number1, **kwargs):
self.test = number1
super().__init__(number1=number1, **kwargs)
parameters = signature(Foo).parameters
parameter_names = list(parameters)
self.assertListEqual(parameter_names, ["number1", "number2", "kwargs"])
f = Foo(number1=32, number2=36)
self.assertEqual(f.test, 32)
self.assertEqual(f.number1, 32)
self.assertEqual(f.number2, 36)
def test_full_init(self):
@signature_has_traits
class Foo(HasTraits):
number1 = Int()
number2 = Int()
value = Unicode("Hello")
def __init__(self, arg1, arg2=None, *pos_args, **kw_args):
self.arg1 = arg1
self.arg2 = arg2
self.pos_args = pos_args
self.kw_args = kw_args
super().__init__(*pos_args, **kw_args)
parameters = signature(Foo).parameters
parameter_names = list(parameters)
self.assertIs(parameters["arg1"].kind, Parameter.POSITIONAL_OR_KEYWORD)
self.assertEqual("arg1", parameter_names[0])
self.assertIs(parameters["arg2"].kind, Parameter.POSITIONAL_OR_KEYWORD)
self.assertEqual("arg2", parameter_names[1])
self.assertIs(parameters["pos_args"].kind, Parameter.VAR_POSITIONAL)
self.assertEqual("pos_args", parameter_names[2])
self.assertIs(parameters["number1"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["number2"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["value"].kind, Parameter.KEYWORD_ONLY)
self.assertIs(parameters["kw_args"].kind, Parameter.VAR_KEYWORD)
self.assertEqual("kw_args", parameter_names[-1])
f = Foo(1, 3, 45, "hey", number1=32, value="World")
self.assertEqual(f.arg1, 1)
self.assertEqual(f.arg2, 3)
self.assertTupleEqual(f.pos_args, (45, "hey"))
self.assertEqual(f.number1, 32)
self.assertEqual(f.number2, 0)
self.assertEqual(f.value, "World")
def test_no_kwargs(self):
with self.assertRaises(RuntimeError):
@signature_has_traits
class Foo(HasTraits):
number1 = Int()
number2 = Int()
def __init__(self, arg1, arg2=None):
pass

View File

@@ -0,0 +1,26 @@
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
#
# Adapted from enthought.traits, Copyright (c) Enthought, Inc.,
# also under the terms of the Modified BSD License.
"""Tests for traitlets.utils.importstring."""
import os
from unittest import TestCase
from ..importstring import import_item
class TestImportItem(TestCase):
def test_import_unicode(self):
self.assertIs(os, import_item("os"))
self.assertIs(os.path, import_item("os.path"))
self.assertIs(os.path.join, import_item("os.path.join"))
def test_bad_input(self):
class NotAString:
pass
msg = "import_item accepts strings, not '%s'." % NotAString
with self.assertRaisesRegex(TypeError, msg):
import_item(NotAString())

View File

@@ -0,0 +1,40 @@
"""
Utilities imported from ipython_genutils
"""
import re
import textwrap
from textwrap import dedent
from textwrap import indent as _indent
from typing import List
def indent(val):
res = _indent(val, " ")
return res
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