aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/setuptools/_static.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/setuptools/_static.py')
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_static.py188
1 files changed, 188 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_static.py b/.venv/lib/python3.12/site-packages/setuptools/_static.py
new file mode 100644
index 00000000..af35862c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_static.py
@@ -0,0 +1,188 @@
+from functools import wraps
+from typing import TypeVar
+
+import packaging.specifiers
+
+from .warnings import SetuptoolsDeprecationWarning
+
+
+class Static:
+ """
+ Wrapper for built-in object types that are allow setuptools to identify
+ static core metadata (in opposition to ``Dynamic``, as defined :pep:`643`).
+
+ The trick is to mark values with :class:`Static` when they come from
+ ``pyproject.toml`` or ``setup.cfg``, so if any plugin overwrite the value
+ with a built-in, setuptools will be able to recognise the change.
+
+ We inherit from built-in classes, so that we don't need to change the existing
+ code base to deal with the new types.
+ We also should strive for immutability objects to avoid changes after the
+ initial parsing.
+ """
+
+ _mutated_: bool = False # TODO: Remove after deprecation warning is solved
+
+
+def _prevent_modification(target: type, method: str, copying: str) -> None:
+ """
+ Because setuptools is very flexible we cannot fully prevent
+ plugins and user customizations from modifying static values that were
+ parsed from config files.
+ But we can attempt to block "in-place" mutations and identify when they
+ were done.
+ """
+ fn = getattr(target, method, None)
+ if fn is None:
+ return
+
+ @wraps(fn)
+ def _replacement(self: Static, *args, **kwargs):
+ # TODO: After deprecation period raise NotImplementedError instead of warning
+ # which obviated the existence and checks of the `_mutated_` attribute.
+ self._mutated_ = True
+ SetuptoolsDeprecationWarning.emit(
+ "Direct modification of value will be disallowed",
+ f"""
+ In an effort to implement PEP 643, direct/in-place changes of static values
+ that come from configuration files are deprecated.
+ If you need to modify this value, please first create a copy with {copying}
+ and make sure conform to all relevant standards when overriding setuptools
+ functionality (https://packaging.python.org/en/latest/specifications/).
+ """,
+ due_date=(2025, 10, 10), # Initially introduced in 2024-09-06
+ )
+ return fn(self, *args, **kwargs)
+
+ _replacement.__doc__ = "" # otherwise doctest may fail.
+ setattr(target, method, _replacement)
+
+
+class Str(str, Static):
+ pass
+
+
+class Tuple(tuple, Static):
+ pass
+
+
+class List(list, Static):
+ """
+ :meta private:
+ >>> x = List([1, 2, 3])
+ >>> is_static(x)
+ True
+ >>> x += [0] # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ SetuptoolsDeprecationWarning: Direct modification ...
+ >>> is_static(x) # no longer static after modification
+ False
+ >>> y = list(x)
+ >>> y.clear()
+ >>> y
+ []
+ >>> y == x
+ False
+ >>> is_static(List(y))
+ True
+ """
+
+
+# Make `List` immutable-ish
+# (certain places of setuptools/distutils issue a warn if we use tuple instead of list)
+for _method in (
+ '__delitem__',
+ '__iadd__',
+ '__setitem__',
+ 'append',
+ 'clear',
+ 'extend',
+ 'insert',
+ 'remove',
+ 'reverse',
+ 'pop',
+):
+ _prevent_modification(List, _method, "`list(value)`")
+
+
+class Dict(dict, Static):
+ """
+ :meta private:
+ >>> x = Dict({'a': 1, 'b': 2})
+ >>> is_static(x)
+ True
+ >>> x['c'] = 0 # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ SetuptoolsDeprecationWarning: Direct modification ...
+ >>> x._mutated_
+ True
+ >>> is_static(x) # no longer static after modification
+ False
+ >>> y = dict(x)
+ >>> y.popitem()
+ ('b', 2)
+ >>> y == x
+ False
+ >>> is_static(Dict(y))
+ True
+ """
+
+
+# Make `Dict` immutable-ish (we cannot inherit from types.MappingProxyType):
+for _method in (
+ '__delitem__',
+ '__ior__',
+ '__setitem__',
+ 'clear',
+ 'pop',
+ 'popitem',
+ 'setdefault',
+ 'update',
+):
+ _prevent_modification(Dict, _method, "`dict(value)`")
+
+
+class SpecifierSet(packaging.specifiers.SpecifierSet, Static):
+ """Not exactly a built-in type but useful for ``requires-python``"""
+
+
+T = TypeVar("T")
+
+
+def noop(value: T) -> T:
+ """
+ >>> noop(42)
+ 42
+ """
+ return value
+
+
+_CONVERSIONS = {str: Str, tuple: Tuple, list: List, dict: Dict}
+
+
+def attempt_conversion(value: T) -> T:
+ """
+ >>> is_static(attempt_conversion("hello"))
+ True
+ >>> is_static(object())
+ False
+ """
+ return _CONVERSIONS.get(type(value), noop)(value) # type: ignore[call-overload]
+
+
+def is_static(value: object) -> bool:
+ """
+ >>> is_static(a := Dict({'a': 1}))
+ True
+ >>> is_static(dict(a))
+ False
+ >>> is_static(b := List([1, 2, 3]))
+ True
+ >>> is_static(list(b))
+ False
+ """
+ return isinstance(value, Static) and not value._mutated_
+
+
+EMPTY_LIST = List()
+EMPTY_DICT = Dict()