leon-ofm007 commited on
Commit
95de5bb
·
verified ·
1 Parent(s): 6cb1454

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. Alina-SDXL-step00000505.safetensors +3 -0
  3. Alina-SDXL-step00001010.safetensors +3 -0
  4. Alina-SDXL-step00001515.safetensors +3 -0
  5. Alina-SDXL-step00002020.safetensors +3 -0
  6. Alina-SDXL-step00002525.safetensors +3 -0
  7. Alina-SDXL-step00004545.safetensors +3 -0
  8. Alina-SDXL-step00006060.safetensors +3 -0
  9. Alina-SDXL-step00006565.safetensors +3 -0
  10. Alina-SDXL-step00008585.safetensors +3 -0
  11. Alina-SDXL-step00009090.safetensors +3 -0
  12. Alina-SDXL-step00009595.safetensors +3 -0
  13. Alina-SDXL.safetensors +3 -0
  14. sd_xl_base_1.0.safetensors +3 -0
  15. venv/lib/python3.10/site-packages/distutils-precedence.pth +3 -0
  16. venv/lib/python3.10/site-packages/setuptools/__init__.py +242 -0
  17. venv/lib/python3.10/site-packages/setuptools/__pycache__/archive_util.cpython-310.pyc +0 -0
  18. venv/lib/python3.10/site-packages/setuptools/__pycache__/glob.cpython-310.pyc +0 -0
  19. venv/lib/python3.10/site-packages/setuptools/_deprecation_warning.py +7 -0
  20. venv/lib/python3.10/site-packages/setuptools/_imp.py +82 -0
  21. venv/lib/python3.10/site-packages/setuptools/archive_util.py +205 -0
  22. venv/lib/python3.10/site-packages/setuptools/build_meta.py +290 -0
  23. venv/lib/python3.10/site-packages/setuptools/cli-32.exe +0 -0
  24. venv/lib/python3.10/site-packages/setuptools/cli-64.exe +0 -0
  25. venv/lib/python3.10/site-packages/setuptools/cli-arm64.exe +3 -0
  26. venv/lib/python3.10/site-packages/setuptools/cli.exe +0 -0
  27. venv/lib/python3.10/site-packages/setuptools/config.py +751 -0
  28. venv/lib/python3.10/site-packages/setuptools/dep_util.py +25 -0
  29. venv/lib/python3.10/site-packages/setuptools/depends.py +176 -0
  30. venv/lib/python3.10/site-packages/setuptools/dist.py +1156 -0
  31. venv/lib/python3.10/site-packages/setuptools/errors.py +40 -0
  32. venv/lib/python3.10/site-packages/setuptools/extension.py +55 -0
  33. venv/lib/python3.10/site-packages/setuptools/glob.py +167 -0
  34. venv/lib/python3.10/site-packages/setuptools/gui-32.exe +0 -0
  35. venv/lib/python3.10/site-packages/setuptools/gui-64.exe +0 -0
  36. venv/lib/python3.10/site-packages/setuptools/gui.exe +0 -0
  37. venv/lib/python3.10/site-packages/setuptools/installer.py +104 -0
  38. venv/lib/python3.10/site-packages/setuptools/launch.py +36 -0
  39. venv/lib/python3.10/site-packages/setuptools/monkey.py +177 -0
  40. venv/lib/python3.10/site-packages/setuptools/msvc.py +1805 -0
  41. venv/lib/python3.10/site-packages/setuptools/namespaces.py +107 -0
  42. venv/lib/python3.10/site-packages/setuptools/package_index.py +1150 -0
  43. venv/lib/python3.10/site-packages/setuptools/py34compat.py +13 -0
  44. venv/lib/python3.10/site-packages/setuptools/sandbox.py +530 -0
  45. venv/lib/python3.10/site-packages/setuptools/script (dev).tmpl +6 -0
  46. venv/lib/python3.10/site-packages/setuptools/script.tmpl +3 -0
  47. venv/lib/python3.10/site-packages/setuptools/unicode_utils.py +42 -0
  48. venv/lib/python3.10/site-packages/setuptools/version.py +6 -0
  49. venv/lib/python3.10/site-packages/setuptools/wheel.py +213 -0
  50. venv/lib/python3.10/site-packages/setuptools/windows_support.py +29 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ venv/lib/python3.10/site-packages/setuptools/cli-arm64.exe filter=lfs diff=lfs merge=lfs -text
Alina-SDXL-step00000505.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f3a6ead6634c714e19d61db7ac271c94e959322a0fa7bf186b1098e64b0cd380
3
+ size 6938043451
Alina-SDXL-step00001010.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ef938c19a1fbf2ac950d71ab6fc3042f6266923b169d82915a884ca6a99ec8d7
3
+ size 6938043451
Alina-SDXL-step00001515.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7f140c3bc9f2b754e46334e0a65f815e395538d633292ed766c5f237af48466b
3
+ size 6938043304
Alina-SDXL-step00002020.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8e858b6e774859d4dedb5c3f1b8c499947aa089b21f26c69c84a71af77e2a352
3
+ size 6938043304
Alina-SDXL-step00002525.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1543d51164601fd2ed78fbb32a474a4c63f765ecc4b9d8392493b1415964bf36
3
+ size 6938043304
Alina-SDXL-step00004545.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a0fd34f03bc95c3ec648df88a96cf450de4ae44f8e2f5f6624e7b84d64ded924
3
+ size 6938043304
Alina-SDXL-step00006060.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:63354552fbbb2a6df5924636720b20d22f4c4bfc04ac8d41f139c304ac1958db
3
+ size 6938043304
Alina-SDXL-step00006565.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:071b4a6976ebdddf87ffefe6ceaf2a4a47ec0d221402dd81bf1863445125683d
3
+ size 6938043304
Alina-SDXL-step00008585.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9a712b42111da7af4d43f099146de963e62bf79ffad859aadacfc1929d907933
3
+ size 6938043304
Alina-SDXL-step00009090.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:82aecbd449b01cd887337495c225ababa3377e1d0619c8537fa227d498bc77fa
3
+ size 6938043304
Alina-SDXL-step00009595.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3b60084283da69642d91dc176c3a282d90baf95b6b2f854790d59514c3f6d58e
3
+ size 6938043304
Alina-SDXL.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:99c6e6641eef8a135aaeb8335c2d7982e16c9ad72390904e391ace393dcdba85
3
+ size 6938043304
sd_xl_base_1.0.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:31e35c80fc4829d14f90153f4c74cd59c90b779f6afe05a74cd6120b893f7e5b
3
+ size 6938078334
venv/lib/python3.10/site-packages/distutils-precedence.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7ea7ffef3fe2a117ee12c68ed6553617f0d7fd2f0590257c25c484959a3b7373
3
+ size 152
venv/lib/python3.10/site-packages/setuptools/__init__.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Extensions to the 'distutils' for large or complex distributions"""
2
+
3
+ from fnmatch import fnmatchcase
4
+ import functools
5
+ import os
6
+ import re
7
+
8
+ import _distutils_hack.override # noqa: F401
9
+
10
+ import distutils.core
11
+ from distutils.errors import DistutilsOptionError
12
+ from distutils.util import convert_path
13
+
14
+ from ._deprecation_warning import SetuptoolsDeprecationWarning
15
+
16
+ import setuptools.version
17
+ from setuptools.extension import Extension
18
+ from setuptools.dist import Distribution
19
+ from setuptools.depends import Require
20
+ from . import monkey
21
+
22
+
23
+ __all__ = [
24
+ 'setup',
25
+ 'Distribution',
26
+ 'Command',
27
+ 'Extension',
28
+ 'Require',
29
+ 'SetuptoolsDeprecationWarning',
30
+ 'find_packages',
31
+ 'find_namespace_packages',
32
+ ]
33
+
34
+ __version__ = setuptools.version.__version__
35
+
36
+ bootstrap_install_from = None
37
+
38
+
39
+ class PackageFinder:
40
+ """
41
+ Generate a list of all Python packages found within a directory
42
+ """
43
+
44
+ @classmethod
45
+ def find(cls, where='.', exclude=(), include=('*',)):
46
+ """Return a list all Python packages found within directory 'where'
47
+
48
+ 'where' is the root directory which will be searched for packages. It
49
+ should be supplied as a "cross-platform" (i.e. URL-style) path; it will
50
+ be converted to the appropriate local path syntax.
51
+
52
+ 'exclude' is a sequence of package names to exclude; '*' can be used
53
+ as a wildcard in the names, such that 'foo.*' will exclude all
54
+ subpackages of 'foo' (but not 'foo' itself).
55
+
56
+ 'include' is a sequence of package names to include. If it's
57
+ specified, only the named packages will be included. If it's not
58
+ specified, all found packages will be included. 'include' can contain
59
+ shell style wildcard patterns just like 'exclude'.
60
+ """
61
+
62
+ return list(
63
+ cls._find_packages_iter(
64
+ convert_path(where),
65
+ cls._build_filter('ez_setup', '*__pycache__', *exclude),
66
+ cls._build_filter(*include),
67
+ )
68
+ )
69
+
70
+ @classmethod
71
+ def _find_packages_iter(cls, where, exclude, include):
72
+ """
73
+ All the packages found in 'where' that pass the 'include' filter, but
74
+ not the 'exclude' filter.
75
+ """
76
+ for root, dirs, files in os.walk(where, followlinks=True):
77
+ # Copy dirs to iterate over it, then empty dirs.
78
+ all_dirs = dirs[:]
79
+ dirs[:] = []
80
+
81
+ for dir in all_dirs:
82
+ full_path = os.path.join(root, dir)
83
+ rel_path = os.path.relpath(full_path, where)
84
+ package = rel_path.replace(os.path.sep, '.')
85
+
86
+ # Skip directory trees that are not valid packages
87
+ if '.' in dir or not cls._looks_like_package(full_path):
88
+ continue
89
+
90
+ # Should this package be included?
91
+ if include(package) and not exclude(package):
92
+ yield package
93
+
94
+ # Keep searching subdirectories, as there may be more packages
95
+ # down there, even if the parent was excluded.
96
+ dirs.append(dir)
97
+
98
+ @staticmethod
99
+ def _looks_like_package(path):
100
+ """Does a directory look like a package?"""
101
+ return os.path.isfile(os.path.join(path, '__init__.py'))
102
+
103
+ @staticmethod
104
+ def _build_filter(*patterns):
105
+ """
106
+ Given a list of patterns, return a callable that will be true only if
107
+ the input matches at least one of the patterns.
108
+ """
109
+ return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns)
110
+
111
+
112
+ class PEP420PackageFinder(PackageFinder):
113
+ @staticmethod
114
+ def _looks_like_package(path):
115
+ return True
116
+
117
+
118
+ find_packages = PackageFinder.find
119
+ find_namespace_packages = PEP420PackageFinder.find
120
+
121
+
122
+ def _install_setup_requires(attrs):
123
+ # Note: do not use `setuptools.Distribution` directly, as
124
+ # our PEP 517 backend patch `distutils.core.Distribution`.
125
+ class MinimalDistribution(distutils.core.Distribution):
126
+ """
127
+ A minimal version of a distribution for supporting the
128
+ fetch_build_eggs interface.
129
+ """
130
+
131
+ def __init__(self, attrs):
132
+ _incl = 'dependency_links', 'setup_requires'
133
+ filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
134
+ distutils.core.Distribution.__init__(self, filtered)
135
+
136
+ def finalize_options(self):
137
+ """
138
+ Disable finalize_options to avoid building the working set.
139
+ Ref #2158.
140
+ """
141
+
142
+ dist = MinimalDistribution(attrs)
143
+
144
+ # Honor setup.cfg's options.
145
+ dist.parse_config_files(ignore_option_errors=True)
146
+ if dist.setup_requires:
147
+ dist.fetch_build_eggs(dist.setup_requires)
148
+
149
+
150
+ def setup(**attrs):
151
+ # Make sure we have any requirements needed to interpret 'attrs'.
152
+ _install_setup_requires(attrs)
153
+ return distutils.core.setup(**attrs)
154
+
155
+
156
+ setup.__doc__ = distutils.core.setup.__doc__
157
+
158
+
159
+ _Command = monkey.get_unpatched(distutils.core.Command)
160
+
161
+
162
+ class Command(_Command):
163
+ __doc__ = _Command.__doc__
164
+
165
+ command_consumes_arguments = False
166
+
167
+ def __init__(self, dist, **kw):
168
+ """
169
+ Construct the command for dist, updating
170
+ vars(self) with any keyword parameters.
171
+ """
172
+ _Command.__init__(self, dist)
173
+ vars(self).update(kw)
174
+
175
+ def _ensure_stringlike(self, option, what, default=None):
176
+ val = getattr(self, option)
177
+ if val is None:
178
+ setattr(self, option, default)
179
+ return default
180
+ elif not isinstance(val, str):
181
+ raise DistutilsOptionError(
182
+ "'%s' must be a %s (got `%s`)" % (option, what, val)
183
+ )
184
+ return val
185
+
186
+ def ensure_string_list(self, option):
187
+ r"""Ensure that 'option' is a list of strings. If 'option' is
188
+ currently a string, we split it either on /,\s*/ or /\s+/, so
189
+ "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become
190
+ ["foo", "bar", "baz"].
191
+ """
192
+ val = getattr(self, option)
193
+ if val is None:
194
+ return
195
+ elif isinstance(val, str):
196
+ setattr(self, option, re.split(r',\s*|\s+', val))
197
+ else:
198
+ if isinstance(val, list):
199
+ ok = all(isinstance(v, str) for v in val)
200
+ else:
201
+ ok = False
202
+ if not ok:
203
+ raise DistutilsOptionError(
204
+ "'%s' must be a list of strings (got %r)" % (option, val)
205
+ )
206
+
207
+ def reinitialize_command(self, command, reinit_subcommands=0, **kw):
208
+ cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
209
+ vars(cmd).update(kw)
210
+ return cmd
211
+
212
+
213
+ def _find_all_simple(path):
214
+ """
215
+ Find all files under 'path'
216
+ """
217
+ results = (
218
+ os.path.join(base, file)
219
+ for base, dirs, files in os.walk(path, followlinks=True)
220
+ for file in files
221
+ )
222
+ return filter(os.path.isfile, results)
223
+
224
+
225
+ def findall(dir=os.curdir):
226
+ """
227
+ Find all files under 'dir' and return the list of full filenames.
228
+ Unless dir is '.', return full filenames with dir prepended.
229
+ """
230
+ files = _find_all_simple(dir)
231
+ if dir == os.curdir:
232
+ make_rel = functools.partial(os.path.relpath, start=dir)
233
+ files = map(make_rel, files)
234
+ return list(files)
235
+
236
+
237
+ class sic(str):
238
+ """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)"""
239
+
240
+
241
+ # Apply monkey patches
242
+ monkey.patch_all()
venv/lib/python3.10/site-packages/setuptools/__pycache__/archive_util.cpython-310.pyc ADDED
Binary file (5.85 kB). View file
 
venv/lib/python3.10/site-packages/setuptools/__pycache__/glob.cpython-310.pyc ADDED
Binary file (3.74 kB). View file
 
venv/lib/python3.10/site-packages/setuptools/_deprecation_warning.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ class SetuptoolsDeprecationWarning(Warning):
2
+ """
3
+ Base class for warning deprecations in ``setuptools``
4
+
5
+ This class is not derived from ``DeprecationWarning``, and as such is
6
+ visible by default.
7
+ """
venv/lib/python3.10/site-packages/setuptools/_imp.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Re-implementation of find_module and get_frozen_object
3
+ from the deprecated imp module.
4
+ """
5
+
6
+ import os
7
+ import importlib.util
8
+ import importlib.machinery
9
+
10
+ from .py34compat import module_from_spec
11
+
12
+
13
+ PY_SOURCE = 1
14
+ PY_COMPILED = 2
15
+ C_EXTENSION = 3
16
+ C_BUILTIN = 6
17
+ PY_FROZEN = 7
18
+
19
+
20
+ def find_spec(module, paths):
21
+ finder = (
22
+ importlib.machinery.PathFinder().find_spec
23
+ if isinstance(paths, list) else
24
+ importlib.util.find_spec
25
+ )
26
+ return finder(module, paths)
27
+
28
+
29
+ def find_module(module, paths=None):
30
+ """Just like 'imp.find_module()', but with package support"""
31
+ spec = find_spec(module, paths)
32
+ if spec is None:
33
+ raise ImportError("Can't find %s" % module)
34
+ if not spec.has_location and hasattr(spec, 'submodule_search_locations'):
35
+ spec = importlib.util.spec_from_loader('__init__.py', spec.loader)
36
+
37
+ kind = -1
38
+ file = None
39
+ static = isinstance(spec.loader, type)
40
+ if spec.origin == 'frozen' or static and issubclass(
41
+ spec.loader, importlib.machinery.FrozenImporter):
42
+ kind = PY_FROZEN
43
+ path = None # imp compabilty
44
+ suffix = mode = '' # imp compatibility
45
+ elif spec.origin == 'built-in' or static and issubclass(
46
+ spec.loader, importlib.machinery.BuiltinImporter):
47
+ kind = C_BUILTIN
48
+ path = None # imp compabilty
49
+ suffix = mode = '' # imp compatibility
50
+ elif spec.has_location:
51
+ path = spec.origin
52
+ suffix = os.path.splitext(path)[1]
53
+ mode = 'r' if suffix in importlib.machinery.SOURCE_SUFFIXES else 'rb'
54
+
55
+ if suffix in importlib.machinery.SOURCE_SUFFIXES:
56
+ kind = PY_SOURCE
57
+ elif suffix in importlib.machinery.BYTECODE_SUFFIXES:
58
+ kind = PY_COMPILED
59
+ elif suffix in importlib.machinery.EXTENSION_SUFFIXES:
60
+ kind = C_EXTENSION
61
+
62
+ if kind in {PY_SOURCE, PY_COMPILED}:
63
+ file = open(path, mode)
64
+ else:
65
+ path = None
66
+ suffix = mode = ''
67
+
68
+ return file, path, (suffix, mode, kind)
69
+
70
+
71
+ def get_frozen_object(module, paths=None):
72
+ spec = find_spec(module, paths)
73
+ if not spec:
74
+ raise ImportError("Can't find %s" % module)
75
+ return spec.loader.get_code(module)
76
+
77
+
78
+ def get_module(module, paths, info):
79
+ spec = find_spec(module, paths)
80
+ if not spec:
81
+ raise ImportError("Can't find %s" % module)
82
+ return module_from_spec(spec)
venv/lib/python3.10/site-packages/setuptools/archive_util.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utilities for extracting common archive formats"""
2
+
3
+ import zipfile
4
+ import tarfile
5
+ import os
6
+ import shutil
7
+ import posixpath
8
+ import contextlib
9
+ from distutils.errors import DistutilsError
10
+
11
+ from pkg_resources import ensure_directory
12
+
13
+ __all__ = [
14
+ "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter",
15
+ "UnrecognizedFormat", "extraction_drivers", "unpack_directory",
16
+ ]
17
+
18
+
19
+ class UnrecognizedFormat(DistutilsError):
20
+ """Couldn't recognize the archive type"""
21
+
22
+
23
+ def default_filter(src, dst):
24
+ """The default progress/filter callback; returns True for all files"""
25
+ return dst
26
+
27
+
28
+ def unpack_archive(
29
+ filename, extract_dir, progress_filter=default_filter,
30
+ drivers=None):
31
+ """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
32
+
33
+ `progress_filter` is a function taking two arguments: a source path
34
+ internal to the archive ('/'-separated), and a filesystem path where it
35
+ will be extracted. The callback must return the desired extract path
36
+ (which may be the same as the one passed in), or else ``None`` to skip
37
+ that file or directory. The callback can thus be used to report on the
38
+ progress of the extraction, as well as to filter the items extracted or
39
+ alter their extraction paths.
40
+
41
+ `drivers`, if supplied, must be a non-empty sequence of functions with the
42
+ same signature as this function (minus the `drivers` argument), that raise
43
+ ``UnrecognizedFormat`` if they do not support extracting the designated
44
+ archive type. The `drivers` are tried in sequence until one is found that
45
+ does not raise an error, or until all are exhausted (in which case
46
+ ``UnrecognizedFormat`` is raised). If you do not supply a sequence of
47
+ drivers, the module's ``extraction_drivers`` constant will be used, which
48
+ means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that
49
+ order.
50
+ """
51
+ for driver in drivers or extraction_drivers:
52
+ try:
53
+ driver(filename, extract_dir, progress_filter)
54
+ except UnrecognizedFormat:
55
+ continue
56
+ else:
57
+ return
58
+ else:
59
+ raise UnrecognizedFormat(
60
+ "Not a recognized archive type: %s" % filename
61
+ )
62
+
63
+
64
+ def unpack_directory(filename, extract_dir, progress_filter=default_filter):
65
+ """"Unpack" a directory, using the same interface as for archives
66
+
67
+ Raises ``UnrecognizedFormat`` if `filename` is not a directory
68
+ """
69
+ if not os.path.isdir(filename):
70
+ raise UnrecognizedFormat("%s is not a directory" % filename)
71
+
72
+ paths = {
73
+ filename: ('', extract_dir),
74
+ }
75
+ for base, dirs, files in os.walk(filename):
76
+ src, dst = paths[base]
77
+ for d in dirs:
78
+ paths[os.path.join(base, d)] = src + d + '/', os.path.join(dst, d)
79
+ for f in files:
80
+ target = os.path.join(dst, f)
81
+ target = progress_filter(src + f, target)
82
+ if not target:
83
+ # skip non-files
84
+ continue
85
+ ensure_directory(target)
86
+ f = os.path.join(base, f)
87
+ shutil.copyfile(f, target)
88
+ shutil.copystat(f, target)
89
+
90
+
91
+ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
92
+ """Unpack zip `filename` to `extract_dir`
93
+
94
+ Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined
95
+ by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation
96
+ of the `progress_filter` argument.
97
+ """
98
+
99
+ if not zipfile.is_zipfile(filename):
100
+ raise UnrecognizedFormat("%s is not a zip file" % (filename,))
101
+
102
+ with zipfile.ZipFile(filename) as z:
103
+ for info in z.infolist():
104
+ name = info.filename
105
+
106
+ # don't extract absolute paths or ones with .. in them
107
+ if name.startswith('/') or '..' in name.split('/'):
108
+ continue
109
+
110
+ target = os.path.join(extract_dir, *name.split('/'))
111
+ target = progress_filter(name, target)
112
+ if not target:
113
+ continue
114
+ if name.endswith('/'):
115
+ # directory
116
+ ensure_directory(target)
117
+ else:
118
+ # file
119
+ ensure_directory(target)
120
+ data = z.read(info.filename)
121
+ with open(target, 'wb') as f:
122
+ f.write(data)
123
+ unix_attributes = info.external_attr >> 16
124
+ if unix_attributes:
125
+ os.chmod(target, unix_attributes)
126
+
127
+
128
+ def _resolve_tar_file_or_dir(tar_obj, tar_member_obj):
129
+ """Resolve any links and extract link targets as normal files."""
130
+ while tar_member_obj is not None and (
131
+ tar_member_obj.islnk() or tar_member_obj.issym()):
132
+ linkpath = tar_member_obj.linkname
133
+ if tar_member_obj.issym():
134
+ base = posixpath.dirname(tar_member_obj.name)
135
+ linkpath = posixpath.join(base, linkpath)
136
+ linkpath = posixpath.normpath(linkpath)
137
+ tar_member_obj = tar_obj._getmember(linkpath)
138
+
139
+ is_file_or_dir = (
140
+ tar_member_obj is not None and
141
+ (tar_member_obj.isfile() or tar_member_obj.isdir())
142
+ )
143
+ if is_file_or_dir:
144
+ return tar_member_obj
145
+
146
+ raise LookupError('Got unknown file type')
147
+
148
+
149
+ def _iter_open_tar(tar_obj, extract_dir, progress_filter):
150
+ """Emit member-destination pairs from a tar archive."""
151
+ # don't do any chowning!
152
+ tar_obj.chown = lambda *args: None
153
+
154
+ with contextlib.closing(tar_obj):
155
+ for member in tar_obj:
156
+ name = member.name
157
+ # don't extract absolute paths or ones with .. in them
158
+ if name.startswith('/') or '..' in name.split('/'):
159
+ continue
160
+
161
+ prelim_dst = os.path.join(extract_dir, *name.split('/'))
162
+
163
+ try:
164
+ member = _resolve_tar_file_or_dir(tar_obj, member)
165
+ except LookupError:
166
+ continue
167
+
168
+ final_dst = progress_filter(name, prelim_dst)
169
+ if not final_dst:
170
+ continue
171
+
172
+ if final_dst.endswith(os.sep):
173
+ final_dst = final_dst[:-1]
174
+
175
+ yield member, final_dst
176
+
177
+
178
+ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):
179
+ """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
180
+
181
+ Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined
182
+ by ``tarfile.open()``). See ``unpack_archive()`` for an explanation
183
+ of the `progress_filter` argument.
184
+ """
185
+ try:
186
+ tarobj = tarfile.open(filename)
187
+ except tarfile.TarError as e:
188
+ raise UnrecognizedFormat(
189
+ "%s is not a compressed or uncompressed tar file" % (filename,)
190
+ ) from e
191
+
192
+ for member, final_dst in _iter_open_tar(
193
+ tarobj, extract_dir, progress_filter,
194
+ ):
195
+ try:
196
+ # XXX Ugh
197
+ tarobj._extract_member(member, final_dst)
198
+ except tarfile.ExtractError:
199
+ # chown/chmod/mkfifo/mknode/makedev failed
200
+ pass
201
+
202
+ return True
203
+
204
+
205
+ extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile
venv/lib/python3.10/site-packages/setuptools/build_meta.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """A PEP 517 interface to setuptools
2
+
3
+ Previously, when a user or a command line tool (let's call it a "frontend")
4
+ needed to make a request of setuptools to take a certain action, for
5
+ example, generating a list of installation requirements, the frontend would
6
+ would call "setup.py egg_info" or "setup.py bdist_wheel" on the command line.
7
+
8
+ PEP 517 defines a different method of interfacing with setuptools. Rather
9
+ than calling "setup.py" directly, the frontend should:
10
+
11
+ 1. Set the current directory to the directory with a setup.py file
12
+ 2. Import this module into a safe python interpreter (one in which
13
+ setuptools can potentially set global variables or crash hard).
14
+ 3. Call one of the functions defined in PEP 517.
15
+
16
+ What each function does is defined in PEP 517. However, here is a "casual"
17
+ definition of the functions (this definition should not be relied on for
18
+ bug reports or API stability):
19
+
20
+ - `build_wheel`: build a wheel in the folder and return the basename
21
+ - `get_requires_for_build_wheel`: get the `setup_requires` to build
22
+ - `prepare_metadata_for_build_wheel`: get the `install_requires`
23
+ - `build_sdist`: build an sdist in the folder and return the basename
24
+ - `get_requires_for_build_sdist`: get the `setup_requires` to build
25
+
26
+ Again, this is not a formal definition! Just a "taste" of the module.
27
+ """
28
+
29
+ import io
30
+ import os
31
+ import sys
32
+ import tokenize
33
+ import shutil
34
+ import contextlib
35
+ import tempfile
36
+ import warnings
37
+
38
+ import setuptools
39
+ import distutils
40
+
41
+ from pkg_resources import parse_requirements
42
+
43
+ __all__ = ['get_requires_for_build_sdist',
44
+ 'get_requires_for_build_wheel',
45
+ 'prepare_metadata_for_build_wheel',
46
+ 'build_wheel',
47
+ 'build_sdist',
48
+ '__legacy__',
49
+ 'SetupRequirementsError']
50
+
51
+
52
+ class SetupRequirementsError(BaseException):
53
+ def __init__(self, specifiers):
54
+ self.specifiers = specifiers
55
+
56
+
57
+ class Distribution(setuptools.dist.Distribution):
58
+ def fetch_build_eggs(self, specifiers):
59
+ specifier_list = list(map(str, parse_requirements(specifiers)))
60
+
61
+ raise SetupRequirementsError(specifier_list)
62
+
63
+ @classmethod
64
+ @contextlib.contextmanager
65
+ def patch(cls):
66
+ """
67
+ Replace
68
+ distutils.dist.Distribution with this class
69
+ for the duration of this context.
70
+ """
71
+ orig = distutils.core.Distribution
72
+ distutils.core.Distribution = cls
73
+ try:
74
+ yield
75
+ finally:
76
+ distutils.core.Distribution = orig
77
+
78
+
79
+ @contextlib.contextmanager
80
+ def no_install_setup_requires():
81
+ """Temporarily disable installing setup_requires
82
+
83
+ Under PEP 517, the backend reports build dependencies to the frontend,
84
+ and the frontend is responsible for ensuring they're installed.
85
+ So setuptools (acting as a backend) should not try to install them.
86
+ """
87
+ orig = setuptools._install_setup_requires
88
+ setuptools._install_setup_requires = lambda attrs: None
89
+ try:
90
+ yield
91
+ finally:
92
+ setuptools._install_setup_requires = orig
93
+
94
+
95
+ def _get_immediate_subdirectories(a_dir):
96
+ return [name for name in os.listdir(a_dir)
97
+ if os.path.isdir(os.path.join(a_dir, name))]
98
+
99
+
100
+ def _file_with_extension(directory, extension):
101
+ matching = (
102
+ f for f in os.listdir(directory)
103
+ if f.endswith(extension)
104
+ )
105
+ try:
106
+ file, = matching
107
+ except ValueError:
108
+ raise ValueError(
109
+ 'No distribution was found. Ensure that `setup.py` '
110
+ 'is not empty and that it calls `setup()`.')
111
+ return file
112
+
113
+
114
+ def _open_setup_script(setup_script):
115
+ if not os.path.exists(setup_script):
116
+ # Supply a default setup.py
117
+ return io.StringIO(u"from setuptools import setup; setup()")
118
+
119
+ return getattr(tokenize, 'open', open)(setup_script)
120
+
121
+
122
+ @contextlib.contextmanager
123
+ def suppress_known_deprecation():
124
+ with warnings.catch_warnings():
125
+ warnings.filterwarnings('ignore', 'setup.py install is deprecated')
126
+ yield
127
+
128
+
129
+ class _BuildMetaBackend(object):
130
+
131
+ def _fix_config(self, config_settings):
132
+ config_settings = config_settings or {}
133
+ config_settings.setdefault('--global-option', [])
134
+ return config_settings
135
+
136
+ def _get_build_requires(self, config_settings, requirements):
137
+ config_settings = self._fix_config(config_settings)
138
+
139
+ sys.argv = sys.argv[:1] + ['egg_info'] + \
140
+ config_settings["--global-option"]
141
+ try:
142
+ with Distribution.patch():
143
+ self.run_setup()
144
+ except SetupRequirementsError as e:
145
+ requirements += e.specifiers
146
+
147
+ return requirements
148
+
149
+ def run_setup(self, setup_script='setup.py'):
150
+ # Note that we can reuse our build directory between calls
151
+ # Correctness comes first, then optimization later
152
+ __file__ = setup_script
153
+ __name__ = '__main__'
154
+
155
+ with _open_setup_script(__file__) as f:
156
+ code = f.read().replace(r'\r\n', r'\n')
157
+
158
+ exec(compile(code, __file__, 'exec'), locals())
159
+
160
+ def get_requires_for_build_wheel(self, config_settings=None):
161
+ config_settings = self._fix_config(config_settings)
162
+ return self._get_build_requires(
163
+ config_settings, requirements=['wheel'])
164
+
165
+ def get_requires_for_build_sdist(self, config_settings=None):
166
+ config_settings = self._fix_config(config_settings)
167
+ return self._get_build_requires(config_settings, requirements=[])
168
+
169
+ def prepare_metadata_for_build_wheel(self, metadata_directory,
170
+ config_settings=None):
171
+ sys.argv = sys.argv[:1] + [
172
+ 'dist_info', '--egg-base', metadata_directory]
173
+ with no_install_setup_requires():
174
+ self.run_setup()
175
+
176
+ dist_info_directory = metadata_directory
177
+ while True:
178
+ dist_infos = [f for f in os.listdir(dist_info_directory)
179
+ if f.endswith('.dist-info')]
180
+
181
+ if (
182
+ len(dist_infos) == 0 and
183
+ len(_get_immediate_subdirectories(dist_info_directory)) == 1
184
+ ):
185
+
186
+ dist_info_directory = os.path.join(
187
+ dist_info_directory, os.listdir(dist_info_directory)[0])
188
+ continue
189
+
190
+ assert len(dist_infos) == 1
191
+ break
192
+
193
+ # PEP 517 requires that the .dist-info directory be placed in the
194
+ # metadata_directory. To comply, we MUST copy the directory to the root
195
+ if dist_info_directory != metadata_directory:
196
+ shutil.move(
197
+ os.path.join(dist_info_directory, dist_infos[0]),
198
+ metadata_directory)
199
+ shutil.rmtree(dist_info_directory, ignore_errors=True)
200
+
201
+ return dist_infos[0]
202
+
203
+ def _build_with_temp_dir(self, setup_command, result_extension,
204
+ result_directory, config_settings):
205
+ config_settings = self._fix_config(config_settings)
206
+ result_directory = os.path.abspath(result_directory)
207
+
208
+ # Build in a temporary directory, then copy to the target.
209
+ os.makedirs(result_directory, exist_ok=True)
210
+ with tempfile.TemporaryDirectory(dir=result_directory) as tmp_dist_dir:
211
+ sys.argv = (sys.argv[:1] + setup_command +
212
+ ['--dist-dir', tmp_dist_dir] +
213
+ config_settings["--global-option"])
214
+ with no_install_setup_requires():
215
+ self.run_setup()
216
+
217
+ result_basename = _file_with_extension(
218
+ tmp_dist_dir, result_extension)
219
+ result_path = os.path.join(result_directory, result_basename)
220
+ if os.path.exists(result_path):
221
+ # os.rename will fail overwriting on non-Unix.
222
+ os.remove(result_path)
223
+ os.rename(os.path.join(tmp_dist_dir, result_basename), result_path)
224
+
225
+ return result_basename
226
+
227
+ def build_wheel(self, wheel_directory, config_settings=None,
228
+ metadata_directory=None):
229
+ with suppress_known_deprecation():
230
+ return self._build_with_temp_dir(['bdist_wheel'], '.whl',
231
+ wheel_directory, config_settings)
232
+
233
+ def build_sdist(self, sdist_directory, config_settings=None):
234
+ return self._build_with_temp_dir(['sdist', '--formats', 'gztar'],
235
+ '.tar.gz', sdist_directory,
236
+ config_settings)
237
+
238
+
239
+ class _BuildMetaLegacyBackend(_BuildMetaBackend):
240
+ """Compatibility backend for setuptools
241
+
242
+ This is a version of setuptools.build_meta that endeavors
243
+ to maintain backwards
244
+ compatibility with pre-PEP 517 modes of invocation. It
245
+ exists as a temporary
246
+ bridge between the old packaging mechanism and the new
247
+ packaging mechanism,
248
+ and will eventually be removed.
249
+ """
250
+ def run_setup(self, setup_script='setup.py'):
251
+ # In order to maintain compatibility with scripts assuming that
252
+ # the setup.py script is in a directory on the PYTHONPATH, inject
253
+ # '' into sys.path. (pypa/setuptools#1642)
254
+ sys_path = list(sys.path) # Save the original path
255
+
256
+ script_dir = os.path.dirname(os.path.abspath(setup_script))
257
+ if script_dir not in sys.path:
258
+ sys.path.insert(0, script_dir)
259
+
260
+ # Some setup.py scripts (e.g. in pygame and numpy) use sys.argv[0] to
261
+ # get the directory of the source code. They expect it to refer to the
262
+ # setup.py script.
263
+ sys_argv_0 = sys.argv[0]
264
+ sys.argv[0] = setup_script
265
+
266
+ try:
267
+ super(_BuildMetaLegacyBackend,
268
+ self).run_setup(setup_script=setup_script)
269
+ finally:
270
+ # While PEP 517 frontends should be calling each hook in a fresh
271
+ # subprocess according to the standard (and thus it should not be
272
+ # strictly necessary to restore the old sys.path), we'll restore
273
+ # the original path so that the path manipulation does not persist
274
+ # within the hook after run_setup is called.
275
+ sys.path[:] = sys_path
276
+ sys.argv[0] = sys_argv_0
277
+
278
+
279
+ # The primary backend
280
+ _BACKEND = _BuildMetaBackend()
281
+
282
+ get_requires_for_build_wheel = _BACKEND.get_requires_for_build_wheel
283
+ get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist
284
+ prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel
285
+ build_wheel = _BACKEND.build_wheel
286
+ build_sdist = _BACKEND.build_sdist
287
+
288
+
289
+ # The legacy backend
290
+ __legacy__ = _BuildMetaLegacyBackend()
venv/lib/python3.10/site-packages/setuptools/cli-32.exe ADDED
Binary file (65.5 kB). View file
 
venv/lib/python3.10/site-packages/setuptools/cli-64.exe ADDED
Binary file (74.8 kB). View file
 
venv/lib/python3.10/site-packages/setuptools/cli-arm64.exe ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a3d6a6c68c2e759f7c36f35687f6b60d163c2e1a0846a4c07a4c4006a96d88c7
3
+ size 137216
venv/lib/python3.10/site-packages/setuptools/cli.exe ADDED
Binary file (65.5 kB). View file
 
venv/lib/python3.10/site-packages/setuptools/config.py ADDED
@@ -0,0 +1,751 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import io
3
+ import os
4
+ import sys
5
+
6
+ import warnings
7
+ import functools
8
+ import importlib
9
+ from collections import defaultdict
10
+ from functools import partial
11
+ from functools import wraps
12
+ from glob import iglob
13
+ import contextlib
14
+
15
+ from distutils.errors import DistutilsOptionError, DistutilsFileError
16
+ from setuptools.extern.packaging.version import Version, InvalidVersion
17
+ from setuptools.extern.packaging.specifiers import SpecifierSet
18
+
19
+
20
+ class StaticModule:
21
+ """
22
+ Attempt to load the module by the name
23
+ """
24
+
25
+ def __init__(self, name):
26
+ spec = importlib.util.find_spec(name)
27
+ with open(spec.origin) as strm:
28
+ src = strm.read()
29
+ module = ast.parse(src)
30
+ vars(self).update(locals())
31
+ del self.self
32
+
33
+ def __getattr__(self, attr):
34
+ try:
35
+ return next(
36
+ ast.literal_eval(statement.value)
37
+ for statement in self.module.body
38
+ if isinstance(statement, ast.Assign)
39
+ for target in statement.targets
40
+ if isinstance(target, ast.Name) and target.id == attr
41
+ )
42
+ except Exception as e:
43
+ raise AttributeError(
44
+ "{self.name} has no attribute {attr}".format(**locals())
45
+ ) from e
46
+
47
+
48
+ @contextlib.contextmanager
49
+ def patch_path(path):
50
+ """
51
+ Add path to front of sys.path for the duration of the context.
52
+ """
53
+ try:
54
+ sys.path.insert(0, path)
55
+ yield
56
+ finally:
57
+ sys.path.remove(path)
58
+
59
+
60
+ def read_configuration(filepath, find_others=False, ignore_option_errors=False):
61
+ """Read given configuration file and returns options from it as a dict.
62
+
63
+ :param str|unicode filepath: Path to configuration file
64
+ to get options from.
65
+
66
+ :param bool find_others: Whether to search for other configuration files
67
+ which could be on in various places.
68
+
69
+ :param bool ignore_option_errors: Whether to silently ignore
70
+ options, values of which could not be resolved (e.g. due to exceptions
71
+ in directives such as file:, attr:, etc.).
72
+ If False exceptions are propagated as expected.
73
+
74
+ :rtype: dict
75
+ """
76
+ from setuptools.dist import Distribution, _Distribution
77
+
78
+ filepath = os.path.abspath(filepath)
79
+
80
+ if not os.path.isfile(filepath):
81
+ raise DistutilsFileError('Configuration file %s does not exist.' % filepath)
82
+
83
+ current_directory = os.getcwd()
84
+ os.chdir(os.path.dirname(filepath))
85
+
86
+ try:
87
+ dist = Distribution()
88
+
89
+ filenames = dist.find_config_files() if find_others else []
90
+ if filepath not in filenames:
91
+ filenames.append(filepath)
92
+
93
+ _Distribution.parse_config_files(dist, filenames=filenames)
94
+
95
+ handlers = parse_configuration(
96
+ dist, dist.command_options, ignore_option_errors=ignore_option_errors
97
+ )
98
+
99
+ finally:
100
+ os.chdir(current_directory)
101
+
102
+ return configuration_to_dict(handlers)
103
+
104
+
105
+ def _get_option(target_obj, key):
106
+ """
107
+ Given a target object and option key, get that option from
108
+ the target object, either through a get_{key} method or
109
+ from an attribute directly.
110
+ """
111
+ getter_name = 'get_{key}'.format(**locals())
112
+ by_attribute = functools.partial(getattr, target_obj, key)
113
+ getter = getattr(target_obj, getter_name, by_attribute)
114
+ return getter()
115
+
116
+
117
+ def configuration_to_dict(handlers):
118
+ """Returns configuration data gathered by given handlers as a dict.
119
+
120
+ :param list[ConfigHandler] handlers: Handlers list,
121
+ usually from parse_configuration()
122
+
123
+ :rtype: dict
124
+ """
125
+ config_dict = defaultdict(dict)
126
+
127
+ for handler in handlers:
128
+ for option in handler.set_options:
129
+ value = _get_option(handler.target_obj, option)
130
+ config_dict[handler.section_prefix][option] = value
131
+
132
+ return config_dict
133
+
134
+
135
+ def parse_configuration(distribution, command_options, ignore_option_errors=False):
136
+ """Performs additional parsing of configuration options
137
+ for a distribution.
138
+
139
+ Returns a list of used option handlers.
140
+
141
+ :param Distribution distribution:
142
+ :param dict command_options:
143
+ :param bool ignore_option_errors: Whether to silently ignore
144
+ options, values of which could not be resolved (e.g. due to exceptions
145
+ in directives such as file:, attr:, etc.).
146
+ If False exceptions are propagated as expected.
147
+ :rtype: list
148
+ """
149
+ options = ConfigOptionsHandler(distribution, command_options, ignore_option_errors)
150
+ options.parse()
151
+
152
+ meta = ConfigMetadataHandler(
153
+ distribution.metadata,
154
+ command_options,
155
+ ignore_option_errors,
156
+ distribution.package_dir,
157
+ )
158
+ meta.parse()
159
+
160
+ return meta, options
161
+
162
+
163
+ class ConfigHandler:
164
+ """Handles metadata supplied in configuration files."""
165
+
166
+ section_prefix = None
167
+ """Prefix for config sections handled by this handler.
168
+ Must be provided by class heirs.
169
+
170
+ """
171
+
172
+ aliases = {}
173
+ """Options aliases.
174
+ For compatibility with various packages. E.g.: d2to1 and pbr.
175
+ Note: `-` in keys is replaced with `_` by config parser.
176
+
177
+ """
178
+
179
+ def __init__(self, target_obj, options, ignore_option_errors=False):
180
+ sections = {}
181
+
182
+ section_prefix = self.section_prefix
183
+ for section_name, section_options in options.items():
184
+ if not section_name.startswith(section_prefix):
185
+ continue
186
+
187
+ section_name = section_name.replace(section_prefix, '').strip('.')
188
+ sections[section_name] = section_options
189
+
190
+ self.ignore_option_errors = ignore_option_errors
191
+ self.target_obj = target_obj
192
+ self.sections = sections
193
+ self.set_options = []
194
+
195
+ @property
196
+ def parsers(self):
197
+ """Metadata item name to parser function mapping."""
198
+ raise NotImplementedError(
199
+ '%s must provide .parsers property' % self.__class__.__name__
200
+ )
201
+
202
+ def __setitem__(self, option_name, value):
203
+ unknown = tuple()
204
+ target_obj = self.target_obj
205
+
206
+ # Translate alias into real name.
207
+ option_name = self.aliases.get(option_name, option_name)
208
+
209
+ current_value = getattr(target_obj, option_name, unknown)
210
+
211
+ if current_value is unknown:
212
+ raise KeyError(option_name)
213
+
214
+ if current_value:
215
+ # Already inhabited. Skipping.
216
+ return
217
+
218
+ skip_option = False
219
+ parser = self.parsers.get(option_name)
220
+ if parser:
221
+ try:
222
+ value = parser(value)
223
+
224
+ except Exception:
225
+ skip_option = True
226
+ if not self.ignore_option_errors:
227
+ raise
228
+
229
+ if skip_option:
230
+ return
231
+
232
+ setter = getattr(target_obj, 'set_%s' % option_name, None)
233
+ if setter is None:
234
+ setattr(target_obj, option_name, value)
235
+ else:
236
+ setter(value)
237
+
238
+ self.set_options.append(option_name)
239
+
240
+ @classmethod
241
+ def _parse_list(cls, value, separator=','):
242
+ """Represents value as a list.
243
+
244
+ Value is split either by separator (defaults to comma) or by lines.
245
+
246
+ :param value:
247
+ :param separator: List items separator character.
248
+ :rtype: list
249
+ """
250
+ if isinstance(value, list): # _get_parser_compound case
251
+ return value
252
+
253
+ if '\n' in value:
254
+ value = value.splitlines()
255
+ else:
256
+ value = value.split(separator)
257
+
258
+ return [chunk.strip() for chunk in value if chunk.strip()]
259
+
260
+ @classmethod
261
+ def _parse_list_glob(cls, value, separator=','):
262
+ """Equivalent to _parse_list() but expands any glob patterns using glob().
263
+
264
+ However, unlike with glob() calls, the results remain relative paths.
265
+
266
+ :param value:
267
+ :param separator: List items separator character.
268
+ :rtype: list
269
+ """
270
+ glob_characters = ('*', '?', '[', ']', '{', '}')
271
+ values = cls._parse_list(value, separator=separator)
272
+ expanded_values = []
273
+ for value in values:
274
+
275
+ # Has globby characters?
276
+ if any(char in value for char in glob_characters):
277
+ # then expand the glob pattern while keeping paths *relative*:
278
+ expanded_values.extend(sorted(
279
+ os.path.relpath(path, os.getcwd())
280
+ for path in iglob(os.path.abspath(value))))
281
+
282
+ else:
283
+ # take the value as-is:
284
+ expanded_values.append(value)
285
+
286
+ return expanded_values
287
+
288
+ @classmethod
289
+ def _parse_dict(cls, value):
290
+ """Represents value as a dict.
291
+
292
+ :param value:
293
+ :rtype: dict
294
+ """
295
+ separator = '='
296
+ result = {}
297
+ for line in cls._parse_list(value):
298
+ key, sep, val = line.partition(separator)
299
+ if sep != separator:
300
+ raise DistutilsOptionError(
301
+ 'Unable to parse option value to dict: %s' % value
302
+ )
303
+ result[key.strip()] = val.strip()
304
+
305
+ return result
306
+
307
+ @classmethod
308
+ def _parse_bool(cls, value):
309
+ """Represents value as boolean.
310
+
311
+ :param value:
312
+ :rtype: bool
313
+ """
314
+ value = value.lower()
315
+ return value in ('1', 'true', 'yes')
316
+
317
+ @classmethod
318
+ def _exclude_files_parser(cls, key):
319
+ """Returns a parser function to make sure field inputs
320
+ are not files.
321
+
322
+ Parses a value after getting the key so error messages are
323
+ more informative.
324
+
325
+ :param key:
326
+ :rtype: callable
327
+ """
328
+
329
+ def parser(value):
330
+ exclude_directive = 'file:'
331
+ if value.startswith(exclude_directive):
332
+ raise ValueError(
333
+ 'Only strings are accepted for the {0} field, '
334
+ 'files are not accepted'.format(key)
335
+ )
336
+ return value
337
+
338
+ return parser
339
+
340
+ @classmethod
341
+ def _parse_file(cls, value):
342
+ """Represents value as a string, allowing including text
343
+ from nearest files using `file:` directive.
344
+
345
+ Directive is sandboxed and won't reach anything outside
346
+ directory with setup.py.
347
+
348
+ Examples:
349
+ file: README.rst, CHANGELOG.md, src/file.txt
350
+
351
+ :param str value:
352
+ :rtype: str
353
+ """
354
+ include_directive = 'file:'
355
+
356
+ if not isinstance(value, str):
357
+ return value
358
+
359
+ if not value.startswith(include_directive):
360
+ return value
361
+
362
+ spec = value[len(include_directive) :]
363
+ filepaths = (os.path.abspath(path.strip()) for path in spec.split(','))
364
+ return '\n'.join(
365
+ cls._read_file(path)
366
+ for path in filepaths
367
+ if (cls._assert_local(path) or True) and os.path.isfile(path)
368
+ )
369
+
370
+ @staticmethod
371
+ def _assert_local(filepath):
372
+ if not filepath.startswith(os.getcwd()):
373
+ raise DistutilsOptionError('`file:` directive can not access %s' % filepath)
374
+
375
+ @staticmethod
376
+ def _read_file(filepath):
377
+ with io.open(filepath, encoding='utf-8') as f:
378
+ return f.read()
379
+
380
+ @classmethod
381
+ def _parse_attr(cls, value, package_dir=None):
382
+ """Represents value as a module attribute.
383
+
384
+ Examples:
385
+ attr: package.attr
386
+ attr: package.module.attr
387
+
388
+ :param str value:
389
+ :rtype: str
390
+ """
391
+ attr_directive = 'attr:'
392
+ if not value.startswith(attr_directive):
393
+ return value
394
+
395
+ attrs_path = value.replace(attr_directive, '').strip().split('.')
396
+ attr_name = attrs_path.pop()
397
+
398
+ module_name = '.'.join(attrs_path)
399
+ module_name = module_name or '__init__'
400
+
401
+ parent_path = os.getcwd()
402
+ if package_dir:
403
+ if attrs_path[0] in package_dir:
404
+ # A custom path was specified for the module we want to import
405
+ custom_path = package_dir[attrs_path[0]]
406
+ parts = custom_path.rsplit('/', 1)
407
+ if len(parts) > 1:
408
+ parent_path = os.path.join(os.getcwd(), parts[0])
409
+ module_name = parts[1]
410
+ else:
411
+ module_name = custom_path
412
+ elif '' in package_dir:
413
+ # A custom parent directory was specified for all root modules
414
+ parent_path = os.path.join(os.getcwd(), package_dir[''])
415
+
416
+ with patch_path(parent_path):
417
+ try:
418
+ # attempt to load value statically
419
+ return getattr(StaticModule(module_name), attr_name)
420
+ except Exception:
421
+ # fallback to simple import
422
+ module = importlib.import_module(module_name)
423
+
424
+ return getattr(module, attr_name)
425
+
426
+ @classmethod
427
+ def _get_parser_compound(cls, *parse_methods):
428
+ """Returns parser function to represents value as a list.
429
+
430
+ Parses a value applying given methods one after another.
431
+
432
+ :param parse_methods:
433
+ :rtype: callable
434
+ """
435
+
436
+ def parse(value):
437
+ parsed = value
438
+
439
+ for method in parse_methods:
440
+ parsed = method(parsed)
441
+
442
+ return parsed
443
+
444
+ return parse
445
+
446
+ @classmethod
447
+ def _parse_section_to_dict(cls, section_options, values_parser=None):
448
+ """Parses section options into a dictionary.
449
+
450
+ Optionally applies a given parser to values.
451
+
452
+ :param dict section_options:
453
+ :param callable values_parser:
454
+ :rtype: dict
455
+ """
456
+ value = {}
457
+ values_parser = values_parser or (lambda val: val)
458
+ for key, (_, val) in section_options.items():
459
+ value[key] = values_parser(val)
460
+ return value
461
+
462
+ def parse_section(self, section_options):
463
+ """Parses configuration file section.
464
+
465
+ :param dict section_options:
466
+ """
467
+ for (name, (_, value)) in section_options.items():
468
+ try:
469
+ self[name] = value
470
+
471
+ except KeyError:
472
+ pass # Keep silent for a new option may appear anytime.
473
+
474
+ def parse(self):
475
+ """Parses configuration file items from one
476
+ or more related sections.
477
+
478
+ """
479
+ for section_name, section_options in self.sections.items():
480
+
481
+ method_postfix = ''
482
+ if section_name: # [section.option] variant
483
+ method_postfix = '_%s' % section_name
484
+
485
+ section_parser_method = getattr(
486
+ self,
487
+ # Dots in section names are translated into dunderscores.
488
+ ('parse_section%s' % method_postfix).replace('.', '__'),
489
+ None,
490
+ )
491
+
492
+ if section_parser_method is None:
493
+ raise DistutilsOptionError(
494
+ 'Unsupported distribution option section: [%s.%s]'
495
+ % (self.section_prefix, section_name)
496
+ )
497
+
498
+ section_parser_method(section_options)
499
+
500
+ def _deprecated_config_handler(self, func, msg, warning_class):
501
+ """this function will wrap around parameters that are deprecated
502
+
503
+ :param msg: deprecation message
504
+ :param warning_class: class of warning exception to be raised
505
+ :param func: function to be wrapped around
506
+ """
507
+
508
+ @wraps(func)
509
+ def config_handler(*args, **kwargs):
510
+ warnings.warn(msg, warning_class)
511
+ return func(*args, **kwargs)
512
+
513
+ return config_handler
514
+
515
+
516
+ class ConfigMetadataHandler(ConfigHandler):
517
+
518
+ section_prefix = 'metadata'
519
+
520
+ aliases = {
521
+ 'home_page': 'url',
522
+ 'summary': 'description',
523
+ 'classifier': 'classifiers',
524
+ 'platform': 'platforms',
525
+ }
526
+
527
+ strict_mode = False
528
+ """We need to keep it loose, to be partially compatible with
529
+ `pbr` and `d2to1` packages which also uses `metadata` section.
530
+
531
+ """
532
+
533
+ def __init__(
534
+ self, target_obj, options, ignore_option_errors=False, package_dir=None
535
+ ):
536
+ super(ConfigMetadataHandler, self).__init__(
537
+ target_obj, options, ignore_option_errors
538
+ )
539
+ self.package_dir = package_dir
540
+
541
+ @property
542
+ def parsers(self):
543
+ """Metadata item name to parser function mapping."""
544
+ parse_list = self._parse_list
545
+ parse_file = self._parse_file
546
+ parse_dict = self._parse_dict
547
+ exclude_files_parser = self._exclude_files_parser
548
+
549
+ return {
550
+ 'platforms': parse_list,
551
+ 'keywords': parse_list,
552
+ 'provides': parse_list,
553
+ 'requires': self._deprecated_config_handler(
554
+ parse_list,
555
+ "The requires parameter is deprecated, please use "
556
+ "install_requires for runtime dependencies.",
557
+ DeprecationWarning,
558
+ ),
559
+ 'obsoletes': parse_list,
560
+ 'classifiers': self._get_parser_compound(parse_file, parse_list),
561
+ 'license': exclude_files_parser('license'),
562
+ 'license_file': self._deprecated_config_handler(
563
+ exclude_files_parser('license_file'),
564
+ "The license_file parameter is deprecated, "
565
+ "use license_files instead.",
566
+ DeprecationWarning,
567
+ ),
568
+ 'license_files': parse_list,
569
+ 'description': parse_file,
570
+ 'long_description': parse_file,
571
+ 'version': self._parse_version,
572
+ 'project_urls': parse_dict,
573
+ }
574
+
575
+ def _parse_version(self, value):
576
+ """Parses `version` option value.
577
+
578
+ :param value:
579
+ :rtype: str
580
+
581
+ """
582
+ version = self._parse_file(value)
583
+
584
+ if version != value:
585
+ version = version.strip()
586
+ # Be strict about versions loaded from file because it's easy to
587
+ # accidentally include newlines and other unintended content
588
+ try:
589
+ Version(version)
590
+ except InvalidVersion:
591
+ tmpl = (
592
+ 'Version loaded from {value} does not '
593
+ 'comply with PEP 440: {version}'
594
+ )
595
+ raise DistutilsOptionError(tmpl.format(**locals()))
596
+
597
+ return version
598
+
599
+ version = self._parse_attr(value, self.package_dir)
600
+
601
+ if callable(version):
602
+ version = version()
603
+
604
+ if not isinstance(version, str):
605
+ if hasattr(version, '__iter__'):
606
+ version = '.'.join(map(str, version))
607
+ else:
608
+ version = '%s' % version
609
+
610
+ return version
611
+
612
+
613
+ class ConfigOptionsHandler(ConfigHandler):
614
+
615
+ section_prefix = 'options'
616
+
617
+ @property
618
+ def parsers(self):
619
+ """Metadata item name to parser function mapping."""
620
+ parse_list = self._parse_list
621
+ parse_list_semicolon = partial(self._parse_list, separator=';')
622
+ parse_bool = self._parse_bool
623
+ parse_dict = self._parse_dict
624
+ parse_cmdclass = self._parse_cmdclass
625
+
626
+ return {
627
+ 'zip_safe': parse_bool,
628
+ 'include_package_data': parse_bool,
629
+ 'package_dir': parse_dict,
630
+ 'scripts': parse_list,
631
+ 'eager_resources': parse_list,
632
+ 'dependency_links': parse_list,
633
+ 'namespace_packages': parse_list,
634
+ 'install_requires': parse_list_semicolon,
635
+ 'setup_requires': parse_list_semicolon,
636
+ 'tests_require': parse_list_semicolon,
637
+ 'packages': self._parse_packages,
638
+ 'entry_points': self._parse_file,
639
+ 'py_modules': parse_list,
640
+ 'python_requires': SpecifierSet,
641
+ 'cmdclass': parse_cmdclass,
642
+ }
643
+
644
+ def _parse_cmdclass(self, value):
645
+ def resolve_class(qualified_class_name):
646
+ idx = qualified_class_name.rfind('.')
647
+ class_name = qualified_class_name[idx + 1 :]
648
+ pkg_name = qualified_class_name[:idx]
649
+
650
+ module = __import__(pkg_name)
651
+
652
+ return getattr(module, class_name)
653
+
654
+ return {k: resolve_class(v) for k, v in self._parse_dict(value).items()}
655
+
656
+ def _parse_packages(self, value):
657
+ """Parses `packages` option value.
658
+
659
+ :param value:
660
+ :rtype: list
661
+ """
662
+ find_directives = ['find:', 'find_namespace:']
663
+ trimmed_value = value.strip()
664
+
665
+ if trimmed_value not in find_directives:
666
+ return self._parse_list(value)
667
+
668
+ findns = trimmed_value == find_directives[1]
669
+
670
+ # Read function arguments from a dedicated section.
671
+ find_kwargs = self.parse_section_packages__find(
672
+ self.sections.get('packages.find', {})
673
+ )
674
+
675
+ if findns:
676
+ from setuptools import find_namespace_packages as find_packages
677
+ else:
678
+ from setuptools import find_packages
679
+
680
+ return find_packages(**find_kwargs)
681
+
682
+ def parse_section_packages__find(self, section_options):
683
+ """Parses `packages.find` configuration file section.
684
+
685
+ To be used in conjunction with _parse_packages().
686
+
687
+ :param dict section_options:
688
+ """
689
+ section_data = self._parse_section_to_dict(section_options, self._parse_list)
690
+
691
+ valid_keys = ['where', 'include', 'exclude']
692
+
693
+ find_kwargs = dict(
694
+ [(k, v) for k, v in section_data.items() if k in valid_keys and v]
695
+ )
696
+
697
+ where = find_kwargs.get('where')
698
+ if where is not None:
699
+ find_kwargs['where'] = where[0] # cast list to single val
700
+
701
+ return find_kwargs
702
+
703
+ def parse_section_entry_points(self, section_options):
704
+ """Parses `entry_points` configuration file section.
705
+
706
+ :param dict section_options:
707
+ """
708
+ parsed = self._parse_section_to_dict(section_options, self._parse_list)
709
+ self['entry_points'] = parsed
710
+
711
+ def _parse_package_data(self, section_options):
712
+ parsed = self._parse_section_to_dict(section_options, self._parse_list)
713
+
714
+ root = parsed.get('*')
715
+ if root:
716
+ parsed[''] = root
717
+ del parsed['*']
718
+
719
+ return parsed
720
+
721
+ def parse_section_package_data(self, section_options):
722
+ """Parses `package_data` configuration file section.
723
+
724
+ :param dict section_options:
725
+ """
726
+ self['package_data'] = self._parse_package_data(section_options)
727
+
728
+ def parse_section_exclude_package_data(self, section_options):
729
+ """Parses `exclude_package_data` configuration file section.
730
+
731
+ :param dict section_options:
732
+ """
733
+ self['exclude_package_data'] = self._parse_package_data(section_options)
734
+
735
+ def parse_section_extras_require(self, section_options):
736
+ """Parses `extras_require` configuration file section.
737
+
738
+ :param dict section_options:
739
+ """
740
+ parse_list = partial(self._parse_list, separator=';')
741
+ self['extras_require'] = self._parse_section_to_dict(
742
+ section_options, parse_list
743
+ )
744
+
745
+ def parse_section_data_files(self, section_options):
746
+ """Parses `data_files` configuration file section.
747
+
748
+ :param dict section_options:
749
+ """
750
+ parsed = self._parse_section_to_dict(section_options, self._parse_list_glob)
751
+ self['data_files'] = [(k, v) for k, v in parsed.items()]
venv/lib/python3.10/site-packages/setuptools/dep_util.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from distutils.dep_util import newer_group
2
+
3
+
4
+ # yes, this is was almost entirely copy-pasted from
5
+ # 'newer_pairwise()', this is just another convenience
6
+ # function.
7
+ def newer_pairwise_group(sources_groups, targets):
8
+ """Walk both arguments in parallel, testing if each source group is newer
9
+ than its corresponding target. Returns a pair of lists (sources_groups,
10
+ targets) where sources is newer than target, according to the semantics
11
+ of 'newer_group()'.
12
+ """
13
+ if len(sources_groups) != len(targets):
14
+ raise ValueError(
15
+ "'sources_group' and 'targets' must be the same length")
16
+
17
+ # build a pair of lists (sources_groups, targets) where source is newer
18
+ n_sources = []
19
+ n_targets = []
20
+ for i in range(len(sources_groups)):
21
+ if newer_group(sources_groups[i], targets[i]):
22
+ n_sources.append(sources_groups[i])
23
+ n_targets.append(targets[i])
24
+
25
+ return n_sources, n_targets
venv/lib/python3.10/site-packages/setuptools/depends.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import marshal
3
+ import contextlib
4
+ import dis
5
+
6
+ from setuptools.extern.packaging import version
7
+
8
+ from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
9
+ from . import _imp
10
+
11
+
12
+ __all__ = [
13
+ 'Require', 'find_module', 'get_module_constant', 'extract_constant'
14
+ ]
15
+
16
+
17
+ class Require:
18
+ """A prerequisite to building or installing a distribution"""
19
+
20
+ def __init__(
21
+ self, name, requested_version, module, homepage='',
22
+ attribute=None, format=None):
23
+
24
+ if format is None and requested_version is not None:
25
+ format = version.Version
26
+
27
+ if format is not None:
28
+ requested_version = format(requested_version)
29
+ if attribute is None:
30
+ attribute = '__version__'
31
+
32
+ self.__dict__.update(locals())
33
+ del self.self
34
+
35
+ def full_name(self):
36
+ """Return full package/distribution name, w/version"""
37
+ if self.requested_version is not None:
38
+ return '%s-%s' % (self.name, self.requested_version)
39
+ return self.name
40
+
41
+ def version_ok(self, version):
42
+ """Is 'version' sufficiently up-to-date?"""
43
+ return self.attribute is None or self.format is None or \
44
+ str(version) != "unknown" and self.format(version) >= self.requested_version
45
+
46
+ def get_version(self, paths=None, default="unknown"):
47
+ """Get version number of installed module, 'None', or 'default'
48
+
49
+ Search 'paths' for module. If not found, return 'None'. If found,
50
+ return the extracted version attribute, or 'default' if no version
51
+ attribute was specified, or the value cannot be determined without
52
+ importing the module. The version is formatted according to the
53
+ requirement's version format (if any), unless it is 'None' or the
54
+ supplied 'default'.
55
+ """
56
+
57
+ if self.attribute is None:
58
+ try:
59
+ f, p, i = find_module(self.module, paths)
60
+ if f:
61
+ f.close()
62
+ return default
63
+ except ImportError:
64
+ return None
65
+
66
+ v = get_module_constant(self.module, self.attribute, default, paths)
67
+
68
+ if v is not None and v is not default and self.format is not None:
69
+ return self.format(v)
70
+
71
+ return v
72
+
73
+ def is_present(self, paths=None):
74
+ """Return true if dependency is present on 'paths'"""
75
+ return self.get_version(paths) is not None
76
+
77
+ def is_current(self, paths=None):
78
+ """Return true if dependency is present and up-to-date on 'paths'"""
79
+ version = self.get_version(paths)
80
+ if version is None:
81
+ return False
82
+ return self.version_ok(str(version))
83
+
84
+
85
+ def maybe_close(f):
86
+ @contextlib.contextmanager
87
+ def empty():
88
+ yield
89
+ return
90
+ if not f:
91
+ return empty()
92
+
93
+ return contextlib.closing(f)
94
+
95
+
96
+ def get_module_constant(module, symbol, default=-1, paths=None):
97
+ """Find 'module' by searching 'paths', and extract 'symbol'
98
+
99
+ Return 'None' if 'module' does not exist on 'paths', or it does not define
100
+ 'symbol'. If the module defines 'symbol' as a constant, return the
101
+ constant. Otherwise, return 'default'."""
102
+
103
+ try:
104
+ f, path, (suffix, mode, kind) = info = find_module(module, paths)
105
+ except ImportError:
106
+ # Module doesn't exist
107
+ return None
108
+
109
+ with maybe_close(f):
110
+ if kind == PY_COMPILED:
111
+ f.read(8) # skip magic & date
112
+ code = marshal.load(f)
113
+ elif kind == PY_FROZEN:
114
+ code = _imp.get_frozen_object(module, paths)
115
+ elif kind == PY_SOURCE:
116
+ code = compile(f.read(), path, 'exec')
117
+ else:
118
+ # Not something we can parse; we'll have to import it. :(
119
+ imported = _imp.get_module(module, paths, info)
120
+ return getattr(imported, symbol, None)
121
+
122
+ return extract_constant(code, symbol, default)
123
+
124
+
125
+ def extract_constant(code, symbol, default=-1):
126
+ """Extract the constant value of 'symbol' from 'code'
127
+
128
+ If the name 'symbol' is bound to a constant value by the Python code
129
+ object 'code', return that value. If 'symbol' is bound to an expression,
130
+ return 'default'. Otherwise, return 'None'.
131
+
132
+ Return value is based on the first assignment to 'symbol'. 'symbol' must
133
+ be a global, or at least a non-"fast" local in the code block. That is,
134
+ only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
135
+ must be present in 'code.co_names'.
136
+ """
137
+ if symbol not in code.co_names:
138
+ # name's not there, can't possibly be an assignment
139
+ return None
140
+
141
+ name_idx = list(code.co_names).index(symbol)
142
+
143
+ STORE_NAME = 90
144
+ STORE_GLOBAL = 97
145
+ LOAD_CONST = 100
146
+
147
+ const = default
148
+
149
+ for byte_code in dis.Bytecode(code):
150
+ op = byte_code.opcode
151
+ arg = byte_code.arg
152
+
153
+ if op == LOAD_CONST:
154
+ const = code.co_consts[arg]
155
+ elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL):
156
+ return const
157
+ else:
158
+ const = default
159
+
160
+
161
+ def _update_globals():
162
+ """
163
+ Patch the globals to remove the objects not available on some platforms.
164
+
165
+ XXX it'd be better to test assertions about bytecode instead.
166
+ """
167
+
168
+ if not sys.platform.startswith('java') and sys.platform != 'cli':
169
+ return
170
+ incompatible = 'extract_constant', 'get_module_constant'
171
+ for name in incompatible:
172
+ del globals()[name]
173
+ __all__.remove(name)
174
+
175
+
176
+ _update_globals()
venv/lib/python3.10/site-packages/setuptools/dist.py ADDED
@@ -0,0 +1,1156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ __all__ = ['Distribution']
3
+
4
+ import io
5
+ import sys
6
+ import re
7
+ import os
8
+ import warnings
9
+ import numbers
10
+ import distutils.log
11
+ import distutils.core
12
+ import distutils.cmd
13
+ import distutils.dist
14
+ import distutils.command
15
+ from distutils.util import strtobool
16
+ from distutils.debug import DEBUG
17
+ from distutils.fancy_getopt import translate_longopt
18
+ from glob import iglob
19
+ import itertools
20
+ import textwrap
21
+ from typing import List, Optional, TYPE_CHECKING
22
+
23
+ from collections import defaultdict
24
+ from email import message_from_file
25
+
26
+ from distutils.errors import DistutilsOptionError, DistutilsSetupError
27
+ from distutils.util import rfc822_escape
28
+
29
+ from setuptools.extern import packaging
30
+ from setuptools.extern import ordered_set
31
+ from setuptools.extern.more_itertools import unique_everseen
32
+
33
+ from . import SetuptoolsDeprecationWarning
34
+
35
+ import setuptools
36
+ import setuptools.command
37
+ from setuptools import windows_support
38
+ from setuptools.monkey import get_unpatched
39
+ from setuptools.config import parse_configuration
40
+ import pkg_resources
41
+ from setuptools.extern.packaging import version
42
+
43
+ if TYPE_CHECKING:
44
+ from email.message import Message
45
+
46
+ __import__('setuptools.extern.packaging.specifiers')
47
+ __import__('setuptools.extern.packaging.version')
48
+
49
+
50
+ def _get_unpatched(cls):
51
+ warnings.warn("Do not call this function", DistDeprecationWarning)
52
+ return get_unpatched(cls)
53
+
54
+
55
+ def get_metadata_version(self):
56
+ mv = getattr(self, 'metadata_version', None)
57
+ if mv is None:
58
+ mv = version.Version('2.1')
59
+ self.metadata_version = mv
60
+ return mv
61
+
62
+
63
+ def rfc822_unescape(content: str) -> str:
64
+ """Reverse RFC-822 escaping by removing leading whitespaces from content."""
65
+ lines = content.splitlines()
66
+ if len(lines) == 1:
67
+ return lines[0].lstrip()
68
+ return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:]))))
69
+
70
+
71
+ def _read_field_from_msg(msg: "Message", field: str) -> Optional[str]:
72
+ """Read Message header field."""
73
+ value = msg[field]
74
+ if value == 'UNKNOWN':
75
+ return None
76
+ return value
77
+
78
+
79
+ def _read_field_unescaped_from_msg(msg: "Message", field: str) -> Optional[str]:
80
+ """Read Message header field and apply rfc822_unescape."""
81
+ value = _read_field_from_msg(msg, field)
82
+ if value is None:
83
+ return value
84
+ return rfc822_unescape(value)
85
+
86
+
87
+ def _read_list_from_msg(msg: "Message", field: str) -> Optional[List[str]]:
88
+ """Read Message header field and return all results as list."""
89
+ values = msg.get_all(field, None)
90
+ if values == []:
91
+ return None
92
+ return values
93
+
94
+
95
+ def _read_payload_from_msg(msg: "Message") -> Optional[str]:
96
+ value = msg.get_payload().strip()
97
+ if value == 'UNKNOWN':
98
+ return None
99
+ return value
100
+
101
+
102
+ def read_pkg_file(self, file):
103
+ """Reads the metadata values from a file object."""
104
+ msg = message_from_file(file)
105
+
106
+ self.metadata_version = version.Version(msg['metadata-version'])
107
+ self.name = _read_field_from_msg(msg, 'name')
108
+ self.version = _read_field_from_msg(msg, 'version')
109
+ self.description = _read_field_from_msg(msg, 'summary')
110
+ # we are filling author only.
111
+ self.author = _read_field_from_msg(msg, 'author')
112
+ self.maintainer = None
113
+ self.author_email = _read_field_from_msg(msg, 'author-email')
114
+ self.maintainer_email = None
115
+ self.url = _read_field_from_msg(msg, 'home-page')
116
+ self.license = _read_field_unescaped_from_msg(msg, 'license')
117
+
118
+ if 'download-url' in msg:
119
+ self.download_url = _read_field_from_msg(msg, 'download-url')
120
+ else:
121
+ self.download_url = None
122
+
123
+ self.long_description = _read_field_unescaped_from_msg(msg, 'description')
124
+ if (
125
+ self.long_description is None and
126
+ self.metadata_version >= version.Version('2.1')
127
+ ):
128
+ self.long_description = _read_payload_from_msg(msg)
129
+ self.description = _read_field_from_msg(msg, 'summary')
130
+
131
+ if 'keywords' in msg:
132
+ self.keywords = _read_field_from_msg(msg, 'keywords').split(',')
133
+
134
+ self.platforms = _read_list_from_msg(msg, 'platform')
135
+ self.classifiers = _read_list_from_msg(msg, 'classifier')
136
+
137
+ # PEP 314 - these fields only exist in 1.1
138
+ if self.metadata_version == version.Version('1.1'):
139
+ self.requires = _read_list_from_msg(msg, 'requires')
140
+ self.provides = _read_list_from_msg(msg, 'provides')
141
+ self.obsoletes = _read_list_from_msg(msg, 'obsoletes')
142
+ else:
143
+ self.requires = None
144
+ self.provides = None
145
+ self.obsoletes = None
146
+
147
+ self.license_files = _read_list_from_msg(msg, 'license-file')
148
+
149
+
150
+ def single_line(val):
151
+ """
152
+ Quick and dirty validation for Summary pypa/setuptools#1390.
153
+ """
154
+ if '\n' in val:
155
+ # TODO: Replace with `raise ValueError("newlines not allowed")`
156
+ # after reviewing #2893.
157
+ warnings.warn("newlines not allowed and will break in the future")
158
+ val = val.strip().split('\n')[0]
159
+ return val
160
+
161
+
162
+ # Based on Python 3.5 version
163
+ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME
164
+ """Write the PKG-INFO format data to a file object."""
165
+ version = self.get_metadata_version()
166
+
167
+ def write_field(key, value):
168
+ file.write("%s: %s\n" % (key, value))
169
+
170
+ write_field('Metadata-Version', str(version))
171
+ write_field('Name', self.get_name())
172
+ write_field('Version', self.get_version())
173
+ write_field('Summary', single_line(self.get_description()))
174
+ write_field('Home-page', self.get_url())
175
+
176
+ optional_fields = (
177
+ ('Author', 'author'),
178
+ ('Author-email', 'author_email'),
179
+ ('Maintainer', 'maintainer'),
180
+ ('Maintainer-email', 'maintainer_email'),
181
+ )
182
+
183
+ for field, attr in optional_fields:
184
+ attr_val = getattr(self, attr, None)
185
+ if attr_val is not None:
186
+ write_field(field, attr_val)
187
+
188
+ license = rfc822_escape(self.get_license())
189
+ write_field('License', license)
190
+ if self.download_url:
191
+ write_field('Download-URL', self.download_url)
192
+ for project_url in self.project_urls.items():
193
+ write_field('Project-URL', '%s, %s' % project_url)
194
+
195
+ keywords = ','.join(self.get_keywords())
196
+ if keywords:
197
+ write_field('Keywords', keywords)
198
+
199
+ for platform in self.get_platforms():
200
+ write_field('Platform', platform)
201
+
202
+ self._write_list(file, 'Classifier', self.get_classifiers())
203
+
204
+ # PEP 314
205
+ self._write_list(file, 'Requires', self.get_requires())
206
+ self._write_list(file, 'Provides', self.get_provides())
207
+ self._write_list(file, 'Obsoletes', self.get_obsoletes())
208
+
209
+ # Setuptools specific for PEP 345
210
+ if hasattr(self, 'python_requires'):
211
+ write_field('Requires-Python', self.python_requires)
212
+
213
+ # PEP 566
214
+ if self.long_description_content_type:
215
+ write_field('Description-Content-Type', self.long_description_content_type)
216
+ if self.provides_extras:
217
+ for extra in sorted(self.provides_extras):
218
+ write_field('Provides-Extra', extra)
219
+
220
+ self._write_list(file, 'License-File', self.license_files or [])
221
+
222
+ file.write("\n%s\n\n" % self.get_long_description())
223
+
224
+
225
+ sequence = tuple, list
226
+
227
+
228
+ def check_importable(dist, attr, value):
229
+ try:
230
+ ep = pkg_resources.EntryPoint.parse('x=' + value)
231
+ assert not ep.extras
232
+ except (TypeError, ValueError, AttributeError, AssertionError) as e:
233
+ raise DistutilsSetupError(
234
+ "%r must be importable 'module:attrs' string (got %r)" % (attr, value)
235
+ ) from e
236
+
237
+
238
+ def assert_string_list(dist, attr, value):
239
+ """Verify that value is a string list"""
240
+ try:
241
+ # verify that value is a list or tuple to exclude unordered
242
+ # or single-use iterables
243
+ assert isinstance(value, (list, tuple))
244
+ # verify that elements of value are strings
245
+ assert ''.join(value) != value
246
+ except (TypeError, ValueError, AttributeError, AssertionError) as e:
247
+ raise DistutilsSetupError(
248
+ "%r must be a list of strings (got %r)" % (attr, value)
249
+ ) from e
250
+
251
+
252
+ def check_nsp(dist, attr, value):
253
+ """Verify that namespace packages are valid"""
254
+ ns_packages = value
255
+ assert_string_list(dist, attr, ns_packages)
256
+ for nsp in ns_packages:
257
+ if not dist.has_contents_for(nsp):
258
+ raise DistutilsSetupError(
259
+ "Distribution contains no modules or packages for "
260
+ + "namespace package %r" % nsp
261
+ )
262
+ parent, sep, child = nsp.rpartition('.')
263
+ if parent and parent not in ns_packages:
264
+ distutils.log.warn(
265
+ "WARNING: %r is declared as a package namespace, but %r"
266
+ " is not: please correct this in setup.py",
267
+ nsp,
268
+ parent,
269
+ )
270
+
271
+
272
+ def check_extras(dist, attr, value):
273
+ """Verify that extras_require mapping is valid"""
274
+ try:
275
+ list(itertools.starmap(_check_extra, value.items()))
276
+ except (TypeError, ValueError, AttributeError) as e:
277
+ raise DistutilsSetupError(
278
+ "'extras_require' must be a dictionary whose values are "
279
+ "strings or lists of strings containing valid project/version "
280
+ "requirement specifiers."
281
+ ) from e
282
+
283
+
284
+ def _check_extra(extra, reqs):
285
+ name, sep, marker = extra.partition(':')
286
+ if marker and pkg_resources.invalid_marker(marker):
287
+ raise DistutilsSetupError("Invalid environment marker: " + marker)
288
+ list(pkg_resources.parse_requirements(reqs))
289
+
290
+
291
+ def assert_bool(dist, attr, value):
292
+ """Verify that value is True, False, 0, or 1"""
293
+ if bool(value) != value:
294
+ tmpl = "{attr!r} must be a boolean value (got {value!r})"
295
+ raise DistutilsSetupError(tmpl.format(attr=attr, value=value))
296
+
297
+
298
+ def invalid_unless_false(dist, attr, value):
299
+ if not value:
300
+ warnings.warn(f"{attr} is ignored.", DistDeprecationWarning)
301
+ return
302
+ raise DistutilsSetupError(f"{attr} is invalid.")
303
+
304
+
305
+ def check_requirements(dist, attr, value):
306
+ """Verify that install_requires is a valid requirements list"""
307
+ try:
308
+ list(pkg_resources.parse_requirements(value))
309
+ if isinstance(value, (dict, set)):
310
+ raise TypeError("Unordered types are not allowed")
311
+ except (TypeError, ValueError) as error:
312
+ tmpl = (
313
+ "{attr!r} must be a string or list of strings "
314
+ "containing valid project/version requirement specifiers; {error}"
315
+ )
316
+ raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error
317
+
318
+
319
+ def check_specifier(dist, attr, value):
320
+ """Verify that value is a valid version specifier"""
321
+ try:
322
+ packaging.specifiers.SpecifierSet(value)
323
+ except (packaging.specifiers.InvalidSpecifier, AttributeError) as error:
324
+ tmpl = (
325
+ "{attr!r} must be a string " "containing valid version specifiers; {error}"
326
+ )
327
+ raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error
328
+
329
+
330
+ def check_entry_points(dist, attr, value):
331
+ """Verify that entry_points map is parseable"""
332
+ try:
333
+ pkg_resources.EntryPoint.parse_map(value)
334
+ except ValueError as e:
335
+ raise DistutilsSetupError(e) from e
336
+
337
+
338
+ def check_test_suite(dist, attr, value):
339
+ if not isinstance(value, str):
340
+ raise DistutilsSetupError("test_suite must be a string")
341
+
342
+
343
+ def check_package_data(dist, attr, value):
344
+ """Verify that value is a dictionary of package names to glob lists"""
345
+ if not isinstance(value, dict):
346
+ raise DistutilsSetupError(
347
+ "{!r} must be a dictionary mapping package names to lists of "
348
+ "string wildcard patterns".format(attr)
349
+ )
350
+ for k, v in value.items():
351
+ if not isinstance(k, str):
352
+ raise DistutilsSetupError(
353
+ "keys of {!r} dict must be strings (got {!r})".format(attr, k)
354
+ )
355
+ assert_string_list(dist, 'values of {!r} dict'.format(attr), v)
356
+
357
+
358
+ def check_packages(dist, attr, value):
359
+ for pkgname in value:
360
+ if not re.match(r'\w+(\.\w+)*', pkgname):
361
+ distutils.log.warn(
362
+ "WARNING: %r not a valid package name; please use only "
363
+ ".-separated package names in setup.py",
364
+ pkgname,
365
+ )
366
+
367
+
368
+ _Distribution = get_unpatched(distutils.core.Distribution)
369
+
370
+
371
+ class Distribution(_Distribution):
372
+ """Distribution with support for tests and package data
373
+
374
+ This is an enhanced version of 'distutils.dist.Distribution' that
375
+ effectively adds the following new optional keyword arguments to 'setup()':
376
+
377
+ 'install_requires' -- a string or sequence of strings specifying project
378
+ versions that the distribution requires when installed, in the format
379
+ used by 'pkg_resources.require()'. They will be installed
380
+ automatically when the package is installed. If you wish to use
381
+ packages that are not available in PyPI, or want to give your users an
382
+ alternate download location, you can add a 'find_links' option to the
383
+ '[easy_install]' section of your project's 'setup.cfg' file, and then
384
+ setuptools will scan the listed web pages for links that satisfy the
385
+ requirements.
386
+
387
+ 'extras_require' -- a dictionary mapping names of optional "extras" to the
388
+ additional requirement(s) that using those extras incurs. For example,
389
+ this::
390
+
391
+ extras_require = dict(reST = ["docutils>=0.3", "reSTedit"])
392
+
393
+ indicates that the distribution can optionally provide an extra
394
+ capability called "reST", but it can only be used if docutils and
395
+ reSTedit are installed. If the user installs your package using
396
+ EasyInstall and requests one of your extras, the corresponding
397
+ additional requirements will be installed if needed.
398
+
399
+ 'test_suite' -- the name of a test suite to run for the 'test' command.
400
+ If the user runs 'python setup.py test', the package will be installed,
401
+ and the named test suite will be run. The format is the same as
402
+ would be used on a 'unittest.py' command line. That is, it is the
403
+ dotted name of an object to import and call to generate a test suite.
404
+
405
+ 'package_data' -- a dictionary mapping package names to lists of filenames
406
+ or globs to use to find data files contained in the named packages.
407
+ If the dictionary has filenames or globs listed under '""' (the empty
408
+ string), those names will be searched for in every package, in addition
409
+ to any names for the specific package. Data files found using these
410
+ names/globs will be installed along with the package, in the same
411
+ location as the package. Note that globs are allowed to reference
412
+ the contents of non-package subdirectories, as long as you use '/' as
413
+ a path separator. (Globs are automatically converted to
414
+ platform-specific paths at runtime.)
415
+
416
+ In addition to these new keywords, this class also has several new methods
417
+ for manipulating the distribution's contents. For example, the 'include()'
418
+ and 'exclude()' methods can be thought of as in-place add and subtract
419
+ commands that add or remove packages, modules, extensions, and so on from
420
+ the distribution.
421
+ """
422
+
423
+ _DISTUTILS_UNSUPPORTED_METADATA = {
424
+ 'long_description_content_type': lambda: None,
425
+ 'project_urls': dict,
426
+ 'provides_extras': ordered_set.OrderedSet,
427
+ 'license_file': lambda: None,
428
+ 'license_files': lambda: None,
429
+ }
430
+
431
+ _patched_dist = None
432
+
433
+ def patch_missing_pkg_info(self, attrs):
434
+ # Fake up a replacement for the data that would normally come from
435
+ # PKG-INFO, but which might not yet be built if this is a fresh
436
+ # checkout.
437
+ #
438
+ if not attrs or 'name' not in attrs or 'version' not in attrs:
439
+ return
440
+ key = pkg_resources.safe_name(str(attrs['name'])).lower()
441
+ dist = pkg_resources.working_set.by_key.get(key)
442
+ if dist is not None and not dist.has_metadata('PKG-INFO'):
443
+ dist._version = pkg_resources.safe_version(str(attrs['version']))
444
+ self._patched_dist = dist
445
+
446
+ def __init__(self, attrs=None):
447
+ have_package_data = hasattr(self, "package_data")
448
+ if not have_package_data:
449
+ self.package_data = {}
450
+ attrs = attrs or {}
451
+ self.dist_files = []
452
+ # Filter-out setuptools' specific options.
453
+ self.src_root = attrs.pop("src_root", None)
454
+ self.patch_missing_pkg_info(attrs)
455
+ self.dependency_links = attrs.pop('dependency_links', [])
456
+ self.setup_requires = attrs.pop('setup_requires', [])
457
+ for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
458
+ vars(self).setdefault(ep.name, None)
459
+ _Distribution.__init__(
460
+ self,
461
+ {
462
+ k: v
463
+ for k, v in attrs.items()
464
+ if k not in self._DISTUTILS_UNSUPPORTED_METADATA
465
+ },
466
+ )
467
+
468
+ self._set_metadata_defaults(attrs)
469
+
470
+ self.metadata.version = self._normalize_version(
471
+ self._validate_version(self.metadata.version)
472
+ )
473
+ self._finalize_requires()
474
+
475
+ def _set_metadata_defaults(self, attrs):
476
+ """
477
+ Fill-in missing metadata fields not supported by distutils.
478
+ Some fields may have been set by other tools (e.g. pbr).
479
+ Those fields (vars(self.metadata)) take precedence to
480
+ supplied attrs.
481
+ """
482
+ for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items():
483
+ vars(self.metadata).setdefault(option, attrs.get(option, default()))
484
+
485
+ @staticmethod
486
+ def _normalize_version(version):
487
+ if isinstance(version, setuptools.sic) or version is None:
488
+ return version
489
+
490
+ normalized = str(packaging.version.Version(version))
491
+ if version != normalized:
492
+ tmpl = "Normalizing '{version}' to '{normalized}'"
493
+ warnings.warn(tmpl.format(**locals()))
494
+ return normalized
495
+ return version
496
+
497
+ @staticmethod
498
+ def _validate_version(version):
499
+ if isinstance(version, numbers.Number):
500
+ # Some people apparently take "version number" too literally :)
501
+ version = str(version)
502
+
503
+ if version is not None:
504
+ try:
505
+ packaging.version.Version(version)
506
+ except (packaging.version.InvalidVersion, TypeError):
507
+ warnings.warn(
508
+ "The version specified (%r) is an invalid version, this "
509
+ "may not work as expected with newer versions of "
510
+ "setuptools, pip, and PyPI. Please see PEP 440 for more "
511
+ "details." % version
512
+ )
513
+ return setuptools.sic(version)
514
+ return version
515
+
516
+ def _finalize_requires(self):
517
+ """
518
+ Set `metadata.python_requires` and fix environment markers
519
+ in `install_requires` and `extras_require`.
520
+ """
521
+ if getattr(self, 'python_requires', None):
522
+ self.metadata.python_requires = self.python_requires
523
+
524
+ if getattr(self, 'extras_require', None):
525
+ for extra in self.extras_require.keys():
526
+ # Since this gets called multiple times at points where the
527
+ # keys have become 'converted' extras, ensure that we are only
528
+ # truly adding extras we haven't seen before here.
529
+ extra = extra.split(':')[0]
530
+ if extra:
531
+ self.metadata.provides_extras.add(extra)
532
+
533
+ self._convert_extras_requirements()
534
+ self._move_install_requirements_markers()
535
+
536
+ def _convert_extras_requirements(self):
537
+ """
538
+ Convert requirements in `extras_require` of the form
539
+ `"extra": ["barbazquux; {marker}"]` to
540
+ `"extra:{marker}": ["barbazquux"]`.
541
+ """
542
+ spec_ext_reqs = getattr(self, 'extras_require', None) or {}
543
+ self._tmp_extras_require = defaultdict(list)
544
+ for section, v in spec_ext_reqs.items():
545
+ # Do not strip empty sections.
546
+ self._tmp_extras_require[section]
547
+ for r in pkg_resources.parse_requirements(v):
548
+ suffix = self._suffix_for(r)
549
+ self._tmp_extras_require[section + suffix].append(r)
550
+
551
+ @staticmethod
552
+ def _suffix_for(req):
553
+ """
554
+ For a requirement, return the 'extras_require' suffix for
555
+ that requirement.
556
+ """
557
+ return ':' + str(req.marker) if req.marker else ''
558
+
559
+ def _move_install_requirements_markers(self):
560
+ """
561
+ Move requirements in `install_requires` that are using environment
562
+ markers `extras_require`.
563
+ """
564
+
565
+ # divide the install_requires into two sets, simple ones still
566
+ # handled by install_requires and more complex ones handled
567
+ # by extras_require.
568
+
569
+ def is_simple_req(req):
570
+ return not req.marker
571
+
572
+ spec_inst_reqs = getattr(self, 'install_requires', None) or ()
573
+ inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs))
574
+ simple_reqs = filter(is_simple_req, inst_reqs)
575
+ complex_reqs = itertools.filterfalse(is_simple_req, inst_reqs)
576
+ self.install_requires = list(map(str, simple_reqs))
577
+
578
+ for r in complex_reqs:
579
+ self._tmp_extras_require[':' + str(r.marker)].append(r)
580
+ self.extras_require = dict(
581
+ (k, [str(r) for r in map(self._clean_req, v)])
582
+ for k, v in self._tmp_extras_require.items()
583
+ )
584
+
585
+ def _clean_req(self, req):
586
+ """
587
+ Given a Requirement, remove environment markers and return it.
588
+ """
589
+ req.marker = None
590
+ return req
591
+
592
+ def _finalize_license_files(self):
593
+ """Compute names of all license files which should be included."""
594
+ license_files: Optional[List[str]] = self.metadata.license_files
595
+ patterns: List[str] = license_files if license_files else []
596
+
597
+ license_file: Optional[str] = self.metadata.license_file
598
+ if license_file and license_file not in patterns:
599
+ patterns.append(license_file)
600
+
601
+ if license_files is None and license_file is None:
602
+ # Default patterns match the ones wheel uses
603
+ # See https://wheel.readthedocs.io/en/stable/user_guide.html
604
+ # -> 'Including license files in the generated wheel file'
605
+ patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*')
606
+
607
+ self.metadata.license_files = list(
608
+ unique_everseen(self._expand_patterns(patterns))
609
+ )
610
+
611
+ @staticmethod
612
+ def _expand_patterns(patterns):
613
+ """
614
+ >>> list(Distribution._expand_patterns(['LICENSE']))
615
+ ['LICENSE']
616
+ >>> list(Distribution._expand_patterns(['setup.cfg', 'LIC*']))
617
+ ['setup.cfg', 'LICENSE']
618
+ """
619
+ return (
620
+ path
621
+ for pattern in patterns
622
+ for path in sorted(iglob(pattern))
623
+ if not path.endswith('~') and os.path.isfile(path)
624
+ )
625
+
626
+ # FIXME: 'Distribution._parse_config_files' is too complex (14)
627
+ def _parse_config_files(self, filenames=None): # noqa: C901
628
+ """
629
+ Adapted from distutils.dist.Distribution.parse_config_files,
630
+ this method provides the same functionality in subtly-improved
631
+ ways.
632
+ """
633
+ from configparser import ConfigParser
634
+
635
+ # Ignore install directory options if we have a venv
636
+ ignore_options = (
637
+ []
638
+ if sys.prefix == sys.base_prefix
639
+ else [
640
+ 'install-base',
641
+ 'install-platbase',
642
+ 'install-lib',
643
+ 'install-platlib',
644
+ 'install-purelib',
645
+ 'install-headers',
646
+ 'install-scripts',
647
+ 'install-data',
648
+ 'prefix',
649
+ 'exec-prefix',
650
+ 'home',
651
+ 'user',
652
+ 'root',
653
+ ]
654
+ )
655
+
656
+ ignore_options = frozenset(ignore_options)
657
+
658
+ if filenames is None:
659
+ filenames = self.find_config_files()
660
+
661
+ if DEBUG:
662
+ self.announce("Distribution.parse_config_files():")
663
+
664
+ parser = ConfigParser()
665
+ parser.optionxform = str
666
+ for filename in filenames:
667
+ with io.open(filename, encoding='utf-8') as reader:
668
+ if DEBUG:
669
+ self.announce(" reading {filename}".format(**locals()))
670
+ parser.read_file(reader)
671
+ for section in parser.sections():
672
+ options = parser.options(section)
673
+ opt_dict = self.get_option_dict(section)
674
+
675
+ for opt in options:
676
+ if opt == '__name__' or opt in ignore_options:
677
+ continue
678
+
679
+ val = parser.get(section, opt)
680
+ opt = self.warn_dash_deprecation(opt, section)
681
+ opt = self.make_option_lowercase(opt, section)
682
+ opt_dict[opt] = (filename, val)
683
+
684
+ # Make the ConfigParser forget everything (so we retain
685
+ # the original filenames that options come from)
686
+ parser.__init__()
687
+
688
+ if 'global' not in self.command_options:
689
+ return
690
+
691
+ # If there was a "global" section in the config file, use it
692
+ # to set Distribution options.
693
+
694
+ for (opt, (src, val)) in self.command_options['global'].items():
695
+ alias = self.negative_opt.get(opt)
696
+ if alias:
697
+ val = not strtobool(val)
698
+ elif opt in ('verbose', 'dry_run'): # ugh!
699
+ val = strtobool(val)
700
+
701
+ try:
702
+ setattr(self, alias or opt, val)
703
+ except ValueError as e:
704
+ raise DistutilsOptionError(e) from e
705
+
706
+ def warn_dash_deprecation(self, opt, section):
707
+ if section in (
708
+ 'options.extras_require',
709
+ 'options.data_files',
710
+ ):
711
+ return opt
712
+
713
+ underscore_opt = opt.replace('-', '_')
714
+ commands = distutils.command.__all__ + self._setuptools_commands()
715
+ if (
716
+ not section.startswith('options')
717
+ and section != 'metadata'
718
+ and section not in commands
719
+ ):
720
+ return underscore_opt
721
+
722
+ if '-' in opt:
723
+ warnings.warn(
724
+ "Usage of dash-separated '%s' will not be supported in future "
725
+ "versions. Please use the underscore name '%s' instead"
726
+ % (opt, underscore_opt)
727
+ )
728
+ return underscore_opt
729
+
730
+ def _setuptools_commands(self):
731
+ try:
732
+ dist = pkg_resources.get_distribution('setuptools')
733
+ return list(dist.get_entry_map('distutils.commands'))
734
+ except pkg_resources.DistributionNotFound:
735
+ # during bootstrapping, distribution doesn't exist
736
+ return []
737
+
738
+ def make_option_lowercase(self, opt, section):
739
+ if section != 'metadata' or opt.islower():
740
+ return opt
741
+
742
+ lowercase_opt = opt.lower()
743
+ warnings.warn(
744
+ "Usage of uppercase key '%s' in '%s' will be deprecated in future "
745
+ "versions. Please use lowercase '%s' instead"
746
+ % (opt, section, lowercase_opt)
747
+ )
748
+ return lowercase_opt
749
+
750
+ # FIXME: 'Distribution._set_command_options' is too complex (14)
751
+ def _set_command_options(self, command_obj, option_dict=None): # noqa: C901
752
+ """
753
+ Set the options for 'command_obj' from 'option_dict'. Basically
754
+ this means copying elements of a dictionary ('option_dict') to
755
+ attributes of an instance ('command').
756
+
757
+ 'command_obj' must be a Command instance. If 'option_dict' is not
758
+ supplied, uses the standard option dictionary for this command
759
+ (from 'self.command_options').
760
+
761
+ (Adopted from distutils.dist.Distribution._set_command_options)
762
+ """
763
+ command_name = command_obj.get_command_name()
764
+ if option_dict is None:
765
+ option_dict = self.get_option_dict(command_name)
766
+
767
+ if DEBUG:
768
+ self.announce(" setting options for '%s' command:" % command_name)
769
+ for (option, (source, value)) in option_dict.items():
770
+ if DEBUG:
771
+ self.announce(" %s = %s (from %s)" % (option, value, source))
772
+ try:
773
+ bool_opts = [translate_longopt(o) for o in command_obj.boolean_options]
774
+ except AttributeError:
775
+ bool_opts = []
776
+ try:
777
+ neg_opt = command_obj.negative_opt
778
+ except AttributeError:
779
+ neg_opt = {}
780
+
781
+ try:
782
+ is_string = isinstance(value, str)
783
+ if option in neg_opt and is_string:
784
+ setattr(command_obj, neg_opt[option], not strtobool(value))
785
+ elif option in bool_opts and is_string:
786
+ setattr(command_obj, option, strtobool(value))
787
+ elif hasattr(command_obj, option):
788
+ setattr(command_obj, option, value)
789
+ else:
790
+ raise DistutilsOptionError(
791
+ "error in %s: command '%s' has no such option '%s'"
792
+ % (source, command_name, option)
793
+ )
794
+ except ValueError as e:
795
+ raise DistutilsOptionError(e) from e
796
+
797
+ def parse_config_files(self, filenames=None, ignore_option_errors=False):
798
+ """Parses configuration files from various levels
799
+ and loads configuration.
800
+
801
+ """
802
+ self._parse_config_files(filenames=filenames)
803
+
804
+ parse_configuration(
805
+ self, self.command_options, ignore_option_errors=ignore_option_errors
806
+ )
807
+ self._finalize_requires()
808
+ self._finalize_license_files()
809
+
810
+ def fetch_build_eggs(self, requires):
811
+ """Resolve pre-setup requirements"""
812
+ resolved_dists = pkg_resources.working_set.resolve(
813
+ pkg_resources.parse_requirements(requires),
814
+ installer=self.fetch_build_egg,
815
+ replace_conflicting=True,
816
+ )
817
+ for dist in resolved_dists:
818
+ pkg_resources.working_set.add(dist, replace=True)
819
+ return resolved_dists
820
+
821
+ def finalize_options(self):
822
+ """
823
+ Allow plugins to apply arbitrary operations to the
824
+ distribution. Each hook may optionally define a 'order'
825
+ to influence the order of execution. Smaller numbers
826
+ go first and the default is 0.
827
+ """
828
+ group = 'setuptools.finalize_distribution_options'
829
+
830
+ def by_order(hook):
831
+ return getattr(hook, 'order', 0)
832
+
833
+ defined = pkg_resources.iter_entry_points(group)
834
+ filtered = itertools.filterfalse(self._removed, defined)
835
+ loaded = map(lambda e: e.load(), filtered)
836
+ for ep in sorted(loaded, key=by_order):
837
+ ep(self)
838
+
839
+ @staticmethod
840
+ def _removed(ep):
841
+ """
842
+ When removing an entry point, if metadata is loaded
843
+ from an older version of Setuptools, that removed
844
+ entry point will attempt to be loaded and will fail.
845
+ See #2765 for more details.
846
+ """
847
+ removed = {
848
+ # removed 2021-09-05
849
+ '2to3_doctests',
850
+ }
851
+ return ep.name in removed
852
+
853
+ def _finalize_setup_keywords(self):
854
+ for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
855
+ value = getattr(self, ep.name, None)
856
+ if value is not None:
857
+ ep.require(installer=self.fetch_build_egg)
858
+ ep.load()(self, ep.name, value)
859
+
860
+ def get_egg_cache_dir(self):
861
+ egg_cache_dir = os.path.join(os.curdir, '.eggs')
862
+ if not os.path.exists(egg_cache_dir):
863
+ os.mkdir(egg_cache_dir)
864
+ windows_support.hide_file(egg_cache_dir)
865
+ readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt')
866
+ with open(readme_txt_filename, 'w') as f:
867
+ f.write(
868
+ 'This directory contains eggs that were downloaded '
869
+ 'by setuptools to build, test, and run plug-ins.\n\n'
870
+ )
871
+ f.write(
872
+ 'This directory caches those eggs to prevent '
873
+ 'repeated downloads.\n\n'
874
+ )
875
+ f.write('However, it is safe to delete this directory.\n\n')
876
+
877
+ return egg_cache_dir
878
+
879
+ def fetch_build_egg(self, req):
880
+ """Fetch an egg needed for building"""
881
+ from setuptools.installer import fetch_build_egg
882
+
883
+ return fetch_build_egg(self, req)
884
+
885
+ def get_command_class(self, command):
886
+ """Pluggable version of get_command_class()"""
887
+ if command in self.cmdclass:
888
+ return self.cmdclass[command]
889
+
890
+ eps = pkg_resources.iter_entry_points('distutils.commands', command)
891
+ for ep in eps:
892
+ ep.require(installer=self.fetch_build_egg)
893
+ self.cmdclass[command] = cmdclass = ep.load()
894
+ return cmdclass
895
+ else:
896
+ return _Distribution.get_command_class(self, command)
897
+
898
+ def print_commands(self):
899
+ for ep in pkg_resources.iter_entry_points('distutils.commands'):
900
+ if ep.name not in self.cmdclass:
901
+ # don't require extras as the commands won't be invoked
902
+ cmdclass = ep.resolve()
903
+ self.cmdclass[ep.name] = cmdclass
904
+ return _Distribution.print_commands(self)
905
+
906
+ def get_command_list(self):
907
+ for ep in pkg_resources.iter_entry_points('distutils.commands'):
908
+ if ep.name not in self.cmdclass:
909
+ # don't require extras as the commands won't be invoked
910
+ cmdclass = ep.resolve()
911
+ self.cmdclass[ep.name] = cmdclass
912
+ return _Distribution.get_command_list(self)
913
+
914
+ def include(self, **attrs):
915
+ """Add items to distribution that are named in keyword arguments
916
+
917
+ For example, 'dist.include(py_modules=["x"])' would add 'x' to
918
+ the distribution's 'py_modules' attribute, if it was not already
919
+ there.
920
+
921
+ Currently, this method only supports inclusion for attributes that are
922
+ lists or tuples. If you need to add support for adding to other
923
+ attributes in this or a subclass, you can add an '_include_X' method,
924
+ where 'X' is the name of the attribute. The method will be called with
925
+ the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})'
926
+ will try to call 'dist._include_foo({"bar":"baz"})', which can then
927
+ handle whatever special inclusion logic is needed.
928
+ """
929
+ for k, v in attrs.items():
930
+ include = getattr(self, '_include_' + k, None)
931
+ if include:
932
+ include(v)
933
+ else:
934
+ self._include_misc(k, v)
935
+
936
+ def exclude_package(self, package):
937
+ """Remove packages, modules, and extensions in named package"""
938
+
939
+ pfx = package + '.'
940
+ if self.packages:
941
+ self.packages = [
942
+ p for p in self.packages if p != package and not p.startswith(pfx)
943
+ ]
944
+
945
+ if self.py_modules:
946
+ self.py_modules = [
947
+ p for p in self.py_modules if p != package and not p.startswith(pfx)
948
+ ]
949
+
950
+ if self.ext_modules:
951
+ self.ext_modules = [
952
+ p
953
+ for p in self.ext_modules
954
+ if p.name != package and not p.name.startswith(pfx)
955
+ ]
956
+
957
+ def has_contents_for(self, package):
958
+ """Return true if 'exclude_package(package)' would do something"""
959
+
960
+ pfx = package + '.'
961
+
962
+ for p in self.iter_distribution_names():
963
+ if p == package or p.startswith(pfx):
964
+ return True
965
+
966
+ def _exclude_misc(self, name, value):
967
+ """Handle 'exclude()' for list/tuple attrs without a special handler"""
968
+ if not isinstance(value, sequence):
969
+ raise DistutilsSetupError(
970
+ "%s: setting must be a list or tuple (%r)" % (name, value)
971
+ )
972
+ try:
973
+ old = getattr(self, name)
974
+ except AttributeError as e:
975
+ raise DistutilsSetupError("%s: No such distribution setting" % name) from e
976
+ if old is not None and not isinstance(old, sequence):
977
+ raise DistutilsSetupError(
978
+ name + ": this setting cannot be changed via include/exclude"
979
+ )
980
+ elif old:
981
+ setattr(self, name, [item for item in old if item not in value])
982
+
983
+ def _include_misc(self, name, value):
984
+ """Handle 'include()' for list/tuple attrs without a special handler"""
985
+
986
+ if not isinstance(value, sequence):
987
+ raise DistutilsSetupError("%s: setting must be a list (%r)" % (name, value))
988
+ try:
989
+ old = getattr(self, name)
990
+ except AttributeError as e:
991
+ raise DistutilsSetupError("%s: No such distribution setting" % name) from e
992
+ if old is None:
993
+ setattr(self, name, value)
994
+ elif not isinstance(old, sequence):
995
+ raise DistutilsSetupError(
996
+ name + ": this setting cannot be changed via include/exclude"
997
+ )
998
+ else:
999
+ new = [item for item in value if item not in old]
1000
+ setattr(self, name, old + new)
1001
+
1002
+ def exclude(self, **attrs):
1003
+ """Remove items from distribution that are named in keyword arguments
1004
+
1005
+ For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from
1006
+ the distribution's 'py_modules' attribute. Excluding packages uses
1007
+ the 'exclude_package()' method, so all of the package's contained
1008
+ packages, modules, and extensions are also excluded.
1009
+
1010
+ Currently, this method only supports exclusion from attributes that are
1011
+ lists or tuples. If you need to add support for excluding from other
1012
+ attributes in this or a subclass, you can add an '_exclude_X' method,
1013
+ where 'X' is the name of the attribute. The method will be called with
1014
+ the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})'
1015
+ will try to call 'dist._exclude_foo({"bar":"baz"})', which can then
1016
+ handle whatever special exclusion logic is needed.
1017
+ """
1018
+ for k, v in attrs.items():
1019
+ exclude = getattr(self, '_exclude_' + k, None)
1020
+ if exclude:
1021
+ exclude(v)
1022
+ else:
1023
+ self._exclude_misc(k, v)
1024
+
1025
+ def _exclude_packages(self, packages):
1026
+ if not isinstance(packages, sequence):
1027
+ raise DistutilsSetupError(
1028
+ "packages: setting must be a list or tuple (%r)" % (packages,)
1029
+ )
1030
+ list(map(self.exclude_package, packages))
1031
+
1032
+ def _parse_command_opts(self, parser, args):
1033
+ # Remove --with-X/--without-X options when processing command args
1034
+ self.global_options = self.__class__.global_options
1035
+ self.negative_opt = self.__class__.negative_opt
1036
+
1037
+ # First, expand any aliases
1038
+ command = args[0]
1039
+ aliases = self.get_option_dict('aliases')
1040
+ while command in aliases:
1041
+ src, alias = aliases[command]
1042
+ del aliases[command] # ensure each alias can expand only once!
1043
+ import shlex
1044
+
1045
+ args[:1] = shlex.split(alias, True)
1046
+ command = args[0]
1047
+
1048
+ nargs = _Distribution._parse_command_opts(self, parser, args)
1049
+
1050
+ # Handle commands that want to consume all remaining arguments
1051
+ cmd_class = self.get_command_class(command)
1052
+ if getattr(cmd_class, 'command_consumes_arguments', None):
1053
+ self.get_option_dict(command)['args'] = ("command line", nargs)
1054
+ if nargs is not None:
1055
+ return []
1056
+
1057
+ return nargs
1058
+
1059
+ def get_cmdline_options(self):
1060
+ """Return a '{cmd: {opt:val}}' map of all command-line options
1061
+
1062
+ Option names are all long, but do not include the leading '--', and
1063
+ contain dashes rather than underscores. If the option doesn't take
1064
+ an argument (e.g. '--quiet'), the 'val' is 'None'.
1065
+
1066
+ Note that options provided by config files are intentionally excluded.
1067
+ """
1068
+
1069
+ d = {}
1070
+
1071
+ for cmd, opts in self.command_options.items():
1072
+
1073
+ for opt, (src, val) in opts.items():
1074
+
1075
+ if src != "command line":
1076
+ continue
1077
+
1078
+ opt = opt.replace('_', '-')
1079
+
1080
+ if val == 0:
1081
+ cmdobj = self.get_command_obj(cmd)
1082
+ neg_opt = self.negative_opt.copy()
1083
+ neg_opt.update(getattr(cmdobj, 'negative_opt', {}))
1084
+ for neg, pos in neg_opt.items():
1085
+ if pos == opt:
1086
+ opt = neg
1087
+ val = None
1088
+ break
1089
+ else:
1090
+ raise AssertionError("Shouldn't be able to get here")
1091
+
1092
+ elif val == 1:
1093
+ val = None
1094
+
1095
+ d.setdefault(cmd, {})[opt] = val
1096
+
1097
+ return d
1098
+
1099
+ def iter_distribution_names(self):
1100
+ """Yield all packages, modules, and extension names in distribution"""
1101
+
1102
+ for pkg in self.packages or ():
1103
+ yield pkg
1104
+
1105
+ for module in self.py_modules or ():
1106
+ yield module
1107
+
1108
+ for ext in self.ext_modules or ():
1109
+ if isinstance(ext, tuple):
1110
+ name, buildinfo = ext
1111
+ else:
1112
+ name = ext.name
1113
+ if name.endswith('module'):
1114
+ name = name[:-6]
1115
+ yield name
1116
+
1117
+ def handle_display_options(self, option_order):
1118
+ """If there were any non-global "display-only" options
1119
+ (--help-commands or the metadata display options) on the command
1120
+ line, display the requested info and return true; else return
1121
+ false.
1122
+ """
1123
+ import sys
1124
+
1125
+ if self.help_commands:
1126
+ return _Distribution.handle_display_options(self, option_order)
1127
+
1128
+ # Stdout may be StringIO (e.g. in tests)
1129
+ if not isinstance(sys.stdout, io.TextIOWrapper):
1130
+ return _Distribution.handle_display_options(self, option_order)
1131
+
1132
+ # Don't wrap stdout if utf-8 is already the encoding. Provides
1133
+ # workaround for #334.
1134
+ if sys.stdout.encoding.lower() in ('utf-8', 'utf8'):
1135
+ return _Distribution.handle_display_options(self, option_order)
1136
+
1137
+ # Print metadata in UTF-8 no matter the platform
1138
+ encoding = sys.stdout.encoding
1139
+ errors = sys.stdout.errors
1140
+ newline = sys.platform != 'win32' and '\n' or None
1141
+ line_buffering = sys.stdout.line_buffering
1142
+
1143
+ sys.stdout = io.TextIOWrapper(
1144
+ sys.stdout.detach(), 'utf-8', errors, newline, line_buffering
1145
+ )
1146
+ try:
1147
+ return _Distribution.handle_display_options(self, option_order)
1148
+ finally:
1149
+ sys.stdout = io.TextIOWrapper(
1150
+ sys.stdout.detach(), encoding, errors, newline, line_buffering
1151
+ )
1152
+
1153
+
1154
+ class DistDeprecationWarning(SetuptoolsDeprecationWarning):
1155
+ """Class for warning about deprecations in dist in
1156
+ setuptools. Not ignored by default, unlike DeprecationWarning."""
venv/lib/python3.10/site-packages/setuptools/errors.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """setuptools.errors
2
+
3
+ Provides exceptions used by setuptools modules.
4
+ """
5
+
6
+ from distutils import errors as _distutils_errors
7
+ from distutils.errors import DistutilsError
8
+
9
+
10
+ class RemovedCommandError(DistutilsError, RuntimeError):
11
+ """Error used for commands that have been removed in setuptools.
12
+
13
+ Since ``setuptools`` is built on ``distutils``, simply removing a command
14
+ from ``setuptools`` will make the behavior fall back to ``distutils``; this
15
+ error is raised if a command exists in ``distutils`` but has been actively
16
+ removed in ``setuptools``.
17
+ """
18
+
19
+
20
+ # Re-export errors from distutils to facilitate the migration to PEP632
21
+
22
+ ByteCompileError = _distutils_errors.DistutilsByteCompileError
23
+ CCompilerError = _distutils_errors.CCompilerError
24
+ ClassError = _distutils_errors.DistutilsClassError
25
+ CompileError = _distutils_errors.CompileError
26
+ ExecError = _distutils_errors.DistutilsExecError
27
+ FileError = _distutils_errors.DistutilsFileError
28
+ InternalError = _distutils_errors.DistutilsInternalError
29
+ LibError = _distutils_errors.LibError
30
+ LinkError = _distutils_errors.LinkError
31
+ ModuleError = _distutils_errors.DistutilsModuleError
32
+ OptionError = _distutils_errors.DistutilsOptionError
33
+ PlatformError = _distutils_errors.DistutilsPlatformError
34
+ PreprocessError = _distutils_errors.PreprocessError
35
+ SetupError = _distutils_errors.DistutilsSetupError
36
+ TemplateError = _distutils_errors.DistutilsTemplateError
37
+ UnknownFileError = _distutils_errors.UnknownFileError
38
+
39
+ # The root error class in the hierarchy
40
+ BaseError = _distutils_errors.DistutilsError
venv/lib/python3.10/site-packages/setuptools/extension.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import functools
3
+ import distutils.core
4
+ import distutils.errors
5
+ import distutils.extension
6
+
7
+ from .monkey import get_unpatched
8
+
9
+
10
+ def _have_cython():
11
+ """
12
+ Return True if Cython can be imported.
13
+ """
14
+ cython_impl = 'Cython.Distutils.build_ext'
15
+ try:
16
+ # from (cython_impl) import build_ext
17
+ __import__(cython_impl, fromlist=['build_ext']).build_ext
18
+ return True
19
+ except Exception:
20
+ pass
21
+ return False
22
+
23
+
24
+ # for compatibility
25
+ have_pyrex = _have_cython
26
+
27
+ _Extension = get_unpatched(distutils.core.Extension)
28
+
29
+
30
+ class Extension(_Extension):
31
+ """Extension that uses '.c' files in place of '.pyx' files"""
32
+
33
+ def __init__(self, name, sources, *args, **kw):
34
+ # The *args is needed for compatibility as calls may use positional
35
+ # arguments. py_limited_api may be set only via keyword.
36
+ self.py_limited_api = kw.pop("py_limited_api", False)
37
+ _Extension.__init__(self, name, sources, *args, **kw)
38
+
39
+ def _convert_pyx_sources_to_lang(self):
40
+ """
41
+ Replace sources with .pyx extensions to sources with the target
42
+ language extension. This mechanism allows language authors to supply
43
+ pre-converted sources but to prefer the .pyx sources.
44
+ """
45
+ if _have_cython():
46
+ # the build has Cython, so allow it to compile the .pyx files
47
+ return
48
+ lang = self.language or ''
49
+ target_ext = '.cpp' if lang.lower() == 'c++' else '.c'
50
+ sub = functools.partial(re.sub, '.pyx$', target_ext)
51
+ self.sources = list(map(sub, self.sources))
52
+
53
+
54
+ class Library(Extension):
55
+ """Just like a regular Extension, but built as a library instead"""
venv/lib/python3.10/site-packages/setuptools/glob.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Filename globbing utility. Mostly a copy of `glob` from Python 3.5.
3
+
4
+ Changes include:
5
+ * `yield from` and PEP3102 `*` removed.
6
+ * Hidden files are not ignored.
7
+ """
8
+
9
+ import os
10
+ import re
11
+ import fnmatch
12
+
13
+ __all__ = ["glob", "iglob", "escape"]
14
+
15
+
16
+ def glob(pathname, recursive=False):
17
+ """Return a list of paths matching a pathname pattern.
18
+
19
+ The pattern may contain simple shell-style wildcards a la
20
+ fnmatch. However, unlike fnmatch, filenames starting with a
21
+ dot are special cases that are not matched by '*' and '?'
22
+ patterns.
23
+
24
+ If recursive is true, the pattern '**' will match any files and
25
+ zero or more directories and subdirectories.
26
+ """
27
+ return list(iglob(pathname, recursive=recursive))
28
+
29
+
30
+ def iglob(pathname, recursive=False):
31
+ """Return an iterator which yields the paths matching a pathname pattern.
32
+
33
+ The pattern may contain simple shell-style wildcards a la
34
+ fnmatch. However, unlike fnmatch, filenames starting with a
35
+ dot are special cases that are not matched by '*' and '?'
36
+ patterns.
37
+
38
+ If recursive is true, the pattern '**' will match any files and
39
+ zero or more directories and subdirectories.
40
+ """
41
+ it = _iglob(pathname, recursive)
42
+ if recursive and _isrecursive(pathname):
43
+ s = next(it) # skip empty string
44
+ assert not s
45
+ return it
46
+
47
+
48
+ def _iglob(pathname, recursive):
49
+ dirname, basename = os.path.split(pathname)
50
+ glob_in_dir = glob2 if recursive and _isrecursive(basename) else glob1
51
+
52
+ if not has_magic(pathname):
53
+ if basename:
54
+ if os.path.lexists(pathname):
55
+ yield pathname
56
+ else:
57
+ # Patterns ending with a slash should match only directories
58
+ if os.path.isdir(dirname):
59
+ yield pathname
60
+ return
61
+
62
+ if not dirname:
63
+ yield from glob_in_dir(dirname, basename)
64
+ return
65
+ # `os.path.split()` returns the argument itself as a dirname if it is a
66
+ # drive or UNC path. Prevent an infinite recursion if a drive or UNC path
67
+ # contains magic characters (i.e. r'\\?\C:').
68
+ if dirname != pathname and has_magic(dirname):
69
+ dirs = _iglob(dirname, recursive)
70
+ else:
71
+ dirs = [dirname]
72
+ if not has_magic(basename):
73
+ glob_in_dir = glob0
74
+ for dirname in dirs:
75
+ for name in glob_in_dir(dirname, basename):
76
+ yield os.path.join(dirname, name)
77
+
78
+
79
+ # These 2 helper functions non-recursively glob inside a literal directory.
80
+ # They return a list of basenames. `glob1` accepts a pattern while `glob0`
81
+ # takes a literal basename (so it only has to check for its existence).
82
+
83
+
84
+ def glob1(dirname, pattern):
85
+ if not dirname:
86
+ if isinstance(pattern, bytes):
87
+ dirname = os.curdir.encode('ASCII')
88
+ else:
89
+ dirname = os.curdir
90
+ try:
91
+ names = os.listdir(dirname)
92
+ except OSError:
93
+ return []
94
+ return fnmatch.filter(names, pattern)
95
+
96
+
97
+ def glob0(dirname, basename):
98
+ if not basename:
99
+ # `os.path.split()` returns an empty basename for paths ending with a
100
+ # directory separator. 'q*x/' should match only directories.
101
+ if os.path.isdir(dirname):
102
+ return [basename]
103
+ else:
104
+ if os.path.lexists(os.path.join(dirname, basename)):
105
+ return [basename]
106
+ return []
107
+
108
+
109
+ # This helper function recursively yields relative pathnames inside a literal
110
+ # directory.
111
+
112
+
113
+ def glob2(dirname, pattern):
114
+ assert _isrecursive(pattern)
115
+ yield pattern[:0]
116
+ for x in _rlistdir(dirname):
117
+ yield x
118
+
119
+
120
+ # Recursively yields relative pathnames inside a literal directory.
121
+ def _rlistdir(dirname):
122
+ if not dirname:
123
+ if isinstance(dirname, bytes):
124
+ dirname = os.curdir.encode('ASCII')
125
+ else:
126
+ dirname = os.curdir
127
+ try:
128
+ names = os.listdir(dirname)
129
+ except os.error:
130
+ return
131
+ for x in names:
132
+ yield x
133
+ path = os.path.join(dirname, x) if dirname else x
134
+ for y in _rlistdir(path):
135
+ yield os.path.join(x, y)
136
+
137
+
138
+ magic_check = re.compile('([*?[])')
139
+ magic_check_bytes = re.compile(b'([*?[])')
140
+
141
+
142
+ def has_magic(s):
143
+ if isinstance(s, bytes):
144
+ match = magic_check_bytes.search(s)
145
+ else:
146
+ match = magic_check.search(s)
147
+ return match is not None
148
+
149
+
150
+ def _isrecursive(pattern):
151
+ if isinstance(pattern, bytes):
152
+ return pattern == b'**'
153
+ else:
154
+ return pattern == '**'
155
+
156
+
157
+ def escape(pathname):
158
+ """Escape all special characters.
159
+ """
160
+ # Escaping is done by wrapping any of "*?[" between square brackets.
161
+ # Metacharacters do not work in the drive part and shouldn't be escaped.
162
+ drive, pathname = os.path.splitdrive(pathname)
163
+ if isinstance(pathname, bytes):
164
+ pathname = magic_check_bytes.sub(br'[\1]', pathname)
165
+ else:
166
+ pathname = magic_check.sub(r'[\1]', pathname)
167
+ return drive + pathname
venv/lib/python3.10/site-packages/setuptools/gui-32.exe ADDED
Binary file (65.5 kB). View file
 
venv/lib/python3.10/site-packages/setuptools/gui-64.exe ADDED
Binary file (75.3 kB). View file
 
venv/lib/python3.10/site-packages/setuptools/gui.exe ADDED
Binary file (65.5 kB). View file
 
venv/lib/python3.10/site-packages/setuptools/installer.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glob
2
+ import os
3
+ import subprocess
4
+ import sys
5
+ import tempfile
6
+ import warnings
7
+ from distutils import log
8
+ from distutils.errors import DistutilsError
9
+
10
+ import pkg_resources
11
+ from setuptools.wheel import Wheel
12
+ from ._deprecation_warning import SetuptoolsDeprecationWarning
13
+
14
+
15
+ def _fixup_find_links(find_links):
16
+ """Ensure find-links option end-up being a list of strings."""
17
+ if isinstance(find_links, str):
18
+ return find_links.split()
19
+ assert isinstance(find_links, (tuple, list))
20
+ return find_links
21
+
22
+
23
+ def fetch_build_egg(dist, req): # noqa: C901 # is too complex (16) # FIXME
24
+ """Fetch an egg needed for building.
25
+
26
+ Use pip/wheel to fetch/build a wheel."""
27
+ warnings.warn(
28
+ "setuptools.installer is deprecated. Requirements should "
29
+ "be satisfied by a PEP 517 installer.",
30
+ SetuptoolsDeprecationWarning,
31
+ )
32
+ # Warn if wheel is not available
33
+ try:
34
+ pkg_resources.get_distribution('wheel')
35
+ except pkg_resources.DistributionNotFound:
36
+ dist.announce('WARNING: The wheel package is not available.', log.WARN)
37
+ # Ignore environment markers; if supplied, it is required.
38
+ req = strip_marker(req)
39
+ # Take easy_install options into account, but do not override relevant
40
+ # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll
41
+ # take precedence.
42
+ opts = dist.get_option_dict('easy_install')
43
+ if 'allow_hosts' in opts:
44
+ raise DistutilsError('the `allow-hosts` option is not supported '
45
+ 'when using pip to install requirements.')
46
+ quiet = 'PIP_QUIET' not in os.environ and 'PIP_VERBOSE' not in os.environ
47
+ if 'PIP_INDEX_URL' in os.environ:
48
+ index_url = None
49
+ elif 'index_url' in opts:
50
+ index_url = opts['index_url'][1]
51
+ else:
52
+ index_url = None
53
+ find_links = (
54
+ _fixup_find_links(opts['find_links'][1])[:] if 'find_links' in opts
55
+ else []
56
+ )
57
+ if dist.dependency_links:
58
+ find_links.extend(dist.dependency_links)
59
+ eggs_dir = os.path.realpath(dist.get_egg_cache_dir())
60
+ environment = pkg_resources.Environment()
61
+ for egg_dist in pkg_resources.find_distributions(eggs_dir):
62
+ if egg_dist in req and environment.can_add(egg_dist):
63
+ return egg_dist
64
+ with tempfile.TemporaryDirectory() as tmpdir:
65
+ cmd = [
66
+ sys.executable, '-m', 'pip',
67
+ '--disable-pip-version-check',
68
+ 'wheel', '--no-deps',
69
+ '-w', tmpdir,
70
+ ]
71
+ if quiet:
72
+ cmd.append('--quiet')
73
+ if index_url is not None:
74
+ cmd.extend(('--index-url', index_url))
75
+ for link in find_links or []:
76
+ cmd.extend(('--find-links', link))
77
+ # If requirement is a PEP 508 direct URL, directly pass
78
+ # the URL to pip, as `req @ url` does not work on the
79
+ # command line.
80
+ cmd.append(req.url or str(req))
81
+ try:
82
+ subprocess.check_call(cmd)
83
+ except subprocess.CalledProcessError as e:
84
+ raise DistutilsError(str(e)) from e
85
+ wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0])
86
+ dist_location = os.path.join(eggs_dir, wheel.egg_name())
87
+ wheel.install_as_egg(dist_location)
88
+ dist_metadata = pkg_resources.PathMetadata(
89
+ dist_location, os.path.join(dist_location, 'EGG-INFO'))
90
+ dist = pkg_resources.Distribution.from_filename(
91
+ dist_location, metadata=dist_metadata)
92
+ return dist
93
+
94
+
95
+ def strip_marker(req):
96
+ """
97
+ Return a new requirement without the environment marker to avoid
98
+ calling pip with something like `babel; extra == "i18n"`, which
99
+ would always be ignored.
100
+ """
101
+ # create a copy to avoid mutating the input
102
+ req = pkg_resources.Requirement.parse(str(req))
103
+ req.marker = None
104
+ return req
venv/lib/python3.10/site-packages/setuptools/launch.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Launch the Python script on the command line after
3
+ setuptools is bootstrapped via import.
4
+ """
5
+
6
+ # Note that setuptools gets imported implicitly by the
7
+ # invocation of this script using python -m setuptools.launch
8
+
9
+ import tokenize
10
+ import sys
11
+
12
+
13
+ def run():
14
+ """
15
+ Run the script in sys.argv[1] as if it had
16
+ been invoked naturally.
17
+ """
18
+ __builtins__
19
+ script_name = sys.argv[1]
20
+ namespace = dict(
21
+ __file__=script_name,
22
+ __name__='__main__',
23
+ __doc__=None,
24
+ )
25
+ sys.argv[:] = sys.argv[1:]
26
+
27
+ open_ = getattr(tokenize, 'open', open)
28
+ with open_(script_name) as fid:
29
+ script = fid.read()
30
+ norm_script = script.replace('\\r\\n', '\\n')
31
+ code = compile(norm_script, script_name, 'exec')
32
+ exec(code, namespace)
33
+
34
+
35
+ if __name__ == '__main__':
36
+ run()
venv/lib/python3.10/site-packages/setuptools/monkey.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Monkey patching of distutils.
3
+ """
4
+
5
+ import sys
6
+ import distutils.filelist
7
+ import platform
8
+ import types
9
+ import functools
10
+ from importlib import import_module
11
+ import inspect
12
+
13
+ import setuptools
14
+
15
+ __all__ = []
16
+ """
17
+ Everything is private. Contact the project team
18
+ if you think you need this functionality.
19
+ """
20
+
21
+
22
+ def _get_mro(cls):
23
+ """
24
+ Returns the bases classes for cls sorted by the MRO.
25
+
26
+ Works around an issue on Jython where inspect.getmro will not return all
27
+ base classes if multiple classes share the same name. Instead, this
28
+ function will return a tuple containing the class itself, and the contents
29
+ of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024.
30
+ """
31
+ if platform.python_implementation() == "Jython":
32
+ return (cls,) + cls.__bases__
33
+ return inspect.getmro(cls)
34
+
35
+
36
+ def get_unpatched(item):
37
+ lookup = (
38
+ get_unpatched_class if isinstance(item, type) else
39
+ get_unpatched_function if isinstance(item, types.FunctionType) else
40
+ lambda item: None
41
+ )
42
+ return lookup(item)
43
+
44
+
45
+ def get_unpatched_class(cls):
46
+ """Protect against re-patching the distutils if reloaded
47
+
48
+ Also ensures that no other distutils extension monkeypatched the distutils
49
+ first.
50
+ """
51
+ external_bases = (
52
+ cls
53
+ for cls in _get_mro(cls)
54
+ if not cls.__module__.startswith('setuptools')
55
+ )
56
+ base = next(external_bases)
57
+ if not base.__module__.startswith('distutils'):
58
+ msg = "distutils has already been patched by %r" % cls
59
+ raise AssertionError(msg)
60
+ return base
61
+
62
+
63
+ def patch_all():
64
+ # we can't patch distutils.cmd, alas
65
+ distutils.core.Command = setuptools.Command
66
+
67
+ has_issue_12885 = sys.version_info <= (3, 5, 3)
68
+
69
+ if has_issue_12885:
70
+ # fix findall bug in distutils (http://bugs.python.org/issue12885)
71
+ distutils.filelist.findall = setuptools.findall
72
+
73
+ needs_warehouse = (
74
+ sys.version_info < (2, 7, 13)
75
+ or
76
+ (3, 4) < sys.version_info < (3, 4, 6)
77
+ or
78
+ (3, 5) < sys.version_info <= (3, 5, 3)
79
+ )
80
+
81
+ if needs_warehouse:
82
+ warehouse = 'https://upload.pypi.org/legacy/'
83
+ distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse
84
+
85
+ _patch_distribution_metadata()
86
+
87
+ # Install Distribution throughout the distutils
88
+ for module in distutils.dist, distutils.core, distutils.cmd:
89
+ module.Distribution = setuptools.dist.Distribution
90
+
91
+ # Install the patched Extension
92
+ distutils.core.Extension = setuptools.extension.Extension
93
+ distutils.extension.Extension = setuptools.extension.Extension
94
+ if 'distutils.command.build_ext' in sys.modules:
95
+ sys.modules['distutils.command.build_ext'].Extension = (
96
+ setuptools.extension.Extension
97
+ )
98
+
99
+ patch_for_msvc_specialized_compiler()
100
+
101
+
102
+ def _patch_distribution_metadata():
103
+ """Patch write_pkg_file and read_pkg_file for higher metadata standards"""
104
+ for attr in ('write_pkg_file', 'read_pkg_file', 'get_metadata_version'):
105
+ new_val = getattr(setuptools.dist, attr)
106
+ setattr(distutils.dist.DistributionMetadata, attr, new_val)
107
+
108
+
109
+ def patch_func(replacement, target_mod, func_name):
110
+ """
111
+ Patch func_name in target_mod with replacement
112
+
113
+ Important - original must be resolved by name to avoid
114
+ patching an already patched function.
115
+ """
116
+ original = getattr(target_mod, func_name)
117
+
118
+ # set the 'unpatched' attribute on the replacement to
119
+ # point to the original.
120
+ vars(replacement).setdefault('unpatched', original)
121
+
122
+ # replace the function in the original module
123
+ setattr(target_mod, func_name, replacement)
124
+
125
+
126
+ def get_unpatched_function(candidate):
127
+ return getattr(candidate, 'unpatched')
128
+
129
+
130
+ def patch_for_msvc_specialized_compiler():
131
+ """
132
+ Patch functions in distutils to use standalone Microsoft Visual C++
133
+ compilers.
134
+ """
135
+ # import late to avoid circular imports on Python < 3.5
136
+ msvc = import_module('setuptools.msvc')
137
+
138
+ if platform.system() != 'Windows':
139
+ # Compilers only available on Microsoft Windows
140
+ return
141
+
142
+ def patch_params(mod_name, func_name):
143
+ """
144
+ Prepare the parameters for patch_func to patch indicated function.
145
+ """
146
+ repl_prefix = 'msvc9_' if 'msvc9' in mod_name else 'msvc14_'
147
+ repl_name = repl_prefix + func_name.lstrip('_')
148
+ repl = getattr(msvc, repl_name)
149
+ mod = import_module(mod_name)
150
+ if not hasattr(mod, func_name):
151
+ raise ImportError(func_name)
152
+ return repl, mod, func_name
153
+
154
+ # Python 2.7 to 3.4
155
+ msvc9 = functools.partial(patch_params, 'distutils.msvc9compiler')
156
+
157
+ # Python 3.5+
158
+ msvc14 = functools.partial(patch_params, 'distutils._msvccompiler')
159
+
160
+ try:
161
+ # Patch distutils.msvc9compiler
162
+ patch_func(*msvc9('find_vcvarsall'))
163
+ patch_func(*msvc9('query_vcvarsall'))
164
+ except ImportError:
165
+ pass
166
+
167
+ try:
168
+ # Patch distutils._msvccompiler._get_vc_env
169
+ patch_func(*msvc14('_get_vc_env'))
170
+ except ImportError:
171
+ pass
172
+
173
+ try:
174
+ # Patch distutils._msvccompiler.gen_lib_options for Numpy
175
+ patch_func(*msvc14('gen_lib_options'))
176
+ except ImportError:
177
+ pass
venv/lib/python3.10/site-packages/setuptools/msvc.py ADDED
@@ -0,0 +1,1805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Improved support for Microsoft Visual C++ compilers.
3
+
4
+ Known supported compilers:
5
+ --------------------------
6
+ Microsoft Visual C++ 9.0:
7
+ Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
8
+ Microsoft Windows SDK 6.1 (x86, x64, ia64)
9
+ Microsoft Windows SDK 7.0 (x86, x64, ia64)
10
+
11
+ Microsoft Visual C++ 10.0:
12
+ Microsoft Windows SDK 7.1 (x86, x64, ia64)
13
+
14
+ Microsoft Visual C++ 14.X:
15
+ Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
16
+ Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
17
+ Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64)
18
+
19
+ This may also support compilers shipped with compatible Visual Studio versions.
20
+ """
21
+
22
+ import json
23
+ from io import open
24
+ from os import listdir, pathsep
25
+ from os.path import join, isfile, isdir, dirname
26
+ import sys
27
+ import contextlib
28
+ import platform
29
+ import itertools
30
+ import subprocess
31
+ import distutils.errors
32
+ from setuptools.extern.packaging.version import LegacyVersion
33
+ from setuptools.extern.more_itertools import unique_everseen
34
+
35
+ from .monkey import get_unpatched
36
+
37
+ if platform.system() == 'Windows':
38
+ import winreg
39
+ from os import environ
40
+ else:
41
+ # Mock winreg and environ so the module can be imported on this platform.
42
+
43
+ class winreg:
44
+ HKEY_USERS = None
45
+ HKEY_CURRENT_USER = None
46
+ HKEY_LOCAL_MACHINE = None
47
+ HKEY_CLASSES_ROOT = None
48
+
49
+ environ = dict()
50
+
51
+ _msvc9_suppress_errors = (
52
+ # msvc9compiler isn't available on some platforms
53
+ ImportError,
54
+
55
+ # msvc9compiler raises DistutilsPlatformError in some
56
+ # environments. See #1118.
57
+ distutils.errors.DistutilsPlatformError,
58
+ )
59
+
60
+ try:
61
+ from distutils.msvc9compiler import Reg
62
+ except _msvc9_suppress_errors:
63
+ pass
64
+
65
+
66
+ def msvc9_find_vcvarsall(version):
67
+ """
68
+ Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone
69
+ compiler build for Python
70
+ (VCForPython / Microsoft Visual C++ Compiler for Python 2.7).
71
+
72
+ Fall back to original behavior when the standalone compiler is not
73
+ available.
74
+
75
+ Redirect the path of "vcvarsall.bat".
76
+
77
+ Parameters
78
+ ----------
79
+ version: float
80
+ Required Microsoft Visual C++ version.
81
+
82
+ Return
83
+ ------
84
+ str
85
+ vcvarsall.bat path
86
+ """
87
+ vc_base = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
88
+ key = vc_base % ('', version)
89
+ try:
90
+ # Per-user installs register the compiler path here
91
+ productdir = Reg.get_value(key, "installdir")
92
+ except KeyError:
93
+ try:
94
+ # All-user installs on a 64-bit system register here
95
+ key = vc_base % ('Wow6432Node\\', version)
96
+ productdir = Reg.get_value(key, "installdir")
97
+ except KeyError:
98
+ productdir = None
99
+
100
+ if productdir:
101
+ vcvarsall = join(productdir, "vcvarsall.bat")
102
+ if isfile(vcvarsall):
103
+ return vcvarsall
104
+
105
+ return get_unpatched(msvc9_find_vcvarsall)(version)
106
+
107
+
108
+ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
109
+ """
110
+ Patched "distutils.msvc9compiler.query_vcvarsall" for support extra
111
+ Microsoft Visual C++ 9.0 and 10.0 compilers.
112
+
113
+ Set environment without use of "vcvarsall.bat".
114
+
115
+ Parameters
116
+ ----------
117
+ ver: float
118
+ Required Microsoft Visual C++ version.
119
+ arch: str
120
+ Target architecture.
121
+
122
+ Return
123
+ ------
124
+ dict
125
+ environment
126
+ """
127
+ # Try to get environment from vcvarsall.bat (Classical way)
128
+ try:
129
+ orig = get_unpatched(msvc9_query_vcvarsall)
130
+ return orig(ver, arch, *args, **kwargs)
131
+ except distutils.errors.DistutilsPlatformError:
132
+ # Pass error if Vcvarsall.bat is missing
133
+ pass
134
+ except ValueError:
135
+ # Pass error if environment not set after executing vcvarsall.bat
136
+ pass
137
+
138
+ # If error, try to set environment directly
139
+ try:
140
+ return EnvironmentInfo(arch, ver).return_env()
141
+ except distutils.errors.DistutilsPlatformError as exc:
142
+ _augment_exception(exc, ver, arch)
143
+ raise
144
+
145
+
146
+ def _msvc14_find_vc2015():
147
+ """Python 3.8 "distutils/_msvccompiler.py" backport"""
148
+ try:
149
+ key = winreg.OpenKey(
150
+ winreg.HKEY_LOCAL_MACHINE,
151
+ r"Software\Microsoft\VisualStudio\SxS\VC7",
152
+ 0,
153
+ winreg.KEY_READ | winreg.KEY_WOW64_32KEY
154
+ )
155
+ except OSError:
156
+ return None, None
157
+
158
+ best_version = 0
159
+ best_dir = None
160
+ with key:
161
+ for i in itertools.count():
162
+ try:
163
+ v, vc_dir, vt = winreg.EnumValue(key, i)
164
+ except OSError:
165
+ break
166
+ if v and vt == winreg.REG_SZ and isdir(vc_dir):
167
+ try:
168
+ version = int(float(v))
169
+ except (ValueError, TypeError):
170
+ continue
171
+ if version >= 14 and version > best_version:
172
+ best_version, best_dir = version, vc_dir
173
+ return best_version, best_dir
174
+
175
+
176
+ def _msvc14_find_vc2017():
177
+ """Python 3.8 "distutils/_msvccompiler.py" backport
178
+
179
+ Returns "15, path" based on the result of invoking vswhere.exe
180
+ If no install is found, returns "None, None"
181
+
182
+ The version is returned to avoid unnecessarily changing the function
183
+ result. It may be ignored when the path is not None.
184
+
185
+ If vswhere.exe is not available, by definition, VS 2017 is not
186
+ installed.
187
+ """
188
+ root = environ.get("ProgramFiles(x86)") or environ.get("ProgramFiles")
189
+ if not root:
190
+ return None, None
191
+
192
+ try:
193
+ path = subprocess.check_output([
194
+ join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
195
+ "-latest",
196
+ "-prerelease",
197
+ "-requiresAny",
198
+ "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
199
+ "-requires", "Microsoft.VisualStudio.Workload.WDExpress",
200
+ "-property", "installationPath",
201
+ "-products", "*",
202
+ ]).decode(encoding="mbcs", errors="strict").strip()
203
+ except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
204
+ return None, None
205
+
206
+ path = join(path, "VC", "Auxiliary", "Build")
207
+ if isdir(path):
208
+ return 15, path
209
+
210
+ return None, None
211
+
212
+
213
+ PLAT_SPEC_TO_RUNTIME = {
214
+ 'x86': 'x86',
215
+ 'x86_amd64': 'x64',
216
+ 'x86_arm': 'arm',
217
+ 'x86_arm64': 'arm64'
218
+ }
219
+
220
+
221
+ def _msvc14_find_vcvarsall(plat_spec):
222
+ """Python 3.8 "distutils/_msvccompiler.py" backport"""
223
+ _, best_dir = _msvc14_find_vc2017()
224
+ vcruntime = None
225
+
226
+ if plat_spec in PLAT_SPEC_TO_RUNTIME:
227
+ vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec]
228
+ else:
229
+ vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86'
230
+
231
+ if best_dir:
232
+ vcredist = join(best_dir, "..", "..", "redist", "MSVC", "**",
233
+ vcruntime_plat, "Microsoft.VC14*.CRT",
234
+ "vcruntime140.dll")
235
+ try:
236
+ import glob
237
+ vcruntime = glob.glob(vcredist, recursive=True)[-1]
238
+ except (ImportError, OSError, LookupError):
239
+ vcruntime = None
240
+
241
+ if not best_dir:
242
+ best_version, best_dir = _msvc14_find_vc2015()
243
+ if best_version:
244
+ vcruntime = join(best_dir, 'redist', vcruntime_plat,
245
+ "Microsoft.VC140.CRT", "vcruntime140.dll")
246
+
247
+ if not best_dir:
248
+ return None, None
249
+
250
+ vcvarsall = join(best_dir, "vcvarsall.bat")
251
+ if not isfile(vcvarsall):
252
+ return None, None
253
+
254
+ if not vcruntime or not isfile(vcruntime):
255
+ vcruntime = None
256
+
257
+ return vcvarsall, vcruntime
258
+
259
+
260
+ def _msvc14_get_vc_env(plat_spec):
261
+ """Python 3.8 "distutils/_msvccompiler.py" backport"""
262
+ if "DISTUTILS_USE_SDK" in environ:
263
+ return {
264
+ key.lower(): value
265
+ for key, value in environ.items()
266
+ }
267
+
268
+ vcvarsall, vcruntime = _msvc14_find_vcvarsall(plat_spec)
269
+ if not vcvarsall:
270
+ raise distutils.errors.DistutilsPlatformError(
271
+ "Unable to find vcvarsall.bat"
272
+ )
273
+
274
+ try:
275
+ out = subprocess.check_output(
276
+ 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
277
+ stderr=subprocess.STDOUT,
278
+ ).decode('utf-16le', errors='replace')
279
+ except subprocess.CalledProcessError as exc:
280
+ raise distutils.errors.DistutilsPlatformError(
281
+ "Error executing {}".format(exc.cmd)
282
+ ) from exc
283
+
284
+ env = {
285
+ key.lower(): value
286
+ for key, _, value in
287
+ (line.partition('=') for line in out.splitlines())
288
+ if key and value
289
+ }
290
+
291
+ if vcruntime:
292
+ env['py_vcruntime_redist'] = vcruntime
293
+ return env
294
+
295
+
296
+ def msvc14_get_vc_env(plat_spec):
297
+ """
298
+ Patched "distutils._msvccompiler._get_vc_env" for support extra
299
+ Microsoft Visual C++ 14.X compilers.
300
+
301
+ Set environment without use of "vcvarsall.bat".
302
+
303
+ Parameters
304
+ ----------
305
+ plat_spec: str
306
+ Target architecture.
307
+
308
+ Return
309
+ ------
310
+ dict
311
+ environment
312
+ """
313
+
314
+ # Always use backport from CPython 3.8
315
+ try:
316
+ return _msvc14_get_vc_env(plat_spec)
317
+ except distutils.errors.DistutilsPlatformError as exc:
318
+ _augment_exception(exc, 14.0)
319
+ raise
320
+
321
+
322
+ def msvc14_gen_lib_options(*args, **kwargs):
323
+ """
324
+ Patched "distutils._msvccompiler.gen_lib_options" for fix
325
+ compatibility between "numpy.distutils" and "distutils._msvccompiler"
326
+ (for Numpy < 1.11.2)
327
+ """
328
+ if "numpy.distutils" in sys.modules:
329
+ import numpy as np
330
+ if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'):
331
+ return np.distutils.ccompiler.gen_lib_options(*args, **kwargs)
332
+ return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs)
333
+
334
+
335
+ def _augment_exception(exc, version, arch=''):
336
+ """
337
+ Add details to the exception message to help guide the user
338
+ as to what action will resolve it.
339
+ """
340
+ # Error if MSVC++ directory not found or environment not set
341
+ message = exc.args[0]
342
+
343
+ if "vcvarsall" in message.lower() or "visual c" in message.lower():
344
+ # Special error message if MSVC++ not installed
345
+ tmpl = 'Microsoft Visual C++ {version:0.1f} or greater is required.'
346
+ message = tmpl.format(**locals())
347
+ msdownload = 'www.microsoft.com/download/details.aspx?id=%d'
348
+ if version == 9.0:
349
+ if arch.lower().find('ia64') > -1:
350
+ # For VC++ 9.0, if IA64 support is needed, redirect user
351
+ # to Windows SDK 7.0.
352
+ # Note: No download link available from Microsoft.
353
+ message += ' Get it with "Microsoft Windows SDK 7.0"'
354
+ else:
355
+ # For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
356
+ # This redirection link is maintained by Microsoft.
357
+ # Contact vspython@microsoft.com if it needs updating.
358
+ message += ' Get it from http://aka.ms/vcpython27'
359
+ elif version == 10.0:
360
+ # For VC++ 10.0 Redirect user to Windows SDK 7.1
361
+ message += ' Get it with "Microsoft Windows SDK 7.1": '
362
+ message += msdownload % 8279
363
+ elif version >= 14.0:
364
+ # For VC++ 14.X Redirect user to latest Visual C++ Build Tools
365
+ message += (' Get it with "Microsoft C++ Build Tools": '
366
+ r'https://visualstudio.microsoft.com'
367
+ r'/visual-cpp-build-tools/')
368
+
369
+ exc.args = (message, )
370
+
371
+
372
+ class PlatformInfo:
373
+ """
374
+ Current and Target Architectures information.
375
+
376
+ Parameters
377
+ ----------
378
+ arch: str
379
+ Target architecture.
380
+ """
381
+ current_cpu = environ.get('processor_architecture', '').lower()
382
+
383
+ def __init__(self, arch):
384
+ self.arch = arch.lower().replace('x64', 'amd64')
385
+
386
+ @property
387
+ def target_cpu(self):
388
+ """
389
+ Return Target CPU architecture.
390
+
391
+ Return
392
+ ------
393
+ str
394
+ Target CPU
395
+ """
396
+ return self.arch[self.arch.find('_') + 1:]
397
+
398
+ def target_is_x86(self):
399
+ """
400
+ Return True if target CPU is x86 32 bits..
401
+
402
+ Return
403
+ ------
404
+ bool
405
+ CPU is x86 32 bits
406
+ """
407
+ return self.target_cpu == 'x86'
408
+
409
+ def current_is_x86(self):
410
+ """
411
+ Return True if current CPU is x86 32 bits..
412
+
413
+ Return
414
+ ------
415
+ bool
416
+ CPU is x86 32 bits
417
+ """
418
+ return self.current_cpu == 'x86'
419
+
420
+ def current_dir(self, hidex86=False, x64=False):
421
+ """
422
+ Current platform specific subfolder.
423
+
424
+ Parameters
425
+ ----------
426
+ hidex86: bool
427
+ return '' and not '\x86' if architecture is x86.
428
+ x64: bool
429
+ return '\x64' and not '\amd64' if architecture is amd64.
430
+
431
+ Return
432
+ ------
433
+ str
434
+ subfolder: '\target', or '' (see hidex86 parameter)
435
+ """
436
+ return (
437
+ '' if (self.current_cpu == 'x86' and hidex86) else
438
+ r'\x64' if (self.current_cpu == 'amd64' and x64) else
439
+ r'\%s' % self.current_cpu
440
+ )
441
+
442
+ def target_dir(self, hidex86=False, x64=False):
443
+ r"""
444
+ Target platform specific subfolder.
445
+
446
+ Parameters
447
+ ----------
448
+ hidex86: bool
449
+ return '' and not '\x86' if architecture is x86.
450
+ x64: bool
451
+ return '\x64' and not '\amd64' if architecture is amd64.
452
+
453
+ Return
454
+ ------
455
+ str
456
+ subfolder: '\current', or '' (see hidex86 parameter)
457
+ """
458
+ return (
459
+ '' if (self.target_cpu == 'x86' and hidex86) else
460
+ r'\x64' if (self.target_cpu == 'amd64' and x64) else
461
+ r'\%s' % self.target_cpu
462
+ )
463
+
464
+ def cross_dir(self, forcex86=False):
465
+ r"""
466
+ Cross platform specific subfolder.
467
+
468
+ Parameters
469
+ ----------
470
+ forcex86: bool
471
+ Use 'x86' as current architecture even if current architecture is
472
+ not x86.
473
+
474
+ Return
475
+ ------
476
+ str
477
+ subfolder: '' if target architecture is current architecture,
478
+ '\current_target' if not.
479
+ """
480
+ current = 'x86' if forcex86 else self.current_cpu
481
+ return (
482
+ '' if self.target_cpu == current else
483
+ self.target_dir().replace('\\', '\\%s_' % current)
484
+ )
485
+
486
+
487
+ class RegistryInfo:
488
+ """
489
+ Microsoft Visual Studio related registry information.
490
+
491
+ Parameters
492
+ ----------
493
+ platform_info: PlatformInfo
494
+ "PlatformInfo" instance.
495
+ """
496
+ HKEYS = (winreg.HKEY_USERS,
497
+ winreg.HKEY_CURRENT_USER,
498
+ winreg.HKEY_LOCAL_MACHINE,
499
+ winreg.HKEY_CLASSES_ROOT)
500
+
501
+ def __init__(self, platform_info):
502
+ self.pi = platform_info
503
+
504
+ @property
505
+ def visualstudio(self):
506
+ """
507
+ Microsoft Visual Studio root registry key.
508
+
509
+ Return
510
+ ------
511
+ str
512
+ Registry key
513
+ """
514
+ return 'VisualStudio'
515
+
516
+ @property
517
+ def sxs(self):
518
+ """
519
+ Microsoft Visual Studio SxS registry key.
520
+
521
+ Return
522
+ ------
523
+ str
524
+ Registry key
525
+ """
526
+ return join(self.visualstudio, 'SxS')
527
+
528
+ @property
529
+ def vc(self):
530
+ """
531
+ Microsoft Visual C++ VC7 registry key.
532
+
533
+ Return
534
+ ------
535
+ str
536
+ Registry key
537
+ """
538
+ return join(self.sxs, 'VC7')
539
+
540
+ @property
541
+ def vs(self):
542
+ """
543
+ Microsoft Visual Studio VS7 registry key.
544
+
545
+ Return
546
+ ------
547
+ str
548
+ Registry key
549
+ """
550
+ return join(self.sxs, 'VS7')
551
+
552
+ @property
553
+ def vc_for_python(self):
554
+ """
555
+ Microsoft Visual C++ for Python registry key.
556
+
557
+ Return
558
+ ------
559
+ str
560
+ Registry key
561
+ """
562
+ return r'DevDiv\VCForPython'
563
+
564
+ @property
565
+ def microsoft_sdk(self):
566
+ """
567
+ Microsoft SDK registry key.
568
+
569
+ Return
570
+ ------
571
+ str
572
+ Registry key
573
+ """
574
+ return 'Microsoft SDKs'
575
+
576
+ @property
577
+ def windows_sdk(self):
578
+ """
579
+ Microsoft Windows/Platform SDK registry key.
580
+
581
+ Return
582
+ ------
583
+ str
584
+ Registry key
585
+ """
586
+ return join(self.microsoft_sdk, 'Windows')
587
+
588
+ @property
589
+ def netfx_sdk(self):
590
+ """
591
+ Microsoft .NET Framework SDK registry key.
592
+
593
+ Return
594
+ ------
595
+ str
596
+ Registry key
597
+ """
598
+ return join(self.microsoft_sdk, 'NETFXSDK')
599
+
600
+ @property
601
+ def windows_kits_roots(self):
602
+ """
603
+ Microsoft Windows Kits Roots registry key.
604
+
605
+ Return
606
+ ------
607
+ str
608
+ Registry key
609
+ """
610
+ return r'Windows Kits\Installed Roots'
611
+
612
+ def microsoft(self, key, x86=False):
613
+ """
614
+ Return key in Microsoft software registry.
615
+
616
+ Parameters
617
+ ----------
618
+ key: str
619
+ Registry key path where look.
620
+ x86: str
621
+ Force x86 software registry.
622
+
623
+ Return
624
+ ------
625
+ str
626
+ Registry key
627
+ """
628
+ node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
629
+ return join('Software', node64, 'Microsoft', key)
630
+
631
+ def lookup(self, key, name):
632
+ """
633
+ Look for values in registry in Microsoft software registry.
634
+
635
+ Parameters
636
+ ----------
637
+ key: str
638
+ Registry key path where look.
639
+ name: str
640
+ Value name to find.
641
+
642
+ Return
643
+ ------
644
+ str
645
+ value
646
+ """
647
+ key_read = winreg.KEY_READ
648
+ openkey = winreg.OpenKey
649
+ closekey = winreg.CloseKey
650
+ ms = self.microsoft
651
+ for hkey in self.HKEYS:
652
+ bkey = None
653
+ try:
654
+ bkey = openkey(hkey, ms(key), 0, key_read)
655
+ except (OSError, IOError):
656
+ if not self.pi.current_is_x86():
657
+ try:
658
+ bkey = openkey(hkey, ms(key, True), 0, key_read)
659
+ except (OSError, IOError):
660
+ continue
661
+ else:
662
+ continue
663
+ try:
664
+ return winreg.QueryValueEx(bkey, name)[0]
665
+ except (OSError, IOError):
666
+ pass
667
+ finally:
668
+ if bkey:
669
+ closekey(bkey)
670
+
671
+
672
+ class SystemInfo:
673
+ """
674
+ Microsoft Windows and Visual Studio related system information.
675
+
676
+ Parameters
677
+ ----------
678
+ registry_info: RegistryInfo
679
+ "RegistryInfo" instance.
680
+ vc_ver: float
681
+ Required Microsoft Visual C++ version.
682
+ """
683
+
684
+ # Variables and properties in this class use originals CamelCase variables
685
+ # names from Microsoft source files for more easy comparison.
686
+ WinDir = environ.get('WinDir', '')
687
+ ProgramFiles = environ.get('ProgramFiles', '')
688
+ ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
689
+
690
+ def __init__(self, registry_info, vc_ver=None):
691
+ self.ri = registry_info
692
+ self.pi = self.ri.pi
693
+
694
+ self.known_vs_paths = self.find_programdata_vs_vers()
695
+
696
+ # Except for VS15+, VC version is aligned with VS version
697
+ self.vs_ver = self.vc_ver = (
698
+ vc_ver or self._find_latest_available_vs_ver())
699
+
700
+ def _find_latest_available_vs_ver(self):
701
+ """
702
+ Find the latest VC version
703
+
704
+ Return
705
+ ------
706
+ float
707
+ version
708
+ """
709
+ reg_vc_vers = self.find_reg_vs_vers()
710
+
711
+ if not (reg_vc_vers or self.known_vs_paths):
712
+ raise distutils.errors.DistutilsPlatformError(
713
+ 'No Microsoft Visual C++ version found')
714
+
715
+ vc_vers = set(reg_vc_vers)
716
+ vc_vers.update(self.known_vs_paths)
717
+ return sorted(vc_vers)[-1]
718
+
719
+ def find_reg_vs_vers(self):
720
+ """
721
+ Find Microsoft Visual Studio versions available in registry.
722
+
723
+ Return
724
+ ------
725
+ list of float
726
+ Versions
727
+ """
728
+ ms = self.ri.microsoft
729
+ vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
730
+ vs_vers = []
731
+ for hkey, key in itertools.product(self.ri.HKEYS, vckeys):
732
+ try:
733
+ bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
734
+ except (OSError, IOError):
735
+ continue
736
+ with bkey:
737
+ subkeys, values, _ = winreg.QueryInfoKey(bkey)
738
+ for i in range(values):
739
+ with contextlib.suppress(ValueError):
740
+ ver = float(winreg.EnumValue(bkey, i)[0])
741
+ if ver not in vs_vers:
742
+ vs_vers.append(ver)
743
+ for i in range(subkeys):
744
+ with contextlib.suppress(ValueError):
745
+ ver = float(winreg.EnumKey(bkey, i))
746
+ if ver not in vs_vers:
747
+ vs_vers.append(ver)
748
+ return sorted(vs_vers)
749
+
750
+ def find_programdata_vs_vers(self):
751
+ r"""
752
+ Find Visual studio 2017+ versions from information in
753
+ "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
754
+
755
+ Return
756
+ ------
757
+ dict
758
+ float version as key, path as value.
759
+ """
760
+ vs_versions = {}
761
+ instances_dir = \
762
+ r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
763
+
764
+ try:
765
+ hashed_names = listdir(instances_dir)
766
+
767
+ except (OSError, IOError):
768
+ # Directory not exists with all Visual Studio versions
769
+ return vs_versions
770
+
771
+ for name in hashed_names:
772
+ try:
773
+ # Get VS installation path from "state.json" file
774
+ state_path = join(instances_dir, name, 'state.json')
775
+ with open(state_path, 'rt', encoding='utf-8') as state_file:
776
+ state = json.load(state_file)
777
+ vs_path = state['installationPath']
778
+
779
+ # Raises OSError if this VS installation does not contain VC
780
+ listdir(join(vs_path, r'VC\Tools\MSVC'))
781
+
782
+ # Store version and path
783
+ vs_versions[self._as_float_version(
784
+ state['installationVersion'])] = vs_path
785
+
786
+ except (OSError, IOError, KeyError):
787
+ # Skip if "state.json" file is missing or bad format
788
+ continue
789
+
790
+ return vs_versions
791
+
792
+ @staticmethod
793
+ def _as_float_version(version):
794
+ """
795
+ Return a string version as a simplified float version (major.minor)
796
+
797
+ Parameters
798
+ ----------
799
+ version: str
800
+ Version.
801
+
802
+ Return
803
+ ------
804
+ float
805
+ version
806
+ """
807
+ return float('.'.join(version.split('.')[:2]))
808
+
809
+ @property
810
+ def VSInstallDir(self):
811
+ """
812
+ Microsoft Visual Studio directory.
813
+
814
+ Return
815
+ ------
816
+ str
817
+ path
818
+ """
819
+ # Default path
820
+ default = join(self.ProgramFilesx86,
821
+ 'Microsoft Visual Studio %0.1f' % self.vs_ver)
822
+
823
+ # Try to get path from registry, if fail use default path
824
+ return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default
825
+
826
+ @property
827
+ def VCInstallDir(self):
828
+ """
829
+ Microsoft Visual C++ directory.
830
+
831
+ Return
832
+ ------
833
+ str
834
+ path
835
+ """
836
+ path = self._guess_vc() or self._guess_vc_legacy()
837
+
838
+ if not isdir(path):
839
+ msg = 'Microsoft Visual C++ directory not found'
840
+ raise distutils.errors.DistutilsPlatformError(msg)
841
+
842
+ return path
843
+
844
+ def _guess_vc(self):
845
+ """
846
+ Locate Visual C++ for VS2017+.
847
+
848
+ Return
849
+ ------
850
+ str
851
+ path
852
+ """
853
+ if self.vs_ver <= 14.0:
854
+ return ''
855
+
856
+ try:
857
+ # First search in known VS paths
858
+ vs_dir = self.known_vs_paths[self.vs_ver]
859
+ except KeyError:
860
+ # Else, search with path from registry
861
+ vs_dir = self.VSInstallDir
862
+
863
+ guess_vc = join(vs_dir, r'VC\Tools\MSVC')
864
+
865
+ # Subdir with VC exact version as name
866
+ try:
867
+ # Update the VC version with real one instead of VS version
868
+ vc_ver = listdir(guess_vc)[-1]
869
+ self.vc_ver = self._as_float_version(vc_ver)
870
+ return join(guess_vc, vc_ver)
871
+ except (OSError, IOError, IndexError):
872
+ return ''
873
+
874
+ def _guess_vc_legacy(self):
875
+ """
876
+ Locate Visual C++ for versions prior to 2017.
877
+
878
+ Return
879
+ ------
880
+ str
881
+ path
882
+ """
883
+ default = join(self.ProgramFilesx86,
884
+ r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver)
885
+
886
+ # Try to get "VC++ for Python" path from registry as default path
887
+ reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver)
888
+ python_vc = self.ri.lookup(reg_path, 'installdir')
889
+ default_vc = join(python_vc, 'VC') if python_vc else default
890
+
891
+ # Try to get path from registry, if fail use default path
892
+ return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc
893
+
894
+ @property
895
+ def WindowsSdkVersion(self):
896
+ """
897
+ Microsoft Windows SDK versions for specified MSVC++ version.
898
+
899
+ Return
900
+ ------
901
+ tuple of str
902
+ versions
903
+ """
904
+ if self.vs_ver <= 9.0:
905
+ return '7.0', '6.1', '6.0a'
906
+ elif self.vs_ver == 10.0:
907
+ return '7.1', '7.0a'
908
+ elif self.vs_ver == 11.0:
909
+ return '8.0', '8.0a'
910
+ elif self.vs_ver == 12.0:
911
+ return '8.1', '8.1a'
912
+ elif self.vs_ver >= 14.0:
913
+ return '10.0', '8.1'
914
+
915
+ @property
916
+ def WindowsSdkLastVersion(self):
917
+ """
918
+ Microsoft Windows SDK last version.
919
+
920
+ Return
921
+ ------
922
+ str
923
+ version
924
+ """
925
+ return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib'))
926
+
927
+ @property # noqa: C901
928
+ def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME
929
+ """
930
+ Microsoft Windows SDK directory.
931
+
932
+ Return
933
+ ------
934
+ str
935
+ path
936
+ """
937
+ sdkdir = ''
938
+ for ver in self.WindowsSdkVersion:
939
+ # Try to get it from registry
940
+ loc = join(self.ri.windows_sdk, 'v%s' % ver)
941
+ sdkdir = self.ri.lookup(loc, 'installationfolder')
942
+ if sdkdir:
943
+ break
944
+ if not sdkdir or not isdir(sdkdir):
945
+ # Try to get "VC++ for Python" version from registry
946
+ path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
947
+ install_base = self.ri.lookup(path, 'installdir')
948
+ if install_base:
949
+ sdkdir = join(install_base, 'WinSDK')
950
+ if not sdkdir or not isdir(sdkdir):
951
+ # If fail, use default new path
952
+ for ver in self.WindowsSdkVersion:
953
+ intver = ver[:ver.rfind('.')]
954
+ path = r'Microsoft SDKs\Windows Kits\%s' % intver
955
+ d = join(self.ProgramFiles, path)
956
+ if isdir(d):
957
+ sdkdir = d
958
+ if not sdkdir or not isdir(sdkdir):
959
+ # If fail, use default old path
960
+ for ver in self.WindowsSdkVersion:
961
+ path = r'Microsoft SDKs\Windows\v%s' % ver
962
+ d = join(self.ProgramFiles, path)
963
+ if isdir(d):
964
+ sdkdir = d
965
+ if not sdkdir:
966
+ # If fail, use Platform SDK
967
+ sdkdir = join(self.VCInstallDir, 'PlatformSDK')
968
+ return sdkdir
969
+
970
+ @property
971
+ def WindowsSDKExecutablePath(self):
972
+ """
973
+ Microsoft Windows SDK executable directory.
974
+
975
+ Return
976
+ ------
977
+ str
978
+ path
979
+ """
980
+ # Find WinSDK NetFx Tools registry dir name
981
+ if self.vs_ver <= 11.0:
982
+ netfxver = 35
983
+ arch = ''
984
+ else:
985
+ netfxver = 40
986
+ hidex86 = True if self.vs_ver <= 12.0 else False
987
+ arch = self.pi.current_dir(x64=True, hidex86=hidex86)
988
+ fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
989
+
990
+ # list all possibles registry paths
991
+ regpaths = []
992
+ if self.vs_ver >= 14.0:
993
+ for ver in self.NetFxSdkVersion:
994
+ regpaths += [join(self.ri.netfx_sdk, ver, fx)]
995
+
996
+ for ver in self.WindowsSdkVersion:
997
+ regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
998
+
999
+ # Return installation folder from the more recent path
1000
+ for path in regpaths:
1001
+ execpath = self.ri.lookup(path, 'installationfolder')
1002
+ if execpath:
1003
+ return execpath
1004
+
1005
+ @property
1006
+ def FSharpInstallDir(self):
1007
+ """
1008
+ Microsoft Visual F# directory.
1009
+
1010
+ Return
1011
+ ------
1012
+ str
1013
+ path
1014
+ """
1015
+ path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver)
1016
+ return self.ri.lookup(path, 'productdir') or ''
1017
+
1018
+ @property
1019
+ def UniversalCRTSdkDir(self):
1020
+ """
1021
+ Microsoft Universal CRT SDK directory.
1022
+
1023
+ Return
1024
+ ------
1025
+ str
1026
+ path
1027
+ """
1028
+ # Set Kit Roots versions for specified MSVC++ version
1029
+ vers = ('10', '81') if self.vs_ver >= 14.0 else ()
1030
+
1031
+ # Find path of the more recent Kit
1032
+ for ver in vers:
1033
+ sdkdir = self.ri.lookup(self.ri.windows_kits_roots,
1034
+ 'kitsroot%s' % ver)
1035
+ if sdkdir:
1036
+ return sdkdir or ''
1037
+
1038
+ @property
1039
+ def UniversalCRTSdkLastVersion(self):
1040
+ """
1041
+ Microsoft Universal C Runtime SDK last version.
1042
+
1043
+ Return
1044
+ ------
1045
+ str
1046
+ version
1047
+ """
1048
+ return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib'))
1049
+
1050
+ @property
1051
+ def NetFxSdkVersion(self):
1052
+ """
1053
+ Microsoft .NET Framework SDK versions.
1054
+
1055
+ Return
1056
+ ------
1057
+ tuple of str
1058
+ versions
1059
+ """
1060
+ # Set FxSdk versions for specified VS version
1061
+ return (('4.7.2', '4.7.1', '4.7',
1062
+ '4.6.2', '4.6.1', '4.6',
1063
+ '4.5.2', '4.5.1', '4.5')
1064
+ if self.vs_ver >= 14.0 else ())
1065
+
1066
+ @property
1067
+ def NetFxSdkDir(self):
1068
+ """
1069
+ Microsoft .NET Framework SDK directory.
1070
+
1071
+ Return
1072
+ ------
1073
+ str
1074
+ path
1075
+ """
1076
+ sdkdir = ''
1077
+ for ver in self.NetFxSdkVersion:
1078
+ loc = join(self.ri.netfx_sdk, ver)
1079
+ sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
1080
+ if sdkdir:
1081
+ break
1082
+ return sdkdir
1083
+
1084
+ @property
1085
+ def FrameworkDir32(self):
1086
+ """
1087
+ Microsoft .NET Framework 32bit directory.
1088
+
1089
+ Return
1090
+ ------
1091
+ str
1092
+ path
1093
+ """
1094
+ # Default path
1095
+ guess_fw = join(self.WinDir, r'Microsoft.NET\Framework')
1096
+
1097
+ # Try to get path from registry, if fail use default path
1098
+ return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
1099
+
1100
+ @property
1101
+ def FrameworkDir64(self):
1102
+ """
1103
+ Microsoft .NET Framework 64bit directory.
1104
+
1105
+ Return
1106
+ ------
1107
+ str
1108
+ path
1109
+ """
1110
+ # Default path
1111
+ guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64')
1112
+
1113
+ # Try to get path from registry, if fail use default path
1114
+ return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
1115
+
1116
+ @property
1117
+ def FrameworkVersion32(self):
1118
+ """
1119
+ Microsoft .NET Framework 32bit versions.
1120
+
1121
+ Return
1122
+ ------
1123
+ tuple of str
1124
+ versions
1125
+ """
1126
+ return self._find_dot_net_versions(32)
1127
+
1128
+ @property
1129
+ def FrameworkVersion64(self):
1130
+ """
1131
+ Microsoft .NET Framework 64bit versions.
1132
+
1133
+ Return
1134
+ ------
1135
+ tuple of str
1136
+ versions
1137
+ """
1138
+ return self._find_dot_net_versions(64)
1139
+
1140
+ def _find_dot_net_versions(self, bits):
1141
+ """
1142
+ Find Microsoft .NET Framework versions.
1143
+
1144
+ Parameters
1145
+ ----------
1146
+ bits: int
1147
+ Platform number of bits: 32 or 64.
1148
+
1149
+ Return
1150
+ ------
1151
+ tuple of str
1152
+ versions
1153
+ """
1154
+ # Find actual .NET version in registry
1155
+ reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
1156
+ dot_net_dir = getattr(self, 'FrameworkDir%d' % bits)
1157
+ ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
1158
+
1159
+ # Set .NET versions for specified MSVC++ version
1160
+ if self.vs_ver >= 12.0:
1161
+ return ver, 'v4.0'
1162
+ elif self.vs_ver >= 10.0:
1163
+ return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
1164
+ elif self.vs_ver == 9.0:
1165
+ return 'v3.5', 'v2.0.50727'
1166
+ elif self.vs_ver == 8.0:
1167
+ return 'v3.0', 'v2.0.50727'
1168
+
1169
+ @staticmethod
1170
+ def _use_last_dir_name(path, prefix=''):
1171
+ """
1172
+ Return name of the last dir in path or '' if no dir found.
1173
+
1174
+ Parameters
1175
+ ----------
1176
+ path: str
1177
+ Use dirs in this path
1178
+ prefix: str
1179
+ Use only dirs starting by this prefix
1180
+
1181
+ Return
1182
+ ------
1183
+ str
1184
+ name
1185
+ """
1186
+ matching_dirs = (
1187
+ dir_name
1188
+ for dir_name in reversed(listdir(path))
1189
+ if isdir(join(path, dir_name)) and
1190
+ dir_name.startswith(prefix)
1191
+ )
1192
+ return next(matching_dirs, None) or ''
1193
+
1194
+
1195
+ class EnvironmentInfo:
1196
+ """
1197
+ Return environment variables for specified Microsoft Visual C++ version
1198
+ and platform : Lib, Include, Path and libpath.
1199
+
1200
+ This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
1201
+
1202
+ Script created by analysing Microsoft environment configuration files like
1203
+ "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
1204
+
1205
+ Parameters
1206
+ ----------
1207
+ arch: str
1208
+ Target architecture.
1209
+ vc_ver: float
1210
+ Required Microsoft Visual C++ version. If not set, autodetect the last
1211
+ version.
1212
+ vc_min_ver: float
1213
+ Minimum Microsoft Visual C++ version.
1214
+ """
1215
+
1216
+ # Variables and properties in this class use originals CamelCase variables
1217
+ # names from Microsoft source files for more easy comparison.
1218
+
1219
+ def __init__(self, arch, vc_ver=None, vc_min_ver=0):
1220
+ self.pi = PlatformInfo(arch)
1221
+ self.ri = RegistryInfo(self.pi)
1222
+ self.si = SystemInfo(self.ri, vc_ver)
1223
+
1224
+ if self.vc_ver < vc_min_ver:
1225
+ err = 'No suitable Microsoft Visual C++ version found'
1226
+ raise distutils.errors.DistutilsPlatformError(err)
1227
+
1228
+ @property
1229
+ def vs_ver(self):
1230
+ """
1231
+ Microsoft Visual Studio.
1232
+
1233
+ Return
1234
+ ------
1235
+ float
1236
+ version
1237
+ """
1238
+ return self.si.vs_ver
1239
+
1240
+ @property
1241
+ def vc_ver(self):
1242
+ """
1243
+ Microsoft Visual C++ version.
1244
+
1245
+ Return
1246
+ ------
1247
+ float
1248
+ version
1249
+ """
1250
+ return self.si.vc_ver
1251
+
1252
+ @property
1253
+ def VSTools(self):
1254
+ """
1255
+ Microsoft Visual Studio Tools.
1256
+
1257
+ Return
1258
+ ------
1259
+ list of str
1260
+ paths
1261
+ """
1262
+ paths = [r'Common7\IDE', r'Common7\Tools']
1263
+
1264
+ if self.vs_ver >= 14.0:
1265
+ arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1266
+ paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
1267
+ paths += [r'Team Tools\Performance Tools']
1268
+ paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
1269
+
1270
+ return [join(self.si.VSInstallDir, path) for path in paths]
1271
+
1272
+ @property
1273
+ def VCIncludes(self):
1274
+ """
1275
+ Microsoft Visual C++ & Microsoft Foundation Class Includes.
1276
+
1277
+ Return
1278
+ ------
1279
+ list of str
1280
+ paths
1281
+ """
1282
+ return [join(self.si.VCInstallDir, 'Include'),
1283
+ join(self.si.VCInstallDir, r'ATLMFC\Include')]
1284
+
1285
+ @property
1286
+ def VCLibraries(self):
1287
+ """
1288
+ Microsoft Visual C++ & Microsoft Foundation Class Libraries.
1289
+
1290
+ Return
1291
+ ------
1292
+ list of str
1293
+ paths
1294
+ """
1295
+ if self.vs_ver >= 15.0:
1296
+ arch_subdir = self.pi.target_dir(x64=True)
1297
+ else:
1298
+ arch_subdir = self.pi.target_dir(hidex86=True)
1299
+ paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
1300
+
1301
+ if self.vs_ver >= 14.0:
1302
+ paths += [r'Lib\store%s' % arch_subdir]
1303
+
1304
+ return [join(self.si.VCInstallDir, path) for path in paths]
1305
+
1306
+ @property
1307
+ def VCStoreRefs(self):
1308
+ """
1309
+ Microsoft Visual C++ store references Libraries.
1310
+
1311
+ Return
1312
+ ------
1313
+ list of str
1314
+ paths
1315
+ """
1316
+ if self.vs_ver < 14.0:
1317
+ return []
1318
+ return [join(self.si.VCInstallDir, r'Lib\store\references')]
1319
+
1320
+ @property
1321
+ def VCTools(self):
1322
+ """
1323
+ Microsoft Visual C++ Tools.
1324
+
1325
+ Return
1326
+ ------
1327
+ list of str
1328
+ paths
1329
+ """
1330
+ si = self.si
1331
+ tools = [join(si.VCInstallDir, 'VCPackages')]
1332
+
1333
+ forcex86 = True if self.vs_ver <= 10.0 else False
1334
+ arch_subdir = self.pi.cross_dir(forcex86)
1335
+ if arch_subdir:
1336
+ tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
1337
+
1338
+ if self.vs_ver == 14.0:
1339
+ path = 'Bin%s' % self.pi.current_dir(hidex86=True)
1340
+ tools += [join(si.VCInstallDir, path)]
1341
+
1342
+ elif self.vs_ver >= 15.0:
1343
+ host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else
1344
+ r'bin\HostX64%s')
1345
+ tools += [join(
1346
+ si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))]
1347
+
1348
+ if self.pi.current_cpu != self.pi.target_cpu:
1349
+ tools += [join(
1350
+ si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))]
1351
+
1352
+ else:
1353
+ tools += [join(si.VCInstallDir, 'Bin')]
1354
+
1355
+ return tools
1356
+
1357
+ @property
1358
+ def OSLibraries(self):
1359
+ """
1360
+ Microsoft Windows SDK Libraries.
1361
+
1362
+ Return
1363
+ ------
1364
+ list of str
1365
+ paths
1366
+ """
1367
+ if self.vs_ver <= 10.0:
1368
+ arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
1369
+ return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
1370
+
1371
+ else:
1372
+ arch_subdir = self.pi.target_dir(x64=True)
1373
+ lib = join(self.si.WindowsSdkDir, 'lib')
1374
+ libver = self._sdk_subdir
1375
+ return [join(lib, '%sum%s' % (libver, arch_subdir))]
1376
+
1377
+ @property
1378
+ def OSIncludes(self):
1379
+ """
1380
+ Microsoft Windows SDK Include.
1381
+
1382
+ Return
1383
+ ------
1384
+ list of str
1385
+ paths
1386
+ """
1387
+ include = join(self.si.WindowsSdkDir, 'include')
1388
+
1389
+ if self.vs_ver <= 10.0:
1390
+ return [include, join(include, 'gl')]
1391
+
1392
+ else:
1393
+ if self.vs_ver >= 14.0:
1394
+ sdkver = self._sdk_subdir
1395
+ else:
1396
+ sdkver = ''
1397
+ return [join(include, '%sshared' % sdkver),
1398
+ join(include, '%sum' % sdkver),
1399
+ join(include, '%swinrt' % sdkver)]
1400
+
1401
+ @property
1402
+ def OSLibpath(self):
1403
+ """
1404
+ Microsoft Windows SDK Libraries Paths.
1405
+
1406
+ Return
1407
+ ------
1408
+ list of str
1409
+ paths
1410
+ """
1411
+ ref = join(self.si.WindowsSdkDir, 'References')
1412
+ libpath = []
1413
+
1414
+ if self.vs_ver <= 9.0:
1415
+ libpath += self.OSLibraries
1416
+
1417
+ if self.vs_ver >= 11.0:
1418
+ libpath += [join(ref, r'CommonConfiguration\Neutral')]
1419
+
1420
+ if self.vs_ver >= 14.0:
1421
+ libpath += [
1422
+ ref,
1423
+ join(self.si.WindowsSdkDir, 'UnionMetadata'),
1424
+ join(
1425
+ ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
1426
+ join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
1427
+ join(
1428
+ ref, 'Windows.Networking.Connectivity.WwanContract',
1429
+ '1.0.0.0'),
1430
+ join(
1431
+ self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
1432
+ '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
1433
+ 'neutral'),
1434
+ ]
1435
+ return libpath
1436
+
1437
+ @property
1438
+ def SdkTools(self):
1439
+ """
1440
+ Microsoft Windows SDK Tools.
1441
+
1442
+ Return
1443
+ ------
1444
+ list of str
1445
+ paths
1446
+ """
1447
+ return list(self._sdk_tools())
1448
+
1449
+ def _sdk_tools(self):
1450
+ """
1451
+ Microsoft Windows SDK Tools paths generator.
1452
+
1453
+ Return
1454
+ ------
1455
+ generator of str
1456
+ paths
1457
+ """
1458
+ if self.vs_ver < 15.0:
1459
+ bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
1460
+ yield join(self.si.WindowsSdkDir, bin_dir)
1461
+
1462
+ if not self.pi.current_is_x86():
1463
+ arch_subdir = self.pi.current_dir(x64=True)
1464
+ path = 'Bin%s' % arch_subdir
1465
+ yield join(self.si.WindowsSdkDir, path)
1466
+
1467
+ if self.vs_ver in (10.0, 11.0):
1468
+ if self.pi.target_is_x86():
1469
+ arch_subdir = ''
1470
+ else:
1471
+ arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
1472
+ path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
1473
+ yield join(self.si.WindowsSdkDir, path)
1474
+
1475
+ elif self.vs_ver >= 15.0:
1476
+ path = join(self.si.WindowsSdkDir, 'Bin')
1477
+ arch_subdir = self.pi.current_dir(x64=True)
1478
+ sdkver = self.si.WindowsSdkLastVersion
1479
+ yield join(path, '%s%s' % (sdkver, arch_subdir))
1480
+
1481
+ if self.si.WindowsSDKExecutablePath:
1482
+ yield self.si.WindowsSDKExecutablePath
1483
+
1484
+ @property
1485
+ def _sdk_subdir(self):
1486
+ """
1487
+ Microsoft Windows SDK version subdir.
1488
+
1489
+ Return
1490
+ ------
1491
+ str
1492
+ subdir
1493
+ """
1494
+ ucrtver = self.si.WindowsSdkLastVersion
1495
+ return ('%s\\' % ucrtver) if ucrtver else ''
1496
+
1497
+ @property
1498
+ def SdkSetup(self):
1499
+ """
1500
+ Microsoft Windows SDK Setup.
1501
+
1502
+ Return
1503
+ ------
1504
+ list of str
1505
+ paths
1506
+ """
1507
+ if self.vs_ver > 9.0:
1508
+ return []
1509
+
1510
+ return [join(self.si.WindowsSdkDir, 'Setup')]
1511
+
1512
+ @property
1513
+ def FxTools(self):
1514
+ """
1515
+ Microsoft .NET Framework Tools.
1516
+
1517
+ Return
1518
+ ------
1519
+ list of str
1520
+ paths
1521
+ """
1522
+ pi = self.pi
1523
+ si = self.si
1524
+
1525
+ if self.vs_ver <= 10.0:
1526
+ include32 = True
1527
+ include64 = not pi.target_is_x86() and not pi.current_is_x86()
1528
+ else:
1529
+ include32 = pi.target_is_x86() or pi.current_is_x86()
1530
+ include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
1531
+
1532
+ tools = []
1533
+ if include32:
1534
+ tools += [join(si.FrameworkDir32, ver)
1535
+ for ver in si.FrameworkVersion32]
1536
+ if include64:
1537
+ tools += [join(si.FrameworkDir64, ver)
1538
+ for ver in si.FrameworkVersion64]
1539
+ return tools
1540
+
1541
+ @property
1542
+ def NetFxSDKLibraries(self):
1543
+ """
1544
+ Microsoft .Net Framework SDK Libraries.
1545
+
1546
+ Return
1547
+ ------
1548
+ list of str
1549
+ paths
1550
+ """
1551
+ if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1552
+ return []
1553
+
1554
+ arch_subdir = self.pi.target_dir(x64=True)
1555
+ return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
1556
+
1557
+ @property
1558
+ def NetFxSDKIncludes(self):
1559
+ """
1560
+ Microsoft .Net Framework SDK Includes.
1561
+
1562
+ Return
1563
+ ------
1564
+ list of str
1565
+ paths
1566
+ """
1567
+ if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
1568
+ return []
1569
+
1570
+ return [join(self.si.NetFxSdkDir, r'include\um')]
1571
+
1572
+ @property
1573
+ def VsTDb(self):
1574
+ """
1575
+ Microsoft Visual Studio Team System Database.
1576
+
1577
+ Return
1578
+ ------
1579
+ list of str
1580
+ paths
1581
+ """
1582
+ return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
1583
+
1584
+ @property
1585
+ def MSBuild(self):
1586
+ """
1587
+ Microsoft Build Engine.
1588
+
1589
+ Return
1590
+ ------
1591
+ list of str
1592
+ paths
1593
+ """
1594
+ if self.vs_ver < 12.0:
1595
+ return []
1596
+ elif self.vs_ver < 15.0:
1597
+ base_path = self.si.ProgramFilesx86
1598
+ arch_subdir = self.pi.current_dir(hidex86=True)
1599
+ else:
1600
+ base_path = self.si.VSInstallDir
1601
+ arch_subdir = ''
1602
+
1603
+ path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir)
1604
+ build = [join(base_path, path)]
1605
+
1606
+ if self.vs_ver >= 15.0:
1607
+ # Add Roslyn C# & Visual Basic Compiler
1608
+ build += [join(base_path, path, 'Roslyn')]
1609
+
1610
+ return build
1611
+
1612
+ @property
1613
+ def HTMLHelpWorkshop(self):
1614
+ """
1615
+ Microsoft HTML Help Workshop.
1616
+
1617
+ Return
1618
+ ------
1619
+ list of str
1620
+ paths
1621
+ """
1622
+ if self.vs_ver < 11.0:
1623
+ return []
1624
+
1625
+ return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
1626
+
1627
+ @property
1628
+ def UCRTLibraries(self):
1629
+ """
1630
+ Microsoft Universal C Runtime SDK Libraries.
1631
+
1632
+ Return
1633
+ ------
1634
+ list of str
1635
+ paths
1636
+ """
1637
+ if self.vs_ver < 14.0:
1638
+ return []
1639
+
1640
+ arch_subdir = self.pi.target_dir(x64=True)
1641
+ lib = join(self.si.UniversalCRTSdkDir, 'lib')
1642
+ ucrtver = self._ucrt_subdir
1643
+ return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
1644
+
1645
+ @property
1646
+ def UCRTIncludes(self):
1647
+ """
1648
+ Microsoft Universal C Runtime SDK Include.
1649
+
1650
+ Return
1651
+ ------
1652
+ list of str
1653
+ paths
1654
+ """
1655
+ if self.vs_ver < 14.0:
1656
+ return []
1657
+
1658
+ include = join(self.si.UniversalCRTSdkDir, 'include')
1659
+ return [join(include, '%sucrt' % self._ucrt_subdir)]
1660
+
1661
+ @property
1662
+ def _ucrt_subdir(self):
1663
+ """
1664
+ Microsoft Universal C Runtime SDK version subdir.
1665
+
1666
+ Return
1667
+ ------
1668
+ str
1669
+ subdir
1670
+ """
1671
+ ucrtver = self.si.UniversalCRTSdkLastVersion
1672
+ return ('%s\\' % ucrtver) if ucrtver else ''
1673
+
1674
+ @property
1675
+ def FSharp(self):
1676
+ """
1677
+ Microsoft Visual F#.
1678
+
1679
+ Return
1680
+ ------
1681
+ list of str
1682
+ paths
1683
+ """
1684
+ if 11.0 > self.vs_ver > 12.0:
1685
+ return []
1686
+
1687
+ return [self.si.FSharpInstallDir]
1688
+
1689
+ @property
1690
+ def VCRuntimeRedist(self):
1691
+ """
1692
+ Microsoft Visual C++ runtime redistributable dll.
1693
+
1694
+ Return
1695
+ ------
1696
+ str
1697
+ path
1698
+ """
1699
+ vcruntime = 'vcruntime%d0.dll' % self.vc_ver
1700
+ arch_subdir = self.pi.target_dir(x64=True).strip('\\')
1701
+
1702
+ # Installation prefixes candidates
1703
+ prefixes = []
1704
+ tools_path = self.si.VCInstallDir
1705
+ redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist'))
1706
+ if isdir(redist_path):
1707
+ # Redist version may not be exactly the same as tools
1708
+ redist_path = join(redist_path, listdir(redist_path)[-1])
1709
+ prefixes += [redist_path, join(redist_path, 'onecore')]
1710
+
1711
+ prefixes += [join(tools_path, 'redist')] # VS14 legacy path
1712
+
1713
+ # CRT directory
1714
+ crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10),
1715
+ # Sometime store in directory with VS version instead of VC
1716
+ 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10))
1717
+
1718
+ # vcruntime path
1719
+ for prefix, crt_dir in itertools.product(prefixes, crt_dirs):
1720
+ path = join(prefix, arch_subdir, crt_dir, vcruntime)
1721
+ if isfile(path):
1722
+ return path
1723
+
1724
+ def return_env(self, exists=True):
1725
+ """
1726
+ Return environment dict.
1727
+
1728
+ Parameters
1729
+ ----------
1730
+ exists: bool
1731
+ It True, only return existing paths.
1732
+
1733
+ Return
1734
+ ------
1735
+ dict
1736
+ environment
1737
+ """
1738
+ env = dict(
1739
+ include=self._build_paths('include',
1740
+ [self.VCIncludes,
1741
+ self.OSIncludes,
1742
+ self.UCRTIncludes,
1743
+ self.NetFxSDKIncludes],
1744
+ exists),
1745
+ lib=self._build_paths('lib',
1746
+ [self.VCLibraries,
1747
+ self.OSLibraries,
1748
+ self.FxTools,
1749
+ self.UCRTLibraries,
1750
+ self.NetFxSDKLibraries],
1751
+ exists),
1752
+ libpath=self._build_paths('libpath',
1753
+ [self.VCLibraries,
1754
+ self.FxTools,
1755
+ self.VCStoreRefs,
1756
+ self.OSLibpath],
1757
+ exists),
1758
+ path=self._build_paths('path',
1759
+ [self.VCTools,
1760
+ self.VSTools,
1761
+ self.VsTDb,
1762
+ self.SdkTools,
1763
+ self.SdkSetup,
1764
+ self.FxTools,
1765
+ self.MSBuild,
1766
+ self.HTMLHelpWorkshop,
1767
+ self.FSharp],
1768
+ exists),
1769
+ )
1770
+ if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist):
1771
+ env['py_vcruntime_redist'] = self.VCRuntimeRedist
1772
+ return env
1773
+
1774
+ def _build_paths(self, name, spec_path_lists, exists):
1775
+ """
1776
+ Given an environment variable name and specified paths,
1777
+ return a pathsep-separated string of paths containing
1778
+ unique, extant, directories from those paths and from
1779
+ the environment variable. Raise an error if no paths
1780
+ are resolved.
1781
+
1782
+ Parameters
1783
+ ----------
1784
+ name: str
1785
+ Environment variable name
1786
+ spec_path_lists: list of str
1787
+ Paths
1788
+ exists: bool
1789
+ It True, only return existing paths.
1790
+
1791
+ Return
1792
+ ------
1793
+ str
1794
+ Pathsep-separated paths
1795
+ """
1796
+ # flatten spec_path_lists
1797
+ spec_paths = itertools.chain.from_iterable(spec_path_lists)
1798
+ env_paths = environ.get(name, '').split(pathsep)
1799
+ paths = itertools.chain(spec_paths, env_paths)
1800
+ extant_paths = list(filter(isdir, paths)) if exists else paths
1801
+ if not extant_paths:
1802
+ msg = "%s environment variable is empty" % name.upper()
1803
+ raise distutils.errors.DistutilsPlatformError(msg)
1804
+ unique_paths = unique_everseen(extant_paths)
1805
+ return pathsep.join(unique_paths)
venv/lib/python3.10/site-packages/setuptools/namespaces.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from distutils import log
3
+ import itertools
4
+
5
+
6
+ flatten = itertools.chain.from_iterable
7
+
8
+
9
+ class Installer:
10
+
11
+ nspkg_ext = '-nspkg.pth'
12
+
13
+ def install_namespaces(self):
14
+ nsp = self._get_all_ns_packages()
15
+ if not nsp:
16
+ return
17
+ filename, ext = os.path.splitext(self._get_target())
18
+ filename += self.nspkg_ext
19
+ self.outputs.append(filename)
20
+ log.info("Installing %s", filename)
21
+ lines = map(self._gen_nspkg_line, nsp)
22
+
23
+ if self.dry_run:
24
+ # always generate the lines, even in dry run
25
+ list(lines)
26
+ return
27
+
28
+ with open(filename, 'wt') as f:
29
+ f.writelines(lines)
30
+
31
+ def uninstall_namespaces(self):
32
+ filename, ext = os.path.splitext(self._get_target())
33
+ filename += self.nspkg_ext
34
+ if not os.path.exists(filename):
35
+ return
36
+ log.info("Removing %s", filename)
37
+ os.remove(filename)
38
+
39
+ def _get_target(self):
40
+ return self.target
41
+
42
+ _nspkg_tmpl = (
43
+ "import sys, types, os",
44
+ "has_mfs = sys.version_info > (3, 5)",
45
+ "p = os.path.join(%(root)s, *%(pth)r)",
46
+ "importlib = has_mfs and __import__('importlib.util')",
47
+ "has_mfs and __import__('importlib.machinery')",
48
+ (
49
+ "m = has_mfs and "
50
+ "sys.modules.setdefault(%(pkg)r, "
51
+ "importlib.util.module_from_spec("
52
+ "importlib.machinery.PathFinder.find_spec(%(pkg)r, "
53
+ "[os.path.dirname(p)])))"
54
+ ),
55
+ (
56
+ "m = m or "
57
+ "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))"
58
+ ),
59
+ "mp = (m or []) and m.__dict__.setdefault('__path__',[])",
60
+ "(p not in mp) and mp.append(p)",
61
+ )
62
+ "lines for the namespace installer"
63
+
64
+ _nspkg_tmpl_multi = (
65
+ 'm and setattr(sys.modules[%(parent)r], %(child)r, m)',
66
+ )
67
+ "additional line(s) when a parent package is indicated"
68
+
69
+ def _get_root(self):
70
+ return "sys._getframe(1).f_locals['sitedir']"
71
+
72
+ def _gen_nspkg_line(self, pkg):
73
+ pth = tuple(pkg.split('.'))
74
+ root = self._get_root()
75
+ tmpl_lines = self._nspkg_tmpl
76
+ parent, sep, child = pkg.rpartition('.')
77
+ if parent:
78
+ tmpl_lines += self._nspkg_tmpl_multi
79
+ return ';'.join(tmpl_lines) % locals() + '\n'
80
+
81
+ def _get_all_ns_packages(self):
82
+ """Return sorted list of all package namespaces"""
83
+ pkgs = self.distribution.namespace_packages or []
84
+ return sorted(flatten(map(self._pkg_names, pkgs)))
85
+
86
+ @staticmethod
87
+ def _pkg_names(pkg):
88
+ """
89
+ Given a namespace package, yield the components of that
90
+ package.
91
+
92
+ >>> names = Installer._pkg_names('a.b.c')
93
+ >>> set(names) == set(['a', 'a.b', 'a.b.c'])
94
+ True
95
+ """
96
+ parts = pkg.split('.')
97
+ while parts:
98
+ yield '.'.join(parts)
99
+ parts.pop()
100
+
101
+
102
+ class DevelopInstaller(Installer):
103
+ def _get_root(self):
104
+ return repr(str(self.egg_path))
105
+
106
+ def _get_target(self):
107
+ return self.egg_link
venv/lib/python3.10/site-packages/setuptools/package_index.py ADDED
@@ -0,0 +1,1150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PyPI and direct package downloading"""
2
+ import sys
3
+ import subprocess
4
+ import os
5
+ import re
6
+ import io
7
+ import shutil
8
+ import socket
9
+ import base64
10
+ import hashlib
11
+ import itertools
12
+ import warnings
13
+ import configparser
14
+ import html
15
+ import http.client
16
+ import urllib.parse
17
+ import urllib.request
18
+ import urllib.error
19
+ from functools import wraps
20
+
21
+ import setuptools
22
+ from pkg_resources import (
23
+ CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
24
+ Environment, find_distributions, safe_name, safe_version,
25
+ to_filename, Requirement, DEVELOP_DIST, EGG_DIST, parse_version,
26
+ )
27
+ from distutils import log
28
+ from distutils.errors import DistutilsError
29
+ from fnmatch import translate
30
+ from setuptools.wheel import Wheel
31
+ from setuptools.extern.more_itertools import unique_everseen
32
+
33
+
34
+ EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$')
35
+ HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I)
36
+ PYPI_MD5 = re.compile(
37
+ r'<a href="([^"#]+)">([^<]+)</a>\n\s+\(<a (?:title="MD5 hash"\n\s+)'
38
+ r'href="[^?]+\?:action=show_md5&amp;digest=([0-9a-f]{32})">md5</a>\)'
39
+ )
40
+ URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match
41
+ EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
42
+
43
+ __all__ = [
44
+ 'PackageIndex', 'distros_for_url', 'parse_bdist_wininst',
45
+ 'interpret_distro_name',
46
+ ]
47
+
48
+ _SOCKET_TIMEOUT = 15
49
+
50
+ _tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}"
51
+ user_agent = _tmpl.format(
52
+ py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools)
53
+
54
+
55
+ def parse_requirement_arg(spec):
56
+ try:
57
+ return Requirement.parse(spec)
58
+ except ValueError as e:
59
+ raise DistutilsError(
60
+ "Not a URL, existing file, or requirement spec: %r" % (spec,)
61
+ ) from e
62
+
63
+
64
+ def parse_bdist_wininst(name):
65
+ """Return (base,pyversion) or (None,None) for possible .exe name"""
66
+
67
+ lower = name.lower()
68
+ base, py_ver, plat = None, None, None
69
+
70
+ if lower.endswith('.exe'):
71
+ if lower.endswith('.win32.exe'):
72
+ base = name[:-10]
73
+ plat = 'win32'
74
+ elif lower.startswith('.win32-py', -16):
75
+ py_ver = name[-7:-4]
76
+ base = name[:-16]
77
+ plat = 'win32'
78
+ elif lower.endswith('.win-amd64.exe'):
79
+ base = name[:-14]
80
+ plat = 'win-amd64'
81
+ elif lower.startswith('.win-amd64-py', -20):
82
+ py_ver = name[-7:-4]
83
+ base = name[:-20]
84
+ plat = 'win-amd64'
85
+ return base, py_ver, plat
86
+
87
+
88
+ def egg_info_for_url(url):
89
+ parts = urllib.parse.urlparse(url)
90
+ scheme, server, path, parameters, query, fragment = parts
91
+ base = urllib.parse.unquote(path.split('/')[-1])
92
+ if server == 'sourceforge.net' and base == 'download': # XXX Yuck
93
+ base = urllib.parse.unquote(path.split('/')[-2])
94
+ if '#' in base:
95
+ base, fragment = base.split('#', 1)
96
+ return base, fragment
97
+
98
+
99
+ def distros_for_url(url, metadata=None):
100
+ """Yield egg or source distribution objects that might be found at a URL"""
101
+ base, fragment = egg_info_for_url(url)
102
+ for dist in distros_for_location(url, base, metadata):
103
+ yield dist
104
+ if fragment:
105
+ match = EGG_FRAGMENT.match(fragment)
106
+ if match:
107
+ for dist in interpret_distro_name(
108
+ url, match.group(1), metadata, precedence=CHECKOUT_DIST
109
+ ):
110
+ yield dist
111
+
112
+
113
+ def distros_for_location(location, basename, metadata=None):
114
+ """Yield egg or source distribution objects based on basename"""
115
+ if basename.endswith('.egg.zip'):
116
+ basename = basename[:-4] # strip the .zip
117
+ if basename.endswith('.egg') and '-' in basename:
118
+ # only one, unambiguous interpretation
119
+ return [Distribution.from_location(location, basename, metadata)]
120
+ if basename.endswith('.whl') and '-' in basename:
121
+ wheel = Wheel(basename)
122
+ if not wheel.is_compatible():
123
+ return []
124
+ return [Distribution(
125
+ location=location,
126
+ project_name=wheel.project_name,
127
+ version=wheel.version,
128
+ # Increase priority over eggs.
129
+ precedence=EGG_DIST + 1,
130
+ )]
131
+ if basename.endswith('.exe'):
132
+ win_base, py_ver, platform = parse_bdist_wininst(basename)
133
+ if win_base is not None:
134
+ return interpret_distro_name(
135
+ location, win_base, metadata, py_ver, BINARY_DIST, platform
136
+ )
137
+ # Try source distro extensions (.zip, .tgz, etc.)
138
+ #
139
+ for ext in EXTENSIONS:
140
+ if basename.endswith(ext):
141
+ basename = basename[:-len(ext)]
142
+ return interpret_distro_name(location, basename, metadata)
143
+ return [] # no extension matched
144
+
145
+
146
+ def distros_for_filename(filename, metadata=None):
147
+ """Yield possible egg or source distribution objects based on a filename"""
148
+ return distros_for_location(
149
+ normalize_path(filename), os.path.basename(filename), metadata
150
+ )
151
+
152
+
153
+ def interpret_distro_name(
154
+ location, basename, metadata, py_version=None, precedence=SOURCE_DIST,
155
+ platform=None
156
+ ):
157
+ """Generate alternative interpretations of a source distro name
158
+
159
+ Note: if `location` is a filesystem filename, you should call
160
+ ``pkg_resources.normalize_path()`` on it before passing it to this
161
+ routine!
162
+ """
163
+ # Generate alternative interpretations of a source distro name
164
+ # Because some packages are ambiguous as to name/versions split
165
+ # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc.
166
+ # So, we generate each possible interpretation (e.g. "adns, python-1.1.0"
167
+ # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice,
168
+ # the spurious interpretations should be ignored, because in the event
169
+ # there's also an "adns" package, the spurious "python-1.1.0" version will
170
+ # compare lower than any numeric version number, and is therefore unlikely
171
+ # to match a request for it. It's still a potential problem, though, and
172
+ # in the long run PyPI and the distutils should go for "safe" names and
173
+ # versions in distribution archive names (sdist and bdist).
174
+
175
+ parts = basename.split('-')
176
+ if not py_version and any(re.match(r'py\d\.\d$', p) for p in parts[2:]):
177
+ # it is a bdist_dumb, not an sdist -- bail out
178
+ return
179
+
180
+ for p in range(1, len(parts) + 1):
181
+ yield Distribution(
182
+ location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]),
183
+ py_version=py_version, precedence=precedence,
184
+ platform=platform
185
+ )
186
+
187
+
188
+ def unique_values(func):
189
+ """
190
+ Wrap a function returning an iterable such that the resulting iterable
191
+ only ever yields unique items.
192
+ """
193
+
194
+ @wraps(func)
195
+ def wrapper(*args, **kwargs):
196
+ return unique_everseen(func(*args, **kwargs))
197
+
198
+ return wrapper
199
+
200
+
201
+ REL = re.compile(r"""<([^>]*\srel\s{0,10}=\s{0,10}['"]?([^'" >]+)[^>]*)>""", re.I)
202
+ # this line is here to fix emacs' cruddy broken syntax highlighting
203
+
204
+
205
+ @unique_values
206
+ def find_external_links(url, page):
207
+ """Find rel="homepage" and rel="download" links in `page`, yielding URLs"""
208
+
209
+ for match in REL.finditer(page):
210
+ tag, rel = match.groups()
211
+ rels = set(map(str.strip, rel.lower().split(',')))
212
+ if 'homepage' in rels or 'download' in rels:
213
+ for match in HREF.finditer(tag):
214
+ yield urllib.parse.urljoin(url, htmldecode(match.group(1)))
215
+
216
+ for tag in ("<th>Home Page", "<th>Download URL"):
217
+ pos = page.find(tag)
218
+ if pos != -1:
219
+ match = HREF.search(page, pos)
220
+ if match:
221
+ yield urllib.parse.urljoin(url, htmldecode(match.group(1)))
222
+
223
+
224
+ class ContentChecker:
225
+ """
226
+ A null content checker that defines the interface for checking content
227
+ """
228
+
229
+ def feed(self, block):
230
+ """
231
+ Feed a block of data to the hash.
232
+ """
233
+ return
234
+
235
+ def is_valid(self):
236
+ """
237
+ Check the hash. Return False if validation fails.
238
+ """
239
+ return True
240
+
241
+ def report(self, reporter, template):
242
+ """
243
+ Call reporter with information about the checker (hash name)
244
+ substituted into the template.
245
+ """
246
+ return
247
+
248
+
249
+ class HashChecker(ContentChecker):
250
+ pattern = re.compile(
251
+ r'(?P<hash_name>sha1|sha224|sha384|sha256|sha512|md5)='
252
+ r'(?P<expected>[a-f0-9]+)'
253
+ )
254
+
255
+ def __init__(self, hash_name, expected):
256
+ self.hash_name = hash_name
257
+ self.hash = hashlib.new(hash_name)
258
+ self.expected = expected
259
+
260
+ @classmethod
261
+ def from_url(cls, url):
262
+ "Construct a (possibly null) ContentChecker from a URL"
263
+ fragment = urllib.parse.urlparse(url)[-1]
264
+ if not fragment:
265
+ return ContentChecker()
266
+ match = cls.pattern.search(fragment)
267
+ if not match:
268
+ return ContentChecker()
269
+ return cls(**match.groupdict())
270
+
271
+ def feed(self, block):
272
+ self.hash.update(block)
273
+
274
+ def is_valid(self):
275
+ return self.hash.hexdigest() == self.expected
276
+
277
+ def report(self, reporter, template):
278
+ msg = template % self.hash_name
279
+ return reporter(msg)
280
+
281
+
282
+ class PackageIndex(Environment):
283
+ """A distribution index that scans web pages for download URLs"""
284
+
285
+ def __init__(
286
+ self, index_url="https://pypi.org/simple/", hosts=('*',),
287
+ ca_bundle=None, verify_ssl=True, *args, **kw
288
+ ):
289
+ Environment.__init__(self, *args, **kw)
290
+ self.index_url = index_url + "/" [:not index_url.endswith('/')]
291
+ self.scanned_urls = {}
292
+ self.fetched_urls = {}
293
+ self.package_pages = {}
294
+ self.allows = re.compile('|'.join(map(translate, hosts))).match
295
+ self.to_scan = []
296
+ self.opener = urllib.request.urlopen
297
+
298
+ def add(self, dist):
299
+ # ignore invalid versions
300
+ try:
301
+ parse_version(dist.version)
302
+ except Exception:
303
+ return
304
+ return super().add(dist)
305
+
306
+ # FIXME: 'PackageIndex.process_url' is too complex (14)
307
+ def process_url(self, url, retrieve=False): # noqa: C901
308
+ """Evaluate a URL as a possible download, and maybe retrieve it"""
309
+ if url in self.scanned_urls and not retrieve:
310
+ return
311
+ self.scanned_urls[url] = True
312
+ if not URL_SCHEME(url):
313
+ self.process_filename(url)
314
+ return
315
+ else:
316
+ dists = list(distros_for_url(url))
317
+ if dists:
318
+ if not self.url_ok(url):
319
+ return
320
+ self.debug("Found link: %s", url)
321
+
322
+ if dists or not retrieve or url in self.fetched_urls:
323
+ list(map(self.add, dists))
324
+ return # don't need the actual page
325
+
326
+ if not self.url_ok(url):
327
+ self.fetched_urls[url] = True
328
+ return
329
+
330
+ self.info("Reading %s", url)
331
+ self.fetched_urls[url] = True # prevent multiple fetch attempts
332
+ tmpl = "Download error on %s: %%s -- Some packages may not be found!"
333
+ f = self.open_url(url, tmpl % url)
334
+ if f is None:
335
+ return
336
+ if isinstance(f, urllib.error.HTTPError) and f.code == 401:
337
+ self.info("Authentication error: %s" % f.msg)
338
+ self.fetched_urls[f.url] = True
339
+ if 'html' not in f.headers.get('content-type', '').lower():
340
+ f.close() # not html, we can't process it
341
+ return
342
+
343
+ base = f.url # handle redirects
344
+ page = f.read()
345
+ if not isinstance(page, str):
346
+ # In Python 3 and got bytes but want str.
347
+ if isinstance(f, urllib.error.HTTPError):
348
+ # Errors have no charset, assume latin1:
349
+ charset = 'latin-1'
350
+ else:
351
+ charset = f.headers.get_param('charset') or 'latin-1'
352
+ page = page.decode(charset, "ignore")
353
+ f.close()
354
+ for match in HREF.finditer(page):
355
+ link = urllib.parse.urljoin(base, htmldecode(match.group(1)))
356
+ self.process_url(link)
357
+ if url.startswith(self.index_url) and getattr(f, 'code', None) != 404:
358
+ page = self.process_index(url, page)
359
+
360
+ def process_filename(self, fn, nested=False):
361
+ # process filenames or directories
362
+ if not os.path.exists(fn):
363
+ self.warn("Not found: %s", fn)
364
+ return
365
+
366
+ if os.path.isdir(fn) and not nested:
367
+ path = os.path.realpath(fn)
368
+ for item in os.listdir(path):
369
+ self.process_filename(os.path.join(path, item), True)
370
+
371
+ dists = distros_for_filename(fn)
372
+ if dists:
373
+ self.debug("Found: %s", fn)
374
+ list(map(self.add, dists))
375
+
376
+ def url_ok(self, url, fatal=False):
377
+ s = URL_SCHEME(url)
378
+ is_file = s and s.group(1).lower() == 'file'
379
+ if is_file or self.allows(urllib.parse.urlparse(url)[1]):
380
+ return True
381
+ msg = (
382
+ "\nNote: Bypassing %s (disallowed host; see "
383
+ "http://bit.ly/2hrImnY for details).\n")
384
+ if fatal:
385
+ raise DistutilsError(msg % url)
386
+ else:
387
+ self.warn(msg, url)
388
+
389
+ def scan_egg_links(self, search_path):
390
+ dirs = filter(os.path.isdir, search_path)
391
+ egg_links = (
392
+ (path, entry)
393
+ for path in dirs
394
+ for entry in os.listdir(path)
395
+ if entry.endswith('.egg-link')
396
+ )
397
+ list(itertools.starmap(self.scan_egg_link, egg_links))
398
+
399
+ def scan_egg_link(self, path, entry):
400
+ with open(os.path.join(path, entry)) as raw_lines:
401
+ # filter non-empty lines
402
+ lines = list(filter(None, map(str.strip, raw_lines)))
403
+
404
+ if len(lines) != 2:
405
+ # format is not recognized; punt
406
+ return
407
+
408
+ egg_path, setup_path = lines
409
+
410
+ for dist in find_distributions(os.path.join(path, egg_path)):
411
+ dist.location = os.path.join(path, *lines)
412
+ dist.precedence = SOURCE_DIST
413
+ self.add(dist)
414
+
415
+ def _scan(self, link):
416
+ # Process a URL to see if it's for a package page
417
+ NO_MATCH_SENTINEL = None, None
418
+ if not link.startswith(self.index_url):
419
+ return NO_MATCH_SENTINEL
420
+
421
+ parts = list(map(
422
+ urllib.parse.unquote, link[len(self.index_url):].split('/')
423
+ ))
424
+ if len(parts) != 2 or '#' in parts[1]:
425
+ return NO_MATCH_SENTINEL
426
+
427
+ # it's a package page, sanitize and index it
428
+ pkg = safe_name(parts[0])
429
+ ver = safe_version(parts[1])
430
+ self.package_pages.setdefault(pkg.lower(), {})[link] = True
431
+ return to_filename(pkg), to_filename(ver)
432
+
433
+ def process_index(self, url, page):
434
+ """Process the contents of a PyPI page"""
435
+
436
+ # process an index page into the package-page index
437
+ for match in HREF.finditer(page):
438
+ try:
439
+ self._scan(urllib.parse.urljoin(url, htmldecode(match.group(1))))
440
+ except ValueError:
441
+ pass
442
+
443
+ pkg, ver = self._scan(url) # ensure this page is in the page index
444
+ if not pkg:
445
+ return "" # no sense double-scanning non-package pages
446
+
447
+ # process individual package page
448
+ for new_url in find_external_links(url, page):
449
+ # Process the found URL
450
+ base, frag = egg_info_for_url(new_url)
451
+ if base.endswith('.py') and not frag:
452
+ if ver:
453
+ new_url += '#egg=%s-%s' % (pkg, ver)
454
+ else:
455
+ self.need_version_info(url)
456
+ self.scan_url(new_url)
457
+
458
+ return PYPI_MD5.sub(
459
+ lambda m: '<a href="%s#md5=%s">%s</a>' % m.group(1, 3, 2), page
460
+ )
461
+
462
+ def need_version_info(self, url):
463
+ self.scan_all(
464
+ "Page at %s links to .py file(s) without version info; an index "
465
+ "scan is required.", url
466
+ )
467
+
468
+ def scan_all(self, msg=None, *args):
469
+ if self.index_url not in self.fetched_urls:
470
+ if msg:
471
+ self.warn(msg, *args)
472
+ self.info(
473
+ "Scanning index of all packages (this may take a while)"
474
+ )
475
+ self.scan_url(self.index_url)
476
+
477
+ def find_packages(self, requirement):
478
+ self.scan_url(self.index_url + requirement.unsafe_name + '/')
479
+
480
+ if not self.package_pages.get(requirement.key):
481
+ # Fall back to safe version of the name
482
+ self.scan_url(self.index_url + requirement.project_name + '/')
483
+
484
+ if not self.package_pages.get(requirement.key):
485
+ # We couldn't find the target package, so search the index page too
486
+ self.not_found_in_index(requirement)
487
+
488
+ for url in list(self.package_pages.get(requirement.key, ())):
489
+ # scan each page that might be related to the desired package
490
+ self.scan_url(url)
491
+
492
+ def obtain(self, requirement, installer=None):
493
+ self.prescan()
494
+ self.find_packages(requirement)
495
+ for dist in self[requirement.key]:
496
+ if dist in requirement:
497
+ return dist
498
+ self.debug("%s does not match %s", requirement, dist)
499
+ return super(PackageIndex, self).obtain(requirement, installer)
500
+
501
+ def check_hash(self, checker, filename, tfp):
502
+ """
503
+ checker is a ContentChecker
504
+ """
505
+ checker.report(
506
+ self.debug,
507
+ "Validating %%s checksum for %s" % filename)
508
+ if not checker.is_valid():
509
+ tfp.close()
510
+ os.unlink(filename)
511
+ raise DistutilsError(
512
+ "%s validation failed for %s; "
513
+ "possible download problem?"
514
+ % (checker.hash.name, os.path.basename(filename))
515
+ )
516
+
517
+ def add_find_links(self, urls):
518
+ """Add `urls` to the list that will be prescanned for searches"""
519
+ for url in urls:
520
+ if (
521
+ self.to_scan is None # if we have already "gone online"
522
+ or not URL_SCHEME(url) # or it's a local file/directory
523
+ or url.startswith('file:')
524
+ or list(distros_for_url(url)) # or a direct package link
525
+ ):
526
+ # then go ahead and process it now
527
+ self.scan_url(url)
528
+ else:
529
+ # otherwise, defer retrieval till later
530
+ self.to_scan.append(url)
531
+
532
+ def prescan(self):
533
+ """Scan urls scheduled for prescanning (e.g. --find-links)"""
534
+ if self.to_scan:
535
+ list(map(self.scan_url, self.to_scan))
536
+ self.to_scan = None # from now on, go ahead and process immediately
537
+
538
+ def not_found_in_index(self, requirement):
539
+ if self[requirement.key]: # we've seen at least one distro
540
+ meth, msg = self.info, "Couldn't retrieve index page for %r"
541
+ else: # no distros seen for this name, might be misspelled
542
+ meth, msg = (
543
+ self.warn,
544
+ "Couldn't find index page for %r (maybe misspelled?)")
545
+ meth(msg, requirement.unsafe_name)
546
+ self.scan_all()
547
+
548
+ def download(self, spec, tmpdir):
549
+ """Locate and/or download `spec` to `tmpdir`, returning a local path
550
+
551
+ `spec` may be a ``Requirement`` object, or a string containing a URL,
552
+ an existing local filename, or a project/version requirement spec
553
+ (i.e. the string form of a ``Requirement`` object). If it is the URL
554
+ of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one
555
+ that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is
556
+ automatically created alongside the downloaded file.
557
+
558
+ If `spec` is a ``Requirement`` object or a string containing a
559
+ project/version requirement spec, this method returns the location of
560
+ a matching distribution (possibly after downloading it to `tmpdir`).
561
+ If `spec` is a locally existing file or directory name, it is simply
562
+ returned unchanged. If `spec` is a URL, it is downloaded to a subpath
563
+ of `tmpdir`, and the local filename is returned. Various errors may be
564
+ raised if a problem occurs during downloading.
565
+ """
566
+ if not isinstance(spec, Requirement):
567
+ scheme = URL_SCHEME(spec)
568
+ if scheme:
569
+ # It's a url, download it to tmpdir
570
+ found = self._download_url(spec, tmpdir)
571
+ base, fragment = egg_info_for_url(spec)
572
+ if base.endswith('.py'):
573
+ found = self.gen_setup(found, fragment, tmpdir)
574
+ return found
575
+ elif os.path.exists(spec):
576
+ # Existing file or directory, just return it
577
+ return spec
578
+ else:
579
+ spec = parse_requirement_arg(spec)
580
+ return getattr(self.fetch_distribution(spec, tmpdir), 'location', None)
581
+
582
+ def fetch_distribution( # noqa: C901 # is too complex (14) # FIXME
583
+ self, requirement, tmpdir, force_scan=False, source=False,
584
+ develop_ok=False, local_index=None):
585
+ """Obtain a distribution suitable for fulfilling `requirement`
586
+
587
+ `requirement` must be a ``pkg_resources.Requirement`` instance.
588
+ If necessary, or if the `force_scan` flag is set, the requirement is
589
+ searched for in the (online) package index as well as the locally
590
+ installed packages. If a distribution matching `requirement` is found,
591
+ the returned distribution's ``location`` is the value you would have
592
+ gotten from calling the ``download()`` method with the matching
593
+ distribution's URL or filename. If no matching distribution is found,
594
+ ``None`` is returned.
595
+
596
+ If the `source` flag is set, only source distributions and source
597
+ checkout links will be considered. Unless the `develop_ok` flag is
598
+ set, development and system eggs (i.e., those using the ``.egg-info``
599
+ format) will be ignored.
600
+ """
601
+ # process a Requirement
602
+ self.info("Searching for %s", requirement)
603
+ skipped = {}
604
+ dist = None
605
+
606
+ def find(req, env=None):
607
+ if env is None:
608
+ env = self
609
+ # Find a matching distribution; may be called more than once
610
+
611
+ for dist in env[req.key]:
612
+
613
+ if dist.precedence == DEVELOP_DIST and not develop_ok:
614
+ if dist not in skipped:
615
+ self.warn(
616
+ "Skipping development or system egg: %s", dist,
617
+ )
618
+ skipped[dist] = 1
619
+ continue
620
+
621
+ test = (
622
+ dist in req
623
+ and (dist.precedence <= SOURCE_DIST or not source)
624
+ )
625
+ if test:
626
+ loc = self.download(dist.location, tmpdir)
627
+ dist.download_location = loc
628
+ if os.path.exists(dist.download_location):
629
+ return dist
630
+
631
+ if force_scan:
632
+ self.prescan()
633
+ self.find_packages(requirement)
634
+ dist = find(requirement)
635
+
636
+ if not dist and local_index is not None:
637
+ dist = find(requirement, local_index)
638
+
639
+ if dist is None:
640
+ if self.to_scan is not None:
641
+ self.prescan()
642
+ dist = find(requirement)
643
+
644
+ if dist is None and not force_scan:
645
+ self.find_packages(requirement)
646
+ dist = find(requirement)
647
+
648
+ if dist is None:
649
+ self.warn(
650
+ "No local packages or working download links found for %s%s",
651
+ (source and "a source distribution of " or ""),
652
+ requirement,
653
+ )
654
+ else:
655
+ self.info("Best match: %s", dist)
656
+ return dist.clone(location=dist.download_location)
657
+
658
+ def fetch(self, requirement, tmpdir, force_scan=False, source=False):
659
+ """Obtain a file suitable for fulfilling `requirement`
660
+
661
+ DEPRECATED; use the ``fetch_distribution()`` method now instead. For
662
+ backward compatibility, this routine is identical but returns the
663
+ ``location`` of the downloaded distribution instead of a distribution
664
+ object.
665
+ """
666
+ dist = self.fetch_distribution(requirement, tmpdir, force_scan, source)
667
+ if dist is not None:
668
+ return dist.location
669
+ return None
670
+
671
+ def gen_setup(self, filename, fragment, tmpdir):
672
+ match = EGG_FRAGMENT.match(fragment)
673
+ dists = match and [
674
+ d for d in
675
+ interpret_distro_name(filename, match.group(1), None) if d.version
676
+ ] or []
677
+
678
+ if len(dists) == 1: # unambiguous ``#egg`` fragment
679
+ basename = os.path.basename(filename)
680
+
681
+ # Make sure the file has been downloaded to the temp dir.
682
+ if os.path.dirname(filename) != tmpdir:
683
+ dst = os.path.join(tmpdir, basename)
684
+ from setuptools.command.easy_install import samefile
685
+ if not samefile(filename, dst):
686
+ shutil.copy2(filename, dst)
687
+ filename = dst
688
+
689
+ with open(os.path.join(tmpdir, 'setup.py'), 'w') as file:
690
+ file.write(
691
+ "from setuptools import setup\n"
692
+ "setup(name=%r, version=%r, py_modules=[%r])\n"
693
+ % (
694
+ dists[0].project_name, dists[0].version,
695
+ os.path.splitext(basename)[0]
696
+ )
697
+ )
698
+ return filename
699
+
700
+ elif match:
701
+ raise DistutilsError(
702
+ "Can't unambiguously interpret project/version identifier %r; "
703
+ "any dashes in the name or version should be escaped using "
704
+ "underscores. %r" % (fragment, dists)
705
+ )
706
+ else:
707
+ raise DistutilsError(
708
+ "Can't process plain .py files without an '#egg=name-version'"
709
+ " suffix to enable automatic setup script generation."
710
+ )
711
+
712
+ dl_blocksize = 8192
713
+
714
+ def _download_to(self, url, filename):
715
+ self.info("Downloading %s", url)
716
+ # Download the file
717
+ fp = None
718
+ try:
719
+ checker = HashChecker.from_url(url)
720
+ fp = self.open_url(url)
721
+ if isinstance(fp, urllib.error.HTTPError):
722
+ raise DistutilsError(
723
+ "Can't download %s: %s %s" % (url, fp.code, fp.msg)
724
+ )
725
+ headers = fp.info()
726
+ blocknum = 0
727
+ bs = self.dl_blocksize
728
+ size = -1
729
+ if "content-length" in headers:
730
+ # Some servers return multiple Content-Length headers :(
731
+ sizes = headers.get_all('Content-Length')
732
+ size = max(map(int, sizes))
733
+ self.reporthook(url, filename, blocknum, bs, size)
734
+ with open(filename, 'wb') as tfp:
735
+ while True:
736
+ block = fp.read(bs)
737
+ if block:
738
+ checker.feed(block)
739
+ tfp.write(block)
740
+ blocknum += 1
741
+ self.reporthook(url, filename, blocknum, bs, size)
742
+ else:
743
+ break
744
+ self.check_hash(checker, filename, tfp)
745
+ return headers
746
+ finally:
747
+ if fp:
748
+ fp.close()
749
+
750
+ def reporthook(self, url, filename, blocknum, blksize, size):
751
+ pass # no-op
752
+
753
+ # FIXME:
754
+ def open_url(self, url, warning=None): # noqa: C901 # is too complex (12)
755
+ if url.startswith('file:'):
756
+ return local_open(url)
757
+ try:
758
+ return open_with_auth(url, self.opener)
759
+ except (ValueError, http.client.InvalidURL) as v:
760
+ msg = ' '.join([str(arg) for arg in v.args])
761
+ if warning:
762
+ self.warn(warning, msg)
763
+ else:
764
+ raise DistutilsError('%s %s' % (url, msg)) from v
765
+ except urllib.error.HTTPError as v:
766
+ return v
767
+ except urllib.error.URLError as v:
768
+ if warning:
769
+ self.warn(warning, v.reason)
770
+ else:
771
+ raise DistutilsError("Download error for %s: %s"
772
+ % (url, v.reason)) from v
773
+ except http.client.BadStatusLine as v:
774
+ if warning:
775
+ self.warn(warning, v.line)
776
+ else:
777
+ raise DistutilsError(
778
+ '%s returned a bad status line. The server might be '
779
+ 'down, %s' %
780
+ (url, v.line)
781
+ ) from v
782
+ except (http.client.HTTPException, socket.error) as v:
783
+ if warning:
784
+ self.warn(warning, v)
785
+ else:
786
+ raise DistutilsError("Download error for %s: %s"
787
+ % (url, v)) from v
788
+
789
+ def _download_url(self, url, tmpdir):
790
+ # Determine download filename
791
+ #
792
+ name, fragment = egg_info_for_url(url)
793
+ if name:
794
+ while '..' in name:
795
+ name = name.replace('..', '.').replace('\\', '_')
796
+ else:
797
+ name = "__downloaded__" # default if URL has no path contents
798
+
799
+ if name.endswith('.egg.zip'):
800
+ name = name[:-4] # strip the extra .zip before download
801
+
802
+ filename = os.path.join(tmpdir, name)
803
+
804
+ return self._download_vcs(url, filename) or self._download_other(url, filename)
805
+
806
+ @staticmethod
807
+ def _resolve_vcs(url):
808
+ """
809
+ >>> rvcs = PackageIndex._resolve_vcs
810
+ >>> rvcs('git+http://foo/bar')
811
+ 'git'
812
+ >>> rvcs('hg+https://foo/bar')
813
+ 'hg'
814
+ >>> rvcs('git:myhost')
815
+ 'git'
816
+ >>> rvcs('hg:myhost')
817
+ >>> rvcs('http://foo/bar')
818
+ """
819
+ scheme = urllib.parse.urlsplit(url).scheme
820
+ pre, sep, post = scheme.partition('+')
821
+ # svn and git have their own protocol; hg does not
822
+ allowed = set(['svn', 'git'] + ['hg'] * bool(sep))
823
+ return next(iter({pre} & allowed), None)
824
+
825
+ def _download_vcs(self, url, spec_filename):
826
+ vcs = self._resolve_vcs(url)
827
+ if not vcs:
828
+ return
829
+ if vcs == 'svn':
830
+ return self._download_svn(url, spec_filename)
831
+
832
+ filename, _, _ = spec_filename.partition('#')
833
+ url, rev = self._vcs_split_rev_from_url(url)
834
+
835
+ self.info(f"Doing {vcs} clone from {url} to {filename}")
836
+ subprocess.check_call([vcs, 'clone', '--quiet', url, filename])
837
+
838
+ co_commands = dict(
839
+ git=[vcs, '-C', filename, 'checkout', '--quiet', rev],
840
+ hg=[vcs, '--cwd', filename, 'up', '-C', '-r', rev, '-q'],
841
+ )
842
+ if rev is not None:
843
+ self.info(f"Checking out {rev}")
844
+ subprocess.check_call(co_commands[vcs])
845
+
846
+ return filename
847
+
848
+ def _download_other(self, url, filename):
849
+ scheme = urllib.parse.urlsplit(url).scheme
850
+ if scheme == 'file': # pragma: no cover
851
+ return urllib.request.url2pathname(urllib.parse.urlparse(url).path)
852
+ # raise error if not allowed
853
+ self.url_ok(url, True)
854
+ return self._attempt_download(url, filename)
855
+
856
+ def scan_url(self, url):
857
+ self.process_url(url, True)
858
+
859
+ def _attempt_download(self, url, filename):
860
+ headers = self._download_to(url, filename)
861
+ if 'html' in headers.get('content-type', '').lower():
862
+ return self._download_html(url, headers, filename)
863
+ else:
864
+ return filename
865
+
866
+ def _download_html(self, url, headers, filename):
867
+ file = open(filename)
868
+ for line in file:
869
+ if line.strip():
870
+ # Check for a subversion index page
871
+ if re.search(r'<title>([^- ]+ - )?Revision \d+:', line):
872
+ # it's a subversion index page:
873
+ file.close()
874
+ os.unlink(filename)
875
+ return self._download_svn(url, filename)
876
+ break # not an index page
877
+ file.close()
878
+ os.unlink(filename)
879
+ raise DistutilsError("Unexpected HTML page found at " + url)
880
+
881
+ def _download_svn(self, url, filename):
882
+ warnings.warn("SVN download support is deprecated", UserWarning)
883
+ url = url.split('#', 1)[0] # remove any fragment for svn's sake
884
+ creds = []
885
+ if url.lower().startswith('svn:') and '@' in url:
886
+ scheme, netloc, path, p, q, f = urllib.parse.urlparse(url)
887
+ if not netloc and path.startswith('//') and '/' in path[2:]:
888
+ netloc, path = path[2:].split('/', 1)
889
+ auth, host = _splituser(netloc)
890
+ if auth:
891
+ if ':' in auth:
892
+ user, pw = auth.split(':', 1)
893
+ creds.extend(["--username", user, "--password", pw])
894
+ else:
895
+ creds.extend(["--username", auth])
896
+ netloc = host
897
+ parts = scheme, netloc, url, p, q, f
898
+ url = urllib.parse.urlunparse(parts)
899
+ self.info("Doing subversion checkout from %s to %s", url, filename)
900
+ cmd = ["svn", "checkout", "-q"] + creds + [url, filename]
901
+ subprocess.check_call(cmd)
902
+
903
+ return filename
904
+
905
+ @staticmethod
906
+ def _vcs_split_rev_from_url(url):
907
+ """
908
+ Given a possible VCS URL, return a clean URL and resolved revision if any.
909
+
910
+ >>> vsrfu = PackageIndex._vcs_split_rev_from_url
911
+ >>> vsrfu('git+https://github.com/pypa/setuptools@v69.0.0#egg-info=setuptools')
912
+ ('https://github.com/pypa/setuptools', 'v69.0.0')
913
+ >>> vsrfu('git+https://github.com/pypa/setuptools#egg-info=setuptools')
914
+ ('https://github.com/pypa/setuptools', None)
915
+ >>> vsrfu('http://foo/bar')
916
+ ('http://foo/bar', None)
917
+ """
918
+ parts = urllib.parse.urlsplit(url)
919
+
920
+ clean_scheme = parts.scheme.split('+', 1)[-1]
921
+
922
+ # Some fragment identification fails
923
+ no_fragment_path, _, _ = parts.path.partition('#')
924
+
925
+ pre, sep, post = no_fragment_path.rpartition('@')
926
+ clean_path, rev = (pre, post) if sep else (post, None)
927
+
928
+ resolved = parts._replace(
929
+ scheme=clean_scheme,
930
+ path=clean_path,
931
+ # discard the fragment
932
+ fragment='',
933
+ ).geturl()
934
+
935
+ return resolved, rev
936
+
937
+ def debug(self, msg, *args):
938
+ log.debug(msg, *args)
939
+
940
+ def info(self, msg, *args):
941
+ log.info(msg, *args)
942
+
943
+ def warn(self, msg, *args):
944
+ log.warn(msg, *args)
945
+
946
+
947
+ # This pattern matches a character entity reference (a decimal numeric
948
+ # references, a hexadecimal numeric reference, or a named reference).
949
+ entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
950
+
951
+
952
+ def decode_entity(match):
953
+ what = match.group(0)
954
+ return html.unescape(what)
955
+
956
+
957
+ def htmldecode(text):
958
+ """
959
+ Decode HTML entities in the given text.
960
+
961
+ >>> htmldecode(
962
+ ... 'https://../package_name-0.1.2.tar.gz'
963
+ ... '?tokena=A&amp;tokenb=B">package_name-0.1.2.tar.gz')
964
+ 'https://../package_name-0.1.2.tar.gz?tokena=A&tokenb=B">package_name-0.1.2.tar.gz'
965
+ """
966
+ return entity_sub(decode_entity, text)
967
+
968
+
969
+ def socket_timeout(timeout=15):
970
+ def _socket_timeout(func):
971
+ def _socket_timeout(*args, **kwargs):
972
+ old_timeout = socket.getdefaulttimeout()
973
+ socket.setdefaulttimeout(timeout)
974
+ try:
975
+ return func(*args, **kwargs)
976
+ finally:
977
+ socket.setdefaulttimeout(old_timeout)
978
+
979
+ return _socket_timeout
980
+
981
+ return _socket_timeout
982
+
983
+
984
+ def _encode_auth(auth):
985
+ """
986
+ Encode auth from a URL suitable for an HTTP header.
987
+ >>> str(_encode_auth('username%3Apassword'))
988
+ 'dXNlcm5hbWU6cGFzc3dvcmQ='
989
+
990
+ Long auth strings should not cause a newline to be inserted.
991
+ >>> long_auth = 'username:' + 'password'*10
992
+ >>> chr(10) in str(_encode_auth(long_auth))
993
+ False
994
+ """
995
+ auth_s = urllib.parse.unquote(auth)
996
+ # convert to bytes
997
+ auth_bytes = auth_s.encode()
998
+ encoded_bytes = base64.b64encode(auth_bytes)
999
+ # convert back to a string
1000
+ encoded = encoded_bytes.decode()
1001
+ # strip the trailing carriage return
1002
+ return encoded.replace('\n', '')
1003
+
1004
+
1005
+ class Credential:
1006
+ """
1007
+ A username/password pair. Use like a namedtuple.
1008
+ """
1009
+
1010
+ def __init__(self, username, password):
1011
+ self.username = username
1012
+ self.password = password
1013
+
1014
+ def __iter__(self):
1015
+ yield self.username
1016
+ yield self.password
1017
+
1018
+ def __str__(self):
1019
+ return '%(username)s:%(password)s' % vars(self)
1020
+
1021
+
1022
+ class PyPIConfig(configparser.RawConfigParser):
1023
+ def __init__(self):
1024
+ """
1025
+ Load from ~/.pypirc
1026
+ """
1027
+ defaults = dict.fromkeys(['username', 'password', 'repository'], '')
1028
+ configparser.RawConfigParser.__init__(self, defaults)
1029
+
1030
+ rc = os.path.join(os.path.expanduser('~'), '.pypirc')
1031
+ if os.path.exists(rc):
1032
+ self.read(rc)
1033
+
1034
+ @property
1035
+ def creds_by_repository(self):
1036
+ sections_with_repositories = [
1037
+ section for section in self.sections()
1038
+ if self.get(section, 'repository').strip()
1039
+ ]
1040
+
1041
+ return dict(map(self._get_repo_cred, sections_with_repositories))
1042
+
1043
+ def _get_repo_cred(self, section):
1044
+ repo = self.get(section, 'repository').strip()
1045
+ return repo, Credential(
1046
+ self.get(section, 'username').strip(),
1047
+ self.get(section, 'password').strip(),
1048
+ )
1049
+
1050
+ def find_credential(self, url):
1051
+ """
1052
+ If the URL indicated appears to be a repository defined in this
1053
+ config, return the credential for that repository.
1054
+ """
1055
+ for repository, cred in self.creds_by_repository.items():
1056
+ if url.startswith(repository):
1057
+ return cred
1058
+
1059
+
1060
+ def open_with_auth(url, opener=urllib.request.urlopen):
1061
+ """Open a urllib2 request, handling HTTP authentication"""
1062
+
1063
+ parsed = urllib.parse.urlparse(url)
1064
+ scheme, netloc, path, params, query, frag = parsed
1065
+
1066
+ # Double scheme does not raise on macOS as revealed by a
1067
+ # failing test. We would expect "nonnumeric port". Refs #20.
1068
+ if netloc.endswith(':'):
1069
+ raise http.client.InvalidURL("nonnumeric port: ''")
1070
+
1071
+ if scheme in ('http', 'https'):
1072
+ auth, address = _splituser(netloc)
1073
+ else:
1074
+ auth = None
1075
+
1076
+ if not auth:
1077
+ cred = PyPIConfig().find_credential(url)
1078
+ if cred:
1079
+ auth = str(cred)
1080
+ info = cred.username, url
1081
+ log.info('Authenticating as %s for %s (from .pypirc)', *info)
1082
+
1083
+ if auth:
1084
+ auth = "Basic " + _encode_auth(auth)
1085
+ parts = scheme, address, path, params, query, frag
1086
+ new_url = urllib.parse.urlunparse(parts)
1087
+ request = urllib.request.Request(new_url)
1088
+ request.add_header("Authorization", auth)
1089
+ else:
1090
+ request = urllib.request.Request(url)
1091
+
1092
+ request.add_header('User-Agent', user_agent)
1093
+ fp = opener(request)
1094
+
1095
+ if auth:
1096
+ # Put authentication info back into request URL if same host,
1097
+ # so that links found on the page will work
1098
+ s2, h2, path2, param2, query2, frag2 = urllib.parse.urlparse(fp.url)
1099
+ if s2 == scheme and h2 == address:
1100
+ parts = s2, netloc, path2, param2, query2, frag2
1101
+ fp.url = urllib.parse.urlunparse(parts)
1102
+
1103
+ return fp
1104
+
1105
+
1106
+ # copy of urllib.parse._splituser from Python 3.8
1107
+ def _splituser(host):
1108
+ """splituser('user[:passwd]@host[:port]')
1109
+ --> 'user[:passwd]', 'host[:port]'."""
1110
+ user, delim, host = host.rpartition('@')
1111
+ return (user if delim else None), host
1112
+
1113
+
1114
+ # adding a timeout to avoid freezing package_index
1115
+ open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth)
1116
+
1117
+
1118
+ def fix_sf_url(url):
1119
+ return url # backward compatibility
1120
+
1121
+
1122
+ def local_open(url):
1123
+ """Read a local path, with special support for directories"""
1124
+ scheme, server, path, param, query, frag = urllib.parse.urlparse(url)
1125
+ filename = urllib.request.url2pathname(path)
1126
+ if os.path.isfile(filename):
1127
+ return urllib.request.urlopen(url)
1128
+ elif path.endswith('/') and os.path.isdir(filename):
1129
+ files = []
1130
+ for f in os.listdir(filename):
1131
+ filepath = os.path.join(filename, f)
1132
+ if f == 'index.html':
1133
+ with open(filepath, 'r') as fp:
1134
+ body = fp.read()
1135
+ break
1136
+ elif os.path.isdir(filepath):
1137
+ f += '/'
1138
+ files.append('<a href="{name}">{name}</a>'.format(name=f))
1139
+ else:
1140
+ tmpl = (
1141
+ "<html><head><title>{url}</title>"
1142
+ "</head><body>{files}</body></html>")
1143
+ body = tmpl.format(url=url, files='\n'.join(files))
1144
+ status, message = 200, "OK"
1145
+ else:
1146
+ status, message, body = 404, "Path not found", "Not found"
1147
+
1148
+ headers = {'content-type': 'text/html'}
1149
+ body_stream = io.StringIO(body)
1150
+ return urllib.error.HTTPError(url, status, message, headers, body_stream)
venv/lib/python3.10/site-packages/setuptools/py34compat.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import importlib
2
+
3
+ try:
4
+ import importlib.util
5
+ except ImportError:
6
+ pass
7
+
8
+
9
+ try:
10
+ module_from_spec = importlib.util.module_from_spec
11
+ except AttributeError:
12
+ def module_from_spec(spec):
13
+ return spec.loader.load_module(spec.name)
venv/lib/python3.10/site-packages/setuptools/sandbox.py ADDED
@@ -0,0 +1,530 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import tempfile
4
+ import operator
5
+ import functools
6
+ import itertools
7
+ import re
8
+ import contextlib
9
+ import pickle
10
+ import textwrap
11
+ import builtins
12
+
13
+ import pkg_resources
14
+ from distutils.errors import DistutilsError
15
+ from pkg_resources import working_set
16
+
17
+ if sys.platform.startswith('java'):
18
+ import org.python.modules.posix.PosixModule as _os
19
+ else:
20
+ _os = sys.modules[os.name]
21
+ try:
22
+ _file = file
23
+ except NameError:
24
+ _file = None
25
+ _open = open
26
+
27
+
28
+ __all__ = [
29
+ "AbstractSandbox",
30
+ "DirectorySandbox",
31
+ "SandboxViolation",
32
+ "run_setup",
33
+ ]
34
+
35
+
36
+ def _execfile(filename, globals, locals=None):
37
+ """
38
+ Python 3 implementation of execfile.
39
+ """
40
+ mode = 'rb'
41
+ with open(filename, mode) as stream:
42
+ script = stream.read()
43
+ if locals is None:
44
+ locals = globals
45
+ code = compile(script, filename, 'exec')
46
+ exec(code, globals, locals)
47
+
48
+
49
+ @contextlib.contextmanager
50
+ def save_argv(repl=None):
51
+ saved = sys.argv[:]
52
+ if repl is not None:
53
+ sys.argv[:] = repl
54
+ try:
55
+ yield saved
56
+ finally:
57
+ sys.argv[:] = saved
58
+
59
+
60
+ @contextlib.contextmanager
61
+ def save_path():
62
+ saved = sys.path[:]
63
+ try:
64
+ yield saved
65
+ finally:
66
+ sys.path[:] = saved
67
+
68
+
69
+ @contextlib.contextmanager
70
+ def override_temp(replacement):
71
+ """
72
+ Monkey-patch tempfile.tempdir with replacement, ensuring it exists
73
+ """
74
+ os.makedirs(replacement, exist_ok=True)
75
+
76
+ saved = tempfile.tempdir
77
+
78
+ tempfile.tempdir = replacement
79
+
80
+ try:
81
+ yield
82
+ finally:
83
+ tempfile.tempdir = saved
84
+
85
+
86
+ @contextlib.contextmanager
87
+ def pushd(target):
88
+ saved = os.getcwd()
89
+ os.chdir(target)
90
+ try:
91
+ yield saved
92
+ finally:
93
+ os.chdir(saved)
94
+
95
+
96
+ class UnpickleableException(Exception):
97
+ """
98
+ An exception representing another Exception that could not be pickled.
99
+ """
100
+
101
+ @staticmethod
102
+ def dump(type, exc):
103
+ """
104
+ Always return a dumped (pickled) type and exc. If exc can't be pickled,
105
+ wrap it in UnpickleableException first.
106
+ """
107
+ try:
108
+ return pickle.dumps(type), pickle.dumps(exc)
109
+ except Exception:
110
+ # get UnpickleableException inside the sandbox
111
+ from setuptools.sandbox import UnpickleableException as cls
112
+
113
+ return cls.dump(cls, cls(repr(exc)))
114
+
115
+
116
+ class ExceptionSaver:
117
+ """
118
+ A Context Manager that will save an exception, serialized, and restore it
119
+ later.
120
+ """
121
+
122
+ def __enter__(self):
123
+ return self
124
+
125
+ def __exit__(self, type, exc, tb):
126
+ if not exc:
127
+ return
128
+
129
+ # dump the exception
130
+ self._saved = UnpickleableException.dump(type, exc)
131
+ self._tb = tb
132
+
133
+ # suppress the exception
134
+ return True
135
+
136
+ def resume(self):
137
+ "restore and re-raise any exception"
138
+
139
+ if '_saved' not in vars(self):
140
+ return
141
+
142
+ type, exc = map(pickle.loads, self._saved)
143
+ raise exc.with_traceback(self._tb)
144
+
145
+
146
+ @contextlib.contextmanager
147
+ def save_modules():
148
+ """
149
+ Context in which imported modules are saved.
150
+
151
+ Translates exceptions internal to the context into the equivalent exception
152
+ outside the context.
153
+ """
154
+ saved = sys.modules.copy()
155
+ with ExceptionSaver() as saved_exc:
156
+ yield saved
157
+
158
+ sys.modules.update(saved)
159
+ # remove any modules imported since
160
+ del_modules = (
161
+ mod_name
162
+ for mod_name in sys.modules
163
+ if mod_name not in saved
164
+ # exclude any encodings modules. See #285
165
+ and not mod_name.startswith('encodings.')
166
+ )
167
+ _clear_modules(del_modules)
168
+
169
+ saved_exc.resume()
170
+
171
+
172
+ def _clear_modules(module_names):
173
+ for mod_name in list(module_names):
174
+ del sys.modules[mod_name]
175
+
176
+
177
+ @contextlib.contextmanager
178
+ def save_pkg_resources_state():
179
+ saved = pkg_resources.__getstate__()
180
+ try:
181
+ yield saved
182
+ finally:
183
+ pkg_resources.__setstate__(saved)
184
+
185
+
186
+ @contextlib.contextmanager
187
+ def setup_context(setup_dir):
188
+ temp_dir = os.path.join(setup_dir, 'temp')
189
+ with save_pkg_resources_state():
190
+ with save_modules():
191
+ with save_path():
192
+ hide_setuptools()
193
+ with save_argv():
194
+ with override_temp(temp_dir):
195
+ with pushd(setup_dir):
196
+ # ensure setuptools commands are available
197
+ __import__('setuptools')
198
+ yield
199
+
200
+
201
+ _MODULES_TO_HIDE = {
202
+ 'setuptools',
203
+ 'distutils',
204
+ 'pkg_resources',
205
+ 'Cython',
206
+ '_distutils_hack',
207
+ }
208
+
209
+
210
+ def _needs_hiding(mod_name):
211
+ """
212
+ >>> _needs_hiding('setuptools')
213
+ True
214
+ >>> _needs_hiding('pkg_resources')
215
+ True
216
+ >>> _needs_hiding('setuptools_plugin')
217
+ False
218
+ >>> _needs_hiding('setuptools.__init__')
219
+ True
220
+ >>> _needs_hiding('distutils')
221
+ True
222
+ >>> _needs_hiding('os')
223
+ False
224
+ >>> _needs_hiding('Cython')
225
+ True
226
+ """
227
+ base_module = mod_name.split('.', 1)[0]
228
+ return base_module in _MODULES_TO_HIDE
229
+
230
+
231
+ def hide_setuptools():
232
+ """
233
+ Remove references to setuptools' modules from sys.modules to allow the
234
+ invocation to import the most appropriate setuptools. This technique is
235
+ necessary to avoid issues such as #315 where setuptools upgrading itself
236
+ would fail to find a function declared in the metadata.
237
+ """
238
+ _distutils_hack = sys.modules.get('_distutils_hack', None)
239
+ if _distutils_hack is not None:
240
+ _distutils_hack.remove_shim()
241
+
242
+ modules = filter(_needs_hiding, sys.modules)
243
+ _clear_modules(modules)
244
+
245
+
246
+ def run_setup(setup_script, args):
247
+ """Run a distutils setup script, sandboxed in its directory"""
248
+ setup_dir = os.path.abspath(os.path.dirname(setup_script))
249
+ with setup_context(setup_dir):
250
+ try:
251
+ sys.argv[:] = [setup_script] + list(args)
252
+ sys.path.insert(0, setup_dir)
253
+ # reset to include setup dir, w/clean callback list
254
+ working_set.__init__()
255
+ working_set.callbacks.append(lambda dist: dist.activate())
256
+
257
+ with DirectorySandbox(setup_dir):
258
+ ns = dict(__file__=setup_script, __name__='__main__')
259
+ _execfile(setup_script, ns)
260
+ except SystemExit as v:
261
+ if v.args and v.args[0]:
262
+ raise
263
+ # Normal exit, just return
264
+
265
+
266
+ class AbstractSandbox:
267
+ """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
268
+
269
+ _active = False
270
+
271
+ def __init__(self):
272
+ self._attrs = [
273
+ name
274
+ for name in dir(_os)
275
+ if not name.startswith('_') and hasattr(self, name)
276
+ ]
277
+
278
+ def _copy(self, source):
279
+ for name in self._attrs:
280
+ setattr(os, name, getattr(source, name))
281
+
282
+ def __enter__(self):
283
+ self._copy(self)
284
+ if _file:
285
+ builtins.file = self._file
286
+ builtins.open = self._open
287
+ self._active = True
288
+
289
+ def __exit__(self, exc_type, exc_value, traceback):
290
+ self._active = False
291
+ if _file:
292
+ builtins.file = _file
293
+ builtins.open = _open
294
+ self._copy(_os)
295
+
296
+ def run(self, func):
297
+ """Run 'func' under os sandboxing"""
298
+ with self:
299
+ return func()
300
+
301
+ def _mk_dual_path_wrapper(name):
302
+ original = getattr(_os, name)
303
+
304
+ def wrap(self, src, dst, *args, **kw):
305
+ if self._active:
306
+ src, dst = self._remap_pair(name, src, dst, *args, **kw)
307
+ return original(src, dst, *args, **kw)
308
+
309
+ return wrap
310
+
311
+ for name in ["rename", "link", "symlink"]:
312
+ if hasattr(_os, name):
313
+ locals()[name] = _mk_dual_path_wrapper(name)
314
+
315
+ def _mk_single_path_wrapper(name, original=None):
316
+ original = original or getattr(_os, name)
317
+
318
+ def wrap(self, path, *args, **kw):
319
+ if self._active:
320
+ path = self._remap_input(name, path, *args, **kw)
321
+ return original(path, *args, **kw)
322
+
323
+ return wrap
324
+
325
+ if _file:
326
+ _file = _mk_single_path_wrapper('file', _file)
327
+ _open = _mk_single_path_wrapper('open', _open)
328
+ for name in [
329
+ "stat",
330
+ "listdir",
331
+ "chdir",
332
+ "open",
333
+ "chmod",
334
+ "chown",
335
+ "mkdir",
336
+ "remove",
337
+ "unlink",
338
+ "rmdir",
339
+ "utime",
340
+ "lchown",
341
+ "chroot",
342
+ "lstat",
343
+ "startfile",
344
+ "mkfifo",
345
+ "mknod",
346
+ "pathconf",
347
+ "access",
348
+ ]:
349
+ if hasattr(_os, name):
350
+ locals()[name] = _mk_single_path_wrapper(name)
351
+
352
+ def _mk_single_with_return(name):
353
+ original = getattr(_os, name)
354
+
355
+ def wrap(self, path, *args, **kw):
356
+ if self._active:
357
+ path = self._remap_input(name, path, *args, **kw)
358
+ return self._remap_output(name, original(path, *args, **kw))
359
+ return original(path, *args, **kw)
360
+
361
+ return wrap
362
+
363
+ for name in ['readlink', 'tempnam']:
364
+ if hasattr(_os, name):
365
+ locals()[name] = _mk_single_with_return(name)
366
+
367
+ def _mk_query(name):
368
+ original = getattr(_os, name)
369
+
370
+ def wrap(self, *args, **kw):
371
+ retval = original(*args, **kw)
372
+ if self._active:
373
+ return self._remap_output(name, retval)
374
+ return retval
375
+
376
+ return wrap
377
+
378
+ for name in ['getcwd', 'tmpnam']:
379
+ if hasattr(_os, name):
380
+ locals()[name] = _mk_query(name)
381
+
382
+ def _validate_path(self, path):
383
+ """Called to remap or validate any path, whether input or output"""
384
+ return path
385
+
386
+ def _remap_input(self, operation, path, *args, **kw):
387
+ """Called for path inputs"""
388
+ return self._validate_path(path)
389
+
390
+ def _remap_output(self, operation, path):
391
+ """Called for path outputs"""
392
+ return self._validate_path(path)
393
+
394
+ def _remap_pair(self, operation, src, dst, *args, **kw):
395
+ """Called for path pairs like rename, link, and symlink operations"""
396
+ return (
397
+ self._remap_input(operation + '-from', src, *args, **kw),
398
+ self._remap_input(operation + '-to', dst, *args, **kw),
399
+ )
400
+
401
+
402
+ if hasattr(os, 'devnull'):
403
+ _EXCEPTIONS = [os.devnull]
404
+ else:
405
+ _EXCEPTIONS = []
406
+
407
+
408
+ class DirectorySandbox(AbstractSandbox):
409
+ """Restrict operations to a single subdirectory - pseudo-chroot"""
410
+
411
+ write_ops = dict.fromkeys(
412
+ [
413
+ "open",
414
+ "chmod",
415
+ "chown",
416
+ "mkdir",
417
+ "remove",
418
+ "unlink",
419
+ "rmdir",
420
+ "utime",
421
+ "lchown",
422
+ "chroot",
423
+ "mkfifo",
424
+ "mknod",
425
+ "tempnam",
426
+ ]
427
+ )
428
+
429
+ _exception_patterns = []
430
+ "exempt writing to paths that match the pattern"
431
+
432
+ def __init__(self, sandbox, exceptions=_EXCEPTIONS):
433
+ self._sandbox = os.path.normcase(os.path.realpath(sandbox))
434
+ self._prefix = os.path.join(self._sandbox, '')
435
+ self._exceptions = [
436
+ os.path.normcase(os.path.realpath(path)) for path in exceptions
437
+ ]
438
+ AbstractSandbox.__init__(self)
439
+
440
+ def _violation(self, operation, *args, **kw):
441
+ from setuptools.sandbox import SandboxViolation
442
+
443
+ raise SandboxViolation(operation, args, kw)
444
+
445
+ if _file:
446
+
447
+ def _file(self, path, mode='r', *args, **kw):
448
+ if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
449
+ self._violation("file", path, mode, *args, **kw)
450
+ return _file(path, mode, *args, **kw)
451
+
452
+ def _open(self, path, mode='r', *args, **kw):
453
+ if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
454
+ self._violation("open", path, mode, *args, **kw)
455
+ return _open(path, mode, *args, **kw)
456
+
457
+ def tmpnam(self):
458
+ self._violation("tmpnam")
459
+
460
+ def _ok(self, path):
461
+ active = self._active
462
+ try:
463
+ self._active = False
464
+ realpath = os.path.normcase(os.path.realpath(path))
465
+ return (
466
+ self._exempted(realpath)
467
+ or realpath == self._sandbox
468
+ or realpath.startswith(self._prefix)
469
+ )
470
+ finally:
471
+ self._active = active
472
+
473
+ def _exempted(self, filepath):
474
+ start_matches = (
475
+ filepath.startswith(exception) for exception in self._exceptions
476
+ )
477
+ pattern_matches = (
478
+ re.match(pattern, filepath) for pattern in self._exception_patterns
479
+ )
480
+ candidates = itertools.chain(start_matches, pattern_matches)
481
+ return any(candidates)
482
+
483
+ def _remap_input(self, operation, path, *args, **kw):
484
+ """Called for path inputs"""
485
+ if operation in self.write_ops and not self._ok(path):
486
+ self._violation(operation, os.path.realpath(path), *args, **kw)
487
+ return path
488
+
489
+ def _remap_pair(self, operation, src, dst, *args, **kw):
490
+ """Called for path pairs like rename, link, and symlink operations"""
491
+ if not self._ok(src) or not self._ok(dst):
492
+ self._violation(operation, src, dst, *args, **kw)
493
+ return (src, dst)
494
+
495
+ def open(self, file, flags, mode=0o777, *args, **kw):
496
+ """Called for low-level os.open()"""
497
+ if flags & WRITE_FLAGS and not self._ok(file):
498
+ self._violation("os.open", file, flags, mode, *args, **kw)
499
+ return _os.open(file, flags, mode, *args, **kw)
500
+
501
+
502
+ WRITE_FLAGS = functools.reduce(
503
+ operator.or_,
504
+ [
505
+ getattr(_os, a, 0)
506
+ for a in "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()
507
+ ],
508
+ )
509
+
510
+
511
+ class SandboxViolation(DistutilsError):
512
+ """A setup script attempted to modify the filesystem outside the sandbox"""
513
+
514
+ tmpl = textwrap.dedent(
515
+ """
516
+ SandboxViolation: {cmd}{args!r} {kwargs}
517
+
518
+ The package setup script has attempted to modify files on your system
519
+ that are not within the EasyInstall build area, and has been aborted.
520
+
521
+ This package cannot be safely installed by EasyInstall, and may not
522
+ support alternate installation locations even if you run its setup
523
+ script by hand. Please inform the package's author and the EasyInstall
524
+ maintainers to find out if a fix or workaround is available.
525
+ """
526
+ ).lstrip()
527
+
528
+ def __str__(self):
529
+ cmd, args, kwargs = self.args
530
+ return self.tmpl.format(**locals())
venv/lib/python3.10/site-packages/setuptools/script (dev).tmpl ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r
2
+ __requires__ = %(spec)r
3
+ __import__('pkg_resources').require(%(spec)r)
4
+ __file__ = %(dev_path)r
5
+ with open(__file__) as f:
6
+ exec(compile(f.read(), __file__, 'exec'))
venv/lib/python3.10/site-packages/setuptools/script.tmpl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r
2
+ __requires__ = %(spec)r
3
+ __import__('pkg_resources').run_script(%(spec)r, %(script_name)r)
venv/lib/python3.10/site-packages/setuptools/unicode_utils.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unicodedata
2
+ import sys
3
+
4
+
5
+ # HFS Plus uses decomposed UTF-8
6
+ def decompose(path):
7
+ if isinstance(path, str):
8
+ return unicodedata.normalize('NFD', path)
9
+ try:
10
+ path = path.decode('utf-8')
11
+ path = unicodedata.normalize('NFD', path)
12
+ path = path.encode('utf-8')
13
+ except UnicodeError:
14
+ pass # Not UTF-8
15
+ return path
16
+
17
+
18
+ def filesys_decode(path):
19
+ """
20
+ Ensure that the given path is decoded,
21
+ NONE when no expected encoding works
22
+ """
23
+
24
+ if isinstance(path, str):
25
+ return path
26
+
27
+ fs_enc = sys.getfilesystemencoding() or 'utf-8'
28
+ candidates = fs_enc, 'utf-8'
29
+
30
+ for enc in candidates:
31
+ try:
32
+ return path.decode(enc)
33
+ except UnicodeDecodeError:
34
+ continue
35
+
36
+
37
+ def try_encode(string, enc):
38
+ "turn unicode encoding into a functional routine"
39
+ try:
40
+ return string.encode(enc)
41
+ except UnicodeEncodeError:
42
+ return None
venv/lib/python3.10/site-packages/setuptools/version.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import pkg_resources
2
+
3
+ try:
4
+ __version__ = pkg_resources.get_distribution('setuptools').version
5
+ except Exception:
6
+ __version__ = 'unknown'
venv/lib/python3.10/site-packages/setuptools/wheel.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Wheels support."""
2
+
3
+ from distutils.util import get_platform
4
+ from distutils import log
5
+ import email
6
+ import itertools
7
+ import os
8
+ import posixpath
9
+ import re
10
+ import zipfile
11
+
12
+ import pkg_resources
13
+ import setuptools
14
+ from pkg_resources import parse_version
15
+ from setuptools.extern.packaging.tags import sys_tags
16
+ from setuptools.extern.packaging.utils import canonicalize_name
17
+ from setuptools.command.egg_info import write_requirements
18
+
19
+
20
+ WHEEL_NAME = re.compile(
21
+ r"""^(?P<project_name>.+?)-(?P<version>\d.*?)
22
+ ((-(?P<build>\d.*?))?-(?P<py_version>.+?)-(?P<abi>.+?)-(?P<platform>.+?)
23
+ )\.whl$""",
24
+ re.VERBOSE).match
25
+
26
+ NAMESPACE_PACKAGE_INIT = \
27
+ "__import__('pkg_resources').declare_namespace(__name__)\n"
28
+
29
+
30
+ def unpack(src_dir, dst_dir):
31
+ '''Move everything under `src_dir` to `dst_dir`, and delete the former.'''
32
+ for dirpath, dirnames, filenames in os.walk(src_dir):
33
+ subdir = os.path.relpath(dirpath, src_dir)
34
+ for f in filenames:
35
+ src = os.path.join(dirpath, f)
36
+ dst = os.path.join(dst_dir, subdir, f)
37
+ os.renames(src, dst)
38
+ for n, d in reversed(list(enumerate(dirnames))):
39
+ src = os.path.join(dirpath, d)
40
+ dst = os.path.join(dst_dir, subdir, d)
41
+ if not os.path.exists(dst):
42
+ # Directory does not exist in destination,
43
+ # rename it and prune it from os.walk list.
44
+ os.renames(src, dst)
45
+ del dirnames[n]
46
+ # Cleanup.
47
+ for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True):
48
+ assert not filenames
49
+ os.rmdir(dirpath)
50
+
51
+
52
+ class Wheel:
53
+
54
+ def __init__(self, filename):
55
+ match = WHEEL_NAME(os.path.basename(filename))
56
+ if match is None:
57
+ raise ValueError('invalid wheel name: %r' % filename)
58
+ self.filename = filename
59
+ for k, v in match.groupdict().items():
60
+ setattr(self, k, v)
61
+
62
+ def tags(self):
63
+ '''List tags (py_version, abi, platform) supported by this wheel.'''
64
+ return itertools.product(
65
+ self.py_version.split('.'),
66
+ self.abi.split('.'),
67
+ self.platform.split('.'),
68
+ )
69
+
70
+ def is_compatible(self):
71
+ '''Is the wheel is compatible with the current platform?'''
72
+ supported_tags = set(
73
+ (t.interpreter, t.abi, t.platform) for t in sys_tags())
74
+ return next((True for t in self.tags() if t in supported_tags), False)
75
+
76
+ def egg_name(self):
77
+ return pkg_resources.Distribution(
78
+ project_name=self.project_name, version=self.version,
79
+ platform=(None if self.platform == 'any' else get_platform()),
80
+ ).egg_name() + '.egg'
81
+
82
+ def get_dist_info(self, zf):
83
+ # find the correct name of the .dist-info dir in the wheel file
84
+ for member in zf.namelist():
85
+ dirname = posixpath.dirname(member)
86
+ if (dirname.endswith('.dist-info') and
87
+ canonicalize_name(dirname).startswith(
88
+ canonicalize_name(self.project_name))):
89
+ return dirname
90
+ raise ValueError("unsupported wheel format. .dist-info not found")
91
+
92
+ def install_as_egg(self, destination_eggdir):
93
+ '''Install wheel as an egg directory.'''
94
+ with zipfile.ZipFile(self.filename) as zf:
95
+ self._install_as_egg(destination_eggdir, zf)
96
+
97
+ def _install_as_egg(self, destination_eggdir, zf):
98
+ dist_basename = '%s-%s' % (self.project_name, self.version)
99
+ dist_info = self.get_dist_info(zf)
100
+ dist_data = '%s.data' % dist_basename
101
+ egg_info = os.path.join(destination_eggdir, 'EGG-INFO')
102
+
103
+ self._convert_metadata(zf, destination_eggdir, dist_info, egg_info)
104
+ self._move_data_entries(destination_eggdir, dist_data)
105
+ self._fix_namespace_packages(egg_info, destination_eggdir)
106
+
107
+ @staticmethod
108
+ def _convert_metadata(zf, destination_eggdir, dist_info, egg_info):
109
+ def get_metadata(name):
110
+ with zf.open(posixpath.join(dist_info, name)) as fp:
111
+ value = fp.read().decode('utf-8')
112
+ return email.parser.Parser().parsestr(value)
113
+
114
+ wheel_metadata = get_metadata('WHEEL')
115
+ # Check wheel format version is supported.
116
+ wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))
117
+ wheel_v1 = (
118
+ parse_version('1.0') <= wheel_version < parse_version('2.0dev0')
119
+ )
120
+ if not wheel_v1:
121
+ raise ValueError(
122
+ 'unsupported wheel format version: %s' % wheel_version)
123
+ # Extract to target directory.
124
+ os.mkdir(destination_eggdir)
125
+ zf.extractall(destination_eggdir)
126
+ # Convert metadata.
127
+ dist_info = os.path.join(destination_eggdir, dist_info)
128
+ dist = pkg_resources.Distribution.from_location(
129
+ destination_eggdir, dist_info,
130
+ metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info),
131
+ )
132
+
133
+ # Note: Evaluate and strip markers now,
134
+ # as it's difficult to convert back from the syntax:
135
+ # foobar; "linux" in sys_platform and extra == 'test'
136
+ def raw_req(req):
137
+ req.marker = None
138
+ return str(req)
139
+ install_requires = list(sorted(map(raw_req, dist.requires())))
140
+ extras_require = {
141
+ extra: sorted(
142
+ req
143
+ for req in map(raw_req, dist.requires((extra,)))
144
+ if req not in install_requires
145
+ )
146
+ for extra in dist.extras
147
+ }
148
+ os.rename(dist_info, egg_info)
149
+ os.rename(
150
+ os.path.join(egg_info, 'METADATA'),
151
+ os.path.join(egg_info, 'PKG-INFO'),
152
+ )
153
+ setup_dist = setuptools.Distribution(
154
+ attrs=dict(
155
+ install_requires=install_requires,
156
+ extras_require=extras_require,
157
+ ),
158
+ )
159
+ # Temporarily disable info traces.
160
+ log_threshold = log._global_log.threshold
161
+ log.set_threshold(log.WARN)
162
+ try:
163
+ write_requirements(
164
+ setup_dist.get_command_obj('egg_info'),
165
+ None,
166
+ os.path.join(egg_info, 'requires.txt'),
167
+ )
168
+ finally:
169
+ log.set_threshold(log_threshold)
170
+
171
+ @staticmethod
172
+ def _move_data_entries(destination_eggdir, dist_data):
173
+ """Move data entries to their correct location."""
174
+ dist_data = os.path.join(destination_eggdir, dist_data)
175
+ dist_data_scripts = os.path.join(dist_data, 'scripts')
176
+ if os.path.exists(dist_data_scripts):
177
+ egg_info_scripts = os.path.join(
178
+ destination_eggdir, 'EGG-INFO', 'scripts')
179
+ os.mkdir(egg_info_scripts)
180
+ for entry in os.listdir(dist_data_scripts):
181
+ # Remove bytecode, as it's not properly handled
182
+ # during easy_install scripts install phase.
183
+ if entry.endswith('.pyc'):
184
+ os.unlink(os.path.join(dist_data_scripts, entry))
185
+ else:
186
+ os.rename(
187
+ os.path.join(dist_data_scripts, entry),
188
+ os.path.join(egg_info_scripts, entry),
189
+ )
190
+ os.rmdir(dist_data_scripts)
191
+ for subdir in filter(os.path.exists, (
192
+ os.path.join(dist_data, d)
193
+ for d in ('data', 'headers', 'purelib', 'platlib')
194
+ )):
195
+ unpack(subdir, destination_eggdir)
196
+ if os.path.exists(dist_data):
197
+ os.rmdir(dist_data)
198
+
199
+ @staticmethod
200
+ def _fix_namespace_packages(egg_info, destination_eggdir):
201
+ namespace_packages = os.path.join(
202
+ egg_info, 'namespace_packages.txt')
203
+ if os.path.exists(namespace_packages):
204
+ with open(namespace_packages) as fp:
205
+ namespace_packages = fp.read().split()
206
+ for mod in namespace_packages:
207
+ mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
208
+ mod_init = os.path.join(mod_dir, '__init__.py')
209
+ if not os.path.exists(mod_dir):
210
+ os.mkdir(mod_dir)
211
+ if not os.path.exists(mod_init):
212
+ with open(mod_init, 'w') as fp:
213
+ fp.write(NAMESPACE_PACKAGE_INIT)
venv/lib/python3.10/site-packages/setuptools/windows_support.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import platform
2
+ import ctypes
3
+
4
+
5
+ def windows_only(func):
6
+ if platform.system() != 'Windows':
7
+ return lambda *args, **kwargs: None
8
+ return func
9
+
10
+
11
+ @windows_only
12
+ def hide_file(path):
13
+ """
14
+ Set the hidden attribute on a file or directory.
15
+
16
+ From http://stackoverflow.com/questions/19622133/
17
+
18
+ `path` must be text.
19
+ """
20
+ __import__('ctypes.wintypes')
21
+ SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW
22
+ SetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD
23
+ SetFileAttributes.restype = ctypes.wintypes.BOOL
24
+
25
+ FILE_ATTRIBUTE_HIDDEN = 0x02
26
+
27
+ ret = SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN)
28
+ if not ret:
29
+ raise ctypes.WinError()