Import Upstream version 1.8.2

This commit is contained in:
su-fang 2023-02-21 09:29:01 +08:00
commit cf42014c48
72 changed files with 4247 additions and 0 deletions

9
.bzrignore Normal file
View File

@ -0,0 +1,9 @@
*.egg-info
./build
./dist
.coverage
__pycache__
.tox
coverage.xml
nosetests.xml
docs/_build

3
.coveragerc Normal file
View File

@ -0,0 +1,3 @@
[report]
show_missing=1

316
CHANGES.txt Normal file
View File

@ -0,0 +1,316 @@
``pkginfo`` Changelog
=====================
1.8.2 (2021-12-01)
------------------
- Add fix for installed distributions with '__package__' set to an empty
string. LP #1952946.
1.8.1 (2021-11-19)
------------------
- Add 'MANIFEST.in' to ensure example files used by tests are included
in source distributions. LP #1951553.
1.8.0 (2021-11-18)
------------------
- Support new standard metadata location for installed dists. LP #1865286.
- Don't overwrite header-based 'description' with empty payload. LP #1885458.
- Add support for Metadata-Version 2.2. LP #1928729.
- Add support for uncompressed tarballs for sdists. LP #1951457.
- Add support for Python 3.10.
1.7.1 (2021-07-09)
------------------
- Use Python3 to build docs, and fix doctest examples to use Python3-
compatible syntax. LP #1933322.
1.7.0 (2021-01-16)
------------------
- Add support for Python 3.9.
- Drop support for Python 3.5.
1.6.1 (2020-10-26)
------------------
- Adjust test classifiers to match supported Python versions. LP #1901127.
1.6.0 (2020-10-20)
------------------
- Add support for Python 3.8.
LP #1869854.
- Drop support for Python 3.4.
- Update tests to match setuptools' change, no longer reporting metadata
version for installed packages w/o explicit metadata. LP #1870197.
1.5.0.1 (2019-01-08)
--------------------
- Fix broken 'sdist'. LP #1639585.
1.5.0 (2019-01-07)
------------------
- Fix 'console_scripts' entry point syntax. LP #1810734.
- Add support for JSON output from the CLI. LP #1700580.
- Add support for installed wheels. E.g., 'dist-info/' dirs. LP #1700200.
- Harden metadata extraction against unexpected encodings. LP #1780454.
- Update tests to match pip/setuptools' use of new metadata version.
LP #1772274.
- Add support for Python 3.6 and 3.7.
- Drop support for Python 3.3.
1.4.2 (2018-03-14)
------------------
- Use relative imports in pkginfo modules. Supports vendoring of the
package into setuptools.
- Add support for ``Provides-Extra`` and ``Description-Content-Type`` fields.
Per https://packaging.python.org/specifications/. See: PEP 566.
- Remove support for old setuptools leaving ``PKG-INFO`` in the root of
the project directory.
1.4.1 (2016-11-07)
------------------
- Packaging only change (invalid sdist built for 1.4.0).
1.4.0 (2016-11-04)
------------------
- Relicense under MIT license: the PSF license is not suitable for
third-party libraries.
1.3.2 (2016-05-24)
------------------
- Packaging-only change (automate fix for wheel built for 1.3.1).
1.3.1 (2016-05-24)
------------------
- Packaging-only change (invalid wheel built for 1.3.0).
1.3.0 (2016-05-23)
------------------
- Update homepage URL to point to Launchpad, rather than PyPI.
- Add support for building wheels.
- Add support for Python 3.5.
- Drop support for Python 2.6 and 3.2.
1.2.1 (2014-01-02)
------------------
- Add overlooked Trove classifier for Python 3.4.
1.2 (2014-01-02)
----------------
- Add support for Python 3.4, PyPy3.
- Add 100% coverage for ``pkginfo.commandline`` module.
1.2b1 (2013-12-05)
------------------
- Add support for the "wheel" distribution format, along with minimal
metadata 2.0 support (not including new PEP 426 JSON properties).
Code (re-)borrowed from Donald Stuft's ``twine`` package.
1.1 (2013-10-09)
----------------
- Fix tests to pass with current PyPy releases.
1.1b1 (2013-05-05)
------------------
- Support "develop" packages which keep their ``*.egg-info`` in a subdirectory.
See https://bugs.launchpad.net/pkginfo/+bug/919147.
- Add support for "unpacked SDists" (thanks to Mike Lundy for the patch).
1.0 (2013-05-05)
----------------
- No changes from 1.0b2.
1.0b2 (2012-12-28)
------------------
- Suppress resource warning leaks reported against clients.
- Fix 'commandline' module under Py3k.
1.0b1 (2012-12-28)
------------------
- Add support for Python 3.2 and 3.3, including testing them under ``tox``.
- Add support for PyPy, including testing it under ``tox``.
- Test supported Python versions under ``tox``.
- Drop support for Python 2.5.
- Add a ``setup.py dev`` alias: runs ``setup.py develop`` and installs
testing extras (``nose`` and ``coverage``).
0.9.1 (2012-10-22)
------------------
- Fix test failure under Python >= 2.7, which is enforcing
'metadata_version == 1.1' because we have classifiers.
0.9 (2012-04-25)
----------------
- Fix introspection of installed namespace packages.
They may be installed as eggs or via dist-installed 'egg-info' files.
https://bugs.launchpad.net/pkginfo/+bug/934311
- Avoid a regression in 0.8 under Python 2.6 / 2.7 when parsing unicode.
https://bugs.launchpad.net/pkginfo/+bug/733827/comments/3
0.8 (2011-03-12)
----------------
- Work around Python 2.7's breakage of StringIO. Fixes
https://bugs.launchpad.net/pkginfo/+bug/733827
- Fix bug in introspection of installed packages missing the
``__package__`` attribute.
0.7 (2010-11-04)
----------------
- Preserve newlines in the ``description`` field. Thanks to Sridhar
Ratnakumar for the patch.
- 100% test coverage.
0.6 (2010-06-01)
----------------
- Replace use of ``StringIO.StringIO`` with ``io.StringIO``, where available
(Python >= 2.6).
- Replace use of ``rfc822`` stdlib module with ``email.parser``, when
available (Python >= 2.5). Ensured that distributions "unfold" wrapped
continuation lines, stripping any leading / trailing whitespace, no matter
which module was used for parsing.
- Remove bogus testing dependency on ``zope.testing``.
- Add tests that the "environment markers" spelled out in the approved
PEP 345 are captured.
- Add ``Project-URL`` for ``1.2`` PKG-INFO metdata (defined in the accepted
version of PEP 345).
0.5 (2009-09-11)
----------------
- Marked package as non-zip-safe.
- Fix Trove metadata misspelling.
- Restore compatibility with Python 2.4.
- Note that the introspection of installed packages / modules works only
in Python 2.6 or later.
- Add ``Index`` class as an abstraction over a collection of distributions.
- Add ``download_url_prefix`` argument to ``pkginfo`` script. If passed,
the script will use the prefix to synthesize a ``download_url`` for
distributions which do not supply that value directly.
0.4.1 (2009-05-07)
------------------
- Fix bugs in handling of installed packages which lack ``__file__``
or ``PKG-INFO``.
0.4 (2009-05-07)
----------------
- Extend the console script to allow output as CSV or INI. Also, added
arguments to specify the metadata version and other parsing / output
policies.
- Add support for the different metadata versions specified in PEPs
241, 314, and 345. Distributions now parse and expose only the attributes
corresponding to their metadata version, which defaults to the version
parsed from the ``PKG-INFO`` file. The programmer can override that version
when creating the distribution object.
0.3 (2009-05-07)
----------------
- Add support for introspection of "development eggs" (checkouts with
``PKG-INFO``, perhaps created via ``setup.py develop``).
- Add a console script, ``pkginfo``, which takes one or more paths
on the command line and writes out the associated information. Thanks
to ``runeh`` for the patch!
- Add ``get_metadata`` helper function, which dispatches a given path or
module across the available distribution types, and returns a distribution
object. Thanks to ``runeh`` for the patch!
- Make distribution objects support iteration over the metadata fields.
Thanks to ``runeh`` for the patch!
- Make ``Distribution`` and subclasses new-style classes. Thanks to ``runeh``
for the patch!
0.2 (2009-04-14)
----------------
- Add support for introspection of ``bdist_egg`` binary distributions.
0.1.1 (2009-04-10)
------------------
- Fix packaging errors.
0.1 (2009-04-10)
----------------
- Initial release.

21
LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2009 Agendaless Consulting, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
MANIFEST.in Normal file
View File

@ -0,0 +1 @@
graft docs/examples/

357
PKG-INFO Normal file
View File

@ -0,0 +1,357 @@
Metadata-Version: 2.1
Name: pkginfo
Version: 1.8.2
Summary: Query metadatdata from sdists / bdists / installed packages.
Home-page: https://code.launchpad.net/~tseaver/pkginfo/trunk
Author: Tres Seaver, Agendaless Consulting
Author-email: tseaver@agendaless.com
License: MIT
Description: ``pkginfo`` README
==================
This package provides an API for querying the distutils metadata written in
the ``PKG-INFO`` file inside a source distriubtion (an ``sdist``) or a
binary distribution (e.g., created by running ``bdist_egg``). It can
also query the ``EGG-INFO`` directory of an installed distribution, and
the ``*.egg-info`` stored in a "development checkout"
(e.g, created by running ``setup.py develop``).
Please see the `pkginfo docs <http://packages.python.org/pkginfo>`_
for detailed documentation.
``pkginfo`` Changelog
=====================
1.8.2 (2021-12-01)
------------------
- Add fix for installed distributions with '__package__' set to an empty
string. LP #1952946.
1.8.1 (2021-11-19)
------------------
- Add 'MANIFEST.in' to ensure example files used by tests are included
in source distributions. LP #1951553.
1.8.0 (2021-11-18)
------------------
- Support new standard metadata location for installed dists. LP #1865286.
- Don't overwrite header-based 'description' with empty payload. LP #1885458.
- Add support for Metadata-Version 2.2. LP #1928729.
- Add support for uncompressed tarballs for sdists. LP #1951457.
- Add support for Python 3.10.
1.7.1 (2021-07-09)
------------------
- Use Python3 to build docs, and fix doctest examples to use Python3-
compatible syntax. LP #1933322.
1.7.0 (2021-01-16)
------------------
- Add support for Python 3.9.
- Drop support for Python 3.5.
1.6.1 (2020-10-26)
------------------
- Adjust test classifiers to match supported Python versions. LP #1901127.
1.6.0 (2020-10-20)
------------------
- Add support for Python 3.8.
LP #1869854.
- Drop support for Python 3.4.
- Update tests to match setuptools' change, no longer reporting metadata
version for installed packages w/o explicit metadata. LP #1870197.
1.5.0.1 (2019-01-08)
--------------------
- Fix broken 'sdist'. LP #1639585.
1.5.0 (2019-01-07)
------------------
- Fix 'console_scripts' entry point syntax. LP #1810734.
- Add support for JSON output from the CLI. LP #1700580.
- Add support for installed wheels. E.g., 'dist-info/' dirs. LP #1700200.
- Harden metadata extraction against unexpected encodings. LP #1780454.
- Update tests to match pip/setuptools' use of new metadata version.
LP #1772274.
- Add support for Python 3.6 and 3.7.
- Drop support for Python 3.3.
1.4.2 (2018-03-14)
------------------
- Use relative imports in pkginfo modules. Supports vendoring of the
package into setuptools.
- Add support for ``Provides-Extra`` and ``Description-Content-Type`` fields.
Per https://packaging.python.org/specifications/. See: PEP 566.
- Remove support for old setuptools leaving ``PKG-INFO`` in the root of
the project directory.
1.4.1 (2016-11-07)
------------------
- Packaging only change (invalid sdist built for 1.4.0).
1.4.0 (2016-11-04)
------------------
- Relicense under MIT license: the PSF license is not suitable for
third-party libraries.
1.3.2 (2016-05-24)
------------------
- Packaging-only change (automate fix for wheel built for 1.3.1).
1.3.1 (2016-05-24)
------------------
- Packaging-only change (invalid wheel built for 1.3.0).
1.3.0 (2016-05-23)
------------------
- Update homepage URL to point to Launchpad, rather than PyPI.
- Add support for building wheels.
- Add support for Python 3.5.
- Drop support for Python 2.6 and 3.2.
1.2.1 (2014-01-02)
------------------
- Add overlooked Trove classifier for Python 3.4.
1.2 (2014-01-02)
----------------
- Add support for Python 3.4, PyPy3.
- Add 100% coverage for ``pkginfo.commandline`` module.
1.2b1 (2013-12-05)
------------------
- Add support for the "wheel" distribution format, along with minimal
metadata 2.0 support (not including new PEP 426 JSON properties).
Code (re-)borrowed from Donald Stuft's ``twine`` package.
1.1 (2013-10-09)
----------------
- Fix tests to pass with current PyPy releases.
1.1b1 (2013-05-05)
------------------
- Support "develop" packages which keep their ``*.egg-info`` in a subdirectory.
See https://bugs.launchpad.net/pkginfo/+bug/919147.
- Add support for "unpacked SDists" (thanks to Mike Lundy for the patch).
1.0 (2013-05-05)
----------------
- No changes from 1.0b2.
1.0b2 (2012-12-28)
------------------
- Suppress resource warning leaks reported against clients.
- Fix 'commandline' module under Py3k.
1.0b1 (2012-12-28)
------------------
- Add support for Python 3.2 and 3.3, including testing them under ``tox``.
- Add support for PyPy, including testing it under ``tox``.
- Test supported Python versions under ``tox``.
- Drop support for Python 2.5.
- Add a ``setup.py dev`` alias: runs ``setup.py develop`` and installs
testing extras (``nose`` and ``coverage``).
0.9.1 (2012-10-22)
------------------
- Fix test failure under Python >= 2.7, which is enforcing
'metadata_version == 1.1' because we have classifiers.
0.9 (2012-04-25)
----------------
- Fix introspection of installed namespace packages.
They may be installed as eggs or via dist-installed 'egg-info' files.
https://bugs.launchpad.net/pkginfo/+bug/934311
- Avoid a regression in 0.8 under Python 2.6 / 2.7 when parsing unicode.
https://bugs.launchpad.net/pkginfo/+bug/733827/comments/3
0.8 (2011-03-12)
----------------
- Work around Python 2.7's breakage of StringIO. Fixes
https://bugs.launchpad.net/pkginfo/+bug/733827
- Fix bug in introspection of installed packages missing the
``__package__`` attribute.
0.7 (2010-11-04)
----------------
- Preserve newlines in the ``description`` field. Thanks to Sridhar
Ratnakumar for the patch.
- 100% test coverage.
0.6 (2010-06-01)
----------------
- Replace use of ``StringIO.StringIO`` with ``io.StringIO``, where available
(Python >= 2.6).
- Replace use of ``rfc822`` stdlib module with ``email.parser``, when
available (Python >= 2.5). Ensured that distributions "unfold" wrapped
continuation lines, stripping any leading / trailing whitespace, no matter
which module was used for parsing.
- Remove bogus testing dependency on ``zope.testing``.
- Add tests that the "environment markers" spelled out in the approved
PEP 345 are captured.
- Add ``Project-URL`` for ``1.2`` PKG-INFO metdata (defined in the accepted
version of PEP 345).
0.5 (2009-09-11)
----------------
- Marked package as non-zip-safe.
- Fix Trove metadata misspelling.
- Restore compatibility with Python 2.4.
- Note that the introspection of installed packages / modules works only
in Python 2.6 or later.
- Add ``Index`` class as an abstraction over a collection of distributions.
- Add ``download_url_prefix`` argument to ``pkginfo`` script. If passed,
the script will use the prefix to synthesize a ``download_url`` for
distributions which do not supply that value directly.
0.4.1 (2009-05-07)
------------------
- Fix bugs in handling of installed packages which lack ``__file__``
or ``PKG-INFO``.
0.4 (2009-05-07)
----------------
- Extend the console script to allow output as CSV or INI. Also, added
arguments to specify the metadata version and other parsing / output
policies.
- Add support for the different metadata versions specified in PEPs
241, 314, and 345. Distributions now parse and expose only the attributes
corresponding to their metadata version, which defaults to the version
parsed from the ``PKG-INFO`` file. The programmer can override that version
when creating the distribution object.
0.3 (2009-05-07)
----------------
- Add support for introspection of "development eggs" (checkouts with
``PKG-INFO``, perhaps created via ``setup.py develop``).
- Add a console script, ``pkginfo``, which takes one or more paths
on the command line and writes out the associated information. Thanks
to ``runeh`` for the patch!
- Add ``get_metadata`` helper function, which dispatches a given path or
module across the available distribution types, and returns a distribution
object. Thanks to ``runeh`` for the patch!
- Make distribution objects support iteration over the metadata fields.
Thanks to ``runeh`` for the patch!
- Make ``Distribution`` and subclasses new-style classes. Thanks to ``runeh``
for the patch!
0.2 (2009-04-14)
----------------
- Add support for introspection of ``bdist_egg`` binary distributions.
0.1.1 (2009-04-10)
------------------
- Fix packaging errors.
0.1 (2009-04-10)
----------------
- Initial release.
Keywords: distribution sdist installed metadata
Platform: Unix
Platform: Windows
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Software Distribution
Provides-Extra: testing

13
README.txt Normal file
View File

@ -0,0 +1,13 @@
``pkginfo`` README
==================
This package provides an API for querying the distutils metadata written in
the ``PKG-INFO`` file inside a source distriubtion (an ``sdist``) or a
binary distribution (e.g., created by running ``bdist_egg``). It can
also query the ``EGG-INFO`` directory of an installed distribution, and
the ``*.egg-info`` stored in a "development checkout"
(e.g, created by running ``setup.py develop``).
Please see the `pkginfo docs <http://packages.python.org/pkginfo>`_
for detailed documentation.

15
TODO.txt Normal file
View File

@ -0,0 +1,15 @@
TODOs
=====
- [X] Catch up to latest changes in PEP345:
* Project-URL header
* "environment markers"
- [_] Add APIs to ``Distribution`` which expose the semantics of the
requirement versions and environment markers.
- [_] Allow the ``pkginfo`` script to process URLs.
- [_] Allow the ``pkginfo`` script to process requirements specs.

75
docs/Makefile Normal file
View File

@ -0,0 +1,75 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " changes to make an overview over all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
clean:
-rm -rf .build/*
html:
mkdir -p .build/html .build/doctrees
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html
@echo
@echo "Build finished. The HTML pages are in .build/html."
pickle:
mkdir -p .build/pickle .build/doctrees
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle
@echo
@echo "Build finished; now you can process the pickle files."
web: pickle
json:
mkdir -p .build/json .build/doctrees
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) .build/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
mkdir -p .build/htmlhelp .build/doctrees
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in .build/htmlhelp."
latex:
mkdir -p .build/latex .build/doctrees
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex
@echo
@echo "Build finished; the LaTeX files are in .build/latex."
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
"run these through (pdf)latex."
changes:
mkdir -p .build/changes .build/doctrees
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes
@echo
@echo "The overview file is in .build/changes."
linkcheck:
mkdir -p .build/linkcheck .build/doctrees
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in .build/linkcheck/output.txt."

194
docs/conf.py Normal file
View File

@ -0,0 +1,194 @@
# -*- coding: utf-8 -*-
#
# pkginfo documentation build configuration file, created by
# sphinx-quickstart on Wed Apr 8 19:26:04 2009.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If your extensions are in another directory, add it here. If the directory
# is relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
# General configuration
# ---------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
]
doctest_path = [os.path.abspath('..')]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'pkginfo'
copyright = u'2009-2013, Tres Seaver'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.2'
# The full version, including alpha/beta/rc tags.
release = '1.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['.build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# Options for HTML output
# -----------------------
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
#html_style = 'default.css'
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, the reST sources are included in the HTML build as _sources/<name>.
#html_copy_source = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'pkginfodoc'
# Options for LaTeX output
# ------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class [howto/manual]).
latex_documents = [
('index', 'pkginfo.tex', u'pkginfo Documentation', u'Tres Seaver', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True

164
docs/distributions.rst Normal file
View File

@ -0,0 +1,164 @@
Distribution Types
==================
The fundamental abstraction provided by this pacakge is the ``Distribution``
base class. Implementations exist for specific cases: source distributions,
binary distributions, installed pakcages, and development checkouts.
.. doctest::
>>> from pkginfo import Distribution
>>> from pkginfo import SDist
>>> assert issubclass(SDist, Distribution)
>>> from pkginfo import UnpackedSDist
>>> assert issubclass(UnpackedSDist, SDist)
>>> from pkginfo import BDist
>>> assert issubclass(BDist, Distribution)
>>> from pkginfo import Wheel
>>> assert issubclass(Wheel, Distribution)
>>> from pkginfo import Installed
>>> assert issubclass(Installed, Distribution)
>>> from pkginfo import Develop
>>> assert issubclass(Develop, Distribution)
Introspecting Source Distributions
----------------------------------
``SDist`` objects are created from a filesystem path to the corresponding
archive, which should have been created via the ``sdist`` command from
distutils:
.. doctest::
>>> mypackage = SDist('docs/examples/mypackage-0.1.tar.gz')
After creation, the ``SDist`` instance will have attributes corrsponding
the the fields defined in the PEP corresponding to the metadata version,
lower-cased and transliterated into valid Python identifiers by mapping
hyphens to underscores. E.g.:
.. doctest::
>>> print(mypackage.metadata_version)
1.0
>>> print(mypackage.name)
mypackage
>>> print(mypackage.version)
0.1
Fields which are optional under the PEP, and which have no value set
in their ``PKG-INFO``, will map to the value ``None``:
.. doctest::
>>> print(mypackage.keywords)
None
Fields which are marked "multiple use" under the PEP map onto sequences;
their names are pluralized to indicate the sequence. "Multiple use" fields
with no occurences in the ``PKG-INFO`` file will map onto an empty sequence:
.. doctest::
>>> print(list(mypackage.supported_platforms))
[]
See `Metadata Versions <metadata.html>`_ for an example with a non-empty,
"multiple-use" field.
Introspecting Unpacked Source Distributions
-------------------------------------------
You can also introspect a previously-unpacked package with ``UnpackedSDist``
either by passing it the path to the unpacked package, or by passing it the
setup.py at the top level:
.. doctest::
>>> mypackage = UnpackedSDist('docs/examples/mypackage-0.1')
>>> print(mypackage.name)
mypackage
>>> myotherpackage = UnpackedSDist('docs/examples/mypackage-0.1/setup.py')
>>> print(myotherpackage.name)
mypackage
``UnpackedSDist`` objects are most useful in conjuction with distutils to
produce sdists that want complex behavior for determining what metadata to use;
these sdists normally break when installed with ``pip``, because metadata in an
sdist is regenerated when pip installed. You can achieve this in your
`setup.py` as follows:
.. code::
>>> from setuptools import dist, setup
>>> dist.Distribution(dict(setup_requires='pkginfo'))
>>> from pkginfo import UnpackedSDist
>>> try:
... d = UnpackedSDist(__file__)
... VERSION = d.version
... except ValueError:
... VERSION = (version_from_source_control() or
... os.getenv('VERSION', '1.0'))
>>> setup(name='mypackage', version=VERSION)
Introspecting Binary Distributions
----------------------------------
``BDist`` objects are created from the filename, which should have been
generated via ``setup.py bdist_egg``.
.. doctest::
>>> mypackage = BDist('docs/examples/mypackage-0.1-py2.6.egg')
After that, they have the same metadata as other ``Distribution`` objects,
Introspecting Wheels
--------------------
``Wheel`` objects are created from the filename, which should have been
generated via ``setup.py bdist_wheel``.
.. doctest::
>>> mypackage = Wheel('docs/examples/mypackage-0.1-cp26-none-linux_x86_64.whl')
After that, they have the same metadata as other ``Distribution`` objects,
Introspecting Installed Packages
--------------------------------
``Installed`` objects are created from either a module object or its
dotted name. Note that this feature only works in Python 2.6 or later:
earlier Python versions did not record ``PKG-INFO`` for installed packages.
.. doctest::
>>> import sys
>>> if sys.version_info >= (2,6):
... dotted = Installed('pkginfo')
... import pkginfo
... direct = Installed(pkginfo)
After that, they have the same metadata as other ``Distribution`` objects,
assuming that the package on which they were based has a discoverable
'.egg-info' file / directory. To be discoverable, the '.egg-info' must
either be located inside the package (e.g., created via ``setup.py develop``
under setuptools), or adjacent to the package (e.g., created via
``setup.py instlall``).
Introspecting Development Checkouts
-----------------------------------
``Develop`` objects are created from a path to a checkout containing
a ``PKG-iNFO`` file, e.g., created by running ``setup.py develop`` under
setuptools.
.. doctest::
>>> develop = Develop('.')
After that, they have the same metadata as other ``Distribution`` objects.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

View File

@ -0,0 +1,15 @@
Metadata-Version: 2.0
Name: mypackage
Version: 0.1
Summary: UNKNOWN
Home-page: http://pypi.python.org/pypi/pkginfo
Author: Tres Seaver
Author-email: tseaver@palladion.com
License: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console (Text Based)
UNKNOWN

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
Metadata-Version: 1.0
Name: mypackage
Version: 0.1
Summary: UNKNOWN
Home-page: http://pypi.python.org/pypi/pkginfo
Author: Tres Seaver
Author-email: tseaver@palladion.com
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console (Text Based)

View File

@ -0,0 +1,4 @@
mypackage README
================
Dummy package for testing ``pkginfo``.

View File

@ -0,0 +1,5 @@
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0

View File

@ -0,0 +1,13 @@
from setuptools import setup
setup(
name='mypackage',
version='0.1',
author='Tres Seaver',
author_email='tseaver@palladion.com',
url='http://pypi.python.org/pypi/pkginfo',
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console (Text Based)',
],
)

Binary file not shown.

Binary file not shown.

Binary file not shown.

18
docs/index.rst Normal file
View File

@ -0,0 +1,18 @@
:mod:`pkginfo` documentation
============================
This package provides an API for querying the distutils metadata written in
the ``PKG-INFO`` file inside a source distriubtion (an ``sdist``) or a
binary distribution (e.g., created by running ``bdist_egg`` or
``bdist_wheel``). It can also query the ``EGG-INFO`` directory of an
installed distribution, and the ``*.egg-info`` stored in a "development
checkout" (e.g, created by running ``setup.py develop``).
Contents:
.. toctree::
:maxdepth: 2
distributions
metadata
indexes

26
docs/indexes.rst Normal file
View File

@ -0,0 +1,26 @@
Distribution Indexes
=====================
An ``Index`` is conceptually a set of ``Distribution`` objects, with some
additional behavior for managing the set as a whole.
.. doctest::
>>> from pkginfo import Distribution
>>> from pkginfo import Index
>>> index = Index()
>>> list(index)
[]
>>> d1 = Distribution()
>>> d1.name = 'foo'
>>> d1.version = '1.0'
>>> index.add(d1)
>>> list(index)
['foo-1.0']
>>> d2 = Distribution()
>>> d2.name = 'foo'
>>> d2.version = '1.1'
>>> index.add(d2)
>>> sorted(list(index))
['foo-1.0', 'foo-1.1']

29
docs/metadata.rst Normal file
View File

@ -0,0 +1,29 @@
Metadata Versions
=================
The allowed ``PKG-INFO`` fields and their semantics are defined in a series
of PEPs, each of which updates the metadata version field.
- Metadata version 1.0 is specified in `PEP 241`_.
- Metadata version 1.1 is specified in `PEP 314`_.
- Metadata version 1.2 is specified in `PEP 345`_ (still in draft).
A given ``Distribution`` object parses / exposes the attributes which
correspond to the metadata version specified in its ``PKG-INFO``.
You can override the metadata version stored in a given distribution by
passing the specific version (as a string) to its constructor. E.g.,
updating the metadata version here in order to expose the classifiers,
which were not defined under version '1.0':
.. doctest::
>>> from pkginfo import SDist
>>> mypackage = SDist('docs/examples/mypackage-0.1.tar.gz',
... metadata_version='1.1')
>>> print([str(x) for x in mypackage.classifiers])
['Development Status :: 4 - Beta', 'Environment :: Console (Text Based)']
.. _`PEP 241`: http://svn.python.org/projects/peps/trunk/pep-0241.txt
.. _`PEP 314`: http://svn.python.org/projects/peps/trunk/pep-0314.txt
.. _`PEP 345`: http://svn.python.org/projects/peps/trunk/pep-0345.txt

357
pkginfo.egg-info/PKG-INFO Normal file
View File

@ -0,0 +1,357 @@
Metadata-Version: 2.1
Name: pkginfo
Version: 1.8.2
Summary: Query metadatdata from sdists / bdists / installed packages.
Home-page: https://code.launchpad.net/~tseaver/pkginfo/trunk
Author: Tres Seaver, Agendaless Consulting
Author-email: tseaver@agendaless.com
License: MIT
Description: ``pkginfo`` README
==================
This package provides an API for querying the distutils metadata written in
the ``PKG-INFO`` file inside a source distriubtion (an ``sdist``) or a
binary distribution (e.g., created by running ``bdist_egg``). It can
also query the ``EGG-INFO`` directory of an installed distribution, and
the ``*.egg-info`` stored in a "development checkout"
(e.g, created by running ``setup.py develop``).
Please see the `pkginfo docs <http://packages.python.org/pkginfo>`_
for detailed documentation.
``pkginfo`` Changelog
=====================
1.8.2 (2021-12-01)
------------------
- Add fix for installed distributions with '__package__' set to an empty
string. LP #1952946.
1.8.1 (2021-11-19)
------------------
- Add 'MANIFEST.in' to ensure example files used by tests are included
in source distributions. LP #1951553.
1.8.0 (2021-11-18)
------------------
- Support new standard metadata location for installed dists. LP #1865286.
- Don't overwrite header-based 'description' with empty payload. LP #1885458.
- Add support for Metadata-Version 2.2. LP #1928729.
- Add support for uncompressed tarballs for sdists. LP #1951457.
- Add support for Python 3.10.
1.7.1 (2021-07-09)
------------------
- Use Python3 to build docs, and fix doctest examples to use Python3-
compatible syntax. LP #1933322.
1.7.0 (2021-01-16)
------------------
- Add support for Python 3.9.
- Drop support for Python 3.5.
1.6.1 (2020-10-26)
------------------
- Adjust test classifiers to match supported Python versions. LP #1901127.
1.6.0 (2020-10-20)
------------------
- Add support for Python 3.8.
LP #1869854.
- Drop support for Python 3.4.
- Update tests to match setuptools' change, no longer reporting metadata
version for installed packages w/o explicit metadata. LP #1870197.
1.5.0.1 (2019-01-08)
--------------------
- Fix broken 'sdist'. LP #1639585.
1.5.0 (2019-01-07)
------------------
- Fix 'console_scripts' entry point syntax. LP #1810734.
- Add support for JSON output from the CLI. LP #1700580.
- Add support for installed wheels. E.g., 'dist-info/' dirs. LP #1700200.
- Harden metadata extraction against unexpected encodings. LP #1780454.
- Update tests to match pip/setuptools' use of new metadata version.
LP #1772274.
- Add support for Python 3.6 and 3.7.
- Drop support for Python 3.3.
1.4.2 (2018-03-14)
------------------
- Use relative imports in pkginfo modules. Supports vendoring of the
package into setuptools.
- Add support for ``Provides-Extra`` and ``Description-Content-Type`` fields.
Per https://packaging.python.org/specifications/. See: PEP 566.
- Remove support for old setuptools leaving ``PKG-INFO`` in the root of
the project directory.
1.4.1 (2016-11-07)
------------------
- Packaging only change (invalid sdist built for 1.4.0).
1.4.0 (2016-11-04)
------------------
- Relicense under MIT license: the PSF license is not suitable for
third-party libraries.
1.3.2 (2016-05-24)
------------------
- Packaging-only change (automate fix for wheel built for 1.3.1).
1.3.1 (2016-05-24)
------------------
- Packaging-only change (invalid wheel built for 1.3.0).
1.3.0 (2016-05-23)
------------------
- Update homepage URL to point to Launchpad, rather than PyPI.
- Add support for building wheels.
- Add support for Python 3.5.
- Drop support for Python 2.6 and 3.2.
1.2.1 (2014-01-02)
------------------
- Add overlooked Trove classifier for Python 3.4.
1.2 (2014-01-02)
----------------
- Add support for Python 3.4, PyPy3.
- Add 100% coverage for ``pkginfo.commandline`` module.
1.2b1 (2013-12-05)
------------------
- Add support for the "wheel" distribution format, along with minimal
metadata 2.0 support (not including new PEP 426 JSON properties).
Code (re-)borrowed from Donald Stuft's ``twine`` package.
1.1 (2013-10-09)
----------------
- Fix tests to pass with current PyPy releases.
1.1b1 (2013-05-05)
------------------
- Support "develop" packages which keep their ``*.egg-info`` in a subdirectory.
See https://bugs.launchpad.net/pkginfo/+bug/919147.
- Add support for "unpacked SDists" (thanks to Mike Lundy for the patch).
1.0 (2013-05-05)
----------------
- No changes from 1.0b2.
1.0b2 (2012-12-28)
------------------
- Suppress resource warning leaks reported against clients.
- Fix 'commandline' module under Py3k.
1.0b1 (2012-12-28)
------------------
- Add support for Python 3.2 and 3.3, including testing them under ``tox``.
- Add support for PyPy, including testing it under ``tox``.
- Test supported Python versions under ``tox``.
- Drop support for Python 2.5.
- Add a ``setup.py dev`` alias: runs ``setup.py develop`` and installs
testing extras (``nose`` and ``coverage``).
0.9.1 (2012-10-22)
------------------
- Fix test failure under Python >= 2.7, which is enforcing
'metadata_version == 1.1' because we have classifiers.
0.9 (2012-04-25)
----------------
- Fix introspection of installed namespace packages.
They may be installed as eggs or via dist-installed 'egg-info' files.
https://bugs.launchpad.net/pkginfo/+bug/934311
- Avoid a regression in 0.8 under Python 2.6 / 2.7 when parsing unicode.
https://bugs.launchpad.net/pkginfo/+bug/733827/comments/3
0.8 (2011-03-12)
----------------
- Work around Python 2.7's breakage of StringIO. Fixes
https://bugs.launchpad.net/pkginfo/+bug/733827
- Fix bug in introspection of installed packages missing the
``__package__`` attribute.
0.7 (2010-11-04)
----------------
- Preserve newlines in the ``description`` field. Thanks to Sridhar
Ratnakumar for the patch.
- 100% test coverage.
0.6 (2010-06-01)
----------------
- Replace use of ``StringIO.StringIO`` with ``io.StringIO``, where available
(Python >= 2.6).
- Replace use of ``rfc822`` stdlib module with ``email.parser``, when
available (Python >= 2.5). Ensured that distributions "unfold" wrapped
continuation lines, stripping any leading / trailing whitespace, no matter
which module was used for parsing.
- Remove bogus testing dependency on ``zope.testing``.
- Add tests that the "environment markers" spelled out in the approved
PEP 345 are captured.
- Add ``Project-URL`` for ``1.2`` PKG-INFO metdata (defined in the accepted
version of PEP 345).
0.5 (2009-09-11)
----------------
- Marked package as non-zip-safe.
- Fix Trove metadata misspelling.
- Restore compatibility with Python 2.4.
- Note that the introspection of installed packages / modules works only
in Python 2.6 or later.
- Add ``Index`` class as an abstraction over a collection of distributions.
- Add ``download_url_prefix`` argument to ``pkginfo`` script. If passed,
the script will use the prefix to synthesize a ``download_url`` for
distributions which do not supply that value directly.
0.4.1 (2009-05-07)
------------------
- Fix bugs in handling of installed packages which lack ``__file__``
or ``PKG-INFO``.
0.4 (2009-05-07)
----------------
- Extend the console script to allow output as CSV or INI. Also, added
arguments to specify the metadata version and other parsing / output
policies.
- Add support for the different metadata versions specified in PEPs
241, 314, and 345. Distributions now parse and expose only the attributes
corresponding to their metadata version, which defaults to the version
parsed from the ``PKG-INFO`` file. The programmer can override that version
when creating the distribution object.
0.3 (2009-05-07)
----------------
- Add support for introspection of "development eggs" (checkouts with
``PKG-INFO``, perhaps created via ``setup.py develop``).
- Add a console script, ``pkginfo``, which takes one or more paths
on the command line and writes out the associated information. Thanks
to ``runeh`` for the patch!
- Add ``get_metadata`` helper function, which dispatches a given path or
module across the available distribution types, and returns a distribution
object. Thanks to ``runeh`` for the patch!
- Make distribution objects support iteration over the metadata fields.
Thanks to ``runeh`` for the patch!
- Make ``Distribution`` and subclasses new-style classes. Thanks to ``runeh``
for the patch!
0.2 (2009-04-14)
----------------
- Add support for introspection of ``bdist_egg`` binary distributions.
0.1.1 (2009-04-10)
------------------
- Fix packaging errors.
0.1 (2009-04-10)
----------------
- Initial release.
Keywords: distribution sdist installed metadata
Platform: Unix
Platform: Windows
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Software Distribution
Provides-Extra: testing

View File

@ -0,0 +1,71 @@
.bzrignore
.coveragerc
CHANGES.txt
LICENSE.txt
MANIFEST.in
README.txt
TODO.txt
setup.cfg
setup.py
tox.ini
docs/Makefile
docs/conf.py
docs/distributions.rst
docs/index.rst
docs/indexes.rst
docs/metadata.rst
docs/examples/distlib-0.3.1-py2.py3-none-any.whl
docs/examples/mypackage-0.1-cp26-none-linux_x86_64.whl
docs/examples/mypackage-0.1-py2.6.egg
docs/examples/mypackage-0.1.bogus
docs/examples/mypackage-0.1.tar
docs/examples/mypackage-0.1.tar.bz2
docs/examples/mypackage-0.1.tar.gz
docs/examples/mypackage-0.1.zip
docs/examples/nodistinfo-0.1-any.whl
docs/examples/nopkginfo-0.1.egg
docs/examples/nopkginfo-0.1.zip
docs/examples/mypackage-0.1/PKG-INFO
docs/examples/mypackage-0.1/README.txt
docs/examples/mypackage-0.1/setup.cfg
docs/examples/mypackage-0.1/setup.py
docs/examples/mypackage-0.1.dist-info/METADATA
pkginfo/__init__.py
pkginfo/_compat.py
pkginfo/bdist.py
pkginfo/commandline.py
pkginfo/develop.py
pkginfo/distribution.py
pkginfo/index.py
pkginfo/installed.py
pkginfo/sdist.py
pkginfo/utils.py
pkginfo/wheel.py
pkginfo.egg-info/PKG-INFO
pkginfo.egg-info/SOURCES.txt
pkginfo.egg-info/dependency_links.txt
pkginfo.egg-info/entry_points.txt
pkginfo.egg-info/not-zip-safe
pkginfo.egg-info/requires.txt
pkginfo.egg-info/top_level.txt
pkginfo/tests/__init__.py
pkginfo/tests/test_bdist.py
pkginfo/tests/test_commandline.py
pkginfo/tests/test_develop.py
pkginfo/tests/test_distribution.py
pkginfo/tests/test_index.py
pkginfo/tests/test_installed.py
pkginfo/tests/test_sdist.py
pkginfo/tests/test_utils.py
pkginfo/tests/test_wheel.py
pkginfo/tests/funny/__init__.py
pkginfo/tests/funny/funny.egg-info
pkginfo/tests/manky/NOT-A-PACKAGE.txt
pkginfo/tests/manky/namespaced/__init__.py
pkginfo/tests/manky/namespaced.manky-0.1.egg-info/PKG-INFO
pkginfo/tests/manky/namespaced/manky/__init__.py
pkginfo/tests/silly/PKG-INFO
pkginfo/tests/wonky/NOT-A-PACKAGE.txt
pkginfo/tests/wonky/EGG-INFO/PKG-INFO
pkginfo/tests/wonky/namespaced/__init__.py
pkginfo/tests/wonky/namespaced/wonky/__init__.py

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,3 @@
[console_scripts]
pkginfo = pkginfo.commandline:main

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,4 @@
[testing]
coverage
nose

View File

@ -0,0 +1 @@
pkginfo

9
pkginfo/__init__.py Normal file
View File

@ -0,0 +1,9 @@
from .bdist import BDist
from .develop import Develop
from .distribution import Distribution
from .index import Index
from .installed import Installed
from .sdist import SDist
from .sdist import UnpackedSDist
from .utils import get_metadata
from .wheel import Wheel

34
pkginfo/_compat.py Normal file
View File

@ -0,0 +1,34 @@
try:
STRING_TYPES = (str, unicode)
except NameError: #pragma NO COVER Python >= 3.0
STRING_TYPES = (str,)
try:
u = unicode
except NameError: #pragma NO COVER Python >= 3.0
u = str
b = bytes
else: #pragma NO COVER Python < 3.0
b = str
try:
from StringIO import StringIO
except ImportError: #pragma NO COVER Python >= 3.0
from io import StringIO
from io import BytesIO
else: #pragma NO COVER Python < 3.0
BytesIO = StringIO
def must_decode(value): #pragma NO COVER
if type(value) is bytes:
try:
return value.decode('utf-8')
except UnicodeDecodeError:
return value.decode('latin1')
return value
def must_encode(value): #pragma NO COVER
if type(value) is u:
return value.encode('utf-8')
return value

39
pkginfo/bdist.py Normal file
View File

@ -0,0 +1,39 @@
import os
import zipfile
from .distribution import Distribution
class BDist(Distribution):
def __init__(self, filename, metadata_version=None):
self.filename = filename
self.metadata_version = metadata_version
self.extractMetadata()
def read(self):
fqn = os.path.abspath(
os.path.normpath(self.filename))
if not os.path.exists(fqn):
raise ValueError('No such file: %s' % fqn)
if fqn.endswith('.egg'):
archive = zipfile.ZipFile(fqn)
names = archive.namelist()
def read_file(name):
return archive.read(name)
else:
raise ValueError('Not a known archive format: %s' % fqn)
try:
tuples = [x.split('/') for x in names if 'PKG-INFO' in x]
schwarz = sorted([(len(x), x) for x in tuples])
for path in [x[1] for x in schwarz]:
candidate = '/'.join(path)
data = read_file(candidate)
if b'Metadata-Version' in data:
return data
finally:
archive.close()
raise ValueError('No PKG-INFO in archive: %s' % fqn)

232
pkginfo/commandline.py Normal file
View File

@ -0,0 +1,232 @@
"""Print the metadata for one or more Python package distributions.
Usage: %prog [options] path+
Each 'path' entry can be one of the following:
o a source distribution: in this case, 'path' should point to an existing
archive file (.tar.gz, .tar.bz2, or .zip) as generated by 'setup.py sdist'.
o a binary distribution: in this case, 'path' should point to an existing
archive file (.egg)
o a "develop" checkout: in this case, 'path' should point to a directory
initialized via 'setup.py develop' (under setuptools).
o an installed package: in this case, 'path' should be the importable name
of the package.
"""
try:
from configparser import ConfigParser
except ImportError: # pragma: NO COVER
from ConfigParser import ConfigParser
from collections import OrderedDict
from csv import writer
import json
import optparse
import os
import sys
from .utils import get_metadata
def _parse_options(args=None):
parser = optparse.OptionParser(usage=__doc__)
parser.add_option("-m", "--metadata-version", default=None,
help="Override metadata version")
parser.add_option("-f", "--field", dest="fields", action="append",
help="Specify an output field (repeatable)",
)
parser.add_option("-d", "--download-url-prefix",
dest="download_url_prefix",
help="Download URL prefix",
)
parser.add_option("--simple", dest="output", action="store_const",
const='simple', default='simple',
help="Output as simple key-value pairs",
)
parser.add_option("-s", "--skip", dest="skip", action="store_true",
default=True,
help="Skip missing values in simple output",
)
parser.add_option("-S", "--no-skip", dest="skip", action="store_false",
help="Don't skip missing values in simple output",
)
parser.add_option("--single", dest="output", action="store_const",
const='single',
help="Output delimited values",
)
parser.add_option("--item-delim", dest="item_delim", action="store",
default=';',
help="Delimiter for fields in single-line output",
)
parser.add_option("--sequence-delim", dest="sequence_delim",
action="store", default=',',
help="Delimiter for multi-valued fields",
)
parser.add_option("--csv", dest="output", action="store_const",
const='csv',
help="Output as CSV",
)
parser.add_option("--ini", dest="output", action="store_const",
const='ini',
help="Output as INI",
)
parser.add_option("--json", dest="output", action="store_const",
const='json',
help="Output as JSON",
)
options, args = parser.parse_args(args)
if len(args)==0:
parser.error("Pass one or more files or directories as arguments.")
else:
return options, args
class Base(object):
_fields = None
def __init__(self, options):
if options.fields:
self._fields = options.fields
def finish(self): # pragma: NO COVER
pass
class Simple(Base):
def __init__(self, options):
super(Simple, self).__init__(options)
self._skip = options.skip
def __call__(self, meta):
for field in self._fields or list(meta):
value = getattr(meta, field)
if (not self._skip) or (value is not None and value!=()):
print("%s: %s" % (field, value))
class SingleLine(Base):
_fields = None
def __init__(self, options):
super(SingleLine, self).__init__(options)
self._item_delim = options.item_delim
self._sequence_delim = options.sequence_delim
def __call__(self, meta):
if self._fields is None:
self._fields = list(meta)
values = []
for field in self._fields:
value = getattr(meta, field)
if isinstance(value, (tuple, list)):
value = self._sequence_delim.join(value)
else:
value = str(value)
values.append(value)
print(self._item_delim.join(values))
class CSV(Base):
_writer = None
def __init__(self, options):
super(CSV, self).__init__(options)
self._sequence_delim = options.sequence_delim
def __call__(self, meta):
if self._fields is None:
self._fields = list(meta) # first dist wins
fields = self._fields
if self._writer is None:
self._writer = writer(sys.stdout)
self._writer.writerow(fields)
values = []
for field in fields:
value = getattr(meta, field)
if isinstance(value, (tuple, list)):
value = self._sequence_delim.join(value)
else:
value = str(value)
values.append(value)
self._writer.writerow(values)
class INI(Base):
_fields = None
def __init__(self, options):
super(INI, self).__init__(options)
self._parser = ConfigParser()
def __call__(self, meta):
name = meta.name
version = meta.version
section = '%s-%s' % (name, version)
if self._parser.has_section(section):
raise ValueError('Duplicate distribution: %s' % section)
self._parser.add_section(section)
for field in self._fields or list(meta):
value = getattr(meta, field)
if isinstance(value, (tuple, list)):
value = '\n\t'.join(value)
self._parser.set(section, field, value)
def finish(self):
self._parser.write(sys.stdout) # pragma: NO COVER
class JSON(Base):
_fields = None
def __init__(self, options):
super(JSON, self).__init__(options)
self._mapping = OrderedDict()
def __call__(self, meta):
if self._fields is None:
self._fields = list(meta)
for field in self._fields:
value = getattr(meta, field)
if value and not isinstance(value, (tuple, list)):
value = str(value)
if field in self._mapping:
raise ValueError('Duplicate field: %(field)r' % locals())
self._mapping[field] = value
def finish(self):
json.dump(self._mapping, sys.stdout, indent=2)
_FORMATTERS = {
'simple': Simple,
'single': SingleLine,
'csv': CSV,
'ini': INI,
'json': JSON,
}
def main(args=None):
"""Entry point for pkginfo tool
"""
options, paths = _parse_options(args)
format = getattr(options, 'output', 'simple')
formatter = _FORMATTERS[format](options)
for path in paths:
meta = get_metadata(path, options.metadata_version)
if meta is None:
continue
if options.download_url_prefix:
if meta.download_url is None:
filename = os.path.basename(path)
meta.download_url = '%s/%s' % (options.download_url_prefix,
filename)
formatter(meta)
formatter.finish()

46
pkginfo/develop.py Normal file
View File

@ -0,0 +1,46 @@
import io
import os
import sys
import warnings
from .distribution import Distribution
def _gather_py2(top, candidates): #pragma NO COVER Py3k
def _filter(candidates, dirname, fnames):
for fname in fnames:
fqn = os.path.join(dirname, fname)
if os.path.isdir(fqn):
if fname == 'EGG-INFO' or fname.endswith('.egg-info'):
candidates.append(fqn)
os.path.walk(top, _filter, candidates)
def _gather_py3(top, candidates): #pragma NO COVER Python2
for dirpath, dirnames, fnames in os.walk(top):
for dirname in dirnames:
fqn = os.path.join(dirpath, dirname)
if dirname == 'EGG-INFO' or dirname.endswith('.egg-info'):
candidates.append(fqn)
if sys.version_info[0] < 3: #pragma NO COVER Python2
_gather = _gather_py2
else: #pragma NO COVER Py3k
_gather = _gather_py3
class Develop(Distribution):
def __init__(self, path, metadata_version=None):
self.path = os.path.abspath(
os.path.normpath(
os.path.expanduser(path)))
self.metadata_version = metadata_version
self.extractMetadata()
def read(self):
candidates = [self.path]
_gather(self.path, candidates)
for candidate in candidates:
path = os.path.join(candidate, 'PKG-INFO')
if os.path.exists(path):
with io.open(path, errors='ignore') as f:
return f.read()
warnings.warn('No PKG-INFO found for path: %s' % self.path)

154
pkginfo/distribution.py Normal file
View File

@ -0,0 +1,154 @@
from email.parser import Parser
from ._compat import StringIO
from ._compat import must_decode
def parse(fp):
return Parser().parse(fp)
def get(msg, header):
return _collapse_leading_ws(header, msg.get(header))
def get_all(msg, header):
return [_collapse_leading_ws(header, x) for x in msg.get_all(header)]
def _collapse_leading_ws(header, txt):
"""
``Description`` header must preserve newlines; all others need not
"""
if header.lower() == 'description': # preserve newlines
return '\n'.join([x[8:] if x.startswith(' ' * 8) else x
for x in txt.strip().splitlines()])
else:
return ' '.join([x.strip() for x in txt.splitlines()])
HEADER_ATTRS_1_0 = ( # PEP 241
('Metadata-Version', 'metadata_version', False),
('Name', 'name', False),
('Version', 'version', False),
('Platform', 'platforms', True),
('Supported-Platform', 'supported_platforms', True),
('Summary', 'summary', False),
('Description', 'description', False),
('Keywords', 'keywords', False),
('Home-Page', 'home_page', False),
('Author', 'author', False),
('Author-email', 'author_email', False),
('License', 'license', False),
)
HEADER_ATTRS_1_1 = HEADER_ATTRS_1_0 + ( # PEP 314
('Classifier', 'classifiers', True),
('Download-URL', 'download_url', False),
('Requires', 'requires', True),
('Provides', 'provides', True),
('Obsoletes', 'obsoletes', True),
)
HEADER_ATTRS_1_2 = HEADER_ATTRS_1_1 + ( # PEP 345
('Maintainer', 'maintainer', False),
('Maintainer-email', 'maintainer_email', False),
('Requires-Python', 'requires_python', False),
('Requires-External', 'requires_external', True),
('Requires-Dist', 'requires_dist', True),
('Provides-Dist', 'provides_dist', True),
('Obsoletes-Dist', 'obsoletes_dist', True),
('Project-URL', 'project_urls', True),
)
HEADER_ATTRS_2_0 = HEADER_ATTRS_1_2 #XXX PEP 426?
HEADER_ATTRS_2_1 = HEADER_ATTRS_1_2 + ( # PEP 566
('Provides-Extra', 'provides_extras', True),
('Description-Content-Type', 'description_content_type', False)
)
HEADER_ATTRS_2_2 = HEADER_ATTRS_2_1 + ( # PEP 643
('Dynamic', 'dynamic', True),
)
HEADER_ATTRS = {
'1.0': HEADER_ATTRS_1_0,
'1.1': HEADER_ATTRS_1_1,
'1.2': HEADER_ATTRS_1_2,
'2.0': HEADER_ATTRS_2_0,
'2.1': HEADER_ATTRS_2_1,
'2.2': HEADER_ATTRS_2_2,
}
class Distribution(object):
metadata_version = None
# version 1.0
name = None
version = None
platforms = ()
supported_platforms = ()
summary = None
description = None
keywords = None
home_page = None
download_url = None
author = None
author_email = None
license = None
# version 1.1
classifiers = ()
requires = ()
provides = ()
obsoletes = ()
# version 1.2
maintainer = None
maintainer_email = None
requires_python = None
requires_external = ()
requires_dist = ()
provides_dist = ()
obsoletes_dist = ()
project_urls = ()
# version 2.1
provides_extras = ()
description_content_type = None
# version 2.2
dynamic = ()
def extractMetadata(self):
data = self.read()
self.parse(data)
def read(self):
raise NotImplementedError
def _getHeaderAttrs(self):
return HEADER_ATTRS.get(self.metadata_version, [])
def parse(self, data):
fp = StringIO(must_decode(data))
msg = parse(fp)
if 'Metadata-Version' in msg and self.metadata_version is None:
value = get(msg, 'Metadata-Version')
metadata_version = self.metadata_version = value
for header_name, attr_name, multiple in self._getHeaderAttrs():
if attr_name == 'metadata_version':
continue
if header_name in msg:
if multiple:
values = get_all(msg, header_name)
setattr(self, attr_name, values)
else:
value = get(msg, header_name)
if value != 'UNKNOWN':
setattr(self, attr_name, value)
body = msg.get_payload()
if body:
setattr(self, 'description', body)
def __iter__(self):
for header_name, attr_name, multiple in self._getHeaderAttrs():
yield attr_name
iterkeys = __iter__

15
pkginfo/index.py Normal file
View File

@ -0,0 +1,15 @@
from .distribution import Distribution
class Index(dict):
def __setitem__(self, key, value):
if not isinstance(value, Distribution):
raise ValueError('Not a distribution: %r.' % value)
if key != '%s-%s' % (value.name, value.version):
raise ValueError('Key must match <name>-<version>.')
super(Index, self).__setitem__(key, value)
def add(self, distribution):
key = '%s-%s' % (distribution.name, distribution.version)
self[key] = distribution

63
pkginfo/installed.py Normal file
View File

@ -0,0 +1,63 @@
import glob
import io
import os
import sys
import warnings
from .distribution import Distribution
from ._compat import STRING_TYPES
class Installed(Distribution):
def __init__(self, package, metadata_version=None):
if isinstance(package, STRING_TYPES):
self.package_name = package
try:
__import__(package)
except ImportError:
package = None
else:
package = sys.modules[package]
else:
self.package_name = package.__name__
self.package = package
self.metadata_version = metadata_version
self.extractMetadata()
def read(self):
opj = os.path.join
if self.package is not None:
package = self.package.__package__
if package in ('', None):
package = self.package.__name__
egg_pattern = '%s*.egg-info' % package
dist_pattern = '%s*.dist-info' % package
file = getattr(self.package, '__file__', None)
if file is not None:
candidates = []
def _add_candidate(where):
candidates.extend(glob.glob(where))
for entry in sys.path:
if file.startswith(entry):
_add_candidate(opj(entry, 'EGG-INFO')) # egg?
_add_candidate(opj(entry, egg_pattern))
_add_candidate(opj(entry, dist_pattern))
dir, name = os.path.split(self.package.__file__)
_add_candidate(opj(dir, egg_pattern))
_add_candidate(opj(dir, '..', egg_pattern))
_add_candidate(opj(dir, dist_pattern))
_add_candidate(opj(dir, '..', dist_pattern))
for candidate in candidates:
if os.path.isdir(candidate):
if candidate.lower().endswith("egg-info"):
path = opj(candidate, 'PKG-INFO')
elif candidate.endswith("dist-info"):
path = opj(candidate, 'METADATA')
else: # pragma: NO COVER
continue
else:
path = candidate
if os.path.exists(path):
with io.open(path, errors='ignore') as f:
return f.read()
warnings.warn('No PKG-INFO found for package: %s' % self.package_name)

75
pkginfo/sdist.py Normal file
View File

@ -0,0 +1,75 @@
import io
import os
import tarfile
import zipfile
from .distribution import Distribution
class SDist(Distribution):
def __init__(self, filename, metadata_version=None):
self.filename = filename
self.metadata_version = metadata_version
self.extractMetadata()
@classmethod
def _get_archive(cls, fqn):
if not os.path.exists(fqn):
raise ValueError('No such file: %s' % fqn)
if zipfile.is_zipfile(fqn):
archive = zipfile.ZipFile(fqn)
names = archive.namelist()
def read_file(name):
return archive.read(name)
elif tarfile.is_tarfile(fqn):
archive = tarfile.TarFile.open(fqn)
names = archive.getnames()
def read_file(name):
return archive.extractfile(name).read()
else:
raise ValueError('Not a known archive format: %s' % fqn)
return archive, names, read_file
def read(self):
fqn = os.path.abspath(
os.path.normpath(self.filename))
archive, names, read_file = self._get_archive(fqn)
try:
tuples = [x.split('/') for x in names if 'PKG-INFO' in x]
schwarz = sorted([(len(x), x) for x in tuples])
for path in [x[1] for x in schwarz]:
candidate = '/'.join(path)
data = read_file(candidate)
if b'Metadata-Version' in data:
return data
finally:
archive.close()
raise ValueError('No PKG-INFO in archive: %s' % fqn)
class UnpackedSDist(SDist):
def __init__(self, filename, metadata_version=None):
if os.path.isdir(filename):
pass
elif os.path.isfile(filename):
filename = os.path.dirname(filename)
else:
raise ValueError('No such file: %s' % filename)
super(UnpackedSDist, self).__init__(
filename, metadata_version=metadata_version)
def read(self):
try:
pkg_info = os.path.join(self.filename, 'PKG-INFO')
with io.open(pkg_info, errors='ignore') as f:
return f.read()
except Exception as e:
raise ValueError('Could not load %s as an unpacked sdist: %s'
% (self.filename, e))

37
pkginfo/tests/__init__.py Normal file
View File

@ -0,0 +1,37 @@
# requirements
def _checkSample(testcase, installed):
try:
import pkg_resources
except ImportError: # no setuptools :(
pass
else:
version = pkg_resources.require('pkginfo')[0].version
testcase.assertEqual(installed.version, version)
testcase.assertEqual(installed.name, 'pkginfo')
testcase.assertEqual(installed.keywords,
'distribution sdist installed metadata' )
testcase.assertEqual(list(installed.supported_platforms), [])
def _checkClassifiers(testcase, installed):
testcase.assertEqual(list(installed.classifiers),
[
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: System :: Software Distribution',
])
def _defaultMetadataVersion():
return '2.1'

View File

@ -0,0 +1,2 @@
# sample installed package w/ .egg-info file.
__package__ = 'funny'

View File

@ -0,0 +1,3 @@
Metadata-Version: 1.0
Name: funny
Version: 0.1

View File

@ -0,0 +1,4 @@
THIS IS NOT A PYTHON PACKAGE!!!!
It is meant to be added to sys.path for testing introspection of namespace
packages installed via setuptools.

View File

@ -0,0 +1,2 @@
Metadata-Version: 1.0
Name: namespaced.wonky

View File

@ -0,0 +1,7 @@
# this is a namespace package
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)

View File

@ -0,0 +1 @@
# Dummy package inside the 'namespaced' namespace.

View File

@ -0,0 +1,3 @@
Metadata-Version: 1.0
Name: silly
Version: 0.1

View File

@ -0,0 +1,60 @@
import unittest
class BDistTests(unittest.TestCase):
def _getTargetClass(self):
from pkginfo.bdist import BDist
return BDist
def _makeOne(self, filename=None, metadata_version=None):
if metadata_version is not None:
return self._getTargetClass()(filename, metadata_version)
return self._getTargetClass()(filename)
def _checkSample(self, bdist, filename):
self.assertEqual(bdist.filename, filename)
self.assertEqual(bdist.name, 'mypackage')
self.assertEqual(bdist.version, '0.1')
self.assertEqual(bdist.keywords, None)
def _checkClassifiers(self, bdist):
self.assertEqual(list(bdist.classifiers),
['Development Status :: 4 - Beta',
'Environment :: Console (Text Based)',
])
self.assertEqual(list(bdist.supported_platforms), [])
def test_ctor_w_bogus_filename(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/nonesuch-0.1-py2.6.egg' % d
self.assertRaises(ValueError, self._makeOne, filename)
def test_ctor_w_non_egg(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.zip' % d
self.assertRaises(ValueError, self._makeOne, filename)
def test_ctor_wo_PKG_INFO(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/nopkginfo-0.1.egg' % d
self.assertRaises(ValueError, self._makeOne, filename)
def test_ctor_w_egg(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1-py2.6.egg' % d
bdist = self._makeOne(filename)
self.assertEqual(bdist.metadata_version, '1.0')
self._checkSample(bdist, filename)
def test_ctor_w_egg_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1-py2.6.egg' % d
bdist = self._makeOne(filename, metadata_version='1.1')
self.assertEqual(bdist.metadata_version, '1.1')
self._checkSample(bdist, filename)
self._checkClassifiers(bdist)

View File

@ -0,0 +1,350 @@
import unittest
class Test__parse_options(unittest.TestCase):
def _callFUT(self, args):
from pkginfo.commandline import _parse_options
return _parse_options(args)
def test_empty(self):
import io
import sys
from pkginfo.commandline import __doc__ as usage
firstline = usage.splitlines()[0]
# parse_args emits "native" error output.
if sys.version_info[0] < 3:
buf = io.BytesIO()
else:
buf = io.StringIO()
with _Monkey(sys, stderr=buf):
self.assertRaises(SystemExit, self._callFUT, [])
self.assertTrue(firstline in buf.getvalue())
def test_nonempty(self):
options, args = self._callFUT(['foo'])
self.assertEqual(args, ['foo'])
class BaseTests(unittest.TestCase):
def _getTargetClass(self):
from pkginfo.commandline import Base
return Base
def _makeOne(self, options):
return self._getTargetClass()(options)
def test___init___defaults(self):
base = self._makeOne(_Options(fields=()))
self.assertTrue(base._fields is None)
def test___init___w_fields(self):
fields = object()
base = self._makeOne(_Options(fields=fields))
self.assertTrue(base._fields is fields)
class _FormatterBase(object):
def _capture_output(self, func, *args, **kw):
import io
import sys
# Emulate stdout as wanting "native" strings
if sys.version_info[0] < 3:
buf = io.BytesIO()
else:
buf = io.StringIO()
with _Monkey(sys, stdout=buf):
func(*args, **kw)
return buf.getvalue()
def _no_output(self, simple, meta):
import sys
with _Monkey(sys, stdout=object()): # raise if write
simple(meta)
class SimpleTests(unittest.TestCase, _FormatterBase):
def _getTargetClass(self):
from pkginfo.commandline import Simple
return Simple
def _makeOne(self, options):
return self._getTargetClass()(options)
def test___init___(self):
simple = self._makeOne(_Options(fields=None, skip=True))
self.assertTrue(simple._skip)
def test___call___w_empty_fields(self):
simple = self._makeOne(_Options(fields=(), skip=False))
meta = _Meta()
self._no_output(simple, meta)
def test___call___w_skip_and_value_None_no_fields(self):
simple = self._makeOne(_Options(fields=(), skip=True))
meta = _Meta(foo=None)
self._no_output(simple, meta)
def test___call___w_skip_and_value_empty_tuple_explicit_fields(self):
simple = self._makeOne(_Options(fields=('foo',), skip=True))
meta = _Meta(foo=(), bar='Bar')
self._no_output(simple, meta)
def test___call___w_skip_but_values_explicit_fields(self):
simple = self._makeOne(_Options(fields=('foo',), skip=True))
meta = _Meta(foo='Foo')
output = self._capture_output(simple, meta)
self.assertEqual(output, 'foo: Foo\n')
class SingleLineTests(unittest.TestCase, _FormatterBase):
def _getTargetClass(self):
from pkginfo.commandline import SingleLine
return SingleLine
def _makeOne(self, options):
return self._getTargetClass()(options)
def test___init___(self):
single = self._makeOne(
_Options(fields=None, item_delim='I', sequence_delim='S'))
self.assertEqual(single._item_delim, 'I')
self.assertEqual(single._sequence_delim, 'S')
def test___call__wo_fields_wo_list(self):
single = self._makeOne(
_Options(fields=(), item_delim='|',
sequence_delim=object())) # raise if used
meta = _Meta(foo='Foo', bar='Bar')
output = self._capture_output(single, meta)
self.assertEqual(output, 'Bar|Foo\n')
def test___call__w_fields_w_list(self):
single = self._makeOne(
_Options(fields=('foo', 'bar'), item_delim='|',
sequence_delim='*'))
meta = _Meta(foo='Foo', bar=['Bar1', 'Bar2'], baz='Baz')
output = self._capture_output(single, meta)
self.assertEqual(output, 'Foo|Bar1*Bar2\n')
class CSVTests(unittest.TestCase, _FormatterBase):
def _getTargetClass(self):
from pkginfo.commandline import CSV
return CSV
def _makeOne(self, options):
return self._getTargetClass()(options)
def test___init___(self):
csv = self._makeOne(
_Options(fields=None, sequence_delim='S'))
self.assertEqual(csv._sequence_delim, 'S')
def test___call__wo_fields_wo_list(self):
meta = _Meta(foo='Foo', bar='Bar')
csv = self._makeOne(
_Options(fields=None,
sequence_delim=object())) # raise if used
output = self._capture_output(csv, meta)
self.assertEqual(output, 'bar,foo\r\nBar,Foo\r\n')
def test___call__w_fields_w_list(self):
meta = _Meta(foo='Foo', bar=['Bar1', 'Bar2'], baz='Baz')
csv = self._makeOne(
_Options(fields=('foo', 'bar'), item_delim='|',
sequence_delim='*'))
output = self._capture_output(csv, meta)
self.assertEqual(output, 'foo,bar\r\nFoo,Bar1*Bar2\r\n')
class INITests(unittest.TestCase, _FormatterBase):
def _getTargetClass(self):
from pkginfo.commandline import INI
return INI
def _makeOne(self, options):
return self._getTargetClass()(options)
def test___call___duplicate(self):
ini = self._makeOne(_Options(fields=('foo',)))
meta = _Meta(name='foo', version='0.1', foo='Foo')
ini._parser.add_section('foo-0.1')
self.assertRaises(ValueError, ini, meta)
def test___call___wo_fields_wo_list(self):
ini = self._makeOne(_Options(fields=None))
meta = _Meta(name='foo', version='0.1', foo='Foo')
ini(meta)
cp = ini._parser
self.assertEqual(cp.sections(), ['foo-0.1'])
self.assertEqual(sorted(cp.options('foo-0.1')),
['foo', 'name', 'version'])
self.assertEqual(cp.get('foo-0.1', 'name'), 'foo')
self.assertEqual(cp.get('foo-0.1', 'version'), '0.1')
self.assertEqual(cp.get('foo-0.1', 'foo'), 'Foo')
def test___call___w_fields_w_list(self):
ini = self._makeOne(_Options(fields=('foo', 'bar')))
meta = _Meta(name='foo', version='0.1',
foo='Foo', bar=['Bar1', 'Bar2'], baz='Baz')
ini(meta)
cp = ini._parser
self.assertEqual(cp.sections(), ['foo-0.1'])
self.assertEqual(sorted(cp.options('foo-0.1')), ['bar', 'foo'])
self.assertEqual(cp.get('foo-0.1', 'foo'), 'Foo')
self.assertEqual(cp.get('foo-0.1', 'bar'), 'Bar1\n\tBar2')
class JSONtests(unittest.TestCase, _FormatterBase):
def _getTargetClass(self):
from pkginfo.commandline import JSON
return JSON
def _makeOne(self, options):
return self._getTargetClass()(options)
def test___call___duplicate_with_meta_and_fields(self):
json = self._makeOne(_Options(fields=('name',)))
meta = _Meta(name='foo', version='0.1', foo='Foo')
json._mapping['name'] = 'foo'
self.assertRaises(ValueError, json, meta)
def test___call___duplicate_with_meta_wo_fields(self):
json = self._makeOne(_Options(fields=None))
meta = _Meta(name='foo', version='0.1', foo='Foo')
json._mapping['name'] = 'foo'
self.assertRaises(ValueError, json, meta)
def test___call___wo_fields_wo_list(self):
from collections import OrderedDict
json = self._makeOne(_Options(fields=None))
meta = _Meta(name='foo', version='0.1', foo='Foo')
json(meta)
expected = OrderedDict([
('foo', 'Foo'), ('name', 'foo'), ('version', '0.1')])
self.assertEqual(expected, json._mapping)
def test___call___w_fields_w_list(self):
from collections import OrderedDict
json = self._makeOne(_Options(fields=('foo', 'bar')))
meta = _Meta(name='foo', version='0.1',
foo='Foo', bar=['Bar1', 'Bar2'], baz='Baz')
json(meta)
expected = OrderedDict([
('foo', 'Foo'), ('bar', ['Bar1', 'Bar2'])])
self.assertEqual(expected, json._mapping)
def test___call___output(self):
from collections import OrderedDict
import json as json_parser
json = self._makeOne(_Options(fields=None))
meta = _Meta(name='foo', version='0.1', foo='Foo')
json(meta)
output = self._capture_output(json.finish)
output = json_parser.loads(
output, object_pairs_hook=OrderedDict)
expected = OrderedDict([
('foo', 'Foo'), ('name', 'foo'), ('version', '0.1')])
self.assertEqual(expected, output)
class Test_main(unittest.TestCase):
def _callFUT(self, args, monkey='simple'):
from pkginfo.commandline import main
from pkginfo.commandline import _FORMATTERS
before = _FORMATTERS[monkey]
dummy = _Formatter()
_FORMATTERS[monkey] = lambda *options: dummy
try:
main(args)
finally:
_FORMATTERS[monkey] = before
return dummy
def test_w_mising_dist(self):
from pkginfo import commandline as MUT
def _get_metadata(path_or_module, md_version):
self.assertEqual(path_or_module, 'foo')
self.assertEqual(md_version, None)
return None
with _Monkey(MUT, get_metadata=_get_metadata):
formatter = self._callFUT(['foo'])
self.assertEqual(formatter._called_with, [])
self.assertTrue(formatter._finished)
def test_w_dist_wo_download_url(self):
from pkginfo import commandline as MUT
meta = _Meta(download_url=None)
def _get_metadata(path_or_module, md_version):
self.assertEqual(path_or_module, '/path/to/foo')
self.assertEqual(md_version, None)
return meta
with _Monkey(MUT, get_metadata=_get_metadata):
formatter = self._callFUT(
['-d', 'http://example.com', '/path/to/foo'])
self.assertEqual(formatter._called_with, [meta])
self.assertTrue(formatter._finished)
self.assertEqual(meta.download_url, 'http://example.com/foo')
def test_w_dist_w_download_url(self):
from pkginfo import commandline as MUT
meta = _Meta(download_url='http://example.com/dist/foo')
def _get_metadata(path_or_module, md_version):
self.assertEqual(path_or_module, '/path/to/foo')
self.assertEqual(md_version, None)
return meta
with _Monkey(MUT, get_metadata=_get_metadata):
formatter = self._callFUT(
['-d', 'http://example.com', '/path/to/foo'])
self.assertEqual(formatter._called_with, [meta])
self.assertTrue(formatter._finished)
self.assertEqual(meta.download_url, 'http://example.com/dist/foo')
class _Options(object):
def __init__(self, **kw):
for k in kw:
self.__dict__[k] = kw[k]
class _Meta(object):
def __init__(self, **kw):
for k in kw:
self.__dict__[k] = kw[k]
def __iter__(self):
return iter(sorted(self.__dict__))
class _Monkey(object):
# context-manager for replacing module names in the scope of a test.
def __init__(self, module, **kw):
self.module = module
self.to_restore = dict([(key, getattr(module, key)) for key in kw])
for key, value in kw.items():
setattr(module, key, value)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
for key, value in self.to_restore.items():
setattr(self.module, key, value)
class _Formatter(object):
_finished = False
def __init__(self):
self._called_with = []
def __call__(self, meta):
self._called_with.append(meta)
def finish(self):
self._finished = True

View File

@ -0,0 +1,27 @@
import unittest
class DevelopTests(unittest.TestCase):
def _getTargetClass(self):
from pkginfo.develop import Develop
return Develop
def _makeOne(self, dirname=None):
return self._getTargetClass()(dirname)
def test_ctor_w_path(self):
from pkginfo.tests import _checkSample
develop = self._makeOne('.')
_checkSample(self, develop)
def test_ctor_w_invalid_path(self):
import warnings
old_filters = warnings.filters[:]
warnings.filterwarnings('ignore')
try:
develop = self._makeOne('/nonesuch')
self.assertEqual(develop.metadata_version, None)
self.assertEqual(develop.name, None)
self.assertEqual(develop.version, None)
finally:
warnings.filters[:] = old_filters

View File

@ -0,0 +1,450 @@
import unittest
class DistributionTests(unittest.TestCase):
def _getTargetClass(self):
from pkginfo.distribution import Distribution
return Distribution
def _makeOne(self, metadata_version='1.0'):
dist = self._getTargetClass()()
if metadata_version is not None:
dist.metadata_version = metadata_version
return dist
def test_ctor_defaults(self):
sdist = self._makeOne(None)
self.assertEqual(sdist.metadata_version, None)
# version 1.0
self.assertEqual(sdist.name, None)
self.assertEqual(sdist.version, None)
self.assertEqual(sdist.platforms, ())
self.assertEqual(sdist.supported_platforms, ())
self.assertEqual(sdist.summary, None)
self.assertEqual(sdist.description, None)
self.assertEqual(sdist.keywords, None)
self.assertEqual(sdist.home_page, None)
self.assertEqual(sdist.download_url, None)
self.assertEqual(sdist.author, None)
self.assertEqual(sdist.author_email, None)
self.assertEqual(sdist.license, None)
# version 1.1
self.assertEqual(sdist.classifiers, ())
self.assertEqual(sdist.requires, ())
self.assertEqual(sdist.provides, ())
self.assertEqual(sdist.obsoletes, ())
# version 1.2
self.assertEqual(sdist.maintainer, None)
self.assertEqual(sdist.maintainer_email, None)
self.assertEqual(sdist.requires_python, None)
self.assertEqual(sdist.requires_external, ())
self.assertEqual(sdist.requires_dist, ())
self.assertEqual(sdist.provides_dist, ())
self.assertEqual(sdist.obsoletes_dist, ())
self.assertEqual(sdist.project_urls, ())
# version 2.1
self.assertEqual(sdist.provides_extras, ())
self.assertEqual(sdist.description_content_type, None)
# version 2.2
self.assertEqual(sdist.dynamic, ())
def test_extractMetadata_raises_NotImplementedError(self):
# 'extractMetadata' calls 'read', which subclasses must override.
dist = self._makeOne(None)
self.assertRaises(NotImplementedError, dist.extractMetadata)
def test_read_raises_NotImplementedError(self):
# Subclasses must override 'read'.
dist = self._makeOne(None)
self.assertRaises(NotImplementedError, dist.read)
def test_parse_given_unicode(self):
from pkginfo._compat import u
dist = self._makeOne()
dist.parse(u('Metadata-Version: 1.0\nName: lp722928_c3')) # no raise
def test_parse_Metadata_Version_1_0(self):
from pkginfo.distribution import HEADER_ATTRS_1_0
dist = self._makeOne(None)
dist.parse('Metadata-Version: 1.0')
self.assertEqual(dist.metadata_version, '1.0')
self.assertEqual(list(dist),
[x[1] for x in HEADER_ATTRS_1_0])
def test_parse_Metadata_Version_1_1(self):
from pkginfo.distribution import HEADER_ATTRS_1_1
dist = self._makeOne(None)
dist.parse('Metadata-Version: 1.1')
self.assertEqual(dist.metadata_version, '1.1')
self.assertEqual(list(dist),
[x[1] for x in HEADER_ATTRS_1_1])
def test_parse_Metadata_Version_1_2(self):
from pkginfo.distribution import HEADER_ATTRS_1_2
dist = self._makeOne(None)
dist.parse('Metadata-Version: 1.2')
self.assertEqual(dist.metadata_version, '1.2')
self.assertEqual(list(dist),
[x[1] for x in HEADER_ATTRS_1_2])
def test_parse_Metadata_Version_2_1(self):
from pkginfo.distribution import HEADER_ATTRS_2_1
dist = self._makeOne(None)
dist.parse('Metadata-Version: 2.1')
self.assertEqual(dist.metadata_version, '2.1')
self.assertEqual(list(dist),
[x[1] for x in HEADER_ATTRS_2_1])
def test_parse_Metadata_Version_2_2(self):
from pkginfo.distribution import HEADER_ATTRS_2_2
dist = self._makeOne(None)
dist.parse('Metadata-Version: 2.2')
self.assertEqual(dist.metadata_version, '2.2')
self.assertEqual(list(dist),
[x[1] for x in HEADER_ATTRS_2_2])
def test_parse_Metadata_Version_unknown(self):
dist = self._makeOne(None)
dist.parse('Metadata-Version: 1.3')
self.assertEqual(dist.metadata_version, '1.3')
self.assertEqual(list(dist), [])
def test_parse_Metadata_Version_override(self):
dist = self._makeOne('1.2')
dist.parse('Metadata-Version: 1.0')
self.assertEqual(dist.metadata_version, '1.2')
def test_parse_Name(self):
dist = self._makeOne()
dist.parse('Name: foobar')
self.assertEqual(dist.name, 'foobar')
def test_parse_Version(self):
dist = self._makeOne()
dist.parse('Version: 2.1.3b5')
self.assertEqual(dist.version, '2.1.3b5')
def test_parse_Platform_single(self):
dist = self._makeOne()
dist.parse('Platform: Plan9')
self.assertEqual(list(dist.platforms), ['Plan9'])
def test_parse_Platform_multiple(self):
dist = self._makeOne()
dist.parse('Platform: Plan9\nPlatform: AIX')
self.assertEqual(list(dist.platforms), ['Plan9', 'AIX'])
def test_parse_Supported_Platform_single(self):
dist = self._makeOne()
dist.parse('Supported-Platform: Plan9')
self.assertEqual(list(dist.supported_platforms), ['Plan9'])
def test_parse_Supported_Platform_multiple(self):
dist = self._makeOne()
dist.parse('Supported-Platform: i386-win32\n'
'Supported-Platform: RedHat 7.2')
self.assertEqual(list(dist.supported_platforms),
['i386-win32', 'RedHat 7.2'])
def test_parse_Summary(self):
dist = self._makeOne()
dist.parse('Summary: Package for foo')
self.assertEqual(dist.summary, 'Package for foo')
def test_parse_Description(self):
dist = self._makeOne()
dist.parse('Description: This package enables integration with '
'foo servers.')
self.assertEqual(dist.description,
'This package enables integration with '
'foo servers.')
def test_parse_Description_multiline(self):
dist = self._makeOne()
dist.parse('Description: This package enables integration with\n'
' foo servers.')
self.assertEqual(dist.description,
'This package enables integration with\n'
'foo servers.')
def test_parse_Description_in_payload(self):
dist = self._makeOne()
dist.parse('Foo: Bar\n'
'\n'
'This package enables integration with\n'
'foo servers.')
self.assertEqual(dist.description,
'This package enables integration with\n'
'foo servers.')
def test_parse_Keywords(self):
dist = self._makeOne()
dist.parse('Keywords: bar foo qux')
self.assertEqual(dist.keywords, 'bar foo qux')
def test_parse_Home_page(self):
dist = self._makeOne()
dist.parse('Home-page: http://example.com/package')
self.assertEqual(dist.home_page, 'http://example.com/package')
def test_parse_Author(self):
dist = self._makeOne()
dist.parse('Author: J. Phredd Bloggs')
self.assertEqual(dist.author, 'J. Phredd Bloggs')
def test_parse_Author_Email(self):
dist = self._makeOne()
dist.parse('Author-email: phreddy@example.com')
self.assertEqual(dist.author_email, 'phreddy@example.com')
def test_parse_License(self):
dist = self._makeOne()
dist.parse('License: Poetic')
self.assertEqual(dist.license, 'Poetic')
# Metadata version 1.1, defined in PEP 314.
def test_parse_Classifier_single(self):
dist = self._makeOne('1.1')
dist.parse('Classifier: Some :: Silly Thing')
self.assertEqual(list(dist.classifiers), ['Some :: Silly Thing'])
def test_parse_Classifier_multiple(self):
dist = self._makeOne('1.1')
dist.parse('Classifier: Some :: Silly Thing\n'
'Classifier: Or :: Other')
self.assertEqual(list(dist.classifiers),
['Some :: Silly Thing', 'Or :: Other'])
def test_parse_Download_URL(self):
dist = self._makeOne('1.1')
dist.parse('Download-URL: '
'http://example.com/package/mypackage-0.1.zip')
self.assertEqual(dist.download_url,
'http://example.com/package/mypackage-0.1.zip')
def test_parse_Requires_single_wo_version(self):
dist = self._makeOne('1.1')
dist.parse('Requires: SpanishInquisition')
self.assertEqual(list(dist.requires), ['SpanishInquisition'])
def test_parse_Requires_single_w_version(self):
dist = self._makeOne('1.1')
dist.parse('Requires: SpanishInquisition (>=1.3)')
self.assertEqual(list(dist.requires), ['SpanishInquisition (>=1.3)'])
def test_parse_Requires_multiple(self):
dist = self._makeOne('1.1')
dist.parse('Requires: SpanishInquisition\n'
'Requires: SillyWalks (1.4)\n'
'Requires: kniggits (>=2.3,<3.0)')
self.assertEqual(list(dist.requires),
['SpanishInquisition',
'SillyWalks (1.4)',
'kniggits (>=2.3,<3.0)',
])
def test_parse_Provides_single_wo_version(self):
dist = self._makeOne('1.1')
dist.parse('Provides: SillyWalks')
self.assertEqual(list(dist.provides), ['SillyWalks'])
def test_parse_Provides_single_w_version(self):
dist = self._makeOne('1.1')
dist.parse('Provides: SillyWalks (1.4)')
self.assertEqual(list(dist.provides), ['SillyWalks (1.4)'])
def test_parse_Provides_multiple(self):
dist = self._makeOne('1.1')
dist.parse('Provides: SillyWalks\n'
'Provides: DeadlyJoke (3.1.4)')
self.assertEqual(list(dist.provides),
['SillyWalks',
'DeadlyJoke (3.1.4)',
])
def test_parse_Obsoletes_single_no_version(self):
dist = self._makeOne('1.1')
dist.parse('Obsoletes: SillyWalks')
self.assertEqual(list(dist.obsoletes), ['SillyWalks'])
def test_parse_Obsoletes_single_w_version(self):
dist = self._makeOne('1.1')
dist.parse('Obsoletes: SillyWalks (<=1.3)')
self.assertEqual(list(dist.obsoletes), ['SillyWalks (<=1.3)'])
def test_parse_Obsoletes_multiple(self):
dist = self._makeOne('1.1')
dist.parse('Obsoletes: kniggits\n'
'Obsoletes: SillyWalks (<=2.0)')
self.assertEqual(list(dist.obsoletes),
['kniggits',
'SillyWalks (<=2.0)',
])
# Metadata version 1.2, defined in PEP 345.
def test_parse_Maintainer(self):
dist = self._makeOne(metadata_version='1.2')
dist.parse('Maintainer: J. Phredd Bloggs')
self.assertEqual(dist.maintainer, 'J. Phredd Bloggs')
def test_parse_Maintainer_Email(self):
dist = self._makeOne(metadata_version='1.2')
dist.parse('Maintainer-email: phreddy@example.com')
self.assertEqual(dist.maintainer_email, 'phreddy@example.com')
def test_parse_Requires_Python_single_spec(self):
dist = self._makeOne('1.2')
dist.parse('Requires-Python: >2.4')
self.assertEqual(dist.requires_python, '>2.4')
def test_parse_Requires_External_single_wo_version(self):
dist = self._makeOne('1.2')
dist.parse('Requires-External: libfoo')
self.assertEqual(list(dist.requires_external), ['libfoo'])
def test_parse_Requires_External_single_w_version(self):
dist = self._makeOne('1.2')
dist.parse('Requires-External: libfoo (>=1.3)')
self.assertEqual(list(dist.requires_external), ['libfoo (>=1.3)'])
def test_parse_Requires_External_multiple(self):
dist = self._makeOne('1.2')
dist.parse('Requires-External: libfoo\n'
'Requires-External: libbar (1.4)\n'
'Requires-External: libbaz (>=2.3,<3.0)')
self.assertEqual(list(dist.requires_external),
['libfoo',
'libbar (1.4)',
'libbaz (>=2.3,<3.0)',
])
def test_parse_Requires_Dist_single_wo_version(self):
dist = self._makeOne('1.2')
dist.parse('Requires-Dist: SpanishInquisition')
self.assertEqual(list(dist.requires_dist), ['SpanishInquisition'])
def test_parse_Requires_Dist_single_w_version(self):
dist = self._makeOne('1.2')
dist.parse('Requires-Dist: SpanishInquisition (>=1.3)')
self.assertEqual(list(dist.requires_dist),
['SpanishInquisition (>=1.3)'])
def test_parse_Requires_Dist_single_w_env_marker(self):
dist = self._makeOne('1.2')
dist.parse("Requires-Dist: SpanishInquisition; "
"python_version == '1.4'")
self.assertEqual(list(dist.requires_dist),
["SpanishInquisition; python_version == '1.4'"])
def test_parse_Requires_Dist_multiple(self):
dist = self._makeOne('1.2')
dist.parse("Requires-Dist: SpanishInquisition\n"
"Requires-Dist: SillyWalks; python_version == '1.4'\n"
"Requires-Dist: kniggits (>=2.3,<3.0)")
self.assertEqual(list(dist.requires_dist),
["SpanishInquisition",
"SillyWalks; python_version == '1.4'",
"kniggits (>=2.3,<3.0)",
])
def test_parse_Provides_Dist_single_wo_version(self):
dist = self._makeOne('1.2')
dist.parse('Provides-Dist: SillyWalks')
self.assertEqual(list(dist.provides_dist), ['SillyWalks'])
def test_parse_Provides_Dist_single_w_version(self):
dist = self._makeOne('1.2')
dist.parse('Provides-Dist: SillyWalks (1.4)')
self.assertEqual(list(dist.provides_dist), ['SillyWalks (1.4)'])
def test_parse_Provides_Dist_single_w_env_marker(self):
dist = self._makeOne('1.2')
dist.parse("Provides-Dist: SillyWalks; sys.platform == 'os2'")
self.assertEqual(list(dist.provides_dist),
["SillyWalks; sys.platform == 'os2'"])
def test_parse_Provides_Dist_multiple(self):
dist = self._makeOne('1.2')
dist.parse("Provides-Dist: SillyWalks\n"
"Provides-Dist: SpanishInquisition; sys.platform == 'os2'\n"
"Provides-Dist: DeadlyJoke (3.1.4)")
self.assertEqual(list(dist.provides_dist),
["SillyWalks",
"SpanishInquisition; sys.platform == 'os2'",
"DeadlyJoke (3.1.4)",
])
def test_parse_Obsoletes_Dist_single_no_version(self):
dist = self._makeOne('1.2')
dist.parse('Obsoletes-Dist: SillyWalks')
self.assertEqual(list(dist.obsoletes_dist), ['SillyWalks'])
def test_parse_Obsoletes_Dist_single_w_version(self):
dist = self._makeOne('1.2')
dist.parse('Obsoletes-Dist: SillyWalks (<=1.3)')
self.assertEqual(list(dist.obsoletes_dist), ['SillyWalks (<=1.3)'])
def test_parse_Obsoletes_Dist_single_w_env_marker(self):
dist = self._makeOne('1.2')
dist.parse("Obsoletes-Dist: SillyWalks; sys.platform == 'os2'")
self.assertEqual(list(dist.obsoletes_dist),
["SillyWalks; sys.platform == 'os2'"])
def test_parse_Obsoletes_Dist_multiple(self):
dist = self._makeOne('1.2')
dist.parse("Obsoletes-Dist: kniggits\n"
"Obsoletes-Dist: SillyWalks; sys.platform == 'os2'\n"
"Obsoletes-Dist: DeadlyJoke (<=2.0)\n"
)
self.assertEqual(list(dist.obsoletes_dist),
["kniggits",
"SillyWalks; sys.platform == 'os2'",
"DeadlyJoke (<=2.0)",
])
def test_parse_Project_URL_single_no_version(self):
dist = self._makeOne('1.2')
dist.parse('Project-URL: Bug tracker, http://bugs.example.com/grail')
self.assertEqual(list(dist.project_urls),
['Bug tracker, http://bugs.example.com/grail'])
def test_parse_Project_URL_multiple(self):
dist = self._makeOne('1.2')
dist.parse('Project-URL: Bug tracker, http://bugs.example.com/grail\n'
'Project-URL: Repository, http://svn.example.com/grail')
self.assertEqual(list(dist.project_urls),
['Bug tracker, http://bugs.example.com/grail',
'Repository, http://svn.example.com/grail',
])
# Metadata version 2.1, defined in PEP 566.
def test_parse_Provides_Extra_single(self):
dist = self._makeOne('2.1')
dist.parse('Provides-Extra: pdf')
self.assertEqual(list(dist.provides_extras), ['pdf'])
def test_parse_Provides_Extra_multiple(self):
dist = self._makeOne('2.1')
dist.parse('Provides-Extra: pdf\n'
'Provides-Extra: tex')
self.assertEqual(list(dist.provides_extras), ['pdf', 'tex'])
def test_parse_Provides_Extra_single(self):
dist = self._makeOne('2.1')
dist.parse('Description-Content-Type: text/plain')
self.assertEqual(dist.description_content_type, 'text/plain')
# Metadata version 2.2, defined in PEP 643.
def test_parse_Dynamic_single(self):
dist = self._makeOne('2.2')
dist.parse('Dynamic: Platforms')
self.assertEqual(list(dist.dynamic), ['Platforms'])
def test_parse_Dynamic_multiple(self):
dist = self._makeOne('2.2')
dist.parse('Dynamic: Platforms\n'
'Dynamic: Supported-Platforms')
self.assertEqual(list(dist.dynamic),
['Platforms', 'Supported-Platforms'])

View File

@ -0,0 +1,76 @@
import unittest
class IndexTests(unittest.TestCase):
def _getTargetClass(self):
from pkginfo.index import Index
return Index
def _makeOne(self):
return self._getTargetClass()()
def test_empty(self):
index = self._makeOne()
self.assertEqual(len(index), 0)
self.assertEqual(len(index.keys()), 0)
self.assertEqual(len(index.values()), 0)
self.assertEqual(len(index.items()), 0)
def _makeDummy(self):
from pkginfo.distribution import Distribution
class DummyDistribution(Distribution):
name = 'dummy'
version = '1.0'
return DummyDistribution()
def test___getitem___miss(self):
index = self._makeOne()
self.assertRaises(KeyError, index.__getitem__, 'nonesuch')
def test___setitem___value_not_dist(self):
class NotDistribution:
name = 'dummy'
version = '1.0'
dummy = NotDistribution()
index = self._makeOne()
self.assertRaises(ValueError, index.__setitem__, 'dummy-1.0', dummy)
def test___setitem___bad_key(self):
index = self._makeOne()
dummy = self._makeDummy()
self.assertRaises(ValueError, index.__setitem__, 'nonesuch', dummy)
def test___setitem___valid_key(self):
index = self._makeOne()
dummy = self._makeDummy()
index['dummy-1.0'] = dummy
self.assertTrue(index['dummy-1.0'] is dummy)
self.assertEqual(len(index), 1)
self.assertEqual(len(index.keys()), 1)
self.assertEqual(list(index.keys())[0], 'dummy-1.0')
self.assertEqual(len(index.values()), 1)
self.assertEqual(list(index.values())[0], dummy)
self.assertEqual(len(index.items()), 1)
self.assertEqual(list(index.items())[0], ('dummy-1.0', dummy))
def test_add_not_dist(self):
index = self._makeOne()
class NotDistribution:
name = 'dummy'
version = '1.0'
dummy = NotDistribution()
self.assertRaises(ValueError, index.add, dummy)
def test_add_valid_dist(self):
index = self._makeOne()
dummy = self._makeDummy()
index.add(dummy)
self.assertTrue(index['dummy-1.0'] is dummy)
self.assertEqual(len(index), 1)
self.assertEqual(len(index.keys()), 1)
self.assertEqual(list(index.keys())[0], 'dummy-1.0')
self.assertEqual(len(index.values()), 1)
self.assertEqual(list(index.values())[0], dummy)
self.assertEqual(len(index.items()), 1)
self.assertEqual(list(index.items())[0], ('dummy-1.0', dummy))

View File

@ -0,0 +1,142 @@
import unittest
class InstalledTests(unittest.TestCase):
def _getTargetClass(self):
from pkginfo.installed import Installed
return Installed
def _makeOne(self, filename=None, metadata_version=None):
if metadata_version is not None:
return self._getTargetClass()(filename, metadata_version)
return self._getTargetClass()(filename)
def test_ctor_w_package_no___file__(self):
import sys
import warnings
with warnings.catch_warnings(record=True):
installed = self._makeOne(sys)
self.assertEqual(installed.package, sys)
self.assertEqual(installed.package_name, 'sys')
self.assertEqual(installed.metadata_version, None)
def test_ctor_w_package(self):
import pkginfo
from pkginfo.tests import _checkSample
from pkginfo.tests import _defaultMetadataVersion
EXPECTED = _defaultMetadataVersion()
installed = self._makeOne(pkginfo)
self.assertEqual(installed.package, pkginfo)
self.assertEqual(installed.package_name, 'pkginfo')
self.assertEqual(installed.metadata_version, EXPECTED)
_checkSample(self, installed)
def test_ctor_w_no___package___falls_back_to___name__(self):
import sys
import wsgiref
import warnings
with warnings.catch_warnings(record=True):
installed = self._makeOne(wsgiref)
self.assertEqual(installed.package, wsgiref)
self.assertEqual(installed.package_name, 'wsgiref')
if sys.version_info[:2] >= (3, 3):
self.assertEqual(installed.metadata_version, None)
else:
self.assertEqual(installed.metadata_version, '1.0')
def test_ctor_w_package_no_PKG_INFO(self):
import sys
import types
import warnings
with warnings.catch_warnings(record=True):
installed = self._makeOne(types)
self.assertEqual(installed.package, types)
self.assertEqual(installed.package_name, 'types')
self.assertEqual(installed.metadata_version, None)
def test_ctor_w_package_and_metadata_version(self):
import pkginfo
from pkginfo.tests import _checkSample
installed = self._makeOne(pkginfo, metadata_version='1.2')
self.assertEqual(installed.metadata_version, '1.2')
self.assertEqual(installed.package.__name__, 'pkginfo')
_checkSample(self, installed)
def test_ctor_w_name(self):
import pkginfo
from pkginfo.tests import _checkSample
from pkginfo.tests import _defaultMetadataVersion
EXPECTED = _defaultMetadataVersion()
installed = self._makeOne('pkginfo')
self.assertEqual(installed.metadata_version, EXPECTED)
self.assertEqual(installed.package, pkginfo)
self.assertEqual(installed.package_name, 'pkginfo')
_checkSample(self, installed)
def test_ctor_w_name_and_metadata_version(self):
import pkginfo
from pkginfo.tests import _checkSample
installed = self._makeOne('pkginfo', metadata_version='1.2')
self.assertEqual(installed.metadata_version, '1.2')
self.assertEqual(installed.package, pkginfo)
self.assertEqual(installed.package_name, 'pkginfo')
_checkSample(self, installed)
def test_ctor_w_invalid_name(self):
import warnings
with warnings.catch_warnings(record=True):
installed = self._makeOne('nonesuch')
self.assertEqual(installed.package, None)
self.assertEqual(installed.package_name, 'nonesuch')
self.assertEqual(installed.metadata_version, None)
def test_ctor_w_egg_info_as_file(self):
import pkginfo.tests.funny
installed = self._makeOne('pkginfo.tests.funny')
self.assertEqual(installed.metadata_version, '1.0')
self.assertEqual(installed.package, pkginfo.tests.funny)
self.assertEqual(installed.package_name, 'pkginfo.tests.funny')
def test_ctor_w_dist_info(self):
import wheel
installed = self._makeOne('wheel')
self.assertEqual(installed.metadata_version, '2.1')
self.assertEqual(installed.package, wheel)
self.assertEqual(installed.package_name, 'wheel')
def test_namespaced_pkg_installed_via_setuptools(self):
import os
import sys
where, _ = os.path.split(__file__)
wonky = os.path.join(where, 'wonky')
oldpath = sys.path[:]
try:
sys.path.append(wonky)
import namespaced.wonky
installed = self._makeOne('namespaced.wonky')
self.assertEqual(installed.metadata_version, '1.0')
self.assertEqual(installed.package, namespaced.wonky)
self.assertEqual(installed.package_name, 'namespaced.wonky')
finally:
sys.path[:] = oldpath
sys.modules.pop('namespaced.wonky', None)
sys.modules.pop('namespaced', None)
def test_namespaced_pkg_installed_via_pth(self):
# E.g., installed by a Linux distro
import os
import sys
where, _ = os.path.split(__file__)
manky = os.path.join(where, 'manky')
oldpath = sys.path[:]
try:
sys.path.append(manky)
import namespaced.manky
installed = self._makeOne('namespaced.manky')
self.assertEqual(installed.metadata_version, '1.0')
self.assertEqual(installed.package, namespaced.manky)
self.assertEqual(installed.package_name, 'namespaced.manky')
finally:
sys.path[:] = oldpath
sys.modules.pop('namespaced.manky', None)
sys.modules.pop('namespaced', None)

158
pkginfo/tests/test_sdist.py Normal file
View File

@ -0,0 +1,158 @@
import shutil
import tempfile
import unittest
class SDistTests(unittest.TestCase):
def _getTargetClass(self):
from pkginfo.sdist import SDist
return SDist
def _makeOne(self, filename=None, metadata_version=None):
if metadata_version is not None:
return self._getTargetClass()(filename, metadata_version)
return self._getTargetClass()(filename)
def _checkSample(self, sdist, filename):
self.assertEqual(sdist.filename, filename)
self.assertEqual(sdist.name, 'mypackage')
self.assertEqual(sdist.version, '0.1')
self.assertEqual(sdist.keywords, None)
self.assertEqual(list(sdist.supported_platforms), [])
def _checkClassifiers(self, sdist):
self.assertEqual(list(sdist.classifiers),
['Development Status :: 4 - Beta',
'Environment :: Console (Text Based)',
])
def test_ctor_w_invalid_filename(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/nonesuch-0.1.tar.gz' % d
self.assertRaises(ValueError, self._makeOne, filename)
def test_ctor_wo_PKG_INFO(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/nopkginfo-0.1.zip' % d
self.assertRaises(ValueError, self._makeOne, filename)
def test_ctor_w_tar(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar' % d
sdist = self._makeOne(filename)
self.assertEqual(sdist.metadata_version, '1.0')
self._checkSample(sdist, filename)
def test_ctor_w_gztar(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar.gz' % d
sdist = self._makeOne(filename)
self.assertEqual(sdist.metadata_version, '1.0')
self._checkSample(sdist, filename)
def test_ctor_w_gztar_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar.gz' % d
sdist = self._makeOne(filename, metadata_version='1.1')
self._checkSample(sdist, filename)
self.assertEqual(sdist.metadata_version, '1.1')
self._checkClassifiers(sdist)
def test_ctor_w_bztar(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar.bz2' % d
sdist = self._makeOne(filename)
self.assertEqual(sdist.metadata_version, '1.0')
self._checkSample(sdist, filename)
def test_ctor_w_bztar_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar.bz2' % d
sdist = self._makeOne(filename, metadata_version='1.1')
self.assertEqual(sdist.metadata_version, '1.1')
self._checkSample(sdist, filename)
self._checkClassifiers(sdist)
def test_ctor_w_zip(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.zip' % d
sdist = self._makeOne(filename)
self.assertEqual(sdist.metadata_version, '1.0')
self._checkSample(sdist, filename)
def test_ctor_w_zip_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.zip' % d
sdist = self._makeOne(filename, metadata_version='1.1')
self.assertEqual(sdist.metadata_version, '1.1')
self._checkSample(sdist, filename)
self._checkClassifiers(sdist)
def test_ctor_w_bogus(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.bogus' % d
with self.assertRaises(ValueError):
self._makeOne(filename, metadata_version='1.1')
class UnpackedMixin(object):
def setUp(self):
super(UnpackedMixin, self).setUp()
self.__tmpdir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.__tmpdir)
super(UnpackedMixin, self).tearDown()
def _getTargetClass(self):
from pkginfo.sdist import UnpackedSDist
return UnpackedSDist
def _getTopDirectory(self):
import os
topnames = os.listdir(self.__tmpdir)
if len(topnames) == 1:
return os.path.join(self.__tmpdir, topnames[0])
else:
return self.__tmpdir
def _getLoadFilename(self):
return self._getTopDirectory()
def _makeOne(self, filename=None, metadata_version=None):
archive, _, _ = self._getTargetClass()._get_archive(filename)
try:
archive.extractall(self.__tmpdir)
finally:
archive.close()
load_filename = self._getLoadFilename()
if metadata_version is not None:
return self._getTargetClass()(load_filename, metadata_version)
return self._getTargetClass()(load_filename)
def _checkSample(self, sdist, filename):
filename = self._getTopDirectory()
super(UnpackedMixin, self)._checkSample(sdist, filename)
class UnpackedSDistGivenDirectoryTests(UnpackedMixin, SDistTests):
pass
class UnpackedSDistGivenFileSDistTests(UnpackedMixin, SDistTests):
def _getLoadFilename(self):
import os
return os.path.join(self._getTopDirectory(), 'setup.py')

176
pkginfo/tests/test_utils.py Normal file
View File

@ -0,0 +1,176 @@
import unittest
class Test_get_metadata(unittest.TestCase):
def _callFUT(self, path, metadata_version=None):
from pkginfo.utils import get_metadata
if metadata_version is not None:
return get_metadata(path, metadata_version)
return get_metadata(path)
def _checkMyPackage(self, dist, filename):
self.assertEqual(dist.filename, filename)
self.assertEqual(dist.name, 'mypackage')
self.assertEqual(dist.version, '0.1')
self.assertEqual(dist.keywords, None)
self.assertEqual(list(dist.supported_platforms), [])
def _checkClassifiers(self, dist):
self.assertEqual(list(dist.classifiers),
['Development Status :: 4 - Beta',
'Environment :: Console (Text Based)',
])
def test_w_gztar(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar.gz' % d
dist = self._callFUT(filename)
self.assertEqual(dist.metadata_version, '1.0')
self._checkMyPackage(dist, filename)
def test_w_gztar_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar.gz' % d
dist = self._callFUT(filename, metadata_version='1.1')
self.assertEqual(dist.metadata_version, '1.1')
self._checkMyPackage(dist, filename)
self._checkClassifiers(dist)
def test_w_bztar(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar.bz2' % d
dist = self._callFUT(filename)
self.assertEqual(dist.metadata_version, '1.0')
self._checkMyPackage(dist, filename)
def test_w_bztar_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.tar.bz2' % d
dist = self._callFUT(filename, metadata_version='1.1')
self.assertEqual(dist.metadata_version, '1.1')
self._checkMyPackage(dist, filename)
self._checkClassifiers(dist)
def test_w_zip(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.zip' % d
dist = self._callFUT(filename)
self.assertEqual(dist.metadata_version, '1.0')
self._checkMyPackage(dist, filename)
def test_w_zip_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.zip' % d
dist = self._callFUT(filename, metadata_version='1.1')
self.assertEqual(dist.metadata_version, '1.1')
self._checkMyPackage(dist, filename)
self._checkClassifiers(dist)
def test_w_egg(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1-py2.6.egg' % d
dist = self._callFUT(filename)
self.assertEqual(dist.metadata_version, '1.0')
self._checkMyPackage(dist, filename)
def test_w_egg_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1-py2.6.egg' % d
dist = self._callFUT(filename, metadata_version='1.1')
self.assertEqual(dist.metadata_version, '1.1')
self._checkMyPackage(dist, filename)
self._checkClassifiers(dist)
def test_w_wheel(self):
import os
d, _ = os.path.split(__file__)
filename = ('%s/../../docs/examples/'
'mypackage-0.1-cp26-none-linux_x86_64.whl') % d
dist = self._callFUT(filename)
self.assertEqual(dist.metadata_version, '2.0')
self._checkMyPackage(dist, filename)
def test_w_wheel_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = ('%s/../../docs/examples/'
'mypackage-0.1-cp26-none-linux_x86_64.whl') % d
dist = self._callFUT(filename, metadata_version='1.1')
self.assertEqual(dist.metadata_version, '1.1')
self._checkMyPackage(dist, filename)
self._checkClassifiers(dist)
def test_w_module(self):
from pkginfo.tests import _defaultMetadataVersion
EXPECTED = _defaultMetadataVersion()
import pkginfo
from pkginfo.tests import _checkSample
dist = self._callFUT(pkginfo)
self.assertEqual(dist.metadata_version, EXPECTED)
_checkSample(self, dist)
def test_w_module_and_metadata_version(self):
import pkginfo
from pkginfo.tests import _checkSample
from pkginfo.tests import _checkClassifiers
dist = self._callFUT(pkginfo, metadata_version='1.2')
self.assertEqual(dist.metadata_version, '1.2')
_checkSample(self, dist)
_checkClassifiers(self, dist)
def test_w_package_name(self):
from pkginfo.tests import _defaultMetadataVersion
EXPECTED = _defaultMetadataVersion()
from pkginfo.tests import _checkSample
dist = self._callFUT('pkginfo')
self.assertEqual(dist.metadata_version, EXPECTED)
_checkSample(self, dist)
def test_w_package_name_and_metadata_version(self):
from pkginfo.tests import _checkSample
from pkginfo.tests import _checkClassifiers
dist = self._callFUT('pkginfo', metadata_version='1.2')
self.assertEqual(dist.metadata_version, '1.2')
_checkSample(self, dist)
_checkClassifiers(self, dist)
def test_w_directory_no_EGG_INFO(self):
import os
import warnings
dir, name = os.path.split(__file__)
subdir = os.path.join(dir, 'funny')
old_filters = warnings.filters[:]
warnings.filterwarnings('ignore')
try:
dist = self._callFUT(subdir)
self.assertEqual(dist.path, subdir)
self.assertEqual(dist.name, None)
self.assertEqual(dist.version, None)
finally:
warnings.filters[:] = old_filters
def test_w_directory(self):
import os
dir, name = os.path.split(__file__)
subdir = os.path.join(dir, 'silly')
dist = self._callFUT(subdir)
self.assertEqual(dist.metadata_version, '1.0')
self.assertEqual(dist.name, 'silly')
self.assertEqual(dist.version, '0.1')
def test_w_directory_and_metadata_version(self):
import os
dir, name = os.path.split(__file__)
subdir = os.path.join(dir, 'silly')
dist = self._callFUT(subdir, metadata_version='1.2')
self.assertEqual(dist.metadata_version, '1.2')
self.assertEqual(dist.name, 'silly')
self.assertEqual(dist.version, '0.1')

105
pkginfo/tests/test_wheel.py Normal file
View File

@ -0,0 +1,105 @@
import unittest
class WheelTests(unittest.TestCase):
def _getTargetClass(self):
from pkginfo.wheel import Wheel
return Wheel
def _makeOne(self, filename=None, metadata_version=None):
if metadata_version is not None:
return self._getTargetClass()(filename, metadata_version)
return self._getTargetClass()(filename)
def _checkSample(self, wheel, filename):
self.assertEqual(wheel.filename, filename)
self.assertEqual(wheel.name, 'mypackage')
self.assertEqual(wheel.version, '0.1')
self.assertEqual(wheel.keywords, None)
def _checkClassifiers(self, wheel):
self.assertEqual(list(wheel.classifiers),
['Development Status :: 4 - Beta',
'Environment :: Console (Text Based)',
])
self.assertEqual(list(wheel.supported_platforms), [])
def test_ctor_w_bogus_filename(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/nonesuch-0.1-any.whl' % d
self.assertRaises(ValueError, self._makeOne, filename)
def test_ctor_w_non_wheel(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/mypackage-0.1.zip' % d
self.assertRaises(ValueError, self._makeOne, filename)
def test_ctor_wo_dist_info(self):
import os
d, _ = os.path.split(__file__)
filename = '%s/../../docs/examples/nodistinfo-0.1-any.whl' % d
self.assertRaises(ValueError, self._makeOne, filename)
def test_ctor_w_valid_wheel(self):
import os
d, _ = os.path.split(__file__)
filename = ('%s/../../docs/examples/'
'mypackage-0.1-cp26-none-linux_x86_64.whl') % d
wheel = self._makeOne(filename)
self.assertEqual(wheel.metadata_version, '2.0')
self._checkSample(wheel, filename)
self._checkClassifiers(wheel)
def test_ctor_w_installed_wheel(self):
import os
d, _ = os.path.split(__file__)
filename = (
'%s/../../docs/examples/mypackage-0.1.dist-info') % d
wheel = self._makeOne(filename)
self.assertEqual(wheel.metadata_version, '2.0')
self._checkSample(wheel, filename)
self._checkClassifiers(wheel)
def test_ctor_w_valid_wheel_and_metadata_version(self):
import os
d, _ = os.path.split(__file__)
filename = ('%s/../../docs/examples/'
'mypackage-0.1-cp26-none-linux_x86_64.whl') % d
wheel = self._makeOne(filename, metadata_version='1.1')
self.assertEqual(wheel.metadata_version, '1.1')
self._checkSample(wheel, filename)
self._checkClassifiers(wheel)
def test_ctor_w_valid_wheel_w_description_header(self):
import os
d, _ = os.path.split(__file__)
filename = ('%s/../../docs/examples/'
'distlib-0.3.1-py2.py3-none-any.whl') % d
wheel = self._makeOne(filename, metadata_version='1.1')
self.assertEqual(wheel.metadata_version, '1.1')
self.assertTrue(wheel.description)
def test_ctor_w_valid_installed_wheel(self):
import os
import shutil
import tempfile
import zipfile
d, _ = os.path.split(__file__)
filename = ('%s/../../docs/examples/'
'mypackage-0.1-cp26-none-linux_x86_64.whl') % d
try:
# note: we mock a wheel installation by unzipping
test_dir = tempfile.mkdtemp()
with zipfile.ZipFile(filename) as zipf:
zipf.extractall(test_dir)
wheel = self._makeOne(filename)
self.assertEqual(wheel.metadata_version, '2.0')
self._checkSample(wheel, filename)
self._checkClassifiers(wheel)
finally:
if os.path.exists(test_dir):
shutil.rmtree(test_dir)

View File

@ -0,0 +1,2 @@
Metadata-Version: 1.0
Name: namespaced.wonky

View File

@ -0,0 +1,4 @@
THIS IS NOT A PYTHON PACKAGE!!!!
It is meant to be added to sys.path for testing introspection of namespace
packages installed via setuptools.

View File

@ -0,0 +1,7 @@
# this is a namespace package
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)

View File

@ -0,0 +1 @@
# Dummy package inside the 'namespaced' namespace.

62
pkginfo/utils.py Normal file
View File

@ -0,0 +1,62 @@
import os
from types import ModuleType
from .bdist import BDist
from .develop import Develop
from .installed import Installed
from .sdist import SDist
from .wheel import Wheel
def get_metadata(path_or_module, metadata_version=None):
""" Try to create a Distribution 'path_or_module'.
o 'path_or_module' may be a module object.
o If a string, 'path_or_module' may point to an sdist file, a bdist
file, an installed package, or a working checkout (if it contains
PKG-INFO).
o Return None if 'path_or_module' can't be parsed.
"""
if isinstance(path_or_module, ModuleType):
try:
return Installed(path_or_module, metadata_version)
except (ValueError, IOError): #pragma NO COVER
pass
try:
__import__(path_or_module)
except ImportError:
pass
else:
try:
return Installed(path_or_module, metadata_version)
except (ValueError, IOError): #pragma NO COVER
pass
if os.path.isfile(path_or_module):
try:
return SDist(path_or_module, metadata_version)
except (ValueError, IOError):
pass
try:
return BDist(path_or_module, metadata_version)
except (ValueError, IOError): #pragma NO COVER
pass
try:
return Wheel(path_or_module, metadata_version)
except (ValueError, IOError): #pragma NO COVER
pass
if os.path.isdir(path_or_module):
try:
return Wheel(path_or_module, metadata_version)
except (ValueError, IOError): #pragma NO COVER
pass
try:
return Develop(path_or_module, metadata_version)
except (ValueError, IOError): #pragma NO COVER
pass

63
pkginfo/wheel.py Normal file
View File

@ -0,0 +1,63 @@
import io
import os
import zipfile
from .distribution import Distribution
from .distribution import must_decode
from .distribution import parse
class Wheel(Distribution):
def __init__(self, filename, metadata_version=None):
self.filename = filename
self.metadata_version = metadata_version
self.extractMetadata()
def read(self):
fqn = os.path.abspath(os.path.normpath(self.filename))
if not os.path.exists(fqn):
raise ValueError('No such file: %s' % fqn)
if fqn.endswith('.whl'):
archive = zipfile.ZipFile(fqn)
names = archive.namelist()
def read_file(name):
return archive.read(name)
close = archive.close
elif fqn.endswith('.dist-info'):
names = [os.path.join(fqn, p) for p in os.listdir(fqn)]
def read_file(name):
with io.open(name, mode='rb') as inf:
return inf.read()
close = lambda : None
else:
raise ValueError('Not a known wheel archive format or '
'installed .dist-info: %s' % fqn)
try:
tuples = [x.split('/') for x in names if 'METADATA' in x]
schwarz = sorted([(len(x), x) for x in tuples])
for path in [x[1] for x in schwarz]:
candidate = '/'.join(path)
data = read_file(candidate)
if b'Metadata-Version' in data:
return data
finally:
close()
raise ValueError('No METADATA in archive: %s' % fqn)
def parse(self, data):
super(Wheel, self).parse(data)
fp = io.StringIO(must_decode(data))
msg = parse(fp)
if self.description is None:
self.description = msg.get_payload()

18
setup.cfg Normal file
View File

@ -0,0 +1,18 @@
[bdist_wheel]
universal = 1
[easy_install]
zip_ok = false
[nosetests]
nocapture = 1
cover-package = pkginfo
cover-erase = 1
[aliases]
dev = develop easy_install pkginfo[testing]
[egg_info]
tag_build =
tag_date = 0

55
setup.py Normal file
View File

@ -0,0 +1,55 @@
#!/usr/bin/env python
import os
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
extras = {}
else:
extras = {
'test_suite': 'pkginfo.tests',
'zip_safe': False,
'extras_require': {
'testing': ['nose', 'coverage'],
},
}
here = os.path.abspath(os.path.dirname(__file__))
README = open(os.path.join(here, 'README.txt')).read()
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
setup(
name='pkginfo',
version='1.8.2',
description='Query metadatdata from sdists / bdists / installed packages.',
platforms=['Unix', 'Windows'],
long_description='\n\n'.join([README, CHANGES]),
keywords='distribution sdist installed metadata',
url='https://code.launchpad.net/~tseaver/pkginfo/trunk',
author='Tres Seaver, Agendaless Consulting',
author_email='tseaver@agendaless.com',
license='MIT',
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: System :: Software Distribution',
],
entry_points={
'console_scripts': [
'pkginfo = pkginfo.commandline:main',
]
},
packages=['pkginfo', 'pkginfo.tests'],
**extras
)

37
tox.ini Normal file
View File

@ -0,0 +1,37 @@
[tox]
envlist =
py27,pypy,py36,py37,py38,py39,py310,pypy3,cover2,cover3,docs
[testenv]
usedevelop = true
commands =
python setup.py test -q
[testenv:cover2]
basepython =
python2.7
commands =
python setup.py nosetests --with-xunit --with-xcoverage
deps =
nose
coverage
nosexcover
[testenv:cover3]
basepython =
python3.7
commands =
python setup.py nosetests --with-xunit --with-xcoverage
deps =
nose
coverage
nosexcover
[testenv:docs]
basepython =
python3.7
commands =
sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
deps =
Sphinx