Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
setuptools
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jérome Perrin
setuptools
Commits
acd94990
Commit
acd94990
authored
Dec 11, 2016
by
Jason R. Coombs
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into issue250-module_from_spec
parents
27b7d4e4
5c940698
Changes
16
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
1349 additions
and
29 deletions
+1349
-29
CHANGES.rst
CHANGES.rst
+22
-0
README.rst
README.rst
+2
-2
conftest.py
conftest.py
+0
-17
docs/_templates/indexsidebar.html
docs/_templates/indexsidebar.html
+1
-1
docs/index.txt
docs/index.txt
+3
-3
docs/setuptools.txt
docs/setuptools.txt
+205
-0
setup.cfg
setup.cfg
+1
-1
setup.py
setup.py
+1
-1
setuptools/command/build_py.py
setuptools/command/build_py.py
+1
-1
setuptools/command/test.py
setuptools/command/test.py
+2
-0
setuptools/config.py
setuptools/config.py
+558
-0
setuptools/dist.py
setuptools/dist.py
+10
-0
setuptools/tests/environment.py
setuptools/tests/environment.py
+1
-1
setuptools/tests/test_config.py
setuptools/tests/test_config.py
+539
-0
setuptools/tests/test_egg_info.py
setuptools/tests/test_egg_info.py
+1
-1
tox.ini
tox.ini
+2
-1
No files found.
CHANGES.rst
View file @
acd94990
v30
.4.0
-------
*
#
879
:
For
declarative
config
:
-
read_configuration
()
now
accepts
ignore_option_errors
argument
.
This
allows
scraping
tools
to
read
metadata
without
a
need
to
download
entire
packages
.
E
.
g
.
we
can
gather
some
stats
right
from
GitHub
repos
just
by
downloading
setup
.
cfg
.
-
packages
find
:
directive
now
supports
fine
tuning
from
a
subsection
.
The
same
arguments
as
for
find
()
are
accepted
.
v30
.3.0
-------
*
#
394
via
#
862
:
Added
support
for
`
declarative
package
config
in
a
setup
.
cfg
file
<
http
://
setuptools
.
readthedocs
.
io
/
en
/
latest
/
setuptools
.
html
#
configuring
-
setup
-
using
-
setup
-
cfg
-
files
>`
_
.
v30
.2.1
-------
*
#
850
:
In
test
command
,
invoke
unittest
.
main
with
indication
not
to
exit
the
process
.
v30
.2.0
-------
...
...
README.rst
View file @
acd94990
...
...
@@ -139,10 +139,10 @@ Package Index`_. Scroll to the very bottom of the page to find the links.
.. _the project's home page in the Python Package Index: https://pypi.python.org/pypi/setuptools
In addition to the PyPI downloads, the development version of ``setuptools``
is available from the `
Bitbucket
repo`_, and in-development versions of the
is available from the `
GitHub
repo`_, and in-development versions of the
`0.6 branch`_ are available as well.
.. _
Bitbucket repo: https://bitbucket.org/pypa/setuptools/get/default
.tar.gz#egg=setuptools-dev
.. _
GitHub repo: https://github.com/pypa/setuptools/archive/master
.tar.gz#egg=setuptools-dev
.. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06
Uninstalling
...
...
conftest.py
View file @
acd94990
import
os
pytest_plugins
=
'setuptools.tests.fixtures'
...
...
@@ -9,17 +6,3 @@ def pytest_addoption(parser):
"--package_name"
,
action
=
"append"
,
default
=
[],
help
=
"list of package_name to pass to test functions"
,
)
def
pytest_configure
():
_issue_852_workaround
()
def
_issue_852_workaround
():
"""
Patch 'setuptools.__file__' with an absolute path
for forward compatibility with Python 3.
Workaround for https://github.com/pypa/setuptools/issues/852
"""
setuptools
=
__import__
(
'setuptools'
)
setuptools
.
__file__
=
os
.
path
.
abspath
(
setuptools
.
__file__
)
docs/_templates/indexsidebar.html
View file @
acd94990
...
...
@@ -5,4 +5,4 @@
<h3>
Questions? Suggestions? Contributions?
</h3>
<p>
Visit the
<a
href=
"https://
bitbucket.org
/pypa/setuptools"
>
Setuptools project page
</a>
</p>
<p>
Visit the
<a
href=
"https://
github.com
/pypa/setuptools"
>
Setuptools project page
</a>
</p>
docs/index.txt
View file @
acd94990
...
...
@@ -16,10 +16,10 @@ Documentation content:
.. toctree::
:maxdepth: 2
history
roadmap
python3
setuptools
easy_install
pkg_resources
python3
development
roadmap
history
docs/setuptools.txt
View file @
acd94990
...
...
@@ -2398,6 +2398,211 @@ The ``upload_docs`` command has the following options:
https://pypi.python.org/pypi (i.e., the main PyPI installation).
-----------------------------------------
Configuring setup() using setup.cfg files
-----------------------------------------
``Setuptools`` allows using configuration files (usually `setup.cfg`)
to define package’s metadata and other options which are normally supplied
to ``setup()`` function.
This approach not only allows automation scenarios, but also reduces
boilerplate code in some cases.
.. note::
Implementation presents limited compatibility with distutils2-like
``setup.cfg`` sections (used by ``pbr`` and ``d2to1`` packages).
Namely: only metadata related keys from ``metadata`` section are supported
(except for ``description-file``); keys from ``files``, ``entry_points``
and ``backwards_compat`` are not supported.
.. code-block:: ini
[metadata]
name = my_package
version = attr: src.VERSION
description = My package description
long_description = file: README.rst
keywords = one, two
license = BSD 3-Clause License
[metadata.classifiers]
Framework :: Django
Programming Language :: Python :: 3.5
[options]
zip_safe = False
include_package_data = True
packages = find:
scripts =
bin/first.py
bin/second.py
[options.package_data]
* = *.txt, *.rst
hello = *.msg
[options.extras_require]
pdf = ReportLab>=1.2; RXP
rest = docutils>=0.3; pack ==1.1, ==1.3
[options.packages.find]
exclude =
src.subpackage1
src.subpackage2
Metadata and options could be set in sections with the same names.
* Keys are the same as keyword arguments one provides to ``setup()`` function.
* Complex values could be placed comma-separated or one per line
in *dangling* sections. The following are the same:
.. code-block:: ini
[metadata]
keywords = one, two
[metadata]
keywords =
one
two
* In some cases complex values could be provided in subsections for clarity.
* Some keys allow ``file:``, ``attr:`` and ``find:`` directives to cover
common usecases.
* Unknown keys are ignored.
Specifying values
=================
Some values are treated as simple strings, some allow more logic.
Type names used below:
* ``str`` - simple string
* ``list-comma`` - dangling list or comma-separated values string
* ``list-semi`` - dangling list or semicolon-separated values string
* ``bool`` - ``True`` is 1, yes, true
* ``dict`` - list-comma where keys from values are separated by =
* ``section`` - values could be read from a dedicated (sub)section
Special directives:
* ``attr:`` - value could be read from module attribute
* ``file:`` - value could be read from a file
.. note::
``file:`` directive is sandboxed and won't reach anything outside
directory with ``setup.py``.
Metadata
--------
.. note::
Aliases given below are supported for compatibility reasons,
but not advised.
================= ================= =====
Key Aliases Accepted value type
================= ================= =====
name str
version attr:, str
url home-page str
download_url download-url str
author str
author_email author-email str
maintainer str
maintainer_email maintainer-email str
classifiers classifier file:, section, list-comma
license file:, str
description summary file:, str
long_description long-description file:, str
keywords list-comma
platforms platform list-comma
provides list-comma
requires list-comma
obsoletes list-comma
================= ================= =====
.. note::
**version** - ``attr:`` supports callables; supports iterables;
unsupported types are casted using ``str()``.
Options
-------
======================= =====
Key Accepted value type
======================= =====
zip_safe bool
setup_requires list-semi
install_requires list-semi
extras_require section
entry_points file:, section
use_2to3 bool
use_2to3_fixers list-comma
use_2to3_exclude_fixers list-comma
convert_2to3_doctests list-comma
scripts list-comma
eager_resources list-comma
dependency_links list-comma
tests_require list-semi
include_package_data bool
packages find:, list-comma
package_dir dict
package_data section
exclude_package_data section
namespace_packages list-comma
======================= =====
.. note::
**packages** - ``find:`` directive can be further configured
in a dedicated subsection `options.packages.find`. This subsection
accepts the same keys as `setuptools.find` function:
`where`, `include`, `exclude`.
Configuration API
=================
Some automation tools may wish to access data from a configuration file.
``Setuptools`` exposes ``read_configuration()`` function allowing
parsing ``metadata`` and ``options`` sections into a dictionary.
.. code-block:: python
from setuptools.config import read_configuration
conf_dict = read_configuration('/home/user/dev/package/setup.cfg')
By default ``read_configuration()`` will read only file provided
in the first argument. To include values from other configuration files
which could be in various places set `find_others` function argument
to ``True``.
If you have only a configuration file but not the whole package you can still
try to get data out of it with the help of `ignore_option_errors` function
argument. When it is set to ``True`` all options with errors possibly produced
by directives, such as ``attr:`` and others will be silently ignored.
As a consequence the resulting dictionary will include no such options.
--------------------------------
Extending and Reusing Setuptools
--------------------------------
...
...
setup.cfg
View file @
acd94990
[bumpversion]
current_version = 30.
2
.0
current_version = 30.
4
.0
commit = True
tag = True
...
...
setup.py
View file @
acd94990
...
...
@@ -85,7 +85,7 @@ def pypi_link(pkg_filename):
setup_params
=
dict
(
name
=
"setuptools"
,
version
=
"30.
2
.0"
,
version
=
"30.
4
.0"
,
description
=
"Easily download, build, install, upgrade, and uninstall "
"Python packages"
,
author
=
"Python Packaging Authority"
,
...
...
setuptools/command/build_py.py
View file @
acd94990
...
...
@@ -219,7 +219,7 @@ class build_py(orig.build_py, Mixin2to3):
@
staticmethod
def
_get_platform_patterns
(
spec
,
package
,
src_dir
):
"""
yield platf
ro
m-specific path patterns (suitable for glob
yield platf
or
m-specific path patterns (suitable for glob
or fn_match) from a glob-based spec (such as
self.package_data or self.exclude_package_data)
matching package in src_dir.
...
...
setuptools/command/test.py
View file @
acd94990
...
...
@@ -225,10 +225,12 @@ class test(Command):
del_modules
.
append
(
name
)
list
(
map
(
sys
.
modules
.
__delitem__
,
del_modules
))
exit_kwarg
=
{}
if
sys
.
version_info
<
(
2
,
7
)
else
{
"exit"
:
False
}
unittest_main
(
None
,
None
,
self
.
_argv
,
testLoader
=
self
.
_resolve_as_ep
(
self
.
test_loader
),
testRunner
=
self
.
_resolve_as_ep
(
self
.
test_runner
),
**
exit_kwarg
)
@
property
...
...
setuptools/config.py
0 → 100644
View file @
acd94990
from
__future__
import
absolute_import
,
unicode_literals
import
io
import
os
import
sys
from
collections
import
defaultdict
from
functools
import
partial
from
distutils.errors
import
DistutilsOptionError
,
DistutilsFileError
from
setuptools.py26compat
import
import_module
from
setuptools.extern.six
import
string_types
def
read_configuration
(
filepath
,
find_others
=
False
,
ignore_option_errors
=
False
):
"""Read given configuration file and returns options from it as a dict.
:param str|unicode filepath: Path to configuration file
to get options from.
:param bool find_others: Whether to search for other configuration files
which could be on in various places.
:param bool ignore_option_errors: Whether to silently ignore
options, values of which could not be resolved (e.g. due to exceptions
in directives such as file:, attr:, etc.).
If False exceptions are propagated as expected.
:rtype: dict
"""
from
setuptools.dist
import
Distribution
,
_Distribution
filepath
=
os
.
path
.
abspath
(
filepath
)
if
not
os
.
path
.
isfile
(
filepath
):
raise
DistutilsFileError
(
'Configuration file %s does not exist.'
%
filepath
)
current_directory
=
os
.
getcwd
()
os
.
chdir
(
os
.
path
.
dirname
(
filepath
))
try
:
dist
=
Distribution
()
filenames
=
dist
.
find_config_files
()
if
find_others
else
[]
if
filepath
not
in
filenames
:
filenames
.
append
(
filepath
)
_Distribution
.
parse_config_files
(
dist
,
filenames
=
filenames
)
handlers
=
parse_configuration
(
dist
,
dist
.
command_options
,
ignore_option_errors
=
ignore_option_errors
)
finally
:
os
.
chdir
(
current_directory
)
return
configuration_to_dict
(
handlers
)
def
configuration_to_dict
(
handlers
):
"""Returns configuration data gathered by given handlers as a dict.
:param list[ConfigHandler] handlers: Handlers list,
usually from parse_configuration()
:rtype: dict
"""
config_dict
=
defaultdict
(
dict
)
for
handler
in
handlers
:
obj_alias
=
handler
.
section_prefix
target_obj
=
handler
.
target_obj
for
option
in
handler
.
set_options
:
getter
=
getattr
(
target_obj
,
'get_%s'
%
option
,
None
)
if
getter
is
None
:
value
=
getattr
(
target_obj
,
option
)
else
:
value
=
getter
()
config_dict
[
obj_alias
][
option
]
=
value
return
config_dict
def
parse_configuration
(
distribution
,
command_options
,
ignore_option_errors
=
False
):
"""Performs additional parsing of configuration options
for a distribution.
Returns a list of used option handlers.
:param Distribution distribution:
:param dict command_options:
:param bool ignore_option_errors: Whether to silently ignore
options, values of which could not be resolved (e.g. due to exceptions
in directives such as file:, attr:, etc.).
If False exceptions are propagated as expected.
:rtype: list
"""
meta
=
ConfigMetadataHandler
(
distribution
.
metadata
,
command_options
,
ignore_option_errors
)
meta
.
parse
()
options
=
ConfigOptionsHandler
(
distribution
,
command_options
,
ignore_option_errors
)
options
.
parse
()
return
[
meta
,
options
]
class
ConfigHandler
(
object
):
"""Handles metadata supplied in configuration files."""
section_prefix
=
None
"""Prefix for config sections handled by this handler.
Must be provided by class heirs.
"""
aliases
=
{}
"""Options aliases.
For compatibility with various packages. E.g.: d2to1 and pbr.
Note: `-` in keys is replaced with `_` by config parser.
"""
def
__init__
(
self
,
target_obj
,
options
,
ignore_option_errors
=
False
):
sections
=
{}
section_prefix
=
self
.
section_prefix
for
section_name
,
section_options
in
options
.
items
():
if
not
section_name
.
startswith
(
section_prefix
):
continue
section_name
=
section_name
.
replace
(
section_prefix
,
''
).
strip
(
'.'
)
sections
[
section_name
]
=
section_options
self
.
ignore_option_errors
=
ignore_option_errors
self
.
target_obj
=
target_obj
self
.
sections
=
sections
self
.
set_options
=
[]
@
property
def
parsers
(
self
):
"""Metadata item name to parser function mapping."""
raise
NotImplementedError
(
'%s must provide .parsers property'
%
self
.
__class__
.
__name__
)
def
__setitem__
(
self
,
option_name
,
value
):
unknown
=
tuple
()
target_obj
=
self
.
target_obj
# Translate alias into real name.
option_name
=
self
.
aliases
.
get
(
option_name
,
option_name
)
current_value
=
getattr
(
target_obj
,
option_name
,
unknown
)
if
current_value
is
unknown
:
raise
KeyError
(
option_name
)
if
current_value
:
# Already inhabited. Skipping.
return
skip_option
=
False
parser
=
self
.
parsers
.
get
(
option_name
)
if
parser
:
try
:
value
=
parser
(
value
)
except
Exception
:
skip_option
=
True
if
not
self
.
ignore_option_errors
:
raise
if
skip_option
:
return
setter
=
getattr
(
target_obj
,
'set_%s'
%
option_name
,
None
)
if
setter
is
None
:
setattr
(
target_obj
,
option_name
,
value
)
else
:
setter
(
value
)
self
.
set_options
.
append
(
option_name
)
@
classmethod
def
_parse_list
(
cls
,
value
,
separator
=
','
):
"""Represents value as a list.
Value is split either by separator (defaults to comma) or by lines.
:param value:
:param separator: List items separator character.
:rtype: list
"""
if
isinstance
(
value
,
list
):
# _get_parser_compound case
return
value
if
'
\
n
'
in
value
:
value
=
value
.
splitlines
()
else
:
value
=
value
.
split
(
separator
)
return
[
chunk
.
strip
()
for
chunk
in
value
if
chunk
.
strip
()]
@
classmethod
def
_parse_dict
(
cls
,
value
):
"""Represents value as a dict.
:param value:
:rtype: dict
"""
separator
=
'='
result
=
{}
for
line
in
cls
.
_parse_list
(
value
):
key
,
sep
,
val
=
line
.
partition
(
separator
)
if
sep
!=
separator
:
raise
DistutilsOptionError
(
'Unable to parse option value to dict: %s'
%
value
)
result
[
key
.
strip
()]
=
val
.
strip
()
return
result
@
classmethod
def
_parse_bool
(
cls
,
value
):
"""Represents value as boolean.
:param value:
:rtype: bool
"""
value
=
value
.
lower
()
return
value
in
(
'1'
,
'true'
,
'yes'
)
@
classmethod
def
_parse_file
(
cls
,
value
):
"""Represents value as a string, allowing including text
from nearest files using `file:` directive.
Directive is sandboxed and won't reach anything outside
directory with setup.py.
Examples:
include: LICENSE
include: src/file.txt
:param str value:
:rtype: str
"""
if
not
isinstance
(
value
,
string_types
):
return
value
include_directive
=
'file:'
if
not
value
.
startswith
(
include_directive
):
return
value
current_directory
=
os
.
getcwd
()
filepath
=
value
.
replace
(
include_directive
,
''
).
strip
()
filepath
=
os
.
path
.
abspath
(
filepath
)
if
not
filepath
.
startswith
(
current_directory
):
raise
DistutilsOptionError
(
'`file:` directive can not access %s'
%
filepath
)
if
os
.
path
.
isfile
(
filepath
):
with
io
.
open
(
filepath
,
encoding
=
'utf-8'
)
as
f
:
value
=
f
.
read
()
return
value
@
classmethod
def
_parse_attr
(
cls
,
value
):
"""Represents value as a module attribute.
Examples:
attr: package.attr
attr: package.module.attr
:param str value:
:rtype: str
"""
attr_directive
=
'attr:'
if
not
value
.
startswith
(
attr_directive
):
return
value
attrs_path
=
value
.
replace
(
attr_directive
,
''
).
strip
().
split
(
'.'
)
attr_name
=
attrs_path
.
pop
()
module_name
=
'.'
.
join
(
attrs_path
)
module_name
=
module_name
or
'__init__'
sys
.
path
.
insert
(
0
,
os
.
getcwd
())
try
:
module
=
import_module
(
module_name
)
value
=
getattr
(
module
,
attr_name
)
finally
:
sys
.
path
=
sys
.
path
[
1
:]
return
value
@
classmethod
def
_get_parser_compound
(
cls
,
*
parse_methods
):
"""Returns parser function to represents value as a list.
Parses a value applying given methods one after another.
:param parse_methods:
:rtype: callable
"""
def
parse
(
value
):
parsed
=
value
for
method
in
parse_methods
:
parsed
=
method
(
parsed
)
return
parsed
return
parse
@
classmethod
def
_parse_section_to_dict
(
cls
,
section_options
,
values_parser
=
None
):
"""Parses section options into a dictionary.
Optionally applies a given parser to values.
:param dict section_options:
:param callable values_parser:
:rtype: dict
"""
value
=
{}
values_parser
=
values_parser
or
(
lambda
val
:
val
)
for
key
,
(
_
,
val
)
in
section_options
.
items
():
value
[
key
]
=
values_parser
(
val
)
return
value
def
parse_section
(
self
,
section_options
):
"""Parses configuration file section.
:param dict section_options:
"""
for
(
name
,
(
_
,
value
))
in
section_options
.
items
():
try
:
self
[
name
]
=
value
except
KeyError
:
pass
# Keep silent for a new option may appear anytime.
def
parse
(
self
):
"""Parses configuration file items from one
or more related sections.
"""
for
section_name
,
section_options
in
self
.
sections
.
items
():
method_postfix
=
''
if
section_name
:
# [section.option] variant
method_postfix
=
'_%s'
%
section_name
section_parser_method
=
getattr
(
self
,
# Dots in section names are tranlsated into dunderscores.
(
'parse_section%s'
%
method_postfix
).
replace
(
'.'
,
'__'
),
None
)
if
section_parser_method
is
None
:
raise
DistutilsOptionError
(
'Unsupported distribution option section: [%s.%s]'
%
(
self
.
section_prefix
,
section_name
))
section_parser_method
(
section_options
)
class
ConfigMetadataHandler
(
ConfigHandler
):
section_prefix
=
'metadata'
aliases
=
{
'home_page'
:
'url'
,
'summary'
:
'description'
,
'classifier'
:
'classifiers'
,
'platform'
:
'platforms'
,
}
strict_mode
=
False
"""We need to keep it loose, to be partially compatible with
`pbr` and `d2to1` packages which also uses `metadata` section.
"""
@
property
def
parsers
(
self
):
"""Metadata item name to parser function mapping."""
parse_list
=
self
.
_parse_list
parse_file
=
self
.
_parse_file
return
{
'platforms'
:
parse_list
,
'keywords'
:
parse_list
,
'provides'
:
parse_list
,
'requires'
:
parse_list
,
'obsoletes'
:
parse_list
,
'classifiers'
:
self
.
_get_parser_compound
(
parse_file
,
parse_list
),
'license'
:
parse_file
,
'description'
:
parse_file
,
'long_description'
:
parse_file
,
'version'
:
self
.
_parse_version
,
}
def
parse_section_classifiers
(
self
,
section_options
):
"""Parses configuration file section.
:param dict section_options:
"""
classifiers
=
[]
for
begin
,
(
_
,
rest
)
in
section_options
.
items
():
classifiers
.
append
(
'%s :%s'
%
(
begin
.
title
(),
rest
))
self
[
'classifiers'
]
=
classifiers
def
_parse_version
(
self
,
value
):
"""Parses `version` option value.
:param value:
:rtype: str
"""
version
=
self
.
_parse_attr
(
value
)
if
callable
(
version
):
version
=
version
()
if
not
isinstance
(
version
,
string_types
):
if
hasattr
(
version
,
'__iter__'
):
version
=
'.'
.
join
(
map
(
str
,
version
))
else
:
version
=
'%s'
%
version
return
version
class
ConfigOptionsHandler
(
ConfigHandler
):
section_prefix
=
'options'
@
property
def
parsers
(
self
):
"""Metadata item name to parser function mapping."""
parse_list
=
self
.
_parse_list
parse_list_semicolon
=
partial
(
self
.
_parse_list
,
separator
=
';'
)
parse_bool
=
self
.
_parse_bool
parse_dict
=
self
.
_parse_dict
return
{
'zip_safe'
:
parse_bool
,
'use_2to3'
:
parse_bool
,
'include_package_data'
:
parse_bool
,
'package_dir'
:
parse_dict
,
'use_2to3_fixers'
:
parse_list
,
'use_2to3_exclude_fixers'
:
parse_list
,
'convert_2to3_doctests'
:
parse_list
,
'scripts'
:
parse_list
,
'eager_resources'
:
parse_list
,
'dependency_links'
:
parse_list
,
'namespace_packages'
:
parse_list
,
'install_requires'
:
parse_list_semicolon
,
'setup_requires'
:
parse_list_semicolon
,
'tests_require'
:
parse_list_semicolon
,
'packages'
:
self
.
_parse_packages
,
'entry_points'
:
self
.
_parse_file
,
}
def
_parse_packages
(
self
,
value
):
"""Parses `packages` option value.
:param value:
:rtype: list
"""
find_directive
=
'find:'
if
not
value
.
startswith
(
find_directive
):
return
self
.
_parse_list
(
value
)
# Read function arguments from a dedicated section.
find_kwargs
=
self
.
parse_section_packages__find
(
self
.
sections
.
get
(
'packages.find'
,
{}))
from
setuptools
import
find_packages
return
find_packages
(
**
find_kwargs
)
def
parse_section_packages__find
(
self
,
section_options
):
"""Parses `packages.find` configuration file section.
To be used in conjunction with _parse_packages().
:param dict section_options:
"""
section_data
=
self
.
_parse_section_to_dict
(
section_options
,
self
.
_parse_list
)
valid_keys
=
[
'where'
,
'include'
,
'exclude'
]
find_kwargs
=
dict
(
[(
k
,
v
)
for
k
,
v
in
section_data
.
items
()
if
k
in
valid_keys
and
v
])
where
=
find_kwargs
.
get
(
'where'
)
if
where
is
not
None
:
find_kwargs
[
'where'
]
=
where
[
0
]
# cast list to single val
return
find_kwargs
def
parse_section_entry_points
(
self
,
section_options
):
"""Parses `entry_points` configuration file section.
:param dict section_options:
"""
parsed
=
self
.
_parse_section_to_dict
(
section_options
,
self
.
_parse_list
)
self
[
'entry_points'
]
=
parsed
def
_parse_package_data
(
self
,
section_options
):
parsed
=
self
.
_parse_section_to_dict
(
section_options
,
self
.
_parse_list
)
root
=
parsed
.
get
(
'*'
)
if
root
:
parsed
[
''
]
=
root
del
parsed
[
'*'
]
return
parsed
def
parse_section_package_data
(
self
,
section_options
):
"""Parses `package_data` configuration file section.
:param dict section_options:
"""
self
[
'package_data'
]
=
self
.
_parse_package_data
(
section_options
)
def
parse_section_exclude_package_data
(
self
,
section_options
):
"""Parses `exclude_package_data` configuration file section.
:param dict section_options:
"""
self
[
'exclude_package_data'
]
=
self
.
_parse_package_data
(
section_options
)
def
parse_section_extras_require
(
self
,
section_options
):
"""Parses `extras_require` configuration file section.
:param dict section_options:
"""
parse_list
=
partial
(
self
.
_parse_list
,
separator
=
';'
)
self
[
'extras_require'
]
=
self
.
_parse_section_to_dict
(
section_options
,
parse_list
)
setuptools/dist.py
View file @
acd94990
...
...
@@ -19,6 +19,7 @@ from pkg_resources.extern import packaging
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
...
...
@@ -342,6 +343,15 @@ class Distribution(_Distribution):
if getattr(self, '
python_requires
', None):
self.metadata.python_requires = self.python_requires
def parse_config_files(self, filenames=None):
"""Parses configuration files from various levels
and loads configuration.
"""
_Distribution.parse_config_files(self, filenames=filenames)
parse_configuration(self, self.command_options)
def parse_command_line(self):
"""Process features after parsing command line options"""
result = _Distribution.parse_command_line(self)
...
...
setuptools/tests/environment.py
View file @
acd94990
...
...
@@ -56,5 +56,5 @@ def run_setup_py(cmd, pypath=None, path=None,
data
=
data
.
decode
()
data
=
unicodedata
.
normalize
(
'NFC'
,
data
)
# commun
ci
ate calls wait()
# commun
ic
ate calls wait()
return
proc
.
returncode
,
data
setuptools/tests/test_config.py
0 → 100644
View file @
acd94990
import
contextlib
import
pytest
from
distutils.errors
import
DistutilsOptionError
,
DistutilsFileError
from
setuptools.dist
import
Distribution
from
setuptools.config
import
ConfigHandler
,
read_configuration
class
ErrConfigHandler
(
ConfigHandler
):
"""Erroneous handler. Fails to implement required methods."""
def
make_package_dir
(
name
,
base_dir
):
dir_package
=
base_dir
.
mkdir
(
name
)
init_file
=
dir_package
.
join
(
'__init__.py'
)
init_file
.
write
(
''
)
return
dir_package
,
init_file
def
fake_env
(
tmpdir
,
setup_cfg
,
setup_py
=
None
):
if
setup_py
is
None
:
setup_py
=
(
'from setuptools import setup
\
n
'
'setup()
\
n
'
)
tmpdir
.
join
(
'setup.py'
).
write
(
setup_py
)
config
=
tmpdir
.
join
(
'setup.cfg'
)
config
.
write
(
setup_cfg
)
package_dir
,
init_file
=
make_package_dir
(
'fake_package'
,
tmpdir
)
init_file
.
write
(
'VERSION = (1, 2, 3)
\
n
'
'
\
n
'
'VERSION_MAJOR = 1'
'
\
n
'
'def get_version():
\
n
'
' return [3, 4, 5, "dev"]
\
n
'
'
\
n
'
)
return
package_dir
,
config
@
contextlib
.
contextmanager
def
get_dist
(
tmpdir
,
kwargs_initial
=
None
,
parse
=
True
):
kwargs_initial
=
kwargs_initial
or
{}
with
tmpdir
.
as_cwd
():
dist
=
Distribution
(
kwargs_initial
)
dist
.
script_name
=
'setup.py'
parse
and
dist
.
parse_config_files
()
yield
dist
def
test_parsers_implemented
():
with
pytest
.
raises
(
NotImplementedError
):
handler
=
ErrConfigHandler
(
None
,
{})
handler
.
parsers
class
TestConfigurationReader
:
def
test_basic
(
self
,
tmpdir
):
_
,
config
=
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'version = 10.1.1
\
n
'
'keywords = one, two
\
n
'
'
\
n
'
'[options]
\
n
'
'scripts = bin/a.py, bin/b.py
\
n
'
)
config_dict
=
read_configuration
(
'%s'
%
config
)
assert
config_dict
[
'metadata'
][
'version'
]
==
'10.1.1'
assert
config_dict
[
'metadata'
][
'keywords'
]
==
[
'one'
,
'two'
]
assert
config_dict
[
'options'
][
'scripts'
]
==
[
'bin/a.py'
,
'bin/b.py'
]
def
test_no_config
(
self
,
tmpdir
):
with
pytest
.
raises
(
DistutilsFileError
):
read_configuration
(
'%s'
%
tmpdir
.
join
(
'setup.cfg'
))
def
test_ignore_errors
(
self
,
tmpdir
):
_
,
config
=
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'version = attr: none.VERSION
\
n
'
'keywords = one, two
\
n
'
)
with
pytest
.
raises
(
ImportError
):
read_configuration
(
'%s'
%
config
)
config_dict
=
read_configuration
(
'%s'
%
config
,
ignore_option_errors
=
True
)
assert
config_dict
[
'metadata'
][
'keywords'
]
==
[
'one'
,
'two'
]
assert
'version'
not
in
config_dict
[
'metadata'
]
config
.
remove
()
class
TestMetadata
:
def
test_basic
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'version = 10.1.1
\
n
'
'description = Some description
\
n
'
'long_description = file: README
\
n
'
'name = fake_name
\
n
'
'keywords = one, two
\
n
'
'provides = package, package.sub
\
n
'
'license = otherlic
\
n
'
'download_url = http://test.test.com/test/
\
n
'
'maintainer_email = test@test.com
\
n
'
)
tmpdir
.
join
(
'README'
).
write
(
'readme contents
\
n
line2'
)
meta_initial
=
{
# This will be used so `otherlic` won't replace it.
'license'
:
'BSD 3-Clause License'
,
}
with
get_dist
(
tmpdir
,
meta_initial
)
as
dist
:
metadata
=
dist
.
metadata
assert
metadata
.
version
==
'10.1.1'
assert
metadata
.
description
==
'Some description'
assert
metadata
.
long_description
==
'readme contents
\
n
line2'
assert
metadata
.
provides
==
[
'package'
,
'package.sub'
]
assert
metadata
.
license
==
'BSD 3-Clause License'
assert
metadata
.
name
==
'fake_name'
assert
metadata
.
keywords
==
[
'one'
,
'two'
]
assert
metadata
.
download_url
==
'http://test.test.com/test/'
assert
metadata
.
maintainer_email
==
'test@test.com'
def
test_file_sandboxed
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'long_description = file: ../../README
\
n
'
)
with
get_dist
(
tmpdir
,
parse
=
False
)
as
dist
:
with
pytest
.
raises
(
DistutilsOptionError
):
dist
.
parse_config_files
()
# file: out of sandbox
def
test_aliases
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'author-email = test@test.com
\
n
'
'home-page = http://test.test.com/test/
\
n
'
'summary = Short summary
\
n
'
'platform = a, b
\
n
'
'classifier =
\
n
'
' Framework :: Django
\
n
'
' Programming Language :: Python :: 3.5
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
metadata
=
dist
.
metadata
assert
metadata
.
author_email
==
'test@test.com'
assert
metadata
.
url
==
'http://test.test.com/test/'
assert
metadata
.
description
==
'Short summary'
assert
metadata
.
platforms
==
[
'a'
,
'b'
]
assert
metadata
.
classifiers
==
[
'Framework :: Django'
,
'Programming Language :: Python :: 3.5'
,
]
def
test_multiline
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'name = fake_name
\
n
'
'keywords =
\
n
'
' one
\
n
'
' two
\
n
'
'classifiers =
\
n
'
' Framework :: Django
\
n
'
' Programming Language :: Python :: 3.5
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
metadata
=
dist
.
metadata
assert
metadata
.
keywords
==
[
'one'
,
'two'
]
assert
metadata
.
classifiers
==
[
'Framework :: Django'
,
'Programming Language :: Python :: 3.5'
,
]
def
test_version
(
self
,
tmpdir
):
_
,
config
=
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'version = attr: fake_package.VERSION
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
metadata
.
version
==
'1.2.3'
config
.
write
(
'[metadata]
\
n
'
'version = attr: fake_package.get_version
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
metadata
.
version
==
'3.4.5.dev'
config
.
write
(
'[metadata]
\
n
'
'version = attr: fake_package.VERSION_MAJOR
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
metadata
.
version
==
'1'
subpack
=
tmpdir
.
join
(
'fake_package'
).
mkdir
(
'subpackage'
)
subpack
.
join
(
'__init__.py'
).
write
(
''
)
subpack
.
join
(
'submodule.py'
).
write
(
'VERSION = (2016, 11, 26)'
)
config
.
write
(
'[metadata]
\
n
'
'version = attr: fake_package.subpackage.submodule.VERSION
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
metadata
.
version
==
'2016.11.26'
def
test_unknown_meta_item
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'name = fake_name
\
n
'
'unknown = some
\
n
'
)
with
get_dist
(
tmpdir
,
parse
=
False
)
as
dist
:
dist
.
parse_config_files
()
# Skip unknown.
def
test_usupported_section
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[metadata.some]
\
n
'
'key = val
\
n
'
)
with
get_dist
(
tmpdir
,
parse
=
False
)
as
dist
:
with
pytest
.
raises
(
DistutilsOptionError
):
dist
.
parse_config_files
()
def
test_classifiers
(
self
,
tmpdir
):
expected
=
set
([
'Framework :: Django'
,
'Programming Language :: Python :: 3.5'
,
])
# From file.
_
,
config
=
fake_env
(
tmpdir
,
'[metadata]
\
n
'
'classifiers = file: classifiers
\
n
'
)
tmpdir
.
join
(
'classifiers'
).
write
(
'Framework :: Django
\
n
'
'Programming Language :: Python :: 3.5
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
set
(
dist
.
metadata
.
classifiers
)
==
expected
# From section.
config
.
write
(
'[metadata.classifiers]
\
n
'
'Framework :: Django
\
n
'
'Programming Language :: Python :: 3.5
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
set
(
dist
.
metadata
.
classifiers
)
==
expected
class
TestOptions
:
def
test_basic
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[options]
\
n
'
'zip_safe = True
\
n
'
'use_2to3 = 1
\
n
'
'include_package_data = yes
\
n
'
'package_dir = b=c, =src
\
n
'
'packages = pack_a, pack_b.subpack
\
n
'
'namespace_packages = pack1, pack2
\
n
'
'use_2to3_fixers = your.fixers, or.here
\
n
'
'use_2to3_exclude_fixers = one.here, two.there
\
n
'
'convert_2to3_doctests = src/tests/one.txt, src/two.txt
\
n
'
'scripts = bin/one.py, bin/two.py
\
n
'
'eager_resources = bin/one.py, bin/two.py
\
n
'
'install_requires = docutils>=0.3; pack ==1.1, ==1.3; hey
\
n
'
'tests_require = mock==0.7.2; pytest
\
n
'
'setup_requires = docutils>=0.3; spack ==1.1, ==1.3; there
\
n
'
'dependency_links = http://some.com/here/1, '
'http://some.com/there/2
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
zip_safe
assert
dist
.
use_2to3
assert
dist
.
include_package_data
assert
dist
.
package_dir
==
{
''
:
'src'
,
'b'
:
'c'
}
assert
dist
.
packages
==
[
'pack_a'
,
'pack_b.subpack'
]
assert
dist
.
namespace_packages
==
[
'pack1'
,
'pack2'
]
assert
dist
.
use_2to3_fixers
==
[
'your.fixers'
,
'or.here'
]
assert
dist
.
use_2to3_exclude_fixers
==
[
'one.here'
,
'two.there'
]
assert
dist
.
convert_2to3_doctests
==
([
'src/tests/one.txt'
,
'src/two.txt'
])
assert
dist
.
scripts
==
[
'bin/one.py'
,
'bin/two.py'
]
assert
dist
.
dependency_links
==
([
'http://some.com/here/1'
,
'http://some.com/there/2'
])
assert
dist
.
install_requires
==
([
'docutils>=0.3'
,
'pack ==1.1, ==1.3'
,
'hey'
])
assert
dist
.
setup_requires
==
([
'docutils>=0.3'
,
'spack ==1.1, ==1.3'
,
'there'
])
assert
dist
.
tests_require
==
[
'mock==0.7.2'
,
'pytest'
]
def
test_multiline
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[options]
\
n
'
'package_dir =
\
n
'
' b=c
\
n
'
' =src
\
n
'
'packages =
\
n
'
' pack_a
\
n
'
' pack_b.subpack
\
n
'
'namespace_packages =
\
n
'
' pack1
\
n
'
' pack2
\
n
'
'use_2to3_fixers =
\
n
'
' your.fixers
\
n
'
' or.here
\
n
'
'use_2to3_exclude_fixers =
\
n
'
' one.here
\
n
'
' two.there
\
n
'
'convert_2to3_doctests =
\
n
'
' src/tests/one.txt
\
n
'
' src/two.txt
\
n
'
'scripts =
\
n
'
' bin/one.py
\
n
'
' bin/two.py
\
n
'
'eager_resources =
\
n
'
' bin/one.py
\
n
'
' bin/two.py
\
n
'
'install_requires =
\
n
'
' docutils>=0.3
\
n
'
' pack ==1.1, ==1.3
\
n
'
' hey
\
n
'
'tests_require =
\
n
'
' mock==0.7.2
\
n
'
' pytest
\
n
'
'setup_requires =
\
n
'
' docutils>=0.3
\
n
'
' spack ==1.1, ==1.3
\
n
'
' there
\
n
'
'dependency_links =
\
n
'
' http://some.com/here/1
\
n
'
' http://some.com/there/2
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
package_dir
==
{
''
:
'src'
,
'b'
:
'c'
}
assert
dist
.
packages
==
[
'pack_a'
,
'pack_b.subpack'
]
assert
dist
.
namespace_packages
==
[
'pack1'
,
'pack2'
]
assert
dist
.
use_2to3_fixers
==
[
'your.fixers'
,
'or.here'
]
assert
dist
.
use_2to3_exclude_fixers
==
[
'one.here'
,
'two.there'
]
assert
dist
.
convert_2to3_doctests
==
(
[
'src/tests/one.txt'
,
'src/two.txt'
])
assert
dist
.
scripts
==
[
'bin/one.py'
,
'bin/two.py'
]
assert
dist
.
dependency_links
==
([
'http://some.com/here/1'
,
'http://some.com/there/2'
])
assert
dist
.
install_requires
==
([
'docutils>=0.3'
,
'pack ==1.1, ==1.3'
,
'hey'
])
assert
dist
.
setup_requires
==
([
'docutils>=0.3'
,
'spack ==1.1, ==1.3'
,
'there'
])
assert
dist
.
tests_require
==
[
'mock==0.7.2'
,
'pytest'
]
def
test_package_dir_fail
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[options]
\
n
'
'package_dir = a b
\
n
'
)
with
get_dist
(
tmpdir
,
parse
=
False
)
as
dist
:
with
pytest
.
raises
(
DistutilsOptionError
):
dist
.
parse_config_files
()
def
test_package_data
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[options.package_data]
\
n
'
'* = *.txt, *.rst
\
n
'
'hello = *.msg
\
n
'
'
\
n
'
'[options.exclude_package_data]
\
n
'
'* = fake1.txt, fake2.txt
\
n
'
'hello = *.dat
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
package_data
==
{
''
:
[
'*.txt'
,
'*.rst'
],
'hello'
:
[
'*.msg'
],
}
assert
dist
.
exclude_package_data
==
{
''
:
[
'fake1.txt'
,
'fake2.txt'
],
'hello'
:
[
'*.dat'
],
}
def
test_packages
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[options]
\
n
'
'packages = find:
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
packages
==
[
'fake_package'
]
def
test_find_directive
(
self
,
tmpdir
):
dir_package
,
config
=
fake_env
(
tmpdir
,
'[options]
\
n
'
'packages = find:
\
n
'
)
dir_sub_one
,
_
=
make_package_dir
(
'sub_one'
,
dir_package
)
dir_sub_two
,
_
=
make_package_dir
(
'sub_two'
,
dir_package
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
set
(
dist
.
packages
)
==
set
([
'fake_package'
,
'fake_package.sub_two'
,
'fake_package.sub_one'
])
config
.
write
(
'[options]
\
n
'
'packages = find:
\
n
'
'
\
n
'
'[options.packages.find]
\
n
'
'where = .
\
n
'
'include =
\
n
'
' fake_package.sub_one
\
n
'
' two
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
packages
==
[
'fake_package.sub_one'
]
config
.
write
(
'[options]
\
n
'
'packages = find:
\
n
'
'
\
n
'
'[options.packages.find]
\
n
'
'exclude =
\
n
'
' fake_package.sub_one
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
set
(
dist
.
packages
)
==
set
(
[
'fake_package'
,
'fake_package.sub_two'
])
def
test_extras_require
(
self
,
tmpdir
):
fake_env
(
tmpdir
,
'[options.extras_require]
\
n
'
'pdf = ReportLab>=1.2; RXP
\
n
'
'rest =
\
n
'
' docutils>=0.3
\
n
'
' pack ==1.1, ==1.3
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
extras_require
==
{
'pdf'
:
[
'ReportLab>=1.2'
,
'RXP'
],
'rest'
:
[
'docutils>=0.3'
,
'pack ==1.1, ==1.3'
]
}
def
test_entry_points
(
self
,
tmpdir
):
_
,
config
=
fake_env
(
tmpdir
,
'[options.entry_points]
\
n
'
'group1 = point1 = pack.module:func, '
'.point2 = pack.module2:func_rest [rest]
\
n
'
'group2 = point3 = pack.module:func2
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
entry_points
==
{
'group1'
:
[
'point1 = pack.module:func'
,
'.point2 = pack.module2:func_rest [rest]'
,
],
'group2'
:
[
'point3 = pack.module:func2'
]
}
expected
=
(
'[blogtool.parsers]
\
n
'
'.rst = some.nested.module:SomeClass.some_classmethod[reST]
\
n
'
)
tmpdir
.
join
(
'entry_points'
).
write
(
expected
)
# From file.
config
.
write
(
'[options]
\
n
'
'entry_points = file: entry_points
\
n
'
)
with
get_dist
(
tmpdir
)
as
dist
:
assert
dist
.
entry_points
==
expected
setuptools/tests/test_egg_info.py
View file @
acd94990
...
...
@@ -237,7 +237,7 @@ class TestEggInfo(object):
pkginfo
=
os
.
path
.
join
(
egg_info_dir
,
'PKG-INFO'
)
assert
'Requires-Python: >=1.2.3'
in
open
(
pkginfo
).
read
().
split
(
'
\
n
'
)
def
test_manifest_maker_warning_suppresion
(
self
):
def
test_manifest_maker_warning_suppres
s
ion
(
self
):
fixtures
=
[
"standard file not found: should have one of foo.py, bar.py"
,
"standard file 'setup.py' not found"
...
...
tox.ini
View file @
acd94990
[testenv]
deps
=
-rtests/requirements.txt
passenv
=
APPDATA USERPROFILE HOMEDRIVE HOMEPATH windir APPVEYOR
commands
=
python -m pytest {posargs:-rsx}
commands
=
py.test {posargs:-rsx}
usedevelop
=
True
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