Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
news
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Sartika Aritonang
news
Commits
4f613b4c
Commit
4f613b4c
authored
May 29, 2020
by
Sartika Aritonang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Upload New File
parent
d05fead1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1280 additions
and
0 deletions
+1280
-0
dist.py
stbi/Lib/site-packages/setuptools/dist.py
+1280
-0
No files found.
stbi/Lib/site-packages/setuptools/dist.py
0 → 100644
View file @
4f613b4c
# -*- coding: utf-8 -*-
__all__
=
[
'Distribution'
]
import
io
import
sys
import
re
import
os
import
warnings
import
numbers
import
distutils.log
import
distutils.core
import
distutils.cmd
import
distutils.dist
from
distutils.util
import
strtobool
from
distutils.debug
import
DEBUG
from
distutils.fancy_getopt
import
translate_longopt
import
itertools
from
collections
import
defaultdict
from
email
import
message_from_file
from
distutils.errors
import
(
DistutilsOptionError
,
DistutilsPlatformError
,
DistutilsSetupError
,
)
from
distutils.util
import
rfc822_escape
from
distutils.version
import
StrictVersion
from
setuptools.extern
import
six
from
setuptools.extern
import
packaging
from
setuptools.extern.six.moves
import
map
,
filter
,
filterfalse
from
.
import
SetuptoolsDeprecationWarning
from
setuptools.depends
import
Require
from
setuptools
import
windows_support
from
setuptools.monkey
import
get_unpatched
from
setuptools.config
import
parse_configuration
import
pkg_resources
__import__
(
'setuptools.extern.packaging.specifiers'
)
__import__
(
'setuptools.extern.packaging.version'
)
def
_get_unpatched
(
cls
):
warnings
.
warn
(
"Do not call this function"
,
DistDeprecationWarning
)
return
get_unpatched
(
cls
)
def
get_metadata_version
(
self
):
mv
=
getattr
(
self
,
'metadata_version'
,
None
)
if
mv
is
None
:
if
self
.
long_description_content_type
or
self
.
provides_extras
:
mv
=
StrictVersion
(
'2.1'
)
elif
(
self
.
maintainer
is
not
None
or
self
.
maintainer_email
is
not
None
or
getattr
(
self
,
'python_requires'
,
None
)
is
not
None
or
self
.
project_urls
):
mv
=
StrictVersion
(
'1.2'
)
elif
(
self
.
provides
or
self
.
requires
or
self
.
obsoletes
or
self
.
classifiers
or
self
.
download_url
):
mv
=
StrictVersion
(
'1.1'
)
else
:
mv
=
StrictVersion
(
'1.0'
)
self
.
metadata_version
=
mv
return
mv
def
read_pkg_file
(
self
,
file
):
"""Reads the metadata values from a file object."""
msg
=
message_from_file
(
file
)
def
_read_field
(
name
):
value
=
msg
[
name
]
if
value
==
'UNKNOWN'
:
return
None
return
value
def
_read_list
(
name
):
values
=
msg
.
get_all
(
name
,
None
)
if
values
==
[]:
return
None
return
values
self
.
metadata_version
=
StrictVersion
(
msg
[
'metadata-version'
])
self
.
name
=
_read_field
(
'name'
)
self
.
version
=
_read_field
(
'version'
)
self
.
description
=
_read_field
(
'summary'
)
# we are filling author only.
self
.
author
=
_read_field
(
'author'
)
self
.
maintainer
=
None
self
.
author_email
=
_read_field
(
'author-email'
)
self
.
maintainer_email
=
None
self
.
url
=
_read_field
(
'home-page'
)
self
.
license
=
_read_field
(
'license'
)
if
'download-url'
in
msg
:
self
.
download_url
=
_read_field
(
'download-url'
)
else
:
self
.
download_url
=
None
self
.
long_description
=
_read_field
(
'description'
)
self
.
description
=
_read_field
(
'summary'
)
if
'keywords'
in
msg
:
self
.
keywords
=
_read_field
(
'keywords'
)
.
split
(
','
)
self
.
platforms
=
_read_list
(
'platform'
)
self
.
classifiers
=
_read_list
(
'classifier'
)
# PEP 314 - these fields only exist in 1.1
if
self
.
metadata_version
==
StrictVersion
(
'1.1'
):
self
.
requires
=
_read_list
(
'requires'
)
self
.
provides
=
_read_list
(
'provides'
)
self
.
obsoletes
=
_read_list
(
'obsoletes'
)
else
:
self
.
requires
=
None
self
.
provides
=
None
self
.
obsoletes
=
None
# Based on Python 3.5 version
def
write_pkg_file
(
self
,
file
):
"""Write the PKG-INFO format data to a file object.
"""
version
=
self
.
get_metadata_version
()
if
six
.
PY2
:
def
write_field
(
key
,
value
):
file
.
write
(
"
%
s:
%
s
\n
"
%
(
key
,
self
.
_encode_field
(
value
)))
else
:
def
write_field
(
key
,
value
):
file
.
write
(
"
%
s:
%
s
\n
"
%
(
key
,
value
))
write_field
(
'Metadata-Version'
,
str
(
version
))
write_field
(
'Name'
,
self
.
get_name
())
write_field
(
'Version'
,
self
.
get_version
())
write_field
(
'Summary'
,
self
.
get_description
())
write_field
(
'Home-page'
,
self
.
get_url
())
if
version
<
StrictVersion
(
'1.2'
):
write_field
(
'Author'
,
self
.
get_contact
())
write_field
(
'Author-email'
,
self
.
get_contact_email
())
else
:
optional_fields
=
(
(
'Author'
,
'author'
),
(
'Author-email'
,
'author_email'
),
(
'Maintainer'
,
'maintainer'
),
(
'Maintainer-email'
,
'maintainer_email'
),
)
for
field
,
attr
in
optional_fields
:
attr_val
=
getattr
(
self
,
attr
)
if
attr_val
is
not
None
:
write_field
(
field
,
attr_val
)
write_field
(
'License'
,
self
.
get_license
())
if
self
.
download_url
:
write_field
(
'Download-URL'
,
self
.
download_url
)
for
project_url
in
self
.
project_urls
.
items
():
write_field
(
'Project-URL'
,
'
%
s,
%
s'
%
project_url
)
long_desc
=
rfc822_escape
(
self
.
get_long_description
())
write_field
(
'Description'
,
long_desc
)
keywords
=
','
.
join
(
self
.
get_keywords
())
if
keywords
:
write_field
(
'Keywords'
,
keywords
)
if
version
>=
StrictVersion
(
'1.2'
):
for
platform
in
self
.
get_platforms
():
write_field
(
'Platform'
,
platform
)
else
:
self
.
_write_list
(
file
,
'Platform'
,
self
.
get_platforms
())
self
.
_write_list
(
file
,
'Classifier'
,
self
.
get_classifiers
())
# PEP 314
self
.
_write_list
(
file
,
'Requires'
,
self
.
get_requires
())
self
.
_write_list
(
file
,
'Provides'
,
self
.
get_provides
())
self
.
_write_list
(
file
,
'Obsoletes'
,
self
.
get_obsoletes
())
# Setuptools specific for PEP 345
if
hasattr
(
self
,
'python_requires'
):
write_field
(
'Requires-Python'
,
self
.
python_requires
)
# PEP 566
if
self
.
long_description_content_type
:
write_field
(
'Description-Content-Type'
,
self
.
long_description_content_type
)
if
self
.
provides_extras
:
for
extra
in
self
.
provides_extras
:
write_field
(
'Provides-Extra'
,
extra
)
sequence
=
tuple
,
list
def
check_importable
(
dist
,
attr
,
value
):
try
:
ep
=
pkg_resources
.
EntryPoint
.
parse
(
'x='
+
value
)
assert
not
ep
.
extras
except
(
TypeError
,
ValueError
,
AttributeError
,
AssertionError
):
raise
DistutilsSetupError
(
"
%
r must be importable 'module:attrs' string (got
%
r)"
%
(
attr
,
value
)
)
def
assert_string_list
(
dist
,
attr
,
value
):
"""Verify that value is a string list"""
try
:
# verify that value is a list or tuple to exclude unordered
# or single-use iterables
assert
isinstance
(
value
,
(
list
,
tuple
))
# verify that elements of value are strings
assert
''
.
join
(
value
)
!=
value
except
(
TypeError
,
ValueError
,
AttributeError
,
AssertionError
):
raise
DistutilsSetupError
(
"
%
r must be a list of strings (got
%
r)"
%
(
attr
,
value
)
)
def
check_nsp
(
dist
,
attr
,
value
):
"""Verify that namespace packages are valid"""
ns_packages
=
value
assert_string_list
(
dist
,
attr
,
ns_packages
)
for
nsp
in
ns_packages
:
if
not
dist
.
has_contents_for
(
nsp
):
raise
DistutilsSetupError
(
"Distribution contains no modules or packages for "
+
"namespace package
%
r"
%
nsp
)
parent
,
sep
,
child
=
nsp
.
rpartition
(
'.'
)
if
parent
and
parent
not
in
ns_packages
:
distutils
.
log
.
warn
(
"WARNING:
%
r is declared as a package namespace, but
%
r"
" is not: please correct this in setup.py"
,
nsp
,
parent
)
def
check_extras
(
dist
,
attr
,
value
):
"""Verify that extras_require mapping is valid"""
try
:
list
(
itertools
.
starmap
(
_check_extra
,
value
.
items
()))
except
(
TypeError
,
ValueError
,
AttributeError
):
raise
DistutilsSetupError
(
"'extras_require' must be a dictionary whose values are "
"strings or lists of strings containing valid project/version "
"requirement specifiers."
)
def
_check_extra
(
extra
,
reqs
):
name
,
sep
,
marker
=
extra
.
partition
(
':'
)
if
marker
and
pkg_resources
.
invalid_marker
(
marker
):
raise
DistutilsSetupError
(
"Invalid environment marker: "
+
marker
)
list
(
pkg_resources
.
parse_requirements
(
reqs
))
def
assert_bool
(
dist
,
attr
,
value
):
"""Verify that value is True, False, 0, or 1"""
if
bool
(
value
)
!=
value
:
tmpl
=
"{attr!r} must be a boolean value (got {value!r})"
raise
DistutilsSetupError
(
tmpl
.
format
(
attr
=
attr
,
value
=
value
))
def
check_requirements
(
dist
,
attr
,
value
):
"""Verify that install_requires is a valid requirements list"""
try
:
list
(
pkg_resources
.
parse_requirements
(
value
))
if
isinstance
(
value
,
(
dict
,
set
)):
raise
TypeError
(
"Unordered types are not allowed"
)
except
(
TypeError
,
ValueError
)
as
error
:
tmpl
=
(
"{attr!r} must be a string or list of strings "
"containing valid project/version requirement specifiers; {error}"
)
raise
DistutilsSetupError
(
tmpl
.
format
(
attr
=
attr
,
error
=
error
))
def
check_specifier
(
dist
,
attr
,
value
):
"""Verify that value is a valid version specifier"""
try
:
packaging
.
specifiers
.
SpecifierSet
(
value
)
except
packaging
.
specifiers
.
InvalidSpecifier
as
error
:
tmpl
=
(
"{attr!r} must be a string "
"containing valid version specifiers; {error}"
)
raise
DistutilsSetupError
(
tmpl
.
format
(
attr
=
attr
,
error
=
error
))
def
check_entry_points
(
dist
,
attr
,
value
):
"""Verify that entry_points map is parseable"""
try
:
pkg_resources
.
EntryPoint
.
parse_map
(
value
)
except
ValueError
as
e
:
raise
DistutilsSetupError
(
e
)
def
check_test_suite
(
dist
,
attr
,
value
):
if
not
isinstance
(
value
,
six
.
string_types
):
raise
DistutilsSetupError
(
"test_suite must be a string"
)
def
check_package_data
(
dist
,
attr
,
value
):
"""Verify that value is a dictionary of package names to glob lists"""
if
not
isinstance
(
value
,
dict
):
raise
DistutilsSetupError
(
"{!r} must be a dictionary mapping package names to lists of "
"string wildcard patterns"
.
format
(
attr
))
for
k
,
v
in
value
.
items
():
if
not
isinstance
(
k
,
six
.
string_types
):
raise
DistutilsSetupError
(
"keys of {!r} dict must be strings (got {!r})"
.
format
(
attr
,
k
)
)
assert_string_list
(
dist
,
'values of {!r} dict'
.
format
(
attr
),
v
)
def
check_packages
(
dist
,
attr
,
value
):
for
pkgname
in
value
:
if
not
re
.
match
(
r'\w+(\.\w+)*'
,
pkgname
):
distutils
.
log
.
warn
(
"WARNING:
%
r not a valid package name; please use only "
".-separated package names in setup.py"
,
pkgname
)
_Distribution
=
get_unpatched
(
distutils
.
core
.
Distribution
)
class
Distribution
(
_Distribution
):
"""Distribution with support for features, tests, and package data
This is an enhanced version of 'distutils.dist.Distribution' that
effectively adds the following new optional keyword arguments to 'setup()':
'install_requires' -- a string or sequence of strings specifying project
versions that the distribution requires when installed, in the format
used by 'pkg_resources.require()'. They will be installed
automatically when the package is installed. If you wish to use
packages that are not available in PyPI, or want to give your users an
alternate download location, you can add a 'find_links' option to the
'[easy_install]' section of your project's 'setup.cfg' file, and then
setuptools will scan the listed web pages for links that satisfy the
requirements.
'extras_require' -- a dictionary mapping names of optional "extras" to the
additional requirement(s) that using those extras incurs. For example,
this::
extras_require = dict(reST = ["docutils>=0.3", "reSTedit"])
indicates that the distribution can optionally provide an extra
capability called "reST", but it can only be used if docutils and
reSTedit are installed. If the user installs your package using
EasyInstall and requests one of your extras, the corresponding
additional requirements will be installed if needed.
'features' **deprecated** -- a dictionary mapping option names to
'setuptools.Feature'
objects. Features are a portion of the distribution that can be
included or excluded based on user options, inter-feature dependencies,
and availability on the current system. Excluded features are omitted
from all setup commands, including source and binary distributions, so
you can create multiple distributions from the same source tree.
Feature names should be valid Python identifiers, except that they may
contain the '-' (minus) sign. Features can be included or excluded
via the command line options '--with-X' and '--without-X', where 'X' is
the name of the feature. Whether a feature is included by default, and
whether you are allowed to control this from the command line, is
determined by the Feature object. See the 'Feature' class for more
information.
'test_suite' -- the name of a test suite to run for the 'test' command.
If the user runs 'python setup.py test', the package will be installed,
and the named test suite will be run. The format is the same as
would be used on a 'unittest.py' command line. That is, it is the
dotted name of an object to import and call to generate a test suite.
'package_data' -- a dictionary mapping package names to lists of filenames
or globs to use to find data files contained in the named packages.
If the dictionary has filenames or globs listed under '""' (the empty
string), those names will be searched for in every package, in addition
to any names for the specific package. Data files found using these
names/globs will be installed along with the package, in the same
location as the package. Note that globs are allowed to reference
the contents of non-package subdirectories, as long as you use '/' as
a path separator. (Globs are automatically converted to
platform-specific paths at runtime.)
In addition to these new keywords, this class also has several new methods
for manipulating the distribution's contents. For example, the 'include()'
and 'exclude()' methods can be thought of as in-place add and subtract
commands that add or remove packages, modules, extensions, and so on from
the distribution. They are used by the feature subsystem to configure the
distribution for the included and excluded features.
"""
_DISTUTILS_UNSUPPORTED_METADATA
=
{
'long_description_content_type'
:
None
,
'project_urls'
:
dict
,
'provides_extras'
:
set
,
}
_patched_dist
=
None
def
patch_missing_pkg_info
(
self
,
attrs
):
# Fake up a replacement for the data that would normally come from
# PKG-INFO, but which might not yet be built if this is a fresh
# checkout.
#
if
not
attrs
or
'name'
not
in
attrs
or
'version'
not
in
attrs
:
return
key
=
pkg_resources
.
safe_name
(
str
(
attrs
[
'name'
]))
.
lower
()
dist
=
pkg_resources
.
working_set
.
by_key
.
get
(
key
)
if
dist
is
not
None
and
not
dist
.
has_metadata
(
'PKG-INFO'
):
dist
.
_version
=
pkg_resources
.
safe_version
(
str
(
attrs
[
'version'
]))
self
.
_patched_dist
=
dist
def
__init__
(
self
,
attrs
=
None
):
have_package_data
=
hasattr
(
self
,
"package_data"
)
if
not
have_package_data
:
self
.
package_data
=
{}
attrs
=
attrs
or
{}
if
'features'
in
attrs
or
'require_features'
in
attrs
:
Feature
.
warn_deprecated
()
self
.
require_features
=
[]
self
.
features
=
{}
self
.
dist_files
=
[]
# Filter-out setuptools' specific options.
self
.
src_root
=
attrs
.
pop
(
"src_root"
,
None
)
self
.
patch_missing_pkg_info
(
attrs
)
self
.
dependency_links
=
attrs
.
pop
(
'dependency_links'
,
[])
self
.
setup_requires
=
attrs
.
pop
(
'setup_requires'
,
[])
for
ep
in
pkg_resources
.
iter_entry_points
(
'distutils.setup_keywords'
):
vars
(
self
)
.
setdefault
(
ep
.
name
,
None
)
_Distribution
.
__init__
(
self
,
{
k
:
v
for
k
,
v
in
attrs
.
items
()
if
k
not
in
self
.
_DISTUTILS_UNSUPPORTED_METADATA
})
# Fill-in missing metadata fields not supported by distutils.
# Note some fields may have been set by other tools (e.g. pbr)
# above; they are taken preferrentially to setup() arguments
for
option
,
default
in
self
.
_DISTUTILS_UNSUPPORTED_METADATA
.
items
():
for
source
in
self
.
metadata
.
__dict__
,
attrs
:
if
option
in
source
:
value
=
source
[
option
]
break
else
:
value
=
default
()
if
default
else
None
setattr
(
self
.
metadata
,
option
,
value
)
if
isinstance
(
self
.
metadata
.
version
,
numbers
.
Number
):
# Some people apparently take "version number" too literally :)
self
.
metadata
.
version
=
str
(
self
.
metadata
.
version
)
if
self
.
metadata
.
version
is
not
None
:
try
:
ver
=
packaging
.
version
.
Version
(
self
.
metadata
.
version
)
normalized_version
=
str
(
ver
)
if
self
.
metadata
.
version
!=
normalized_version
:
warnings
.
warn
(
"Normalizing '
%
s' to '
%
s'"
%
(
self
.
metadata
.
version
,
normalized_version
,
)
)
self
.
metadata
.
version
=
normalized_version
except
(
packaging
.
version
.
InvalidVersion
,
TypeError
):
warnings
.
warn
(
"The version specified (
%
r) is an invalid version, this "
"may not work as expected with newer versions of "
"setuptools, pip, and PyPI. Please see PEP 440 for more "
"details."
%
self
.
metadata
.
version
)
self
.
_finalize_requires
()
def
_finalize_requires
(
self
):
"""
Set `metadata.python_requires` and fix environment markers
in `install_requires` and `extras_require`.
"""
if
getattr
(
self
,
'python_requires'
,
None
):
self
.
metadata
.
python_requires
=
self
.
python_requires
if
getattr
(
self
,
'extras_require'
,
None
):
for
extra
in
self
.
extras_require
.
keys
():
# Since this gets called multiple times at points where the
# keys have become 'converted' extras, ensure that we are only
# truly adding extras we haven't seen before here.
extra
=
extra
.
split
(
':'
)[
0
]
if
extra
:
self
.
metadata
.
provides_extras
.
add
(
extra
)
self
.
_convert_extras_requirements
()
self
.
_move_install_requirements_markers
()
def
_convert_extras_requirements
(
self
):
"""
Convert requirements in `extras_require` of the form
`"extra": ["barbazquux; {marker}"]` to
`"extra:{marker}": ["barbazquux"]`.
"""
spec_ext_reqs
=
getattr
(
self
,
'extras_require'
,
None
)
or
{}
self
.
_tmp_extras_require
=
defaultdict
(
list
)
for
section
,
v
in
spec_ext_reqs
.
items
():
# Do not strip empty sections.
self
.
_tmp_extras_require
[
section
]
for
r
in
pkg_resources
.
parse_requirements
(
v
):
suffix
=
self
.
_suffix_for
(
r
)
self
.
_tmp_extras_require
[
section
+
suffix
]
.
append
(
r
)
@staticmethod
def
_suffix_for
(
req
):
"""
For a requirement, return the 'extras_require' suffix for
that requirement.
"""
return
':'
+
str
(
req
.
marker
)
if
req
.
marker
else
''
def
_move_install_requirements_markers
(
self
):
"""
Move requirements in `install_requires` that are using environment
markers `extras_require`.
"""
# divide the install_requires into two sets, simple ones still
# handled by install_requires and more complex ones handled
# by extras_require.
def
is_simple_req
(
req
):
return
not
req
.
marker
spec_inst_reqs
=
getattr
(
self
,
'install_requires'
,
None
)
or
()
inst_reqs
=
list
(
pkg_resources
.
parse_requirements
(
spec_inst_reqs
))
simple_reqs
=
filter
(
is_simple_req
,
inst_reqs
)
complex_reqs
=
filterfalse
(
is_simple_req
,
inst_reqs
)
self
.
install_requires
=
list
(
map
(
str
,
simple_reqs
))
for
r
in
complex_reqs
:
self
.
_tmp_extras_require
[
':'
+
str
(
r
.
marker
)]
.
append
(
r
)
self
.
extras_require
=
dict
(
(
k
,
[
str
(
r
)
for
r
in
map
(
self
.
_clean_req
,
v
)])
for
k
,
v
in
self
.
_tmp_extras_require
.
items
()
)
def
_clean_req
(
self
,
req
):
"""
Given a Requirement, remove environment markers and return it.
"""
req
.
marker
=
None
return
req
def
_parse_config_files
(
self
,
filenames
=
None
):
"""
Adapted from distutils.dist.Distribution.parse_config_files,
this method provides the same functionality in subtly-improved
ways.
"""
from
setuptools.extern.six.moves.configparser
import
ConfigParser
# Ignore install directory options if we have a venv
if
six
.
PY3
and
sys
.
prefix
!=
sys
.
base_prefix
:
ignore_options
=
[
'install-base'
,
'install-platbase'
,
'install-lib'
,
'install-platlib'
,
'install-purelib'
,
'install-headers'
,
'install-scripts'
,
'install-data'
,
'prefix'
,
'exec-prefix'
,
'home'
,
'user'
,
'root'
]
else
:
ignore_options
=
[]
ignore_options
=
frozenset
(
ignore_options
)
if
filenames
is
None
:
filenames
=
self
.
find_config_files
()
if
DEBUG
:
self
.
announce
(
"Distribution.parse_config_files():"
)
parser
=
ConfigParser
()
for
filename
in
filenames
:
with
io
.
open
(
filename
,
encoding
=
'utf-8'
)
as
reader
:
if
DEBUG
:
self
.
announce
(
" reading {filename}"
.
format
(
**
locals
()))
(
parser
.
read_file
if
six
.
PY3
else
parser
.
readfp
)(
reader
)
for
section
in
parser
.
sections
():
options
=
parser
.
options
(
section
)
opt_dict
=
self
.
get_option_dict
(
section
)
for
opt
in
options
:
if
opt
!=
'__name__'
and
opt
not
in
ignore_options
:
val
=
self
.
_try_str
(
parser
.
get
(
section
,
opt
))
opt
=
opt
.
replace
(
'-'
,
'_'
)
opt_dict
[
opt
]
=
(
filename
,
val
)
# Make the ConfigParser forget everything (so we retain
# the original filenames that options come from)
parser
.
__init__
()
# If there was a "global" section in the config file, use it
# to set Distribution options.
if
'global'
in
self
.
command_options
:
for
(
opt
,
(
src
,
val
))
in
self
.
command_options
[
'global'
]
.
items
():
alias
=
self
.
negative_opt
.
get
(
opt
)
try
:
if
alias
:
setattr
(
self
,
alias
,
not
strtobool
(
val
))
elif
opt
in
(
'verbose'
,
'dry_run'
):
# ugh!
setattr
(
self
,
opt
,
strtobool
(
val
))
else
:
setattr
(
self
,
opt
,
val
)
except
ValueError
as
msg
:
raise
DistutilsOptionError
(
msg
)
@staticmethod
def
_try_str
(
val
):
"""
On Python 2, much of distutils relies on string values being of
type 'str' (bytes) and not unicode text. If the value can be safely
encoded to bytes using the default encoding, prefer that.
Why the default encoding? Because that value can be implicitly
decoded back to text if needed.
Ref #1653
"""
if
six
.
PY3
:
return
val
try
:
return
val
.
encode
()
except
UnicodeEncodeError
:
pass
return
val
def
_set_command_options
(
self
,
command_obj
,
option_dict
=
None
):
"""
Set the options for 'command_obj' from 'option_dict'. Basically
this means copying elements of a dictionary ('option_dict') to
attributes of an instance ('command').
'command_obj' must be a Command instance. If 'option_dict' is not
supplied, uses the standard option dictionary for this command
(from 'self.command_options').
(Adopted from distutils.dist.Distribution._set_command_options)
"""
command_name
=
command_obj
.
get_command_name
()
if
option_dict
is
None
:
option_dict
=
self
.
get_option_dict
(
command_name
)
if
DEBUG
:
self
.
announce
(
" setting options for '
%
s' command:"
%
command_name
)
for
(
option
,
(
source
,
value
))
in
option_dict
.
items
():
if
DEBUG
:
self
.
announce
(
"
%
s =
%
s (from
%
s)"
%
(
option
,
value
,
source
))
try
:
bool_opts
=
[
translate_longopt
(
o
)
for
o
in
command_obj
.
boolean_options
]
except
AttributeError
:
bool_opts
=
[]
try
:
neg_opt
=
command_obj
.
negative_opt
except
AttributeError
:
neg_opt
=
{}
try
:
is_string
=
isinstance
(
value
,
six
.
string_types
)
if
option
in
neg_opt
and
is_string
:
setattr
(
command_obj
,
neg_opt
[
option
],
not
strtobool
(
value
))
elif
option
in
bool_opts
and
is_string
:
setattr
(
command_obj
,
option
,
strtobool
(
value
))
elif
hasattr
(
command_obj
,
option
):
setattr
(
command_obj
,
option
,
value
)
else
:
raise
DistutilsOptionError
(
"error in
%
s: command '
%
s' has no such option '
%
s'"
%
(
source
,
command_name
,
option
))
except
ValueError
as
msg
:
raise
DistutilsOptionError
(
msg
)
def
parse_config_files
(
self
,
filenames
=
None
,
ignore_option_errors
=
False
):
"""Parses configuration files from various levels
and loads configuration.
"""
self
.
_parse_config_files
(
filenames
=
filenames
)
parse_configuration
(
self
,
self
.
command_options
,
ignore_option_errors
=
ignore_option_errors
)
self
.
_finalize_requires
()
def
parse_command_line
(
self
):
"""Process features after parsing command line options"""
result
=
_Distribution
.
parse_command_line
(
self
)
if
self
.
features
:
self
.
_finalize_features
()
return
result
def
_feature_attrname
(
self
,
name
):
"""Convert feature name to corresponding option attribute name"""
return
'with_'
+
name
.
replace
(
'-'
,
'_'
)
def
fetch_build_eggs
(
self
,
requires
):
"""Resolve pre-setup requirements"""
resolved_dists
=
pkg_resources
.
working_set
.
resolve
(
pkg_resources
.
parse_requirements
(
requires
),
installer
=
self
.
fetch_build_egg
,
replace_conflicting
=
True
,
)
for
dist
in
resolved_dists
:
pkg_resources
.
working_set
.
add
(
dist
,
replace
=
True
)
return
resolved_dists
def
finalize_options
(
self
):
_Distribution
.
finalize_options
(
self
)
if
self
.
features
:
self
.
_set_global_opts_from_features
()
for
ep
in
pkg_resources
.
iter_entry_points
(
'distutils.setup_keywords'
):
value
=
getattr
(
self
,
ep
.
name
,
None
)
if
value
is
not
None
:
ep
.
require
(
installer
=
self
.
fetch_build_egg
)
ep
.
load
()(
self
,
ep
.
name
,
value
)
if
getattr
(
self
,
'convert_2to3_doctests'
,
None
):
# XXX may convert to set here when we can rely on set being builtin
self
.
convert_2to3_doctests
=
[
os
.
path
.
abspath
(
p
)
for
p
in
self
.
convert_2to3_doctests
]
else
:
self
.
convert_2to3_doctests
=
[]
def
get_egg_cache_dir
(
self
):
egg_cache_dir
=
os
.
path
.
join
(
os
.
curdir
,
'.eggs'
)
if
not
os
.
path
.
exists
(
egg_cache_dir
):
os
.
mkdir
(
egg_cache_dir
)
windows_support
.
hide_file
(
egg_cache_dir
)
readme_txt_filename
=
os
.
path
.
join
(
egg_cache_dir
,
'README.txt'
)
with
open
(
readme_txt_filename
,
'w'
)
as
f
:
f
.
write
(
'This directory contains eggs that were downloaded '
'by setuptools to build, test, and run plug-ins.
\n\n
'
)
f
.
write
(
'This directory caches those eggs to prevent '
'repeated downloads.
\n\n
'
)
f
.
write
(
'However, it is safe to delete this directory.
\n\n
'
)
return
egg_cache_dir
def
fetch_build_egg
(
self
,
req
):
"""Fetch an egg needed for building"""
from
setuptools.command.easy_install
import
easy_install
dist
=
self
.
__class__
({
'script_args'
:
[
'easy_install'
]})
opts
=
dist
.
get_option_dict
(
'easy_install'
)
opts
.
clear
()
opts
.
update
(
(
k
,
v
)
for
k
,
v
in
self
.
get_option_dict
(
'easy_install'
)
.
items
()
if
k
in
(
# don't use any other settings
'find_links'
,
'site_dirs'
,
'index_url'
,
'optimize'
,
'site_dirs'
,
'allow_hosts'
,
))
if
self
.
dependency_links
:
links
=
self
.
dependency_links
[:]
if
'find_links'
in
opts
:
links
=
opts
[
'find_links'
][
1
]
+
links
opts
[
'find_links'
]
=
(
'setup'
,
links
)
install_dir
=
self
.
get_egg_cache_dir
()
cmd
=
easy_install
(
dist
,
args
=
[
"x"
],
install_dir
=
install_dir
,
exclude_scripts
=
True
,
always_copy
=
False
,
build_directory
=
None
,
editable
=
False
,
upgrade
=
False
,
multi_version
=
True
,
no_report
=
True
,
user
=
False
)
cmd
.
ensure_finalized
()
return
cmd
.
easy_install
(
req
)
def
_set_global_opts_from_features
(
self
):
"""Add --with-X/--without-X options based on optional features"""
go
=
[]
no
=
self
.
negative_opt
.
copy
()
for
name
,
feature
in
self
.
features
.
items
():
self
.
_set_feature
(
name
,
None
)
feature
.
validate
(
self
)
if
feature
.
optional
:
descr
=
feature
.
description
incdef
=
' (default)'
excdef
=
''
if
not
feature
.
include_by_default
():
excdef
,
incdef
=
incdef
,
excdef
new
=
(
(
'with-'
+
name
,
None
,
'include '
+
descr
+
incdef
),
(
'without-'
+
name
,
None
,
'exclude '
+
descr
+
excdef
),
)
go
.
extend
(
new
)
no
[
'without-'
+
name
]
=
'with-'
+
name
self
.
global_options
=
self
.
feature_options
=
go
+
self
.
global_options
self
.
negative_opt
=
self
.
feature_negopt
=
no
def
_finalize_features
(
self
):
"""Add/remove features and resolve dependencies between them"""
# First, flag all the enabled items (and thus their dependencies)
for
name
,
feature
in
self
.
features
.
items
():
enabled
=
self
.
feature_is_included
(
name
)
if
enabled
or
(
enabled
is
None
and
feature
.
include_by_default
()):
feature
.
include_in
(
self
)
self
.
_set_feature
(
name
,
1
)
# Then disable the rest, so that off-by-default features don't
# get flagged as errors when they're required by an enabled feature
for
name
,
feature
in
self
.
features
.
items
():
if
not
self
.
feature_is_included
(
name
):
feature
.
exclude_from
(
self
)
self
.
_set_feature
(
name
,
0
)
def
get_command_class
(
self
,
command
):
"""Pluggable version of get_command_class()"""
if
command
in
self
.
cmdclass
:
return
self
.
cmdclass
[
command
]
eps
=
pkg_resources
.
iter_entry_points
(
'distutils.commands'
,
command
)
for
ep
in
eps
:
ep
.
require
(
installer
=
self
.
fetch_build_egg
)
self
.
cmdclass
[
command
]
=
cmdclass
=
ep
.
load
()
return
cmdclass
else
:
return
_Distribution
.
get_command_class
(
self
,
command
)
def
print_commands
(
self
):
for
ep
in
pkg_resources
.
iter_entry_points
(
'distutils.commands'
):
if
ep
.
name
not
in
self
.
cmdclass
:
# don't require extras as the commands won't be invoked
cmdclass
=
ep
.
resolve
()
self
.
cmdclass
[
ep
.
name
]
=
cmdclass
return
_Distribution
.
print_commands
(
self
)
def
get_command_list
(
self
):
for
ep
in
pkg_resources
.
iter_entry_points
(
'distutils.commands'
):
if
ep
.
name
not
in
self
.
cmdclass
:
# don't require extras as the commands won't be invoked
cmdclass
=
ep
.
resolve
()
self
.
cmdclass
[
ep
.
name
]
=
cmdclass
return
_Distribution
.
get_command_list
(
self
)
def
_set_feature
(
self
,
name
,
status
):
"""Set feature's inclusion status"""
setattr
(
self
,
self
.
_feature_attrname
(
name
),
status
)
def
feature_is_included
(
self
,
name
):
"""Return 1 if feature is included, 0 if excluded, 'None' if unknown"""
return
getattr
(
self
,
self
.
_feature_attrname
(
name
))
def
include_feature
(
self
,
name
):
"""Request inclusion of feature named 'name'"""
if
self
.
feature_is_included
(
name
)
==
0
:
descr
=
self
.
features
[
name
]
.
description
raise
DistutilsOptionError
(
descr
+
" is required, but was excluded or is not available"
)
self
.
features
[
name
]
.
include_in
(
self
)
self
.
_set_feature
(
name
,
1
)
def
include
(
self
,
**
attrs
):
"""Add items to distribution that are named in keyword arguments
For example, 'dist.include(py_modules=["x"])' would add 'x' to
the distribution's 'py_modules' attribute, if it was not already
there.
Currently, this method only supports inclusion for attributes that are
lists or tuples. If you need to add support for adding to other
attributes in this or a subclass, you can add an '_include_X' method,
where 'X' is the name of the attribute. The method will be called with
the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})'
will try to call 'dist._include_foo({"bar":"baz"})', which can then
handle whatever special inclusion logic is needed.
"""
for
k
,
v
in
attrs
.
items
():
include
=
getattr
(
self
,
'_include_'
+
k
,
None
)
if
include
:
include
(
v
)
else
:
self
.
_include_misc
(
k
,
v
)
def
exclude_package
(
self
,
package
):
"""Remove packages, modules, and extensions in named package"""
pfx
=
package
+
'.'
if
self
.
packages
:
self
.
packages
=
[
p
for
p
in
self
.
packages
if
p
!=
package
and
not
p
.
startswith
(
pfx
)
]
if
self
.
py_modules
:
self
.
py_modules
=
[
p
for
p
in
self
.
py_modules
if
p
!=
package
and
not
p
.
startswith
(
pfx
)
]
if
self
.
ext_modules
:
self
.
ext_modules
=
[
p
for
p
in
self
.
ext_modules
if
p
.
name
!=
package
and
not
p
.
name
.
startswith
(
pfx
)
]
def
has_contents_for
(
self
,
package
):
"""Return true if 'exclude_package(package)' would do something"""
pfx
=
package
+
'.'
for
p
in
self
.
iter_distribution_names
():
if
p
==
package
or
p
.
startswith
(
pfx
):
return
True
def
_exclude_misc
(
self
,
name
,
value
):
"""Handle 'exclude()' for list/tuple attrs without a special handler"""
if
not
isinstance
(
value
,
sequence
):
raise
DistutilsSetupError
(
"
%
s: setting must be a list or tuple (
%
r)"
%
(
name
,
value
)
)
try
:
old
=
getattr
(
self
,
name
)
except
AttributeError
:
raise
DistutilsSetupError
(
"
%
s: No such distribution setting"
%
name
)
if
old
is
not
None
and
not
isinstance
(
old
,
sequence
):
raise
DistutilsSetupError
(
name
+
": this setting cannot be changed via include/exclude"
)
elif
old
:
setattr
(
self
,
name
,
[
item
for
item
in
old
if
item
not
in
value
])
def
_include_misc
(
self
,
name
,
value
):
"""Handle 'include()' for list/tuple attrs without a special handler"""
if
not
isinstance
(
value
,
sequence
):
raise
DistutilsSetupError
(
"
%
s: setting must be a list (
%
r)"
%
(
name
,
value
)
)
try
:
old
=
getattr
(
self
,
name
)
except
AttributeError
:
raise
DistutilsSetupError
(
"
%
s: No such distribution setting"
%
name
)
if
old
is
None
:
setattr
(
self
,
name
,
value
)
elif
not
isinstance
(
old
,
sequence
):
raise
DistutilsSetupError
(
name
+
": this setting cannot be changed via include/exclude"
)
else
:
new
=
[
item
for
item
in
value
if
item
not
in
old
]
setattr
(
self
,
name
,
old
+
new
)
def
exclude
(
self
,
**
attrs
):
"""Remove items from distribution that are named in keyword arguments
For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from
the distribution's 'py_modules' attribute. Excluding packages uses
the 'exclude_package()' method, so all of the package's contained
packages, modules, and extensions are also excluded.
Currently, this method only supports exclusion from attributes that are
lists or tuples. If you need to add support for excluding from other
attributes in this or a subclass, you can add an '_exclude_X' method,
where 'X' is the name of the attribute. The method will be called with
the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})'
will try to call 'dist._exclude_foo({"bar":"baz"})', which can then
handle whatever special exclusion logic is needed.
"""
for
k
,
v
in
attrs
.
items
():
exclude
=
getattr
(
self
,
'_exclude_'
+
k
,
None
)
if
exclude
:
exclude
(
v
)
else
:
self
.
_exclude_misc
(
k
,
v
)
def
_exclude_packages
(
self
,
packages
):
if
not
isinstance
(
packages
,
sequence
):
raise
DistutilsSetupError
(
"packages: setting must be a list or tuple (
%
r)"
%
(
packages
,)
)
list
(
map
(
self
.
exclude_package
,
packages
))
def
_parse_command_opts
(
self
,
parser
,
args
):
# Remove --with-X/--without-X options when processing command args
self
.
global_options
=
self
.
__class__
.
global_options
self
.
negative_opt
=
self
.
__class__
.
negative_opt
# First, expand any aliases
command
=
args
[
0
]
aliases
=
self
.
get_option_dict
(
'aliases'
)
while
command
in
aliases
:
src
,
alias
=
aliases
[
command
]
del
aliases
[
command
]
# ensure each alias can expand only once!
import
shlex
args
[:
1
]
=
shlex
.
split
(
alias
,
True
)
command
=
args
[
0
]
nargs
=
_Distribution
.
_parse_command_opts
(
self
,
parser
,
args
)
# Handle commands that want to consume all remaining arguments
cmd_class
=
self
.
get_command_class
(
command
)
if
getattr
(
cmd_class
,
'command_consumes_arguments'
,
None
):
self
.
get_option_dict
(
command
)[
'args'
]
=
(
"command line"
,
nargs
)
if
nargs
is
not
None
:
return
[]
return
nargs
def
get_cmdline_options
(
self
):
"""Return a '{cmd: {opt:val}}' map of all command-line options
Option names are all long, but do not include the leading '--', and
contain dashes rather than underscores. If the option doesn't take
an argument (e.g. '--quiet'), the 'val' is 'None'.
Note that options provided by config files are intentionally excluded.
"""
d
=
{}
for
cmd
,
opts
in
self
.
command_options
.
items
():
for
opt
,
(
src
,
val
)
in
opts
.
items
():
if
src
!=
"command line"
:
continue
opt
=
opt
.
replace
(
'_'
,
'-'
)
if
val
==
0
:
cmdobj
=
self
.
get_command_obj
(
cmd
)
neg_opt
=
self
.
negative_opt
.
copy
()
neg_opt
.
update
(
getattr
(
cmdobj
,
'negative_opt'
,
{}))
for
neg
,
pos
in
neg_opt
.
items
():
if
pos
==
opt
:
opt
=
neg
val
=
None
break
else
:
raise
AssertionError
(
"Shouldn't be able to get here"
)
elif
val
==
1
:
val
=
None
d
.
setdefault
(
cmd
,
{})[
opt
]
=
val
return
d
def
iter_distribution_names
(
self
):
"""Yield all packages, modules, and extension names in distribution"""
for
pkg
in
self
.
packages
or
():
yield
pkg
for
module
in
self
.
py_modules
or
():
yield
module
for
ext
in
self
.
ext_modules
or
():
if
isinstance
(
ext
,
tuple
):
name
,
buildinfo
=
ext
else
:
name
=
ext
.
name
if
name
.
endswith
(
'module'
):
name
=
name
[:
-
6
]
yield
name
def
handle_display_options
(
self
,
option_order
):
"""If there were any non-global "display-only" options
(--help-commands or the metadata display options) on the command
line, display the requested info and return true; else return
false.
"""
import
sys
if
six
.
PY2
or
self
.
help_commands
:
return
_Distribution
.
handle_display_options
(
self
,
option_order
)
# Stdout may be StringIO (e.g. in tests)
if
not
isinstance
(
sys
.
stdout
,
io
.
TextIOWrapper
):
return
_Distribution
.
handle_display_options
(
self
,
option_order
)
# Don't wrap stdout if utf-8 is already the encoding. Provides
# workaround for #334.
if
sys
.
stdout
.
encoding
.
lower
()
in
(
'utf-8'
,
'utf8'
):
return
_Distribution
.
handle_display_options
(
self
,
option_order
)
# Print metadata in UTF-8 no matter the platform
encoding
=
sys
.
stdout
.
encoding
errors
=
sys
.
stdout
.
errors
newline
=
sys
.
platform
!=
'win32'
and
'
\n
'
or
None
line_buffering
=
sys
.
stdout
.
line_buffering
sys
.
stdout
=
io
.
TextIOWrapper
(
sys
.
stdout
.
detach
(),
'utf-8'
,
errors
,
newline
,
line_buffering
)
try
:
return
_Distribution
.
handle_display_options
(
self
,
option_order
)
finally
:
sys
.
stdout
=
io
.
TextIOWrapper
(
sys
.
stdout
.
detach
(),
encoding
,
errors
,
newline
,
line_buffering
)
class
Feature
:
"""
**deprecated** -- The `Feature` facility was never completely implemented
or supported, `has reported issues
<https://github.com/pypa/setuptools/issues/58>`_ and will be removed in
a future version.
A subset of the distribution that can be excluded if unneeded/wanted
Features are created using these keyword arguments:
'description' -- a short, human readable description of the feature, to
be used in error messages, and option help messages.
'standard' -- if true, the feature is included by default if it is
available on the current system. Otherwise, the feature is only
included if requested via a command line '--with-X' option, or if
another included feature requires it. The default setting is 'False'.
'available' -- if true, the feature is available for installation on the
current system. The default setting is 'True'.
'optional' -- if true, the feature's inclusion can be controlled from the
command line, using the '--with-X' or '--without-X' options. If
false, the feature's inclusion status is determined automatically,
based on 'availabile', 'standard', and whether any other feature
requires it. The default setting is 'True'.
'require_features' -- a string or sequence of strings naming features
that should also be included if this feature is included. Defaults to
empty list. May also contain 'Require' objects that should be
added/removed from the distribution.
'remove' -- a string or list of strings naming packages to be removed
from the distribution if this feature is *not* included. If the
feature *is* included, this argument is ignored. This argument exists
to support removing features that "crosscut" a distribution, such as
defining a 'tests' feature that removes all the 'tests' subpackages
provided by other features. The default for this argument is an empty
list. (Note: the named package(s) or modules must exist in the base
distribution when the 'setup()' function is initially called.)
other keywords -- any other keyword arguments are saved, and passed to
the distribution's 'include()' and 'exclude()' methods when the
feature is included or excluded, respectively. So, for example, you
could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be
added or removed from the distribution as appropriate.
A feature must include at least one 'requires', 'remove', or other
keyword argument. Otherwise, it can't affect the distribution in any way.
Note also that you can subclass 'Feature' to create your own specialized
feature types that modify the distribution in other ways when included or
excluded. See the docstrings for the various methods here for more detail.
Aside from the methods, the only feature attributes that distributions look
at are 'description' and 'optional'.
"""
@staticmethod
def
warn_deprecated
():
msg
=
(
"Features are deprecated and will be removed in a future "
"version. See https://github.com/pypa/setuptools/issues/65."
)
warnings
.
warn
(
msg
,
DistDeprecationWarning
,
stacklevel
=
3
)
def
__init__
(
self
,
description
,
standard
=
False
,
available
=
True
,
optional
=
True
,
require_features
=
(),
remove
=
(),
**
extras
):
self
.
warn_deprecated
()
self
.
description
=
description
self
.
standard
=
standard
self
.
available
=
available
self
.
optional
=
optional
if
isinstance
(
require_features
,
(
str
,
Require
)):
require_features
=
require_features
,
self
.
require_features
=
[
r
for
r
in
require_features
if
isinstance
(
r
,
str
)
]
er
=
[
r
for
r
in
require_features
if
not
isinstance
(
r
,
str
)]
if
er
:
extras
[
'require_features'
]
=
er
if
isinstance
(
remove
,
str
):
remove
=
remove
,
self
.
remove
=
remove
self
.
extras
=
extras
if
not
remove
and
not
require_features
and
not
extras
:
raise
DistutilsSetupError
(
"Feature
%
s: must define 'require_features', 'remove', or "
"at least one of 'packages', 'py_modules', etc."
)
def
include_by_default
(
self
):
"""Should this feature be included by default?"""
return
self
.
available
and
self
.
standard
def
include_in
(
self
,
dist
):
"""Ensure feature and its requirements are included in distribution
You may override this in a subclass to perform additional operations on
the distribution. Note that this method may be called more than once
per feature, and so should be idempotent.
"""
if
not
self
.
available
:
raise
DistutilsPlatformError
(
self
.
description
+
" is required, "
"but is not available on this platform"
)
dist
.
include
(
**
self
.
extras
)
for
f
in
self
.
require_features
:
dist
.
include_feature
(
f
)
def
exclude_from
(
self
,
dist
):
"""Ensure feature is excluded from distribution
You may override this in a subclass to perform additional operations on
the distribution. This method will be called at most once per
feature, and only after all included features have been asked to
include themselves.
"""
dist
.
exclude
(
**
self
.
extras
)
if
self
.
remove
:
for
item
in
self
.
remove
:
dist
.
exclude_package
(
item
)
def
validate
(
self
,
dist
):
"""Verify that feature makes sense in context of distribution
This method is called by the distribution just before it parses its
command line. It checks to ensure that the 'remove' attribute, if any,
contains only valid package/module names that are present in the base
distribution when 'setup()' is called. You may override it in a
subclass to perform any other required validation of the feature
against a target distribution.
"""
for
item
in
self
.
remove
:
if
not
dist
.
has_contents_for
(
item
):
raise
DistutilsSetupError
(
"
%
s wants to be able to remove
%
s, but the distribution"
" doesn't contain any packages or modules under
%
s"
%
(
self
.
description
,
item
,
item
)
)
class
DistDeprecationWarning
(
SetuptoolsDeprecationWarning
):
"""Class for warning about deprecations in dist in
setuptools. Not ignored by default, unlike DeprecationWarning."""
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment