diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2025-01-17 19:30:56 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2025-01-17 20:31:21 +0300 |
commit | f4f4ef7f7ee5c0dbaf18a9cbcfd8161888ada142 (patch) | |
tree | 22bb2605d8e040c1ec808f695c9deada7293fae7 | |
parent | ded0c6a5d340d85ccd15d96999e726133527091a (diff) | |
download | ydb-f4f4ef7f7ee5c0dbaf18a9cbcfd8161888ada142.tar.gz |
Intermediate changes
commit_hash:4bbc3602d0ac6ac79bb8e6d7b176dded9cefc306
120 files changed, 2618 insertions, 8761 deletions
diff --git a/contrib/python/ordered-set/.dist-info/METADATA b/contrib/python/ordered-set/.dist-info/METADATA deleted file mode 100644 index 7aea136818..0000000000 --- a/contrib/python/ordered-set/.dist-info/METADATA +++ /dev/null @@ -1,158 +0,0 @@ -Metadata-Version: 2.1 -Name: ordered-set -Version: 4.1.0 -Summary: An OrderedSet is a custom MutableSet that remembers its order, so that every -Author-email: Elia Robyn Lake <gh@arborelia.net> -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Dist: pytest ; extra == "dev" -Requires-Dist: black ; extra == "dev" -Requires-Dist: mypy ; extra == "dev" -Project-URL: Home, https://github.com/rspeer/ordered-set -Provides-Extra: dev - -[](https://pypi.python.org/pypi/ordered-set) - -An OrderedSet is a mutable data structure that is a hybrid of a list and a set. -It remembers the order of its entries, and every entry has an index number that -can be looked up. - -## Installation - -`ordered_set` is available on PyPI and packaged as a wheel. You can list it -as a dependency of your project, in whatever form that takes. - -To install it into your current Python environment: - - pip install ordered-set - -To install the code for development, after checking out the repository: - - pip install flit - flit install - -## Usage examples - -An OrderedSet is created and used like a set: - - >>> from ordered_set import OrderedSet - - >>> letters = OrderedSet('abracadabra') - - >>> letters - OrderedSet(['a', 'b', 'r', 'c', 'd']) - - >>> 'r' in letters - True - -It is efficient to find the index of an entry in an OrderedSet, or find an -entry by its index. To help with this use case, the `.add()` method returns -the index of the added item, whether it was already in the set or not. - - >>> letters.index('r') - 2 - - >>> letters[2] - 'r' - - >>> letters.add('r') - 2 - - >>> letters.add('x') - 5 - -OrderedSets implement the union (`|`), intersection (`&`), and difference (`-`) -operators like sets do. - - >>> letters |= OrderedSet('shazam') - - >>> letters - OrderedSet(['a', 'b', 'r', 'c', 'd', 'x', 's', 'h', 'z', 'm']) - - >>> letters & set('aeiou') - OrderedSet(['a']) - - >>> letters -= 'abcd' - - >>> letters - OrderedSet(['r', 'x', 's', 'h', 'z', 'm']) - -The `__getitem__()` and `index()` methods have been extended to accept any -iterable except a string, returning a list, to perform NumPy-like "fancy -indexing". - - >>> letters = OrderedSet('abracadabra') - - >>> letters[[0, 2, 3]] - ['a', 'r', 'c'] - - >>> letters.index(['a', 'r', 'c']) - [0, 2, 3] - -OrderedSet implements `__getstate__` and `__setstate__` so it can be pickled, -and implements the abstract base classes `collections.MutableSet` and -`collections.Sequence`. - -OrderedSet can be used as a generic collection type, similar to the collections -in the `typing` module like List, Dict, and Set. For example, you can annotate -a variable as having the type `OrderedSet[str]` or `OrderedSet[Tuple[int, -str]]`. - - -## OrderedSet in data science applications - -An OrderedSet can be used as a bi-directional mapping between a sparse -vocabulary and dense index numbers. As of version 3.1, it accepts NumPy arrays -of index numbers as well as lists. - -This combination of features makes OrderedSet a simple implementation of many -of the things that `pandas.Index` is used for, and many of its operations are -faster than the equivalent pandas operations. - -For further compatibility with pandas.Index, `get_loc` (the pandas method for -looking up a single index) and `get_indexer` (the pandas method for fancy -indexing in reverse) are both aliases for `index` (which handles both cases -in OrderedSet). - - -## Authors - -OrderedSet was implemented by Elia Robyn Lake (maiden name: Robyn Speer). -Jon Crall contributed changes and tests to make it fit the Python set API. -Roman Inflianskas added the original type annotations. - - -## Comparisons - -The original implementation of OrderedSet was a [recipe posted to ActiveState -Recipes][recipe] by Raymond Hettiger, released under the MIT license. - -[recipe]: https://code.activestate.com/recipes/576694-orderedset/ - -Hettiger's implementation kept its content in a doubly-linked list referenced by a -dict. As a result, looking up an item by its index was an O(N) operation, while -deletion was O(1). - -This version makes different trade-offs for the sake of efficient lookups. Its -content is a standard Python list instead of a doubly-linked list. This -provides O(1) lookups by index at the expense of O(N) deletion, as well as -slightly faster iteration. - -In Python 3.6 and later, the built-in `dict` type is inherently ordered. If you -ignore the dictionary values, that also gives you a simple ordered set, with -fast O(1) insertion, deletion, iteration and membership testing. However, `dict` -does not provide the list-like random access features of OrderedSet. You -would have to convert it to a list in O(N) to look up the index of an entry or -look up an entry by its index. - diff --git a/contrib/python/ordered-set/.dist-info/top_level.txt b/contrib/python/ordered-set/.dist-info/top_level.txt deleted file mode 100644 index 1c191eef52..0000000000 --- a/contrib/python/ordered-set/.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -ordered_set diff --git a/contrib/python/ordered-set/MIT-LICENSE b/contrib/python/ordered-set/MIT-LICENSE deleted file mode 100644 index 02185ee496..0000000000 --- a/contrib/python/ordered-set/MIT-LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2012-2022 Elia Robyn Lake - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/contrib/python/ordered-set/README.md b/contrib/python/ordered-set/README.md deleted file mode 100644 index f66fadda0f..0000000000 --- a/contrib/python/ordered-set/README.md +++ /dev/null @@ -1,133 +0,0 @@ -[](https://pypi.python.org/pypi/ordered-set) - -An OrderedSet is a mutable data structure that is a hybrid of a list and a set. -It remembers the order of its entries, and every entry has an index number that -can be looked up. - -## Installation - -`ordered_set` is available on PyPI and packaged as a wheel. You can list it -as a dependency of your project, in whatever form that takes. - -To install it into your current Python environment: - - pip install ordered-set - -To install the code for development, after checking out the repository: - - pip install flit - flit install - -## Usage examples - -An OrderedSet is created and used like a set: - - >>> from ordered_set import OrderedSet - - >>> letters = OrderedSet('abracadabra') - - >>> letters - OrderedSet(['a', 'b', 'r', 'c', 'd']) - - >>> 'r' in letters - True - -It is efficient to find the index of an entry in an OrderedSet, or find an -entry by its index. To help with this use case, the `.add()` method returns -the index of the added item, whether it was already in the set or not. - - >>> letters.index('r') - 2 - - >>> letters[2] - 'r' - - >>> letters.add('r') - 2 - - >>> letters.add('x') - 5 - -OrderedSets implement the union (`|`), intersection (`&`), and difference (`-`) -operators like sets do. - - >>> letters |= OrderedSet('shazam') - - >>> letters - OrderedSet(['a', 'b', 'r', 'c', 'd', 'x', 's', 'h', 'z', 'm']) - - >>> letters & set('aeiou') - OrderedSet(['a']) - - >>> letters -= 'abcd' - - >>> letters - OrderedSet(['r', 'x', 's', 'h', 'z', 'm']) - -The `__getitem__()` and `index()` methods have been extended to accept any -iterable except a string, returning a list, to perform NumPy-like "fancy -indexing". - - >>> letters = OrderedSet('abracadabra') - - >>> letters[[0, 2, 3]] - ['a', 'r', 'c'] - - >>> letters.index(['a', 'r', 'c']) - [0, 2, 3] - -OrderedSet implements `__getstate__` and `__setstate__` so it can be pickled, -and implements the abstract base classes `collections.MutableSet` and -`collections.Sequence`. - -OrderedSet can be used as a generic collection type, similar to the collections -in the `typing` module like List, Dict, and Set. For example, you can annotate -a variable as having the type `OrderedSet[str]` or `OrderedSet[Tuple[int, -str]]`. - - -## OrderedSet in data science applications - -An OrderedSet can be used as a bi-directional mapping between a sparse -vocabulary and dense index numbers. As of version 3.1, it accepts NumPy arrays -of index numbers as well as lists. - -This combination of features makes OrderedSet a simple implementation of many -of the things that `pandas.Index` is used for, and many of its operations are -faster than the equivalent pandas operations. - -For further compatibility with pandas.Index, `get_loc` (the pandas method for -looking up a single index) and `get_indexer` (the pandas method for fancy -indexing in reverse) are both aliases for `index` (which handles both cases -in OrderedSet). - - -## Authors - -OrderedSet was implemented by Elia Robyn Lake (maiden name: Robyn Speer). -Jon Crall contributed changes and tests to make it fit the Python set API. -Roman Inflianskas added the original type annotations. - - -## Comparisons - -The original implementation of OrderedSet was a [recipe posted to ActiveState -Recipes][recipe] by Raymond Hettiger, released under the MIT license. - -[recipe]: https://code.activestate.com/recipes/576694-orderedset/ - -Hettiger's implementation kept its content in a doubly-linked list referenced by a -dict. As a result, looking up an item by its index was an O(N) operation, while -deletion was O(1). - -This version makes different trade-offs for the sake of efficient lookups. Its -content is a standard Python list instead of a doubly-linked list. This -provides O(1) lookups by index at the expense of O(N) deletion, as well as -slightly faster iteration. - -In Python 3.6 and later, the built-in `dict` type is inherently ordered. If you -ignore the dictionary values, that also gives you a simple ordered set, with -fast O(1) insertion, deletion, iteration and membership testing. However, `dict` -does not provide the list-like random access features of OrderedSet. You -would have to convert it to a list in O(N) to look up the index of an entry or -look up an entry by its index. diff --git a/contrib/python/ordered-set/ordered_set/__init__.py b/contrib/python/ordered-set/ordered_set/__init__.py deleted file mode 100644 index e86c70ed80..0000000000 --- a/contrib/python/ordered-set/ordered_set/__init__.py +++ /dev/null @@ -1,536 +0,0 @@ -""" -An OrderedSet is a custom MutableSet that remembers its order, so that every -entry has an index that can be looked up. It can also act like a Sequence. - -Based on a recipe originally posted to ActiveState Recipes by Raymond Hettiger, -and released under the MIT license. -""" -import itertools as it -from typing import ( - Any, - Dict, - Iterable, - Iterator, - List, - MutableSet, - AbstractSet, - Sequence, - Set, - TypeVar, - Union, - overload, -) - -SLICE_ALL = slice(None) -__version__ = "4.1.0" - - -T = TypeVar("T") - -# SetLike[T] is either a set of elements of type T, or a sequence, which -# we will convert to an OrderedSet by adding its elements in order. -SetLike = Union[AbstractSet[T], Sequence[T]] -OrderedSetInitializer = Union[AbstractSet[T], Sequence[T], Iterable[T]] - - -def _is_atomic(obj: Any) -> bool: - """ - Returns True for objects which are iterable but should not be iterated in - the context of indexing an OrderedSet. - - When we index by an iterable, usually that means we're being asked to look - up a list of things. - - However, in the case of the .index() method, we shouldn't handle strings - and tuples like other iterables. They're not sequences of things to look - up, they're the single, atomic thing we're trying to find. - - As an example, oset.index('hello') should give the index of 'hello' in an - OrderedSet of strings. It shouldn't give the indexes of each individual - character. - """ - return isinstance(obj, str) or isinstance(obj, tuple) - - -class OrderedSet(MutableSet[T], Sequence[T]): - """ - An OrderedSet is a custom MutableSet that remembers its order, so that - every entry has an index that can be looked up. - - Example: - >>> OrderedSet([1, 1, 2, 3, 2]) - OrderedSet([1, 2, 3]) - """ - - def __init__(self, initial: OrderedSetInitializer[T] = None): - self.items: List[T] = [] - self.map: Dict[T, int] = {} - if initial is not None: - # In terms of duck-typing, the default __ior__ is compatible with - # the types we use, but it doesn't expect all the types we - # support as values for `initial`. - self |= initial # type: ignore - - def __len__(self): - """ - Returns the number of unique elements in the ordered set - - Example: - >>> len(OrderedSet([])) - 0 - >>> len(OrderedSet([1, 2])) - 2 - """ - return len(self.items) - - @overload - def __getitem__(self, index: slice) -> "OrderedSet[T]": - ... - - @overload - def __getitem__(self, index: Sequence[int]) -> List[T]: - ... - - @overload - def __getitem__(self, index: int) -> T: - ... - - # concrete implementation - def __getitem__(self, index): - """ - Get the item at a given index. - - If `index` is a slice, you will get back that slice of items, as a - new OrderedSet. - - If `index` is a list or a similar iterable, you'll get a list of - items corresponding to those indices. This is similar to NumPy's - "fancy indexing". The result is not an OrderedSet because you may ask - for duplicate indices, and the number of elements returned should be - the number of elements asked for. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset[1] - 2 - """ - if isinstance(index, slice) and index == SLICE_ALL: - return self.copy() - elif isinstance(index, Iterable): - return [self.items[i] for i in index] - elif isinstance(index, slice) or hasattr(index, "__index__"): - result = self.items[index] - if isinstance(result, list): - return self.__class__(result) - else: - return result - else: - raise TypeError("Don't know how to index an OrderedSet by %r" % index) - - def copy(self) -> "OrderedSet[T]": - """ - Return a shallow copy of this object. - - Example: - >>> this = OrderedSet([1, 2, 3]) - >>> other = this.copy() - >>> this == other - True - >>> this is other - False - """ - return self.__class__(self) - - # Define the gritty details of how an OrderedSet is serialized as a pickle. - # We leave off type annotations, because the only code that should interact - # with these is a generalized tool such as pickle. - def __getstate__(self): - if len(self) == 0: - # In pickle, the state can't be an empty list. - # We need to return a truthy value, or else __setstate__ won't be run. - # - # This could have been done more gracefully by always putting the state - # in a tuple, but this way is backwards- and forwards- compatible with - # previous versions of OrderedSet. - return (None,) - else: - return list(self) - - def __setstate__(self, state): - if state == (None,): - self.__init__([]) - else: - self.__init__(state) - - def __contains__(self, key: Any) -> bool: - """ - Test if the item is in this ordered set. - - Example: - >>> 1 in OrderedSet([1, 3, 2]) - True - >>> 5 in OrderedSet([1, 3, 2]) - False - """ - return key in self.map - - # Technically type-incompatible with MutableSet, because we return an - # int instead of nothing. This is also one of the things that makes - # OrderedSet convenient to use. - def add(self, key: T) -> int: - """ - Add `key` as an item to this OrderedSet, then return its index. - - If `key` is already in the OrderedSet, return the index it already - had. - - Example: - >>> oset = OrderedSet() - >>> oset.append(3) - 0 - >>> print(oset) - OrderedSet([3]) - """ - if key not in self.map: - self.map[key] = len(self.items) - self.items.append(key) - return self.map[key] - - append = add - - def update(self, sequence: SetLike[T]) -> int: - """ - Update the set with the given iterable sequence, then return the index - of the last element inserted. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.update([3, 1, 5, 1, 4]) - 4 - >>> print(oset) - OrderedSet([1, 2, 3, 5, 4]) - """ - item_index = 0 - try: - for item in sequence: - item_index = self.add(item) - except TypeError: - raise ValueError( - "Argument needs to be an iterable, got %s" % type(sequence) - ) - return item_index - - @overload - def index(self, key: Sequence[T]) -> List[int]: - ... - - @overload - def index(self, key: T) -> int: - ... - - # concrete implementation - def index(self, key): - """ - Get the index of a given entry, raising an IndexError if it's not - present. - - `key` can be an iterable of entries that is not a string, in which case - this returns a list of indices. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.index(2) - 1 - """ - if isinstance(key, Iterable) and not _is_atomic(key): - return [self.index(subkey) for subkey in key] - return self.map[key] - - # Provide some compatibility with pd.Index - get_loc = index - get_indexer = index - - def pop(self, index=-1) -> T: - """ - Remove and return item at index (default last). - - Raises KeyError if the set is empty. - Raises IndexError if index is out of range. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.pop() - 3 - """ - if not self.items: - raise KeyError("Set is empty") - - elem = self.items[index] - del self.items[index] - del self.map[elem] - return elem - - def discard(self, key: T) -> None: - """ - Remove an element. Do not raise an exception if absent. - - The MutableSet mixin uses this to implement the .remove() method, which - *does* raise an error when asked to remove a non-existent item. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.discard(2) - >>> print(oset) - OrderedSet([1, 3]) - >>> oset.discard(2) - >>> print(oset) - OrderedSet([1, 3]) - """ - if key in self: - i = self.map[key] - del self.items[i] - del self.map[key] - for k, v in self.map.items(): - if v >= i: - self.map[k] = v - 1 - - def clear(self) -> None: - """ - Remove all items from this OrderedSet. - """ - del self.items[:] - self.map.clear() - - def __iter__(self) -> Iterator[T]: - """ - Example: - >>> list(iter(OrderedSet([1, 2, 3]))) - [1, 2, 3] - """ - return iter(self.items) - - def __reversed__(self) -> Iterator[T]: - """ - Example: - >>> list(reversed(OrderedSet([1, 2, 3]))) - [3, 2, 1] - """ - return reversed(self.items) - - def __repr__(self) -> str: - if not self: - return "%s()" % (self.__class__.__name__,) - return "%s(%r)" % (self.__class__.__name__, list(self)) - - def __eq__(self, other: Any) -> bool: - """ - Returns true if the containers have the same items. If `other` is a - Sequence, then order is checked, otherwise it is ignored. - - Example: - >>> oset = OrderedSet([1, 3, 2]) - >>> oset == [1, 3, 2] - True - >>> oset == [1, 2, 3] - False - >>> oset == [2, 3] - False - >>> oset == OrderedSet([3, 2, 1]) - False - """ - if isinstance(other, Sequence): - # Check that this OrderedSet contains the same elements, in the - # same order, as the other object. - return list(self) == list(other) - try: - other_as_set = set(other) - except TypeError: - # If `other` can't be converted into a set, it's not equal. - return False - else: - return set(self) == other_as_set - - def union(self, *sets: SetLike[T]) -> "OrderedSet[T]": - """ - Combines all unique items. - Each items order is defined by its first appearance. - - Example: - >>> oset = OrderedSet.union(OrderedSet([3, 1, 4, 1, 5]), [1, 3], [2, 0]) - >>> print(oset) - OrderedSet([3, 1, 4, 5, 2, 0]) - >>> oset.union([8, 9]) - OrderedSet([3, 1, 4, 5, 2, 0, 8, 9]) - >>> oset | {10} - OrderedSet([3, 1, 4, 5, 2, 0, 10]) - """ - cls: type = OrderedSet - if isinstance(self, OrderedSet): - cls = self.__class__ - containers = map(list, it.chain([self], sets)) - items = it.chain.from_iterable(containers) - return cls(items) - - def __and__(self, other: SetLike[T]) -> "OrderedSet[T]": - # the parent implementation of this is backwards - return self.intersection(other) - - def intersection(self, *sets: SetLike[T]) -> "OrderedSet[T]": - """ - Returns elements in common between all sets. Order is defined only - by the first set. - - Example: - >>> oset = OrderedSet.intersection(OrderedSet([0, 1, 2, 3]), [1, 2, 3]) - >>> print(oset) - OrderedSet([1, 2, 3]) - >>> oset.intersection([2, 4, 5], [1, 2, 3, 4]) - OrderedSet([2]) - >>> oset.intersection() - OrderedSet([1, 2, 3]) - """ - cls: type = OrderedSet - items: OrderedSetInitializer[T] = self - if isinstance(self, OrderedSet): - cls = self.__class__ - if sets: - common = set.intersection(*map(set, sets)) - items = (item for item in self if item in common) - return cls(items) - - def difference(self, *sets: SetLike[T]) -> "OrderedSet[T]": - """ - Returns all elements that are in this set but not the others. - - Example: - >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2])) - OrderedSet([1, 3]) - >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2]), OrderedSet([3])) - OrderedSet([1]) - >>> OrderedSet([1, 2, 3]) - OrderedSet([2]) - OrderedSet([1, 3]) - >>> OrderedSet([1, 2, 3]).difference() - OrderedSet([1, 2, 3]) - """ - cls = self.__class__ - items: OrderedSetInitializer[T] = self - if sets: - other = set.union(*map(set, sets)) - items = (item for item in self if item not in other) - return cls(items) - - def issubset(self, other: SetLike[T]) -> bool: - """ - Report whether another set contains this set. - - Example: - >>> OrderedSet([1, 2, 3]).issubset({1, 2}) - False - >>> OrderedSet([1, 2, 3]).issubset({1, 2, 3, 4}) - True - >>> OrderedSet([1, 2, 3]).issubset({1, 4, 3, 5}) - False - """ - if len(self) > len(other): # Fast check for obvious cases - return False - return all(item in other for item in self) - - def issuperset(self, other: SetLike[T]) -> bool: - """ - Report whether this set contains another set. - - Example: - >>> OrderedSet([1, 2]).issuperset([1, 2, 3]) - False - >>> OrderedSet([1, 2, 3, 4]).issuperset({1, 2, 3}) - True - >>> OrderedSet([1, 4, 3, 5]).issuperset({1, 2, 3}) - False - """ - if len(self) < len(other): # Fast check for obvious cases - return False - return all(item in self for item in other) - - def symmetric_difference(self, other: SetLike[T]) -> "OrderedSet[T]": - """ - Return the symmetric difference of two OrderedSets as a new set. - That is, the new set will contain all elements that are in exactly - one of the sets. - - Their order will be preserved, with elements from `self` preceding - elements from `other`. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.symmetric_difference(other) - OrderedSet([4, 5, 9, 2]) - """ - cls: type = OrderedSet - if isinstance(self, OrderedSet): - cls = self.__class__ - diff1 = cls(self).difference(other) - diff2 = cls(other).difference(self) - return diff1.union(diff2) - - def _update_items(self, items: list) -> None: - """ - Replace the 'items' list of this OrderedSet with a new one, updating - self.map accordingly. - """ - self.items = items - self.map = {item: idx for (idx, item) in enumerate(items)} - - def difference_update(self, *sets: SetLike[T]) -> None: - """ - Update this OrderedSet to remove items from one or more other sets. - - Example: - >>> this = OrderedSet([1, 2, 3]) - >>> this.difference_update(OrderedSet([2, 4])) - >>> print(this) - OrderedSet([1, 3]) - - >>> this = OrderedSet([1, 2, 3, 4, 5]) - >>> this.difference_update(OrderedSet([2, 4]), OrderedSet([1, 4, 6])) - >>> print(this) - OrderedSet([3, 5]) - """ - items_to_remove = set() # type: Set[T] - for other in sets: - items_as_set = set(other) # type: Set[T] - items_to_remove |= items_as_set - self._update_items([item for item in self.items if item not in items_to_remove]) - - def intersection_update(self, other: SetLike[T]) -> None: - """ - Update this OrderedSet to keep only items in another set, preserving - their order in this set. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.intersection_update(other) - >>> print(this) - OrderedSet([1, 3, 7]) - """ - other = set(other) - self._update_items([item for item in self.items if item in other]) - - def symmetric_difference_update(self, other: SetLike[T]) -> None: - """ - Update this OrderedSet to remove items from another set, then - add items from the other set that were not present in this set. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.symmetric_difference_update(other) - >>> print(this) - OrderedSet([4, 5, 9, 2]) - """ - items_to_add = [item for item in other if item not in self] - items_to_remove = set(other) - self._update_items( - [item for item in self.items if item not in items_to_remove] + items_to_add - ) diff --git a/contrib/python/ordered-set/ordered_set/py.typed b/contrib/python/ordered-set/ordered_set/py.typed deleted file mode 100644 index e69de29bb2..0000000000 --- a/contrib/python/ordered-set/ordered_set/py.typed +++ /dev/null diff --git a/contrib/python/ordered-set/ya.make b/contrib/python/ordered-set/ya.make deleted file mode 100644 index 2d052f4c9f..0000000000 --- a/contrib/python/ordered-set/ya.make +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by devtools/yamaker (pypi). - -PY3_LIBRARY() - -VERSION(4.1.0) - -LICENSE(MIT) - -NO_LINT() - -PY_SRCS( - TOP_LEVEL - ordered_set/__init__.py -) - -RESOURCE_FILES( - PREFIX contrib/python/ordered-set/ - .dist-info/METADATA - .dist-info/top_level.txt - ordered_set/py.typed -) - -END() diff --git a/contrib/python/setuptools/py3/.dist-info/METADATA b/contrib/python/setuptools/py3/.dist-info/METADATA index 55b94d8d5a..11cc0cda4e 100644 --- a/contrib/python/setuptools/py3/.dist-info/METADATA +++ b/contrib/python/setuptools/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: setuptools -Version: 71.1.0 +Version: 74.1.3 Summary: Easily download, build, install, upgrade, and uninstall Python packages Author-email: Python Packaging Authority <distutils-sig@python.org> Project-URL: Source, https://github.com/pypa/setuptools @@ -20,9 +20,12 @@ Requires-Python: >=3.8 Description-Content-Type: text/x-rst License-File: LICENSE Provides-Extra: certs +Provides-Extra: check +Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'check' +Requires-Dist: pytest-ruff >=0.2.1 ; (sys_platform != "cygwin") and extra == 'check' +Requires-Dist: ruff >=0.5.2 ; (sys_platform != "cygwin") and extra == 'check' Provides-Extra: core Requires-Dist: packaging >=24 ; extra == 'core' -Requires-Dist: ordered-set >=3.1.1 ; extra == 'core' Requires-Dist: more-itertools >=8.8 ; extra == 'core' Requires-Dist: jaraco.text >=3.7 ; extra == 'core' Requires-Dist: wheel >=0.43.0 ; extra == 'core' @@ -30,6 +33,8 @@ Requires-Dist: platformdirs >=2.6.2 ; extra == 'core' Requires-Dist: importlib-metadata >=6 ; (python_version < "3.10") and extra == 'core' Requires-Dist: tomli >=2.0.1 ; (python_version < "3.11") and extra == 'core' Requires-Dist: importlib-resources >=5.10.2 ; (python_version < "3.9") and extra == 'core' +Provides-Extra: cover +Requires-Dist: pytest-cov ; extra == 'cover' Provides-Extra: doc Requires-Dist: sphinx >=3.5 ; extra == 'doc' Requires-Dist: jaraco.packaging >=9.3 ; extra == 'doc' @@ -44,15 +49,14 @@ Requires-Dist: sphinx-reredirects ; extra == 'doc' Requires-Dist: sphinxcontrib-towncrier ; extra == 'doc' Requires-Dist: sphinx-notfound-page <2,>=1 ; extra == 'doc' Requires-Dist: pyproject-hooks !=1.1 ; extra == 'doc' +Requires-Dist: towncrier <24.7 ; extra == 'doc' +Provides-Extra: enabler +Requires-Dist: pytest-enabler >=2.2 ; extra == 'enabler' Provides-Extra: ssl Provides-Extra: test Requires-Dist: pytest !=8.1.*,>=6 ; extra == 'test' -Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'test' -Requires-Dist: pytest-cov ; extra == 'test' -Requires-Dist: pytest-mypy ; extra == 'test' -Requires-Dist: pytest-enabler >=2.2 ; extra == 'test' Requires-Dist: virtualenv >=13.0.0 ; extra == 'test' -Requires-Dist: wheel ; extra == 'test' +Requires-Dist: wheel >=0.44.0 ; extra == 'test' Requires-Dist: pip >=19.1 ; extra == 'test' Requires-Dist: packaging >=23.2 ; extra == 'test' Requires-Dist: jaraco.envs >=2.2 ; extra == 'test' @@ -64,17 +68,16 @@ Requires-Dist: ini2toml[lite] >=0.14 ; extra == 'test' Requires-Dist: tomli-w >=1.0.0 ; extra == 'test' Requires-Dist: pytest-timeout ; extra == 'test' Requires-Dist: pytest-home >=0.5 ; extra == 'test' -Requires-Dist: mypy ==1.11.* ; extra == 'test' -Requires-Dist: tomli ; extra == 'test' -Requires-Dist: importlib-metadata ; extra == 'test' Requires-Dist: pytest-subprocess ; extra == 'test' Requires-Dist: pyproject-hooks !=1.1 ; extra == 'test' Requires-Dist: jaraco.test ; extra == 'test' -Requires-Dist: pytest-ruff <0.4 ; (platform_system == "Windows") and extra == 'test' Requires-Dist: jaraco.develop >=7.21 ; (python_version >= "3.9" and sys_platform != "cygwin") and extra == 'test' -Requires-Dist: pytest-ruff >=0.2.1 ; (sys_platform != "cygwin") and extra == 'test' Requires-Dist: pytest-perf ; (sys_platform != "cygwin") and extra == 'test' -Requires-Dist: pytest-ruff >=0.3.2 ; (sys_platform != "cygwin") and extra == 'test' +Provides-Extra: type +Requires-Dist: pytest-mypy ; extra == 'type' +Requires-Dist: mypy ==1.11.* ; extra == 'type' +Requires-Dist: importlib-metadata >=7.0.2 ; (python_version < "3.10") and extra == 'type' +Requires-Dist: jaraco.develop >=7.21 ; (sys_platform != "cygwin") and extra == 'type' .. |pypi-version| image:: https://img.shields.io/pypi/v/setuptools.svg :target: https://pypi.org/project/setuptools diff --git a/contrib/python/setuptools/py3/.dist-info/entry_points.txt b/contrib/python/setuptools/py3/.dist-info/entry_points.txt index 84d34dadf1..abc3b7833c 100644 --- a/contrib/python/setuptools/py3/.dist-info/entry_points.txt +++ b/contrib/python/setuptools/py3/.dist-info/entry_points.txt @@ -20,7 +20,6 @@ rotate = setuptools.command.rotate:rotate saveopts = setuptools.command.saveopts:saveopts sdist = setuptools.command.sdist:sdist setopt = setuptools.command.setopt:setopt -test = setuptools.command.test:test upload_docs = setuptools.command.upload_docs:upload_docs [distutils.setup_keywords] @@ -36,10 +35,6 @@ package_data = setuptools.dist:check_package_data packages = setuptools.dist:check_packages python_requires = setuptools.dist:check_specifier setup_requires = setuptools.dist:check_requirements -test_loader = setuptools.dist:check_importable -test_runner = setuptools.dist:check_importable -test_suite = setuptools.dist:check_test_suite -tests_require = setuptools.dist:check_requirements use_2to3 = setuptools.dist:invalid_unless_false zip_safe = setuptools.dist:assert_bool diff --git a/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml b/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml index ffe47f851a..4ac46cc577 100644 --- a/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml +++ b/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml @@ -2,7 +2,6 @@ requirements: - jaraco.context - jaraco.functools - jaraco.text - - ordered-set - more-itertools - packaging - platformdirs diff --git a/contrib/python/setuptools/py3/_distutils_hack/__init__.py b/contrib/python/setuptools/py3/_distutils_hack/__init__.py index 35ab5cad49..30ac3a7403 100644 --- a/contrib/python/setuptools/py3/_distutils_hack/__init__.py +++ b/contrib/python/setuptools/py3/_distutils_hack/__init__.py @@ -1,7 +1,6 @@ # don't import any costly modules -import sys import os - +import sys report_url = ( "https://github.com/pypa/setuptools/issues/new?" @@ -218,10 +217,10 @@ def add_shim(): class shim: - def __enter__(self): + def __enter__(self) -> None: insert_shim() - def __exit__(self, exc, value, tb): + def __exit__(self, exc: object, value: object, tb: object) -> None: _remove_shim() diff --git a/contrib/python/setuptools/py3/pkg_resources/__init__.py b/contrib/python/setuptools/py3/pkg_resources/__init__.py index 3f3956b614..4100470fcc 100644 --- a/contrib/python/setuptools/py3/pkg_resources/__init__.py +++ b/contrib/python/setuptools/py3/pkg_resources/__init__.py @@ -1,6 +1,3 @@ -# TODO: Add Generic type annotations to initialized collections. -# For now we'd simply use implicit Any/Unknown which would add redundant annotations -# mypy: disable-error-code="var-annotated" """ Package resource API -------------------- @@ -27,62 +24,60 @@ import sys if sys.version_info < (3, 8): # noqa: UP036 # Check for unsupported versions raise RuntimeError("Python 3.8 or later is required") -import os +import _imp +import collections +import email.parser +import errno +import functools +import importlib +import importlib.abc +import importlib.machinery +import inspect import io -import time +import ntpath +import operator +import os +import pkgutil +import platform +import plistlib +import posixpath import re +import stat +import tempfile +import textwrap +import time import types +import warnings +import zipfile +import zipimport +from pkgutil import get_importer from typing import ( + TYPE_CHECKING, Any, BinaryIO, - Literal, + Callable, Dict, + Iterable, Iterator, + Literal, Mapping, MutableSequence, NamedTuple, NoReturn, - Tuple, - Union, - TYPE_CHECKING, Protocol, - Callable, - Iterable, + Tuple, TypeVar, + Union, overload, ) -import zipfile -import zipimport -import warnings -import stat -import functools -import pkgutil -import operator -import platform -import collections -import plistlib -import email.parser -import errno -import tempfile -import textwrap -import inspect -import ntpath -import posixpath -import importlib -import importlib.abc -import importlib.machinery -from pkgutil import get_importer - -import _imp sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path]) # fmt: skip # workaround for #4476 sys.modules.pop('backports', None) # capture these to bypass sandboxing -from os import utime -from os import open as os_open -from os.path import isdir, split +from os import open as os_open, utime # isort: skip +from os.path import isdir, split # isort: skip try: from os import mkdir, rename, unlink @@ -92,21 +87,18 @@ except ImportError: # no write support, probably under GAE WRITE_SUPPORT = False +import packaging.markers +import packaging.requirements import packaging.specifiers -from jaraco.text import ( - yield_lines, - drop_comment, - join_continuation, -) -from packaging import markers as _packaging_markers -from packaging import requirements as _packaging_requirements -from packaging import utils as _packaging_utils -from packaging import version as _packaging_version +import packaging.utils +import packaging.version +from jaraco.text import drop_comment, join_continuation, yield_lines from platformdirs import user_cache_dir as _user_cache_dir if TYPE_CHECKING: - from _typeshed import BytesPath, StrPath, StrOrBytesPath - from typing_extensions import Self + from _typeshed import BytesPath, StrOrBytesPath, StrPath + from _typeshed.importlib import LoaderProtocol + from typing_extensions import Self, TypeAlias warnings.warn( "pkg_resources is deprecated as an API. " @@ -118,30 +110,25 @@ warnings.warn( _T = TypeVar("_T") _DistributionT = TypeVar("_DistributionT", bound="Distribution") # Type aliases -_NestedStr = Union[str, Iterable[Union[str, Iterable["_NestedStr"]]]] -_InstallerTypeT = Callable[["Requirement"], "_DistributionT"] -_InstallerType = Callable[["Requirement"], Union["Distribution", None]] -_PkgReqType = Union[str, "Requirement"] -_EPDistType = Union["Distribution", _PkgReqType] -_MetadataType = Union["IResourceProvider", None] -_ResolvedEntryPoint = Any # Can be any attribute in the module -_ResourceStream = Any # TODO / Incomplete: A readable file-like object +_NestedStr: TypeAlias = Union[str, Iterable[Union[str, Iterable["_NestedStr"]]]] +_StrictInstallerType: TypeAlias = Callable[["Requirement"], "_DistributionT"] +_InstallerType: TypeAlias = Callable[["Requirement"], Union["Distribution", None]] +_PkgReqType: TypeAlias = Union[str, "Requirement"] +_EPDistType: TypeAlias = Union["Distribution", _PkgReqType] +_MetadataType: TypeAlias = Union["IResourceProvider", None] +_ResolvedEntryPoint: TypeAlias = Any # Can be any attribute in the module +_ResourceStream: TypeAlias = Any # TODO / Incomplete: A readable file-like object # Any object works, but let's indicate we expect something like a module (optionally has __loader__ or __file__) -_ModuleLike = Union[object, types.ModuleType] +_ModuleLike: TypeAlias = Union[object, types.ModuleType] # Any: Should be _ModuleLike but we end up with issues where _ModuleLike doesn't have _ZipLoaderModule's __loader__ -_ProviderFactoryType = Callable[[Any], "IResourceProvider"] -_DistFinderType = Callable[[_T, str, bool], Iterable["Distribution"]] -_NSHandlerType = Callable[[_T, str, str, types.ModuleType], Union[str, None]] +_ProviderFactoryType: TypeAlias = Callable[[Any], "IResourceProvider"] +_DistFinderType: TypeAlias = Callable[[_T, str, bool], Iterable["Distribution"]] +_NSHandlerType: TypeAlias = Callable[[_T, str, str, types.ModuleType], Union[str, None]] _AdapterT = TypeVar( "_AdapterT", _DistFinderType[Any], _ProviderFactoryType, _NSHandlerType[Any] ) -# Use _typeshed.importlib.LoaderProtocol once available https://github.com/python/typeshed/pull/11890 -class _LoaderProtocol(Protocol): - def load_module(self, fullname: str, /) -> types.ModuleType: ... - - class _ZipLoaderModule(Protocol): __loader__: zipimport.zipimporter @@ -156,7 +143,7 @@ class PEP440Warning(RuntimeWarning): """ -parse_version = _packaging_version.Version +parse_version = packaging.version.Version _state_vars: dict[str, str] = {} @@ -314,7 +301,7 @@ __all__ = [ class ResolutionError(Exception): """Abstract base for dependency resolution errors""" - def __repr__(self): + def __repr__(self) -> str: return self.__class__.__name__ + repr(self.args) @@ -390,7 +377,7 @@ class DistributionNotFound(ResolutionError): def report(self): return self._template.format(**locals()) - def __str__(self): + def __str__(self) -> str: return self.report() @@ -574,24 +561,30 @@ def get_entry_info(dist: _EPDistType, group: str, name: str) -> EntryPoint | Non class IMetadataProvider(Protocol): def has_metadata(self, name: str) -> bool: """Does the package's distribution contain the named metadata?""" + ... def get_metadata(self, name: str) -> str: """The named metadata resource as a string""" + ... def get_metadata_lines(self, name: str) -> Iterator[str]: """Yield named metadata resource as list of non-blank non-comment lines Leading and trailing whitespace is stripped from each line, and lines with ``#`` as the first non-blank character are omitted.""" + ... def metadata_isdir(self, name: str) -> bool: """Is the named metadata a directory? (like ``os.path.isdir()``)""" + ... def metadata_listdir(self, name: str) -> list[str]: """List of metadata names in the directory (like ``os.listdir()``)""" + ... def run_script(self, script_name: str, namespace: dict[str, Any]) -> None: """Execute the named script in the supplied namespace dictionary""" + ... class IResourceProvider(IMetadataProvider, Protocol): @@ -603,6 +596,7 @@ class IResourceProvider(IMetadataProvider, Protocol): """Return a true filesystem path for `resource_name` `manager` must be a ``ResourceManager``""" + ... def get_resource_stream( self, manager: ResourceManager, resource_name: str @@ -610,6 +604,7 @@ class IResourceProvider(IMetadataProvider, Protocol): """Return a readable file-like object for `resource_name` `manager` must be a ``ResourceManager``""" + ... def get_resource_string( self, manager: ResourceManager, resource_name: str @@ -617,27 +612,31 @@ class IResourceProvider(IMetadataProvider, Protocol): """Return the contents of `resource_name` as :obj:`bytes` `manager` must be a ``ResourceManager``""" + ... def has_resource(self, resource_name: str) -> bool: """Does the package contain the named resource?""" + ... def resource_isdir(self, resource_name: str) -> bool: """Is the named resource a directory? (like ``os.path.isdir()``)""" + ... def resource_listdir(self, resource_name: str) -> list[str]: """List of resource names in the directory (like ``os.listdir()``)""" + ... class WorkingSet: """A collection of active distributions on sys.path (or a similar list)""" - def __init__(self, entries: Iterable[str] | None = None): + def __init__(self, entries: Iterable[str] | None = None) -> None: """Create working set from list of path entries (default=sys.path)""" self.entries: list[str] = [] - self.entry_keys = {} - self.by_key = {} - self.normalized_to_canonical_keys = {} - self.callbacks = [] + self.entry_keys: dict[str | None, list[str]] = {} + self.by_key: dict[str, Distribution] = {} + self.normalized_to_canonical_keys: dict[str, str] = {} + self.callbacks: list[Callable[[Distribution], object]] = [] if entries is None: entries = sys.path @@ -801,7 +800,7 @@ class WorkingSet: return self.by_key[dist.key] = dist - normalized_name = _packaging_utils.canonicalize_name(dist.key) + normalized_name = packaging.utils.canonicalize_name(dist.key) self.normalized_to_canonical_keys[normalized_name] = dist.key if dist.key not in keys: keys.append(dist.key) @@ -814,7 +813,7 @@ class WorkingSet: self, requirements: Iterable[Requirement], env: Environment | None, - installer: _InstallerTypeT[_DistributionT], + installer: _StrictInstallerType[_DistributionT], replace_conflicting: bool = False, extras: tuple[str, ...] | None = None, ) -> list[_DistributionT]: ... @@ -824,7 +823,7 @@ class WorkingSet: requirements: Iterable[Requirement], env: Environment | None = None, *, - installer: _InstallerTypeT[_DistributionT], + installer: _StrictInstallerType[_DistributionT], replace_conflicting: bool = False, extras: tuple[str, ...] | None = None, ) -> list[_DistributionT]: ... @@ -841,7 +840,7 @@ class WorkingSet: self, requirements: Iterable[Requirement], env: Environment | None = None, - installer: _InstallerType | None | _InstallerTypeT[_DistributionT] = None, + installer: _InstallerType | None | _StrictInstallerType[_DistributionT] = None, replace_conflicting: bool = False, extras: tuple[str, ...] | None = None, ) -> list[Distribution] | list[_DistributionT]: @@ -874,14 +873,16 @@ class WorkingSet: # set of processed requirements processed = set() # key -> dist - best = {} - to_activate = [] + best: dict[str, Distribution] = {} + to_activate: list[Distribution] = [] req_extras = _ReqExtras() # Mapping of requirement to set of distributions that required it; # useful for reporting info about conflicts. - required_by = collections.defaultdict(set) + required_by: collections.defaultdict[Requirement, set[str]] = ( + collections.defaultdict(set) + ) while requirements: # process dependencies breadth-first @@ -947,7 +948,7 @@ class WorkingSet: self, plugin_env: Environment, full_env: Environment | None, - installer: _InstallerTypeT[_DistributionT], + installer: _StrictInstallerType[_DistributionT], fallback: bool = True, ) -> tuple[list[_DistributionT], dict[Distribution, Exception]]: ... @overload @@ -956,7 +957,7 @@ class WorkingSet: plugin_env: Environment, full_env: Environment | None = None, *, - installer: _InstallerTypeT[_DistributionT], + installer: _StrictInstallerType[_DistributionT], fallback: bool = True, ) -> tuple[list[_DistributionT], dict[Distribution, Exception]]: ... @overload @@ -971,7 +972,7 @@ class WorkingSet: self, plugin_env: Environment, full_env: Environment | None = None, - installer: _InstallerType | None | _InstallerTypeT[_DistributionT] = None, + installer: _InstallerType | None | _StrictInstallerType[_DistributionT] = None, fallback: bool = True, ) -> tuple[ list[Distribution] | list[_DistributionT], @@ -1093,7 +1094,15 @@ class WorkingSet: for callback in self.callbacks: callback(dist) - def __getstate__(self): + def __getstate__( + self, + ) -> tuple[ + list[str], + dict[str | None, list[str]], + dict[str, Distribution], + dict[str, str], + list[Callable[[Distribution], object]], + ]: return ( self.entries[:], self.entry_keys.copy(), @@ -1102,7 +1111,7 @@ class WorkingSet: self.callbacks[:], ) - def __setstate__(self, e_k_b_n_c): + def __setstate__(self, e_k_b_n_c) -> None: entries, keys, by_key, normalized_to_canonical_keys, callbacks = e_k_b_n_c self.entries = entries[:] self.entry_keys = keys.copy() @@ -1138,7 +1147,7 @@ class Environment: search_path: Iterable[str] | None = None, platform: str | None = get_supported_platform(), python: str | None = PY_MAJOR, - ): + ) -> None: """Snapshot distributions available on a search path Any distributions found on `search_path` are added to the environment. @@ -1155,7 +1164,7 @@ class Environment: wish to map *all* distributions, not just those compatible with the running platform or Python version. """ - self._distmap = {} + self._distmap: dict[str, list[Distribution]] = {} self.platform = platform self.python = python self.scan(search_path) @@ -1217,7 +1226,7 @@ class Environment: self, req: Requirement, working_set: WorkingSet, - installer: _InstallerTypeT[_DistributionT], + installer: _StrictInstallerType[_DistributionT], replace_conflicting: bool = False, ) -> _DistributionT: ... @overload @@ -1232,7 +1241,7 @@ class Environment: self, req: Requirement, working_set: WorkingSet, - installer: _InstallerType | None | _InstallerTypeT[_DistributionT] = None, + installer: _InstallerType | None | _StrictInstallerType[_DistributionT] = None, replace_conflicting: bool = False, ) -> Distribution | None: """Find distribution best matching `req` and usable on `working_set` @@ -1265,7 +1274,7 @@ class Environment: def obtain( self, requirement: Requirement, - installer: _InstallerTypeT[_DistributionT], + installer: _StrictInstallerType[_DistributionT], ) -> _DistributionT: ... @overload def obtain( @@ -1285,7 +1294,7 @@ class Environment: installer: Callable[[Requirement], None] | _InstallerType | None - | _InstallerTypeT[_DistributionT] = None, + | _StrictInstallerType[_DistributionT] = None, ) -> Distribution | None: """Obtain a distribution matching `requirement` (e.g. via download) @@ -1303,7 +1312,7 @@ class Environment: if self[key]: yield key - def __iadd__(self, other: Distribution | Environment): + def __iadd__(self, other: Distribution | Environment) -> Self: """In-place addition of a distribution or environment""" if isinstance(other, Distribution): self.add(other) @@ -1315,7 +1324,7 @@ class Environment: raise TypeError("Can't add %r to environment" % (other,)) return self - def __add__(self, other: Distribution | Environment): + def __add__(self, other: Distribution | Environment) -> Self: """Add an environment or distribution to an environment""" new = self.__class__([], platform=None, python=None) for env in self, other: @@ -1352,8 +1361,9 @@ class ResourceManager: extraction_path: str | None = None - def __init__(self): - self.cached_files = {} + def __init__(self) -> None: + # acts like a set + self.cached_files: dict[str, Literal[True]] = {} def resource_exists( self, package_or_requirement: _PkgReqType, resource_name: str @@ -1561,8 +1571,8 @@ def safe_version(version: str) -> str: """ try: # normalize the version - return str(_packaging_version.Version(version)) - except _packaging_version.InvalidVersion: + return str(packaging.version.Version(version)) + except packaging.version.InvalidVersion: version = version.replace(' ', '.') return re.sub('[^A-Za-z0-9.]+', '-', version) @@ -1639,9 +1649,9 @@ def evaluate_marker(text: str, extra: str | None = None) -> bool: This implementation uses the 'pyparsing' module. """ try: - marker = _packaging_markers.Marker(text) + marker = packaging.markers.Marker(text) return marker.evaluate() - except _packaging_markers.InvalidMarker as e: + except packaging.markers.InvalidMarker as e: raise SyntaxError(e) from e @@ -1650,9 +1660,9 @@ class NullProvider: egg_name: str | None = None egg_info: str | None = None - loader: _LoaderProtocol | None = None + loader: LoaderProtocol | None = None - def __init__(self, module: _ModuleLike): + def __init__(self, module: _ModuleLike) -> None: self.loader = getattr(module, '__loader__', None) self.module_path = os.path.dirname(getattr(module, '__file__', '')) @@ -1869,7 +1879,7 @@ def _parents(path): class EggProvider(NullProvider): """Provider based on a virtual filesystem""" - def __init__(self, module: _ModuleLike): + def __init__(self, module: _ModuleLike) -> None: super().__init__(module) self._setup_prefix() @@ -1935,7 +1945,7 @@ class EmptyProvider(NullProvider): def _listdir(self, path): return [] - def __init__(self): + def __init__(self) -> None: pass @@ -2001,7 +2011,7 @@ class ZipProvider(EggProvider): # ZipProvider's loader should always be a zipimporter or equivalent loader: zipimport.zipimporter - def __init__(self, module: _ZipLoaderModule): + def __init__(self, module: _ZipLoaderModule) -> None: super().__init__(module) self.zip_pre = self.loader.archive + os.sep @@ -2180,7 +2190,7 @@ class FileMetadata(EmptyProvider): the provided location. """ - def __init__(self, path: StrPath): + def __init__(self, path: StrPath) -> None: self.path = path def _get_metadata_path(self, name): @@ -2229,7 +2239,7 @@ class PathMetadata(DefaultProvider): dist = Distribution.from_filename(egg_path, metadata=metadata) """ - def __init__(self, path: str, egg_info: str): + def __init__(self, path: str, egg_info: str) -> None: self.module_path = path self.egg_info = egg_info @@ -2237,7 +2247,7 @@ class PathMetadata(DefaultProvider): class EggMetadata(ZipProvider): """Metadata provider for .egg files""" - def __init__(self, importer: zipimport.zipimporter): + def __init__(self, importer: zipimport.zipimporter) -> None: """Create a metadata provider from a zipimporter""" self.zip_pre = importer.archive + os.sep @@ -2361,10 +2371,10 @@ class NoDists: [] """ - def __bool__(self): + def __bool__(self) -> Literal[False]: return False - def __call__(self, fullpath): + def __call__(self, fullpath: object): return iter(()) @@ -2714,7 +2724,7 @@ class EntryPoint: attrs: Iterable[str] = (), extras: Iterable[str] = (), dist: Distribution | None = None, - ): + ) -> None: if not MODULE(module_name): raise ValueError("Invalid module name", module_name) self.name = name @@ -2723,7 +2733,7 @@ class EntryPoint: self.extras = tuple(extras) self.dist = dist - def __str__(self): + def __str__(self) -> str: s = "%s = %s" % (self.name, self.module_name) if self.attrs: s += ':' + '.'.join(self.attrs) @@ -2731,7 +2741,7 @@ class EntryPoint: s += ' [%s]' % ','.join(self.extras) return s - def __repr__(self): + def __repr__(self) -> str: return "EntryPoint.parse(%r)" % str(self) @overload @@ -2908,7 +2918,7 @@ class Distribution: py_version: str | None = PY_MAJOR, platform: str | None = None, precedence: int = EGG_DIST, - ): + ) -> None: self.project_name = safe_name(project_name or 'Unknown') if version is not None: self._version = safe_version(version) @@ -2960,28 +2970,28 @@ class Distribution: self.platform or '', ) - def __hash__(self): + def __hash__(self) -> int: return hash(self.hashcmp) - def __lt__(self, other: Distribution): + def __lt__(self, other: Distribution) -> bool: return self.hashcmp < other.hashcmp - def __le__(self, other: Distribution): + def __le__(self, other: Distribution) -> bool: return self.hashcmp <= other.hashcmp - def __gt__(self, other: Distribution): + def __gt__(self, other: Distribution) -> bool: return self.hashcmp > other.hashcmp - def __ge__(self, other: Distribution): + def __ge__(self, other: Distribution) -> bool: return self.hashcmp >= other.hashcmp - def __eq__(self, other: object): + def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): # It's not a Distribution, so they are not equal return False return self.hashcmp == other.hashcmp - def __ne__(self, other: object): + def __ne__(self, other: object) -> bool: return not self == other # These properties have to be lazy so that we don't have to load any @@ -3001,12 +3011,12 @@ class Distribution: if not hasattr(self, "_parsed_version"): try: self._parsed_version = parse_version(self.version) - except _packaging_version.InvalidVersion as ex: + except packaging.version.InvalidVersion as ex: info = f"(package: {self.project_name})" if hasattr(ex, "add_note"): ex.add_note(info) # PEP 678 raise - raise _packaging_version.InvalidVersion(f"{str(ex)} {info}") from None + raise packaging.version.InvalidVersion(f"{str(ex)} {info}") from None return self._parsed_version @@ -3014,7 +3024,7 @@ class Distribution: def _forgiving_parsed_version(self): try: return self.parsed_version - except _packaging_version.InvalidVersion as ex: + except packaging.version.InvalidVersion as ex: self._parsed_version = parse_version(_forgiving_version(self.version)) notes = "\n".join(getattr(ex, "__notes__", [])) # PEP 678 @@ -3155,13 +3165,13 @@ class Distribution: filename += '-' + self.platform return filename - def __repr__(self): + def __repr__(self) -> str: if self.location: return "%s (%s)" % (self, self.location) else: return str(self) - def __str__(self): + def __str__(self) -> str: try: version = getattr(self, 'version', None) except ValueError: @@ -3169,7 +3179,7 @@ class Distribution: version = version or "[unknown version]" return "%s %s" % (self.project_name, version) - def __getattr__(self, attr): + def __getattr__(self, attr: str): """Delegate all unrecognized public attributes to .metadata provider""" if attr.startswith('_'): raise AttributeError(attr) @@ -3194,7 +3204,7 @@ class Distribution: def as_requirement(self): """Return a ``Requirement`` that matches this distribution exactly""" - if isinstance(self.parsed_version, _packaging_version.Version): + if isinstance(self.parsed_version, packaging.version.Version): spec = "%s==%s" % (self.project_name, self.parsed_version) else: spec = "%s===%s" % (self.project_name, self.parsed_version) @@ -3452,16 +3462,16 @@ def parse_requirements(strs: _NestedStr) -> map[Requirement]: return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs)))) -class RequirementParseError(_packaging_requirements.InvalidRequirement): +class RequirementParseError(packaging.requirements.InvalidRequirement): "Compatibility wrapper for InvalidRequirement" -class Requirement(_packaging_requirements.Requirement): +class Requirement(packaging.requirements.Requirement): # prefer variable length tuple to set (as found in # packaging.requirements.Requirement) extras: tuple[str, ...] # type: ignore[assignment] - def __init__(self, requirement_string: str): + def __init__(self, requirement_string: str) -> None: """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" super().__init__(requirement_string) self.unsafe_name = self.name @@ -3478,10 +3488,10 @@ class Requirement(_packaging_requirements.Requirement): ) self.__hash = hash(self.hashCmp) - def __eq__(self, other: object): + def __eq__(self, other: object) -> bool: return isinstance(other, Requirement) and self.hashCmp == other.hashCmp - def __ne__(self, other): + def __ne__(self, other: object) -> bool: return not self == other def __contains__( @@ -3503,10 +3513,10 @@ class Requirement(_packaging_requirements.Requirement): prereleases=True, ) - def __hash__(self): + def __hash__(self) -> int: return self.__hash - def __repr__(self): + def __repr__(self) -> str: return "Requirement.parse(%r)" % str(self) @staticmethod @@ -3564,7 +3574,7 @@ def split_sections(s: _NestedStr) -> Iterator[tuple[str | None, list[str]]]: header, they're returned in a first ``section`` of ``None``. """ section = None - content = [] + content: list[str] = [] for line in yield_lines(s): if line.startswith("["): if line.endswith("]"): diff --git a/contrib/python/setuptools/py3/setuptools/__init__.py b/contrib/python/setuptools/py3/setuptools/__init__.py index afca08be9c..ab373c51d6 100644 --- a/contrib/python/setuptools/py3/setuptools/__init__.py +++ b/contrib/python/setuptools/py3/setuptools/__init__.py @@ -1,27 +1,37 @@ """Extensions to the 'distutils' for large or complex distributions""" +# mypy: disable_error_code=override +# Command.reinitialize_command has an extra **kw param that distutils doesn't have +# Can't disable on the exact line because distutils doesn't exists on Python 3.12 +# and mypy isn't aware of distutils_hack, causing distutils.core.Command to be Any, +# and a [unused-ignore] to be raised on 3.12+ + +from __future__ import annotations import functools import os import re import sys -from typing import TYPE_CHECKING +from abc import abstractmethod +from collections.abc import Mapping +from typing import TYPE_CHECKING, TypeVar, overload sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path]) # fmt: skip # workaround for #4476 sys.modules.pop('backports', None) import _distutils_hack.override # noqa: F401 -import distutils.core -from distutils.errors import DistutilsOptionError from . import logging, monkey -from . import version as _version_module from .depends import Require from .discovery import PackageFinder, PEP420PackageFinder from .dist import Distribution from .extension import Extension +from .version import __version__ as __version__ from .warnings import SetuptoolsDeprecationWarning +import distutils.core +from distutils.errors import DistutilsOptionError + __all__ = [ 'setup', 'Distribution', @@ -33,11 +43,10 @@ __all__ = [ 'find_namespace_packages', ] -__version__ = _version_module.__version__ +_CommandT = TypeVar("_CommandT", bound="_Command") bootstrap_install_from = None - find_packages = PackageFinder.find find_namespace_packages = PEP420PackageFinder.find @@ -51,7 +60,7 @@ def _install_setup_requires(attrs): fetch_build_eggs interface. """ - def __init__(self, attrs): + def __init__(self, attrs: Mapping[str, object]): _incl = 'dependency_links', 'setup_requires' filtered = {k: attrs[k] for k in set(_incl) & set(attrs)} super().__init__(filtered) @@ -111,8 +120,10 @@ def setup(**attrs): setup.__doc__ = distutils.core.setup.__doc__ if TYPE_CHECKING: + from typing_extensions import TypeAlias + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 - _Command = distutils.core.Command + _Command: TypeAlias = distutils.core.Command else: _Command = monkey.get_unpatched(distutils.core.Command) @@ -130,42 +141,25 @@ class Command(_Command): When creating a new command from scratch, custom defined classes **SHOULD** inherit from ``setuptools.Command`` and implement a few mandatory methods. Between these mandatory methods, are listed: - - .. method:: initialize_options(self) - - Set or (reset) all options/attributes/caches used by the command - to their default values. Note that these values may be overwritten during - the build. - - .. method:: finalize_options(self) - - Set final values for all options/attributes used by the command. - Most of the time, each option/attribute/cache should only be set if it does not - have any value yet (e.g. ``if self.attr is None: self.attr = val``). - - .. method:: run(self) - - Execute the actions intended by the command. - (Side effects **SHOULD** only take place when ``run`` is executed, - for example, creating new files or writing to the terminal output). + :meth:`initialize_options`, :meth:`finalize_options` and :meth:`run`. A useful analogy for command classes is to think of them as subroutines with local - variables called "options". The options are "declared" in ``initialize_options()`` - and "defined" (given their final values, aka "finalized") in ``finalize_options()``, + variables called "options". The options are "declared" in :meth:`initialize_options` + and "defined" (given their final values, aka "finalized") in :meth:`finalize_options`, both of which must be defined by every command class. The "body" of the subroutine, - (where it does all the work) is the ``run()`` method. - Between ``initialize_options()`` and ``finalize_options()``, ``setuptools`` may set + (where it does all the work) is the :meth:`run` method. + Between :meth:`initialize_options` and :meth:`finalize_options`, ``setuptools`` may set the values for options/attributes based on user's input (or circumstance), which means that the implementation should be careful to not overwrite values in - ``finalize_options`` unless necessary. + :meth:`finalize_options` unless necessary. Please note that other commands (or other parts of setuptools) may also overwrite the values of the command's options/attributes multiple times during the build process. - Therefore it is important to consistently implement ``initialize_options()`` and - ``finalize_options()``. For example, all derived attributes (or attributes that + Therefore it is important to consistently implement :meth:`initialize_options` and + :meth:`finalize_options`. For example, all derived attributes (or attributes that depend on the value of other attributes) **SHOULD** be recomputed in - ``finalize_options``. + :meth:`finalize_options`. When overwriting existing commands, custom defined classes **MUST** abide by the same APIs implemented by the original class. They also **SHOULD** inherit from the @@ -221,11 +215,48 @@ class Command(_Command): "'%s' must be a list of strings (got %r)" % (option, val) ) - def reinitialize_command(self, command, reinit_subcommands=False, **kw): + @overload + def reinitialize_command( + self, command: str, reinit_subcommands: bool = False, **kw + ) -> _Command: ... + @overload + def reinitialize_command( + self, command: _CommandT, reinit_subcommands: bool = False, **kw + ) -> _CommandT: ... + def reinitialize_command( + self, command: str | _Command, reinit_subcommands: bool = False, **kw + ) -> _Command: cmd = _Command.reinitialize_command(self, command, reinit_subcommands) vars(cmd).update(kw) return cmd + @abstractmethod + def initialize_options(self) -> None: + """ + Set or (reset) all options/attributes/caches used by the command + to their default values. Note that these values may be overwritten during + the build. + """ + raise NotImplementedError + + @abstractmethod + def finalize_options(self) -> None: + """ + Set final values for all options/attributes used by the command. + Most of the time, each option/attribute/cache should only be set if it does not + have any value yet (e.g. ``if self.attr is None: self.attr = val``). + """ + raise NotImplementedError + + @abstractmethod + def run(self) -> None: + """ + Execute the actions intended by the command. + (Side effects **SHOULD** only take place when :meth:`run` is executed, + for example, creating new files or writing to the terminal output). + """ + raise NotImplementedError + def _find_all_simple(path): """ diff --git a/contrib/python/setuptools/py3/setuptools/_core_metadata.py b/contrib/python/setuptools/py3/setuptools/_core_metadata.py index 82ec19fc75..2e9c48a77b 100644 --- a/contrib/python/setuptools/py3/setuptools/_core_metadata.py +++ b/contrib/python/setuptools/py3/setuptools/_core_metadata.py @@ -13,15 +13,16 @@ from email import message_from_file from email.message import Message from tempfile import NamedTemporaryFile -from distutils.util import rfc822_escape - -from . import _normalization, _reqs from packaging.markers import Marker from packaging.requirements import Requirement from packaging.utils import canonicalize_name, canonicalize_version from packaging.version import Version + +from . import _normalization, _reqs from .warnings import SetuptoolsDeprecationWarning +from distutils.util import rfc822_escape + def get_metadata_version(self): mv = getattr(self, 'metadata_version', None) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_collections.py b/contrib/python/setuptools/py3/setuptools/_distutils/_collections.py index d11a83467c..863030b3cf 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_collections.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/_collections.py @@ -1,11 +1,7 @@ from __future__ import annotations import collections -import functools import itertools -import operator -from collections.abc import Mapping -from typing import Any # from jaraco.collections 3.5.1 @@ -60,144 +56,3 @@ class DictStack(list, collections.abc.Mapping): def __len__(self): return len(list(iter(self))) - - -# from jaraco.collections 5.0.1 -class RangeMap(dict): - """ - A dictionary-like object that uses the keys as bounds for a range. - Inclusion of the value for that range is determined by the - key_match_comparator, which defaults to less-than-or-equal. - A value is returned for a key if it is the first key that matches in - the sorted list of keys. - - One may supply keyword parameters to be passed to the sort function used - to sort keys (i.e. key, reverse) as sort_params. - - Create a map that maps 1-3 -> 'a', 4-6 -> 'b' - - >>> r = RangeMap({3: 'a', 6: 'b'}) # boy, that was easy - >>> r[1], r[2], r[3], r[4], r[5], r[6] - ('a', 'a', 'a', 'b', 'b', 'b') - - Even float values should work so long as the comparison operator - supports it. - - >>> r[4.5] - 'b' - - Notice that the way rangemap is defined, it must be open-ended - on one side. - - >>> r[0] - 'a' - >>> r[-1] - 'a' - - One can close the open-end of the RangeMap by using undefined_value - - >>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'}) - >>> r[0] - Traceback (most recent call last): - ... - KeyError: 0 - - One can get the first or last elements in the range by using RangeMap.Item - - >>> last_item = RangeMap.Item(-1) - >>> r[last_item] - 'b' - - .last_item is a shortcut for Item(-1) - - >>> r[RangeMap.last_item] - 'b' - - Sometimes it's useful to find the bounds for a RangeMap - - >>> r.bounds() - (0, 6) - - RangeMap supports .get(key, default) - - >>> r.get(0, 'not found') - 'not found' - - >>> r.get(7, 'not found') - 'not found' - - One often wishes to define the ranges by their left-most values, - which requires use of sort params and a key_match_comparator. - - >>> r = RangeMap({1: 'a', 4: 'b'}, - ... sort_params=dict(reverse=True), - ... key_match_comparator=operator.ge) - >>> r[1], r[2], r[3], r[4], r[5], r[6] - ('a', 'a', 'a', 'b', 'b', 'b') - - That wasn't nearly as easy as before, so an alternate constructor - is provided: - - >>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value}) - >>> r[1], r[2], r[3], r[4], r[5], r[6] - ('a', 'a', 'a', 'b', 'b', 'b') - - """ - - def __init__( - self, - source, - sort_params: Mapping[str, Any] = {}, - key_match_comparator=operator.le, - ): - dict.__init__(self, source) - self.sort_params = sort_params - self.match = key_match_comparator - - @classmethod - def left(cls, source): - return cls( - source, sort_params=dict(reverse=True), key_match_comparator=operator.ge - ) - - def __getitem__(self, item): - sorted_keys = sorted(self.keys(), **self.sort_params) - if isinstance(item, RangeMap.Item): - result = self.__getitem__(sorted_keys[item]) - else: - key = self._find_first_match_(sorted_keys, item) - result = dict.__getitem__(self, key) - if result is RangeMap.undefined_value: - raise KeyError(key) - return result - - def get(self, key, default=None): - """ - Return the value for key if key is in the dictionary, else default. - If default is not given, it defaults to None, so that this method - never raises a KeyError. - """ - try: - return self[key] - except KeyError: - return default - - def _find_first_match_(self, keys, item): - is_match = functools.partial(self.match, item) - matches = list(filter(is_match, keys)) - if matches: - return matches[0] - raise KeyError(item) - - def bounds(self): - sorted_keys = sorted(self.keys(), **self.sort_params) - return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item]) - - # some special values for the RangeMap - undefined_value = type('RangeValueUndefined', (), {})() - - class Item(int): - "RangeMap Item" - - first_item = Item(0) - last_item = Item(-1) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py b/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py index 6532aa1073..b7bdaa2943 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py @@ -63,7 +63,7 @@ def newer_group(sources, target, missing='error'): return missing == 'newer' and not os.path.exists(source) ignored = os.path.exists if missing == 'ignore' else None - return any( + return not os.path.exists(target) or any( missing_as_newer(source) or _newer(source, target) for source in filter(ignored, sources) ) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py index b0322410c5..bf10ae2365 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py @@ -3,8 +3,7 @@ Contains MSVCCompiler, an implementation of the abstract CCompiler class for Microsoft Visual Studio 2015. -The module is compatible with VS 2015 and later. You can find legacy support -for older versions in distutils.msvc9compiler and distutils.msvccompiler. +This module requires VS 2015 or later. """ # Written by Perry Stoll @@ -33,7 +32,7 @@ from .errors import ( LibError, LinkError, ) -from .util import get_platform +from .util import get_host_platform, get_platform def _find_vc2015(): @@ -79,32 +78,40 @@ def _find_vc2017(): if not root: return None, None - try: - path = subprocess.check_output( - [ - os.path.join( - root, "Microsoft Visual Studio", "Installer", "vswhere.exe" - ), - "-latest", - "-prerelease", - "-requires", - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", - "installationPath", - "-products", - "*", - ], - encoding="mbcs", - errors="strict", - ).strip() - except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): - return None, None + variant = 'arm64' if get_platform() == 'win-arm64' else 'x86.x64' + suitable_components = ( + f"Microsoft.VisualStudio.Component.VC.Tools.{variant}", + "Microsoft.VisualStudio.Workload.WDExpress", + ) + + for component in suitable_components: + # Workaround for `-requiresAny` (only available on VS 2017 > 15.6) + with contextlib.suppress( + subprocess.CalledProcessError, OSError, UnicodeDecodeError + ): + path = ( + subprocess.check_output([ + os.path.join( + root, "Microsoft Visual Studio", "Installer", "vswhere.exe" + ), + "-latest", + "-prerelease", + "-requires", + component, + "-property", + "installationPath", + "-products", + "*", + ]) + .decode(encoding="mbcs", errors="strict") + .strip() + ) - path = os.path.join(path, "VC", "Auxiliary", "Build") - if os.path.isdir(path): - return 15, path + path = os.path.join(path, "VC", "Auxiliary", "Build") + if os.path.isdir(path): + return 15, path - return None, None + return None, None # no suitable component found PLAT_SPEC_TO_RUNTIME = { @@ -140,7 +147,11 @@ def _get_vc_env(plat_spec): vcvarsall, _ = _find_vcvarsall(plat_spec) if not vcvarsall: - raise DistutilsPlatformError("Unable to find vcvarsall.bat") + raise DistutilsPlatformError( + 'Microsoft Visual C++ 14.0 or greater is required. ' + 'Get it with "Microsoft C++ Build Tools": ' + 'https://visualstudio.microsoft.com/visual-cpp-build-tools/' + ) try: out = subprocess.check_output( @@ -148,7 +159,7 @@ def _get_vc_env(plat_spec): stderr=subprocess.STDOUT, ).decode('utf-16le', errors='replace') except subprocess.CalledProcessError as exc: - log.error(exc.output) + log.error(exc.output) # noqa: RUF100, TRY400 raise DistutilsPlatformError(f"Error executing {exc.cmd}") env = { @@ -178,17 +189,43 @@ def _find_exe(exe, paths=None): return exe -# A map keyed by get_platform() return values to values accepted by -# 'vcvarsall.bat'. Always cross-compile from x86 to work with the -# lighter-weight MSVC installs that do not include native 64-bit tools. -PLAT_TO_VCVARS = { +_vcvars_names = { 'win32': 'x86', - 'win-amd64': 'x86_amd64', - 'win-arm32': 'x86_arm', - 'win-arm64': 'x86_arm64', + 'win-amd64': 'amd64', + 'win-arm32': 'arm', + 'win-arm64': 'arm64', } +def _get_vcvars_spec(host_platform, platform): + """ + Given a host platform and platform, determine the spec for vcvarsall. + + Uses the native MSVC host if the host platform would need expensive + emulation for x86. + + >>> _get_vcvars_spec('win-arm64', 'win32') + 'arm64_x86' + >>> _get_vcvars_spec('win-arm64', 'win-amd64') + 'arm64_amd64' + + Otherwise, always cross-compile from x86 to work with the + lighter-weight MSVC installs that do not include native 64-bit tools. + + >>> _get_vcvars_spec('win32', 'win32') + 'x86' + >>> _get_vcvars_spec('win-arm32', 'win-arm32') + 'x86_arm' + >>> _get_vcvars_spec('win-amd64', 'win-arm64') + 'x86_arm64' + """ + if host_platform != 'win-arm64': + host_platform = 'win32' + vc_hp = _vcvars_names[host_platform] + vc_plat = _vcvars_names[platform] + return vc_hp if vc_hp == vc_plat else f'{vc_hp}_{vc_plat}' + + class MSVCCompiler(CCompiler): """Concrete class that implements an interface to Microsoft Visual C++, as defined by the CCompiler abstract class.""" @@ -242,13 +279,12 @@ class MSVCCompiler(CCompiler): if plat_name is None: plat_name = get_platform() # sanity check for platforms to prevent obscure errors later. - if plat_name not in PLAT_TO_VCVARS: + if plat_name not in _vcvars_names: raise DistutilsPlatformError( - f"--plat-name must be one of {tuple(PLAT_TO_VCVARS)}" + f"--plat-name must be one of {tuple(_vcvars_names)}" ) - # Get the vcvarsall.bat spec for the requested platform. - plat_spec = PLAT_TO_VCVARS[plat_name] + plat_spec = _get_vcvars_spec(get_host_platform(), plat_name) vc_env = _get_vc_env(plat_spec) if not vc_env: diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/__init__.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/__init__.py +++ /dev/null diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/__init__.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/__init__.py deleted file mode 100644 index e7c0aa12ca..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "24.0" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "2014 %s" % __author__ diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_elffile.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_elffile.py deleted file mode 100644 index 6fb19b30bb..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_elffile.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -ELF file parser. - -This provides a class ``ELFFile`` that parses an ELF executable in a similar -interface to ``ZipFile``. Only the read interface is implemented. - -Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca -ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html -""" - -import enum -import os -import struct -from typing import IO, Optional, Tuple - - -class ELFInvalid(ValueError): - pass - - -class EIClass(enum.IntEnum): - C32 = 1 - C64 = 2 - - -class EIData(enum.IntEnum): - Lsb = 1 - Msb = 2 - - -class EMachine(enum.IntEnum): - I386 = 3 - S390 = 22 - Arm = 40 - X8664 = 62 - AArc64 = 183 - - -class ELFFile: - """ - Representation of an ELF executable. - """ - - def __init__(self, f: IO[bytes]) -> None: - self._f = f - - try: - ident = self._read("16B") - except struct.error: - raise ELFInvalid("unable to parse identification") - magic = bytes(ident[:4]) - if magic != b"\x7fELF": - raise ELFInvalid(f"invalid magic: {magic!r}") - - self.capacity = ident[4] # Format for program header (bitness). - self.encoding = ident[5] # Data structure encoding (endianness). - - try: - # e_fmt: Format for program header. - # p_fmt: Format for section header. - # p_idx: Indexes to find p_type, p_offset, and p_filesz. - e_fmt, self._p_fmt, self._p_idx = { - (1, 1): ("<HHIIIIIHHH", "<IIIIIIII", (0, 1, 4)), # 32-bit LSB. - (1, 2): (">HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. - (2, 1): ("<HHIQQQIHHH", "<IIQQQQQQ", (0, 2, 5)), # 64-bit LSB. - (2, 2): (">HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. - }[(self.capacity, self.encoding)] - except KeyError: - raise ELFInvalid( - f"unrecognized capacity ({self.capacity}) or " - f"encoding ({self.encoding})" - ) - - try: - ( - _, - self.machine, # Architecture type. - _, - _, - self._e_phoff, # Offset of program header. - _, - self.flags, # Processor-specific flags. - _, - self._e_phentsize, # Size of section. - self._e_phnum, # Number of sections. - ) = self._read(e_fmt) - except struct.error as e: - raise ELFInvalid("unable to parse machine and section information") from e - - def _read(self, fmt: str) -> Tuple[int, ...]: - return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) - - @property - def interpreter(self) -> Optional[str]: - """ - The path recorded in the ``PT_INTERP`` section header. - """ - for index in range(self._e_phnum): - self._f.seek(self._e_phoff + self._e_phentsize * index) - try: - data = self._read(self._p_fmt) - except struct.error: - continue - if data[self._p_idx[0]] != 3: # Not PT_INTERP. - continue - self._f.seek(data[self._p_idx[1]]) - return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") - return None diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_manylinux.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_manylinux.py deleted file mode 100644 index ad62505f3f..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_manylinux.py +++ /dev/null @@ -1,260 +0,0 @@ -import collections -import contextlib -import functools -import os -import re -import sys -import warnings -from typing import Dict, Generator, Iterator, NamedTuple, Optional, Sequence, Tuple - -from ._elffile import EIClass, EIData, ELFFile, EMachine - -EF_ARM_ABIMASK = 0xFF000000 -EF_ARM_ABI_VER5 = 0x05000000 -EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - -# `os.PathLike` not a generic type until Python 3.9, so sticking with `str` -# as the type for `path` until then. -@contextlib.contextmanager -def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: - try: - with open(path, "rb") as f: - yield ELFFile(f) - except (OSError, TypeError, ValueError): - yield None - - -def _is_linux_armhf(executable: str) -> bool: - # hard-float ABI can be detected from the ELF header of the running - # process - # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - with _parse_elf(executable) as f: - return ( - f is not None - and f.capacity == EIClass.C32 - and f.encoding == EIData.Lsb - and f.machine == EMachine.Arm - and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 - and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD - ) - - -def _is_linux_i686(executable: str) -> bool: - with _parse_elf(executable) as f: - return ( - f is not None - and f.capacity == EIClass.C32 - and f.encoding == EIData.Lsb - and f.machine == EMachine.I386 - ) - - -def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: - if "armv7l" in archs: - return _is_linux_armhf(executable) - if "i686" in archs: - return _is_linux_i686(executable) - allowed_archs = { - "x86_64", - "aarch64", - "ppc64", - "ppc64le", - "s390x", - "loongarch64", - "riscv64", - } - return any(arch in allowed_archs for arch in archs) - - -# If glibc ever changes its major version, we need to know what the last -# minor version was, so we can build the complete list of all versions. -# For now, guess what the highest minor version might be, assume it will -# be 50 for testing. Once this actually happens, update the dictionary -# with the actual value. -_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) - - -class _GLibCVersion(NamedTuple): - major: int - minor: int - - -def _glibc_version_string_confstr() -> Optional[str]: - """ - Primary implementation of glibc_version_string using os.confstr. - """ - # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely - # to be broken or missing. This strategy is used in the standard library - # platform module. - # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 - try: - # Should be a string like "glibc 2.17". - version_string: Optional[str] = os.confstr("CS_GNU_LIBC_VERSION") - assert version_string is not None - _, version = version_string.rsplit() - except (AssertionError, AttributeError, OSError, ValueError): - # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... - return None - return version - - -def _glibc_version_string_ctypes() -> Optional[str]: - """ - Fallback implementation of glibc_version_string using ctypes. - """ - try: - import ctypes - except ImportError: - return None - - # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen - # manpage says, "If filename is NULL, then the returned handle is for the - # main program". This way we can let the linker do the work to figure out - # which libc our process is actually using. - # - # We must also handle the special case where the executable is not a - # dynamically linked executable. This can occur when using musl libc, - # for example. In this situation, dlopen() will error, leading to an - # OSError. Interestingly, at least in the case of musl, there is no - # errno set on the OSError. The single string argument used to construct - # OSError comes from libc itself and is therefore not portable to - # hard code here. In any case, failure to call dlopen() means we - # can proceed, so we bail on our attempt. - try: - process_namespace = ctypes.CDLL(None) - except OSError: - return None - - try: - gnu_get_libc_version = process_namespace.gnu_get_libc_version - except AttributeError: - # Symbol doesn't exist -> therefore, we are not linked to - # glibc. - return None - - # Call gnu_get_libc_version, which returns a string like "2.5" - gnu_get_libc_version.restype = ctypes.c_char_p - version_str: str = gnu_get_libc_version() - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -def _glibc_version_string() -> Optional[str]: - """Returns glibc version string, or None if not using glibc.""" - return _glibc_version_string_confstr() or _glibc_version_string_ctypes() - - -def _parse_glibc_version(version_str: str) -> Tuple[int, int]: - """Parse glibc version. - - We use a regexp instead of str.split because we want to discard any - random junk that might come after the minor version -- this might happen - in patched/forked versions of glibc (e.g. Linaro's version of glibc - uses version strings like "2.20-2014.11"). See gh-3588. - """ - m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str) - if not m: - warnings.warn( - f"Expected glibc version with 2 components major.minor," - f" got: {version_str}", - RuntimeWarning, - ) - return -1, -1 - return int(m.group("major")), int(m.group("minor")) - - -@functools.lru_cache() -def _get_glibc_version() -> Tuple[int, int]: - version_str = _glibc_version_string() - if version_str is None: - return (-1, -1) - return _parse_glibc_version(version_str) - - -# From PEP 513, PEP 600 -def _is_compatible(arch: str, version: _GLibCVersion) -> bool: - sys_glibc = _get_glibc_version() - if sys_glibc < version: - return False - # Check for presence of _manylinux module. - try: - import _manylinux - except ImportError: - return True - if hasattr(_manylinux, "manylinux_compatible"): - result = _manylinux.manylinux_compatible(version[0], version[1], arch) - if result is not None: - return bool(result) - return True - if version == _GLibCVersion(2, 5): - if hasattr(_manylinux, "manylinux1_compatible"): - return bool(_manylinux.manylinux1_compatible) - if version == _GLibCVersion(2, 12): - if hasattr(_manylinux, "manylinux2010_compatible"): - return bool(_manylinux.manylinux2010_compatible) - if version == _GLibCVersion(2, 17): - if hasattr(_manylinux, "manylinux2014_compatible"): - return bool(_manylinux.manylinux2014_compatible) - return True - - -_LEGACY_MANYLINUX_MAP = { - # CentOS 7 w/ glibc 2.17 (PEP 599) - (2, 17): "manylinux2014", - # CentOS 6 w/ glibc 2.12 (PEP 571) - (2, 12): "manylinux2010", - # CentOS 5 w/ glibc 2.5 (PEP 513) - (2, 5): "manylinux1", -} - - -def platform_tags(archs: Sequence[str]) -> Iterator[str]: - """Generate manylinux tags compatible to the current platform. - - :param archs: Sequence of compatible architectures. - The first one shall be the closest to the actual architecture and be the part of - platform tag after the ``linux_`` prefix, e.g. ``x86_64``. - The ``linux_`` prefix is assumed as a prerequisite for the current platform to - be manylinux-compatible. - - :returns: An iterator of compatible manylinux tags. - """ - if not _have_compatible_abi(sys.executable, archs): - return - # Oldest glibc to be supported regardless of architecture is (2, 17). - too_old_glibc2 = _GLibCVersion(2, 16) - if set(archs) & {"x86_64", "i686"}: - # On x86/i686 also oldest glibc to be supported is (2, 5). - too_old_glibc2 = _GLibCVersion(2, 4) - current_glibc = _GLibCVersion(*_get_glibc_version()) - glibc_max_list = [current_glibc] - # We can assume compatibility across glibc major versions. - # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 - # - # Build a list of maximum glibc versions so that we can - # output the canonical list of all glibc from current_glibc - # down to too_old_glibc2, including all intermediary versions. - for glibc_major in range(current_glibc.major - 1, 1, -1): - glibc_minor = _LAST_GLIBC_MINOR[glibc_major] - glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) - for arch in archs: - for glibc_max in glibc_max_list: - if glibc_max.major == too_old_glibc2.major: - min_minor = too_old_glibc2.minor - else: - # For other glibc major versions oldest supported is (x, 0). - min_minor = -1 - for glibc_minor in range(glibc_max.minor, min_minor, -1): - glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) - tag = "manylinux_{}_{}".format(*glibc_version) - if _is_compatible(arch, glibc_version): - yield f"{tag}_{arch}" - # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. - if glibc_version in _LEGACY_MANYLINUX_MAP: - legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] - if _is_compatible(arch, glibc_version): - yield f"{legacy_tag}_{arch}" diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_musllinux.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_musllinux.py deleted file mode 100644 index 86419df9d7..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_musllinux.py +++ /dev/null @@ -1,83 +0,0 @@ -"""PEP 656 support. - -This module implements logic to detect if the currently running Python is -linked against musl, and what musl version is used. -""" - -import functools -import re -import subprocess -import sys -from typing import Iterator, NamedTuple, Optional, Sequence - -from ._elffile import ELFFile - - -class _MuslVersion(NamedTuple): - major: int - minor: int - - -def _parse_musl_version(output: str) -> Optional[_MuslVersion]: - lines = [n for n in (n.strip() for n in output.splitlines()) if n] - if len(lines) < 2 or lines[0][:4] != "musl": - return None - m = re.match(r"Version (\d+)\.(\d+)", lines[1]) - if not m: - return None - return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) - - -@functools.lru_cache() -def _get_musl_version(executable: str) -> Optional[_MuslVersion]: - """Detect currently-running musl runtime version. - - This is done by checking the specified executable's dynamic linking - information, and invoking the loader to parse its output for a version - string. If the loader is musl, the output would be something like:: - - musl libc (x86_64) - Version 1.2.2 - Dynamic Program Loader - """ - try: - with open(executable, "rb") as f: - ld = ELFFile(f).interpreter - except (OSError, TypeError, ValueError): - return None - if ld is None or "musl" not in ld: - return None - proc = subprocess.run([ld], stderr=subprocess.PIPE, text=True) - return _parse_musl_version(proc.stderr) - - -def platform_tags(archs: Sequence[str]) -> Iterator[str]: - """Generate musllinux tags compatible to the current platform. - - :param archs: Sequence of compatible architectures. - The first one shall be the closest to the actual architecture and be the part of - platform tag after the ``linux_`` prefix, e.g. ``x86_64``. - The ``linux_`` prefix is assumed as a prerequisite for the current platform to - be musllinux-compatible. - - :returns: An iterator of compatible musllinux tags. - """ - sys_musl = _get_musl_version(sys.executable) - if sys_musl is None: # Python not dynamically linked against musl. - return - for arch in archs: - for minor in range(sys_musl.minor, -1, -1): - yield f"musllinux_{sys_musl.major}_{minor}_{arch}" - - -if __name__ == "__main__": # pragma: no cover - import sysconfig - - plat = sysconfig.get_platform() - assert plat.startswith("linux-"), "not linux" - - print("plat:", plat) - print("musl:", _get_musl_version(sys.executable)) - print("tags:", end=" ") - for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): - print(t, end="\n ") diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_parser.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_parser.py deleted file mode 100644 index 684df75457..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_parser.py +++ /dev/null @@ -1,356 +0,0 @@ -"""Handwritten parser of dependency specifiers. - -The docstring for each __parse_* function contains ENBF-inspired grammar representing -the implementation. -""" - -import ast -from typing import Any, List, NamedTuple, Optional, Tuple, Union - -from ._tokenizer import DEFAULT_RULES, Tokenizer - - -class Node: - def __init__(self, value: str) -> None: - self.value = value - - def __str__(self) -> str: - return self.value - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}('{self}')>" - - def serialize(self) -> str: - raise NotImplementedError - - -class Variable(Node): - def serialize(self) -> str: - return str(self) - - -class Value(Node): - def serialize(self) -> str: - return f'"{self}"' - - -class Op(Node): - def serialize(self) -> str: - return str(self) - - -MarkerVar = Union[Variable, Value] -MarkerItem = Tuple[MarkerVar, Op, MarkerVar] -# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]] -# MarkerList = List[Union["MarkerList", MarkerAtom, str]] -# mypy does not support recursive type definition -# https://github.com/python/mypy/issues/731 -MarkerAtom = Any -MarkerList = List[Any] - - -class ParsedRequirement(NamedTuple): - name: str - url: str - extras: List[str] - specifier: str - marker: Optional[MarkerList] - - -# -------------------------------------------------------------------------------------- -# Recursive descent parser for dependency specifier -# -------------------------------------------------------------------------------------- -def parse_requirement(source: str) -> ParsedRequirement: - return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) - - -def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: - """ - requirement = WS? IDENTIFIER WS? extras WS? requirement_details - """ - tokenizer.consume("WS") - - name_token = tokenizer.expect( - "IDENTIFIER", expected="package name at the start of dependency specifier" - ) - name = name_token.text - tokenizer.consume("WS") - - extras = _parse_extras(tokenizer) - tokenizer.consume("WS") - - url, specifier, marker = _parse_requirement_details(tokenizer) - tokenizer.expect("END", expected="end of dependency specifier") - - return ParsedRequirement(name, url, extras, specifier, marker) - - -def _parse_requirement_details( - tokenizer: Tokenizer, -) -> Tuple[str, str, Optional[MarkerList]]: - """ - requirement_details = AT URL (WS requirement_marker?)? - | specifier WS? (requirement_marker)? - """ - - specifier = "" - url = "" - marker = None - - if tokenizer.check("AT"): - tokenizer.read() - tokenizer.consume("WS") - - url_start = tokenizer.position - url = tokenizer.expect("URL", expected="URL after @").text - if tokenizer.check("END", peek=True): - return (url, specifier, marker) - - tokenizer.expect("WS", expected="whitespace after URL") - - # The input might end after whitespace. - if tokenizer.check("END", peek=True): - return (url, specifier, marker) - - marker = _parse_requirement_marker( - tokenizer, span_start=url_start, after="URL and whitespace" - ) - else: - specifier_start = tokenizer.position - specifier = _parse_specifier(tokenizer) - tokenizer.consume("WS") - - if tokenizer.check("END", peek=True): - return (url, specifier, marker) - - marker = _parse_requirement_marker( - tokenizer, - span_start=specifier_start, - after=( - "version specifier" - if specifier - else "name and no valid version specifier" - ), - ) - - return (url, specifier, marker) - - -def _parse_requirement_marker( - tokenizer: Tokenizer, *, span_start: int, after: str -) -> MarkerList: - """ - requirement_marker = SEMICOLON marker WS? - """ - - if not tokenizer.check("SEMICOLON"): - tokenizer.raise_syntax_error( - f"Expected end or semicolon (after {after})", - span_start=span_start, - ) - tokenizer.read() - - marker = _parse_marker(tokenizer) - tokenizer.consume("WS") - - return marker - - -def _parse_extras(tokenizer: Tokenizer) -> List[str]: - """ - extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? - """ - if not tokenizer.check("LEFT_BRACKET", peek=True): - return [] - - with tokenizer.enclosing_tokens( - "LEFT_BRACKET", - "RIGHT_BRACKET", - around="extras", - ): - tokenizer.consume("WS") - extras = _parse_extras_list(tokenizer) - tokenizer.consume("WS") - - return extras - - -def _parse_extras_list(tokenizer: Tokenizer) -> List[str]: - """ - extras_list = identifier (wsp* ',' wsp* identifier)* - """ - extras: List[str] = [] - - if not tokenizer.check("IDENTIFIER"): - return extras - - extras.append(tokenizer.read().text) - - while True: - tokenizer.consume("WS") - if tokenizer.check("IDENTIFIER", peek=True): - tokenizer.raise_syntax_error("Expected comma between extra names") - elif not tokenizer.check("COMMA"): - break - - tokenizer.read() - tokenizer.consume("WS") - - extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") - extras.append(extra_token.text) - - return extras - - -def _parse_specifier(tokenizer: Tokenizer) -> str: - """ - specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS - | WS? version_many WS? - """ - with tokenizer.enclosing_tokens( - "LEFT_PARENTHESIS", - "RIGHT_PARENTHESIS", - around="version specifier", - ): - tokenizer.consume("WS") - parsed_specifiers = _parse_version_many(tokenizer) - tokenizer.consume("WS") - - return parsed_specifiers - - -def _parse_version_many(tokenizer: Tokenizer) -> str: - """ - version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? - """ - parsed_specifiers = "" - while tokenizer.check("SPECIFIER"): - span_start = tokenizer.position - parsed_specifiers += tokenizer.read().text - if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True): - tokenizer.raise_syntax_error( - ".* suffix can only be used with `==` or `!=` operators", - span_start=span_start, - span_end=tokenizer.position + 1, - ) - if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): - tokenizer.raise_syntax_error( - "Local version label can only be used with `==` or `!=` operators", - span_start=span_start, - span_end=tokenizer.position, - ) - tokenizer.consume("WS") - if not tokenizer.check("COMMA"): - break - parsed_specifiers += tokenizer.read().text - tokenizer.consume("WS") - - return parsed_specifiers - - -# -------------------------------------------------------------------------------------- -# Recursive descent parser for marker expression -# -------------------------------------------------------------------------------------- -def parse_marker(source: str) -> MarkerList: - return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES)) - - -def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList: - retval = _parse_marker(tokenizer) - tokenizer.expect("END", expected="end of marker expression") - return retval - - -def _parse_marker(tokenizer: Tokenizer) -> MarkerList: - """ - marker = marker_atom (BOOLOP marker_atom)+ - """ - expression = [_parse_marker_atom(tokenizer)] - while tokenizer.check("BOOLOP"): - token = tokenizer.read() - expr_right = _parse_marker_atom(tokenizer) - expression.extend((token.text, expr_right)) - return expression - - -def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: - """ - marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? - | WS? marker_item WS? - """ - - tokenizer.consume("WS") - if tokenizer.check("LEFT_PARENTHESIS", peek=True): - with tokenizer.enclosing_tokens( - "LEFT_PARENTHESIS", - "RIGHT_PARENTHESIS", - around="marker expression", - ): - tokenizer.consume("WS") - marker: MarkerAtom = _parse_marker(tokenizer) - tokenizer.consume("WS") - else: - marker = _parse_marker_item(tokenizer) - tokenizer.consume("WS") - return marker - - -def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: - """ - marker_item = WS? marker_var WS? marker_op WS? marker_var WS? - """ - tokenizer.consume("WS") - marker_var_left = _parse_marker_var(tokenizer) - tokenizer.consume("WS") - marker_op = _parse_marker_op(tokenizer) - tokenizer.consume("WS") - marker_var_right = _parse_marker_var(tokenizer) - tokenizer.consume("WS") - return (marker_var_left, marker_op, marker_var_right) - - -def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: - """ - marker_var = VARIABLE | QUOTED_STRING - """ - if tokenizer.check("VARIABLE"): - return process_env_var(tokenizer.read().text.replace(".", "_")) - elif tokenizer.check("QUOTED_STRING"): - return process_python_str(tokenizer.read().text) - else: - tokenizer.raise_syntax_error( - message="Expected a marker variable or quoted string" - ) - - -def process_env_var(env_var: str) -> Variable: - if env_var in ("platform_python_implementation", "python_implementation"): - return Variable("platform_python_implementation") - else: - return Variable(env_var) - - -def process_python_str(python_str: str) -> Value: - value = ast.literal_eval(python_str) - return Value(str(value)) - - -def _parse_marker_op(tokenizer: Tokenizer) -> Op: - """ - marker_op = IN | NOT IN | OP - """ - if tokenizer.check("IN"): - tokenizer.read() - return Op("in") - elif tokenizer.check("NOT"): - tokenizer.read() - tokenizer.expect("WS", expected="whitespace after 'not'") - tokenizer.expect("IN", expected="'in' after 'not'") - return Op("not in") - elif tokenizer.check("OP"): - return Op(tokenizer.read().text) - else: - return tokenizer.raise_syntax_error( - "Expected marker operator, one of " - "<=, <, !=, ==, >=, >, ~=, ===, in, not in" - ) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_structures.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_structures.py deleted file mode 100644 index 90a6465f96..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_structures.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -class InfinityType: - def __repr__(self) -> str: - return "Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: object) -> bool: - return False - - def __le__(self, other: object) -> bool: - return False - - def __eq__(self, other: object) -> bool: - return isinstance(other, self.__class__) - - def __gt__(self, other: object) -> bool: - return True - - def __ge__(self, other: object) -> bool: - return True - - def __neg__(self: object) -> "NegativeInfinityType": - return NegativeInfinity - - -Infinity = InfinityType() - - -class NegativeInfinityType: - def __repr__(self) -> str: - return "-Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: object) -> bool: - return True - - def __le__(self, other: object) -> bool: - return True - - def __eq__(self, other: object) -> bool: - return isinstance(other, self.__class__) - - def __gt__(self, other: object) -> bool: - return False - - def __ge__(self, other: object) -> bool: - return False - - def __neg__(self: object) -> InfinityType: - return Infinity - - -NegativeInfinity = NegativeInfinityType() diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_tokenizer.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_tokenizer.py deleted file mode 100644 index dd0d648d49..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/_tokenizer.py +++ /dev/null @@ -1,192 +0,0 @@ -import contextlib -import re -from dataclasses import dataclass -from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union - -from .specifiers import Specifier - - -@dataclass -class Token: - name: str - text: str - position: int - - -class ParserSyntaxError(Exception): - """The provided source text could not be parsed correctly.""" - - def __init__( - self, - message: str, - *, - source: str, - span: Tuple[int, int], - ) -> None: - self.span = span - self.message = message - self.source = source - - super().__init__() - - def __str__(self) -> str: - marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" - return "\n ".join([self.message, self.source, marker]) - - -DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { - "LEFT_PARENTHESIS": r"\(", - "RIGHT_PARENTHESIS": r"\)", - "LEFT_BRACKET": r"\[", - "RIGHT_BRACKET": r"\]", - "SEMICOLON": r";", - "COMMA": r",", - "QUOTED_STRING": re.compile( - r""" - ( - ('[^']*') - | - ("[^"]*") - ) - """, - re.VERBOSE, - ), - "OP": r"(===|==|~=|!=|<=|>=|<|>)", - "BOOLOP": r"\b(or|and)\b", - "IN": r"\bin\b", - "NOT": r"\bnot\b", - "VARIABLE": re.compile( - r""" - \b( - python_version - |python_full_version - |os[._]name - |sys[._]platform - |platform_(release|system) - |platform[._](version|machine|python_implementation) - |python_implementation - |implementation_(name|version) - |extra - )\b - """, - re.VERBOSE, - ), - "SPECIFIER": re.compile( - Specifier._operator_regex_str + Specifier._version_regex_str, - re.VERBOSE | re.IGNORECASE, - ), - "AT": r"\@", - "URL": r"[^ \t]+", - "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", - "VERSION_PREFIX_TRAIL": r"\.\*", - "VERSION_LOCAL_LABEL_TRAIL": r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*", - "WS": r"[ \t]+", - "END": r"$", -} - - -class Tokenizer: - """Context-sensitive token parsing. - - Provides methods to examine the input stream to check whether the next token - matches. - """ - - def __init__( - self, - source: str, - *, - rules: "Dict[str, Union[str, re.Pattern[str]]]", - ) -> None: - self.source = source - self.rules: Dict[str, re.Pattern[str]] = { - name: re.compile(pattern) for name, pattern in rules.items() - } - self.next_token: Optional[Token] = None - self.position = 0 - - def consume(self, name: str) -> None: - """Move beyond provided token name, if at current position.""" - if self.check(name): - self.read() - - def check(self, name: str, *, peek: bool = False) -> bool: - """Check whether the next token has the provided name. - - By default, if the check succeeds, the token *must* be read before - another check. If `peek` is set to `True`, the token is not loaded and - would need to be checked again. - """ - assert ( - self.next_token is None - ), f"Cannot check for {name!r}, already have {self.next_token!r}" - assert name in self.rules, f"Unknown token name: {name!r}" - - expression = self.rules[name] - - match = expression.match(self.source, self.position) - if match is None: - return False - if not peek: - self.next_token = Token(name, match[0], self.position) - return True - - def expect(self, name: str, *, expected: str) -> Token: - """Expect a certain token name next, failing with a syntax error otherwise. - - The token is *not* read. - """ - if not self.check(name): - raise self.raise_syntax_error(f"Expected {expected}") - return self.read() - - def read(self) -> Token: - """Consume the next token and return it.""" - token = self.next_token - assert token is not None - - self.position += len(token.text) - self.next_token = None - - return token - - def raise_syntax_error( - self, - message: str, - *, - span_start: Optional[int] = None, - span_end: Optional[int] = None, - ) -> NoReturn: - """Raise ParserSyntaxError at the given position.""" - span = ( - self.position if span_start is None else span_start, - self.position if span_end is None else span_end, - ) - raise ParserSyntaxError( - message, - source=self.source, - span=span, - ) - - @contextlib.contextmanager - def enclosing_tokens( - self, open_token: str, close_token: str, *, around: str - ) -> Iterator[None]: - if self.check(open_token): - open_position = self.position - self.read() - else: - open_position = None - - yield - - if open_position is None: - return - - if not self.check(close_token): - self.raise_syntax_error( - f"Expected matching {close_token} for {open_token}, after {around}", - span_start=open_position, - ) - - self.read() diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/markers.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/markers.py deleted file mode 100644 index 8b98fca723..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/markers.py +++ /dev/null @@ -1,252 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import operator -import os -import platform -import sys -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -from ._parser import ( - MarkerAtom, - MarkerList, - Op, - Value, - Variable, - parse_marker as _parse_marker, -) -from ._tokenizer import ParserSyntaxError -from .specifiers import InvalidSpecifier, Specifier -from .utils import canonicalize_name - -__all__ = [ - "InvalidMarker", - "UndefinedComparison", - "UndefinedEnvironmentName", - "Marker", - "default_environment", -] - -Operator = Callable[[str, str], bool] - - -class InvalidMarker(ValueError): - """ - An invalid marker was found, users should refer to PEP 508. - """ - - -class UndefinedComparison(ValueError): - """ - An invalid operation was attempted on a value that doesn't support it. - """ - - -class UndefinedEnvironmentName(ValueError): - """ - A name was attempted to be used that does not exist inside of the - environment. - """ - - -def _normalize_extra_values(results: Any) -> Any: - """ - Normalize extra values. - """ - if isinstance(results[0], tuple): - lhs, op, rhs = results[0] - if isinstance(lhs, Variable) and lhs.value == "extra": - normalized_extra = canonicalize_name(rhs.value) - rhs = Value(normalized_extra) - elif isinstance(rhs, Variable) and rhs.value == "extra": - normalized_extra = canonicalize_name(lhs.value) - lhs = Value(normalized_extra) - results[0] = lhs, op, rhs - return results - - -def _format_marker( - marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True -) -> str: - - assert isinstance(marker, (list, tuple, str)) - - # Sometimes we have a structure like [[...]] which is a single item list - # where the single item is itself it's own list. In that case we want skip - # the rest of this function so that we don't get extraneous () on the - # outside. - if ( - isinstance(marker, list) - and len(marker) == 1 - and isinstance(marker[0], (list, tuple)) - ): - return _format_marker(marker[0]) - - if isinstance(marker, list): - inner = (_format_marker(m, first=False) for m in marker) - if first: - return " ".join(inner) - else: - return "(" + " ".join(inner) + ")" - elif isinstance(marker, tuple): - return " ".join([m.serialize() for m in marker]) - else: - return marker - - -_operators: Dict[str, Operator] = { - "in": lambda lhs, rhs: lhs in rhs, - "not in": lambda lhs, rhs: lhs not in rhs, - "<": operator.lt, - "<=": operator.le, - "==": operator.eq, - "!=": operator.ne, - ">=": operator.ge, - ">": operator.gt, -} - - -def _eval_op(lhs: str, op: Op, rhs: str) -> bool: - try: - spec = Specifier("".join([op.serialize(), rhs])) - except InvalidSpecifier: - pass - else: - return spec.contains(lhs, prereleases=True) - - oper: Optional[Operator] = _operators.get(op.serialize()) - if oper is None: - raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") - - return oper(lhs, rhs) - - -def _normalize(*values: str, key: str) -> Tuple[str, ...]: - # PEP 685 – Comparison of extra names for optional distribution dependencies - # https://peps.python.org/pep-0685/ - # > When comparing extra names, tools MUST normalize the names being - # > compared using the semantics outlined in PEP 503 for names - if key == "extra": - return tuple(canonicalize_name(v) for v in values) - - # other environment markers don't have such standards - return values - - -def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: - groups: List[List[bool]] = [[]] - - for marker in markers: - assert isinstance(marker, (list, tuple, str)) - - if isinstance(marker, list): - groups[-1].append(_evaluate_markers(marker, environment)) - elif isinstance(marker, tuple): - lhs, op, rhs = marker - - if isinstance(lhs, Variable): - environment_key = lhs.value - lhs_value = environment[environment_key] - rhs_value = rhs.value - else: - lhs_value = lhs.value - environment_key = rhs.value - rhs_value = environment[environment_key] - - lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) - groups[-1].append(_eval_op(lhs_value, op, rhs_value)) - else: - assert marker in ["and", "or"] - if marker == "or": - groups.append([]) - - return any(all(item) for item in groups) - - -def format_full_version(info: "sys._version_info") -> str: - version = "{0.major}.{0.minor}.{0.micro}".format(info) - kind = info.releaselevel - if kind != "final": - version += kind[0] + str(info.serial) - return version - - -def default_environment() -> Dict[str, str]: - iver = format_full_version(sys.implementation.version) - implementation_name = sys.implementation.name - return { - "implementation_name": implementation_name, - "implementation_version": iver, - "os_name": os.name, - "platform_machine": platform.machine(), - "platform_release": platform.release(), - "platform_system": platform.system(), - "platform_version": platform.version(), - "python_full_version": platform.python_version(), - "platform_python_implementation": platform.python_implementation(), - "python_version": ".".join(platform.python_version_tuple()[:2]), - "sys_platform": sys.platform, - } - - -class Marker: - def __init__(self, marker: str) -> None: - # Note: We create a Marker object without calling this constructor in - # packaging.requirements.Requirement. If any additional logic is - # added here, make sure to mirror/adapt Requirement. - try: - self._markers = _normalize_extra_values(_parse_marker(marker)) - # The attribute `_markers` can be described in terms of a recursive type: - # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] - # - # For example, the following expression: - # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") - # - # is parsed into: - # [ - # (<Variable('python_version')>, <Op('>')>, <Value('3.6')>), - # 'and', - # [ - # (<Variable('python_version')>, <Op('==')>, <Value('3.6')>), - # 'or', - # (<Variable('os_name')>, <Op('==')>, <Value('unix')>) - # ] - # ] - except ParserSyntaxError as e: - raise InvalidMarker(str(e)) from e - - def __str__(self) -> str: - return _format_marker(self._markers) - - def __repr__(self) -> str: - return f"<Marker('{self}')>" - - def __hash__(self) -> int: - return hash((self.__class__.__name__, str(self))) - - def __eq__(self, other: Any) -> bool: - if not isinstance(other, Marker): - return NotImplemented - - return str(self) == str(other) - - def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: - """Evaluate a marker. - - Return the boolean from evaluating the given marker against the - environment. environment is an optional argument to override all or - part of the determined environment. - - The environment is determined from the current Python process. - """ - current_environment = default_environment() - current_environment["extra"] = "" - if environment is not None: - current_environment.update(environment) - # The API used to allow setting extra to None. We need to handle this - # case for backwards compatibility. - if current_environment["extra"] is None: - current_environment["extra"] = "" - - return _evaluate_markers(self._markers, current_environment) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/metadata.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/metadata.py deleted file mode 100644 index fb27493079..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/metadata.py +++ /dev/null @@ -1,825 +0,0 @@ -import email.feedparser -import email.header -import email.message -import email.parser -import email.policy -import sys -import typing -from typing import ( - Any, - Callable, - Dict, - Generic, - List, - Optional, - Tuple, - Type, - Union, - cast, -) - -from . import requirements, specifiers, utils, version as version_module - -T = typing.TypeVar("T") -if sys.version_info[:2] >= (3, 8): # pragma: no cover - from typing import Literal, TypedDict -else: # pragma: no cover - if typing.TYPE_CHECKING: - from typing_extensions import Literal, TypedDict - else: - try: - from typing_extensions import Literal, TypedDict - except ImportError: - - class Literal: - def __init_subclass__(*_args, **_kwargs): - pass - - class TypedDict: - def __init_subclass__(*_args, **_kwargs): - pass - - -try: - ExceptionGroup -except NameError: # pragma: no cover - - class ExceptionGroup(Exception): # noqa: N818 - """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11. - - If :external:exc:`ExceptionGroup` is already defined by Python itself, - that version is used instead. - """ - - message: str - exceptions: List[Exception] - - def __init__(self, message: str, exceptions: List[Exception]) -> None: - self.message = message - self.exceptions = exceptions - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" - -else: # pragma: no cover - ExceptionGroup = ExceptionGroup - - -class InvalidMetadata(ValueError): - """A metadata field contains invalid data.""" - - field: str - """The name of the field that contains invalid data.""" - - def __init__(self, field: str, message: str) -> None: - self.field = field - super().__init__(message) - - -# The RawMetadata class attempts to make as few assumptions about the underlying -# serialization formats as possible. The idea is that as long as a serialization -# formats offer some very basic primitives in *some* way then we can support -# serializing to and from that format. -class RawMetadata(TypedDict, total=False): - """A dictionary of raw core metadata. - - Each field in core metadata maps to a key of this dictionary (when data is - provided). The key is lower-case and underscores are used instead of dashes - compared to the equivalent core metadata field. Any core metadata field that - can be specified multiple times or can hold multiple values in a single - field have a key with a plural name. See :class:`Metadata` whose attributes - match the keys of this dictionary. - - Core metadata fields that can be specified multiple times are stored as a - list or dict depending on which is appropriate for the field. Any fields - which hold multiple values in a single field are stored as a list. - - """ - - # Metadata 1.0 - PEP 241 - metadata_version: str - name: str - version: str - platforms: List[str] - summary: str - description: str - keywords: List[str] - home_page: str - author: str - author_email: str - license: str - - # Metadata 1.1 - PEP 314 - supported_platforms: List[str] - download_url: str - classifiers: List[str] - requires: List[str] - provides: List[str] - obsoletes: List[str] - - # Metadata 1.2 - PEP 345 - maintainer: str - maintainer_email: str - requires_dist: List[str] - provides_dist: List[str] - obsoletes_dist: List[str] - requires_python: str - requires_external: List[str] - project_urls: Dict[str, str] - - # Metadata 2.0 - # PEP 426 attempted to completely revamp the metadata format - # but got stuck without ever being able to build consensus on - # it and ultimately ended up withdrawn. - # - # However, a number of tools had started emitting METADATA with - # `2.0` Metadata-Version, so for historical reasons, this version - # was skipped. - - # Metadata 2.1 - PEP 566 - description_content_type: str - provides_extra: List[str] - - # Metadata 2.2 - PEP 643 - dynamic: List[str] - - # Metadata 2.3 - PEP 685 - # No new fields were added in PEP 685, just some edge case were - # tightened up to provide better interoptability. - - -_STRING_FIELDS = { - "author", - "author_email", - "description", - "description_content_type", - "download_url", - "home_page", - "license", - "maintainer", - "maintainer_email", - "metadata_version", - "name", - "requires_python", - "summary", - "version", -} - -_LIST_FIELDS = { - "classifiers", - "dynamic", - "obsoletes", - "obsoletes_dist", - "platforms", - "provides", - "provides_dist", - "provides_extra", - "requires", - "requires_dist", - "requires_external", - "supported_platforms", -} - -_DICT_FIELDS = { - "project_urls", -} - - -def _parse_keywords(data: str) -> List[str]: - """Split a string of comma-separate keyboards into a list of keywords.""" - return [k.strip() for k in data.split(",")] - - -def _parse_project_urls(data: List[str]) -> Dict[str, str]: - """Parse a list of label/URL string pairings separated by a comma.""" - urls = {} - for pair in data: - # Our logic is slightly tricky here as we want to try and do - # *something* reasonable with malformed data. - # - # The main thing that we have to worry about, is data that does - # not have a ',' at all to split the label from the Value. There - # isn't a singular right answer here, and we will fail validation - # later on (if the caller is validating) so it doesn't *really* - # matter, but since the missing value has to be an empty str - # and our return value is dict[str, str], if we let the key - # be the missing value, then they'd have multiple '' values that - # overwrite each other in a accumulating dict. - # - # The other potentional issue is that it's possible to have the - # same label multiple times in the metadata, with no solid "right" - # answer with what to do in that case. As such, we'll do the only - # thing we can, which is treat the field as unparseable and add it - # to our list of unparsed fields. - parts = [p.strip() for p in pair.split(",", 1)] - parts.extend([""] * (max(0, 2 - len(parts)))) # Ensure 2 items - - # TODO: The spec doesn't say anything about if the keys should be - # considered case sensitive or not... logically they should - # be case-preserving and case-insensitive, but doing that - # would open up more cases where we might have duplicate - # entries. - label, url = parts - if label in urls: - # The label already exists in our set of urls, so this field - # is unparseable, and we can just add the whole thing to our - # unparseable data and stop processing it. - raise KeyError("duplicate labels in project urls") - urls[label] = url - - return urls - - -def _get_payload(msg: email.message.Message, source: Union[bytes, str]) -> str: - """Get the body of the message.""" - # If our source is a str, then our caller has managed encodings for us, - # and we don't need to deal with it. - if isinstance(source, str): - payload: str = msg.get_payload() - return payload - # If our source is a bytes, then we're managing the encoding and we need - # to deal with it. - else: - bpayload: bytes = msg.get_payload(decode=True) - try: - return bpayload.decode("utf8", "strict") - except UnicodeDecodeError: - raise ValueError("payload in an invalid encoding") - - -# The various parse_FORMAT functions here are intended to be as lenient as -# possible in their parsing, while still returning a correctly typed -# RawMetadata. -# -# To aid in this, we also generally want to do as little touching of the -# data as possible, except where there are possibly some historic holdovers -# that make valid data awkward to work with. -# -# While this is a lower level, intermediate format than our ``Metadata`` -# class, some light touch ups can make a massive difference in usability. - -# Map METADATA fields to RawMetadata. -_EMAIL_TO_RAW_MAPPING = { - "author": "author", - "author-email": "author_email", - "classifier": "classifiers", - "description": "description", - "description-content-type": "description_content_type", - "download-url": "download_url", - "dynamic": "dynamic", - "home-page": "home_page", - "keywords": "keywords", - "license": "license", - "maintainer": "maintainer", - "maintainer-email": "maintainer_email", - "metadata-version": "metadata_version", - "name": "name", - "obsoletes": "obsoletes", - "obsoletes-dist": "obsoletes_dist", - "platform": "platforms", - "project-url": "project_urls", - "provides": "provides", - "provides-dist": "provides_dist", - "provides-extra": "provides_extra", - "requires": "requires", - "requires-dist": "requires_dist", - "requires-external": "requires_external", - "requires-python": "requires_python", - "summary": "summary", - "supported-platform": "supported_platforms", - "version": "version", -} -_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()} - - -def parse_email(data: Union[bytes, str]) -> Tuple[RawMetadata, Dict[str, List[str]]]: - """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``). - - This function returns a two-item tuple of dicts. The first dict is of - recognized fields from the core metadata specification. Fields that can be - parsed and translated into Python's built-in types are converted - appropriately. All other fields are left as-is. Fields that are allowed to - appear multiple times are stored as lists. - - The second dict contains all other fields from the metadata. This includes - any unrecognized fields. It also includes any fields which are expected to - be parsed into a built-in type but were not formatted appropriately. Finally, - any fields that are expected to appear only once but are repeated are - included in this dict. - - """ - raw: Dict[str, Union[str, List[str], Dict[str, str]]] = {} - unparsed: Dict[str, List[str]] = {} - - if isinstance(data, str): - parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data) - else: - parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data) - - # We have to wrap parsed.keys() in a set, because in the case of multiple - # values for a key (a list), the key will appear multiple times in the - # list of keys, but we're avoiding that by using get_all(). - for name in frozenset(parsed.keys()): - # Header names in RFC are case insensitive, so we'll normalize to all - # lower case to make comparisons easier. - name = name.lower() - - # We use get_all() here, even for fields that aren't multiple use, - # because otherwise someone could have e.g. two Name fields, and we - # would just silently ignore it rather than doing something about it. - headers = parsed.get_all(name) or [] - - # The way the email module works when parsing bytes is that it - # unconditionally decodes the bytes as ascii using the surrogateescape - # handler. When you pull that data back out (such as with get_all() ), - # it looks to see if the str has any surrogate escapes, and if it does - # it wraps it in a Header object instead of returning the string. - # - # As such, we'll look for those Header objects, and fix up the encoding. - value = [] - # Flag if we have run into any issues processing the headers, thus - # signalling that the data belongs in 'unparsed'. - valid_encoding = True - for h in headers: - # It's unclear if this can return more types than just a Header or - # a str, so we'll just assert here to make sure. - assert isinstance(h, (email.header.Header, str)) - - # If it's a header object, we need to do our little dance to get - # the real data out of it. In cases where there is invalid data - # we're going to end up with mojibake, but there's no obvious, good - # way around that without reimplementing parts of the Header object - # ourselves. - # - # That should be fine since, if mojibacked happens, this key is - # going into the unparsed dict anyways. - if isinstance(h, email.header.Header): - # The Header object stores it's data as chunks, and each chunk - # can be independently encoded, so we'll need to check each - # of them. - chunks: List[Tuple[bytes, Optional[str]]] = [] - for bin, encoding in email.header.decode_header(h): - try: - bin.decode("utf8", "strict") - except UnicodeDecodeError: - # Enable mojibake. - encoding = "latin1" - valid_encoding = False - else: - encoding = "utf8" - chunks.append((bin, encoding)) - - # Turn our chunks back into a Header object, then let that - # Header object do the right thing to turn them into a - # string for us. - value.append(str(email.header.make_header(chunks))) - # This is already a string, so just add it. - else: - value.append(h) - - # We've processed all of our values to get them into a list of str, - # but we may have mojibake data, in which case this is an unparsed - # field. - if not valid_encoding: - unparsed[name] = value - continue - - raw_name = _EMAIL_TO_RAW_MAPPING.get(name) - if raw_name is None: - # This is a bit of a weird situation, we've encountered a key that - # we don't know what it means, so we don't know whether it's meant - # to be a list or not. - # - # Since we can't really tell one way or another, we'll just leave it - # as a list, even though it may be a single item list, because that's - # what makes the most sense for email headers. - unparsed[name] = value - continue - - # If this is one of our string fields, then we'll check to see if our - # value is a list of a single item. If it is then we'll assume that - # it was emitted as a single string, and unwrap the str from inside - # the list. - # - # If it's any other kind of data, then we haven't the faintest clue - # what we should parse it as, and we have to just add it to our list - # of unparsed stuff. - if raw_name in _STRING_FIELDS and len(value) == 1: - raw[raw_name] = value[0] - # If this is one of our list of string fields, then we can just assign - # the value, since email *only* has strings, and our get_all() call - # above ensures that this is a list. - elif raw_name in _LIST_FIELDS: - raw[raw_name] = value - # Special Case: Keywords - # The keywords field is implemented in the metadata spec as a str, - # but it conceptually is a list of strings, and is serialized using - # ", ".join(keywords), so we'll do some light data massaging to turn - # this into what it logically is. - elif raw_name == "keywords" and len(value) == 1: - raw[raw_name] = _parse_keywords(value[0]) - # Special Case: Project-URL - # The project urls is implemented in the metadata spec as a list of - # specially-formatted strings that represent a key and a value, which - # is fundamentally a mapping, however the email format doesn't support - # mappings in a sane way, so it was crammed into a list of strings - # instead. - # - # We will do a little light data massaging to turn this into a map as - # it logically should be. - elif raw_name == "project_urls": - try: - raw[raw_name] = _parse_project_urls(value) - except KeyError: - unparsed[name] = value - # Nothing that we've done has managed to parse this, so it'll just - # throw it in our unparseable data and move on. - else: - unparsed[name] = value - - # We need to support getting the Description from the message payload in - # addition to getting it from the the headers. This does mean, though, there - # is the possibility of it being set both ways, in which case we put both - # in 'unparsed' since we don't know which is right. - try: - payload = _get_payload(parsed, data) - except ValueError: - unparsed.setdefault("description", []).append( - parsed.get_payload(decode=isinstance(data, bytes)) - ) - else: - if payload: - # Check to see if we've already got a description, if so then both - # it, and this body move to unparseable. - if "description" in raw: - description_header = cast(str, raw.pop("description")) - unparsed.setdefault("description", []).extend( - [description_header, payload] - ) - elif "description" in unparsed: - unparsed["description"].append(payload) - else: - raw["description"] = payload - - # We need to cast our `raw` to a metadata, because a TypedDict only support - # literal key names, but we're computing our key names on purpose, but the - # way this function is implemented, our `TypedDict` can only have valid key - # names. - return cast(RawMetadata, raw), unparsed - - -_NOT_FOUND = object() - - -# Keep the two values in sync. -_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] -_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3"] - -_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) - - -class _Validator(Generic[T]): - """Validate a metadata field. - - All _process_*() methods correspond to a core metadata field. The method is - called with the field's raw value. If the raw value is valid it is returned - in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field). - If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause - as appropriate). - """ - - name: str - raw_name: str - added: _MetadataVersion - - def __init__( - self, - *, - added: _MetadataVersion = "1.0", - ) -> None: - self.added = added - - def __set_name__(self, _owner: "Metadata", name: str) -> None: - self.name = name - self.raw_name = _RAW_TO_EMAIL_MAPPING[name] - - def __get__(self, instance: "Metadata", _owner: Type["Metadata"]) -> T: - # With Python 3.8, the caching can be replaced with functools.cached_property(). - # No need to check the cache as attribute lookup will resolve into the - # instance's __dict__ before __get__ is called. - cache = instance.__dict__ - value = instance._raw.get(self.name) - - # To make the _process_* methods easier, we'll check if the value is None - # and if this field is NOT a required attribute, and if both of those - # things are true, we'll skip the the converter. This will mean that the - # converters never have to deal with the None union. - if self.name in _REQUIRED_ATTRS or value is not None: - try: - converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}") - except AttributeError: - pass - else: - value = converter(value) - - cache[self.name] = value - try: - del instance._raw[self.name] # type: ignore[misc] - except KeyError: - pass - - return cast(T, value) - - def _invalid_metadata( - self, msg: str, cause: Optional[Exception] = None - ) -> InvalidMetadata: - exc = InvalidMetadata( - self.raw_name, msg.format_map({"field": repr(self.raw_name)}) - ) - exc.__cause__ = cause - return exc - - def _process_metadata_version(self, value: str) -> _MetadataVersion: - # Implicitly makes Metadata-Version required. - if value not in _VALID_METADATA_VERSIONS: - raise self._invalid_metadata(f"{value!r} is not a valid metadata version") - return cast(_MetadataVersion, value) - - def _process_name(self, value: str) -> str: - if not value: - raise self._invalid_metadata("{field} is a required field") - # Validate the name as a side-effect. - try: - utils.canonicalize_name(value, validate=True) - except utils.InvalidName as exc: - raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc - ) - else: - return value - - def _process_version(self, value: str) -> version_module.Version: - if not value: - raise self._invalid_metadata("{field} is a required field") - try: - return version_module.parse(value) - except version_module.InvalidVersion as exc: - raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc - ) - - def _process_summary(self, value: str) -> str: - """Check the field contains no newlines.""" - if "\n" in value: - raise self._invalid_metadata("{field} must be a single line") - return value - - def _process_description_content_type(self, value: str) -> str: - content_types = {"text/plain", "text/x-rst", "text/markdown"} - message = email.message.EmailMessage() - message["content-type"] = value - - content_type, parameters = ( - # Defaults to `text/plain` if parsing failed. - message.get_content_type().lower(), - message["content-type"].params, - ) - # Check if content-type is valid or defaulted to `text/plain` and thus was - # not parseable. - if content_type not in content_types or content_type not in value.lower(): - raise self._invalid_metadata( - f"{{field}} must be one of {list(content_types)}, not {value!r}" - ) - - charset = parameters.get("charset", "UTF-8") - if charset != "UTF-8": - raise self._invalid_metadata( - f"{{field}} can only specify the UTF-8 charset, not {list(charset)}" - ) - - markdown_variants = {"GFM", "CommonMark"} - variant = parameters.get("variant", "GFM") # Use an acceptable default. - if content_type == "text/markdown" and variant not in markdown_variants: - raise self._invalid_metadata( - f"valid Markdown variants for {{field}} are {list(markdown_variants)}, " - f"not {variant!r}", - ) - return value - - def _process_dynamic(self, value: List[str]) -> List[str]: - for dynamic_field in map(str.lower, value): - if dynamic_field in {"name", "version", "metadata-version"}: - raise self._invalid_metadata( - f"{value!r} is not allowed as a dynamic field" - ) - elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: - raise self._invalid_metadata(f"{value!r} is not a valid dynamic field") - return list(map(str.lower, value)) - - def _process_provides_extra( - self, - value: List[str], - ) -> List[utils.NormalizedName]: - normalized_names = [] - try: - for name in value: - normalized_names.append(utils.canonicalize_name(name, validate=True)) - except utils.InvalidName as exc: - raise self._invalid_metadata( - f"{name!r} is invalid for {{field}}", cause=exc - ) - else: - return normalized_names - - def _process_requires_python(self, value: str) -> specifiers.SpecifierSet: - try: - return specifiers.SpecifierSet(value) - except specifiers.InvalidSpecifier as exc: - raise self._invalid_metadata( - f"{value!r} is invalid for {{field}}", cause=exc - ) - - def _process_requires_dist( - self, - value: List[str], - ) -> List[requirements.Requirement]: - reqs = [] - try: - for req in value: - reqs.append(requirements.Requirement(req)) - except requirements.InvalidRequirement as exc: - raise self._invalid_metadata(f"{req!r} is invalid for {{field}}", cause=exc) - else: - return reqs - - -class Metadata: - """Representation of distribution metadata. - - Compared to :class:`RawMetadata`, this class provides objects representing - metadata fields instead of only using built-in types. Any invalid metadata - will cause :exc:`InvalidMetadata` to be raised (with a - :py:attr:`~BaseException.__cause__` attribute as appropriate). - """ - - _raw: RawMetadata - - @classmethod - def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> "Metadata": - """Create an instance from :class:`RawMetadata`. - - If *validate* is true, all metadata will be validated. All exceptions - related to validation will be gathered and raised as an :class:`ExceptionGroup`. - """ - ins = cls() - ins._raw = data.copy() # Mutations occur due to caching enriched values. - - if validate: - exceptions: List[Exception] = [] - try: - metadata_version = ins.metadata_version - metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version) - except InvalidMetadata as metadata_version_exc: - exceptions.append(metadata_version_exc) - metadata_version = None - - # Make sure to check for the fields that are present, the required - # fields (so their absence can be reported). - fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS - # Remove fields that have already been checked. - fields_to_check -= {"metadata_version"} - - for key in fields_to_check: - try: - if metadata_version: - # Can't use getattr() as that triggers descriptor protocol which - # will fail due to no value for the instance argument. - try: - field_metadata_version = cls.__dict__[key].added - except KeyError: - exc = InvalidMetadata(key, f"unrecognized field: {key!r}") - exceptions.append(exc) - continue - field_age = _VALID_METADATA_VERSIONS.index( - field_metadata_version - ) - if field_age > metadata_age: - field = _RAW_TO_EMAIL_MAPPING[key] - exc = InvalidMetadata( - field, - "{field} introduced in metadata version " - "{field_metadata_version}, not {metadata_version}", - ) - exceptions.append(exc) - continue - getattr(ins, key) - except InvalidMetadata as exc: - exceptions.append(exc) - - if exceptions: - raise ExceptionGroup("invalid metadata", exceptions) - - return ins - - @classmethod - def from_email( - cls, data: Union[bytes, str], *, validate: bool = True - ) -> "Metadata": - """Parse metadata from email headers. - - If *validate* is true, the metadata will be validated. All exceptions - related to validation will be gathered and raised as an :class:`ExceptionGroup`. - """ - raw, unparsed = parse_email(data) - - if validate: - exceptions: list[Exception] = [] - for unparsed_key in unparsed: - if unparsed_key in _EMAIL_TO_RAW_MAPPING: - message = f"{unparsed_key!r} has invalid data" - else: - message = f"unrecognized field: {unparsed_key!r}" - exceptions.append(InvalidMetadata(unparsed_key, message)) - - if exceptions: - raise ExceptionGroup("unparsed", exceptions) - - try: - return cls.from_raw(raw, validate=validate) - except ExceptionGroup as exc_group: - raise ExceptionGroup( - "invalid or unparsed metadata", exc_group.exceptions - ) from None - - metadata_version: _Validator[_MetadataVersion] = _Validator() - """:external:ref:`core-metadata-metadata-version` - (required; validated to be a valid metadata version)""" - name: _Validator[str] = _Validator() - """:external:ref:`core-metadata-name` - (required; validated using :func:`~packaging.utils.canonicalize_name` and its - *validate* parameter)""" - version: _Validator[version_module.Version] = _Validator() - """:external:ref:`core-metadata-version` (required)""" - dynamic: _Validator[Optional[List[str]]] = _Validator( - added="2.2", - ) - """:external:ref:`core-metadata-dynamic` - (validated against core metadata field names and lowercased)""" - platforms: _Validator[Optional[List[str]]] = _Validator() - """:external:ref:`core-metadata-platform`""" - supported_platforms: _Validator[Optional[List[str]]] = _Validator(added="1.1") - """:external:ref:`core-metadata-supported-platform`""" - summary: _Validator[Optional[str]] = _Validator() - """:external:ref:`core-metadata-summary` (validated to contain no newlines)""" - description: _Validator[Optional[str]] = _Validator() # TODO 2.1: can be in body - """:external:ref:`core-metadata-description`""" - description_content_type: _Validator[Optional[str]] = _Validator(added="2.1") - """:external:ref:`core-metadata-description-content-type` (validated)""" - keywords: _Validator[Optional[List[str]]] = _Validator() - """:external:ref:`core-metadata-keywords`""" - home_page: _Validator[Optional[str]] = _Validator() - """:external:ref:`core-metadata-home-page`""" - download_url: _Validator[Optional[str]] = _Validator(added="1.1") - """:external:ref:`core-metadata-download-url`""" - author: _Validator[Optional[str]] = _Validator() - """:external:ref:`core-metadata-author`""" - author_email: _Validator[Optional[str]] = _Validator() - """:external:ref:`core-metadata-author-email`""" - maintainer: _Validator[Optional[str]] = _Validator(added="1.2") - """:external:ref:`core-metadata-maintainer`""" - maintainer_email: _Validator[Optional[str]] = _Validator(added="1.2") - """:external:ref:`core-metadata-maintainer-email`""" - license: _Validator[Optional[str]] = _Validator() - """:external:ref:`core-metadata-license`""" - classifiers: _Validator[Optional[List[str]]] = _Validator(added="1.1") - """:external:ref:`core-metadata-classifier`""" - requires_dist: _Validator[Optional[List[requirements.Requirement]]] = _Validator( - added="1.2" - ) - """:external:ref:`core-metadata-requires-dist`""" - requires_python: _Validator[Optional[specifiers.SpecifierSet]] = _Validator( - added="1.2" - ) - """:external:ref:`core-metadata-requires-python`""" - # Because `Requires-External` allows for non-PEP 440 version specifiers, we - # don't do any processing on the values. - requires_external: _Validator[Optional[List[str]]] = _Validator(added="1.2") - """:external:ref:`core-metadata-requires-external`""" - project_urls: _Validator[Optional[Dict[str, str]]] = _Validator(added="1.2") - """:external:ref:`core-metadata-project-url`""" - # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation - # regardless of metadata version. - provides_extra: _Validator[Optional[List[utils.NormalizedName]]] = _Validator( - added="2.1", - ) - """:external:ref:`core-metadata-provides-extra`""" - provides_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2") - """:external:ref:`core-metadata-provides-dist`""" - obsoletes_dist: _Validator[Optional[List[str]]] = _Validator(added="1.2") - """:external:ref:`core-metadata-obsoletes-dist`""" - requires: _Validator[Optional[List[str]]] = _Validator(added="1.1") - """``Requires`` (deprecated)""" - provides: _Validator[Optional[List[str]]] = _Validator(added="1.1") - """``Provides`` (deprecated)""" - obsoletes: _Validator[Optional[List[str]]] = _Validator(added="1.1") - """``Obsoletes`` (deprecated)""" diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/py.typed b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/py.typed deleted file mode 100644 index e69de29bb2..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/py.typed +++ /dev/null diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/requirements.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/requirements.py deleted file mode 100644 index bdc43a7e98..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/requirements.py +++ /dev/null @@ -1,90 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from typing import Any, Iterator, Optional, Set - -from ._parser import parse_requirement as _parse_requirement -from ._tokenizer import ParserSyntaxError -from .markers import Marker, _normalize_extra_values -from .specifiers import SpecifierSet -from .utils import canonicalize_name - - -class InvalidRequirement(ValueError): - """ - An invalid requirement was found, users should refer to PEP 508. - """ - - -class Requirement: - """Parse a requirement. - - Parse a given requirement string into its parts, such as name, specifier, - URL, and extras. Raises InvalidRequirement on a badly-formed requirement - string. - """ - - # TODO: Can we test whether something is contained within a requirement? - # If so how do we do that? Do we need to test against the _name_ of - # the thing as well as the version? What about the markers? - # TODO: Can we normalize the name and extra name? - - def __init__(self, requirement_string: str) -> None: - try: - parsed = _parse_requirement(requirement_string) - except ParserSyntaxError as e: - raise InvalidRequirement(str(e)) from e - - self.name: str = parsed.name - self.url: Optional[str] = parsed.url or None - self.extras: Set[str] = set(parsed.extras or []) - self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) - self.marker: Optional[Marker] = None - if parsed.marker is not None: - self.marker = Marker.__new__(Marker) - self.marker._markers = _normalize_extra_values(parsed.marker) - - def _iter_parts(self, name: str) -> Iterator[str]: - yield name - - if self.extras: - formatted_extras = ",".join(sorted(self.extras)) - yield f"[{formatted_extras}]" - - if self.specifier: - yield str(self.specifier) - - if self.url: - yield f"@ {self.url}" - if self.marker: - yield " " - - if self.marker: - yield f"; {self.marker}" - - def __str__(self) -> str: - return "".join(self._iter_parts(self.name)) - - def __repr__(self) -> str: - return f"<Requirement('{self}')>" - - def __hash__(self) -> int: - return hash( - ( - self.__class__.__name__, - *self._iter_parts(canonicalize_name(self.name)), - ) - ) - - def __eq__(self, other: Any) -> bool: - if not isinstance(other, Requirement): - return NotImplemented - - return ( - canonicalize_name(self.name) == canonicalize_name(other.name) - and self.extras == other.extras - and self.specifier == other.specifier - and self.url == other.url - and self.marker == other.marker - ) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/specifiers.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/specifiers.py deleted file mode 100644 index 2d015bab59..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/specifiers.py +++ /dev/null @@ -1,1017 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -""" -.. testsetup:: - - from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier - from packaging.version import Version -""" - -import abc -import itertools -import re -from typing import Callable, Iterable, Iterator, List, Optional, Tuple, TypeVar, Union - -from .utils import canonicalize_version -from .version import Version - -UnparsedVersion = Union[Version, str] -UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) -CallableOperator = Callable[[Version, str], bool] - - -def _coerce_version(version: UnparsedVersion) -> Version: - if not isinstance(version, Version): - version = Version(version) - return version - - -class InvalidSpecifier(ValueError): - """ - Raised when attempting to create a :class:`Specifier` with a specifier - string that is invalid. - - >>> Specifier("lolwat") - Traceback (most recent call last): - ... - packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' - """ - - -class BaseSpecifier(metaclass=abc.ABCMeta): - @abc.abstractmethod - def __str__(self) -> str: - """ - Returns the str representation of this Specifier-like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self) -> int: - """ - Returns a hash value for this Specifier-like object. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Returns a boolean representing whether or not the two Specifier-like - objects are equal. - - :param other: The other object to check against. - """ - - @property - @abc.abstractmethod - def prereleases(self) -> Optional[bool]: - """Whether or not pre-releases as a whole are allowed. - - This can be set to either ``True`` or ``False`` to explicitly enable or disable - prereleases or it can be set to ``None`` (the default) to use default semantics. - """ - - @prereleases.setter - def prereleases(self, value: bool) -> None: - """Setter for :attr:`prereleases`. - - :param value: The value to set. - """ - - @abc.abstractmethod - def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None - ) -> Iterator[UnparsedVersionVar]: - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class Specifier(BaseSpecifier): - """This class abstracts handling of version specifiers. - - .. tip:: - - It is generally not required to instantiate this manually. You should instead - prefer to work with :class:`SpecifierSet` instead, which can parse - comma-separated version specifiers (which is what package metadata contains). - """ - - _operator_regex_str = r""" - (?P<operator>(~=|==|!=|<=|>=|<|>|===)) - """ - _version_regex_str = r""" - (?P<version> - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s;)]* # The arbitrary version can be just about anything, - # we match everything except for whitespace, a - # semi-colon for marker support, and a closing paren - # since versions can be enclosed in them. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - - # You cannot use a wild card and a pre-release, post-release, a dev or - # local version together so group them with a | and make them optional. - (?: - \.\* # Wild card syntax of .* - | - (?: # pre release - [-_\.]? - (alpha|beta|preview|pre|a|b|c|rc) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (alpha|beta|preview|pre|a|b|c|rc) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?<!==|!=|~=) # We have special cases for these - # operators so we want to make sure they - # don't match here. - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (alpha|beta|preview|pre|a|b|c|rc) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - ) - """ - - _regex = re.compile( - r"^\s*" + _operator_regex_str + _version_regex_str + r"\s*$", - re.VERBOSE | re.IGNORECASE, - ) - - _operators = { - "~=": "compatible", - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - """Initialize a Specifier instance. - - :param spec: - The string representation of a specifier which will be parsed and - normalized before use. - :param prereleases: - This tells the specifier if it should accept prerelease versions if - applicable or not. The default of ``None`` will autodetect it from the - given specifiers. - :raises InvalidSpecifier: - If the given specifier is invalid (i.e. bad syntax). - """ - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - - self._spec: Tuple[str, str] = ( - match.group("operator").strip(), - match.group("version").strip(), - ) - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - # https://github.com/python/mypy/pull/13475#pullrequestreview-1079784515 - @property # type: ignore[override] - def prereleases(self) -> bool: - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] - - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if Version(version).is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - @property - def operator(self) -> str: - """The operator of this specifier. - - >>> Specifier("==1.2.3").operator - '==' - """ - return self._spec[0] - - @property - def version(self) -> str: - """The version of this specifier. - - >>> Specifier("==1.2.3").version - '1.2.3' - """ - return self._spec[1] - - def __repr__(self) -> str: - """A representation of the Specifier that shows all internal state. - - >>> Specifier('>=1.0.0') - <Specifier('>=1.0.0')> - >>> Specifier('>=1.0.0', prereleases=False) - <Specifier('>=1.0.0', prereleases=False)> - >>> Specifier('>=1.0.0', prereleases=True) - <Specifier('>=1.0.0', prereleases=True)> - """ - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return f"<{self.__class__.__name__}({str(self)!r}{pre})>" - - def __str__(self) -> str: - """A string representation of the Specifier that can be round-tripped. - - >>> str(Specifier('>=1.0.0')) - '>=1.0.0' - >>> str(Specifier('>=1.0.0', prereleases=False)) - '>=1.0.0' - """ - return "{}{}".format(*self._spec) - - @property - def _canonical_spec(self) -> Tuple[str, str]: - canonical_version = canonicalize_version( - self._spec[1], - strip_trailing_zero=(self._spec[0] != "~="), - ) - return self._spec[0], canonical_version - - def __hash__(self) -> int: - return hash(self._canonical_spec) - - def __eq__(self, other: object) -> bool: - """Whether or not the two Specifier-like objects are equal. - - :param other: The other object to check against. - - The value of :attr:`prereleases` is ignored. - - >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") - True - >>> (Specifier("==1.2.3", prereleases=False) == - ... Specifier("==1.2.3", prereleases=True)) - True - >>> Specifier("==1.2.3") == "==1.2.3" - True - >>> Specifier("==1.2.3") == Specifier("==1.2.4") - False - >>> Specifier("==1.2.3") == Specifier("~=1.2.3") - False - """ - if isinstance(other, str): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def _get_operator(self, op: str) -> CallableOperator: - operator_callable: CallableOperator = getattr( - self, f"_compare_{self._operators[op]}" - ) - return operator_callable - - def _compare_compatible(self, prospective: Version, spec: str) -> bool: - - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore suffix segments. - prefix = _version_join( - list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( - prospective, prefix - ) - - def _compare_equal(self, prospective: Version, spec: str) -> bool: - - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # In the case of prefix matching we want to ignore local segment. - normalized_prospective = canonicalize_version( - prospective.public, strip_trailing_zero=False - ) - # Get the normalized version string ignoring the trailing .* - normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) - # Split the spec out by bangs and dots, and pretend that there is - # an implicit dot in between a release segment and a pre-release segment. - split_spec = _version_split(normalized_spec) - - # Split the prospective version out by bangs and dots, and pretend - # that there is an implicit dot in between a release segment and - # a pre-release segment. - split_prospective = _version_split(normalized_prospective) - - # 0-pad the prospective version before shortening it to get the correct - # shortened version. - padded_prospective, _ = _pad_version(split_prospective, split_spec) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - shortened_prospective = padded_prospective[: len(split_spec)] - - return shortened_prospective == split_spec - else: - # Convert our spec string into a Version - spec_version = Version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec_version.local: - prospective = Version(prospective.public) - - return prospective == spec_version - - def _compare_not_equal(self, prospective: Version, spec: str) -> bool: - return not self._compare_equal(prospective, spec) - - def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) <= Version(spec) - - def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) >= Version(spec) - - def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if not spec.is_prerelease and prospective.is_prerelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if not spec.is_postrelease and prospective.is_postrelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is technically greater than, to match. - if prospective.local is not None: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: - return str(prospective).lower() == str(spec).lower() - - def __contains__(self, item: Union[str, Version]) -> bool: - """Return whether or not the item is contained in this specifier. - - :param item: The item to check for. - - This is used for the ``in`` operator and behaves the same as - :meth:`contains` with no ``prereleases`` argument passed. - - >>> "1.2.3" in Specifier(">=1.2.3") - True - >>> Version("1.2.3") in Specifier(">=1.2.3") - True - >>> "1.0.0" in Specifier(">=1.2.3") - False - >>> "1.3.0a1" in Specifier(">=1.2.3") - False - >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) - True - """ - return self.contains(item) - - def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None - ) -> bool: - """Return whether or not the item is contained in this specifier. - - :param item: - The item to check for, which can be a version string or a - :class:`Version` instance. - :param prereleases: - Whether or not to match prereleases with this Specifier. If set to - ``None`` (the default), it uses :attr:`prereleases` to determine - whether or not prereleases are allowed. - - >>> Specifier(">=1.2.3").contains("1.2.3") - True - >>> Specifier(">=1.2.3").contains(Version("1.2.3")) - True - >>> Specifier(">=1.2.3").contains("1.0.0") - False - >>> Specifier(">=1.2.3").contains("1.3.0a1") - False - >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") - True - >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) - True - """ - - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version, this allows us to have a shortcut for - # "2.0" in Specifier(">=2") - normalized_item = _coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if normalized_item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - operator_callable: CallableOperator = self._get_operator(self.operator) - return operator_callable(normalized_item, self.version) - - def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None - ) -> Iterator[UnparsedVersionVar]: - """Filter items in the given iterable, that match the specifier. - - :param iterable: - An iterable that can contain version strings and :class:`Version` instances. - The items in the iterable will be filtered according to the specifier. - :param prereleases: - Whether or not to allow prereleases in the returned iterator. If set to - ``None`` (the default), it will be intelligently decide whether to allow - prereleases or not (based on the :attr:`prereleases` attribute, and - whether the only versions matching are prereleases). - - This method is smarter than just ``filter(Specifier().contains, [...])`` - because it implements the rule from :pep:`440` that a prerelease item - SHOULD be accepted if no other versions match the given specifier. - - >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) - ['1.3'] - >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) - ['1.2.3', '1.3', <Version('1.4')>] - >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) - ['1.5a1'] - >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) - ['1.3', '1.5a1'] - >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) - ['1.3', '1.5a1'] - """ - - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = _coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later in case nothing - # else matches this specifier. - if parsed_version.is_prerelease and not ( - prereleases or self.prereleases - ): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") - - -def _version_split(version: str) -> List[str]: - """Split version into components. - - The split components are intended for version comparison. The logic does - not attempt to retain the original version string, so joining the - components back with :func:`_version_join` may not produce the original - version string. - """ - result: List[str] = [] - - epoch, _, rest = version.rpartition("!") - result.append(epoch or "0") - - for item in rest.split("."): - match = _prefix_regex.search(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _version_join(components: List[str]) -> str: - """Join split version components into a version string. - - This function assumes the input came from :func:`_version_split`, where the - first component must be the epoch (either empty or numeric), and all other - components numeric. - """ - epoch, *rest = components - return f"{epoch}!{'.'.join(rest)}" - - -def _is_not_suffix(segment: str) -> bool: - return not any( - segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") - ) - - -def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) - - # Get the rest of our versions - left_split.append(left[len(left_split[0]) :]) - right_split.append(right[len(right_split[0]) :]) - - # Insert our padding - left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) - right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) - - return ( - list(itertools.chain.from_iterable(left_split)), - list(itertools.chain.from_iterable(right_split)), - ) - - -class SpecifierSet(BaseSpecifier): - """This class abstracts handling of a set of version specifiers. - - It can be passed a single specifier (``>=3.0``), a comma-separated list of - specifiers (``>=3.0,!=3.1``), or no specifier at all. - """ - - def __init__( - self, specifiers: str = "", prereleases: Optional[bool] = None - ) -> None: - """Initialize a SpecifierSet instance. - - :param specifiers: - The string representation of a specifier or a comma-separated list of - specifiers which will be parsed and normalized before use. - :param prereleases: - This tells the SpecifierSet if it should accept prerelease versions if - applicable or not. The default of ``None`` will autodetect it from the - given specifiers. - - :raises InvalidSpecifier: - If the given ``specifiers`` are not parseable than this exception will be - raised. - """ - - # Split on `,` to break each individual specifier into it's own item, and - # strip each item to remove leading/trailing whitespace. - split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - - # Make each individual specifier a Specifier and save in a frozen set for later. - self._specs = frozenset(map(Specifier, split_specifiers)) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - @property - def prereleases(self) -> Optional[bool]: - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - def __repr__(self) -> str: - """A representation of the specifier set that shows all internal state. - - Note that the ordering of the individual specifiers within the set may not - match the input string. - - >>> SpecifierSet('>=1.0.0,!=2.0.0') - <SpecifierSet('!=2.0.0,>=1.0.0')> - >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) - <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)> - >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) - <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)> - """ - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return f"<SpecifierSet({str(self)!r}{pre})>" - - def __str__(self) -> str: - """A string representation of the specifier set that can be round-tripped. - - Note that the ordering of the individual specifiers within the set may not - match the input string. - - >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) - '!=1.0.1,>=1.0.0' - >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) - '!=1.0.1,>=1.0.0' - """ - return ",".join(sorted(str(s) for s in self._specs)) - - def __hash__(self) -> int: - return hash(self._specs) - - def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": - """Return a SpecifierSet which is a combination of the two sets. - - :param other: The other object to combine with. - - >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' - <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')> - >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') - <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')> - """ - if isinstance(other, str): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif self._prereleases is not None and other._prereleases is None: - specifier._prereleases = self._prereleases - elif self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." - ) - - return specifier - - def __eq__(self, other: object) -> bool: - """Whether or not the two SpecifierSet-like objects are equal. - - :param other: The other object to check against. - - The value of :attr:`prereleases` is ignored. - - >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") - True - >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == - ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) - True - >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" - True - >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") - False - >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") - False - """ - if isinstance(other, (str, Specifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __len__(self) -> int: - """Returns the number of specifiers in this specifier set.""" - return len(self._specs) - - def __iter__(self) -> Iterator[Specifier]: - """ - Returns an iterator over all the underlying :class:`Specifier` instances - in this specifier set. - - >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) - [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>] - """ - return iter(self._specs) - - def __contains__(self, item: UnparsedVersion) -> bool: - """Return whether or not the item is contained in this specifier. - - :param item: The item to check for. - - This is used for the ``in`` operator and behaves the same as - :meth:`contains` with no ``prereleases`` argument passed. - - >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") - True - >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") - True - >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") - False - >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") - False - >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) - True - """ - return self.contains(item) - - def contains( - self, - item: UnparsedVersion, - prereleases: Optional[bool] = None, - installed: Optional[bool] = None, - ) -> bool: - """Return whether or not the item is contained in this SpecifierSet. - - :param item: - The item to check for, which can be a version string or a - :class:`Version` instance. - :param prereleases: - Whether or not to match prereleases with this SpecifierSet. If set to - ``None`` (the default), it uses :attr:`prereleases` to determine - whether or not prereleases are allowed. - - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") - True - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) - True - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") - False - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") - False - >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1") - True - >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) - True - """ - # Ensure that our item is a Version instance. - if not isinstance(item, Version): - item = Version(item) - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # We can determine if we're going to allow pre-releases by looking to - # see if any of the underlying items supports them. If none of them do - # and this item is a pre-release then we do not allow it and we can - # short circuit that here. - # Note: This means that 1.0.dev1 would not be contained in something - # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 - if not prereleases and item.is_prerelease: - return False - - if installed and item.is_prerelease: - item = Version(item.base_version) - - # We simply dispatch to the underlying specs here to make sure that the - # given version is contained within all of them. - # Note: This use of all() here means that an empty set of specifiers - # will always return True, this is an explicit design decision. - return all(s.contains(item, prereleases=prereleases) for s in self._specs) - - def filter( - self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None - ) -> Iterator[UnparsedVersionVar]: - """Filter items in the given iterable, that match the specifiers in this set. - - :param iterable: - An iterable that can contain version strings and :class:`Version` instances. - The items in the iterable will be filtered according to the specifier. - :param prereleases: - Whether or not to allow prereleases in the returned iterator. If set to - ``None`` (the default), it will be intelligently decide whether to allow - prereleases or not (based on the :attr:`prereleases` attribute, and - whether the only versions matching are prereleases). - - This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` - because it implements the rule from :pep:`440` that a prerelease item - SHOULD be accepted if no other versions match the given specifier. - - >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) - ['1.3'] - >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) - ['1.3', <Version('1.4')>] - >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) - [] - >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) - ['1.3', '1.5a1'] - >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) - ['1.3', '1.5a1'] - - An "empty" SpecifierSet will filter items based on the presence of prerelease - versions in the set. - - >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) - ['1.3'] - >>> list(SpecifierSet("").filter(["1.5a1"])) - ['1.5a1'] - >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) - ['1.3', '1.5a1'] - >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) - ['1.3', '1.5a1'] - """ - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - for spec in self._specs: - iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iter(iterable) - # If we do not have any specifiers, then we need to have a rough filter - # which will filter out any pre-releases, unless there are no final - # releases. - else: - filtered: List[UnparsedVersionVar] = [] - found_prereleases: List[UnparsedVersionVar] = [] - - for item in iterable: - parsed_version = _coerce_version(item) - - # Store any item which is a pre-release for later unless we've - # already found a final version or we are accepting prereleases - if parsed_version.is_prerelease and not prereleases: - if not filtered: - found_prereleases.append(item) - else: - filtered.append(item) - - # If we've found no items except for pre-releases, then we'll go - # ahead and use the pre-releases - if not filtered and found_prereleases and prereleases is None: - return iter(found_prereleases) - - return iter(filtered) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/tags.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/tags.py deleted file mode 100644 index 89f1926137..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/tags.py +++ /dev/null @@ -1,571 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import logging -import platform -import re -import struct -import subprocess -import sys -import sysconfig -from importlib.machinery import EXTENSION_SUFFIXES -from typing import ( - Dict, - FrozenSet, - Iterable, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, - cast, -) - -from . import _manylinux, _musllinux - -logger = logging.getLogger(__name__) - -PythonVersion = Sequence[int] -MacVersion = Tuple[int, int] - -INTERPRETER_SHORT_NAMES: Dict[str, str] = { - "python": "py", # Generic. - "cpython": "cp", - "pypy": "pp", - "ironpython": "ip", - "jython": "jy", -} - - -_32_BIT_INTERPRETER = struct.calcsize("P") == 4 - - -class Tag: - """ - A representation of the tag triple for a wheel. - - Instances are considered immutable and thus are hashable. Equality checking - is also supported. - """ - - __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] - - def __init__(self, interpreter: str, abi: str, platform: str) -> None: - self._interpreter = interpreter.lower() - self._abi = abi.lower() - self._platform = platform.lower() - # The __hash__ of every single element in a Set[Tag] will be evaluated each time - # that a set calls its `.disjoint()` method, which may be called hundreds of - # times when scanning a page of links for packages with tags matching that - # Set[Tag]. Pre-computing the value here produces significant speedups for - # downstream consumers. - self._hash = hash((self._interpreter, self._abi, self._platform)) - - @property - def interpreter(self) -> str: - return self._interpreter - - @property - def abi(self) -> str: - return self._abi - - @property - def platform(self) -> str: - return self._platform - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Tag): - return NotImplemented - - return ( - (self._hash == other._hash) # Short-circuit ASAP for perf reasons. - and (self._platform == other._platform) - and (self._abi == other._abi) - and (self._interpreter == other._interpreter) - ) - - def __hash__(self) -> int: - return self._hash - - def __str__(self) -> str: - return f"{self._interpreter}-{self._abi}-{self._platform}" - - def __repr__(self) -> str: - return f"<{self} @ {id(self)}>" - - -def parse_tag(tag: str) -> FrozenSet[Tag]: - """ - Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. - - Returning a set is required due to the possibility that the tag is a - compressed tag set. - """ - tags = set() - interpreters, abis, platforms = tag.split("-") - for interpreter in interpreters.split("."): - for abi in abis.split("."): - for platform_ in platforms.split("."): - tags.add(Tag(interpreter, abi, platform_)) - return frozenset(tags) - - -def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: - value: Union[int, str, None] = sysconfig.get_config_var(name) - if value is None and warn: - logger.debug( - "Config variable '%s' is unset, Python ABI tag may be incorrect", name - ) - return value - - -def _normalize_string(string: str) -> str: - return string.replace(".", "_").replace("-", "_").replace(" ", "_") - - -def _is_threaded_cpython(abis: List[str]) -> bool: - """ - Determine if the ABI corresponds to a threaded (`--disable-gil`) build. - - The threaded builds are indicated by a "t" in the abiflags. - """ - if len(abis) == 0: - return False - # expect e.g., cp313 - m = re.match(r"cp\d+(.*)", abis[0]) - if not m: - return False - abiflags = m.group(1) - return "t" in abiflags - - -def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: - """ - Determine if the Python version supports abi3. - - PEP 384 was first implemented in Python 3.2. The threaded (`--disable-gil`) - builds do not support abi3. - """ - return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading - - -def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: - py_version = tuple(py_version) # To allow for version comparison. - abis = [] - version = _version_nodot(py_version[:2]) - threading = debug = pymalloc = ucs4 = "" - with_debug = _get_config_var("Py_DEBUG", warn) - has_refcount = hasattr(sys, "gettotalrefcount") - # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled - # extension modules is the best option. - # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 - has_ext = "_d.pyd" in EXTENSION_SUFFIXES - if with_debug or (with_debug is None and (has_refcount or has_ext)): - debug = "d" - if py_version >= (3, 13) and _get_config_var("Py_GIL_DISABLED", warn): - threading = "t" - if py_version < (3, 8): - with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) - if with_pymalloc or with_pymalloc is None: - pymalloc = "m" - if py_version < (3, 3): - unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) - if unicode_size == 4 or ( - unicode_size is None and sys.maxunicode == 0x10FFFF - ): - ucs4 = "u" - elif debug: - # Debug builds can also load "normal" extension modules. - # We can also assume no UCS-4 or pymalloc requirement. - abis.append(f"cp{version}{threading}") - abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}") - return abis - - -def cpython_tags( - python_version: Optional[PythonVersion] = None, - abis: Optional[Iterable[str]] = None, - platforms: Optional[Iterable[str]] = None, - *, - warn: bool = False, -) -> Iterator[Tag]: - """ - Yields the tags for a CPython interpreter. - - The tags consist of: - - cp<python_version>-<abi>-<platform> - - cp<python_version>-abi3-<platform> - - cp<python_version>-none-<platform> - - cp<less than python_version>-abi3-<platform> # Older Python versions down to 3.2. - - If python_version only specifies a major version then user-provided ABIs and - the 'none' ABItag will be used. - - If 'abi3' or 'none' are specified in 'abis' then they will be yielded at - their normal position and not at the beginning. - """ - if not python_version: - python_version = sys.version_info[:2] - - interpreter = f"cp{_version_nodot(python_version[:2])}" - - if abis is None: - if len(python_version) > 1: - abis = _cpython_abis(python_version, warn) - else: - abis = [] - abis = list(abis) - # 'abi3' and 'none' are explicitly handled later. - for explicit_abi in ("abi3", "none"): - try: - abis.remove(explicit_abi) - except ValueError: - pass - - platforms = list(platforms or platform_tags()) - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - threading = _is_threaded_cpython(abis) - use_abi3 = _abi3_applies(python_version, threading) - if use_abi3: - yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) - yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) - - if use_abi3: - for minor_version in range(python_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{version}".format( - version=_version_nodot((python_version[0], minor_version)) - ) - yield Tag(interpreter, "abi3", platform_) - - -def _generic_abi() -> List[str]: - """ - Return the ABI tag based on EXT_SUFFIX. - """ - # The following are examples of `EXT_SUFFIX`. - # We want to keep the parts which are related to the ABI and remove the - # parts which are related to the platform: - # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 - # - mac: '.cpython-310-darwin.so' => cp310 - # - win: '.cp310-win_amd64.pyd' => cp310 - # - win: '.pyd' => cp37 (uses _cpython_abis()) - # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 - # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' - # => graalpy_38_native - - ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) - if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": - raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") - parts = ext_suffix.split(".") - if len(parts) < 3: - # CPython3.7 and earlier uses ".pyd" on Windows. - return _cpython_abis(sys.version_info[:2]) - soabi = parts[1] - if soabi.startswith("cpython"): - # non-windows - abi = "cp" + soabi.split("-")[1] - elif soabi.startswith("cp"): - # windows - abi = soabi.split("-")[0] - elif soabi.startswith("pypy"): - abi = "-".join(soabi.split("-")[:2]) - elif soabi.startswith("graalpy"): - abi = "-".join(soabi.split("-")[:3]) - elif soabi: - # pyston, ironpython, others? - abi = soabi - else: - return [] - return [_normalize_string(abi)] - - -def generic_tags( - interpreter: Optional[str] = None, - abis: Optional[Iterable[str]] = None, - platforms: Optional[Iterable[str]] = None, - *, - warn: bool = False, -) -> Iterator[Tag]: - """ - Yields the tags for a generic interpreter. - - The tags consist of: - - <interpreter>-<abi>-<platform> - - The "none" ABI will be added if it was not explicitly provided. - """ - if not interpreter: - interp_name = interpreter_name() - interp_version = interpreter_version(warn=warn) - interpreter = "".join([interp_name, interp_version]) - if abis is None: - abis = _generic_abi() - else: - abis = list(abis) - platforms = list(platforms or platform_tags()) - if "none" not in abis: - abis.append("none") - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - -def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: - """ - Yields Python versions in descending order. - - After the latest version, the major-only version will be yielded, and then - all previous versions of that major version. - """ - if len(py_version) > 1: - yield f"py{_version_nodot(py_version[:2])}" - yield f"py{py_version[0]}" - if len(py_version) > 1: - for minor in range(py_version[1] - 1, -1, -1): - yield f"py{_version_nodot((py_version[0], minor))}" - - -def compatible_tags( - python_version: Optional[PythonVersion] = None, - interpreter: Optional[str] = None, - platforms: Optional[Iterable[str]] = None, -) -> Iterator[Tag]: - """ - Yields the sequence of tags that are compatible with a specific version of Python. - - The tags consist of: - - py*-none-<platform> - - <interpreter>-none-any # ... if `interpreter` is provided. - - py*-none-any - """ - if not python_version: - python_version = sys.version_info[:2] - platforms = list(platforms or platform_tags()) - for version in _py_interpreter_range(python_version): - for platform_ in platforms: - yield Tag(version, "none", platform_) - if interpreter: - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(python_version): - yield Tag(version, "none", "any") - - -def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: - if not is_32bit: - return arch - - if arch.startswith("ppc"): - return "ppc" - - return "i386" - - -def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: - formats = [cpu_arch] - if cpu_arch == "x86_64": - if version < (10, 4): - return [] - formats.extend(["intel", "fat64", "fat32"]) - - elif cpu_arch == "i386": - if version < (10, 4): - return [] - formats.extend(["intel", "fat32", "fat"]) - - elif cpu_arch == "ppc64": - # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? - if version > (10, 5) or version < (10, 4): - return [] - formats.append("fat64") - - elif cpu_arch == "ppc": - if version > (10, 6): - return [] - formats.extend(["fat32", "fat"]) - - if cpu_arch in {"arm64", "x86_64"}: - formats.append("universal2") - - if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: - formats.append("universal") - - return formats - - -def mac_platforms( - version: Optional[MacVersion] = None, arch: Optional[str] = None -) -> Iterator[str]: - """ - Yields the platform tags for a macOS system. - - The `version` parameter is a two-item tuple specifying the macOS version to - generate platform tags for. The `arch` parameter is the CPU architecture to - generate platform tags for. Both parameters default to the appropriate value - for the current system. - """ - version_str, _, cpu_arch = platform.mac_ver() - if version is None: - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) - if version == (10, 16): - # When built against an older macOS SDK, Python will report macOS 10.16 - # instead of the real version. - version_str = subprocess.run( - [ - sys.executable, - "-sS", - "-c", - "import platform; print(platform.mac_ver()[0])", - ], - check=True, - env={"SYSTEM_VERSION_COMPAT": "0"}, - stdout=subprocess.PIPE, - text=True, - ).stdout - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) - else: - version = version - if arch is None: - arch = _mac_arch(cpu_arch) - else: - arch = arch - - if (10, 0) <= version and version < (11, 0): - # Prior to Mac OS 11, each yearly release of Mac OS bumped the - # "minor" version number. The major version was always 10. - for minor_version in range(version[1], -1, -1): - compat_version = 10, minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=10, minor=minor_version, binary_format=binary_format - ) - - if version >= (11, 0): - # Starting with Mac OS 11, each yearly release bumps the major version - # number. The minor versions are now the midyear updates. - for major_version in range(version[0], 10, -1): - compat_version = major_version, 0 - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=major_version, minor=0, binary_format=binary_format - ) - - if version >= (11, 0): - # Mac OS 11 on x86_64 is compatible with binaries from previous releases. - # Arm64 support was introduced in 11.0, so no Arm binaries from previous - # releases exist. - # - # However, the "universal2" binary format can have a - # macOS version earlier than 11.0 when the x86_64 part of the binary supports - # that version of macOS. - if arch == "x86_64": - for minor_version in range(16, 3, -1): - compat_version = 10, minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - else: - for minor_version in range(16, 3, -1): - compat_version = 10, minor_version - binary_format = "universal2" - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - - -def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: - linux = _normalize_string(sysconfig.get_platform()) - if not linux.startswith("linux_"): - # we should never be here, just yield the sysconfig one and return - yield linux - return - if is_32bit: - if linux == "linux_x86_64": - linux = "linux_i686" - elif linux == "linux_aarch64": - linux = "linux_armv8l" - _, arch = linux.split("_", 1) - archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch]) - yield from _manylinux.platform_tags(archs) - yield from _musllinux.platform_tags(archs) - for arch in archs: - yield f"linux_{arch}" - - -def _generic_platforms() -> Iterator[str]: - yield _normalize_string(sysconfig.get_platform()) - - -def platform_tags() -> Iterator[str]: - """ - Provides the platform tags for this installation. - """ - if platform.system() == "Darwin": - return mac_platforms() - elif platform.system() == "Linux": - return _linux_platforms() - else: - return _generic_platforms() - - -def interpreter_name() -> str: - """ - Returns the name of the running interpreter. - - Some implementations have a reserved, two-letter abbreviation which will - be returned when appropriate. - """ - name = sys.implementation.name - return INTERPRETER_SHORT_NAMES.get(name) or name - - -def interpreter_version(*, warn: bool = False) -> str: - """ - Returns the version of the running interpreter. - """ - version = _get_config_var("py_version_nodot", warn=warn) - if version: - version = str(version) - else: - version = _version_nodot(sys.version_info[:2]) - return version - - -def _version_nodot(version: PythonVersion) -> str: - return "".join(map(str, version)) - - -def sys_tags(*, warn: bool = False) -> Iterator[Tag]: - """ - Returns the sequence of tag triples for the running interpreter. - - The order of the sequence corresponds to priority order for the - interpreter, from most to least important. - """ - - interp_name = interpreter_name() - if interp_name == "cp": - yield from cpython_tags(warn=warn) - else: - yield from generic_tags() - - if interp_name == "pp": - interp = "pp3" - elif interp_name == "cp": - interp = "cp" + interpreter_version(warn=warn) - else: - interp = None - yield from compatible_tags(interpreter=interp) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/utils.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/utils.py deleted file mode 100644 index c2c2f75aa8..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/utils.py +++ /dev/null @@ -1,172 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import re -from typing import FrozenSet, NewType, Tuple, Union, cast - -from .tags import Tag, parse_tag -from .version import InvalidVersion, Version - -BuildTag = Union[Tuple[()], Tuple[int, str]] -NormalizedName = NewType("NormalizedName", str) - - -class InvalidName(ValueError): - """ - An invalid distribution name; users should refer to the packaging user guide. - """ - - -class InvalidWheelFilename(ValueError): - """ - An invalid wheel filename was found, users should refer to PEP 427. - """ - - -class InvalidSdistFilename(ValueError): - """ - An invalid sdist filename was found, users should refer to the packaging user guide. - """ - - -# Core metadata spec for `Name` -_validate_regex = re.compile( - r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE -) -_canonicalize_regex = re.compile(r"[-_.]+") -_normalized_regex = re.compile(r"^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$") -# PEP 427: The build number must start with a digit. -_build_tag_regex = re.compile(r"(\d+)(.*)") - - -def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName: - if validate and not _validate_regex.match(name): - raise InvalidName(f"name is invalid: {name!r}") - # This is taken from PEP 503. - value = _canonicalize_regex.sub("-", name).lower() - return cast(NormalizedName, value) - - -def is_normalized_name(name: str) -> bool: - return _normalized_regex.match(name) is not None - - -def canonicalize_version( - version: Union[Version, str], *, strip_trailing_zero: bool = True -) -> str: - """ - This is very similar to Version.__str__, but has one subtle difference - with the way it handles the release segment. - """ - if isinstance(version, str): - try: - parsed = Version(version) - except InvalidVersion: - # Legacy versions cannot be normalized - return version - else: - parsed = version - - parts = [] - - # Epoch - if parsed.epoch != 0: - parts.append(f"{parsed.epoch}!") - - # Release segment - release_segment = ".".join(str(x) for x in parsed.release) - if strip_trailing_zero: - # NB: This strips trailing '.0's to normalize - release_segment = re.sub(r"(\.0)+$", "", release_segment) - parts.append(release_segment) - - # Pre-release - if parsed.pre is not None: - parts.append("".join(str(x) for x in parsed.pre)) - - # Post-release - if parsed.post is not None: - parts.append(f".post{parsed.post}") - - # Development release - if parsed.dev is not None: - parts.append(f".dev{parsed.dev}") - - # Local version segment - if parsed.local is not None: - parts.append(f"+{parsed.local}") - - return "".join(parts) - - -def parse_wheel_filename( - filename: str, -) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: - if not filename.endswith(".whl"): - raise InvalidWheelFilename( - f"Invalid wheel filename (extension must be '.whl'): {filename}" - ) - - filename = filename[:-4] - dashes = filename.count("-") - if dashes not in (4, 5): - raise InvalidWheelFilename( - f"Invalid wheel filename (wrong number of parts): {filename}" - ) - - parts = filename.split("-", dashes - 2) - name_part = parts[0] - # See PEP 427 for the rules on escaping the project name. - if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: - raise InvalidWheelFilename(f"Invalid project name: {filename}") - name = canonicalize_name(name_part) - - try: - version = Version(parts[1]) - except InvalidVersion as e: - raise InvalidWheelFilename( - f"Invalid wheel filename (invalid version): {filename}" - ) from e - - if dashes == 5: - build_part = parts[2] - build_match = _build_tag_regex.match(build_part) - if build_match is None: - raise InvalidWheelFilename( - f"Invalid build number: {build_part} in '{filename}'" - ) - build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) - else: - build = () - tags = parse_tag(parts[-1]) - return (name, version, build, tags) - - -def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: - if filename.endswith(".tar.gz"): - file_stem = filename[: -len(".tar.gz")] - elif filename.endswith(".zip"): - file_stem = filename[: -len(".zip")] - else: - raise InvalidSdistFilename( - f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" - f" {filename}" - ) - - # We are requiring a PEP 440 version, which cannot contain dashes, - # so we split on the last dash. - name_part, sep, version_part = file_stem.rpartition("-") - if not sep: - raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") - - name = canonicalize_name(name_part) - - try: - version = Version(version_part) - except InvalidVersion as e: - raise InvalidSdistFilename( - f"Invalid sdist filename (invalid version): {filename}" - ) from e - - return (name, version) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/version.py b/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/version.py deleted file mode 100644 index 5faab9bd0d..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/_vendor/packaging/version.py +++ /dev/null @@ -1,563 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -""" -.. testsetup:: - - from packaging.version import parse, Version -""" - -import itertools -import re -from typing import Any, Callable, NamedTuple, Optional, SupportsInt, Tuple, Union - -from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType - -__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] - -LocalType = Tuple[Union[int, str], ...] - -CmpPrePostDevType = Union[InfinityType, NegativeInfinityType, Tuple[str, int]] -CmpLocalType = Union[ - NegativeInfinityType, - Tuple[Union[Tuple[int, str], Tuple[NegativeInfinityType, Union[int, str]]], ...], -] -CmpKey = Tuple[ - int, - Tuple[int, ...], - CmpPrePostDevType, - CmpPrePostDevType, - CmpPrePostDevType, - CmpLocalType, -] -VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] - - -class _Version(NamedTuple): - epoch: int - release: Tuple[int, ...] - dev: Optional[Tuple[str, int]] - pre: Optional[Tuple[str, int]] - post: Optional[Tuple[str, int]] - local: Optional[LocalType] - - -def parse(version: str) -> "Version": - """Parse the given version string. - - >>> parse('1.0.dev1') - <Version('1.0.dev1')> - - :param version: The version string to parse. - :raises InvalidVersion: When the version string is not a valid version. - """ - return Version(version) - - -class InvalidVersion(ValueError): - """Raised when a version string is not a valid version. - - >>> Version("invalid") - Traceback (most recent call last): - ... - packaging.version.InvalidVersion: Invalid version: 'invalid' - """ - - -class _BaseVersion: - _key: Tuple[Any, ...] - - def __hash__(self) -> int: - return hash(self._key) - - # Please keep the duplicated `isinstance` check - # in the six comparisons hereunder - # unless you find a way to avoid adding overhead function calls. - def __lt__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key < other._key - - def __le__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key <= other._key - - def __eq__(self, other: object) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key == other._key - - def __ge__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key >= other._key - - def __gt__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key > other._key - - def __ne__(self, other: object) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key != other._key - - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse -_VERSION_PATTERN = r""" - v? - (?: - (?:(?P<epoch>[0-9]+)!)? # epoch - (?P<release>[0-9]+(?:\.[0-9]+)*) # release segment - (?P<pre> # pre-release - [-_\.]? - (?P<pre_l>alpha|a|beta|b|preview|pre|c|rc) - [-_\.]? - (?P<pre_n>[0-9]+)? - )? - (?P<post> # post release - (?:-(?P<post_n1>[0-9]+)) - | - (?: - [-_\.]? - (?P<post_l>post|rev|r) - [-_\.]? - (?P<post_n2>[0-9]+)? - ) - )? - (?P<dev> # dev release - [-_\.]? - (?P<dev_l>dev) - [-_\.]? - (?P<dev_n>[0-9]+)? - )? - ) - (?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version -""" - -VERSION_PATTERN = _VERSION_PATTERN -""" -A string containing the regular expression used to match a valid version. - -The pattern is not anchored at either end, and is intended for embedding in larger -expressions (for example, matching a version number as part of a file name). The -regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE`` -flags set. - -:meta hide-value: -""" - - -class Version(_BaseVersion): - """This class abstracts handling of a project's versions. - - A :class:`Version` instance is comparison aware and can be compared and - sorted using the standard Python interfaces. - - >>> v1 = Version("1.0a5") - >>> v2 = Version("1.0") - >>> v1 - <Version('1.0a5')> - >>> v2 - <Version('1.0')> - >>> v1 < v2 - True - >>> v1 == v2 - False - >>> v1 > v2 - False - >>> v1 >= v2 - False - >>> v1 <= v2 - True - """ - - _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) - _key: CmpKey - - def __init__(self, version: str) -> None: - """Initialize a Version object. - - :param version: - The string representation of a version which will be parsed and normalized - before use. - :raises InvalidVersion: - If the ``version`` does not conform to PEP 440 in any way then this - exception will be raised. - """ - - # Validate the version and parse it into pieces - match = self._regex.search(version) - if not match: - raise InvalidVersion(f"Invalid version: '{version}'") - - # Store the parsed out pieces of the version - self._version = _Version( - epoch=int(match.group("epoch")) if match.group("epoch") else 0, - release=tuple(int(i) for i in match.group("release").split(".")), - pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")), - post=_parse_letter_version( - match.group("post_l"), match.group("post_n1") or match.group("post_n2") - ), - dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")), - local=_parse_local_version(match.group("local")), - ) - - # Generate a key which will be used for sorting - self._key = _cmpkey( - self._version.epoch, - self._version.release, - self._version.pre, - self._version.post, - self._version.dev, - self._version.local, - ) - - def __repr__(self) -> str: - """A representation of the Version that shows all internal state. - - >>> Version('1.0.0') - <Version('1.0.0')> - """ - return f"<Version('{self}')>" - - def __str__(self) -> str: - """A string representation of the version that can be rounded-tripped. - - >>> str(Version("1.0a5")) - '1.0a5' - """ - parts = [] - - # Epoch - if self.epoch != 0: - parts.append(f"{self.epoch}!") - - # Release segment - parts.append(".".join(str(x) for x in self.release)) - - # Pre-release - if self.pre is not None: - parts.append("".join(str(x) for x in self.pre)) - - # Post-release - if self.post is not None: - parts.append(f".post{self.post}") - - # Development release - if self.dev is not None: - parts.append(f".dev{self.dev}") - - # Local version segment - if self.local is not None: - parts.append(f"+{self.local}") - - return "".join(parts) - - @property - def epoch(self) -> int: - """The epoch of the version. - - >>> Version("2.0.0").epoch - 0 - >>> Version("1!2.0.0").epoch - 1 - """ - return self._version.epoch - - @property - def release(self) -> Tuple[int, ...]: - """The components of the "release" segment of the version. - - >>> Version("1.2.3").release - (1, 2, 3) - >>> Version("2.0.0").release - (2, 0, 0) - >>> Version("1!2.0.0.post0").release - (2, 0, 0) - - Includes trailing zeroes but not the epoch or any pre-release / development / - post-release suffixes. - """ - return self._version.release - - @property - def pre(self) -> Optional[Tuple[str, int]]: - """The pre-release segment of the version. - - >>> print(Version("1.2.3").pre) - None - >>> Version("1.2.3a1").pre - ('a', 1) - >>> Version("1.2.3b1").pre - ('b', 1) - >>> Version("1.2.3rc1").pre - ('rc', 1) - """ - return self._version.pre - - @property - def post(self) -> Optional[int]: - """The post-release number of the version. - - >>> print(Version("1.2.3").post) - None - >>> Version("1.2.3.post1").post - 1 - """ - return self._version.post[1] if self._version.post else None - - @property - def dev(self) -> Optional[int]: - """The development number of the version. - - >>> print(Version("1.2.3").dev) - None - >>> Version("1.2.3.dev1").dev - 1 - """ - return self._version.dev[1] if self._version.dev else None - - @property - def local(self) -> Optional[str]: - """The local version segment of the version. - - >>> print(Version("1.2.3").local) - None - >>> Version("1.2.3+abc").local - 'abc' - """ - if self._version.local: - return ".".join(str(x) for x in self._version.local) - else: - return None - - @property - def public(self) -> str: - """The public portion of the version. - - >>> Version("1.2.3").public - '1.2.3' - >>> Version("1.2.3+abc").public - '1.2.3' - >>> Version("1.2.3+abc.dev1").public - '1.2.3' - """ - return str(self).split("+", 1)[0] - - @property - def base_version(self) -> str: - """The "base version" of the version. - - >>> Version("1.2.3").base_version - '1.2.3' - >>> Version("1.2.3+abc").base_version - '1.2.3' - >>> Version("1!1.2.3+abc.dev1").base_version - '1!1.2.3' - - The "base version" is the public version of the project without any pre or post - release markers. - """ - parts = [] - - # Epoch - if self.epoch != 0: - parts.append(f"{self.epoch}!") - - # Release segment - parts.append(".".join(str(x) for x in self.release)) - - return "".join(parts) - - @property - def is_prerelease(self) -> bool: - """Whether this version is a pre-release. - - >>> Version("1.2.3").is_prerelease - False - >>> Version("1.2.3a1").is_prerelease - True - >>> Version("1.2.3b1").is_prerelease - True - >>> Version("1.2.3rc1").is_prerelease - True - >>> Version("1.2.3dev1").is_prerelease - True - """ - return self.dev is not None or self.pre is not None - - @property - def is_postrelease(self) -> bool: - """Whether this version is a post-release. - - >>> Version("1.2.3").is_postrelease - False - >>> Version("1.2.3.post1").is_postrelease - True - """ - return self.post is not None - - @property - def is_devrelease(self) -> bool: - """Whether this version is a development release. - - >>> Version("1.2.3").is_devrelease - False - >>> Version("1.2.3.dev1").is_devrelease - True - """ - return self.dev is not None - - @property - def major(self) -> int: - """The first item of :attr:`release` or ``0`` if unavailable. - - >>> Version("1.2.3").major - 1 - """ - return self.release[0] if len(self.release) >= 1 else 0 - - @property - def minor(self) -> int: - """The second item of :attr:`release` or ``0`` if unavailable. - - >>> Version("1.2.3").minor - 2 - >>> Version("1").minor - 0 - """ - return self.release[1] if len(self.release) >= 2 else 0 - - @property - def micro(self) -> int: - """The third item of :attr:`release` or ``0`` if unavailable. - - >>> Version("1.2.3").micro - 3 - >>> Version("1").micro - 0 - """ - return self.release[2] if len(self.release) >= 3 else 0 - - -def _parse_letter_version( - letter: Optional[str], number: Union[str, bytes, SupportsInt, None] -) -> Optional[Tuple[str, int]]: - - if letter: - # We consider there to be an implicit 0 in a pre-release if there is - # not a numeral associated with it. - if number is None: - number = 0 - - # We normalize any letters to their lower case form - letter = letter.lower() - - # We consider some words to be alternate spellings of other words and - # in those cases we want to normalize the spellings to our preferred - # spelling. - if letter == "alpha": - letter = "a" - elif letter == "beta": - letter = "b" - elif letter in ["c", "pre", "preview"]: - letter = "rc" - elif letter in ["rev", "r"]: - letter = "post" - - return letter, int(number) - if not letter and number: - # We assume if we are given a number, but we are not given a letter - # then this is using the implicit post release syntax (e.g. 1.0-1) - letter = "post" - - return letter, int(number) - - return None - - -_local_version_separators = re.compile(r"[\._-]") - - -def _parse_local_version(local: Optional[str]) -> Optional[LocalType]: - """ - Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). - """ - if local is not None: - return tuple( - part.lower() if not part.isdigit() else int(part) - for part in _local_version_separators.split(local) - ) - return None - - -def _cmpkey( - epoch: int, - release: Tuple[int, ...], - pre: Optional[Tuple[str, int]], - post: Optional[Tuple[str, int]], - dev: Optional[Tuple[str, int]], - local: Optional[LocalType], -) -> CmpKey: - - # When we compare a release version, we want to compare it with all of the - # trailing zeros removed. So we'll use a reverse the list, drop all the now - # leading zeros until we come to something non zero, then take the rest - # re-reverse it back into the correct order and make it a tuple and use - # that for our sorting key. - _release = tuple( - reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) - ) - - # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. - # We'll do this by abusing the pre segment, but we _only_ want to do this - # if there is not a pre or a post segment. If we have one of those then - # the normal sorting rules will handle this case correctly. - if pre is None and post is None and dev is not None: - _pre: CmpPrePostDevType = NegativeInfinity - # Versions without a pre-release (except as noted above) should sort after - # those with one. - elif pre is None: - _pre = Infinity - else: - _pre = pre - - # Versions without a post segment should sort before those with one. - if post is None: - _post: CmpPrePostDevType = NegativeInfinity - - else: - _post = post - - # Versions without a development segment should sort after those with one. - if dev is None: - _dev: CmpPrePostDevType = Infinity - - else: - _dev = dev - - if local is None: - # Versions without a local segment should sort before those with one. - _local: CmpLocalType = NegativeInfinity - else: - # Versions with a local segment need that segment parsed to implement - # the sorting rules in PEP440. - # - Alpha numeric segments sort before numeric segments - # - Alpha numeric segments sort lexicographically - # - Numeric segments sort numerically - # - Shorter versions sort before longer versions when the prefixes - # match exactly - _local = tuple( - (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local - ) - - return epoch, _release, _pre, _post, _dev, _local diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py b/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py index 07cd97f4d0..cc4699b1a3 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py @@ -266,8 +266,7 @@ def make_archive( raise ValueError(f"unknown archive format '{format}'") func = format_info[0] - for arg, val in format_info[1]: - kwargs[arg] = val + kwargs.update(format_info[1]) if format != 'zip': kwargs['owner'] = owner diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py index e47dca5d09..9157b43328 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py @@ -236,8 +236,7 @@ class BCPPCompiler(CCompiler): temp_dir = os.path.dirname(objects[0]) # preserve tree structure def_file = os.path.join(temp_dir, f'{modname}.def') contents = ['EXPORTS'] - for sym in export_symbols or []: - contents.append(f' {sym}=_{sym}') + contents.extend(f' {sym}=_{sym}' for sym in export_symbols) self.execute(write_file, (def_file, contents), f"writing {def_file}") # Borland C++ has problems with '/' in paths diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py index 9d5297b944..bc4743bcbf 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py @@ -22,7 +22,7 @@ from .errors import ( ) from .file_util import move_file from .spawn import spawn -from .util import execute, split_quoted, is_mingw +from .util import execute, is_mingw, split_quoted class CCompiler: @@ -1124,10 +1124,10 @@ def show_compilers(): # commands that use it. from distutils.fancy_getopt import FancyGetopt - compilers = [] - for compiler in compiler_class.keys(): - compilers.append(("compiler=" + compiler, None, compiler_class[compiler][2])) - compilers.sort() + compilers = sorted( + ("compiler=" + compiler, None, compiler_class[compiler][2]) + for compiler in compiler_class.keys() + ) pretty_printer = FancyGetopt(compilers) pretty_printer.print_help("List of available compilers:") @@ -1218,8 +1218,7 @@ def gen_preprocess_options(macros, include_dirs): # shell at all costs when we spawn the command! pp_opts.append("-D{}={}".format(*macro)) - for dir in include_dirs: - pp_opts.append(f"-I{dir}") + pp_opts.extend(f"-I{dir}" for dir in include_dirs) return pp_opts @@ -1230,10 +1229,7 @@ def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries): directories. Returns a list of command-line options suitable for use with some compiler (depending on the two format strings passed in). """ - lib_opts = [] - - for dir in library_dirs: - lib_opts.append(compiler.library_dir_option(dir)) + lib_opts = [compiler.library_dir_option(dir) for dir in library_dirs] for dir in runtime_library_dirs: lib_opts.extend(always_iterable(compiler.runtime_library_dir_option(dir))) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py index 1738f4e56b..f334075159 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py @@ -15,9 +15,10 @@ def show_formats(): """Print list of available formats (arguments to "--format" option).""" from ..fancy_getopt import FancyGetopt - formats = [] - for format in bdist.format_commands: - formats.append(("formats=" + format, None, bdist.format_commands[format][1])) + formats = [ + ("formats=" + format, None, bdist.format_commands[format][1]) + for format in bdist.format_commands + ] pretty_printer = FancyGetopt(formats) pretty_printer.print_help("List of available distribution formats:") diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py index 18e1601a28..a7e3038be6 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py @@ -465,10 +465,7 @@ class build_ext(Command): # And build the list of output (built) filenames. Note that this # ignores the 'inplace' flag, and assumes everything goes in the # "build" tree. - outputs = [] - for ext in self.extensions: - outputs.append(self.get_ext_fullpath(ext.name)) - return outputs + return [self.get_ext_fullpath(ext.name) for ext in self.extensions] def build_extensions(self): # First, sanity-check the 'extensions' list @@ -641,8 +638,7 @@ class build_ext(Command): # Do not override commandline arguments if not self.swig_opts: - for o in extension.swig_opts: - swig_cmd.append(o) + swig_cmd.extend(extension.swig_opts) for source in swig_sources: target = swig_targets[source] diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/check.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/check.py index 58b3f949f9..93d754e73d 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/command/check.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/check.py @@ -100,10 +100,9 @@ class check(Command): """ metadata = self.distribution.metadata - missing = [] - for attr in 'name', 'version': - if not getattr(metadata, attr, None): - missing.append(attr) + missing = [ + attr for attr in ('name', 'version') if not getattr(metadata, attr, None) + ] if missing: self.warn("missing required meta-data: {}".format(', '.join(missing))) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py index 1fc09eef89..b83e061e02 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py @@ -680,7 +680,7 @@ class install(Command): if not self.user: return home = convert_path(os.path.expanduser("~")) - for _name, path in self.config_vars.items(): + for path in self.config_vars.values(): if str(path).startswith(home) and not os.path.isdir(path): self.debug_print(f"os.makedirs('{path}', 0o700)") os.makedirs(path, 0o700) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py index 624c0b901b..a90ec3b4d0 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py @@ -5,7 +5,11 @@ platform-independent data files.""" # contributed by Bastian Kleineidam +from __future__ import annotations + +import functools import os +from typing import Iterable from ..core import Command from ..util import change_root, convert_path @@ -46,36 +50,42 @@ class install_data(Command): def run(self): self.mkpath(self.install_dir) for f in self.data_files: - if isinstance(f, str): - # it's a simple file, so copy it - f = convert_path(f) - if self.warn_dir: - self.warn( - "setup script did not provide a directory for " - f"'{f}' -- installing right in '{self.install_dir}'" - ) - (out, _) = self.copy_file(f, self.install_dir) + self._copy(f) + + @functools.singledispatchmethod + def _copy(self, f: tuple[str | os.PathLike, Iterable[str | os.PathLike]]): + # it's a tuple with path to install to and a list of files + dir = convert_path(f[0]) + if not os.path.isabs(dir): + dir = os.path.join(self.install_dir, dir) + elif self.root: + dir = change_root(self.root, dir) + self.mkpath(dir) + + if f[1] == []: + # If there are no files listed, the user must be + # trying to create an empty directory, so add the + # directory to the list of output files. + self.outfiles.append(dir) + else: + # Copy files, adding them to the list of output files. + for data in f[1]: + data = convert_path(data) + (out, _) = self.copy_file(data, dir) self.outfiles.append(out) - else: - # it's a tuple with path to install to and a list of files - dir = convert_path(f[0]) - if not os.path.isabs(dir): - dir = os.path.join(self.install_dir, dir) - elif self.root: - dir = change_root(self.root, dir) - self.mkpath(dir) - - if f[1] == []: - # If there are no files listed, the user must be - # trying to create an empty directory, so add the - # directory to the list of output files. - self.outfiles.append(dir) - else: - # Copy files, adding them to the list of output files. - for data in f[1]: - data = convert_path(data) - (out, _) = self.copy_file(data, dir) - self.outfiles.append(out) + + @_copy.register(str) + @_copy.register(os.PathLike) + def _(self, f: str | os.PathLike): + # it's a simple file, so copy it + f = convert_path(f) + if self.warn_dir: + self.warn( + "setup script did not provide a directory for " + f"'{f}' -- installing right in '{self.install_dir}'" + ) + (out, _) = self.copy_file(f, self.install_dir) + self.outfiles.append(out) def get_inputs(self): return self.data_files or [] diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py index 54a12d38a8..4c1230a286 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py @@ -81,9 +81,9 @@ class install_lib(Command): if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) - if self.optimize not in (0, 1, 2): - raise AssertionError - except (ValueError, AssertionError): + except ValueError: + pass + if self.optimize not in (0, 1, 2): raise DistutilsOptionError("optimize must be 0, 1, or 2") def run(self): @@ -161,9 +161,7 @@ class install_lib(Command): build_dir = getattr(build_cmd, cmd_option) prefix_len = len(build_dir) + len(os.sep) - outputs = [] - for file in build_files: - outputs.append(os.path.join(output_dir, file[prefix_len:])) + outputs = [os.path.join(output_dir, file[prefix_len:]) for file in build_files] return outputs diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py index 04333dd214..eda6afe811 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py @@ -24,10 +24,10 @@ def show_formats(): from ..archive_util import ARCHIVE_FORMATS from ..fancy_getopt import FancyGetopt - formats = [] - for format in ARCHIVE_FORMATS.keys(): - formats.append(("formats=" + format, None, ARCHIVE_FORMATS[format][2])) - formats.sort() + formats = sorted( + ("formats=" + format, None, ARCHIVE_FORMATS[format][2]) + for format in ARCHIVE_FORMATS.keys() + ) FancyGetopt(formats).print_help("List of available source distribution formats:") @@ -391,7 +391,7 @@ class sdist(Command): build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() - self.filelist.exclude_pattern(None, prefix=build.build_base) + self.filelist.exclude_pattern(None, prefix=os.fspath(build.build_base)) self.filelist.exclude_pattern(None, prefix=base_dir) if sys.platform == 'win32': diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py b/contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py index 2d44211147..03ec73ef0e 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py @@ -14,6 +14,7 @@ if sys.version_info < (3, 9): return self[len(prefix) :] else: return self[:] + else: def removesuffix(self, suffix): diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py index 7b812fd055..18b1b3557b 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py @@ -9,13 +9,11 @@ cygwin in no-cygwin mode). import copy import os import pathlib -import re import shlex import sys import warnings from subprocess import check_output -from ._collections import RangeMap from .errors import ( CCompilerError, CompileError, @@ -26,42 +24,10 @@ from .file_util import write_file from .unixccompiler import UnixCCompiler from .version import LooseVersion, suppress_known_deprecation -_msvcr_lookup = RangeMap.left( - { - # MSVC 7.0 - 1300: ['msvcr70'], - # MSVC 7.1 - 1310: ['msvcr71'], - # VS2005 / MSVC 8.0 - 1400: ['msvcr80'], - # VS2008 / MSVC 9.0 - 1500: ['msvcr90'], - # VS2010 / MSVC 10.0 - 1600: ['msvcr100'], - # VS2012 / MSVC 11.0 - 1700: ['msvcr110'], - # VS2013 / MSVC 12.0 - 1800: ['msvcr120'], - # VS2015 / MSVC 14.0 - 1900: ['vcruntime140'], - 2000: RangeMap.undefined_value, - }, -) - def get_msvcr(): - """Include the appropriate MSVC runtime library if Python was built - with MSVC 7.0 or later. - """ - match = re.search(r'MSC v\.(\d{4})', sys.version) - try: - msc_ver = int(match.group(1)) - except AttributeError: - return [] - try: - return _msvcr_lookup[msc_ver] - except KeyError: - raise ValueError(f"Unknown MS Compiler version {msc_ver} ") + """No longer needed, but kept for backward compatibility.""" + return [] _runtime_library_dirs_msg = ( @@ -99,18 +65,20 @@ class CygwinCCompiler(UnixCCompiler): self.cxx = os.environ.get('CXX', 'g++') self.linker_dll = self.cc + self.linker_dll_cxx = self.cxx shared_option = "-shared" self.set_executables( compiler=f'{self.cc} -mcygwin -O -Wall', compiler_so=f'{self.cc} -mcygwin -mdll -O -Wall', compiler_cxx=f'{self.cxx} -mcygwin -O -Wall', + compiler_so_cxx=f'{self.cxx} -mcygwin -mdll -O -Wall', linker_exe=f'{self.cc} -mcygwin', - linker_so=(f'{self.linker_dll} -mcygwin {shared_option}'), + linker_so=f'{self.linker_dll} -mcygwin {shared_option}', + linker_exe_cxx=f'{self.cxx} -mcygwin', + linker_so_cxx=f'{self.linker_dll_cxx} -mcygwin {shared_option}', ) - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() @property @@ -138,9 +106,17 @@ class CygwinCCompiler(UnixCCompiler): raise CompileError(msg) else: # for other files use the C-compiler try: - self.spawn( - self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs - ) + if self.detect_language(src) == 'c++': + self.spawn( + self.compiler_so_cxx + + cc_args + + [src, '-o', obj] + + extra_postargs + ) + else: + self.spawn( + self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs + ) except DistutilsExecError as msg: raise CompileError(msg) @@ -196,8 +172,7 @@ class CygwinCCompiler(UnixCCompiler): # Generate .def file contents = [f"LIBRARY {os.path.basename(output_filename)}", "EXPORTS"] - for sym in export_symbols: - contents.append(sym) + contents.extend(export_symbols) self.execute(write_file, (def_file, contents), f"writing {def_file}") # next add options for def-file @@ -276,9 +251,12 @@ class Mingw32CCompiler(CygwinCCompiler): self.set_executables( compiler=f'{self.cc} -O -Wall', compiler_so=f'{self.cc} -shared -O -Wall', + compiler_so_cxx=f'{self.cxx} -shared -O -Wall', compiler_cxx=f'{self.cxx} -O -Wall', linker_exe=f'{self.cc}', linker_so=f'{self.linker_dll} {shared_option}', + linker_exe_cxx=f'{self.cxx}', + linker_so_cxx=f'{self.linker_dll_cxx} {shared_option}', ) def runtime_library_dir_option(self, dir): @@ -330,6 +308,9 @@ def check_config_h(): fn = sysconfig.get_config_h_filename() try: config_h = pathlib.Path(fn).read_text(encoding='utf-8') + except OSError as exc: + return (CONFIG_H_UNCERTAIN, f"couldn't read '{fn}': {exc.strerror}") + else: substring = '__GNUC__' if substring in config_h: code = CONFIG_H_OK @@ -338,8 +319,6 @@ def check_config_h(): code = CONFIG_H_NOTOK mention_inflected = 'does not mention' return code, f"{fn!r} {mention_inflected} {substring!r}" - except OSError as exc: - return (CONFIG_H_UNCERTAIN, f"couldn't read '{fn}': {exc.strerror}") def is_cygwincc(cc): diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/dist.py b/contrib/python/setuptools/py3/setuptools/_distutils/dist.py index d7d4ca8fc8..154301baff 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/dist.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/dist.py @@ -10,15 +10,11 @@ import os import pathlib import re import sys +import warnings from collections.abc import Iterable from email import message_from_file -from ._vendor.packaging.utils import canonicalize_name, canonicalize_version - -try: - import warnings -except ImportError: - warnings = None +from packaging.utils import canonicalize_name, canonicalize_version from ._log import log from .debug import DEBUG @@ -249,10 +245,7 @@ Common commands: (see '--help-commands' for more) attrs['license'] = attrs['licence'] del attrs['licence'] msg = "'licence' distribution option is deprecated; use 'license'" - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + "\n") + warnings.warn(msg) # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! @@ -354,7 +347,8 @@ Common commands: (see '--help-commands' for more) prefix = '.' * (os.name == 'posix') filename = prefix + 'pydistutils.cfg' if self.want_user_cfg: - yield pathlib.Path('~').expanduser() / filename + with contextlib.suppress(RuntimeError): + yield pathlib.Path('~').expanduser() / filename # All platforms support local setup.cfg yield pathlib.Path('setup.cfg') @@ -658,7 +652,7 @@ Common commands: (see '--help-commands' for more) ) print() - for command in self.commands: + for command in commands: if isinstance(command, type) and issubclass(command, Command): klass = command else: @@ -741,14 +735,9 @@ Common commands: (see '--help-commands' for more) import distutils.command std_commands = distutils.command.__all__ - is_std = set() - for cmd in std_commands: - is_std.add(cmd) + is_std = set(std_commands) - extra_commands = [] - for cmd in self.cmdclass.keys(): - if cmd not in is_std: - extra_commands.append(cmd) + extra_commands = [cmd for cmd in self.cmdclass.keys() if cmd not in is_std] max_length = 0 for cmd in std_commands + extra_commands: @@ -772,14 +761,9 @@ Common commands: (see '--help-commands' for more) import distutils.command std_commands = distutils.command.__all__ - is_std = set() - for cmd in std_commands: - is_std.add(cmd) + is_std = set(std_commands) - extra_commands = [] - for cmd in self.cmdclass.keys(): - if cmd not in is_std: - extra_commands.append(cmd) + extra_commands = [cmd for cmd in self.cmdclass.keys() if cmd not in is_std] rv = [] for cmd in std_commands + extra_commands: @@ -1301,7 +1285,4 @@ def fix_help_options(options): """Convert a 4-tuple 'help_options' list as found in various command classes to the 3-tuple form required by FancyGetopt. """ - new_options = [] - for help_tuple in options: - new_options.append(help_tuple[0:3]) - return new_options + return [opt[0:3] for opt in options] diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/extension.py b/contrib/python/setuptools/py3/setuptools/_distutils/extension.py index 04e871bcd6..33159079c1 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/extension.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/extension.py @@ -26,7 +26,7 @@ class Extension: name : string the full name of the extension, including any packages -- ie. *not* a filename or pathname, but Python dotted name - sources : [string] + sources : [string | os.PathLike] list of source filenames, relative to the distribution root (where the setup script lives), in Unix form (slash-separated) for portability. Source files may be C, C++, SWIG (.i), @@ -105,12 +105,17 @@ class Extension: **kw, # To catch unknown keywords ): if not isinstance(name, str): - raise AssertionError("'name' must be a string") - if not (isinstance(sources, list) and all(isinstance(v, str) for v in sources)): - raise AssertionError("'sources' must be a list of strings") + raise AssertionError("'name' must be a string") # noqa: TRY004 + if not ( + isinstance(sources, list) + and all(isinstance(v, (str, os.PathLike)) for v in sources) + ): + raise AssertionError( + "'sources' must be a list of strings or PathLike objects." + ) self.name = name - self.sources = sources + self.sources = list(map(os.fspath, sources)) self.include_dirs = include_dirs or [] self.define_macros = define_macros or [] self.undef_macros = undef_macros or [] diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/file_util.py b/contrib/python/setuptools/py3/setuptools/_distutils/file_util.py index b19a5dcfa4..85ee4dafcb 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/file_util.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/file_util.py @@ -140,12 +140,13 @@ def copy_file( # noqa: C901 if not (os.path.exists(dst) and os.path.samefile(src, dst)): try: os.link(src, dst) - return (dst, 1) except OSError: # If hard linking fails, fall back on copying file # (some special filesystems don't support hard linking # even under Unix, see issue #8876). pass + else: + return (dst, 1) elif link == 'sym': if not (os.path.exists(dst) and os.path.samefile(src, dst)): os.symlink(src, dst) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/msvc9compiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/msvc9compiler.py deleted file mode 100644 index f860a8d383..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/msvc9compiler.py +++ /dev/null @@ -1,824 +0,0 @@ -"""distutils.msvc9compiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio 2008. - -The module is compatible with VS 2005 and VS 2008. You can find legacy support -for older versions of VS in distutils.msvccompiler. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) -# ported to VS2005 and VS 2008 by Christian Heimes - -import os -import re -import subprocess -import sys -import warnings -import winreg - -from ._log import log -from .ccompiler import CCompiler, gen_lib_options -from .errors import ( - CompileError, - DistutilsExecError, - DistutilsPlatformError, - LibError, - LinkError, -) -from .util import get_platform - -warnings.warn( - "msvc9compiler is deprecated and slated to be removed " - "in the future. Please discontinue use or file an issue " - "with pypa/distutils describing your use case.", - DeprecationWarning, -) - -RegOpenKeyEx = winreg.OpenKeyEx -RegEnumKey = winreg.EnumKey -RegEnumValue = winreg.EnumValue -RegError = winreg.error - -HKEYS = ( - winreg.HKEY_USERS, - winreg.HKEY_CURRENT_USER, - winreg.HKEY_LOCAL_MACHINE, - winreg.HKEY_CLASSES_ROOT, -) - -NATIVE_WIN64 = sys.platform == 'win32' and sys.maxsize > 2**32 -if NATIVE_WIN64: - # Visual C++ is a 32-bit application, so we need to look in - # the corresponding registry branch, if we're running a - # 64-bit Python on Win64 - VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" - WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" - NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" -else: - VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" - WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" - NET_BASE = r"Software\Microsoft\.NETFramework" - -# A map keyed by get_platform() return values to values accepted by -# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -# the param to cross-compile on x86 targeting amd64.) -PLAT_TO_VCVARS = { - 'win32': 'x86', - 'win-amd64': 'amd64', -} - - -class Reg: - """Helper class to read values from the registry""" - - def get_value(cls, path, key): - for base in HKEYS: - d = cls.read_values(base, path) - if d and key in d: - return d[key] - raise KeyError(key) - - get_value = classmethod(get_value) - - def read_keys(cls, base, key): - """Return list of registry keys.""" - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - L = [] - i = 0 - while True: - try: - k = RegEnumKey(handle, i) - except RegError: - break - L.append(k) - i += 1 - return L - - read_keys = classmethod(read_keys) - - def read_values(cls, base, key): - """Return dict of registry keys and values. - - All names are converted to lowercase. - """ - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - d = {} - i = 0 - while True: - try: - name, value, type = RegEnumValue(handle, i) - except RegError: - break - name = name.lower() - d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) - i += 1 - return d - - read_values = classmethod(read_values) - - def convert_mbcs(s): - dec = getattr(s, "decode", None) - if dec is not None: - try: - s = dec("mbcs") - except UnicodeError: - pass - return s - - convert_mbcs = staticmethod(convert_mbcs) - - -class MacroExpander: - def __init__(self, version): - self.macros = {} - self.vsbase = VS_BASE % version - self.load_macros(version) - - def set_macro(self, macro, path, key): - self.macros[f"$({macro})"] = Reg.get_value(path, key) - - def load_macros(self, version): - self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") - self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") - self.set_macro("FrameworkDir", NET_BASE, "installroot") - try: - if version >= 8.0: - self.set_macro("FrameworkSDKDir", NET_BASE, "sdkinstallrootv2.0") - else: - raise KeyError("sdkinstallrootv2.0") - except KeyError: - raise DistutilsPlatformError( - """Python was built with Visual Studio 2008; -extensions must be built with a compiler than can generate compatible binaries. -Visual Studio 2008 was not found on this system. If you have Cygwin installed, -you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""" - ) - - if version >= 9.0: - self.set_macro("FrameworkVersion", self.vsbase, "clr version") - self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") - else: - p = r"Software\Microsoft\NET Framework Setup\Product" - for base in HKEYS: - try: - h = RegOpenKeyEx(base, p) - except RegError: - continue - key = RegEnumKey(h, 0) - d = Reg.get_value(base, rf"{p}\{key}") - self.macros["$(FrameworkVersion)"] = d["version"] - - def sub(self, s): - for k, v in self.macros.items(): - s = s.replace(k, v) - return s - - -def get_build_version(): - """Return the version of MSVC that was used to build Python. - - For Python 2.3 and up, the version number is included in - sys.version. For earlier versions, assume the compiler is MSVC 6. - """ - prefix = "MSC v." - i = sys.version.find(prefix) - if i == -1: - return 6 - i = i + len(prefix) - s, rest = sys.version[i:].split(" ", 1) - majorVersion = int(s[:-2]) - 6 - if majorVersion >= 13: - # v13 was skipped and should be v14 - majorVersion += 1 - minorVersion = int(s[2:3]) / 10.0 - # I don't think paths are affected by minor version in version 6 - if majorVersion == 6: - minorVersion = 0 - if majorVersion >= 6: - return majorVersion + minorVersion - # else we don't know what version of the compiler this is - return None - - -def normalize_and_reduce_paths(paths): - """Return a list of normalized paths with duplicates removed. - - The current order of paths is maintained. - """ - # Paths are normalized so things like: /a and /a/ aren't both preserved. - reduced_paths = [] - for p in paths: - np = os.path.normpath(p) - # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. - if np not in reduced_paths: - reduced_paths.append(np) - return reduced_paths - - -def removeDuplicates(variable): - """Remove duplicate values of an environment variable.""" - oldList = variable.split(os.pathsep) - newList = [] - for i in oldList: - if i not in newList: - newList.append(i) - newVariable = os.pathsep.join(newList) - return newVariable - - -def find_vcvarsall(version): - """Find the vcvarsall.bat file - - At first it tries to find the productdir of VS 2008 in the registry. If - that fails it falls back to the VS90COMNTOOLS env var. - """ - vsbase = VS_BASE % version - try: - productdir = Reg.get_value(rf"{vsbase}\Setup\VC", "productdir") - except KeyError: - log.debug("Unable to find productdir in registry") - productdir = None - - if not productdir or not os.path.isdir(productdir): - toolskey = f"VS{version:0.0f}0COMNTOOLS" - toolsdir = os.environ.get(toolskey, None) - - if toolsdir and os.path.isdir(toolsdir): - productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") - productdir = os.path.abspath(productdir) - if not os.path.isdir(productdir): - log.debug(f"{productdir} is not a valid directory") - return None - else: - log.debug(f"Env var {toolskey} is not set or invalid") - if not productdir: - log.debug("No productdir found") - return None - vcvarsall = os.path.join(productdir, "vcvarsall.bat") - if os.path.isfile(vcvarsall): - return vcvarsall - log.debug("Unable to find vcvarsall.bat") - return None - - -def query_vcvarsall(version, arch="x86"): - """Launch vcvarsall.bat and read the settings from its environment""" - vcvarsall = find_vcvarsall(version) - interesting = {"include", "lib", "libpath", "path"} - result = {} - - if vcvarsall is None: - raise DistutilsPlatformError("Unable to find vcvarsall.bat") - log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) - popen = subprocess.Popen( - f'"{vcvarsall}" {arch} & set', - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - try: - stdout, stderr = popen.communicate() - if popen.wait() != 0: - raise DistutilsPlatformError(stderr.decode("mbcs")) - - stdout = stdout.decode("mbcs") - for line in stdout.split("\n"): - line = Reg.convert_mbcs(line) - if '=' not in line: - continue - line = line.strip() - key, value = line.split('=', 1) - key = key.lower() - if key in interesting: - if value.endswith(os.pathsep): - value = value[:-1] - result[key] = removeDuplicates(value) - - finally: - popen.stdout.close() - popen.stderr.close() - - if len(result) != len(interesting): - raise ValueError(str(list(result.keys()))) - - return result - - -# More globals -VERSION = get_build_version() -# MACROS = MacroExpander(VERSION) - - -class MSVCCompiler(CCompiler): - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - def __init__(self, verbose=False, dry_run=False, force=False): - super().__init__(verbose, dry_run, force) - self.__version = VERSION - self.__root = r"Software\Microsoft\VisualStudio" - # self.__macros = MACROS - self.__paths = [] - # target platform (.plat_name is consistent with 'bdist') - self.plat_name = None - self.__arch = None # deprecated name - self.initialized = False - - def initialize(self, plat_name=None): # noqa: C901 - # multi-init means we would need to check platform same each time... - assert not self.initialized, "don't init multiple times" - if self.__version < 8.0: - raise DistutilsPlatformError( - f"VC {self.__version:0.1f} is not supported by this module" - ) - if plat_name is None: - plat_name = get_platform() - # sanity check for platforms to prevent obscure errors later. - ok_plats = 'win32', 'win-amd64' - if plat_name not in ok_plats: - raise DistutilsPlatformError(f"--plat-name must be one of {ok_plats}") - - if ( - "DISTUTILS_USE_SDK" in os.environ - and "MSSdk" in os.environ - and self.find_exe("cl.exe") - ): - # Assume that the SDK set up everything alright; don't try to be - # smarter - self.cc = "cl.exe" - self.linker = "link.exe" - self.lib = "lib.exe" - self.rc = "rc.exe" - self.mc = "mc.exe" - else: - # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; - # to cross compile, you use 'x86_amd64'. - # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross - # compile use 'x86' (ie, it runs the x86 compiler directly) - if plat_name in (get_platform(), 'win32'): - # native build or cross-compile to win32 - plat_spec = PLAT_TO_VCVARS[plat_name] - else: - # cross compile from win32 -> some 64bit - plat_spec = ( - PLAT_TO_VCVARS[get_platform()] + '_' + PLAT_TO_VCVARS[plat_name] - ) - - vc_env = query_vcvarsall(VERSION, plat_spec) - - self.__paths = vc_env['path'].split(os.pathsep) - os.environ['lib'] = vc_env['lib'] - os.environ['include'] = vc_env['include'] - - if len(self.__paths) == 0: - raise DistutilsPlatformError( - f"Python was built with {self.__product}, " - "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." - ) - - self.cc = self.find_exe("cl.exe") - self.linker = self.find_exe("link.exe") - self.lib = self.find_exe("lib.exe") - self.rc = self.find_exe("rc.exe") # resource compiler - self.mc = self.find_exe("mc.exe") # message compiler - # self.set_path_env_var('lib') - # self.set_path_env_var('include') - - # extend the MSVC path with the current path - try: - for p in os.environ['path'].split(';'): - self.__paths.append(p) - except KeyError: - pass - self.__paths = normalize_and_reduce_paths(self.__paths) - os.environ['path'] = ";".join(self.__paths) - - self.preprocess_options = None - if self.__arch == "x86": - self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/DNDEBUG'] - self.compile_options_debug = [ - '/nologo', - '/Od', - '/MDd', - '/W3', - '/Z7', - '/D_DEBUG', - ] - else: - # Win64 - self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG'] - self.compile_options_debug = [ - '/nologo', - '/Od', - '/MDd', - '/W3', - '/GS-', - '/Z7', - '/D_DEBUG', - ] - - self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] - if self.__version >= 7: - self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'] - self.ldflags_static = ['/nologo'] - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, source_filenames, strip_dir=False, output_dir=''): - # Copied from ccompiler.py, extended to return .res as 'object'-file - # for .rc input file - if output_dir is None: - output_dir = '' - obj_names = [] - for src_name in source_filenames: - (base, ext) = os.path.splitext(src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base) :] # If abs, chop off leading / - if ext not in self.src_extensions: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError(f"Don't know how to compile {src_name}") - if strip_dir: - base = os.path.basename(base) - if ext in self._rc_extensions: - obj_names.append(os.path.join(output_dir, base + self.res_extension)) - elif ext in self._mc_extensions: - obj_names.append(os.path.join(output_dir, base + self.res_extension)) - else: - obj_names.append(os.path.join(output_dir, base + self.obj_extension)) - return obj_names - - def compile( # noqa: C901 - self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=False, - extra_preargs=None, - extra_postargs=None, - depends=None, - ): - if not self.initialized: - self.initialize() - compile_info = self._setup_compile( - output_dir, macros, include_dirs, sources, depends, extra_postargs - ) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src]) - base, _ = os.path.splitext(os.path.basename(src)) - rc_file = os.path.join(rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError(f"Don't know how to compile {src} to {obj}") - - output_opt = "/Fo" + obj - try: - self.spawn( - [self.cc] - + compile_opts - + pp_opts - + [input_opt, output_opt] - + extra_postargs - ) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - def create_static_lib( - self, objects, output_libname, output_dir=None, debug=False, target_lang=None - ): - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def link( # noqa: C901 - self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=False, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None, - ): - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) - (libraries, library_dirs, runtime_library_dirs) = fixed_args - - if runtime_library_dirs: - self.warn( - "I don't know what to do with 'runtime_library_dirs': " - + str(runtime_library_dirs) - ) - - lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - if target_desc == CCompiler.EXECUTABLE: - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - else: - if debug: - ldflags = self.ldflags_shared_debug - else: - ldflags = self.ldflags_shared - - export_opts = [] - for sym in export_symbols or []: - export_opts.append("/EXPORT:" + sym) - - ld_args = ( - ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename] - ) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - build_temp = os.path.dirname(objects[0]) - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename) - ) - implib_file = os.path.join(build_temp, self.library_filename(dll_name)) - ld_args.append('/IMPLIB:' + implib_file) - - self.manifest_setup_ldargs(output_filename, build_temp, ld_args) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath(os.path.dirname(output_filename)) - try: - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - # embed the manifest - # XXX - this is somewhat fragile - if mt.exe fails, distutils - # will still consider the DLL up-to-date, but it will not have a - # manifest. Maybe we should link to a temp file? OTOH, that - # implies a build environment error that shouldn't go undetected. - mfinfo = self.manifest_get_embed_info(target_desc, ld_args) - if mfinfo is not None: - mffilename, mfid = mfinfo - out_arg = f'-outputresource:{output_filename};{mfid}' - try: - self.spawn(['mt.exe', '-nologo', '-manifest', mffilename, out_arg]) - except DistutilsExecError as msg: - raise LinkError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): - # If we need a manifest at all, an embedded manifest is recommended. - # See MSDN article titled - # "Understanding manifest generation for C/C++ programs" - # (currently at https://learn.microsoft.com/en-us/cpp/build/understanding-manifest-generation-for-c-cpp-programs) - # Ask the linker to generate the manifest in the temp dir, so - # we can check it, and possibly embed it, later. - temp_manifest = os.path.join( - build_temp, os.path.basename(output_filename) + ".manifest" - ) - ld_args.append('/MANIFESTFILE:' + temp_manifest) - - def manifest_get_embed_info(self, target_desc, ld_args): - # If a manifest should be embedded, return a tuple of - # (manifest_filename, resource_id). Returns None if no manifest - # should be embedded. See https://bugs.python.org/issue7833 for why - # we want to avoid any manifest for extension modules if we can) - for arg in ld_args: - if arg.startswith("/MANIFESTFILE:"): - temp_manifest = arg.split(":", 1)[1] - break - else: - # no /MANIFESTFILE so nothing to do. - return None - if target_desc == CCompiler.EXECUTABLE: - # by default, executables always get the manifest with the - # CRT referenced. - mfid = 1 - else: - # Extension modules try and avoid any manifest if possible. - mfid = 2 - temp_manifest = self._remove_visual_c_ref(temp_manifest) - if temp_manifest is None: - return None - return temp_manifest, mfid - - def _remove_visual_c_ref(self, manifest_file): - try: - # Remove references to the Visual C runtime, so they will - # fall through to the Visual C dependency of Python.exe. - # This way, when installed for a restricted user (e.g. - # runtimes are not in WinSxS folder, but in Python's own - # folder), the runtimes do not need to be in every folder - # with .pyd's. - # Returns either the filename of the modified manifest or - # None if no manifest should be embedded. - manifest_f = open(manifest_file) - try: - manifest_buf = manifest_f.read() - finally: - manifest_f.close() - pattern = re.compile( - r"""<assemblyIdentity.*?name=("|')Microsoft\.""" - r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""", - re.DOTALL, - ) - manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = r"<dependentAssembly>\s*</dependentAssembly>" - manifest_buf = re.sub(pattern, "", manifest_buf) - # Now see if any other assemblies are referenced - if not, we - # don't want a manifest embedded. - pattern = re.compile( - r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')""" - r""".*?(?:/>|</assemblyIdentity>)""", - re.DOTALL, - ) - if re.search(pattern, manifest_buf) is None: - return None - - manifest_f = open(manifest_file, 'w') - try: - manifest_f.write(manifest_buf) - return manifest_file - finally: - manifest_f.close() - except OSError: - pass - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC++" - ) - - def library_option(self, lib): - return self.library_filename(lib) - - def find_library_file(self, dirs, lib, debug=False): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename(name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # Helper methods for using the MSVC registry settings - - def find_exe(self, exe): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - for p in self.__paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - # didn't find it; try existing path - for p in os.environ['Path'].split(';'): - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - return exe diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/msvccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/msvccompiler.py deleted file mode 100644 index 2bf94e60c9..0000000000 --- a/contrib/python/setuptools/py3/setuptools/_distutils/msvccompiler.py +++ /dev/null @@ -1,689 +0,0 @@ -"""distutils.msvccompiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) - -import os -import sys -import warnings - -from ._log import log -from .ccompiler import CCompiler, gen_lib_options -from .errors import ( - CompileError, - DistutilsExecError, - DistutilsPlatformError, - LibError, - LinkError, -) - -_can_read_reg = False -try: - import winreg - - _can_read_reg = True - hkey_mod = winreg - - RegOpenKeyEx = winreg.OpenKeyEx - RegEnumKey = winreg.EnumKey - RegEnumValue = winreg.EnumValue - RegError = winreg.error - -except ImportError: - try: - import win32api - import win32con - - _can_read_reg = True - hkey_mod = win32con - - RegOpenKeyEx = win32api.RegOpenKeyEx - RegEnumKey = win32api.RegEnumKey - RegEnumValue = win32api.RegEnumValue - RegError = win32api.error - except ImportError: - log.info( - "Warning: Can't read registry to find the " - "necessary compiler setting\n" - "Make sure that Python modules winreg, " - "win32api or win32con are installed." - ) - pass - -if _can_read_reg: - HKEYS = ( - hkey_mod.HKEY_USERS, - hkey_mod.HKEY_CURRENT_USER, - hkey_mod.HKEY_LOCAL_MACHINE, - hkey_mod.HKEY_CLASSES_ROOT, - ) - - -warnings.warn( - "msvccompiler is deprecated and slated to be removed " - "in the future. Please discontinue use or file an issue " - "with pypa/distutils describing your use case.", - DeprecationWarning, -) - - -def read_keys(base, key): - """Return list of registry keys.""" - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - L = [] - i = 0 - while True: - try: - k = RegEnumKey(handle, i) - except RegError: - break - L.append(k) - i += 1 - return L - - -def read_values(base, key): - """Return dict of registry keys and values. - - All names are converted to lowercase. - """ - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - d = {} - i = 0 - while True: - try: - name, value, type = RegEnumValue(handle, i) - except RegError: - break - name = name.lower() - d[convert_mbcs(name)] = convert_mbcs(value) - i += 1 - return d - - -def convert_mbcs(s): - dec = getattr(s, "decode", None) - if dec is not None: - try: - s = dec("mbcs") - except UnicodeError: - pass - return s - - -class MacroExpander: - def __init__(self, version): - self.macros = {} - self.load_macros(version) - - def set_macro(self, macro, path, key): - for base in HKEYS: - d = read_values(base, path) - if d: - self.macros[f"$({macro})"] = d[key] - break - - def load_macros(self, version): - vsbase = rf"Software\Microsoft\VisualStudio\{version:0.1f}" - self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") - self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") - net = r"Software\Microsoft\.NETFramework" - self.set_macro("FrameworkDir", net, "installroot") - try: - if version > 7.0: - self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") - else: - self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") - except KeyError: - raise DistutilsPlatformError( - """Python was built with Visual Studio 2003; -extensions must be built with a compiler than can generate compatible binaries. -Visual Studio 2003 was not found on this system. If you have Cygwin installed, -you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""" - ) - - p = r"Software\Microsoft\NET Framework Setup\Product" - for base in HKEYS: - try: - h = RegOpenKeyEx(base, p) - except RegError: - continue - key = RegEnumKey(h, 0) - d = read_values(base, rf"{p}\{key}") - self.macros["$(FrameworkVersion)"] = d["version"] - - def sub(self, s): - for k, v in self.macros.items(): - s = s.replace(k, v) - return s - - -def get_build_version(): - """Return the version of MSVC that was used to build Python. - - For Python 2.3 and up, the version number is included in - sys.version. For earlier versions, assume the compiler is MSVC 6. - """ - prefix = "MSC v." - i = sys.version.find(prefix) - if i == -1: - return 6 - i = i + len(prefix) - s, rest = sys.version[i:].split(" ", 1) - majorVersion = int(s[:-2]) - 6 - if majorVersion >= 13: - # v13 was skipped and should be v14 - majorVersion += 1 - minorVersion = int(s[2:3]) / 10.0 - # I don't think paths are affected by minor version in version 6 - if majorVersion == 6: - minorVersion = 0 - if majorVersion >= 6: - return majorVersion + minorVersion - # else we don't know what version of the compiler this is - return None - - -def get_build_architecture(): - """Return the processor architecture. - - Possible results are "Intel" or "AMD64". - """ - - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return "Intel" - j = sys.version.find(")", i) - return sys.version[i + len(prefix) : j] - - -def normalize_and_reduce_paths(paths): - """Return a list of normalized paths with duplicates removed. - - The current order of paths is maintained. - """ - # Paths are normalized so things like: /a and /a/ aren't both preserved. - reduced_paths = [] - for p in paths: - np = os.path.normpath(p) - # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. - if np not in reduced_paths: - reduced_paths.append(np) - return reduced_paths - - -class MSVCCompiler(CCompiler): - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - def __init__(self, verbose=False, dry_run=False, force=False): - super().__init__(verbose, dry_run, force) - self.__version = get_build_version() - self.__arch = get_build_architecture() - if self.__arch == "Intel": - # x86 - if self.__version >= 7: - self.__root = r"Software\Microsoft\VisualStudio" - self.__macros = MacroExpander(self.__version) - else: - self.__root = r"Software\Microsoft\Devstudio" - self.__product = f"Visual Studio version {self.__version}" - else: - # Win64. Assume this was built with the platform SDK - self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) - - self.initialized = False - - def initialize(self): - self.__paths = [] - if ( - "DISTUTILS_USE_SDK" in os.environ - and "MSSdk" in os.environ - and self.find_exe("cl.exe") - ): - # Assume that the SDK set up everything alright; don't try to be - # smarter - self.cc = "cl.exe" - self.linker = "link.exe" - self.lib = "lib.exe" - self.rc = "rc.exe" - self.mc = "mc.exe" - else: - self.__paths = self.get_msvc_paths("path") - - if len(self.__paths) == 0: - raise DistutilsPlatformError( - f"Python was built with {self.__product}, " - "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." - ) - - self.cc = self.find_exe("cl.exe") - self.linker = self.find_exe("link.exe") - self.lib = self.find_exe("lib.exe") - self.rc = self.find_exe("rc.exe") # resource compiler - self.mc = self.find_exe("mc.exe") # message compiler - self.set_path_env_var('lib') - self.set_path_env_var('include') - - # extend the MSVC path with the current path - try: - for p in os.environ['path'].split(';'): - self.__paths.append(p) - except KeyError: - pass - self.__paths = normalize_and_reduce_paths(self.__paths) - os.environ['path'] = ";".join(self.__paths) - - self.preprocess_options = None - if self.__arch == "Intel": - self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GX', '/DNDEBUG'] - self.compile_options_debug = [ - '/nologo', - '/Od', - '/MDd', - '/W3', - '/GX', - '/Z7', - '/D_DEBUG', - ] - else: - # Win64 - self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG'] - self.compile_options_debug = [ - '/nologo', - '/Od', - '/MDd', - '/W3', - '/GS-', - '/Z7', - '/D_DEBUG', - ] - - self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] - if self.__version >= 7: - self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'] - else: - self.ldflags_shared_debug = [ - '/DLL', - '/nologo', - '/INCREMENTAL:no', - '/pdb:None', - '/DEBUG', - ] - self.ldflags_static = ['/nologo'] - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, source_filenames, strip_dir=False, output_dir=''): - # Copied from ccompiler.py, extended to return .res as 'object'-file - # for .rc input file - if output_dir is None: - output_dir = '' - obj_names = [] - for src_name in source_filenames: - (base, ext) = os.path.splitext(src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base) :] # If abs, chop off leading / - if ext not in self.src_extensions: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError(f"Don't know how to compile {src_name}") - if strip_dir: - base = os.path.basename(base) - if ext in self._rc_extensions: - obj_names.append(os.path.join(output_dir, base + self.res_extension)) - elif ext in self._mc_extensions: - obj_names.append(os.path.join(output_dir, base + self.res_extension)) - else: - obj_names.append(os.path.join(output_dir, base + self.obj_extension)) - return obj_names - - def compile( # noqa: C901 - self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=False, - extra_preargs=None, - extra_postargs=None, - depends=None, - ): - if not self.initialized: - self.initialize() - compile_info = self._setup_compile( - output_dir, macros, include_dirs, sources, depends, extra_postargs - ) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src]) - base, _ = os.path.splitext(os.path.basename(src)) - rc_file = os.path.join(rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError(f"Don't know how to compile {src} to {obj}") - - output_opt = "/Fo" + obj - try: - self.spawn( - [self.cc] - + compile_opts - + pp_opts - + [input_opt, output_opt] - + extra_postargs - ) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - def create_static_lib( - self, objects, output_libname, output_dir=None, debug=False, target_lang=None - ): - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def link( # noqa: C901 - self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=False, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None, - ): - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) - (libraries, library_dirs, runtime_library_dirs) = fixed_args - - if runtime_library_dirs: - self.warn( - "I don't know what to do with 'runtime_library_dirs': " - + str(runtime_library_dirs) - ) - - lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - if target_desc == CCompiler.EXECUTABLE: - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - else: - if debug: - ldflags = self.ldflags_shared_debug - else: - ldflags = self.ldflags_shared - - export_opts = [] - for sym in export_symbols or []: - export_opts.append("/EXPORT:" + sym) - - ld_args = ( - ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename] - ) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename) - ) - implib_file = os.path.join( - os.path.dirname(objects[0]), self.library_filename(dll_name) - ) - ld_args.append('/IMPLIB:' + implib_file) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath(os.path.dirname(output_filename)) - try: - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - else: - log.debug("skipping %s (up-to-date)", output_filename) - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC++" - ) - - def library_option(self, lib): - return self.library_filename(lib) - - def find_library_file(self, dirs, lib, debug=False): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename(name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # Helper methods for using the MSVC registry settings - - def find_exe(self, exe): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - for p in self.__paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - # didn't find it; try existing path - for p in os.environ['Path'].split(';'): - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - return exe - - def get_msvc_paths(self, path, platform='x86'): - """Get a list of devstudio directories (include, lib or path). - - Return a list of strings. The list will be empty if unable to - access the registry or appropriate registry keys not found. - """ - if not _can_read_reg: - return [] - - path = path + " dirs" - if self.__version >= 7: - key = rf"{self.__root}\{self.__version:0.1f}\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" - else: - key = ( - rf"{self.__root}\6.0\Build System\Components\Platforms" - rf"\Win32 ({platform})\Directories" - ) - - for base in HKEYS: - d = read_values(base, key) - if d: - if self.__version >= 7: - return self.__macros.sub(d[path]).split(";") - else: - return d[path].split(";") - # MSVC 6 seems to create the registry entries we need only when - # the GUI is run. - if self.__version == 6: - for base in HKEYS: - if read_values(base, rf"{self.__root}\6.0") is not None: - self.warn( - "It seems you have Visual Studio 6 installed, " - "but the expected registry settings are not present.\n" - "You must at least run the Visual Studio GUI once " - "so that these entries are created." - ) - break - return [] - - def set_path_env_var(self, name): - """Set environment variable 'name' to an MSVC path type value. - - This is equivalent to a SET command prior to execution of spawned - commands. - """ - - if name == "lib": - p = self.get_msvc_paths("library") - else: - p = self.get_msvc_paths(name) - if p: - os.environ[name] = ';'.join(p) - - -if get_build_version() >= 8.0: - log.debug("Importing new compiler from distutils.msvc9compiler") - OldMSVCCompiler = MSVCCompiler - # get_build_architecture not really relevant now we support cross-compile - from distutils.msvc9compiler import ( - MacroExpander, - MSVCCompiler, - ) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py b/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py index 50d30a2761..107b011397 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py @@ -12,7 +12,6 @@ import shutil import subprocess import sys import warnings - from typing import Mapping from ._log import log diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py b/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py index 7ebe67687e..28a7c571dc 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py @@ -236,7 +236,7 @@ def get_python_lib(plat_specific=False, standard_lib=False, prefix=None): if prefix is None: prefix = PREFIX if standard_lib: - return os.path.join(prefix, "lib-python", sys.version[0]) + return os.path.join(prefix, "lib-python", sys.version_info.major) return os.path.join(prefix, 'site-packages') early_prefix = prefix @@ -287,7 +287,7 @@ def _customize_macos(): ) -def customize_compiler(compiler): # noqa: C901 +def customize_compiler(compiler): """Do any platform-specific customization of a CCompiler instance. Mainly needed on Unix, so we can plug in the information that @@ -304,6 +304,7 @@ def customize_compiler(compiler): # noqa: C901 cflags, ccshared, ldshared, + ldcxxshared, shlib_suffix, ar, ar_flags, @@ -313,11 +314,14 @@ def customize_compiler(compiler): # noqa: C901 'CFLAGS', 'CCSHARED', 'LDSHARED', + 'LDCXXSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS', ) + cxxflags = cflags + if 'CC' in os.environ: newcc = os.environ['CC'] if 'LDSHARED' not in os.environ and ldshared.startswith(cc): @@ -325,38 +329,42 @@ def customize_compiler(compiler): # noqa: C901 # command for LDSHARED as well ldshared = newcc + ldshared[len(cc) :] cc = newcc - if 'CXX' in os.environ: - cxx = os.environ['CXX'] - if 'LDSHARED' in os.environ: - ldshared = os.environ['LDSHARED'] - if 'CPP' in os.environ: - cpp = os.environ['CPP'] - else: - cpp = cc + " -E" # not always - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: - cflags = cflags + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] - cflags = cflags + ' ' + os.environ['CPPFLAGS'] - ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - if 'AR' in os.environ: - ar = os.environ['AR'] - if 'ARFLAGS' in os.environ: - archiver = ar + ' ' + os.environ['ARFLAGS'] - else: - archiver = ar + ' ' + ar_flags + cxx = os.environ.get('CXX', cxx) + ldshared = os.environ.get('LDSHARED', ldshared) + ldcxxshared = os.environ.get('LDCXXSHARED', ldcxxshared) + cpp = os.environ.get( + 'CPP', + cc + " -E", # not always + ) + ldshared = _add_flags(ldshared, 'LD') + ldcxxshared = _add_flags(ldcxxshared, 'LD') + cflags = _add_flags(cflags, 'C') + ldshared = _add_flags(ldshared, 'C') + cxxflags = os.environ.get('CXXFLAGS', cxxflags) + ldcxxshared = _add_flags(ldcxxshared, 'CXX') + cpp = _add_flags(cpp, 'CPP') + cflags = _add_flags(cflags, 'CPP') + cxxflags = _add_flags(cxxflags, 'CPP') + ldshared = _add_flags(ldshared, 'CPP') + ldcxxshared = _add_flags(ldcxxshared, 'CPP') + + ar = os.environ.get('AR', ar) + + archiver = ar + ' ' + os.environ.get('ARFLAGS', ar_flags) cc_cmd = cc + ' ' + cflags + cxx_cmd = cxx + ' ' + cxxflags + compiler.set_executables( preprocessor=cpp, compiler=cc_cmd, compiler_so=cc_cmd + ' ' + ccshared, - compiler_cxx=cxx, + compiler_cxx=cxx_cmd, + compiler_so_cxx=cxx_cmd + ' ' + ccshared, linker_so=ldshared, + linker_so_cxx=ldcxxshared, linker_exe=cc, + linker_exe_cxx=cxx, archiver=archiver, ) @@ -561,3 +569,14 @@ def get_config_var(name): warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) return get_config_vars().get(name) + + +@pass_none +def _add_flags(value: str, type: str) -> str: + """ + Add any flags from the environment for the given type. + + type is the prefix to FLAGS in the environment key (e.g. "C" for "CFLAGS"). + """ + flags = os.environ.get(f'{type}FLAGS') + return f'{value} {flags}' if flags else value diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py index 7e68596b26..6c1116ae8f 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py @@ -118,9 +118,12 @@ class UnixCCompiler(CCompiler): 'preprocessor': None, 'compiler': ["cc"], 'compiler_so': ["cc"], - 'compiler_cxx': ["cc"], + 'compiler_cxx': ["c++"], + 'compiler_so_cxx': ["c++"], 'linker_so': ["cc", "-shared"], + 'linker_so_cxx': ["c++", "-shared"], 'linker_exe': ["cc"], + 'linker_exe_cxx': ["c++", "-shared"], 'archiver': ["ar", "-cr"], 'ranlib': None, } @@ -187,8 +190,14 @@ class UnixCCompiler(CCompiler): def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): compiler_so = compiler_fixup(self.compiler_so, cc_args + extra_postargs) + compiler_so_cxx = compiler_fixup(self.compiler_so_cxx, cc_args + extra_postargs) try: - self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) + if self.detect_language(src) == 'c++': + self.spawn( + compiler_so_cxx + cc_args + [src, '-o', obj] + extra_postargs + ) + else: + self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError as msg: raise CompileError(msg) @@ -256,7 +265,13 @@ class UnixCCompiler(CCompiler): # building an executable or linker_so (with shared options) # when building a shared library. building_exe = target_desc == CCompiler.EXECUTABLE - linker = (self.linker_exe if building_exe else self.linker_so)[:] + linker = ( + self.linker_exe + if building_exe + else ( + self.linker_so_cxx if target_lang == "c++" else self.linker_so + ) + )[:] if target_lang == "c++" and self.compiler_cxx: env, linker_ne = _split_env(linker) @@ -366,27 +381,11 @@ class UnixCCompiler(CCompiler): return os.path.join(match.group(1), dir[1:]) if apply_root else dir def find_library_file(self, dirs, lib, debug=False): - r""" + """ Second-guess the linker with not much hard data to go on: GCC seems to prefer the shared library, so assume that *all* Unix C compilers do, ignoring even GCC's "-static" option. - - >>> compiler = UnixCCompiler() - >>> compiler._library_root = lambda dir: dir - >>> monkeypatch = getfixture('monkeypatch') - >>> monkeypatch.setattr(os.path, 'exists', lambda d: 'existing' in d) - >>> dirs = ('/foo/bar/missing', '/foo/bar/existing') - >>> compiler.find_library_file(dirs, 'abc').replace('\\', '/') - '/foo/bar/existing/libabc.dylib' - >>> compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/') - '/foo/bar/existing/libabc.dylib' - >>> monkeypatch.setattr(os.path, 'exists', - ... lambda d: 'existing' in d and '.a' in d) - >>> compiler.find_library_file(dirs, 'abc').replace('\\', '/') - '/foo/bar/existing/libabc.a' - >>> compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/') - '/foo/bar/existing/libabc.a' """ lib_names = ( self.library_filename(lib, lib_type=type) diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/util.py b/contrib/python/setuptools/py3/setuptools/_distutils/util.py index 9db89b0979..4cc6bd283c 100644 --- a/contrib/python/setuptools/py3/setuptools/_distutils/util.py +++ b/contrib/python/setuptools/py3/setuptools/_distutils/util.py @@ -4,9 +4,12 @@ Miscellaneous utility functions -- anything that doesn't fit into one of the other *util.py modules. """ +from __future__ import annotations + import functools import importlib.util import os +import pathlib import re import string import subprocess @@ -14,6 +17,7 @@ import sys import sysconfig import tempfile +from ._functools import pass_none from ._log import log from ._modified import newer from .errors import DistutilsByteCompileError, DistutilsPlatformError @@ -116,33 +120,23 @@ def split_version(s): return [int(n) for n in s.split('.')] -def convert_path(pathname): - """Return 'pathname' as a name that will work on the native filesystem, - i.e. split it on '/' and put it back together again using the current - directory separator. Needed because filenames in the setup script are - always supplied in Unix style, and have to be converted to the local - convention before we can actually use them in the filesystem. Raises - ValueError on non-Unix-ish systems if 'pathname' either starts or - ends with a slash. +@pass_none +def convert_path(pathname: str | os.PathLike) -> str: + r""" + Allow for pathlib.Path inputs, coax to a native path string. + + If None is passed, will just pass it through as + Setuptools relies on this behavior. + + >>> convert_path(None) is None + True + + Removes empty paths. + + >>> convert_path('foo/./bar').replace('\\', '/') + 'foo/bar' """ - if os.sep == '/': - return pathname - if not pathname: - return pathname - if pathname[0] == '/': - raise ValueError(f"path '{pathname}' cannot be absolute") - if pathname[-1] == '/': - raise ValueError(f"path '{pathname}' cannot end with '/'") - - paths = pathname.split('/') - while '.' in paths: - paths.remove('.') - if not paths: - return os.curdir - return os.path.join(*paths) - - -# convert_path () + return os.fspath(pathlib.PurePath(pathname)) def change_root(new_root, pathname): diff --git a/contrib/python/setuptools/py3/setuptools/_entry_points.py b/contrib/python/setuptools/py3/setuptools/_entry_points.py index 5de12582be..e785fc7df8 100644 --- a/contrib/python/setuptools/py3/setuptools/_entry_points.py +++ b/contrib/python/setuptools/py3/setuptools/_entry_points.py @@ -1,13 +1,14 @@ import functools -import operator import itertools +import operator -from .errors import OptionError -from jaraco.text import yield_lines from jaraco.functools import pass_none +from jaraco.text import yield_lines +from more_itertools import consume + from ._importlib import metadata from ._itertools import ensure_unique -from more_itertools import consume +from .errors import OptionError def ensure_valid(ep): diff --git a/contrib/python/setuptools/py3/setuptools/_imp.py b/contrib/python/setuptools/py3/setuptools/_imp.py index 38b146fc4d..bddbf6a683 100644 --- a/contrib/python/setuptools/py3/setuptools/_imp.py +++ b/contrib/python/setuptools/py3/setuptools/_imp.py @@ -3,14 +3,12 @@ Re-implementation of find_module and get_frozen_object from the deprecated imp module. """ -import os -import importlib.util import importlib.machinery +import importlib.util +import os import tokenize - from importlib.util import module_from_spec - PY_SOURCE = 1 PY_COMPILED = 2 C_EXTENSION = 3 diff --git a/contrib/python/setuptools/py3/setuptools/_importlib.py b/contrib/python/setuptools/py3/setuptools/_importlib.py index b2d5b5b84a..5317be0fa0 100644 --- a/contrib/python/setuptools/py3/setuptools/_importlib.py +++ b/contrib/python/setuptools/py3/setuptools/_importlib.py @@ -1,6 +1,5 @@ import sys - if sys.version_info < (3, 10): import importlib_metadata as metadata # pragma: no cover else: diff --git a/contrib/python/setuptools/py3/setuptools/_path.py b/contrib/python/setuptools/py3/setuptools/_path.py index fb8ef0e198..dd4a9db8cb 100644 --- a/contrib/python/setuptools/py3/setuptools/_path.py +++ b/contrib/python/setuptools/py3/setuptools/_path.py @@ -1,11 +1,20 @@ +from __future__ import annotations + +import contextlib import os import sys -from typing import Union +from typing import TYPE_CHECKING, Union + +if TYPE_CHECKING: + from typing_extensions import TypeAlias + + +from more_itertools import unique_everseen if sys.version_info >= (3, 9): - StrPath = Union[str, os.PathLike[str]] # Same as _typeshed.StrPath + StrPath: TypeAlias = Union[str, os.PathLike[str]] # Same as _typeshed.StrPath else: - StrPath = Union[str, os.PathLike] + StrPath: TypeAlias = Union[str, os.PathLike] def ensure_directory(path): @@ -38,3 +47,41 @@ def normpath(filename: StrPath) -> str: # See pkg_resources.normalize_path for notes about cygwin file = os.path.abspath(filename) if sys.platform == 'cygwin' else filename return os.path.normcase(os.path.realpath(os.path.normpath(file))) + + +@contextlib.contextmanager +def paths_on_pythonpath(paths): + """ + Add the indicated paths to the head of the PYTHONPATH environment + variable so that subprocesses will also see the packages at + these paths. + + Do this in a context that restores the value on exit. + + >>> getfixture('monkeypatch').setenv('PYTHONPATH', 'anything') + >>> with paths_on_pythonpath(['foo', 'bar']): + ... assert 'foo' in os.environ['PYTHONPATH'] + ... assert 'anything' in os.environ['PYTHONPATH'] + >>> os.environ['PYTHONPATH'] + 'anything' + + >>> getfixture('monkeypatch').delenv('PYTHONPATH') + >>> with paths_on_pythonpath(['foo', 'bar']): + ... assert 'foo' in os.environ['PYTHONPATH'] + >>> os.environ.get('PYTHONPATH') + """ + nothing = object() + orig_pythonpath = os.environ.get('PYTHONPATH', nothing) + current_pythonpath = os.environ.get('PYTHONPATH', '') + try: + prefix = os.pathsep.join(unique_everseen(paths)) + to_join = filter(None, [prefix, current_pythonpath]) + new_path = os.pathsep.join(to_join) + if new_path: + os.environ['PYTHONPATH'] = new_path + yield + finally: + if orig_pythonpath is nothing: + os.environ.pop('PYTHONPATH', None) + else: + os.environ['PYTHONPATH'] = orig_pythonpath diff --git a/contrib/python/setuptools/py3/setuptools/_reqs.py b/contrib/python/setuptools/py3/setuptools/_reqs.py index 1b64d9df79..71ea23dea9 100644 --- a/contrib/python/setuptools/py3/setuptools/_reqs.py +++ b/contrib/python/setuptools/py3/setuptools/_reqs.py @@ -1,11 +1,16 @@ +from __future__ import annotations + from functools import lru_cache -from typing import Callable, Iterable, Iterator, TypeVar, Union, overload +from typing import TYPE_CHECKING, Callable, Iterable, Iterator, TypeVar, Union, overload import jaraco.text as text from packaging.requirements import Requirement +if TYPE_CHECKING: + from typing_extensions import TypeAlias + _T = TypeVar("_T") -_StrOrIter = Union[str, Iterable[str]] +_StrOrIter: TypeAlias = Union[str, Iterable[str]] parse_req: Callable[[str], Requirement] = lru_cache()(Requirement) @@ -23,15 +28,13 @@ def parse_strings(strs: _StrOrIter) -> Iterator[str]: return text.join_continuation(map(text.drop_comment, text.yield_lines(strs))) +# These overloads are only needed because of a mypy false-positive, pyright gets it right +# https://github.com/python/mypy/issues/3737 @overload def parse(strs: _StrOrIter) -> Iterator[Requirement]: ... - - @overload def parse(strs: _StrOrIter, parser: Callable[[str], _T]) -> Iterator[_T]: ... - - -def parse(strs, parser=parse_req): +def parse(strs: _StrOrIter, parser: Callable[[str], _T] = parse_req) -> Iterator[_T]: # type: ignore[assignment] """ Replacement for ``pkg_resources.parse_requirements`` that uses ``packaging``. """ diff --git a/contrib/python/setuptools/py3/setuptools/archive_util.py b/contrib/python/setuptools/py3/setuptools/archive_util.py index 6b8460bd92..e4acd75f9b 100644 --- a/contrib/python/setuptools/py3/setuptools/archive_util.py +++ b/contrib/python/setuptools/py3/setuptools/archive_util.py @@ -1,15 +1,16 @@ """Utilities for extracting common archive formats""" -import zipfile -import tarfile +import contextlib import os -import shutil import posixpath -import contextlib -from distutils.errors import DistutilsError +import shutil +import tarfile +import zipfile from ._path import ensure_directory +from distutils.errors import DistutilsError + __all__ = [ "unpack_archive", "unpack_zipfile", diff --git a/contrib/python/setuptools/py3/setuptools/build_meta.py b/contrib/python/setuptools/py3/setuptools/build_meta.py index c52c872fd0..a6b85afc42 100644 --- a/contrib/python/setuptools/py3/setuptools/build_meta.py +++ b/contrib/python/setuptools/py3/setuptools/build_meta.py @@ -28,26 +28,31 @@ Again, this is not a formal definition! Just a "taste" of the module. from __future__ import annotations +import contextlib import io import os import shlex -import sys -import tokenize import shutil -import contextlib +import sys import tempfile +import tokenize import warnings from pathlib import Path -from typing import Dict, Iterator, List, Optional, Union, Iterable +from typing import TYPE_CHECKING, Dict, Iterable, Iterator, List, Union import setuptools -import distutils + from . import errors -from ._path import same_path, StrPath +from ._path import StrPath, same_path from ._reqs import parse_strings from .warnings import SetuptoolsDeprecationWarning + +import distutils from distutils.util import strtobool +if TYPE_CHECKING: + from typing_extensions import TypeAlias + __all__ = [ 'get_requires_for_build_sdist', @@ -142,7 +147,7 @@ def suppress_known_deprecation(): yield -_ConfigSettings = Optional[Dict[str, Union[str, List[str], None]]] +_ConfigSettings: TypeAlias = Union[Dict[str, Union[str, List[str], None]], None] """ Currently the user can run:: @@ -382,9 +387,10 @@ class _BuildMetaBackend(_ConfigSettingsTranslator): # Build in a temporary directory, then copy to the target. os.makedirs(result_directory, exist_ok=True) - temp_opts = {"prefix": ".tmp-", "dir": result_directory} - with tempfile.TemporaryDirectory(**temp_opts) as tmp_dist_dir: + with tempfile.TemporaryDirectory( + prefix=".tmp-", dir=result_directory + ) as tmp_dist_dir: sys.argv = [ *sys.argv[:1], *self._global_args(config_settings), diff --git a/contrib/python/setuptools/py3/setuptools/command/__init__.py b/contrib/python/setuptools/py3/setuptools/command/__init__.py index 5acd7687d6..bf011e896d 100644 --- a/contrib/python/setuptools/py3/setuptools/command/__init__.py +++ b/contrib/python/setuptools/py3/setuptools/command/__init__.py @@ -1,6 +1,7 @@ -from distutils.command.bdist import bdist import sys +from distutils.command.bdist import bdist + if 'egg' not in bdist.format_commands: try: bdist.format_commands['egg'] = ('bdist_egg', "Python .egg file") diff --git a/contrib/python/setuptools/py3/setuptools/command/_requirestxt.py b/contrib/python/setuptools/py3/setuptools/command/_requirestxt.py index ef35d183e8..b87476d6f4 100644 --- a/contrib/python/setuptools/py3/setuptools/command/_requirestxt.py +++ b/contrib/python/setuptools/py3/setuptools/command/_requirestxt.py @@ -14,10 +14,10 @@ from collections import defaultdict from itertools import filterfalse from typing import Dict, Mapping, TypeVar -from .. import _reqs from jaraco.text import yield_lines from packaging.requirements import Requirement +from .. import _reqs # dict can work as an ordered set _T = TypeVar("_T") diff --git a/contrib/python/setuptools/py3/setuptools/command/alias.py b/contrib/python/setuptools/py3/setuptools/command/alias.py index e7b4d5456a..4eed652381 100644 --- a/contrib/python/setuptools/py3/setuptools/command/alias.py +++ b/contrib/python/setuptools/py3/setuptools/command/alias.py @@ -1,6 +1,6 @@ -from distutils.errors import DistutilsOptionError +from setuptools.command.setopt import config_file, edit_config, option_base -from setuptools.command.setopt import edit_config, option_base, config_file +from distutils.errors import DistutilsOptionError def shquote(arg): diff --git a/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py b/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py index 559f7d6032..f3b7150208 100644 --- a/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py +++ b/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py @@ -2,20 +2,21 @@ Build .egg distributions""" -from distutils.dir_util import remove_tree, mkpath -from distutils import log -from types import CodeType -import sys +import marshal import os import re +import sys import textwrap -import marshal +from sysconfig import get_path, get_python_version +from types import CodeType -from setuptools.extension import Library from setuptools import Command +from setuptools.extension import Library + from .._path import ensure_directory -from sysconfig import get_path, get_python_version +from distutils import log +from distutils.dir_util import mkpath, remove_tree def _get_purelib(): diff --git a/contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py b/contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py index 70ed6b6097..e0d4caf2e9 100644 --- a/contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py +++ b/contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py @@ -1,7 +1,8 @@ -import distutils.command.bdist_rpm as orig - +from ..dist import Distribution from ..warnings import SetuptoolsDeprecationWarning +import distutils.command.bdist_rpm as orig + class bdist_rpm(orig.bdist_rpm): """ @@ -12,6 +13,8 @@ class bdist_rpm(orig.bdist_rpm): disable eggs in RPM distributions. """ + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def run(self): SetuptoolsDeprecationWarning.emit( "Deprecated command", diff --git a/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py b/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py index 5b9bcec60c..8f06786659 100644 --- a/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py +++ b/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py @@ -16,20 +16,22 @@ import sysconfig import warnings from email.generator import BytesGenerator, Generator from email.policy import EmailPolicy -from distutils import log from glob import iglob from shutil import rmtree from typing import TYPE_CHECKING, Callable, Iterable, Literal, Sequence, cast from zipfile import ZIP_DEFLATED, ZIP_STORED -from .. import Command, __version__ +from packaging import tags, version as _packaging_version from wheel.metadata import pkginfo_to_metadata -from packaging import tags -from packaging import version as _packaging_version from wheel.wheelfile import WheelFile +from .. import Command, __version__ +from .egg_info import egg_info as egg_info_cls + +from distutils import log + if TYPE_CHECKING: - import types + from _typeshed import ExcInfo def safe_name(name: str) -> str: @@ -152,12 +154,14 @@ def safer_version(version: str) -> str: def remove_readonly( func: Callable[..., object], path: str, - excinfo: tuple[type[Exception], Exception, types.TracebackType], + excinfo: ExcInfo, ) -> None: remove_readonly_exc(func, path, excinfo[1]) -def remove_readonly_exc(func: Callable[..., object], path: str, exc: Exception) -> None: +def remove_readonly_exc( + func: Callable[..., object], path: str, exc: BaseException +) -> None: os.chmod(path, stat.S_IWRITE) func(path) @@ -232,40 +236,35 @@ class bdist_wheel(Command): def initialize_options(self) -> None: self.bdist_dir: str | None = None - self.data_dir = None + self.data_dir: str | None = None self.plat_name: str | None = None - self.plat_tag = None + self.plat_tag: str | None = None self.format = "zip" self.keep_temp = False self.dist_dir: str | None = None - self.egginfo_dir = None + self.egginfo_dir: str | None = None self.root_is_pure: bool | None = None - self.skip_build = None + self.skip_build = False self.relative = False self.owner = None self.group = None self.universal: bool = False - self.compression: str | int = "deflated" + self.compression: int | str = "deflated" self.python_tag: str = python_tag() self.build_number: str | None = None self.py_limited_api: str | Literal[False] = False self.plat_name_supplied = False - def finalize_options(self): - if self.bdist_dir is None: + def finalize_options(self) -> None: + if not self.bdist_dir: bdist_base = self.get_finalized_command("bdist").bdist_base self.bdist_dir = os.path.join(bdist_base, "wheel") - egg_info = self.distribution.get_command_obj("egg_info") + egg_info = cast(egg_info_cls, self.distribution.get_command_obj("egg_info")) egg_info.ensure_finalized() # needed for correct `wheel_dist_name` self.data_dir = self.wheel_dist_name + ".data" - self.plat_name_supplied = self.plat_name is not None - - try: - self.compression = self.supported_compressions[self.compression] - except KeyError: - raise ValueError(f"Unsupported compression: {self.compression}") from None + self.plat_name_supplied = bool(self.plat_name) need_options = ("dist_dir", "plat_name", "skip_build") @@ -275,10 +274,7 @@ class bdist_wheel(Command): self.distribution.has_ext_modules() or self.distribution.has_c_libraries() ) - if self.py_limited_api and not re.match( - PY_LIMITED_API_PATTERN, self.py_limited_api - ): - raise ValueError(f"py-limited-api must match '{PY_LIMITED_API_PATTERN}'") + self._validate_py_limited_api() # Support legacy [wheel] section for setting universal wheel = self.distribution.get_option_dict("wheel") @@ -292,22 +288,37 @@ class bdist_wheel(Command): if self.build_number is not None and not self.build_number[:1].isdigit(): raise ValueError("Build tag (build-number) must start with a digit.") + def _validate_py_limited_api(self) -> None: + if not self.py_limited_api: + return + + if not re.match(PY_LIMITED_API_PATTERN, self.py_limited_api): + raise ValueError(f"py-limited-api must match '{PY_LIMITED_API_PATTERN}'") + + if sysconfig.get_config_var("Py_GIL_DISABLED"): + raise ValueError( + f"`py_limited_api={self.py_limited_api!r}` not supported. " + "`Py_LIMITED_API` is currently incompatible with " + f"`Py_GIL_DISABLED` ({sys.abiflags=!r}). " + "See https://github.com/python/cpython/issues/111506." + ) + @property - def wheel_dist_name(self): + def wheel_dist_name(self) -> str: """Return distribution full name with - replaced with _""" - components = ( + components = [ safer_name(self.distribution.get_name()), safer_version(self.distribution.get_version()), - ) + ] if self.build_number: - components += (self.build_number,) + components.append(self.build_number) return "-".join(components) def get_tag(self) -> tuple[str, str, str]: # bdist sets self.plat_name if unset, we should only use it for purepy # wheels if the user supplied it. - if self.plat_name_supplied: - plat_name = cast(str, self.plat_name) + if self.plat_name_supplied and self.plat_name: + plat_name = self.plat_name elif self.root_is_pure: plat_name = "any" else: @@ -431,7 +442,7 @@ class bdist_wheel(Command): os.makedirs(self.dist_dir) wheel_path = os.path.join(self.dist_dir, archive_basename + ".whl") - with WheelFile(wheel_path, "w", self.compression) as wf: + with WheelFile(wheel_path, "w", self._zip_compression()) as wf: wf.write_files(archive_root) # Add to 'Distribution.dist_files' so that the "upload" command works @@ -595,3 +606,16 @@ class bdist_wheel(Command): shutil.copy(license_path, os.path.join(distinfo_path, filename)) adios(egginfo_path) + + def _zip_compression(self) -> int: + if ( + isinstance(self.compression, int) + and self.compression in self.supported_compressions.values() + ): + return self.compression + + compression = self.supported_compressions.get(str(self.compression)) + if compression is not None: + return compression + + raise ValueError(f"Unsupported compression: {self.compression!r}") diff --git a/contrib/python/setuptools/py3/setuptools/command/build.py b/contrib/python/setuptools/py3/setuptools/command/build.py index bc765a17ae..f60fcbda15 100644 --- a/contrib/python/setuptools/py3/setuptools/command/build.py +++ b/contrib/python/setuptools/py3/setuptools/command/build.py @@ -1,12 +1,17 @@ from __future__ import annotations from typing import Protocol + +from ..dist import Distribution + from distutils.command.build import build as _build _ORIGINAL_SUBCOMMANDS = {"build_py", "build_clib", "build_ext", "build_scripts"} class build(_build): + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + # copy to avoid sharing the object with parent class sub_commands = _build.sub_commands[:] @@ -82,12 +87,15 @@ class SubCommand(Protocol): def initialize_options(self): """(Required by the original :class:`setuptools.Command` interface)""" + ... def finalize_options(self): """(Required by the original :class:`setuptools.Command` interface)""" + ... def run(self): """(Required by the original :class:`setuptools.Command` interface)""" + ... def get_source_files(self) -> list[str]: """ @@ -99,6 +107,7 @@ class SubCommand(Protocol): with all the files necessary to build the distribution. All files should be strings relative to the project root directory. """ + ... def get_outputs(self) -> list[str]: """ @@ -112,6 +121,7 @@ class SubCommand(Protocol): in ``get_output_mapping()`` plus files that are generated during the build and don't correspond to any source file already present in the project. """ + ... def get_output_mapping(self) -> dict[str, str]: """ @@ -122,3 +132,4 @@ class SubCommand(Protocol): Destination files should be strings in the form of ``"{build_lib}/destination/file/path"``. """ + ... diff --git a/contrib/python/setuptools/py3/setuptools/command/build_clib.py b/contrib/python/setuptools/py3/setuptools/command/build_clib.py index acd4d1d3ba..d532762ebe 100644 --- a/contrib/python/setuptools/py3/setuptools/command/build_clib.py +++ b/contrib/python/setuptools/py3/setuptools/command/build_clib.py @@ -1,9 +1,13 @@ +from ..dist import Distribution + import distutils.command.build_clib as orig -from distutils.errors import DistutilsSetupError from distutils import log +from distutils.errors import DistutilsSetupError try: - from distutils._modified import newer_pairwise_group + from distutils._modified import ( # pyright: ignore[reportMissingImports] + newer_pairwise_group, + ) except ImportError: # fallback for SETUPTOOLS_USE_DISTUTILS=stdlib from .._distutils._modified import newer_pairwise_group @@ -25,6 +29,8 @@ class build_clib(orig.build_clib): the compiler. """ + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def build_libraries(self, libraries): for lib_name, build_info in libraries: sources = build_info.get('sources') diff --git a/contrib/python/setuptools/py3/setuptools/command/build_ext.py b/contrib/python/setuptools/py3/setuptools/command/build_ext.py index 508704f3c0..e2a88ce218 100644 --- a/contrib/python/setuptools/py3/setuptools/command/build_ext.py +++ b/contrib/python/setuptools/py3/setuptools/command/build_ext.py @@ -1,36 +1,40 @@ from __future__ import annotations +import itertools import os import sys -import itertools from importlib.machinery import EXTENSION_SUFFIXES from importlib.util import cache_from_source as _compiled_file_name -from typing import Iterator from pathlib import Path +from typing import TYPE_CHECKING, Iterator -from distutils.command.build_ext import build_ext as _du_build_ext -from distutils.ccompiler import new_compiler -from distutils.sysconfig import customize_compiler, get_config_var -from distutils import log - +from setuptools.dist import Distribution from setuptools.errors import BaseError from setuptools.extension import Extension, Library -try: - # Attempt to use Cython for building extensions, if available - from Cython.Distutils.build_ext import build_ext as _build_ext # type: ignore[import-not-found] # Cython not installed on CI tests +from distutils import log +from distutils.ccompiler import new_compiler +from distutils.sysconfig import customize_compiler, get_config_var + +if TYPE_CHECKING: + # Cython not installed on CI tests, causing _build_ext to be `Any` + from distutils.command.build_ext import build_ext as _build_ext +else: + try: + # Attempt to use Cython for building extensions, if available + from Cython.Distutils.build_ext import build_ext as _build_ext - # Additionally, assert that the compiler module will load - # also. Ref #1229. - __import__('Cython.Compiler.Main') -except ImportError: - _build_ext = _du_build_ext + # Additionally, assert that the compiler module will load + # also. Ref #1229. + __import__('Cython.Compiler.Main') + except ImportError: + from distutils.command.build_ext import build_ext as _build_ext # make sure _config_vars is initialized get_config_var("LDSHARED") # Not publicly exposed in typeshed distutils stubs, but this is done on purpose # See https://github.com/pypa/setuptools/pull/4228#issuecomment-1959856400 -from distutils.sysconfig import _config_vars as _CONFIG_VARS # type: ignore # noqa +from distutils.sysconfig import _config_vars as _CONFIG_VARS # noqa: E402 def _customize_compiler_for_shlib(compiler): @@ -84,6 +88,7 @@ def get_abi3_suffix(): class build_ext(_build_ext): + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution editable_mode: bool = False inplace: bool = False @@ -152,21 +157,25 @@ class build_ext(_build_ext): output_cache = _compiled_file_name(regular_stub, optimization=opt) yield (output_cache, inplace_cache) - def get_ext_filename(self, fullname): + def get_ext_filename(self, fullname: str) -> str: so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX') if so_ext: filename = os.path.join(*fullname.split('.')) + so_ext else: filename = _build_ext.get_ext_filename(self, fullname) - so_ext = get_config_var('EXT_SUFFIX') + ext_suffix = get_config_var('EXT_SUFFIX') + if not isinstance(ext_suffix, str): + raise OSError( + "Configuration variable EXT_SUFFIX not found for this platform " + + "and environment variable SETUPTOOLS_EXT_SUFFIX is missing" + ) + so_ext = ext_suffix if fullname in self.ext_map: ext = self.ext_map[fullname] - use_abi3 = ext.py_limited_api and get_abi3_suffix() - if use_abi3: - filename = filename[: -len(so_ext)] - so_ext = get_abi3_suffix() - filename = filename + so_ext + abi3_suffix = get_abi3_suffix() + if ext.py_limited_api and abi3_suffix: # Use abi3 + filename = filename[: -len(so_ext)] + abi3_suffix if isinstance(ext, Library): fn, ext = os.path.splitext(filename) return self.shlib_compiler.library_filename(fn, libtype) diff --git a/contrib/python/setuptools/py3/setuptools/command/build_py.py b/contrib/python/setuptools/py3/setuptools/command/build_py.py index 15a4f63fdd..584d2c15ac 100644 --- a/contrib/python/setuptools/py3/setuptools/command/build_py.py +++ b/contrib/python/setuptools/py3/setuptools/command/build_py.py @@ -1,21 +1,23 @@ from __future__ import annotations -from functools import partial -from glob import glob -from distutils.util import convert_path -import distutils.command.build_py as orig -import os import fnmatch -import textwrap -import distutils.errors import itertools +import os import stat +import textwrap +from functools import partial +from glob import glob from pathlib import Path from typing import Iterable, Iterator from more_itertools import unique_everseen + +from ..dist import Distribution from ..warnings import SetuptoolsDeprecationWarning +import distutils.command.build_py as orig +import distutils.errors +from distutils.util import convert_path _IMPLICIT_DATA_FILES = ('*.pyi', 'py.typed') @@ -34,6 +36,7 @@ class build_py(orig.build_py): 'py_modules' and 'packages' in the same setup operation. """ + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution editable_mode: bool = False existing_egg_info_dir: str | None = None #: Private API, internal use only. @@ -78,7 +81,7 @@ class build_py(orig.build_py): # output files are. self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=False)) - def __getattr__(self, attr): + def __getattr__(self, attr: str): "lazily compute data files" if attr == 'data_files': self.data_files = self._get_data_files() diff --git a/contrib/python/setuptools/py3/setuptools/command/develop.py b/contrib/python/setuptools/py3/setuptools/command/develop.py index 55f24f396c..4ecbd5a1e8 100644 --- a/contrib/python/setuptools/py3/setuptools/command/develop.py +++ b/contrib/python/setuptools/py3/setuptools/command/develop.py @@ -1,17 +1,16 @@ -from distutils.util import convert_path -from distutils import log -from distutils.errors import DistutilsOptionError -import os import glob +import os -from setuptools.command.easy_install import easy_install -from setuptools import _normalization -from setuptools import _path -from setuptools import namespaces import setuptools +from setuptools import _normalization, _path, namespaces +from setuptools.command.easy_install import easy_install from ..unicode_utils import _read_utf8_with_fallback +from distutils import log +from distutils.errors import DistutilsOptionError +from distutils.util import convert_path + class develop(namespaces.DevelopInstaller, easy_install): """Set up package for development""" @@ -189,7 +188,7 @@ class VersionlessRequirement: def __init__(self, dist): self.__dist = dist - def __getattr__(self, name): + def __getattr__(self, name: str): return getattr(self.__dist, name) def as_requirement(self): diff --git a/contrib/python/setuptools/py3/setuptools/command/dist_info.py b/contrib/python/setuptools/py3/setuptools/command/dist_info.py index 2adc1c46f3..1db3fbf6bd 100644 --- a/contrib/python/setuptools/py3/setuptools/command/dist_info.py +++ b/contrib/python/setuptools/py3/setuptools/command/dist_info.py @@ -6,14 +6,15 @@ As defined in the wheel specification import os import shutil from contextlib import contextmanager -from distutils import log -from distutils.core import Command from pathlib import Path from typing import cast from .. import _normalization from .egg_info import egg_info as egg_info_cls +from distutils import log +from distutils.core import Command + class dist_info(Command): """ diff --git a/contrib/python/setuptools/py3/setuptools/command/easy_install.py b/contrib/python/setuptools/py3/setuptools/command/easy_install.py index 36114d40ed..3f7fc17a88 100644 --- a/contrib/python/setuptools/py3/setuptools/command/easy_install.py +++ b/contrib/python/setuptools/py3/setuptools/command/easy_install.py @@ -12,72 +12,72 @@ __ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html from __future__ import annotations -from glob import glob -from distutils.util import get_platform -from distutils.util import convert_path, subst_vars -from distutils.errors import ( - DistutilsArgError, - DistutilsOptionError, - DistutilsError, - DistutilsPlatformError, -) -from distutils import log, dir_util -from distutils.command.build_scripts import first_line_re -from distutils.command import install -import sys +import configparser +import contextlib +import io import os -import zipimport -import shutil -import tempfile -import zipfile -import re -import stat import random -import textwrap -import warnings +import re +import shlex +import shutil import site +import stat import struct -import contextlib import subprocess -import shlex -import io -import configparser +import sys import sysconfig - +import tempfile +import textwrap +import warnings +import zipfile +import zipimport +from collections.abc import Iterable +from glob import glob from sysconfig import get_path +from typing import TYPE_CHECKING, Callable, TypeVar -from setuptools import Command -from setuptools.sandbox import run_setup -from setuptools.command import setopt -from setuptools.archive_util import unpack_archive -from setuptools.package_index import ( - PackageIndex, - parse_requirement_arg, - URL_SCHEME, -) -from setuptools.command import bdist_egg, egg_info -from setuptools.warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning -from setuptools.wheel import Wheel +from jaraco.text import yield_lines + +import pkg_resources from pkg_resources import ( - normalize_path, - resource_string, - get_distribution, - find_distributions, - Environment, - Requirement, + DEVELOP_DIST, Distribution, - PathMetadata, - EggMetadata, - WorkingSet, DistributionNotFound, + EggMetadata, + Environment, + PathMetadata, + Requirement, VersionConflict, - DEVELOP_DIST, + WorkingSet, + find_distributions, + get_distribution, + normalize_path, + resource_string, ) -import pkg_resources -from ..compat import py39, py311 +from setuptools import Command +from setuptools.archive_util import unpack_archive +from setuptools.command import bdist_egg, egg_info, setopt +from setuptools.package_index import URL_SCHEME, PackageIndex, parse_requirement_arg +from setuptools.sandbox import run_setup +from setuptools.warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning +from setuptools.wheel import Wheel + from .._path import ensure_directory -from jaraco.text import yield_lines +from ..compat import py39, py311, py312 + +from distutils import dir_util, log +from distutils.command import install +from distutils.command.build_scripts import first_line_re +from distutils.errors import ( + DistutilsArgError, + DistutilsError, + DistutilsOptionError, + DistutilsPlatformError, +) +from distutils.util import convert_path, get_platform, subst_vars +if TYPE_CHECKING: + from typing_extensions import Self # Turn on PEP440Warnings warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) @@ -89,6 +89,8 @@ __all__ = [ 'get_exe_prefixes', ] +_T = TypeVar("_T") + def is_64bit(): return struct.calcsize("P") == 8 @@ -588,8 +590,9 @@ class easy_install(Command): os.unlink(ok_file) dirname = os.path.dirname(ok_file) os.makedirs(dirname, exist_ok=True) - f = open(pth_file, 'w', encoding=py39.LOCALE_ENCODING) - # ^-- Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + f = open(pth_file, 'w', encoding=py312.PTH_ENCODING) + # ^-- Python<3.13 require encoding="locale" instead of "utf-8", + # see python/cpython#77102. except OSError: self.cant_write_to_target() else: @@ -1280,8 +1283,9 @@ class easy_install(Command): if os.path.islink(filename): os.unlink(filename) - with open(filename, 'wt', encoding=py39.LOCALE_ENCODING) as f: - # Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + with open(filename, 'wt', encoding=py312.PTH_ENCODING) as f: + # ^-- Python<3.13 require encoding="locale" instead of "utf-8", + # see python/cpython#77102. f.write(self.pth_file.make_relative(dist.location) + '\n') def unpack_progress(self, src, dst): @@ -1507,9 +1511,8 @@ def expand_paths(inputs): # noqa: C901 # is too complex (11) # FIXME continue # Read the .pth file - with open(os.path.join(dirname, name), encoding=py39.LOCALE_ENCODING) as f: - # Requires encoding="locale" instead of "utf-8" (python/cpython#77102). - lines = list(yield_lines(f)) + content = _read_pth(os.path.join(dirname, name)) + lines = list(yield_lines(content)) # Yield existing non-dupe, non-import directory lines from it for line in lines: @@ -1623,9 +1626,8 @@ class PthDistributions(Environment): paths = [] dirty = saw_import = False seen = set(self.sitedirs) - f = open(self.filename, 'rt', encoding=py39.LOCALE_ENCODING) - # ^-- Requires encoding="locale" instead of "utf-8" (python/cpython#77102). - for line in f: + content = _read_pth(self.filename) + for line in content.splitlines(): path = line.rstrip() # still keep imports and empty/commented lines for formatting paths.append(path) @@ -1644,7 +1646,6 @@ class PthDistributions(Environment): paths.pop() continue seen.add(normalized_path) - f.close() # remove any trailing empty/blank line while paths and not paths[-1].strip(): paths.pop() @@ -1695,8 +1696,9 @@ class PthDistributions(Environment): data = '\n'.join(lines) + '\n' if os.path.islink(self.filename): os.unlink(self.filename) - with open(self.filename, 'wt', encoding=py39.LOCALE_ENCODING) as f: - # Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + with open(self.filename, 'wt', encoding=py312.PTH_ENCODING) as f: + # ^-- Python<3.13 require encoding="locale" instead of "utf-8", + # see python/cpython#77102. f.write(data) elif os.path.exists(self.filename): log.debug("Deleting empty %s", self.filename) @@ -1786,13 +1788,14 @@ def _first_line_re(): return re.compile(first_line_re.pattern.decode()) -def auto_chmod(func, arg, exc): +# Must match shutil._OnExcCallback +def auto_chmod(func: Callable[..., _T], arg: str, exc: BaseException) -> _T: + """shutils onexc callback to automatically call chmod for certain functions.""" + # Only retry for scenarios known to have an issue if func in [os.unlink, os.remove] and os.name == 'nt': chmod(arg, stat.S_IWRITE) return func(arg) - et, ev, _ = sys.exc_info() - # TODO: This code doesn't make sense. What is it trying to do? - raise (ev[0], ev[1] + (" %s %s" % (func, arg))) + raise exc def update_dist_caches(dist_path, fix_zipimporter_caches): @@ -2018,7 +2021,9 @@ def is_python_script(script_text, filename): try: - from os import chmod as _chmod + from os import ( + chmod as _chmod, # pyright: ignore[reportAssignmentType] # Loosing type-safety w/ pyright, but that's ok + ) except ImportError: # Jython compatibility def _chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy re-uses the imported definition anyway @@ -2055,19 +2060,20 @@ class CommandSpec(list): return os.environ.get('__PYVENV_LAUNCHER__', _default) @classmethod - def from_param(cls, param): + def from_param(cls, param: Self | str | Iterable[str] | None) -> Self: """ Construct a CommandSpec from a parameter to build_scripts, which may be None. """ if isinstance(param, cls): return param - if isinstance(param, list): + if isinstance(param, str): + return cls.from_string(param) + if isinstance(param, Iterable): return cls(param) if param is None: return cls.from_environment() - # otherwise, assume it's a string. - return cls.from_string(param) + raise TypeError(f"Argument has an unsupported type {type(param)}") @classmethod def from_environment(cls): @@ -2351,6 +2357,26 @@ def only_strs(values): return filter(lambda val: isinstance(val, str), values) +def _read_pth(fullname: str) -> str: + # Python<3.13 require encoding="locale" instead of "utf-8", see python/cpython#77102 + # In the case old versions of setuptools are producing `pth` files with + # different encodings that might be problematic... So we fallback to "locale". + + try: + with open(fullname, encoding=py312.PTH_ENCODING) as f: + return f.read() + except UnicodeDecodeError: # pragma: no cover + # This error may only happen for Python >= 3.13 + # TODO: Possible deprecation warnings to be added in the future: + # ``.pth file {fullname!r} is not UTF-8.`` + # Your environment contain {fullname!r} that cannot be read as UTF-8. + # This is likely to have been produced with an old version of setuptools. + # Please be mindful that this is deprecated and in the future, non-utf8 + # .pth files may cause setuptools to fail. + with open(fullname, encoding=py39.LOCALE_ENCODING) as f: + return f.read() + + class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): _SUMMARY = "easy_install command is deprecated." _DETAILS = """ diff --git a/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py b/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py index 49fd609b15..2b21eacbad 100644 --- a/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py +++ b/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py @@ -12,8 +12,8 @@ Create a wheel that, when installed, will make the source package 'editable' from __future__ import annotations -import logging import io +import logging import os import shutil import traceback @@ -23,32 +23,15 @@ from inspect import cleandoc from itertools import chain, starmap from pathlib import Path from tempfile import TemporaryDirectory -from typing import ( - TYPE_CHECKING, - Iterable, - Iterator, - Mapping, - Protocol, - TypeVar, - cast, -) - -from .. import ( - Command, - _normalization, - _path, - errors, - namespaces, -) +from types import TracebackType +from typing import TYPE_CHECKING, Iterable, Iterator, Mapping, Protocol, TypeVar, cast + +from .. import Command, _normalization, _path, errors, namespaces from .._path import StrPath -from ..compat import py39 +from ..compat import py312 from ..discovery import find_package_path from ..dist import Distribution -from ..warnings import ( - InformationOnly, - SetuptoolsDeprecationWarning, - SetuptoolsWarning, -) +from ..warnings import InformationOnly, SetuptoolsDeprecationWarning, SetuptoolsWarning from .build import build as build_cls from .build_py import build_py as build_py_cls from .dist_info import dist_info as dist_info_cls @@ -57,6 +40,8 @@ from .install import install as install_cls from .install_scripts import install_scripts as install_scripts_cls if TYPE_CHECKING: + from typing_extensions import Self + from .._vendor.wheel.wheelfile import WheelFile _P = TypeVar("_P", bound=StrPath) @@ -153,6 +138,7 @@ class editable_wheel(Command): self._create_wheel_file(bdist_wheel) except Exception: traceback.print_exc() + # TODO: Fix false-positive [attr-defined] in typeshed project = self.distribution.name or self.distribution.get_name() _DebuggingTips.emit(project=project) raise @@ -396,9 +382,15 @@ class editable_wheel(Command): class EditableStrategy(Protocol): def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): ... - def __enter__(self): ... + def __enter__(self) -> Self: ... - def __exit__(self, _exc_type, _exc_value, _traceback): ... + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + /, + ) -> object: ... class _StaticPth: @@ -412,7 +404,7 @@ class _StaticPth: contents = _encode_pth(f"{entries}\n") wheel.writestr(f"__editable__.{self.name}.pth", contents) - def __enter__(self): + def __enter__(self) -> Self: msg = f""" Editable install will be performed using .pth file to extend `sys.path` with: {list(map(os.fspath, self.path_entries))!r} @@ -420,7 +412,13 @@ class _StaticPth: _logger.warning(msg + _LENIENT_WARNING) return self - def __exit__(self, _exc_type, _exc_value, _traceback): ... + def __exit__( + self, + _exc_type: object, + _exc_value: object, + _traceback: object, + ) -> None: + pass class _LinkTree(_StaticPth): @@ -466,8 +464,9 @@ class _LinkTree(_StaticPth): def _create_links(self, outputs, output_mapping): self.auxiliary_dir.mkdir(parents=True, exist_ok=True) link_type = "sym" if _can_symlink_files(self.auxiliary_dir) else "hard" - mappings = {self._normalize_output(k): v for k, v in output_mapping.items()} - mappings.pop(None, None) # remove files that are not relative to build_lib + normalised = ((self._normalize_output(k), v) for k, v in output_mapping.items()) + # remove files that are not relative to build_lib + mappings = {k: v for k, v in normalised if k is not None} for output in outputs: relative = self._normalize_output(output) @@ -477,12 +476,17 @@ class _LinkTree(_StaticPth): for relative, src in mappings.items(): self._create_file(relative, src, link=link_type) - def __enter__(self): + def __enter__(self) -> Self: msg = "Strict editable install will be performed using a link tree.\n" _logger.warning(msg + _STRICT_WARNING) return self - def __exit__(self, _exc_type, _exc_value, _traceback): + def __exit__( + self, + _exc_type: object, + _exc_value: object, + _traceback: object, + ) -> None: msg = f"""\n Strict editable installation performed using the auxiliary directory: {self.auxiliary_dir} @@ -538,12 +542,17 @@ class _TopLevelFinder: for file, content in self.get_implementation(): wheel.writestr(file, content) - def __enter__(self): + def __enter__(self) -> Self: msg = "Editable install will be performed using a meta path finder.\n" _logger.warning(msg + _LENIENT_WARNING) return self - def __exit__(self, _exc_type, _exc_value, _traceback): + def __exit__( + self, + _exc_type: object, + _exc_value: object, + _traceback: object, + ) -> None: msg = """\n Please be careful with folders in your working directory with the same name as your package as they may take precedence during imports. @@ -552,7 +561,9 @@ class _TopLevelFinder: def _encode_pth(content: str) -> bytes: - """.pth files are always read with 'locale' encoding, the recommendation + """ + Prior to Python 3.13 (see https://github.com/python/cpython/issues/77102), + .pth files are always read with 'locale' encoding, the recommendation from the cpython core developers is to write them as ``open(path, "w")`` and ignore warnings (see python/cpython#77102, pypa/setuptools#3937). This function tries to simulate this behaviour without having to create an @@ -562,7 +573,8 @@ def _encode_pth(content: str) -> bytes: or ``locale.getencoding()``). """ with io.BytesIO() as buffer: - wrapper = io.TextIOWrapper(buffer, encoding=py39.LOCALE_ENCODING) + wrapper = io.TextIOWrapper(buffer, encoding=py312.PTH_ENCODING) + # TODO: Python 3.13 replace the whole function with `bytes(content, "utf-8")` wrapper.write(content) wrapper.flush() buffer.seek(0) diff --git a/contrib/python/setuptools/py3/setuptools/command/egg_info.py b/contrib/python/setuptools/py3/setuptools/command/egg_info.py index 30b62f5f2e..280eb5e807 100644 --- a/contrib/python/setuptools/py3/setuptools/command/egg_info.py +++ b/contrib/python/setuptools/py3/setuptools/command/egg_info.py @@ -2,34 +2,35 @@ Create a distribution's .egg-info directory and contents""" -from distutils.filelist import FileList as _FileList -from distutils.errors import DistutilsInternalError -from distutils.util import convert_path -from distutils import log -import distutils.errors -import distutils.filelist +import collections import functools import os import re import sys import time -import collections -from .._importlib import metadata -from .. import _entry_points, _normalization -from . import _requirestxt +import packaging +import packaging.requirements +import packaging.version +import setuptools.unicode_utils as unicode_utils from setuptools import Command -from setuptools.command.sdist import sdist -from setuptools.command.sdist import walk_revctrl -from setuptools.command.setopt import edit_config from setuptools.command import bdist_egg -import setuptools.unicode_utils as unicode_utils +from setuptools.command.sdist import sdist, walk_revctrl +from setuptools.command.setopt import edit_config from setuptools.glob import glob -import packaging +from .. import _entry_points, _normalization +from .._importlib import metadata from ..warnings import SetuptoolsDeprecationWarning +from . import _requirestxt +import distutils.errors +import distutils.filelist +from distutils import log +from distutils.errors import DistutilsInternalError +from distutils.filelist import FileList as _FileList +from distutils.util import convert_path PY_MAJOR = '{}.{}'.format(*sys.version_info) @@ -605,16 +606,6 @@ class manifest_maker(sdist): log.debug("adding file referenced by config '%s'", rf) self.filelist.extend(referenced) - def prune_file_list(self): - build = self.get_finalized_command('build') - base_dir = self.distribution.get_fullname() - self.filelist.prune(build.build_base) - self.filelist.prune(base_dir) - sep = re.escape(os.sep) - self.filelist.exclude_pattern( - r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep, is_regex=True - ) - def _safe_data_files(self, build_py): """ The parent class implementation of this method diff --git a/contrib/python/setuptools/py3/setuptools/command/install.py b/contrib/python/setuptools/py3/setuptools/command/install.py index f1ea2adf1d..e2ec1abdde 100644 --- a/contrib/python/setuptools/py3/setuptools/command/install.py +++ b/contrib/python/setuptools/py3/setuptools/command/install.py @@ -1,17 +1,20 @@ from __future__ import annotations -from collections.abc import Callable -from distutils.errors import DistutilsArgError -import inspect import glob +import inspect import platform -import distutils.command.install as orig +from collections.abc import Callable from typing import Any, ClassVar, cast import setuptools + +from ..dist import Distribution from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning from .bdist_egg import bdist_egg as bdist_egg_cls +import distutils.command.install as orig +from distutils.errors import DistutilsArgError + # Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for # now. See https://github.com/pypa/setuptools/issues/199/ _install = orig.install @@ -20,6 +23,8 @@ _install = orig.install class install(orig.install): """Use easy_install to install the package, w/dependencies""" + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + user_options = orig.install.user_options + [ ('old-and-unmanageable', None, "Try not to use this!"), ( diff --git a/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py b/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py index a1d2e8184f..be47254f00 100644 --- a/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py +++ b/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py @@ -1,11 +1,12 @@ -from distutils import log, dir_util import os -from setuptools import Command -from setuptools import namespaces +from setuptools import Command, namespaces from setuptools.archive_util import unpack_archive + from .._path import ensure_directory +from distutils import dir_util, log + class install_egg_info(namespaces.Installer, Command): """Install an .egg-info directory for the package""" diff --git a/contrib/python/setuptools/py3/setuptools/command/install_lib.py b/contrib/python/setuptools/py3/setuptools/command/install_lib.py index 3c77c6ebc6..292f07ab6e 100644 --- a/contrib/python/setuptools/py3/setuptools/command/install_lib.py +++ b/contrib/python/setuptools/py3/setuptools/command/install_lib.py @@ -1,14 +1,20 @@ from __future__ import annotations + import os import sys from itertools import product, starmap -import distutils.command.install_lib as orig + from .._path import StrPath +from ..dist import Distribution + +import distutils.command.install_lib as orig class install_lib(orig.install_lib): """Don't add compiled flags to filenames of non-Python files""" + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def run(self): self.build() outfiles = self.install() @@ -103,6 +109,7 @@ class install_lib(orig.install_lib): # Exclude namespace package __init__.py* files from the output from setuptools.archive_util import unpack_directory + from distutils import log outfiles: list[str] = [] diff --git a/contrib/python/setuptools/py3/setuptools/command/install_scripts.py b/contrib/python/setuptools/py3/setuptools/command/install_scripts.py index f44281b49b..7b90611d1c 100644 --- a/contrib/python/setuptools/py3/setuptools/command/install_scripts.py +++ b/contrib/python/setuptools/py3/setuptools/command/install_scripts.py @@ -1,16 +1,20 @@ from __future__ import annotations -from distutils import log -import distutils.command.install_scripts as orig import os import sys from .._path import ensure_directory +from ..dist import Distribution + +import distutils.command.install_scripts as orig +from distutils import log class install_scripts(orig.install_scripts): """Do normal script install, plus any egg_info wrapper scripts""" + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def initialize_options(self): orig.install_scripts.initialize_options(self) self.no_ep = False @@ -29,6 +33,7 @@ class install_scripts(orig.install_scripts): def _install_ep_scripts(self): # Delay import side-effects from pkg_resources import Distribution, PathMetadata + from . import easy_install as ei ei_cmd = self.get_finalized_command("egg_info") diff --git a/contrib/python/setuptools/py3/setuptools/command/register.py b/contrib/python/setuptools/py3/setuptools/command/register.py index beee9782e7..93ef04aa0e 100644 --- a/contrib/python/setuptools/py3/setuptools/command/register.py +++ b/contrib/python/setuptools/py3/setuptools/command/register.py @@ -1,12 +1,16 @@ -from distutils import log -import distutils.command.register as orig - from setuptools.errors import RemovedCommandError +from ..dist import Distribution + +import distutils.command.register as orig +from distutils import log + class register(orig.register): """Formerly used to register packages on PyPI.""" + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def run(self): msg = ( "The register command has been removed, use twine to upload " diff --git a/contrib/python/setuptools/py3/setuptools/command/rotate.py b/contrib/python/setuptools/py3/setuptools/command/rotate.py index 064d7959ff..dcdfafbcf7 100644 --- a/contrib/python/setuptools/py3/setuptools/command/rotate.py +++ b/contrib/python/setuptools/py3/setuptools/command/rotate.py @@ -1,13 +1,14 @@ from __future__ import annotations -from distutils.util import convert_path -from distutils import log -from distutils.errors import DistutilsOptionError import os import shutil from setuptools import Command +from distutils import log +from distutils.errors import DistutilsOptionError +from distutils.util import convert_path + class rotate(Command): """Delete older distributions""" diff --git a/contrib/python/setuptools/py3/setuptools/command/sdist.py b/contrib/python/setuptools/py3/setuptools/command/sdist.py index a834ba4a78..fa9a2c4d81 100644 --- a/contrib/python/setuptools/py3/setuptools/command/sdist.py +++ b/contrib/python/setuptools/py3/setuptools/command/sdist.py @@ -1,12 +1,17 @@ -from distutils import log -import distutils.command.sdist as orig -import os +from __future__ import annotations + import contextlib +import os +import re from itertools import chain from .._importlib import metadata +from ..dist import Distribution from .build import _ORIGINAL_SUBCOMMANDS +import distutils.command.sdist as orig +from distutils import log + _default_revctrl = list @@ -43,7 +48,8 @@ class sdist(orig.sdist): ), ] - negative_opt = {} + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + negative_opt: dict[str, str] = {} README_EXTENSIONS = ['', '.rst', '.txt', '.md'] READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS) @@ -151,6 +157,12 @@ class sdist(orig.sdist): except TypeError: log.warn("data_files contains unexpected objects") + def prune_file_list(self): + super().prune_file_list() + # Prevent accidental inclusion of test-related cache dirs at the project root + sep = re.escape(os.sep) + self.filelist.exclude_pattern(r"^(\.tox|\.nox|\.venv)" + sep, is_regex=True) + def check_readme(self): for f in self.READMES: if os.path.exists(f): diff --git a/contrib/python/setuptools/py3/setuptools/command/setopt.py b/contrib/python/setuptools/py3/setuptools/command/setopt.py index b78d845e60..e351af22f0 100644 --- a/contrib/python/setuptools/py3/setuptools/command/setopt.py +++ b/contrib/python/setuptools/py3/setuptools/command/setopt.py @@ -1,13 +1,14 @@ -from distutils.util import convert_path -from distutils import log -from distutils.errors import DistutilsOptionError -import distutils -import os import configparser +import os from .. import Command from ..unicode_utils import _cfg_read_utf8_with_fallback +import distutils +from distutils import log +from distutils.errors import DistutilsOptionError +from distutils.util import convert_path + __all__ = ['config_file', 'edit_config', 'option_base', 'setopt'] diff --git a/contrib/python/setuptools/py3/setuptools/command/test.py b/contrib/python/setuptools/py3/setuptools/command/test.py index fbdf9fb942..341b11a20e 100644 --- a/contrib/python/setuptools/py3/setuptools/command/test.py +++ b/contrib/python/setuptools/py3/setuptools/command/test.py @@ -1,82 +1,29 @@ -import os -import operator -import sys -import contextlib -import itertools -import unittest -from distutils.errors import DistutilsError, DistutilsOptionError -from distutils import log -from unittest import TestLoader +from __future__ import annotations -from pkg_resources import ( - resource_listdir, - resource_exists, - normalize_path, - working_set, - evaluate_marker, - add_activation_listener, - require, -) -from .._importlib import metadata from setuptools import Command -from more_itertools import unique_everseen -from jaraco.functools import pass_none - - -class ScanningLoader(TestLoader): - def __init__(self): - TestLoader.__init__(self) - self._visited = set() - - def loadTestsFromModule(self, module, pattern=None): - """Return a suite of all tests cases contained in the given module - - If the module is a package, load tests from all the modules in it. - If the module has an ``additional_tests`` function, call it and add - the return value to the tests. - """ - if module in self._visited: - return None - self._visited.add(module) - - tests = [] - tests.append(TestLoader.loadTestsFromModule(self, module)) - - if hasattr(module, "additional_tests"): - tests.append(module.additional_tests()) - - if hasattr(module, '__path__'): - for file in resource_listdir(module.__name__, ''): - if file.endswith('.py') and file != '__init__.py': - submodule = module.__name__ + '.' + file[:-3] - else: - if resource_exists(module.__name__, file + '/__init__.py'): - submodule = module.__name__ + '.' + file - else: - continue - tests.append(self.loadTestsFromName(submodule)) - - if len(tests) != 1: - return self.suiteClass(tests) - else: - return tests[0] # don't create a nested suite for only one return - - -# adapted from jaraco.classes.properties:NonDataProperty -class NonDataProperty: - def __init__(self, fget): - self.fget = fget - - def __get__(self, obj, objtype=None): - if obj is None: - return self - return self.fget(obj) +from setuptools.warnings import SetuptoolsDeprecationWarning + + +# Would restrict to Literal["test"], but mypy doesn't support it: https://github.com/python/mypy/issues/8203 +def __getattr__(name: str) -> type[_test]: + if name == 'test': + SetuptoolsDeprecationWarning.emit( + "The test command is disabled and references to it are deprecated.", + "Please remove any references to `setuptools.command.test` in all " + "supported versions of the affected package.", + due_date=(2024, 11, 15), + stacklevel=2, + ) + return _test + raise AttributeError(name) -class test(Command): - """Command to run unit tests after in-place build""" +class _test(Command): + """ + Stub to warn when test command is referenced or used. + """ - description = "run unit tests after in-place build (deprecated)" + description = "stub for old test command (do not use)" user_options = [ ('test-module=', 'm', "Run 'test_suite' in specified module"), @@ -89,162 +36,10 @@ class test(Command): ] def initialize_options(self): - self.test_suite = None - self.test_module = None - self.test_loader = None - self.test_runner = None + pass def finalize_options(self): - if self.test_suite and self.test_module: - msg = "You may specify a module or a suite, but not both" - raise DistutilsOptionError(msg) - - if self.test_suite is None: - if self.test_module is None: - self.test_suite = self.distribution.test_suite - else: - self.test_suite = self.test_module + ".test_suite" - - if self.test_loader is None: - self.test_loader = getattr(self.distribution, 'test_loader', None) - if self.test_loader is None: - self.test_loader = "setuptools.command.test:ScanningLoader" - if self.test_runner is None: - self.test_runner = getattr(self.distribution, 'test_runner', None) - - @NonDataProperty - def test_args(self): - return list(self._test_args()) - - def _test_args(self): - if not self.test_suite: - yield 'discover' - if self.verbose: - yield '--verbose' - if self.test_suite: - yield self.test_suite - - def with_project_on_sys_path(self, func): - """ - Backward compatibility for project_on_sys_path context. - """ - with self.project_on_sys_path(): - func() - - @contextlib.contextmanager - def project_on_sys_path(self, include_dists=()): - self.run_command('egg_info') - - # Build extensions in-place - self.reinitialize_command('build_ext', inplace=True) - self.run_command('build_ext') - - ei_cmd = self.get_finalized_command("egg_info") - - old_path = sys.path[:] - old_modules = sys.modules.copy() - - try: - project_path = normalize_path(ei_cmd.egg_base) - sys.path.insert(0, project_path) - working_set.__init__() - add_activation_listener(lambda dist: dist.activate()) - require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) - with self.paths_on_pythonpath([project_path]): - yield - finally: - sys.path[:] = old_path - sys.modules.clear() - sys.modules.update(old_modules) - working_set.__init__() - - @staticmethod - @contextlib.contextmanager - def paths_on_pythonpath(paths): - """ - Add the indicated paths to the head of the PYTHONPATH environment - variable so that subprocesses will also see the packages at - these paths. - - Do this in a context that restores the value on exit. - """ - nothing = object() - orig_pythonpath = os.environ.get('PYTHONPATH', nothing) - current_pythonpath = os.environ.get('PYTHONPATH', '') - try: - prefix = os.pathsep.join(unique_everseen(paths)) - to_join = filter(None, [prefix, current_pythonpath]) - new_path = os.pathsep.join(to_join) - if new_path: - os.environ['PYTHONPATH'] = new_path - yield - finally: - if orig_pythonpath is nothing: - os.environ.pop('PYTHONPATH', None) - else: - os.environ['PYTHONPATH'] = orig_pythonpath - - @staticmethod - def install_dists(dist): - """ - Install the requirements indicated by self.distribution and - return an iterable of the dists that were built. - """ - ir_d = dist.fetch_build_eggs(dist.install_requires) - tr_d = dist.fetch_build_eggs(dist.tests_require or []) - er_d = dist.fetch_build_eggs( - v - for k, v in dist.extras_require.items() - if k.startswith(':') and evaluate_marker(k[1:]) - ) - return itertools.chain(ir_d, tr_d, er_d) + pass def run(self): - self.announce( - "WARNING: Testing via this command is deprecated and will be " - "removed in a future version. Users looking for a generic test " - "entry point independent of test runner are encouraged to use " - "tox.", - log.WARN, - ) - - installed_dists = self.install_dists(self.distribution) - - cmd = ' '.join(self._argv) - if self.dry_run: - self.announce('skipping "%s" (dry run)' % cmd) - return - - self.announce('running "%s"' % cmd) - - paths = map(operator.attrgetter('location'), installed_dists) - with self.paths_on_pythonpath(paths): - with self.project_on_sys_path(): - self.run_tests() - - def run_tests(self): - test = unittest.main( - None, - None, - self._argv, - testLoader=self._resolve_as_ep(self.test_loader), - testRunner=self._resolve_as_ep(self.test_runner), - exit=False, - ) - if not test.result.wasSuccessful(): - msg = 'Test failed: %s' % test.result - self.announce(msg, log.ERROR) - raise DistutilsError(msg) - - @property - def _argv(self): - return ['unittest'] + self.test_args - - @staticmethod - @pass_none - def _resolve_as_ep(val): - """ - Load the indicated attribute value, called, as a as if it were - specified as an entry point. - """ - return metadata.EntryPoint(value=val, name=None, group=None).load()() + raise RuntimeError("Support for the test command was removed in Setuptools 72") diff --git a/contrib/python/setuptools/py3/setuptools/command/upload.py b/contrib/python/setuptools/py3/setuptools/command/upload.py index 1cca47cea9..649b41fa30 100644 --- a/contrib/python/setuptools/py3/setuptools/command/upload.py +++ b/contrib/python/setuptools/py3/setuptools/command/upload.py @@ -1,12 +1,15 @@ +from setuptools.dist import Distribution +from setuptools.errors import RemovedCommandError + from distutils import log from distutils.command import upload as orig -from setuptools.errors import RemovedCommandError - class upload(orig.upload): """Formerly used to upload packages to PyPI.""" + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def run(self): msg = ( "The upload command has been removed, use twine to upload " diff --git a/contrib/python/setuptools/py3/setuptools/command/upload_docs.py b/contrib/python/setuptools/py3/setuptools/command/upload_docs.py index 32c9abd796..3c2946cfc8 100644 --- a/contrib/python/setuptools/py3/setuptools/command/upload_docs.py +++ b/contrib/python/setuptools/py3/setuptools/command/upload_docs.py @@ -4,23 +4,23 @@ Implements a Distutils 'upload_docs' subcommand (upload documentation to sites other than PyPi such as devpi). """ -from base64 import standard_b64encode -from distutils import log -from distutils.errors import DistutilsOptionError -import os -import zipfile -import tempfile -import shutil -import itertools import functools import http.client +import itertools +import os +import shutil +import tempfile import urllib.parse +import zipfile +from base64 import standard_b64encode from .._importlib import metadata from ..warnings import SetuptoolsDeprecationWarning - from .upload import upload +from distutils import log +from distutils.errors import DistutilsOptionError + def _encode(s): return s.encode('utf-8', 'surrogateescape') diff --git a/contrib/python/setuptools/py3/setuptools/compat/py310.py b/contrib/python/setuptools/py3/setuptools/compat/py310.py index cc875c004b..b3912f8e02 100644 --- a/contrib/python/setuptools/py3/setuptools/compat/py310.py +++ b/contrib/python/setuptools/py3/setuptools/compat/py310.py @@ -1,6 +1,5 @@ import sys - __all__ = ['tomllib'] diff --git a/contrib/python/setuptools/py3/setuptools/compat/py311.py b/contrib/python/setuptools/py3/setuptools/compat/py311.py index 5069c441c4..52b58af32a 100644 --- a/contrib/python/setuptools/py3/setuptools/compat/py311.py +++ b/contrib/python/setuptools/py3/setuptools/compat/py311.py @@ -2,13 +2,14 @@ from __future__ import annotations import shutil import sys -from typing import Any, Callable, TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Callable if TYPE_CHECKING: - from _typeshed import StrOrBytesPath, ExcInfo + from _typeshed import ExcInfo, StrOrBytesPath + from typing_extensions import TypeAlias # Same as shutil._OnExcCallback from typeshed -_OnExcCallback = Callable[[Callable[..., Any], str, BaseException], object] +_OnExcCallback: TypeAlias = Callable[[Callable[..., Any], str, BaseException], object] def shutil_rmtree( diff --git a/contrib/python/setuptools/py3/setuptools/compat/py312.py b/contrib/python/setuptools/py3/setuptools/compat/py312.py new file mode 100644 index 0000000000..b20c5f697a --- /dev/null +++ b/contrib/python/setuptools/py3/setuptools/compat/py312.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +import sys + +if sys.version_info >= (3, 12, 4): + # Python 3.13 should support `.pth` files encoded in UTF-8 + # See discussion in https://github.com/python/cpython/issues/77102 + PTH_ENCODING: str | None = "utf-8" +else: + from .py39 import LOCALE_ENCODING + + # PTH_ENCODING = "locale" + PTH_ENCODING = LOCALE_ENCODING diff --git a/contrib/python/setuptools/py3/setuptools/config/NOTICE b/contrib/python/setuptools/py3/setuptools/config/NOTICE new file mode 100644 index 0000000000..01864511b0 --- /dev/null +++ b/contrib/python/setuptools/py3/setuptools/config/NOTICE @@ -0,0 +1,10 @@ +The following files include code from opensource projects +(either as direct copies or modified versions): + +- `setuptools.schema.json`, `distutils.schema.json`: + - project: `validate-pyproject` - licensed under MPL-2.0 + (https://github.com/abravalheri/validate-pyproject): + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this file, + You can obtain one at https://mozilla.org/MPL/2.0/. diff --git a/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py b/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py index 6cc59d2b95..23179f3548 100644 --- a/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py +++ b/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py @@ -17,27 +17,26 @@ from functools import partial, reduce from inspect import cleandoc from itertools import chain from types import MappingProxyType -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - Mapping, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Dict, Mapping, TypeVar, Union + from .._path import StrPath from ..errors import RemovedConfigError +from ..extension import Extension from ..warnings import SetuptoolsWarning if TYPE_CHECKING: - from distutils.dist import _OptionsList + from typing_extensions import TypeAlias + from setuptools._importlib import metadata from setuptools.dist import Distribution + from distutils.dist import _OptionsList + EMPTY: Mapping = MappingProxyType({}) # Immutable dict-like -_ProjectReadmeValue = Union[str, Dict[str, str]] -_CorrespFn = Callable[["Distribution", Any, StrPath], None] -_Correspondence = Union[str, _CorrespFn] +_ProjectReadmeValue: TypeAlias = Union[str, Dict[str, str]] +_CorrespFn: TypeAlias = Callable[["Distribution", Any, StrPath], None] +_Correspondence: TypeAlias = Union[str, _CorrespFn] +_T = TypeVar("_T") _logger = logging.getLogger(__name__) @@ -120,13 +119,14 @@ def json_compatible_key(key: str) -> str: def _set_config(dist: Distribution, field: str, value: Any): + val = _PREPROCESS.get(field, _noop)(dist, value) setter = getattr(dist.metadata, f"set_{field}", None) if setter: - setter(value) + setter(val) elif hasattr(dist.metadata, field) or field in SETUPTOOLS_PATCHES: - setattr(dist.metadata, field, value) + setattr(dist.metadata, field, val) else: - setattr(dist, field, value) + setattr(dist, field, val) _CONTENT_TYPES = { @@ -221,6 +221,17 @@ def _optional_dependencies(dist: Distribution, val: dict, _root_dir): dist.extras_require = {**existing, **val} +def _ext_modules(dist: Distribution, val: list[dict]) -> list[Extension]: + existing = dist.ext_modules or [] + args = ({k.replace("-", "_"): v for k, v in x.items()} for x in val) + new = [Extension(**kw) for kw in args] + return [*existing, *new] + + +def _noop(_dist: Distribution, val: _T) -> _T: + return val + + def _unify_entry_points(project_table: dict): project = project_table entry_points = project.pop("entry-points", project.pop("entry_points", {})) @@ -261,9 +272,10 @@ def _copy_command_options(pyproject: dict, dist: Distribution, filename: StrPath def _valid_command_options(cmdclass: Mapping = EMPTY) -> dict[str, set[str]]: - from .._importlib import metadata from setuptools.dist import Distribution + from .._importlib import metadata + valid_options = {"global": _normalise_cmd_options(Distribution.global_options)} unloaded_entry_points = metadata.entry_points(group='distutils.commands') @@ -278,6 +290,11 @@ def _valid_command_options(cmdclass: Mapping = EMPTY) -> dict[str, set[str]]: def _load_ep(ep: metadata.EntryPoint) -> tuple[str, type] | None: + if ep.value.startswith("wheel.bdist_wheel"): + # Ignore deprecated entrypoint from wheel and avoid warning pypa/wheel#631 + # TODO: remove check when `bdist_wheel` has been fully removed from pypa/wheel + return None + # Ignore all the errors try: return (ep.name, ep.load()) @@ -373,6 +390,10 @@ SETUPTOOLS_PATCHES = { "license_files", } +_PREPROCESS = { + "ext_modules": _ext_modules, +} + _PREVIOUSLY_DEFINED = { "name": _attrgetter("metadata.name"), "version": _attrgetter("metadata.version"), diff --git a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/NOTICE b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/NOTICE new file mode 100644 index 0000000000..74e8821fc8 --- /dev/null +++ b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/NOTICE @@ -0,0 +1,438 @@ +The code contained in this directory was automatically generated using the +following command: + + python -m validate_pyproject.pre_compile --output-dir=setuptools/config/_validate_pyproject --enable-plugins setuptools distutils --very-verbose -t distutils=setuptools/config/distutils.schema.json -t setuptools=setuptools/config/setuptools.schema.json + +Please avoid changing it manually. + + +You can report issues or suggest changes directly to `validate-pyproject` +(or to the relevant plugin repository) + +- https://github.com/abravalheri/validate-pyproject/issues + + +*** + +The following files include code from opensource projects +(either as direct copies or modified versions): + +- `fastjsonschema_exceptions.py`: + - project: `fastjsonschema` - licensed under BSD-3-Clause + (https://github.com/horejsek/python-fastjsonschema) +- `extra_validations.py` and `format.py`, `error_reporting.py`: + - project: `validate-pyproject` - licensed under MPL-2.0 + (https://github.com/abravalheri/validate-pyproject) + + +Additionally the following files are automatically generated by tools provided +by the same projects: + +- `__init__.py` +- `fastjsonschema_validations.py` + +The relevant copyright notes and licenses are included below. + + +*** + +`fastjsonschema` +================ + +Copyright (c) 2018, Michal Horejsek +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +*** + +`validate-pyproject` +==================== + +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. diff --git a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/error_reporting.py b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/error_reporting.py index a6753604f5..3591231c09 100644 --- a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/error_reporting.py +++ b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/error_reporting.py @@ -6,7 +6,7 @@ import re import typing from contextlib import contextmanager from textwrap import indent, wrap -from typing import Any, Dict, Generator, Iterator, List, Optional, Sequence, Union, cast +from typing import Any, Dict, Generator, Iterator, List, Optional, Sequence, Union from .fastjsonschema_exceptions import JsonSchemaValueException @@ -316,9 +316,7 @@ class _SummaryWriter: def _value(self, value: Any, path: Sequence[str]) -> str: if path[-1] == "type" and not self._is_property(path): type_ = self._jargon(value) - return ( - f"[{', '.join(type_)}]" if isinstance(value, list) else cast(str, type_) - ) + return f"[{', '.join(type_)}]" if isinstance(type_, list) else type_ return repr(value) def _inline_attrs(self, schema: dict, path: Sequence[str]) -> Iterator[str]: diff --git a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py index 1c58a55ea8..42e7aa5e33 100644 --- a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py +++ b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py @@ -10,7 +10,7 @@ # *** PLEASE DO NOT MODIFY DIRECTLY: Automatically generated code *** -VERSION = "2.19.1" +VERSION = "2.20.0" from decimal import Decimal import re from .fastjsonschema_exceptions import JsonSchemaValueException @@ -31,7 +31,7 @@ def validate(data, custom_formats={}, name_prefix=None): def validate_https___packaging_python_org_en_latest_specifications_declaring_build_dependencies(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/classifiers/>`_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '<https://packaging.python.org/specifications/entry-points/>`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/classifiers/>`_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '<https://packaging.python.org/specifications/entry-points/>`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/classifiers/>`_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '<https://packaging.python.org/specifications/entry-points/>`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/classifiers/>`_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '<https://packaging.python.org/specifications/entry-points/>`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_keys = set(data.keys()) @@ -86,7 +86,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_bui data_keys.remove("tool") data__tool = data["tool"] if not isinstance(data__tool, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".tool must be object", value=data__tool, name="" + (name_prefix or "data") + ".tool", definition={'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".tool must be object", value=data__tool, name="" + (name_prefix or "data") + ".tool", definition={'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, rule='type') data__tool_is_dict = isinstance(data__tool, dict) if data__tool_is_dict: data__tool_keys = set(data__tool.keys()) @@ -99,12 +99,12 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_bui data__tool__setuptools = data__tool["setuptools"] validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html(data__tool__setuptools, custom_formats, (name_prefix or "data") + ".tool.setuptools") if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/classifiers/>`_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '<https://packaging.python.org/specifications/entry-points/>`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/classifiers/>`_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '<https://packaging.python.org/specifications/entry-points/>`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/classifiers/>`_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '<https://packaging.python.org/specifications/entry-points/>`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/classifiers/>`_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/entry-points/>`_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '<https://packaging.python.org/specifications/entry-points/>`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') return data def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_keys = set(data.keys()) @@ -181,7 +181,7 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm if data__packages_one_of_count1 < 2: try: if not isinstance(data__packages, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be array", value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be array", value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, rule='type') data__packages_is_list = isinstance(data__packages, (list, tuple)) if data__packages_is_list: data__packages_len = len(data__packages) @@ -195,12 +195,12 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm data__packages_one_of_count1 += 1 except JsonSchemaValueException: pass if data__packages_one_of_count1 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count1) + " matches found)"), value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, rule='oneOf') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count1) + " matches found)"), value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, rule='oneOf') if "package-dir" in data_keys: data_keys.remove("package-dir") data__packagedir = data["package-dir"] if not isinstance(data__packagedir, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='type') data__packagedir_is_dict = isinstance(data__packagedir, dict) if data__packagedir_is_dict: data__packagedir_keys = set(data__packagedir.keys()) @@ -211,7 +211,7 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm if not isinstance(data__packagedir_val, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + " must be string", value=data__packagedir_val, name="" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + "", definition={'type': 'string'}, rule='type') if data__packagedir_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must not contain "+str(data__packagedir_keys)+" properties", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must not contain "+str(data__packagedir_keys)+" properties", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='additionalProperties') data__packagedir_len = len(data__packagedir) if data__packagedir_len != 0: data__packagedir_property_names = True @@ -230,11 +230,11 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm data__packagedir_key_any_of_count2 += 1 except JsonSchemaValueException: pass if not data__packagedir_key_any_of_count2: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir cannot be validated by any definition", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, rule='anyOf') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir cannot be validated by any definition", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, rule='anyOf') except JsonSchemaValueException: data__packagedir_property_names = False if not data__packagedir_property_names: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be named by propertyName definition", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='propertyNames') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be named by propertyName definition", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='propertyNames') if "package-data" in data_keys: data_keys.remove("package-data") data__packagedata = data["package-data"] @@ -342,30 +342,40 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm data_keys.remove("namespace-packages") data__namespacepackages = data["namespace-packages"] if not isinstance(data__namespacepackages, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages must be array", value=data__namespacepackages, name="" + (name_prefix or "data") + ".namespace-packages", definition={'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages must be array", value=data__namespacepackages, name="" + (name_prefix or "data") + ".namespace-packages", definition={'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, rule='type') data__namespacepackages_is_list = isinstance(data__namespacepackages, (list, tuple)) if data__namespacepackages_is_list: data__namespacepackages_len = len(data__namespacepackages) for data__namespacepackages_x, data__namespacepackages_item in enumerate(data__namespacepackages): if not isinstance(data__namespacepackages_item, (str)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + " must be string", value=data__namespacepackages_item, name="" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + " must be string", value=data__namespacepackages_item, name="" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='type') if isinstance(data__namespacepackages_item, str): - if not custom_formats["python-module-name"](data__namespacepackages_item): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + " must be python-module-name", value=data__namespacepackages_item, name="" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') + if not custom_formats["python-module-name-relaxed"](data__namespacepackages_item): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + " must be python-module-name-relaxed", value=data__namespacepackages_item, name="" + (name_prefix or "data") + ".namespace-packages[{data__namespacepackages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format') if "py-modules" in data_keys: data_keys.remove("py-modules") data__pymodules = data["py-modules"] if not isinstance(data__pymodules, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules must be array", value=data__pymodules, name="" + (name_prefix or "data") + ".py-modules", definition={'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules must be array", value=data__pymodules, name="" + (name_prefix or "data") + ".py-modules", definition={'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, rule='type') data__pymodules_is_list = isinstance(data__pymodules, (list, tuple)) if data__pymodules_is_list: data__pymodules_len = len(data__pymodules) for data__pymodules_x, data__pymodules_item in enumerate(data__pymodules): if not isinstance(data__pymodules_item, (str)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + " must be string", value=data__pymodules_item, name="" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + " must be string", value=data__pymodules_item, name="" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='type') if isinstance(data__pymodules_item, str): - if not custom_formats["python-module-name"](data__pymodules_item): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + " must be python-module-name", value=data__pymodules_item, name="" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') + if not custom_formats["python-module-name-relaxed"](data__pymodules_item): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + " must be python-module-name-relaxed", value=data__pymodules_item, name="" + (name_prefix or "data") + ".py-modules[{data__pymodules_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format') + if "ext-modules" in data_keys: + data_keys.remove("ext-modules") + data__extmodules = data["ext-modules"] + if not isinstance(data__extmodules, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".ext-modules must be array", value=data__extmodules, name="" + (name_prefix or "data") + ".ext-modules", definition={'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}}, rule='type') + data__extmodules_is_list = isinstance(data__extmodules, (list, tuple)) + if data__extmodules_is_list: + data__extmodules_len = len(data__extmodules) + for data__extmodules_x, data__extmodules_item in enumerate(data__extmodules): + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_ext_module(data__extmodules_item, custom_formats, (name_prefix or "data") + ".ext-modules[{data__extmodules_x}]".format(**locals())) if "data-files" in data_keys: data_keys.remove("data-files") data__datafiles = data["data-files"] @@ -524,7 +534,7 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm if data__dynamic_keys: raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must not contain "+str(data__dynamic_keys)+" properties", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}, rule='additionalProperties') if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_.']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='additionalProperties') return data def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_properties_file(data, custom_formats={}, name_prefix=None): @@ -613,6 +623,211 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, rule='additionalProperties') return data +def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_ext_module(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, rule='type') + data_is_dict = isinstance(data, dict) + if data_is_dict: + data__missing_keys = set(['name', 'sources']) - data.keys() + if data__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, rule='required') + data_keys = set(data.keys()) + if "name" in data_keys: + data_keys.remove("name") + data__name = data["name"] + if not isinstance(data__name, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be string", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='type') + if isinstance(data__name, str): + if not custom_formats["python-module-name-relaxed"](data__name): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".name must be python-module-name-relaxed", value=data__name, name="" + (name_prefix or "data") + ".name", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format') + if "sources" in data_keys: + data_keys.remove("sources") + data__sources = data["sources"] + if not isinstance(data__sources, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".sources must be array", value=data__sources, name="" + (name_prefix or "data") + ".sources", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__sources_is_list = isinstance(data__sources, (list, tuple)) + if data__sources_is_list: + data__sources_len = len(data__sources) + for data__sources_x, data__sources_item in enumerate(data__sources): + if not isinstance(data__sources_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".sources[{data__sources_x}]".format(**locals()) + " must be string", value=data__sources_item, name="" + (name_prefix or "data") + ".sources[{data__sources_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "include-dirs" in data_keys: + data_keys.remove("include-dirs") + data__includedirs = data["include-dirs"] + if not isinstance(data__includedirs, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".include-dirs must be array", value=data__includedirs, name="" + (name_prefix or "data") + ".include-dirs", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__includedirs_is_list = isinstance(data__includedirs, (list, tuple)) + if data__includedirs_is_list: + data__includedirs_len = len(data__includedirs) + for data__includedirs_x, data__includedirs_item in enumerate(data__includedirs): + if not isinstance(data__includedirs_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".include-dirs[{data__includedirs_x}]".format(**locals()) + " must be string", value=data__includedirs_item, name="" + (name_prefix or "data") + ".include-dirs[{data__includedirs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "define-macros" in data_keys: + data_keys.remove("define-macros") + data__definemacros = data["define-macros"] + if not isinstance(data__definemacros, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros must be array", value=data__definemacros, name="" + (name_prefix or "data") + ".define-macros", definition={'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, rule='type') + data__definemacros_is_list = isinstance(data__definemacros, (list, tuple)) + if data__definemacros_is_list: + data__definemacros_len = len(data__definemacros) + for data__definemacros_x, data__definemacros_item in enumerate(data__definemacros): + if not isinstance(data__definemacros_item, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + " must be array", value=data__definemacros_item, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + "", definition={'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}, rule='type') + data__definemacros_item_is_list = isinstance(data__definemacros_item, (list, tuple)) + if data__definemacros_item_is_list: + data__definemacros_item_len = len(data__definemacros_item) + if data__definemacros_item_len > 0: + data__definemacros_item__0 = data__definemacros_item[0] + if not isinstance(data__definemacros_item__0, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][0]".format(**locals()) + " must be string", value=data__definemacros_item__0, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][0]".format(**locals()) + "", definition={'description': 'macro name', 'type': 'string'}, rule='type') + if data__definemacros_item_len > 1: + data__definemacros_item__1 = data__definemacros_item[1] + data__definemacros_item__1_one_of_count9 = 0 + if data__definemacros_item__1_one_of_count9 < 2: + try: + if not isinstance(data__definemacros_item__1, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be string", value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + data__definemacros_item__1_one_of_count9 += 1 + except JsonSchemaValueException: pass + if data__definemacros_item__1_one_of_count9 < 2: + try: + if not isinstance(data__definemacros_item__1, (NoneType)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be null", value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'type': 'null'}, rule='type') + data__definemacros_item__1_one_of_count9 += 1 + except JsonSchemaValueException: pass + if data__definemacros_item__1_one_of_count9 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be valid exactly by one definition" + (" (" + str(data__definemacros_item__1_one_of_count9) + " matches found)"), value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}, rule='oneOf') + if data__definemacros_item_len > 2: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + " must contain only specified items", value=data__definemacros_item, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + "", definition={'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}, rule='items') + if "undef-macros" in data_keys: + data_keys.remove("undef-macros") + data__undefmacros = data["undef-macros"] + if not isinstance(data__undefmacros, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".undef-macros must be array", value=data__undefmacros, name="" + (name_prefix or "data") + ".undef-macros", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__undefmacros_is_list = isinstance(data__undefmacros, (list, tuple)) + if data__undefmacros_is_list: + data__undefmacros_len = len(data__undefmacros) + for data__undefmacros_x, data__undefmacros_item in enumerate(data__undefmacros): + if not isinstance(data__undefmacros_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".undef-macros[{data__undefmacros_x}]".format(**locals()) + " must be string", value=data__undefmacros_item, name="" + (name_prefix or "data") + ".undef-macros[{data__undefmacros_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "library-dirs" in data_keys: + data_keys.remove("library-dirs") + data__librarydirs = data["library-dirs"] + if not isinstance(data__librarydirs, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".library-dirs must be array", value=data__librarydirs, name="" + (name_prefix or "data") + ".library-dirs", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__librarydirs_is_list = isinstance(data__librarydirs, (list, tuple)) + if data__librarydirs_is_list: + data__librarydirs_len = len(data__librarydirs) + for data__librarydirs_x, data__librarydirs_item in enumerate(data__librarydirs): + if not isinstance(data__librarydirs_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".library-dirs[{data__librarydirs_x}]".format(**locals()) + " must be string", value=data__librarydirs_item, name="" + (name_prefix or "data") + ".library-dirs[{data__librarydirs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "libraries" in data_keys: + data_keys.remove("libraries") + data__libraries = data["libraries"] + if not isinstance(data__libraries, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".libraries must be array", value=data__libraries, name="" + (name_prefix or "data") + ".libraries", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__libraries_is_list = isinstance(data__libraries, (list, tuple)) + if data__libraries_is_list: + data__libraries_len = len(data__libraries) + for data__libraries_x, data__libraries_item in enumerate(data__libraries): + if not isinstance(data__libraries_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".libraries[{data__libraries_x}]".format(**locals()) + " must be string", value=data__libraries_item, name="" + (name_prefix or "data") + ".libraries[{data__libraries_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "runtime-library-dirs" in data_keys: + data_keys.remove("runtime-library-dirs") + data__runtimelibrarydirs = data["runtime-library-dirs"] + if not isinstance(data__runtimelibrarydirs, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".runtime-library-dirs must be array", value=data__runtimelibrarydirs, name="" + (name_prefix or "data") + ".runtime-library-dirs", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__runtimelibrarydirs_is_list = isinstance(data__runtimelibrarydirs, (list, tuple)) + if data__runtimelibrarydirs_is_list: + data__runtimelibrarydirs_len = len(data__runtimelibrarydirs) + for data__runtimelibrarydirs_x, data__runtimelibrarydirs_item in enumerate(data__runtimelibrarydirs): + if not isinstance(data__runtimelibrarydirs_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".runtime-library-dirs[{data__runtimelibrarydirs_x}]".format(**locals()) + " must be string", value=data__runtimelibrarydirs_item, name="" + (name_prefix or "data") + ".runtime-library-dirs[{data__runtimelibrarydirs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "extra-objects" in data_keys: + data_keys.remove("extra-objects") + data__extraobjects = data["extra-objects"] + if not isinstance(data__extraobjects, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-objects must be array", value=data__extraobjects, name="" + (name_prefix or "data") + ".extra-objects", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__extraobjects_is_list = isinstance(data__extraobjects, (list, tuple)) + if data__extraobjects_is_list: + data__extraobjects_len = len(data__extraobjects) + for data__extraobjects_x, data__extraobjects_item in enumerate(data__extraobjects): + if not isinstance(data__extraobjects_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-objects[{data__extraobjects_x}]".format(**locals()) + " must be string", value=data__extraobjects_item, name="" + (name_prefix or "data") + ".extra-objects[{data__extraobjects_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "extra-compile-args" in data_keys: + data_keys.remove("extra-compile-args") + data__extracompileargs = data["extra-compile-args"] + if not isinstance(data__extracompileargs, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-compile-args must be array", value=data__extracompileargs, name="" + (name_prefix or "data") + ".extra-compile-args", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__extracompileargs_is_list = isinstance(data__extracompileargs, (list, tuple)) + if data__extracompileargs_is_list: + data__extracompileargs_len = len(data__extracompileargs) + for data__extracompileargs_x, data__extracompileargs_item in enumerate(data__extracompileargs): + if not isinstance(data__extracompileargs_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-compile-args[{data__extracompileargs_x}]".format(**locals()) + " must be string", value=data__extracompileargs_item, name="" + (name_prefix or "data") + ".extra-compile-args[{data__extracompileargs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "extra-link-args" in data_keys: + data_keys.remove("extra-link-args") + data__extralinkargs = data["extra-link-args"] + if not isinstance(data__extralinkargs, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-link-args must be array", value=data__extralinkargs, name="" + (name_prefix or "data") + ".extra-link-args", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__extralinkargs_is_list = isinstance(data__extralinkargs, (list, tuple)) + if data__extralinkargs_is_list: + data__extralinkargs_len = len(data__extralinkargs) + for data__extralinkargs_x, data__extralinkargs_item in enumerate(data__extralinkargs): + if not isinstance(data__extralinkargs_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".extra-link-args[{data__extralinkargs_x}]".format(**locals()) + " must be string", value=data__extralinkargs_item, name="" + (name_prefix or "data") + ".extra-link-args[{data__extralinkargs_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "export-symbols" in data_keys: + data_keys.remove("export-symbols") + data__exportsymbols = data["export-symbols"] + if not isinstance(data__exportsymbols, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".export-symbols must be array", value=data__exportsymbols, name="" + (name_prefix or "data") + ".export-symbols", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__exportsymbols_is_list = isinstance(data__exportsymbols, (list, tuple)) + if data__exportsymbols_is_list: + data__exportsymbols_len = len(data__exportsymbols) + for data__exportsymbols_x, data__exportsymbols_item in enumerate(data__exportsymbols): + if not isinstance(data__exportsymbols_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".export-symbols[{data__exportsymbols_x}]".format(**locals()) + " must be string", value=data__exportsymbols_item, name="" + (name_prefix or "data") + ".export-symbols[{data__exportsymbols_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "swig-opts" in data_keys: + data_keys.remove("swig-opts") + data__swigopts = data["swig-opts"] + if not isinstance(data__swigopts, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".swig-opts must be array", value=data__swigopts, name="" + (name_prefix or "data") + ".swig-opts", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__swigopts_is_list = isinstance(data__swigopts, (list, tuple)) + if data__swigopts_is_list: + data__swigopts_len = len(data__swigopts) + for data__swigopts_x, data__swigopts_item in enumerate(data__swigopts): + if not isinstance(data__swigopts_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".swig-opts[{data__swigopts_x}]".format(**locals()) + " must be string", value=data__swigopts_item, name="" + (name_prefix or "data") + ".swig-opts[{data__swigopts_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "depends" in data_keys: + data_keys.remove("depends") + data__depends = data["depends"] + if not isinstance(data__depends, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".depends must be array", value=data__depends, name="" + (name_prefix or "data") + ".depends", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data__depends_is_list = isinstance(data__depends, (list, tuple)) + if data__depends_is_list: + data__depends_len = len(data__depends) + for data__depends_x, data__depends_item in enumerate(data__depends): + if not isinstance(data__depends_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".depends[{data__depends_x}]".format(**locals()) + " must be string", value=data__depends_item, name="" + (name_prefix or "data") + ".depends[{data__depends_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + if "language" in data_keys: + data_keys.remove("language") + data__language = data["language"] + if not isinstance(data__language, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".language must be string", value=data__language, name="" + (name_prefix or "data") + ".language", definition={'type': 'string'}, rule='type') + if "optional" in data_keys: + data_keys.remove("optional") + data__optional = data["optional"] + if not isinstance(data__optional, (bool)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional must be boolean", value=data__optional, name="" + (name_prefix or "data") + ".optional", definition={'type': 'boolean'}, rule='type') + if "py-limited-api" in data_keys: + data_keys.remove("py-limited-api") + data__pylimitedapi = data["py-limited-api"] + if not isinstance(data__pylimitedapi, (bool)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".py-limited-api must be boolean", value=data__pylimitedapi, name="" + (name_prefix or "data") + ".py-limited-api", definition={'type': 'boolean'}, rule='type') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, rule='additionalProperties') + return data + def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_find_directive(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}, rule='type') @@ -673,28 +888,28 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data, custom_formats={}, name_prefix=None): if not isinstance(data, (str)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='type') - data_any_of_count9 = 0 - if not data_any_of_count9: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='type') + data_any_of_count10 = 0 + if not data_any_of_count10: try: if not isinstance(data, (str)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='type') if isinstance(data, str): - if not custom_formats["python-module-name"](data): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-module-name", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') - data_any_of_count9 += 1 + if not custom_formats["python-module-name-relaxed"](data): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-module-name-relaxed", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format') + data_any_of_count10 += 1 except JsonSchemaValueException: pass - if not data_any_of_count9: + if not data_any_of_count10: try: if not isinstance(data, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'pep561-stub-name'}, rule='type') if isinstance(data, str): if not custom_formats["pep561-stub-name"](data): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep561-stub-name", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'pep561-stub-name'}, rule='format') - data_any_of_count9 += 1 + data_any_of_count10 += 1 except JsonSchemaValueException: pass - if not data_any_of_count9: - raise JsonSchemaValueException("" + (name_prefix or "data") + " cannot be validated by any definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='anyOf') + if not data_any_of_count10: + raise JsonSchemaValueException("" + (name_prefix or "data") + " cannot be validated by any definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='anyOf') return data def validate_https___setuptools_pypa_io_en_latest_deprecated_distutils_configfile_html(data, custom_formats={}, name_prefix=None): @@ -749,19 +964,19 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom if "readme" in data_keys: data_keys.remove("readme") data__readme = data["readme"] - data__readme_one_of_count10 = 0 - if data__readme_one_of_count10 < 2: + data__readme_one_of_count11 = 0 + if data__readme_one_of_count11 < 2: try: if not isinstance(data__readme, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be string", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, rule='type') - data__readme_one_of_count10 += 1 + data__readme_one_of_count11 += 1 except JsonSchemaValueException: pass - if data__readme_one_of_count10 < 2: + if data__readme_one_of_count11 < 2: try: if not isinstance(data__readme, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be object", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}, rule='type') - data__readme_any_of_count11 = 0 - if not data__readme_any_of_count11: + data__readme_any_of_count12 = 0 + if not data__readme_any_of_count12: try: data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: @@ -774,9 +989,9 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom data__readme__file = data__readme["file"] if not isinstance(data__readme__file, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.file must be string", value=data__readme__file, name="" + (name_prefix or "data") + ".readme.file", definition={'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}, rule='type') - data__readme_any_of_count11 += 1 + data__readme_any_of_count12 += 1 except JsonSchemaValueException: pass - if not data__readme_any_of_count11: + if not data__readme_any_of_count12: try: data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: @@ -789,9 +1004,9 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom data__readme__text = data__readme["text"] if not isinstance(data__readme__text, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.text must be string", value=data__readme__text, name="" + (name_prefix or "data") + ".readme.text", definition={'type': 'string', 'description': 'Full text describing the project.'}, rule='type') - data__readme_any_of_count11 += 1 + data__readme_any_of_count12 += 1 except JsonSchemaValueException: pass - if not data__readme_any_of_count11: + if not data__readme_any_of_count12: raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme cannot be validated by any definition", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, rule='anyOf') data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: @@ -804,10 +1019,10 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom data__readme__contenttype = data__readme["content-type"] if not isinstance(data__readme__contenttype, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.content-type must be string", value=data__readme__contenttype, name="" + (name_prefix or "data") + ".readme.content-type", definition={'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}, rule='type') - data__readme_one_of_count10 += 1 + data__readme_one_of_count11 += 1 except JsonSchemaValueException: pass - if data__readme_one_of_count10 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count10) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, rule='oneOf') + if data__readme_one_of_count11 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count11) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#description>`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, rule='oneOf') if "requires-python" in data_keys: data_keys.remove("requires-python") data__requirespython = data["requires-python"] @@ -819,8 +1034,8 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom if "license" in data_keys: data_keys.remove("license") data__license = data["license"] - data__license_one_of_count12 = 0 - if data__license_one_of_count12 < 2: + data__license_one_of_count13 = 0 + if data__license_one_of_count13 < 2: try: data__license_is_dict = isinstance(data__license, dict) if data__license_is_dict: @@ -833,9 +1048,9 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom data__license__file = data__license["file"] if not isinstance(data__license__file, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.file must be string", value=data__license__file, name="" + (name_prefix or "data") + ".license.file", definition={'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}, rule='type') - data__license_one_of_count12 += 1 + data__license_one_of_count13 += 1 except JsonSchemaValueException: pass - if data__license_one_of_count12 < 2: + if data__license_one_of_count13 < 2: try: data__license_is_dict = isinstance(data__license, dict) if data__license_is_dict: @@ -848,10 +1063,10 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom data__license__text = data__license["text"] if not isinstance(data__license__text, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.text must be string", value=data__license__text, name="" + (name_prefix or "data") + ".license.text", definition={'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}, rule='type') - data__license_one_of_count12 += 1 + data__license_one_of_count13 += 1 except JsonSchemaValueException: pass - if data__license_one_of_count12 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count12) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, rule='oneOf') + if data__license_one_of_count13 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count13) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, rule='oneOf') if "authors" in data_keys: data_keys.remove("authors") data__authors = data["authors"] diff --git a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py index aacf4092b0..153b1f0b27 100644 --- a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py +++ b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py @@ -83,7 +83,9 @@ try: from packaging import requirements as _req except ImportError: # pragma: no cover # let's try setuptools vendored version - from setuptools._vendor.packaging import requirements as _req # type: ignore + from setuptools._vendor.packaging import ( # type: ignore[no-redef] + requirements as _req, + ) def pep508(value: str) -> bool: """See :ref:`PyPA's dependency specifiers <pypa:dependency-specifiers>` @@ -91,9 +93,9 @@ try: """ try: _req.Requirement(value) + return True except _req.InvalidRequirement: return False - return True except ImportError: # pragma: no cover _logger.warning( @@ -289,6 +291,25 @@ def python_module_name(value: str) -> bool: return python_qualified_identifier(value) +def python_module_name_relaxed(value: str) -> bool: + """Similar to :obj:`python_module_name`, but relaxed to also accept + dash characters (``-``) and cover special cases like ``pip-run``. + + It is recommended, however, that beginners avoid dash characters, + as they require advanced knowledge about Python internals. + + The following are disallowed: + + * names starting/ending in dashes, + * names ending in ``-stubs`` (potentially collide with :obj:`pep561_stub_name`). + """ + if value.startswith("-") or value.endswith("-"): + return False + if value.endswith("-stubs"): + return False # Avoid collision with PEP 561 + return python_module_name(value.replace("-", "_")) + + def python_entrypoint_group(value: str) -> bool: """See ``Data model > group`` in the :ref:`PyPA's entry-points specification <pypa:entry-points>`. diff --git a/contrib/python/setuptools/py3/setuptools/config/distutils.schema.json b/contrib/python/setuptools/py3/setuptools/config/distutils.schema.json new file mode 100644 index 0000000000..93cd2e868a --- /dev/null +++ b/contrib/python/setuptools/py3/setuptools/config/distutils.schema.json @@ -0,0 +1,26 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "$id": "https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html", + "title": "``tool.distutils`` table", + "$$description": [ + "**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``", + "subtables to configure arguments for ``distutils`` commands.", + "Originally, ``distutils`` allowed developers to configure arguments for", + "``setup.py`` commands via `distutils configuration files", + "<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.", + "See also `the old Python docs <https://docs.python.org/3.11/install/>_`." + ], + + "type": "object", + "properties": { + "global": { + "type": "object", + "description": "Global options applied to all ``distutils`` commands" + } + }, + "patternProperties": { + ".+": {"type": "object"} + }, + "$comment": "TODO: Is there a practical way of making this schema more specific?" +} diff --git a/contrib/python/setuptools/py3/setuptools/config/expand.py b/contrib/python/setuptools/py3/setuptools/config/expand.py index de6339fa42..e11bcf9b42 100644 --- a/contrib/python/setuptools/py3/setuptools/config/expand.py +++ b/contrib/python/setuptools/py3/setuptools/config/expand.py @@ -25,29 +25,23 @@ import importlib import os import pathlib import sys -from glob import iglob from configparser import ConfigParser +from glob import iglob from importlib.machinery import ModuleSpec, all_suffixes from itertools import chain -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Iterable, - Iterator, - Mapping, - TypeVar, -) from pathlib import Path -from types import ModuleType - -from distutils.errors import DistutilsOptionError +from types import ModuleType, TracebackType +from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Mapping, TypeVar -from .._path import same_path as _same_path, StrPath +from .._path import StrPath, same_path as _same_path from ..discovery import find_package_path from ..warnings import SetuptoolsWarning +from distutils.errors import DistutilsOptionError + if TYPE_CHECKING: + from typing_extensions import Self + from setuptools.dist import Distribution _K = TypeVar("_K") @@ -69,7 +63,7 @@ class StaticModule: elif isinstance(statement, ast.AnnAssign) and statement.value: yield (statement.target, statement.value) - def __getattr__(self, attr): + def __getattr__(self, attr: str): """Attempt to load an attribute "statically", via :func:`ast.literal_eval`.""" try: return next( @@ -287,8 +281,9 @@ def find_packages( :rtype: list """ + from more_itertools import always_iterable, unique_everseen + from setuptools.discovery import construct_package_dir - from more_itertools import unique_everseen, always_iterable if namespaces: from setuptools.discovery import PEP420PackageFinder as PackageFinder @@ -392,10 +387,15 @@ class EnsurePackagesDiscovered: self._called = True self._dist.set_defaults(name=False) # Skip name, we can still be parsing - def __enter__(self): + def __enter__(self) -> Self: return self - def __exit__(self, _exc_type, _exc_value, _traceback): + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: if self._called: self._dist.set_defaults.analyse_name() # Now we can set a default name diff --git a/contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py b/contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py index a83e43bb35..5d95e18b83 100644 --- a/contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py +++ b/contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py @@ -15,19 +15,20 @@ import logging import os from contextlib import contextmanager from functools import partial +from types import TracebackType from typing import TYPE_CHECKING, Any, Callable, Mapping from .._path import StrPath from ..errors import FileError, InvalidConfigError from ..warnings import SetuptoolsWarning from . import expand as _expand -from ._apply_pyprojecttoml import _PREVIOUSLY_DEFINED, _MissingDynamic -from ._apply_pyprojecttoml import apply as _apply +from ._apply_pyprojecttoml import _PREVIOUSLY_DEFINED, _MissingDynamic, apply as _apply if TYPE_CHECKING: - from setuptools.dist import Distribution from typing_extensions import Self + from setuptools.dist import Distribution + _logger = logging.getLogger(__name__) @@ -121,7 +122,7 @@ def read_configuration( # the default would be an improvement. # `ini2toml` backfills include_package_data=False when nothing is explicitly given, # therefore setting a default here is backwards compatible. - if dist and getattr(dist, "include_package_data", None) is not None: + if dist and dist.include_package_data is not None: setuptools_table.setdefault("include-package-data", dist.include_package_data) else: setuptools_table.setdefault("include-package-data", True) @@ -129,6 +130,9 @@ def read_configuration( asdict["tool"] = tool_table tool_table["setuptools"] = setuptools_table + if "ext-modules" in setuptools_table: + _ExperimentalConfiguration.emit(subject="[tool.setuptools.ext-modules]") + with _ignore_errors(ignore_option_errors): # Don't complain about unrelated errors (e.g. tools not using the "tool" table) subset = {"project": project_table, "tool": {"setuptools": setuptools_table}} @@ -303,7 +307,10 @@ class _ConfigExpander: def _obtain_version(self, dist: Distribution, package_dir: Mapping[str, str]): # Since plugins can set version, let's silently skip if it cannot be obtained if "version" in self.dynamic and "version" in self.dynamic_cfg: - return _expand.version(self._obtain(dist, "version", package_dir)) + return _expand.version( + # We already do an early check for the presence of "version" + self._obtain(dist, "version", package_dir) # pyright: ignore[reportArgumentType] + ) return None def _obtain_readme(self, dist: Distribution) -> dict[str, str] | None: @@ -313,9 +320,10 @@ class _ConfigExpander: dynamic_cfg = self.dynamic_cfg if "readme" in dynamic_cfg: return { + # We already do an early check for the presence of "readme" "text": self._obtain(dist, "readme", {}), "content-type": dynamic_cfg["readme"].get("content-type", "text/x-rst"), - } + } # pyright: ignore[reportReturnType] self._ensure_previously_set(dist, "readme") return None @@ -430,7 +438,12 @@ class _EnsurePackagesDiscovered(_expand.EnsurePackagesDiscovered): return super().__enter__() - def __exit__(self, exc_type, exc_value, traceback): + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: """When exiting the context, if values of ``packages``, ``py_modules`` and ``package_dir`` are missing in ``setuptools_cfg``, copy from ``dist``. """ diff --git a/contrib/python/setuptools/py3/setuptools/config/setupcfg.py b/contrib/python/setuptools/py3/setuptools/config/setupcfg.py index 772b8d00e0..54469f74a3 100644 --- a/contrib/python/setuptools/py3/setuptools/config/setupcfg.py +++ b/contrib/python/setuptools/py3/setuptools/config/setupcfg.py @@ -15,35 +15,37 @@ import contextlib import functools import os from collections import defaultdict -from functools import partial -from functools import wraps +from functools import partial, wraps from typing import ( TYPE_CHECKING, - Callable, Any, + Callable, Dict, Generic, Iterable, Iterator, + List, Tuple, TypeVar, Union, + cast, ) -from .._path import StrPath -from ..errors import FileError, OptionError from packaging.markers import default_environment as marker_env from packaging.requirements import InvalidRequirement, Requirement from packaging.specifiers import SpecifierSet from packaging.version import InvalidVersion, Version + +from .._path import StrPath +from ..errors import FileError, OptionError from ..warnings import SetuptoolsDeprecationWarning from . import expand if TYPE_CHECKING: - from distutils.dist import DistributionMetadata - from setuptools.dist import Distribution + from distutils.dist import DistributionMetadata + SingleCommandOptions = Dict["str", Tuple["str", Any]] """Dict that associate the name of the options of a particular command to a tuple. The first element of the tuple indicates the origin of the option value @@ -108,7 +110,8 @@ def _apply( filenames = [*other_files, filepath] try: - _Distribution.parse_config_files(dist, filenames=filenames) # type: ignore[arg-type] # TODO: fix in distutils stubs + # TODO: Temporary cast until mypy 1.12 is released with upstream fixes from typeshed + _Distribution.parse_config_files(dist, filenames=cast(List[str], filenames)) handlers = parse_configuration( dist, dist.command_options, ignore_option_errors=ignore_option_errors ) @@ -277,7 +280,7 @@ class ConfigHandler(Generic[Target]): '%s must provide .parsers property' % self.__class__.__name__ ) - def __setitem__(self, option_name, value): + def __setitem__(self, option_name, value) -> None: target_obj = self.target_obj # Translate alias into real name. @@ -649,7 +652,6 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]): self._parse_requirements_list, "install_requires" ), 'setup_requires': self._parse_list_semicolon, - 'tests_require': self._parse_list_semicolon, 'packages': self._parse_packages, 'entry_points': self._parse_file_in_root, 'py_modules': parse_list, diff --git a/contrib/python/setuptools/py3/setuptools/config/setuptools.schema.json b/contrib/python/setuptools/py3/setuptools/config/setuptools.schema.json new file mode 100644 index 0000000000..ec887b3573 --- /dev/null +++ b/contrib/python/setuptools/py3/setuptools/config/setuptools.schema.json @@ -0,0 +1,433 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "$id": "https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html", + "title": "``tool.setuptools`` table", + "$$description": [ + "``setuptools``-specific configurations that can be set by users that require", + "customization.", + "These configurations are completely optional and probably can be skipped when", + "creating simple packages. They are equivalent to some of the `Keywords", + "<https://setuptools.pypa.io/en/latest/references/keywords.html>`_", + "used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.", + "It considers only ``setuptools`` `parameters", + "<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_", + "that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``", + "and ``setup_requires`` (incompatible with modern workflows/standards)." + ], + + "type": "object", + "additionalProperties": false, + "properties": { + "platforms": { + "type": "array", + "items": {"type": "string"} + }, + "provides": { + "$$description": [ + "Package and virtual package names contained within this package", + "**(not supported by pip)**" + ], + "type": "array", + "items": {"type": "string", "format": "pep508-identifier"} + }, + "obsoletes": { + "$$description": [ + "Packages which this package renders obsolete", + "**(not supported by pip)**" + ], + "type": "array", + "items": {"type": "string", "format": "pep508-identifier"} + }, + "zip-safe": { + "$$description": [ + "Whether the project can be safely installed and run from a zip file.", + "**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and", + "``setup.py install`` in the context of ``eggs`` (**DEPRECATED**)." + ], + "type": "boolean" + }, + "script-files": { + "$$description": [ + "Legacy way of defining scripts (entry-points are preferred).", + "Equivalent to the ``script`` keyword in ``setup.py``", + "(it was renamed to avoid confusion with entry-point based ``project.scripts``", + "defined in :pep:`621`).", + "**DISCOURAGED**: generic script wrappers are tricky and may not work properly.", + "Whenever possible, please use ``project.scripts`` instead." + ], + "type": "array", + "items": {"type": "string"}, + "$comment": "TODO: is this field deprecated/should be removed?" + }, + "eager-resources": { + "$$description": [ + "Resources that should be extracted together, if any of them is needed,", + "or if any C extensions included in the project are imported.", + "**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and", + "``setup.py install`` in the context of ``eggs`` (**DEPRECATED**)." + ], + "type": "array", + "items": {"type": "string"} + }, + "packages": { + "$$description": [ + "Packages that should be included in the distribution.", + "It can be given either as a list of package identifiers", + "or as a ``dict``-like structure with a single key ``find``", + "which corresponds to a dynamic call to", + "``setuptools.config.expand.find_packages`` function.", + "The ``find`` key is associated with a nested ``dict``-like structure that can", + "contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,", + "mimicking the keyword arguments of the associated function." + ], + "oneOf": [ + { + "title": "Array of Python package identifiers", + "type": "array", + "items": {"$ref": "#/definitions/package-name"} + }, + {"$ref": "#/definitions/find-directive"} + ] + }, + "package-dir": { + "$$description": [ + ":class:`dict`-like structure mapping from package names to directories where their", + "code can be found.", + "The empty string (as key) means that all packages are contained inside", + "the given directory will be included in the distribution." + ], + "type": "object", + "additionalProperties": false, + "propertyNames": { + "anyOf": [{"const": ""}, {"$ref": "#/definitions/package-name"}] + }, + "patternProperties": { + "^.*$": {"type": "string" } + } + }, + "package-data": { + "$$description": [ + "Mapping from package names to lists of glob patterns.", + "Usually this option is not needed when using ``include-package-data = true``", + "For more information on how to include data files, check ``setuptools`` `docs", + "<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_." + ], + "type": "object", + "additionalProperties": false, + "propertyNames": { + "anyOf": [{"type": "string", "format": "python-module-name"}, {"const": "*"}] + }, + "patternProperties": { + "^.*$": {"type": "array", "items": {"type": "string"}} + } + }, + "include-package-data": { + "$$description": [ + "Automatically include any data files inside the package directories", + "that are specified by ``MANIFEST.in``", + "For more information on how to include data files, check ``setuptools`` `docs", + "<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_." + ], + "type": "boolean" + }, + "exclude-package-data": { + "$$description": [ + "Mapping from package names to lists of glob patterns that should be excluded", + "For more information on how to include data files, check ``setuptools`` `docs", + "<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_." + ], + "type": "object", + "additionalProperties": false, + "propertyNames": { + "anyOf": [{"type": "string", "format": "python-module-name"}, {"const": "*"}] + }, + "patternProperties": { + "^.*$": {"type": "array", "items": {"type": "string"}} + } + }, + "namespace-packages": { + "type": "array", + "items": {"type": "string", "format": "python-module-name-relaxed"}, + "$comment": "https://setuptools.pypa.io/en/latest/userguide/package_discovery.html", + "description": "**DEPRECATED**: use implicit namespaces instead (:pep:`420`)." + }, + "py-modules": { + "description": "Modules that setuptools will manipulate", + "type": "array", + "items": {"type": "string", "format": "python-module-name-relaxed"}, + "$comment": "TODO: clarify the relationship with ``packages``" + }, + "ext-modules": { + "description": "Extension modules to be compiled by setuptools", + "type": "array", + "items": {"$ref": "#/definitions/ext-module"} + }, + "data-files": { + "$$description": [ + "``dict``-like structure where each key represents a directory and", + "the value is a list of glob patterns that should be installed in them.", + "**DISCOURAGED**: please notice this might not work as expected with wheels.", + "Whenever possible, consider using data files inside the package directories", + "(or create a new namespace package that only contains data files).", + "See `data files support", + "<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_." + ], + "type": "object", + "patternProperties": { + "^.*$": {"type": "array", "items": {"type": "string"}} + } + }, + "cmdclass": { + "$$description": [ + "Mapping of distutils-style command names to ``setuptools.Command`` subclasses", + "which in turn should be represented by strings with a qualified class name", + "(i.e., \"dotted\" form with module), e.g.::\n\n", + " cmdclass = {mycmd = \"pkg.subpkg.module.CommandClass\"}\n\n", + "The command class should be a directly defined at the top-level of the", + "containing module (no class nesting)." + ], + "type": "object", + "patternProperties": { + "^.*$": {"type": "string", "format": "python-qualified-identifier"} + } + }, + "license-files": { + "type": "array", + "items": {"type": "string"}, + "$$description": [ + "**PROVISIONAL**: list of glob patterns for all license files being distributed.", + "(likely to become standard with :pep:`639`).", + "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``" + ], + "$comment": "TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?" + }, + "dynamic": { + "type": "object", + "description": "Instructions for loading :pep:`621`-related metadata dynamically", + "additionalProperties": false, + "properties": { + "version": { + "$$description": [ + "A version dynamically loaded via either the ``attr:`` or ``file:``", + "directives. Please make sure the given file or attribute respects :pep:`440`.", + "Also ensure to set ``project.dynamic`` accordingly." + ], + "oneOf": [ + {"$ref": "#/definitions/attr-directive"}, + {"$ref": "#/definitions/file-directive"} + ] + }, + "classifiers": {"$ref": "#/definitions/file-directive"}, + "description": {"$ref": "#/definitions/file-directive"}, + "entry-points": {"$ref": "#/definitions/file-directive"}, + "dependencies": {"$ref": "#/definitions/file-directive-for-dependencies"}, + "optional-dependencies": { + "type": "object", + "propertyNames": {"type": "string", "format": "pep508-identifier"}, + "additionalProperties": false, + "patternProperties": { + ".+": {"$ref": "#/definitions/file-directive-for-dependencies"} + } + }, + "readme": { + "type": "object", + "anyOf": [ + {"$ref": "#/definitions/file-directive"}, + { + "type": "object", + "properties": { + "content-type": {"type": "string"}, + "file": { "$ref": "#/definitions/file-directive/properties/file" } + }, + "additionalProperties": false} + ], + "required": ["file"] + } + } + } + }, + + "definitions": { + "package-name": { + "$id": "#/definitions/package-name", + "title": "Valid package name", + "description": "Valid package name (importable or :pep:`561`).", + "type": "string", + "anyOf": [ + {"type": "string", "format": "python-module-name-relaxed"}, + {"type": "string", "format": "pep561-stub-name"} + ] + }, + "ext-module": { + "$id": "#/definitions/ext-module", + "title": "Extension module", + "description": "Parameters to construct a :class:`setuptools.Extension` object", + "type": "object", + "required": ["name", "sources"], + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "format": "python-module-name-relaxed" + }, + "sources": { + "type": "array", + "items": {"type": "string"} + }, + "include-dirs":{ + "type": "array", + "items": {"type": "string"} + }, + "define-macros": { + "type": "array", + "items": { + "type": "array", + "items": [ + {"description": "macro name", "type": "string"}, + {"description": "macro value", "oneOf": [{"type": "string"}, {"type": "null"}]} + ], + "additionalItems": false + } + }, + "undef-macros": { + "type": "array", + "items": {"type": "string"} + }, + "library-dirs": { + "type": "array", + "items": {"type": "string"} + }, + "libraries": { + "type": "array", + "items": {"type": "string"} + }, + "runtime-library-dirs": { + "type": "array", + "items": {"type": "string"} + }, + "extra-objects": { + "type": "array", + "items": {"type": "string"} + }, + "extra-compile-args": { + "type": "array", + "items": {"type": "string"} + }, + "extra-link-args": { + "type": "array", + "items": {"type": "string"} + }, + "export-symbols": { + "type": "array", + "items": {"type": "string"} + }, + "swig-opts": { + "type": "array", + "items": {"type": "string"} + }, + "depends": { + "type": "array", + "items": {"type": "string"} + }, + "language": {"type": "string"}, + "optional": {"type": "boolean"}, + "py-limited-api": {"type": "boolean"} + } + }, + "file-directive": { + "$id": "#/definitions/file-directive", + "title": "'file:' directive", + "description": + "Value is read from a file (or list of files and then concatenated)", + "type": "object", + "additionalProperties": false, + "properties": { + "file": { + "oneOf": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + } + }, + "required": ["file"] + }, + "file-directive-for-dependencies": { + "title": "'file:' directive for dependencies", + "allOf": [ + { + "$$description": [ + "**BETA**: subset of the ``requirements.txt`` format", + "without ``pip`` flags and options", + "(one :pep:`508`-compliant string per line,", + "lines that are blank or start with ``#`` are excluded).", + "See `dynamic metadata", + "<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#dynamic-metadata>`_." + ] + }, + {"$ref": "#/definitions/file-directive"} + ] + }, + "attr-directive": { + "title": "'attr:' directive", + "$id": "#/definitions/attr-directive", + "$$description": [ + "Value is read from a module attribute. Supports callables and iterables;", + "unsupported types are cast via ``str()``" + ], + "type": "object", + "additionalProperties": false, + "properties": { + "attr": {"type": "string", "format": "python-qualified-identifier"} + }, + "required": ["attr"] + }, + "find-directive": { + "$id": "#/definitions/find-directive", + "title": "'find:' directive", + "type": "object", + "additionalProperties": false, + "properties": { + "find": { + "type": "object", + "$$description": [ + "Dynamic `package discovery", + "<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_." + ], + "additionalProperties": false, + "properties": { + "where": { + "description": + "Directories to be searched for packages (Unix-style relative path)", + "type": "array", + "items": {"type": "string"} + }, + "exclude": { + "type": "array", + "$$description": [ + "Exclude packages that match the values listed in this field.", + "Can container shell-style wildcards (e.g. ``'pkg.*'``)" + ], + "items": {"type": "string"} + }, + "include": { + "type": "array", + "$$description": [ + "Restrict the found packages to just the ones listed in this field.", + "Can container shell-style wildcards (e.g. ``'pkg.*'``)" + ], + "items": {"type": "string"} + }, + "namespaces": { + "type": "boolean", + "$$description": [ + "When ``True``, directories without a ``__init__.py`` file will also", + "be scanned for :pep:`420`-style implicit namespaces" + ] + } + } + } + } + } + } +} diff --git a/contrib/python/setuptools/py3/setuptools/depends.py b/contrib/python/setuptools/py3/setuptools/depends.py index 871a0925ef..9398b95331 100644 --- a/contrib/python/setuptools/py3/setuptools/depends.py +++ b/contrib/python/setuptools/py3/setuptools/depends.py @@ -1,13 +1,12 @@ -import sys -import marshal import contextlib import dis +import marshal +import sys - -from . import _imp -from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE from packaging.version import Version +from . import _imp +from ._imp import PY_COMPILED, PY_FROZEN, PY_SOURCE, find_module __all__ = ['Require', 'find_module'] diff --git a/contrib/python/setuptools/py3/setuptools/discovery.py b/contrib/python/setuptools/py3/setuptools/discovery.py index 3179852c69..577be2f16b 100644 --- a/contrib/python/setuptools/py3/setuptools/discovery.py +++ b/contrib/python/setuptools/py3/setuptools/discovery.py @@ -44,16 +44,12 @@ import os from fnmatch import fnmatchcase from glob import glob from pathlib import Path -from typing import ( - TYPE_CHECKING, - Iterable, - Iterator, - Mapping, -) +from typing import TYPE_CHECKING, Iterable, Iterator, Mapping import _distutils_hack.override # noqa: F401 from ._path import StrPath + from distutils import log from distutils.util import convert_path diff --git a/contrib/python/setuptools/py3/setuptools/dist.py b/contrib/python/setuptools/py3/setuptools/dist.py index b4496ab986..68f877decd 100644 --- a/contrib/python/setuptools/py3/setuptools/dist.py +++ b/contrib/python/setuptools/py3/setuptools/dist.py @@ -10,31 +10,32 @@ from glob import iglob from pathlib import Path from typing import TYPE_CHECKING, MutableMapping -import distutils.cmd -import distutils.command -import distutils.core -import distutils.dist -import distutils.log -from distutils.debug import DEBUG -from distutils.errors import DistutilsOptionError, DistutilsSetupError -from distutils.fancy_getopt import translate_longopt -from distutils.util import strtobool - from more_itertools import partition, unique_everseen -from ordered_set import OrderedSet from packaging.markers import InvalidMarker, Marker from packaging.specifiers import InvalidSpecifier, SpecifierSet from packaging.version import Version -from . import _entry_points -from . import _reqs -from . import command as _ # noqa -- imported for side-effects +from . import ( + _entry_points, + _reqs, + command as _, # noqa: F401 # imported for side-effects +) from ._importlib import metadata -from .config import setupcfg, pyprojecttoml +from .config import pyprojecttoml, setupcfg from .discovery import ConfigDiscovery from .monkey import get_unpatched from .warnings import InformationOnly, SetuptoolsDeprecationWarning +import distutils.cmd +import distutils.command +import distutils.core +import distutils.dist +import distutils.log +from distutils.debug import DEBUG +from distutils.errors import DistutilsOptionError, DistutilsSetupError +from distutils.fancy_getopt import translate_longopt +from distutils.util import strtobool + __all__ = ['Distribution'] sequence = tuple, list @@ -55,7 +56,7 @@ def assert_string_list(dist, attr, value): try: # verify that value is a list or tuple to exclude unordered # or single-use iterables - assert isinstance(value, (list, tuple)) + assert isinstance(value, sequence) # verify that elements of value are strings assert ''.join(value) != value except (TypeError, ValueError, AttributeError, AssertionError) as e: @@ -168,11 +169,6 @@ def check_entry_points(dist, attr, value): raise DistutilsSetupError(e) from e -def check_test_suite(dist, attr, value): - if not isinstance(value, str): - raise DistutilsSetupError("test_suite must be a string") - - def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" if not isinstance(value, dict): @@ -199,8 +195,10 @@ def check_packages(dist, attr, value): if TYPE_CHECKING: + from typing_extensions import TypeAlias + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 - _Distribution = distutils.core.Distribution + _Distribution: TypeAlias = distutils.core.Distribution else: _Distribution = get_unpatched(distutils.core.Distribution) @@ -233,12 +231,6 @@ class Distribution(_Distribution): EasyInstall and requests one of your extras, the corresponding additional requirements will be installed if needed. - 'test_suite' -- the name of a test suite to run for the 'test' command. - If the user runs 'python setup.py test', the package will be installed, - and the named test suite will be run. The format is the same as - would be used on a 'unittest.py' command line. That is, it is the - dotted name of an object to import and call to generate a test suite. - 'package_data' -- a dictionary mapping package names to lists of filenames or globs to use to find data files contained in the named packages. If the dictionary has filenames or globs listed under '""' (the empty @@ -260,7 +252,7 @@ class Distribution(_Distribution): _DISTUTILS_UNSUPPORTED_METADATA = { 'long_description_content_type': lambda: None, 'project_urls': dict, - 'provides_extras': OrderedSet, + 'provides_extras': dict, # behaves like an ordered set 'license_file': lambda: None, 'license_files': lambda: None, 'install_requires': list, @@ -276,6 +268,8 @@ class Distribution(_Distribution): self.package_data: dict[str, list[str]] = {} attrs = attrs or {} self.dist_files: list[tuple[str, str, str]] = [] + self.include_package_data: bool | None = None + self.exclude_package_data: dict[str, list[str]] | None = None # Filter-out setuptools' specific options. self.src_root = attrs.pop("src_root", None) self.dependency_links = attrs.pop('dependency_links', []) @@ -356,7 +350,7 @@ class Distribution(_Distribution): # Setuptools allows a weird "<name>:<env markers> syntax for extras extra = extra.split(':')[0] if extra: - self.metadata.provides_extras.add(extra) + self.metadata.provides_extras.setdefault(extra) def _normalize_requires(self): """Make sure requirement-related attributes exist and are normalized""" diff --git a/contrib/python/setuptools/py3/setuptools/errors.py b/contrib/python/setuptools/py3/setuptools/errors.py index 67a5a1df10..90fcf7170e 100644 --- a/contrib/python/setuptools/py3/setuptools/errors.py +++ b/contrib/python/setuptools/py3/setuptools/errors.py @@ -3,30 +3,36 @@ Provides exceptions used by setuptools modules. """ +from __future__ import annotations + +from typing import TYPE_CHECKING + from distutils import errors as _distutils_errors +if TYPE_CHECKING: + from typing_extensions import TypeAlias # Re-export errors from distutils to facilitate the migration to PEP632 -ByteCompileError = _distutils_errors.DistutilsByteCompileError -CCompilerError = _distutils_errors.CCompilerError -ClassError = _distutils_errors.DistutilsClassError -CompileError = _distutils_errors.CompileError -ExecError = _distutils_errors.DistutilsExecError -FileError = _distutils_errors.DistutilsFileError -InternalError = _distutils_errors.DistutilsInternalError -LibError = _distutils_errors.LibError -LinkError = _distutils_errors.LinkError -ModuleError = _distutils_errors.DistutilsModuleError -OptionError = _distutils_errors.DistutilsOptionError -PlatformError = _distutils_errors.DistutilsPlatformError -PreprocessError = _distutils_errors.PreprocessError -SetupError = _distutils_errors.DistutilsSetupError -TemplateError = _distutils_errors.DistutilsTemplateError -UnknownFileError = _distutils_errors.UnknownFileError +ByteCompileError: TypeAlias = _distutils_errors.DistutilsByteCompileError +CCompilerError: TypeAlias = _distutils_errors.CCompilerError +ClassError: TypeAlias = _distutils_errors.DistutilsClassError +CompileError: TypeAlias = _distutils_errors.CompileError +ExecError: TypeAlias = _distutils_errors.DistutilsExecError +FileError: TypeAlias = _distutils_errors.DistutilsFileError +InternalError: TypeAlias = _distutils_errors.DistutilsInternalError +LibError: TypeAlias = _distutils_errors.LibError +LinkError: TypeAlias = _distutils_errors.LinkError +ModuleError: TypeAlias = _distutils_errors.DistutilsModuleError +OptionError: TypeAlias = _distutils_errors.DistutilsOptionError +PlatformError: TypeAlias = _distutils_errors.DistutilsPlatformError +PreprocessError: TypeAlias = _distutils_errors.PreprocessError +SetupError: TypeAlias = _distutils_errors.DistutilsSetupError +TemplateError: TypeAlias = _distutils_errors.DistutilsTemplateError +UnknownFileError: TypeAlias = _distutils_errors.UnknownFileError # The root error class in the hierarchy -BaseError = _distutils_errors.DistutilsError +BaseError: TypeAlias = _distutils_errors.DistutilsError class InvalidConfigError(OptionError): diff --git a/contrib/python/setuptools/py3/setuptools/extension.py b/contrib/python/setuptools/py3/setuptools/extension.py index 25420f42de..cbefc72508 100644 --- a/contrib/python/setuptools/py3/setuptools/extension.py +++ b/contrib/python/setuptools/py3/setuptools/extension.py @@ -1,12 +1,15 @@ -import re +from __future__ import annotations + import functools -import distutils.core -import distutils.errors -import distutils.extension +import re from typing import TYPE_CHECKING from .monkey import get_unpatched +import distutils.core +import distutils.errors +import distutils.extension + def _have_cython(): """ @@ -24,8 +27,10 @@ def _have_cython(): # for compatibility have_pyrex = _have_cython if TYPE_CHECKING: + from typing_extensions import TypeAlias + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 - _Extension = distutils.core.Extension + _Extension: TypeAlias = distutils.core.Extension else: _Extension = get_unpatched(distutils.core.Extension) @@ -122,14 +127,23 @@ class Extension(_Extension): :keyword bool py_limited_api: opt-in flag for the usage of :doc:`Python's limited API <python:c-api/stable>`. - :raises setuptools.errors.PlatformError: if 'runtime_library_dirs' is + :raises setuptools.errors.PlatformError: if ``runtime_library_dirs`` is specified on Windows. (since v63) """ - def __init__(self, name, sources, *args, **kw): + # These 4 are set and used in setuptools/command/build_ext.py + # The lack of a default value and risk of `AttributeError` is purposeful + # to avoid people forgetting to call finalize_options if they modify the extension list. + # See example/rationale in https://github.com/pypa/setuptools/issues/4529. + _full_name: str #: Private API, internal use only. + _links_to_dynamic: bool #: Private API, internal use only. + _needs_stub: bool #: Private API, internal use only. + _file_name: str #: Private API, internal use only. + + def __init__(self, name: str, sources, *args, py_limited_api: bool = False, **kw): # The *args is needed for compatibility as calls may use positional # arguments. py_limited_api may be set only via keyword. - self.py_limited_api = kw.pop("py_limited_api", False) + self.py_limited_api = py_limited_api super().__init__(name, sources, *args, **kw) def _convert_pyx_sources_to_lang(self): diff --git a/contrib/python/setuptools/py3/setuptools/glob.py b/contrib/python/setuptools/py3/setuptools/glob.py index a184c0b643..ffe0ae92cb 100644 --- a/contrib/python/setuptools/py3/setuptools/glob.py +++ b/contrib/python/setuptools/py3/setuptools/glob.py @@ -6,9 +6,9 @@ Changes include: * Hidden files are not ignored. """ +import fnmatch import os import re -import fnmatch __all__ = ["glob", "iglob", "escape"] diff --git a/contrib/python/setuptools/py3/setuptools/installer.py b/contrib/python/setuptools/py3/setuptools/installer.py index a6aff723c2..ce3559cd93 100644 --- a/contrib/python/setuptools/py3/setuptools/installer.py +++ b/contrib/python/setuptools/py3/setuptools/installer.py @@ -3,13 +3,14 @@ import os import subprocess import sys import tempfile -from distutils import log -from distutils.errors import DistutilsError from functools import partial from . import _reqs -from .wheel import Wheel from .warnings import SetuptoolsDeprecationWarning +from .wheel import Wheel + +from distutils import log +from distutils.errors import DistutilsError def _fixup_find_links(find_links): diff --git a/contrib/python/setuptools/py3/setuptools/launch.py b/contrib/python/setuptools/py3/setuptools/launch.py index 0208fdf33b..56c7d035f1 100644 --- a/contrib/python/setuptools/py3/setuptools/launch.py +++ b/contrib/python/setuptools/py3/setuptools/launch.py @@ -6,8 +6,8 @@ setuptools is bootstrapped via import. # Note that setuptools gets imported implicitly by the # invocation of this script using python -m setuptools.launch -import tokenize import sys +import tokenize def run(): diff --git a/contrib/python/setuptools/py3/setuptools/logging.py b/contrib/python/setuptools/py3/setuptools/logging.py index ceca99ca76..e9674c5a81 100644 --- a/contrib/python/setuptools/py3/setuptools/logging.py +++ b/contrib/python/setuptools/py3/setuptools/logging.py @@ -1,9 +1,11 @@ -import sys import inspect import logging -import distutils.log +import sys + from . import monkey +import distutils.log + def _not_warning(record): return record.levelno < logging.WARNING diff --git a/contrib/python/setuptools/py3/setuptools/modified.py b/contrib/python/setuptools/py3/setuptools/modified.py index af6ceeac89..245a61580b 100644 --- a/contrib/python/setuptools/py3/setuptools/modified.py +++ b/contrib/python/setuptools/py3/setuptools/modified.py @@ -1,7 +1,7 @@ from ._distutils._modified import ( newer, - newer_pairwise, newer_group, + newer_pairwise, newer_pairwise_group, ) diff --git a/contrib/python/setuptools/py3/setuptools/monkey.py b/contrib/python/setuptools/py3/setuptools/monkey.py index e513f95245..3b6eefb4dd 100644 --- a/contrib/python/setuptools/py3/setuptools/monkey.py +++ b/contrib/python/setuptools/py3/setuptools/monkey.py @@ -4,18 +4,17 @@ Monkey patching of distutils. from __future__ import annotations -import functools import inspect import platform import sys import types -from importlib import import_module -from typing import TypeVar +from typing import Type, TypeVar, cast, overload import distutils.filelist - _T = TypeVar("_T") +_UnpatchT = TypeVar("_UnpatchT", type, types.FunctionType) + __all__: list[str] = [] """ @@ -38,25 +37,30 @@ def _get_mro(cls): return inspect.getmro(cls) -def get_unpatched(item: _T) -> _T: - lookup = ( - get_unpatched_class - if isinstance(item, type) - else get_unpatched_function - if isinstance(item, types.FunctionType) - else lambda item: None - ) - return lookup(item) +@overload +def get_unpatched(item: _UnpatchT) -> _UnpatchT: ... +@overload +def get_unpatched(item: object) -> None: ... +def get_unpatched( + item: type | types.FunctionType | object, +) -> type | types.FunctionType | None: + if isinstance(item, type): + return get_unpatched_class(item) + if isinstance(item, types.FunctionType): + return get_unpatched_function(item) + return None -def get_unpatched_class(cls): +def get_unpatched_class(cls: type[_T]) -> type[_T]: """Protect against re-patching the distutils if reloaded Also ensures that no other distutils extension monkeypatched the distutils first. """ external_bases = ( - cls for cls in _get_mro(cls) if not cls.__module__.startswith('setuptools') + cast(Type[_T], cls) + for cls in _get_mro(cls) + if not cls.__module__.startswith('setuptools') ) base = next(external_bases) if not base.__module__.startswith('distutils'): @@ -85,8 +89,6 @@ def patch_all(): 'distutils.command.build_ext' ].Extension = setuptools.extension.Extension - patch_for_msvc_specialized_compiler() - def _patch_distribution_metadata(): from . import _core_metadata @@ -122,36 +124,3 @@ def patch_func(replacement, target_mod, func_name): def get_unpatched_function(candidate): return candidate.unpatched - - -def patch_for_msvc_specialized_compiler(): - """ - Patch functions in distutils to use standalone Microsoft Visual C++ - compilers. - """ - from . import msvc - - if platform.system() != 'Windows': - # Compilers only available on Microsoft Windows - return - - def patch_params(mod_name, func_name): - """ - Prepare the parameters for patch_func to patch indicated function. - """ - repl_prefix = 'msvc14_' - repl_name = repl_prefix + func_name.lstrip('_') - repl = getattr(msvc, repl_name) - mod = import_module(mod_name) - if not hasattr(mod, func_name): - raise ImportError(func_name) - return repl, mod, func_name - - # Python 3.5+ - msvc14 = functools.partial(patch_params, 'distutils._msvccompiler') - - try: - # Patch distutils._msvccompiler._get_vc_env - patch_func(*msvc14('_get_vc_env')) - except ImportError: - pass diff --git a/contrib/python/setuptools/py3/setuptools/msvc.py b/contrib/python/setuptools/py3/setuptools/msvc.py index 2768059213..de4b05f928 100644 --- a/contrib/python/setuptools/py3/setuptools/msvc.py +++ b/contrib/python/setuptools/py3/setuptools/msvc.py @@ -1,31 +1,24 @@ """ -Improved support for Microsoft Visual C++ compilers. +Environment info about Microsoft Compilers. -Known supported compilers: --------------------------- -Microsoft Visual C++ 14.X: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) - Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) - Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64) - -This may also support compilers shipped with compatible Visual Studio versions. +>>> getfixture('windows_only') +>>> ei = EnvironmentInfo('amd64') """ from __future__ import annotations -import json -from os import listdir, pathsep -from os.path import join, isfile, isdir, dirname -from subprocess import CalledProcessError import contextlib -import platform import itertools -import subprocess -import distutils.errors +import json +import os +import os.path +import platform from typing import TYPE_CHECKING from more_itertools import unique_everseen +import distutils.errors + # https://github.com/python/mypy/issues/8166 if not TYPE_CHECKING and platform.system() == 'Windows': import winreg @@ -42,239 +35,6 @@ else: environ: dict[str, str] = dict() -def _msvc14_find_vc2015(): - """Python 3.8 "distutils/_msvccompiler.py" backport""" - try: - key = winreg.OpenKey( - winreg.HKEY_LOCAL_MACHINE, - r"Software\Microsoft\VisualStudio\SxS\VC7", - 0, - winreg.KEY_READ | winreg.KEY_WOW64_32KEY, - ) - except OSError: - return None, None - - best_version = 0 - best_dir = None - with key: - for i in itertools.count(): - try: - v, vc_dir, vt = winreg.EnumValue(key, i) - except OSError: - break - if v and vt == winreg.REG_SZ and isdir(vc_dir): - try: - version = int(float(v)) - except (ValueError, TypeError): - continue - if version >= 14 and version > best_version: - best_version, best_dir = version, vc_dir - return best_version, best_dir - - -def _msvc14_find_vc2017(): - """Python 3.8 "distutils/_msvccompiler.py" backport - - Returns "15, path" based on the result of invoking vswhere.exe - If no install is found, returns "None, None" - - The version is returned to avoid unnecessarily changing the function - result. It may be ignored when the path is not None. - - If vswhere.exe is not available, by definition, VS 2017 is not - installed. - """ - root = environ.get("ProgramFiles(x86)") or environ.get("ProgramFiles") - if not root: - return None, None - - suitable_components = ( - "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "Microsoft.VisualStudio.Workload.WDExpress", - ) - - for component in suitable_components: - # Workaround for `-requiresAny` (only available on VS 2017 > 15.6) - with contextlib.suppress(CalledProcessError, OSError, UnicodeDecodeError): - path = ( - subprocess.check_output([ - join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), - "-latest", - "-prerelease", - "-requires", - component, - "-property", - "installationPath", - "-products", - "*", - ]) - .decode(encoding="mbcs", errors="strict") - .strip() - ) - - path = join(path, "VC", "Auxiliary", "Build") - if isdir(path): - return 15, path - - return None, None # no suitable component found - - -PLAT_SPEC_TO_RUNTIME = { - 'x86': 'x86', - 'x86_amd64': 'x64', - 'x86_arm': 'arm', - 'x86_arm64': 'arm64', -} - - -def _msvc14_find_vcvarsall(plat_spec): - """Python 3.8 "distutils/_msvccompiler.py" backport""" - _, best_dir = _msvc14_find_vc2017() - vcruntime = None - - if plat_spec in PLAT_SPEC_TO_RUNTIME: - vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec] - else: - vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86' - - if best_dir: - vcredist = join( - best_dir, - "..", - "..", - "redist", - "MSVC", - "**", - vcruntime_plat, - "Microsoft.VC14*.CRT", - "vcruntime140.dll", - ) - try: - import glob - - vcruntime = glob.glob(vcredist, recursive=True)[-1] - except (ImportError, OSError, LookupError): - vcruntime = None - - if not best_dir: - best_version, best_dir = _msvc14_find_vc2015() - if best_version: - vcruntime = join( - best_dir, - 'redist', - vcruntime_plat, - "Microsoft.VC140.CRT", - "vcruntime140.dll", - ) - - if not best_dir: - return None, None - - vcvarsall = join(best_dir, "vcvarsall.bat") - if not isfile(vcvarsall): - return None, None - - if not vcruntime or not isfile(vcruntime): - vcruntime = None - - return vcvarsall, vcruntime - - -def _msvc14_get_vc_env(plat_spec): - """Python 3.8 "distutils/_msvccompiler.py" backport""" - if "DISTUTILS_USE_SDK" in environ: - return {key.lower(): value for key, value in environ.items()} - - vcvarsall, vcruntime = _msvc14_find_vcvarsall(plat_spec) - if not vcvarsall: - raise distutils.errors.DistutilsPlatformError("Unable to find vcvarsall.bat") - - try: - out = subprocess.check_output( - 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), - stderr=subprocess.STDOUT, - ).decode('utf-16le', errors='replace') - except subprocess.CalledProcessError as exc: - raise distutils.errors.DistutilsPlatformError( - "Error executing {}".format(exc.cmd) - ) from exc - - env = { - key.lower(): value - for key, _, value in (line.partition('=') for line in out.splitlines()) - if key and value - } - - if vcruntime: - env['py_vcruntime_redist'] = vcruntime - return env - - -def msvc14_get_vc_env(plat_spec): - """ - Patched "distutils._msvccompiler._get_vc_env" for support extra - Microsoft Visual C++ 14.X compilers. - - Set environment without use of "vcvarsall.bat". - - Parameters - ---------- - plat_spec: str - Target architecture. - - Return - ------ - dict - environment - """ - - # Always use backport from CPython 3.8 - try: - return _msvc14_get_vc_env(plat_spec) - except distutils.errors.DistutilsPlatformError as exc: - _augment_exception(exc, 14.0) - raise - - -def _augment_exception(exc, version, arch=''): - """ - Add details to the exception message to help guide the user - as to what action will resolve it. - """ - # Error if MSVC++ directory not found or environment not set - message = exc.args[0] - - if "vcvarsall" in message.lower() or "visual c" in message.lower(): - # Special error message if MSVC++ not installed - tmpl = 'Microsoft Visual C++ {version:0.1f} or greater is required.' - message = tmpl.format(**locals()) - msdownload = 'www.microsoft.com/download/details.aspx?id=%d' - if version == 9.0: - if arch.lower().find('ia64') > -1: - # For VC++ 9.0, if IA64 support is needed, redirect user - # to Windows SDK 7.0. - # Note: No download link available from Microsoft. - message += ' Get it with "Microsoft Windows SDK 7.0"' - else: - # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : - # This redirection link is maintained by Microsoft. - # Contact vspython@microsoft.com if it needs updating. - message += ' Get it from http://aka.ms/vcpython27' - elif version == 10.0: - # For VC++ 10.0 Redirect user to Windows SDK 7.1 - message += ' Get it with "Microsoft Windows SDK 7.1": ' - message += msdownload % 8279 - elif version >= 14.0: - # For VC++ 14.X Redirect user to latest Visual C++ Build Tools - message += ( - ' Get it with "Microsoft C++ Build Tools": ' - r'https://visualstudio.microsoft.com' - r'/visual-cpp-build-tools/' - ) - - exc.args = (message,) - - class PlatformInfo: """ Current and Target Architectures information. @@ -438,7 +198,7 @@ class RegistryInfo: str Registry key """ - return join(self.visualstudio, 'SxS') + return os.path.join(self.visualstudio, 'SxS') @property def vc(self): @@ -450,7 +210,7 @@ class RegistryInfo: str Registry key """ - return join(self.sxs, 'VC7') + return os.path.join(self.sxs, 'VC7') @property def vs(self): @@ -462,7 +222,7 @@ class RegistryInfo: str Registry key """ - return join(self.sxs, 'VS7') + return os.path.join(self.sxs, 'VS7') @property def vc_for_python(self): @@ -498,7 +258,7 @@ class RegistryInfo: str Registry key """ - return join(self.microsoft_sdk, 'Windows') + return os.path.join(self.microsoft_sdk, 'Windows') @property def netfx_sdk(self): @@ -510,7 +270,7 @@ class RegistryInfo: str Registry key """ - return join(self.microsoft_sdk, 'NETFXSDK') + return os.path.join(self.microsoft_sdk, 'NETFXSDK') @property def windows_kits_roots(self): @@ -541,7 +301,7 @@ class RegistryInfo: Registry key """ node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' - return join('Software', node64, 'Microsoft', key) + return os.path.join('Software', node64, 'Microsoft', key) def lookup(self, key, name): """ @@ -677,7 +437,7 @@ class SystemInfo: instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances' try: - hashed_names = listdir(instances_dir) + hashed_names = os.listdir(instances_dir) except OSError: # Directory not exists with all Visual Studio versions @@ -686,13 +446,13 @@ class SystemInfo: for name in hashed_names: try: # Get VS installation path from "state.json" file - state_path = join(instances_dir, name, 'state.json') + state_path = os.path.join(instances_dir, name, 'state.json') with open(state_path, 'rt', encoding='utf-8') as state_file: state = json.load(state_file) vs_path = state['installationPath'] # Raises OSError if this VS installation does not contain VC - listdir(join(vs_path, r'VC\Tools\MSVC')) + os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC')) # Store version and path vs_versions[self._as_float_version(state['installationVersion'])] = ( @@ -733,7 +493,7 @@ class SystemInfo: path """ # Default path - default = join( + default = os.path.join( self.ProgramFilesx86, 'Microsoft Visual Studio %0.1f' % self.vs_ver ) @@ -752,7 +512,7 @@ class SystemInfo: """ path = self._guess_vc() or self._guess_vc_legacy() - if not isdir(path): + if not os.path.isdir(path): msg = 'Microsoft Visual C++ directory not found' raise distutils.errors.DistutilsPlatformError(msg) @@ -777,14 +537,14 @@ class SystemInfo: # Else, search with path from registry vs_dir = self.VSInstallDir - guess_vc = join(vs_dir, r'VC\Tools\MSVC') + guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC') # Subdir with VC exact version as name try: # Update the VC version with real one instead of VS version - vc_ver = listdir(guess_vc)[-1] + vc_ver = os.listdir(guess_vc)[-1] self.vc_ver = self._as_float_version(vc_ver) - return join(guess_vc, vc_ver) + return os.path.join(guess_vc, vc_ver) except (OSError, IndexError): return '' @@ -797,14 +557,14 @@ class SystemInfo: str path """ - default = join( + default = os.path.join( self.ProgramFilesx86, r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver ) # Try to get "VC++ for Python" path from registry as default path - reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver) + reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vs_ver) python_vc = self.ri.lookup(reg_path, 'installdir') - default_vc = join(python_vc, 'VC') if python_vc else default + default_vc = os.path.join(python_vc, 'VC') if python_vc else default # Try to get path from registry, if fail use default path return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc @@ -841,7 +601,7 @@ class SystemInfo: str version """ - return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib')) + return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib')) @property def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME @@ -856,34 +616,34 @@ class SystemInfo: sdkdir = '' for ver in self.WindowsSdkVersion: # Try to get it from registry - loc = join(self.ri.windows_sdk, 'v%s' % ver) + loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) sdkdir = self.ri.lookup(loc, 'installationfolder') if sdkdir: break - if not sdkdir or not isdir(sdkdir): + if not sdkdir or not os.path.isdir(sdkdir): # Try to get "VC++ for Python" version from registry - path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) install_base = self.ri.lookup(path, 'installdir') if install_base: - sdkdir = join(install_base, 'WinSDK') - if not sdkdir or not isdir(sdkdir): + sdkdir = os.path.join(install_base, 'WinSDK') + if not sdkdir or not os.path.isdir(sdkdir): # If fail, use default new path for ver in self.WindowsSdkVersion: intver = ver[: ver.rfind('.')] path = r'Microsoft SDKs\Windows Kits\%s' % intver - d = join(self.ProgramFiles, path) - if isdir(d): + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): sdkdir = d - if not sdkdir or not isdir(sdkdir): + if not sdkdir or not os.path.isdir(sdkdir): # If fail, use default old path for ver in self.WindowsSdkVersion: path = r'Microsoft SDKs\Windows\v%s' % ver - d = join(self.ProgramFiles, path) - if isdir(d): + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): sdkdir = d if not sdkdir: # If fail, use Platform SDK - sdkdir = join(self.VCInstallDir, 'PlatformSDK') + sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') return sdkdir @property @@ -910,10 +670,10 @@ class SystemInfo: regpaths = [] if self.vs_ver >= 14.0: for ver in self.NetFxSdkVersion: - regpaths += [join(self.ri.netfx_sdk, ver, fx)] + regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] for ver in self.WindowsSdkVersion: - regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)] + regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)] # Return installation folder from the more recent path for path in regpaths: @@ -933,7 +693,7 @@ class SystemInfo: str path """ - path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver) + path = os.path.join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver) return self.ri.lookup(path, 'productdir') or '' @property @@ -967,7 +727,7 @@ class SystemInfo: str version """ - return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib')) + return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib')) @property def NetFxSdkVersion(self): @@ -998,7 +758,7 @@ class SystemInfo: """ sdkdir = '' for ver in self.NetFxSdkVersion: - loc = join(self.ri.netfx_sdk, ver) + loc = os.path.join(self.ri.netfx_sdk, ver) sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') if sdkdir: break @@ -1015,7 +775,7 @@ class SystemInfo: path """ # Default path - guess_fw = join(self.WinDir, r'Microsoft.NET\Framework') + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') # Try to get path from registry, if fail use default path return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw @@ -1031,7 +791,7 @@ class SystemInfo: path """ # Default path - guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64') + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') # Try to get path from registry, if fail use default path return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw @@ -1109,8 +869,9 @@ class SystemInfo: """ matching_dirs = ( dir_name - for dir_name in reversed(listdir(path)) - if isdir(join(path, dir_name)) and dir_name.startswith(prefix) + for dir_name in reversed(os.listdir(path)) + if os.path.isdir(os.path.join(path, dir_name)) + and dir_name.startswith(prefix) ) return next(matching_dirs, None) or '' @@ -1190,7 +951,7 @@ class EnvironmentInfo: paths += [r'Team Tools\Performance Tools'] paths += [r'Team Tools\Performance Tools%s' % arch_subdir] - return [join(self.si.VSInstallDir, path) for path in paths] + return [os.path.join(self.si.VSInstallDir, path) for path in paths] @property def VCIncludes(self): @@ -1203,8 +964,8 @@ class EnvironmentInfo: paths """ return [ - join(self.si.VCInstallDir, 'Include'), - join(self.si.VCInstallDir, r'ATLMFC\Include'), + os.path.join(self.si.VCInstallDir, 'Include'), + os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'), ] @property @@ -1226,7 +987,7 @@ class EnvironmentInfo: if self.vs_ver >= 14.0: paths += [r'Lib\store%s' % arch_subdir] - return [join(self.si.VCInstallDir, path) for path in paths] + return [os.path.join(self.si.VCInstallDir, path) for path in paths] @property def VCStoreRefs(self): @@ -1240,7 +1001,7 @@ class EnvironmentInfo: """ if self.vs_ver < 14.0: return [] - return [join(self.si.VCInstallDir, r'Lib\store\references')] + return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] @property def VCTools(self): @@ -1253,30 +1014,34 @@ class EnvironmentInfo: paths """ si = self.si - tools = [join(si.VCInstallDir, 'VCPackages')] + tools = [os.path.join(si.VCInstallDir, 'VCPackages')] forcex86 = True if self.vs_ver <= 10.0 else False arch_subdir = self.pi.cross_dir(forcex86) if arch_subdir: - tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)] + tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] if self.vs_ver == 14.0: path = 'Bin%s' % self.pi.current_dir(hidex86=True) - tools += [join(si.VCInstallDir, path)] + tools += [os.path.join(si.VCInstallDir, path)] elif self.vs_ver >= 15.0: host_dir = ( r'bin\HostX86%s' if self.pi.current_is_x86() else r'bin\HostX64%s' ) - tools += [join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))] + tools += [ + os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True)) + ] if self.pi.current_cpu != self.pi.target_cpu: tools += [ - join(si.VCInstallDir, host_dir % self.pi.current_dir(x64=True)) + os.path.join( + si.VCInstallDir, host_dir % self.pi.current_dir(x64=True) + ) ] else: - tools += [join(si.VCInstallDir, 'Bin')] + tools += [os.path.join(si.VCInstallDir, 'Bin')] return tools @@ -1292,13 +1057,13 @@ class EnvironmentInfo: """ if self.vs_ver <= 10.0: arch_subdir = self.pi.target_dir(hidex86=True, x64=True) - return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] + return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] else: arch_subdir = self.pi.target_dir(x64=True) - lib = join(self.si.WindowsSdkDir, 'lib') + lib = os.path.join(self.si.WindowsSdkDir, 'lib') libver = self._sdk_subdir - return [join(lib, '%sum%s' % (libver, arch_subdir))] + return [os.path.join(lib, '%sum%s' % (libver, arch_subdir))] @property def OSIncludes(self): @@ -1310,10 +1075,10 @@ class EnvironmentInfo: list of str paths """ - include = join(self.si.WindowsSdkDir, 'include') + include = os.path.join(self.si.WindowsSdkDir, 'include') if self.vs_ver <= 10.0: - return [include, join(include, 'gl')] + return [include, os.path.join(include, 'gl')] else: if self.vs_ver >= 14.0: @@ -1321,9 +1086,9 @@ class EnvironmentInfo: else: sdkver = '' return [ - join(include, '%sshared' % sdkver), - join(include, '%sum' % sdkver), - join(include, '%swinrt' % sdkver), + os.path.join(include, '%sshared' % sdkver), + os.path.join(include, '%sum' % sdkver), + os.path.join(include, '%swinrt' % sdkver), ] @property @@ -1336,23 +1101,25 @@ class EnvironmentInfo: list of str paths """ - ref = join(self.si.WindowsSdkDir, 'References') + ref = os.path.join(self.si.WindowsSdkDir, 'References') libpath = [] if self.vs_ver <= 9.0: libpath += self.OSLibraries if self.vs_ver >= 11.0: - libpath += [join(ref, r'CommonConfiguration\Neutral')] + libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] if self.vs_ver >= 14.0: libpath += [ ref, - join(self.si.WindowsSdkDir, 'UnionMetadata'), - join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), - join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), - join(ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0'), - join( + os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), + os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), + os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), + os.path.join( + ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0' + ), + os.path.join( self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs', @@ -1387,12 +1154,12 @@ class EnvironmentInfo: """ if self.vs_ver < 15.0: bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86' - yield join(self.si.WindowsSdkDir, bin_dir) + yield os.path.join(self.si.WindowsSdkDir, bin_dir) if not self.pi.current_is_x86(): arch_subdir = self.pi.current_dir(x64=True) path = 'Bin%s' % arch_subdir - yield join(self.si.WindowsSdkDir, path) + yield os.path.join(self.si.WindowsSdkDir, path) if self.vs_ver in (10.0, 11.0): if self.pi.target_is_x86(): @@ -1400,13 +1167,13 @@ class EnvironmentInfo: else: arch_subdir = self.pi.current_dir(hidex86=True, x64=True) path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir - yield join(self.si.WindowsSdkDir, path) + yield os.path.join(self.si.WindowsSdkDir, path) elif self.vs_ver >= 15.0: - path = join(self.si.WindowsSdkDir, 'Bin') + path = os.path.join(self.si.WindowsSdkDir, 'Bin') arch_subdir = self.pi.current_dir(x64=True) sdkver = self.si.WindowsSdkLastVersion - yield join(path, '%s%s' % (sdkver, arch_subdir)) + yield os.path.join(path, '%s%s' % (sdkver, arch_subdir)) if self.si.WindowsSDKExecutablePath: yield self.si.WindowsSDKExecutablePath @@ -1437,7 +1204,7 @@ class EnvironmentInfo: if self.vs_ver > 9.0: return [] - return [join(self.si.WindowsSdkDir, 'Setup')] + return [os.path.join(self.si.WindowsSdkDir, 'Setup')] @property def FxTools(self): @@ -1461,9 +1228,13 @@ class EnvironmentInfo: tools = [] if include32: - tools += [join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32] + tools += [ + os.path.join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32 + ] if include64: - tools += [join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64] + tools += [ + os.path.join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64 + ] return tools @property @@ -1480,7 +1251,7 @@ class EnvironmentInfo: return [] arch_subdir = self.pi.target_dir(x64=True) - return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] + return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] @property def NetFxSDKIncludes(self): @@ -1495,7 +1266,7 @@ class EnvironmentInfo: if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: return [] - return [join(self.si.NetFxSdkDir, r'include\um')] + return [os.path.join(self.si.NetFxSdkDir, r'include\um')] @property def VsTDb(self): @@ -1507,7 +1278,7 @@ class EnvironmentInfo: list of str paths """ - return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')] + return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] @property def MSBuild(self): @@ -1529,11 +1300,11 @@ class EnvironmentInfo: arch_subdir = '' path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir) - build = [join(base_path, path)] + build = [os.path.join(base_path, path)] if self.vs_ver >= 15.0: # Add Roslyn C# & Visual Basic Compiler - build += [join(base_path, path, 'Roslyn')] + build += [os.path.join(base_path, path, 'Roslyn')] return build @@ -1550,7 +1321,7 @@ class EnvironmentInfo: if self.vs_ver < 11.0: return [] - return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')] + return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] @property def UCRTLibraries(self): @@ -1566,9 +1337,9 @@ class EnvironmentInfo: return [] arch_subdir = self.pi.target_dir(x64=True) - lib = join(self.si.UniversalCRTSdkDir, 'lib') + lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') ucrtver = self._ucrt_subdir - return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] + return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] @property def UCRTIncludes(self): @@ -1583,8 +1354,8 @@ class EnvironmentInfo: if self.vs_ver < 14.0: return [] - include = join(self.si.UniversalCRTSdkDir, 'include') - return [join(include, '%sucrt' % self._ucrt_subdir)] + include = os.path.join(self.si.UniversalCRTSdkDir, 'include') + return [os.path.join(include, '%sucrt' % self._ucrt_subdir)] @property def _ucrt_subdir(self): @@ -1615,14 +1386,11 @@ class EnvironmentInfo: return [self.si.FSharpInstallDir] @property - def VCRuntimeRedist(self): + def VCRuntimeRedist(self) -> str | None: """ Microsoft Visual C++ runtime redistributable dll. - Return - ------ - str - path + Returns the first suitable path found or None. """ vcruntime = 'vcruntime%d0.dll' % self.vc_ver arch_subdir = self.pi.target_dir(x64=True).strip('\\') @@ -1630,13 +1398,13 @@ class EnvironmentInfo: # Installation prefixes candidates prefixes = [] tools_path = self.si.VCInstallDir - redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist')) - if isdir(redist_path): + redist_path = os.path.dirname(tools_path.replace(r'\Tools', r'\Redist')) + if os.path.isdir(redist_path): # Redist version may not be exactly the same as tools - redist_path = join(redist_path, listdir(redist_path)[-1]) - prefixes += [redist_path, join(redist_path, 'onecore')] + redist_path = os.path.join(redist_path, os.listdir(redist_path)[-1]) + prefixes += [redist_path, os.path.join(redist_path, 'onecore')] - prefixes += [join(tools_path, 'redist')] # VS14 legacy path + prefixes += [os.path.join(tools_path, 'redist')] # VS14 legacy path # CRT directory crt_dirs = ( @@ -1646,11 +1414,11 @@ class EnvironmentInfo: ) # vcruntime path - for prefix, crt_dir in itertools.product(prefixes, crt_dirs): - path = join(prefix, arch_subdir, crt_dir, vcruntime) - if isfile(path): - return path - return None + candidate_paths = ( + os.path.join(prefix, arch_subdir, crt_dir, vcruntime) + for (prefix, crt_dir) in itertools.product(prefixes, crt_dirs) + ) + return next(filter(os.path.isfile, candidate_paths), None) def return_env(self, exists=True): """ @@ -1709,7 +1477,7 @@ class EnvironmentInfo: exists, ), ) - if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist): + if self.vs_ver >= 14 and self.VCRuntimeRedist: env['py_vcruntime_redist'] = self.VCRuntimeRedist return env @@ -1737,11 +1505,11 @@ class EnvironmentInfo: """ # flatten spec_path_lists spec_paths = itertools.chain.from_iterable(spec_path_lists) - env_paths = environ.get(name, '').split(pathsep) + env_paths = environ.get(name, '').split(os.pathsep) paths = itertools.chain(spec_paths, env_paths) - extant_paths = list(filter(isdir, paths)) if exists else paths + extant_paths = list(filter(os.path.isdir, paths)) if exists else paths if not extant_paths: msg = "%s environment variable is empty" % name.upper() raise distutils.errors.DistutilsPlatformError(msg) unique_paths = unique_everseen(extant_paths) - return pathsep.join(unique_paths) + return os.pathsep.join(unique_paths) diff --git a/contrib/python/setuptools/py3/setuptools/namespaces.py b/contrib/python/setuptools/py3/setuptools/namespaces.py index 2f2c1cfbe1..299fdd9479 100644 --- a/contrib/python/setuptools/py3/setuptools/namespaces.py +++ b/contrib/python/setuptools/py3/setuptools/namespaces.py @@ -1,9 +1,9 @@ -import os -from distutils import log import itertools +import os -from .compat import py39 +from .compat import py312 +from distutils import log flatten = itertools.chain.from_iterable @@ -25,8 +25,9 @@ class Installer: list(lines) return - with open(filename, 'wt', encoding=py39.LOCALE_ENCODING) as f: - # Requires encoding="locale" instead of "utf-8" (python/cpython#77102). + with open(filename, 'wt', encoding=py312.PTH_ENCODING) as f: + # Python<3.13 requires encoding="locale" instead of "utf-8" + # See: python/cpython#77102 f.writelines(lines) def uninstall_namespaces(self): diff --git a/contrib/python/setuptools/py3/setuptools/package_index.py b/contrib/python/setuptools/py3/setuptools/package_index.py index c24c783762..9e01d5e082 100644 --- a/contrib/python/setuptools/py3/setuptools/package_index.py +++ b/contrib/python/setuptools/py3/setuptools/package_index.py @@ -1,49 +1,50 @@ """PyPI and direct package downloading.""" -import sys -import subprocess -import os -import re -import io -import shutil -import socket import base64 -import hashlib -import itertools import configparser +import hashlib import html import http.client +import io +import itertools +import os +import re +import shutil +import socket +import subprocess +import sys +import urllib.error import urllib.parse import urllib.request -import urllib.error +from fnmatch import translate from functools import wraps +from typing import NamedTuple + +from more_itertools import unique_everseen import setuptools from pkg_resources import ( - CHECKOUT_DIST, - Distribution, BINARY_DIST, - normalize_path, + CHECKOUT_DIST, + DEVELOP_DIST, + EGG_DIST, SOURCE_DIST, + Distribution, Environment, + Requirement, find_distributions, + normalize_path, + parse_version, safe_name, safe_version, to_filename, - Requirement, - DEVELOP_DIST, - EGG_DIST, - parse_version, ) -from distutils import log -from distutils.errors import DistutilsError -from fnmatch import translate from setuptools.wheel import Wheel -from more_itertools import unique_everseen - -from .unicode_utils import _read_utf8_with_fallback, _cfg_read_utf8_with_fallback +from .unicode_utils import _cfg_read_utf8_with_fallback, _read_utf8_with_fallback +from distutils import log +from distutils.errors import DistutilsError EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I) @@ -1001,21 +1002,20 @@ def _encode_auth(auth): return encoded.replace('\n', '') -class Credential: - """ - A username/password pair. Use like a namedtuple. +class Credential(NamedTuple): """ + A username/password pair. - def __init__(self, username, password): - self.username = username - self.password = password + Displayed separated by `:`. + >>> str(Credential('username', 'password')) + 'username:password' + """ - def __iter__(self): - yield self.username - yield self.password + username: str + password: str - def __str__(self): - return '%(username)s:%(password)s' % vars(self) + def __str__(self) -> str: + return f'{self.username}:{self.password}' class PyPIConfig(configparser.RawConfigParser): @@ -1072,7 +1072,7 @@ def open_with_auth(url, opener=urllib.request.urlopen): if scheme in ('http', 'https'): auth, address = _splituser(netloc) else: - auth = None + auth, address = (None, None) if not auth: cred = PyPIConfig().find_credential(url) diff --git a/contrib/python/setuptools/py3/setuptools/sandbox.py b/contrib/python/setuptools/py3/setuptools/sandbox.py index 147b26749e..9c2c78a32c 100644 --- a/contrib/python/setuptools/py3/setuptools/sandbox.py +++ b/contrib/python/setuptools/py3/setuptools/sandbox.py @@ -1,32 +1,34 @@ from __future__ import annotations -import os -import sys -import tempfile -import operator +import builtins +import contextlib import functools import itertools -import re -import contextlib +import operator +import os import pickle +import re +import sys +import tempfile import textwrap -import builtins +from types import TracebackType +from typing import TYPE_CHECKING import pkg_resources -from distutils.errors import DistutilsError from pkg_resources import working_set +from distutils.errors import DistutilsError + if sys.platform.startswith('java'): - import org.python.modules.posix.PosixModule as _os + import org.python.modules.posix.PosixModule as _os # pyright: ignore[reportMissingImports] else: _os = sys.modules[os.name] -try: - _file = file # type: ignore[name-defined] # Check for global variable -except NameError: - _file = None _open = open +if TYPE_CHECKING: + from typing_extensions import Self + __all__ = [ "AbstractSandbox", "DirectorySandbox", @@ -121,10 +123,15 @@ class ExceptionSaver: later. """ - def __enter__(self): + def __enter__(self) -> Self: return self - def __exit__(self, type, exc, tb): + def __exit__( + self, + type: type[BaseException] | None, + exc: BaseException | None, + tb: TracebackType | None, + ) -> bool: if not exc: return False @@ -281,17 +288,18 @@ class AbstractSandbox: for name in self._attrs: setattr(os, name, getattr(source, name)) - def __enter__(self): + def __enter__(self) -> None: self._copy(self) - if _file: - builtins.file = self._file builtins.open = self._open self._active = True - def __exit__(self, exc_type, exc_value, traceback): + def __exit__( + self, + exc_type: object, + exc_value: object, + traceback: object, + ) -> None: self._active = False - if _file: - builtins.file = _file builtins.open = _open self._copy(_os) @@ -324,8 +332,6 @@ class AbstractSandbox: return wrap - if _file: - _file = _mk_single_path_wrapper('file', _file) _open = _mk_single_path_wrapper('open', _open) for __name in [ "stat", @@ -442,13 +448,6 @@ class DirectorySandbox(AbstractSandbox): raise SandboxViolation(operation, args, kw) - if _file: - - def _file(self, path, mode='r', *args, **kw): - if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): - self._violation("file", path, mode, *args, **kw) - return _file(path, mode, *args, **kw) - def _open(self, path, mode='r', *args, **kw): if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): self._violation("open", path, mode, *args, **kw) @@ -525,6 +524,6 @@ class SandboxViolation(DistutilsError): """ ).lstrip() - def __str__(self): + def __str__(self) -> str: cmd, args, kwargs = self.args return self.tmpl.format(**locals()) diff --git a/contrib/python/setuptools/py3/setuptools/unicode_utils.py b/contrib/python/setuptools/py3/setuptools/unicode_utils.py index 696b34c46a..862d79e898 100644 --- a/contrib/python/setuptools/py3/setuptools/unicode_utils.py +++ b/contrib/python/setuptools/py3/setuptools/unicode_utils.py @@ -1,5 +1,5 @@ -import unicodedata import sys +import unicodedata from configparser import ConfigParser from .compat import py39 diff --git a/contrib/python/setuptools/py3/setuptools/wheel.py b/contrib/python/setuptools/py3/setuptools/wheel.py index a05cd98d1f..69a73df244 100644 --- a/contrib/python/setuptools/py3/setuptools/wheel.py +++ b/contrib/python/setuptools/py3/setuptools/wheel.py @@ -1,26 +1,25 @@ """Wheels support.""" +import contextlib import email -import itertools import functools +import itertools import os import posixpath import re import zipfile -import contextlib -from packaging.version import Version as parse_version from packaging.tags import sys_tags from packaging.utils import canonicalize_name - -from distutils.util import get_platform +from packaging.version import Version as parse_version import setuptools -from setuptools.command.egg_info import write_requirements, _egg_basename from setuptools.archive_util import _unpack_zipfile_obj +from setuptools.command.egg_info import _egg_basename, write_requirements from .unicode_utils import _read_utf8_with_fallback +from distutils.util import get_platform WHEEL_NAME = re.compile( r"""^(?P<project_name>.+?)-(?P<version>\d.*?) diff --git a/contrib/python/setuptools/py3/setuptools/windows_support.py b/contrib/python/setuptools/py3/setuptools/windows_support.py index 8299ac1cdf..7a2b53a291 100644 --- a/contrib/python/setuptools/py3/setuptools/windows_support.py +++ b/contrib/python/setuptools/py3/setuptools/windows_support.py @@ -8,7 +8,7 @@ def windows_only(func): @windows_only -def hide_file(path): +def hide_file(path: str) -> None: """ Set the hidden attribute on a file or directory. @@ -17,8 +17,8 @@ def hide_file(path): `path` must be text. """ import ctypes + import ctypes.wintypes - __import__('ctypes.wintypes') SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW SetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD SetFileAttributes.restype = ctypes.wintypes.BOOL diff --git a/contrib/python/setuptools/py3/ya.make b/contrib/python/setuptools/py3/ya.make index 189d711341..2f3f3937c2 100644 --- a/contrib/python/setuptools/py3/ya.make +++ b/contrib/python/setuptools/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(71.1.0) +VERSION(74.1.3) LICENSE(MIT) @@ -11,7 +11,6 @@ PEERDIR( contrib/python/jaraco.functools contrib/python/jaraco.text contrib/python/more-itertools - contrib/python/ordered-set contrib/python/packaging contrib/python/platformdirs contrib/python/typeguard @@ -43,21 +42,6 @@ PY_SRCS( setuptools/_distutils/_macos_compat.py setuptools/_distutils/_modified.py setuptools/_distutils/_msvccompiler.py - setuptools/_distutils/_vendor/__init__.py - setuptools/_distutils/_vendor/packaging/__init__.py - setuptools/_distutils/_vendor/packaging/_elffile.py - setuptools/_distutils/_vendor/packaging/_manylinux.py - setuptools/_distutils/_vendor/packaging/_musllinux.py - setuptools/_distutils/_vendor/packaging/_parser.py - setuptools/_distutils/_vendor/packaging/_structures.py - setuptools/_distutils/_vendor/packaging/_tokenizer.py - setuptools/_distutils/_vendor/packaging/markers.py - setuptools/_distutils/_vendor/packaging/metadata.py - setuptools/_distutils/_vendor/packaging/requirements.py - setuptools/_distutils/_vendor/packaging/specifiers.py - setuptools/_distutils/_vendor/packaging/tags.py - setuptools/_distutils/_vendor/packaging/utils.py - setuptools/_distutils/_vendor/packaging/version.py setuptools/_distutils/archive_util.py setuptools/_distutils/bcppcompiler.py setuptools/_distutils/ccompiler.py @@ -100,8 +84,6 @@ PY_SRCS( setuptools/_distutils/file_util.py setuptools/_distutils/filelist.py setuptools/_distutils/log.py - setuptools/_distutils/msvc9compiler.py - setuptools/_distutils/msvccompiler.py setuptools/_distutils/spawn.py setuptools/_distutils/sysconfig.py setuptools/_distutils/text_file.py @@ -149,6 +131,7 @@ PY_SRCS( setuptools/compat/__init__.py setuptools/compat/py310.py setuptools/compat/py311.py + setuptools/compat/py312.py setuptools/compat/py39.py setuptools/config/__init__.py setuptools/config/_apply_pyprojecttoml.py @@ -190,7 +173,10 @@ RESOURCE_FILES( .dist-info/top_level.txt pkg_resources/api_tests.txt pkg_resources/py.typed - setuptools/_distutils/_vendor/packaging/py.typed + setuptools/config/NOTICE + setuptools/config/_validate_pyproject/NOTICE + setuptools/config/distutils.schema.json + setuptools/config/setuptools.schema.json setuptools/script.tmpl ) |